BSL v0.0.0
AMMOS Bundle Protocol Security Library (BSL)
Loading...
Searching...
No Matches
CryptoInterface.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2025 The Johns Hopkins University Applied Physics
3 * Laboratory LLC.
4 *
5 * This file is part of the Bundle Protocol Security Library (BSL).
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 * This work was performed for the Jet Propulsion Laboratory, California
18 * Institute of Technology, sponsored by the United States Government under
19 * the prime contract 80NM0018D0004 between the Caltech and NASA under
20 * subcontract 1700763.
21 */
26#include <BPSecLib_Private.h>
27#include <CryptoInterface.h>
28
29#include <m-dict.h>
30#include <openssl/err.h>
31#include <openssl/rand.h>
32
43
44static int BSLB_CryptoKey_Deinit(BSLB_CryptoKey_t *key)
45{
46 fflush(stdout); // NOLINT
47 EVP_PKEY_free(key->pkey);
48 BSL_Data_Deinit(&(key->raw));
49 return 0;
50}
51
52// NOLINTBEGIN
54#define M_OPL_BSLB_CryptoKey_t() M_OPEXTEND(M_POD_OPLIST, CLEAR(API_2(BSLB_CryptoKey_Deinit)))
56DICT_DEF2(BSLB_CryptoKeyDict, uint64_t, M_BASIC_OPLIST, BSLB_CryptoKey_t, M_OPL_BSLB_CryptoKey_t())
58
60static BSLB_CryptoKeyDict_t StaticKeyRegistry;
61static pthread_mutex_t StaticCryptoMutex = PTHREAD_MUTEX_INITIALIZER;
62// NOLINTEND
63
65{
66 BSLB_CryptoKeyDict_init(StaticKeyRegistry);
67}
68
70{
71 BSLB_CryptoKeyDict_clear(StaticKeyRegistry);
72}
73
74int BSL_Crypto_UnwrapKey(BSL_Data_t *unwrapped_key_output, BSL_Data_t wrapped_key_plaintext, size_t key_id,
75 size_t aes_variant)
76{
77 const EVP_CIPHER *cipher = (aes_variant == BSL_CRYPTO_AES_128) ? EVP_aes_128_wrap() : EVP_aes_256_wrap();
78 EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
79 assert(ctx != NULL);
80
81 // Give the actual key extra margin on each side.
82 uint8_t keybuf[128];
83 uint8_t *key = &keybuf[16];
84 memset(keybuf, 0, sizeof(keybuf));
85
86 size_t keylen = 0;
87 assert(BSLB_Crypto_GetRegistryKey(key_id, (const uint8_t **)&key, &keylen) == 0);
88 assert(keylen > 0);
89
90 int dec_result = EVP_DecryptInit_ex(ctx, cipher, NULL, key, NULL);
91 assert(dec_result == 1);
92 EVP_CIPHER_CTX_set_padding(ctx, 0);
93
94 int unwrapped_key_len = 16;
95 int decrypt_res = EVP_DecryptUpdate(ctx, unwrapped_key_output->ptr, &unwrapped_key_len, wrapped_key_plaintext.ptr,
96 (int)wrapped_key_plaintext.len);
97 if (decrypt_res != 1)
98 {
99 BSL_LOG_ERR("EVP_DecryptUpdate: %s", ERR_error_string(ERR_get_error(), NULL));
100 EVP_CIPHER_CTX_free(ctx);
101 return -1;
102 }
103
104 unwrapped_key_output->len = (size_t)unwrapped_key_len;
105
106 int final_len = 0;
107 int res = EVP_DecryptFinal_ex(ctx, &unwrapped_key_output->ptr[unwrapped_key_output->len], &final_len);
108 if (res != 1)
109 {
110 BSL_LOG_ERR("Failed DecryptFinal: %s", ERR_error_string(ERR_get_error(), NULL));
111 EVP_CIPHER_CTX_free(ctx);
112 return -1;
113 }
114 unwrapped_key_output->len += (size_t)final_len;
115
116 EVP_CIPHER_CTX_free(ctx);
117 return 0;
118}
119
120int BSL_Crypto_WrapKey(BSL_Data_t *wrapped_key, BSL_Data_t cek, size_t content_key_id, size_t aes_variant)
121{
122 const EVP_CIPHER *cipher = (aes_variant == BSL_CRYPTO_AES_128) ? EVP_aes_128_wrap() : EVP_aes_256_wrap();
123 EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
124 if (ctx == NULL)
125 {
126 BSL_LOG_ERR("Could not create cipher context");
127 return -1;
128 }
129
130 uint8_t keybuf[128];
131 uint8_t *key = &keybuf[16];
132 memset(keybuf, 0, sizeof(keybuf));
133 size_t keylen = 64;
134
135 // TODO(bvb) replace w error checking
136 BSL_LOG_INFO("Content key ID = %lu", cek);
137 int got_crypto_key = BSLB_Crypto_GetRegistryKey(content_key_id, (const uint8_t **)&key, &keylen);
138 assert(got_crypto_key == 0);
139 assert(keylen > 0);
140
141 int enc_result = EVP_EncryptInit_ex(ctx, cipher, NULL, cek.ptr, NULL);
142 if (!enc_result)
143 {
144 EVP_CIPHER_CTX_free(ctx);
145 return -1;
146 }
147
148 int len = (int)wrapped_key->len;
149 if (!EVP_EncryptUpdate(ctx, wrapped_key->ptr, &len,
150 // (int*)&wrapped_key->len,
151 key, (int)keylen))
152 {
153 EVP_CIPHER_CTX_free(ctx);
154 return -2;
155 }
156
157 wrapped_key->len = (size_t)len;
158 int final_len = 0;
159 if (!EVP_EncryptFinal_ex(ctx, &wrapped_key->ptr[wrapped_key->len], &final_len))
160 {
161 EVP_CIPHER_CTX_free(ctx);
162 return -1;
163 }
164 wrapped_key->len += (size_t)final_len;
165 EVP_CIPHER_CTX_free(ctx);
166 return 0;
167}
168
169int BSL_AuthCtx_Init(BSL_AuthCtx_t *hmac_ctx, uint64_t keyid, BSL_CryptoCipherSHAVariant_e sha_var)
170{
171 CHK_ARG_NONNULL(hmac_ctx);
172
173 hmac_ctx->libhandle = EVP_MD_CTX_new();
174 CHK_PRECONDITION(hmac_ctx->libhandle != NULL);
175
176 hmac_ctx->SHA_variant = sha_var;
177
178 const EVP_MD *sha = NULL;
179 switch (hmac_ctx->SHA_variant)
180 {
181 case BSL_CRYPTO_SHA_256:
182 sha = EVP_sha256();
183 break;
184 case BSL_CRYPTO_SHA_384:
185 sha = EVP_sha384();
186 break;
187 case BSL_CRYPTO_SHA_512:
188 sha = EVP_sha512();
189 break;
190 default:
191 BSL_LOG_ERR("Invalid SHA variant");
192 return BSL_ERR_FAILURE;
193 }
194
195 pthread_mutex_lock(&StaticCryptoMutex);
196 const BSLB_CryptoKey_t *key_info = BSLB_CryptoKeyDict_cget(StaticKeyRegistry, keyid);
197 if (key_info == NULL)
198 {
199 // Special case which should not happen
200 BSL_LOG_ERR("Failed to lookup Key ID %" PRId64, keyid);
201 pthread_mutex_unlock(&StaticCryptoMutex);
202 return BSL_ERR_NOT_FOUND;
203 }
204
205 int res = EVP_DigestSignInit(hmac_ctx->libhandle, NULL, sha, NULL, key_info->pkey);
206 pthread_mutex_unlock(&StaticCryptoMutex);
207 CHK_PROPERTY(res == 1);
208
209 hmac_ctx->block_size = (size_t)EVP_MD_CTX_block_size(hmac_ctx->libhandle);
210
211 return 0;
212}
213
214int BSL_AuthCtx_DigestBuffer(BSL_AuthCtx_t *hmac_ctx, const void *data, size_t data_len)
215{
216 assert(data != NULL);
217 int res = EVP_DigestSignUpdate(hmac_ctx->libhandle, data, data_len);
218 CHK_PROPERTY(res == 1);
219
220 return 0;
221}
222
224{
225 uint8_t buf[hmac_ctx->block_size];
226
227 size_t block_size = hmac_ctx->block_size;
228 while (block_size == hmac_ctx->block_size)
229 {
230 BSL_SeqReader_Get(reader, buf, &block_size);
231 EVP_DigestSignUpdate(hmac_ctx->libhandle, buf, block_size);
232 }
233
234 return 0;
235}
236
237int BSL_AuthCtx_Finalize(BSL_AuthCtx_t *hmac_ctx, void **hmac, size_t *hmac_len)
238{
239 size_t req = 0;
240 int res = EVP_DigestSignFinal(hmac_ctx->libhandle, NULL, &req);
241 CHK_PROPERTY(res == 1);
242
243 *hmac_len = req;
244 res = EVP_DigestSignFinal(hmac_ctx->libhandle, *hmac, hmac_len);
245 CHK_PROPERTY(res == 1);
246
247 return 0;
248}
249
251{
252 EVP_MD_CTX_free(hmac_ctx->libhandle);
253 return 0;
254}
255
256int BSL_Cipher_Init(BSL_Cipher_t *cipher_ctx, BSL_CipherMode_e enc, BSL_CryptoCipherAESVariant_e aes_var,
257 const void *init_vec, int iv_len, BSL_Data_t content_enc_key)
258{
259 assert(cipher_ctx != NULL);
260 assert(init_vec != NULL);
261 assert(content_enc_key.ptr != NULL);
262 assert(content_enc_key.len > 0);
263
264 cipher_ctx->libhandle = EVP_CIPHER_CTX_new();
265 cipher_ctx->enc = enc;
266 cipher_ctx->AES_variant = aes_var;
267
268 const EVP_CIPHER *cipher = NULL;
269 switch (cipher_ctx->AES_variant)
270 {
271 case BSL_CRYPTO_AES_128:
272 cipher = EVP_aes_128_gcm();
273 break;
274 case BSL_CRYPTO_AES_256:
275 cipher = EVP_aes_256_gcm();
276 break;
277 default:
278 BSL_LOG_ERR("Invalid AES variant");
279 return BSL_ERR_FAILURE;
280 }
281
282 int res = EVP_CipherInit_ex(cipher_ctx->libhandle, cipher, NULL, NULL, NULL, (cipher_ctx->enc == BSL_CRYPTO_ENCRYPT));
283 CHK_PROPERTY(res == 1);
284
285 cipher_ctx->block_size = (size_t)EVP_CIPHER_CTX_block_size(cipher_ctx->libhandle);
286
287 res = EVP_CIPHER_CTX_ctrl(cipher_ctx->libhandle, EVP_CTRL_GCM_SET_IVLEN, iv_len, NULL);
288 CHK_PROPERTY(res == 1);
289
290 res = EVP_CipherInit_ex(cipher_ctx->libhandle, NULL, NULL, content_enc_key.ptr, init_vec, -1);
291 CHK_PROPERTY(res == 1);
292
293 return 0;
294}
295
296int BSL_Cipher_AddAAD(BSL_Cipher_t *cipher_ctx, const void *aad, int aad_len)
297{
298 // len needs to be passed or function call will crash program, no NULL checking on that param it seems
299 int len = 0;
300 int res = EVP_CipherUpdate(cipher_ctx->libhandle, NULL, &len, aad, aad_len);
301 CHK_PROPERTY(res == 1);
302 return 0;
303}
304
305int BSL_Cipher_AddData(BSL_Cipher_t *cipher_ctx, BSL_Data_t plaintext, BSL_Data_t ciphertext)
306{
307 assert(cipher_ctx != NULL);
308 int cipherlen = (int)ciphertext.len;
309 if (EVP_CipherUpdate(cipher_ctx->libhandle, ciphertext.ptr, &cipherlen, plaintext.ptr, (int)plaintext.len) != 1)
310 {
311 return -1;
312 }
313 return cipherlen;
314}
315
317{
318 uint8_t read_buf[cipher_ctx->block_size];
319 uint8_t write_buf[cipher_ctx->block_size];
320
321 size_t block_size = cipher_ctx->block_size;
322 while (block_size == (cipher_ctx->block_size))
323 {
324 BSL_SeqReader_Get(reader, read_buf, &block_size);
325 int block_size_int = (int)block_size;
326 int res = EVP_CipherUpdate(cipher_ctx->libhandle, write_buf, &block_size_int, read_buf, block_size_int);
327 CHK_PROPERTY(res == 1);
328 block_size = (size_t)block_size_int;
329 BSL_SeqWriter_Put(writer, write_buf, &block_size);
330 }
331
332 return 0;
333}
334
335int BSL_Cipher_GetTag(BSL_Cipher_t *cipher_ctx, void **tag)
336{
337 int res = EVP_CIPHER_CTX_ctrl(cipher_ctx->libhandle, EVP_CTRL_GCM_GET_TAG, BSL_CRYPTO_AESGCM_AUTH_TAG_LEN, *tag);
338 CHK_PROPERTY(res == 1);
339 return 0;
340}
341
342int BSL_Cipher_SetTag(BSL_Cipher_t *cipher_ctx, const void *tag)
343{
344 int res =
345 EVP_CIPHER_CTX_ctrl(cipher_ctx->libhandle, EVP_CTRL_GCM_SET_TAG, BSL_CRYPTO_AESGCM_AUTH_TAG_LEN, (void *)tag);
346 BSL_LOG_INFO("Completed EVP_CIPHER_CTX_ctrl *tag=%p", (uint8_t *)tag);
347 CHK_PROPERTY(res == 1);
348
349 return 0;
350}
351
352int BSL_Cipher_FinalizeData(BSL_Cipher_t *cipher_ctx, BSL_Data_t *extra)
353{
354 CHK_ARG_NONNULL(cipher_ctx);
355 CHK_ARG_EXPR(extra->ptr != NULL);
356 uint8_t buf[EVP_CIPHER_CTX_block_size(cipher_ctx->libhandle)];
357 CHK_PRECONDITION(extra->len >= sizeof(buf));
358
359 BSL_LOG_DEBUG("extra: ptr=0x%p len=%lu", extra->ptr, extra->len);
360
361 int len = 0;
362 int res = EVP_CipherFinal_ex(cipher_ctx->libhandle, buf, &len);
363 if (res != 1)
364 {
365 BSL_LOG_ERR("%s", ERR_error_string(ERR_get_error(), NULL));
366 }
367 CHK_PROPERTY(res == 1);
368 BSL_LOG_DEBUG("extra->len = %lu", extra->len);
369 memset(extra->ptr, 0, extra->len);
370 BSL_LOG_INFO("Completed EVP_CipherFinal_ex");
371 if (len > 0)
372 {
373 memcpy(extra->ptr, buf, sizeof(buf));
374 extra->len = len;
375 }
376 return 0;
377}
378
380{
381 // finalize can add 1 cipher block
382 uint8_t buf[EVP_CIPHER_CTX_block_size(cipher_ctx->libhandle)];
383
384 int len = 0;
385 int res = EVP_CipherFinal_ex(cipher_ctx->libhandle, buf, &len);
386 CHK_PROPERTY(res == 1);
387
388 if (len > 0)
389 {
390 BSL_SeqWriter_Put(writer, buf, (size_t *)&len);
391 }
392
393 return 0;
394}
395
397{
398 CHK_ARG_NONNULL(cipher_ctx);
399 EVP_CIPHER_CTX_free(cipher_ctx->libhandle);
400 memset(cipher_ctx, 0, sizeof(*cipher_ctx));
401 return BSL_SUCCESS;
402}
403
404int BSL_Crypto_GenKey(uint8_t *key_buffer, size_t key_length)
405{
406 CHK_ARG_NONNULL(key_buffer);
407 CHK_ARG_EXPR(key_length == 16 || key_length == 32);
408
409 int key_length_int = (int)key_length;
410 if (RAND_bytes(key_buffer, key_length_int) != 1)
411 {
412 OPENSSL_cleanse(key_buffer, key_length_int);
413 return -2;
414 }
415
416 return BSL_SUCCESS;
417}
418
419int BSL_Crypto_GenIV(void *buf, int size)
420{
421 CHK_ARG_NONNULL(buf);
422 if (!(size >= 8 && size <= 16))
423 {
424 return -1;
425 }
426
427 memset(buf, 0, size);
428 CHK_PROPERTY(RAND_bytes((unsigned char *)buf, size) == 1);
429 return 0;
430}
431
432int BSL_Crypto_AddRegistryKey(uint64_t keyid, const uint8_t *secret, size_t secret_len)
433{
434 CHK_ARG_NONNULL(secret);
435 CHK_ARG_EXPR(secret_len > 0);
436
438 EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HMAC, NULL);
439 int res = EVP_PKEY_keygen_init(ctx);
440 CHK_PROPERTY(res == 1);
441
442 key.pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, secret, (int)secret_len);
443 EVP_PKEY_CTX_free(ctx);
444
445 BSL_Data_Init(&key.raw);
446
447 int ecode = 0;
448 if ((ecode = BSL_Data_CopyFrom(&key.raw, secret_len, secret)) < 0)
449 {
450 BSL_LOG_ERR("Failed to copy key");
451 return ecode;
452 }
453
454 pthread_mutex_lock(&StaticCryptoMutex);
455 BSLB_CryptoKeyDict_set_at(StaticKeyRegistry, keyid, key);
456 pthread_mutex_unlock(&StaticCryptoMutex);
457
458 return 0;
459}
460
461int BSLB_Crypto_GetRegistryKey(uint64_t keyid, const uint8_t **secret, size_t *secret_len)
462{
463 CHK_ARG_NONNULL(secret);
464 // CHK_ARG_NONNULL(secret_len); // Note: secret_len CAN be NULL - this maybe should be fixed.
465
466 pthread_mutex_lock(&StaticCryptoMutex);
467 const BSLB_CryptoKey_t *found = BSLB_CryptoKeyDict_cget(StaticKeyRegistry, keyid);
468
469 if (!found)
470 {
471 return BSL_ERR_NOT_FOUND;
472 }
473
474 *secret = found->raw.ptr;
475
476 if (secret_len)
477 {
478 *secret_len = found->raw.len;
479 }
480
481 pthread_mutex_unlock(&StaticCryptoMutex);
482 return BSL_SUCCESS;
483}
Single entry-point include file for all of the BPSec Lib (BSL) frontend API.
#define BSL_LOG_DEBUG(...)
This is an overloaded member function, provided for convenience. It differs from the above function o...
#define BSL_LOG_INFO(...)
This is an overloaded member function, provided for convenience. It differs from the above function o...
#define BSL_LOG_ERR(...)
This is an overloaded member function, provided for convenience. It differs from the above function o...
@ BSL_ERR_NOT_FOUND
Requested value not found for key.
@ BSL_SUCCESS
Placeholder for non-error code.
@ BSL_ERR_FAILURE
Uncategorized failed (prefer to avoid)
int BSL_AuthCtx_Finalize(BSL_AuthCtx_t *hmac_ctx, void **hmac, size_t *hmac_len)
Finalize HMAC tag.
int BSL_AuthCtx_Init(BSL_AuthCtx_t *hmac_ctx, uint64_t keyid, BSL_CryptoCipherSHAVariant_e sha_var)
Initialize HMAC context resources and set private key and SHA variant.
int BSL_AuthCtx_DigestBuffer(BSL_AuthCtx_t *hmac_ctx, const void *data, size_t data_len)
Input data to HMAC sign to context.
int BSL_Crypto_GenIV(void *buf, int size)
Generate initialization vector (IV) for AES-GCM for BCBs.
int BSL_AuthCtx_DigestSeq(BSL_AuthCtx_t *hmac_ctx, BSL_SeqReader_t *reader)
Input data to HMAC sign to context.
int BSL_Crypto_AddRegistryKey(uint64_t keyid, const uint8_t *secret, size_t secret_len)
Add a new key to the crypto key registry.
int BSL_Cipher_AddData(BSL_Cipher_t *cipher_ctx, BSL_Data_t plaintext, BSL_Data_t ciphertext)
int BSL_AuthCtx_Deinit(BSL_AuthCtx_t *hmac_ctx)
Deinitialize HMAC context resources.
int BSLB_Crypto_GetRegistryKey(uint64_t keyid, const uint8_t **secret, size_t *secret_len)
Get pointers to an existing key, if present.
int BSL_Cipher_Init(BSL_Cipher_t *cipher_ctx, BSL_CipherMode_e enc, BSL_CryptoCipherAESVariant_e aes_var, const void *init_vec, int iv_len, BSL_Data_t content_enc_key)
Initialize crypto context resources and set as encoding or decoding.
struct BSLB_CryptoKey_s BSLB_CryptoKey_t
Struct to hold private key information.
static BSLB_CryptoKeyDict_t StaticKeyRegistry
Crypto key registry.
int BSL_Crypto_WrapKey(BSL_Data_t *wrapped_key, BSL_Data_t cek, size_t content_key_id, size_t aes_variant)
void BSL_CryptoInit(void)
Initialize the crypto subsystem.
int BSL_Cipher_Deinit(BSL_Cipher_t *cipher_ctx)
De-initialize crypto context resources.
int BSL_Cipher_FinalizeSeq(BSL_Cipher_t *cipher_ctx, BSL_SeqWriter_t *writer)
Finalize crypto operation.
int BSL_Cipher_AddAAD(BSL_Cipher_t *cipher_ctx, const void *aad, int aad_len)
Add additional authenticated data (AAD) to cipher context.
int BSL_Crypto_UnwrapKey(BSL_Data_t *unwrapped_key_output, BSL_Data_t wrapped_key_plaintext, size_t key_id, size_t aes_variant)
void BSL_CryptoDeinit(void)
Deinitialize the crypto subsystem.
int BSL_Cipher_SetTag(BSL_Cipher_t *cipher_ctx, const void *tag)
Set the tag of the crypto operation.
int BSL_Cipher_GetTag(BSL_Cipher_t *cipher_ctx, void **tag)
Get the tag of the crypto operation.
int BSL_Cipher_AddSeq(BSL_Cipher_t *cipher_ctx, BSL_SeqReader_t *reader, BSL_SeqWriter_t *writer)
Add data to encrypt or decrypt to the context sequentially.
Abstract interface for crypto processing.
BSL_CipherMode_e
Enum def to define cipher contexts as encryption or decryption operations.
@ BSL_CRYPTO_ENCRYPT
We use undefined for zero, in case this value is never explicitly set and is just zero by default.
int BSL_Data_Deinit(BSL_Data_t *data)
De-initialize a data struct, freeing if necessary.
int BSL_Data_CopyFrom(BSL_Data_t *data, size_t len, BSL_DataConstPtr_t src)
Set an initialized data struct to a given size.
int BSL_Data_Init(BSL_Data_t *data)
Initialize an empty data struct.
int BSL_SeqWriter_Put(BSL_SeqWriter_t *obj, const uint8_t *buf, size_t *bufsize)
Iterate a sequential writer.
int BSL_SeqReader_Get(BSL_SeqReader_t *obj, uint8_t *buf, size_t *bufsize)
Iterate a sequential reader.
Struct to hold private key information.
EVP_PKEY * pkey
Pointer to OpenSSL PKEY struct (used in hmac ctx)
BSL_Data_t raw
Pointer to raw key information (used in cipher ctx)
Struct def for HMAC operation context.
size_t block_size
Block size used by backend.
void * libhandle
pointer to library specific data
BSL_CryptoCipherSHAVariant_e SHA_variant
SHA variant of context.
Struct def for cipher operation context.
BSL_CipherMode_e enc
indicates if operation is encryption or decryption
BSL_CryptoCipherAESVariant_e AES_variant
AES variant of context.
size_t block_size
block size of cipher context
void * libhandle
pointer to library specific data
Heap data storage and views.
size_t len
Size of the data buffer.
BSL_DataPtr_t ptr
Pointer to the front of the buffer.
Definition of a simple flat buffer iterator.
Definition of a simple flat buffer iterator.