/* * @brief Encrypt an ldb value using an aead algorithm. * * This function uses the samba internal implementation to perform the encryption. However * the encrypted data and tag are stored in a manner compatible with gnutls, * so the gnutls aead functions can be used to decrypt and verify the data. * * @param err Pointer to an error code, set to: * LDB_SUCESS If the value was successfully encrypted * LDB_ERR_OPERATIONS_ERROR If there was an error. * * @param ctx Talloc memory context the will own the memory allocated * @param ldb ldb context, to allow logging. * @param val The ldb value to encrypt, not altered or freed * @param data The context data for this module. * * @return The encrypted ldb_val, or data_blob_null if there was an error. */ static struct ldb_val samba_encrypt_aead(int *err, TALLOC_CTX *ctx, struct ldb_context *ldb, const struct ldb_val val, const struct es_data *data) { struct aes_gcm_128_context cctx; struct EncryptedSecret *es = NULL; DATA_BLOB pt = data_blob_null; struct ldb_val enc = data_blob_null; DATA_BLOB key_blob = data_blob_null; int rc; TALLOC_CTX *frame = talloc_stackframe(); es = makeEncryptedSecret(ldb, frame); if (es == NULL) { goto error_exit; } pt = makePlainText(frame, ldb, val); if (pt.length == 0) { goto error_exit; } /* * Set the encryption key */ key_blob = get_key(data); if (key_blob.length != AES_BLOCK_SIZE) { ldb_asprintf_errstring(ldb, "Invalid EncryptedSecrets key size, " "expected %u bytes and is %zu bytes\n", AES_BLOCK_SIZE, key_blob.length); goto error_exit; } /* * Set the initialisation vector */ { uint8_t *iv = talloc_zero_size(frame, AES_GCM_128_IV_SIZE); if (iv == NULL) { ldb_set_errstring(ldb, "Out of memory allocating iv\n"); goto error_exit; } generate_random_buffer(iv, AES_GCM_128_IV_SIZE); es->iv.length = AES_GCM_128_IV_SIZE; es->iv.data = iv; } /* * Encrypt the value, and append the GCM digest to the encrypted * data so that it can be decrypted and validated by the * gnutls aead decryption routines. */ { uint8_t *ct = talloc_zero_size(frame, pt.length + AES_BLOCK_SIZE); if (ct == NULL) { ldb_oom(ldb); goto error_exit; } memcpy(ct, pt.data, pt.length); es->encrypted.length = pt.length + AES_BLOCK_SIZE; es->encrypted.data = ct; } aes_gcm_128_init(&cctx, key_blob.data, es->iv.data); aes_gcm_128_updateA(&cctx, (uint8_t *)&es->header, sizeof(struct EncryptedSecretHeader)); aes_gcm_128_crypt(&cctx, es->encrypted.data, pt.length); aes_gcm_128_updateC(&cctx, es->encrypted.data, pt.length); aes_gcm_128_digest(&cctx, &es->encrypted.data[pt.length]); rc = ndr_push_struct_blob(&enc, ctx, es, (ndr_push_flags_fn_t) ndr_push_EncryptedSecret); if (!NDR_ERR_CODE_IS_SUCCESS(rc)) { ldb_set_errstring(ldb, "Unable to ndr push EncryptedSecret\n"); goto error_exit; } TALLOC_FREE(frame); return enc; error_exit: *err = LDB_ERR_OPERATIONS_ERROR; TALLOC_FREE(frame); return data_blob_null; }
/* * @brief Decrypt data encrypted using an aead algorithm. * * Decrypt the data in ed and insert it into ev. The data was encrypted * with the samba aes gcm implementation. * * @param err Pointer to an error code, set to: * LDB_SUCESS If the value was successfully decrypted * LDB_ERR_OPERATIONS_ERROR If there was an error. * * @param ctx Talloc memory context that will own the memory allocated * @param ldb ldb context, to allow logging. * @param ev The value to be updated with the decrypted data. * @param ed The data to decrypt. * @param data The context data for this module. * * @return ev is updated with the unencrypted data. */ static void samba_decrypt_aead(int *err, TALLOC_CTX *ctx, struct ldb_context *ldb, struct EncryptedSecret *es, struct PlaintextSecret *ps, const struct es_data *data) { struct aes_gcm_128_context cctx; DATA_BLOB pt = data_blob_null; DATA_BLOB key_blob = data_blob_null; uint8_t sig[AES_BLOCK_SIZE] = {0, }; int rc; int cmp; TALLOC_CTX *frame = talloc_stackframe(); /* * Set the encryption key */ key_blob = get_key(data); if (key_blob.length != AES_BLOCK_SIZE) { ldb_asprintf_errstring(ldb, "Invalid EncryptedSecrets key size, " "expected %u bytes and is %zu bytes\n", AES_BLOCK_SIZE, key_blob.length); goto error_exit; } if (es->iv.length < AES_GCM_128_IV_SIZE) { ldb_asprintf_errstring(ldb, "Invalid EncryptedSecrets iv size, " "expected %u bytes and is %zu bytes\n", AES_GCM_128_IV_SIZE, es->iv.length); goto error_exit; } if (es->encrypted.length < AES_BLOCK_SIZE) { ldb_asprintf_errstring(ldb, "Invalid EncryptedData size, " "expected %u bytes and is %zu bytes\n", AES_BLOCK_SIZE, es->encrypted.length); goto error_exit; } pt.length = es->encrypted.length - AES_BLOCK_SIZE; pt.data = talloc_zero_size(ctx, pt.length); if (pt.data == NULL) { ldb_set_errstring(ldb, "Out of memory allocating space for " "plain text\n"); goto error_exit; } memcpy(pt.data, es->encrypted.data, pt.length); aes_gcm_128_init(&cctx, key_blob.data, es->iv.data); aes_gcm_128_updateA(&cctx, (uint8_t *)&es->header, sizeof(struct EncryptedSecretHeader)); aes_gcm_128_updateC(&cctx, pt.data, pt.length); aes_gcm_128_crypt(&cctx, pt.data, pt.length); aes_gcm_128_digest(&cctx, sig); /* * Check the authentication tag */ cmp = memcmp(&es->encrypted.data[pt.length], sig, AES_BLOCK_SIZE); if (cmp != 0) { ldb_set_errstring(ldb, "Tag does not match, " "data corrupted or altered\n"); goto error_exit; } rc = ndr_pull_struct_blob(&pt, ctx, ps, (ndr_pull_flags_fn_t) ndr_pull_PlaintextSecret); if(!NDR_ERR_CODE_IS_SUCCESS(rc)) { ldb_asprintf_errstring(ldb, "Error(%d) unpacking decrypted data, " "data possibly corrupted or altered\n", rc); goto error_exit; } TALLOC_FREE(frame); return; error_exit: *err = LDB_ERR_OPERATIONS_ERROR; TALLOC_FREE(frame); return; }
/* This uses the test values from ... */ bool torture_local_crypto_aes_gcm_128(struct torture_context *torture) { bool ret = true; uint32_t i; struct { DATA_BLOB K; DATA_BLOB IV; DATA_BLOB A; DATA_BLOB P; DATA_BLOB C; DATA_BLOB T; } testarray[5]; TALLOC_CTX *tctx = talloc_new(torture); if (!tctx) { return false; }; ZERO_STRUCT(testarray); testarray[0].K = strhex_to_data_blob(tctx, "00000000000000000000000000000000"); testarray[0].IV = strhex_to_data_blob(tctx, "000000000000000000000000"); testarray[0].A = data_blob_null; testarray[0].P = data_blob_null; testarray[0].C = data_blob_null; testarray[0].T = strhex_to_data_blob(tctx, "58e2fccefa7e3061367f1d57a4e7455a"); testarray[1].K = strhex_to_data_blob(tctx, "00000000000000000000000000000000"); testarray[1].IV = strhex_to_data_blob(tctx, "000000000000000000000000"); testarray[1].A = data_blob_null; testarray[1].P = strhex_to_data_blob(tctx, "00000000000000000000000000000000"); testarray[1].C = strhex_to_data_blob(tctx, "0388dace60b6a392f328c2b971b2fe78"); testarray[1].T = strhex_to_data_blob(tctx, "ab6e47d42cec13bdf53a67b21257bddf"); testarray[2].K = strhex_to_data_blob(tctx, "feffe9928665731c6d6a8f9467308308"); testarray[2].IV = strhex_to_data_blob(tctx, "cafebabefacedbaddecaf888"); testarray[2].A = data_blob_null; testarray[2].P = strhex_to_data_blob(tctx, "d9313225f88406e5a55909c5aff5269a" "86a7a9531534f7da2e4c303d8a318a72" "1c3c0c95956809532fcf0e2449a6b525" "b16aedf5aa0de657ba637b391aafd255"); testarray[2].C = strhex_to_data_blob(tctx, "42831ec2217774244b7221b784d0d49c" "e3aa212f2c02a4e035c17e2329aca12e" "21d514b25466931c7d8f6a5aac84aa05" "1ba30b396a0aac973d58e091473f5985"); testarray[2].T = strhex_to_data_blob(tctx, "4d5c2af327cd64a62cf35abd2ba6fab4"); testarray[3].K = strhex_to_data_blob(tctx, "feffe9928665731c6d6a8f9467308308"); testarray[3].IV = strhex_to_data_blob(tctx, "cafebabefacedbaddecaf888"); testarray[3].A = strhex_to_data_blob(tctx, "feedfacedeadbeeffeedfacedeadbeef" "abaddad2"); testarray[3].P = strhex_to_data_blob(tctx, "d9313225f88406e5a55909c5aff5269a" "86a7a9531534f7da2e4c303d8a318a72" "1c3c0c95956809532fcf0e2449a6b525" "b16aedf5aa0de657ba637b39"); testarray[3].C = strhex_to_data_blob(tctx, "42831ec2217774244b7221b784d0d49c" "e3aa212f2c02a4e035c17e2329aca12e" "21d514b25466931c7d8f6a5aac84aa05" "1ba30b396a0aac973d58e091"); testarray[3].T = strhex_to_data_blob(tctx, "5bc94fbc3221a5db94fae95ae7121a47"); for (i=1; testarray[i].T.length != 0; i++) { struct aes_gcm_128_context ctx; uint8_t T[AES_BLOCK_SIZE]; DATA_BLOB C; int e; C = data_blob_dup_talloc(tctx, testarray[i].P); aes_gcm_128_init(&ctx, testarray[i].K.data, testarray[i].IV.data); aes_gcm_128_updateA(&ctx, testarray[i].A.data, testarray[i].A.length); aes_gcm_128_crypt(&ctx, C.data, C.length); aes_gcm_128_updateC(&ctx, C.data, C.length); aes_gcm_128_digest(&ctx, T); e = memcmp(testarray[i].T.data, T, sizeof(T)); if (e != 0) { printf("%s: aes_gcm_128 test[%u]: failed\n", __location__, i); printf("K\n"); dump_data(0, testarray[i].K.data, testarray[i].K.length); printf("IV\n"); dump_data(0, testarray[i].IV.data, testarray[i].IV.length); printf("A\n"); dump_data(0, testarray[i].A.data, testarray[i].A.length); printf("P\n"); dump_data(0, testarray[i].P.data, testarray[i].P.length); printf("C1\n"); dump_data(0, testarray[i].C.data, testarray[i].C.length); printf("C2\n"); dump_data(0, C.data, C.length); printf("T1\n"); dump_data(0, testarray[i].T.data, testarray[i].T.length); printf("T2\n"); dump_data(0, T, sizeof(T)); ret = false; goto fail; } e = memcmp(testarray[i].C.data, C.data, C.length); if (e != 0) { printf("%s: aes_gcm_128 test[%u]: failed\n", __location__, i); printf("K\n"); dump_data(0, testarray[i].K.data, testarray[i].K.length); printf("IV\n"); dump_data(0, testarray[i].IV.data, testarray[i].IV.length); printf("A\n"); dump_data(0, testarray[i].A.data, testarray[i].A.length); printf("P\n"); dump_data(0, testarray[i].P.data, testarray[i].P.length); printf("C1\n"); dump_data(0, testarray[i].C.data, testarray[i].C.length); printf("C2\n"); dump_data(0, C.data, C.length); printf("T1\n"); dump_data(0, testarray[i].T.data, testarray[i].T.length); printf("T2\n"); dump_data(0, T, sizeof(T)); ret = false; goto fail; } } for (i=1; testarray[i].T.length != 0; i++) { struct aes_gcm_128_context ctx; uint8_t T[AES_BLOCK_SIZE]; DATA_BLOB C; int e; size_t j; C = data_blob_dup_talloc(tctx, testarray[i].P); aes_gcm_128_init(&ctx, testarray[i].K.data, testarray[i].IV.data); for (j=0; j < testarray[i].A.length; j++) { aes_gcm_128_updateA(&ctx, &testarray[i].A.data[j], 1); } for (j=0; j < C.length; j++) { aes_gcm_128_crypt(&ctx, &C.data[j], 1); aes_gcm_128_updateC(&ctx, &C.data[j], 1); } aes_gcm_128_digest(&ctx, T); e = memcmp(testarray[i].T.data, T, sizeof(T)); if (e != 0) { printf("%s: aes_gcm_128 test[%u]: failed\n", __location__, i); printf("K\n"); dump_data(0, testarray[i].K.data, testarray[i].K.length); printf("IV\n"); dump_data(0, testarray[i].IV.data, testarray[i].IV.length); printf("A\n"); dump_data(0, testarray[i].A.data, testarray[i].A.length); printf("P\n"); dump_data(0, testarray[i].P.data, testarray[i].P.length); printf("C1\n"); dump_data(0, testarray[i].C.data, testarray[i].C.length); printf("C2\n"); dump_data(0, C.data, C.length); printf("T1\n"); dump_data(0, testarray[i].T.data, testarray[i].T.length); printf("T2\n"); dump_data(0, T, sizeof(T)); ret = false; goto fail; } e = memcmp(testarray[i].C.data, C.data, C.length); if (e != 0) { printf("%s: aes_gcm_128 test[%u]: failed\n", __location__, i); printf("K\n"); dump_data(0, testarray[i].K.data, testarray[i].K.length); printf("IV\n"); dump_data(0, testarray[i].IV.data, testarray[i].IV.length); printf("A\n"); dump_data(0, testarray[i].A.data, testarray[i].A.length); printf("P\n"); dump_data(0, testarray[i].P.data, testarray[i].P.length); printf("C1\n"); dump_data(0, testarray[i].C.data, testarray[i].C.length); printf("C2\n"); dump_data(0, C.data, C.length); printf("T1\n"); dump_data(0, testarray[i].T.data, testarray[i].T.length); printf("T2\n"); dump_data(0, T, sizeof(T)); ret = false; goto fail; } } for (i=1; testarray[i].T.length != 0; i++) { struct aes_gcm_128_context ctx; uint8_t T[AES_BLOCK_SIZE]; DATA_BLOB P; int e; size_t j; P = data_blob_dup_talloc(tctx, testarray[i].C); aes_gcm_128_init(&ctx, testarray[i].K.data, testarray[i].IV.data); for (j=0; j < testarray[i].A.length; j++) { aes_gcm_128_updateA(&ctx, &testarray[i].A.data[j], 1); } for (j=0; j < P.length; j++) { aes_gcm_128_updateC(&ctx, &P.data[j], 1); aes_gcm_128_crypt(&ctx, &P.data[j], 1); } aes_gcm_128_digest(&ctx, T); e = memcmp(testarray[i].T.data, T, sizeof(T)); if (e != 0) { printf("%s: aes_gcm_128 test[%u]: failed\n", __location__, i); printf("K\n"); dump_data(0, testarray[i].K.data, testarray[i].K.length); printf("IV\n"); dump_data(0, testarray[i].IV.data, testarray[i].IV.length); printf("A\n"); dump_data(0, testarray[i].A.data, testarray[i].A.length); printf("P1\n"); dump_data(0, testarray[i].P.data, testarray[i].P.length); printf("P2\n"); dump_data(0, P.data, P.length); printf("C\n"); dump_data(0, testarray[i].C.data, testarray[i].C.length); printf("T1\n"); dump_data(0, testarray[i].T.data, testarray[i].T.length); printf("T2\n"); dump_data(0, T, sizeof(T)); ret = false; goto fail; } e = memcmp(testarray[i].P.data, P.data, P.length); if (e != 0) { printf("%s: aes_gcm_128 test[%u]: failed\n", __location__, i); printf("K\n"); dump_data(0, testarray[i].K.data, testarray[i].K.length); printf("IV\n"); dump_data(0, testarray[i].IV.data, testarray[i].IV.length); printf("A\n"); dump_data(0, testarray[i].A.data, testarray[i].A.length); printf("P1\n"); dump_data(0, testarray[i].P.data, testarray[i].P.length); printf("P2\n"); dump_data(0, P.data, P.length); printf("C\n"); dump_data(0, testarray[i].C.data, testarray[i].C.length); printf("T1\n"); dump_data(0, testarray[i].T.data, testarray[i].T.length); printf("T2\n"); dump_data(0, T, sizeof(T)); ret = false; goto fail; } } for (i=1; testarray[i].T.length != 0; i++) { struct aes_gcm_128_context ctx; uint8_t T[AES_BLOCK_SIZE]; DATA_BLOB P; int e; P = data_blob_dup_talloc(tctx, testarray[i].C); aes_gcm_128_init(&ctx, testarray[i].K.data, testarray[i].IV.data); aes_gcm_128_updateA(&ctx, testarray[i].A.data, testarray[i].A.length); aes_gcm_128_updateC(&ctx, P.data, P.length); aes_gcm_128_crypt(&ctx, P.data, P.length); aes_gcm_128_digest(&ctx, T); e = memcmp(testarray[i].T.data, T, sizeof(T)); if (e != 0) { printf("%s: aes_gcm_128 test[%u]: failed\n", __location__, i); printf("K\n"); dump_data(0, testarray[i].K.data, testarray[i].K.length); printf("IV\n"); dump_data(0, testarray[i].IV.data, testarray[i].IV.length); printf("A\n"); dump_data(0, testarray[i].A.data, testarray[i].A.length); printf("P1\n"); dump_data(0, testarray[i].P.data, testarray[i].P.length); printf("P2\n"); dump_data(0, P.data, P.length); printf("C\n"); dump_data(0, testarray[i].C.data, testarray[i].C.length); printf("T1\n"); dump_data(0, testarray[i].T.data, testarray[i].T.length); printf("T2\n"); dump_data(0, T, sizeof(T)); ret = false; goto fail; } e = memcmp(testarray[i].P.data, P.data, P.length); if (e != 0) { printf("%s: aes_gcm_128 test[%u]: failed\n", __location__, i); printf("K\n"); dump_data(0, testarray[i].K.data, testarray[i].K.length); printf("IV\n"); dump_data(0, testarray[i].IV.data, testarray[i].IV.length); printf("A\n"); dump_data(0, testarray[i].A.data, testarray[i].A.length); printf("P1\n"); dump_data(0, testarray[i].P.data, testarray[i].P.length); printf("P2\n"); dump_data(0, P.data, P.length); printf("C\n"); dump_data(0, testarray[i].C.data, testarray[i].C.length); printf("T1\n"); dump_data(0, testarray[i].T.data, testarray[i].T.length); printf("T2\n"); dump_data(0, T, sizeof(T)); ret = false; goto fail; } } fail: talloc_free(tctx); return ret; }