ae_error_t CertificateProvisioningProtocol::aesGCMDecrypt(const upse::Buffer& iv, const upse::Buffer& key, const upse::Buffer& cipherText, const upse::Buffer& aad, const upse::Buffer& mac, upse::Buffer& plainText) { ae_error_t status = AE_FAILURE; do { if (key.getSize() != sizeof(sgx_aes_gcm_128bit_key_t)) break; status = plainText.Alloc(cipherText.getSize()); if (AE_FAILED(status)) break; uint8_t* pPlainText = NULL; status = upse::BufferWriter(plainText).reserve(plainText.getSize(), &pPlainText); if (AE_FAILED(status)) break; sgx_status_t sgx_status; sgx_status = sgx_rijndael128GCM_decrypt(reinterpret_cast<const sgx_aes_gcm_128bit_key_t *>(key.getData()), cipherText.getData(), cipherText.getSize(), pPlainText, iv.getData(), IV_SIZE, aad.getData(), aad.getSize(), reinterpret_cast<const sgx_aes_gcm_128bit_tag_t *>(mac.getData())); if (SGX_SUCCESS != sgx_status) { AESM_LOG_ERROR("%s", g_event_string_table[SGX_EVENT_PSE_CERT_PROV_INTEGRITY_ERROR]); status = AE_FAILURE; break; } status = AE_SUCCESS; } while (0); return status; }
sgx_status_t put_secret_data( sgx_ra_context_t context, uint8_t *p_secret, uint32_t secret_size, uint8_t *p_gcm_mac) { sgx_status_t ret = SGX_SUCCESS; sgx_ec_key_128bit_t sk_key; do { if(secret_size != 8) { ret = SGX_ERROR_INVALID_PARAMETER; break; } ret = sgx_ra_get_keys(context, SGX_RA_KEY_SK, &sk_key); if(SGX_SUCCESS != ret) { break; } uint8_t aes_gcm_iv[12] = {0}; ret = sgx_rijndael128GCM_decrypt(&sk_key, p_secret, secret_size, &g_secret[0], &aes_gcm_iv[0], 12, NULL, 0, (const sgx_aes_gcm_128bit_tag_t *) (p_gcm_mac)); uint32_t i; bool secret_match = true; for(i=0;i<secret_size;i++) { if(g_secret[i] != i) { secret_match = false; } } if(!secret_match) { ret = SGX_ERROR_UNEXPECTED; } // Once the server has the shared secret, it should be sealed to // persistent storage for future use. This will prevents having to // perform remote attestation until the secret goes stale. Once the // enclave is created again, the secret can be unsealed. } while(0); return ret; }
uint32_t aes128gcm_decrypt(const sgx_aes_gcm_128bit_key_t *key, const uint8_t *bufin, const size_t bufinlen, uint8_t *bufout, size_t bufoutlen) { size_t ciphertextlen = bufinlen - (SGX_AESGCM_IV_SIZE + SGX_AESGCM_MAC_SIZE); // check buffer bounds if(bufoutlen < aes128gcm_plaintext_size(bufinlen)) { return 0Xffffffff; } // decrypt if(SGX_SUCCESS != sgx_rijndael128GCM_decrypt(key, bufin + SGX_AESGCM_IV_SIZE + SGX_AESGCM_MAC_SIZE, ciphertextlen, // ciphertext bufout, // plaintext bufin, SGX_AESGCM_IV_SIZE, // IV NULL, 0, // aad (const sgx_aes_gcm_128bit_tag_t*) (bufin + SGX_AESGCM_IV_SIZE) )) { return 0xffffffff; } return 0; }
//Function to decode ProvMsg4 and generate epid data blob uint32_t CPVEClass::proc_prov_msg4( bool use_ek2_in_input, const uint8_t ek2[SK_SIZE], const uint8_t* msg4, uint32_t msg4_size, uint8_t* data_blob, uint32_t blob_size) { ae_error_t ret = AE_SUCCESS; uint8_t local_ek2[SK_SIZE]; uint8_t *decoded_msg4 = NULL; const provision_response_header_t *msg4_header = reinterpret_cast<const provision_response_header_t *>(msg4); if(msg4_size < PROVISION_RESPONSE_HEADER_SIZE){ AESM_DBG_ERROR("invalid msg4 size"); return PVE_MSG_ERROR; } if(blob_size != HARD_CODED_EPID_BLOB_SIZE){ AESM_DBG_FATAL("invalid input epid blob size"); return PVE_PARAMETER_ERROR; } ret = check_prov_msg4_header(msg4_header, msg4_size); if( AE_SUCCESS != ret){ AESM_DBG_ERROR("Invalid ProvMsg4 Header:%d",ret); return ret; } ret = check_epid_pve_pg_status_before_mac_verification(msg4_header); if( AE_SUCCESS != ret){ AESM_DBG_ERROR("Backend return failure in ProvMsg4 Header:%d",ret); return ret; } do{ TLVsMsg tlvs_msg4; tlv_status_t tlv_status; tlv_status = tlvs_msg4.init_from_buffer(msg4+static_cast<uint32_t>(PROVISION_RESPONSE_HEADER_SIZE), msg4_size - static_cast<uint32_t>(PROVISION_RESPONSE_HEADER_SIZE)); ret = tlv_error_2_pve_error(tlv_status); if(AE_SUCCESS!=ret){ AESM_DBG_ERROR("fail to decode ProvMsg4:%d",tlv_status); break; } ret = msg4_integrity_checking(tlvs_msg4); if(AE_SUCCESS != ret){ AESM_DBG_ERROR("ProvMsg4 integrity checking error:%d",ret); break; } AESM_DBG_TRACE("ProvMsg4 decoded"); if(!use_ek2_in_input){ //we need generate ek2 prov_get_ek2_input_t ek2_input; if(memcpy_s(ek2_input.nonce, NONCE_SIZE, MSG4_TOP_FIELD_NONCE.payload, NONCE_SIZE)!=0){ AESM_DBG_ERROR("fail in memcpy"); ret = PVE_UNEXPECTED_ERROR; break; } if(memcpy_s(ek2_input.xid, XID_SIZE, msg4_header->xid, XID_SIZE)!=0){ AESM_DBG_ERROR("fail in memcpy"); ret = PVE_UNEXPECTED_ERROR; break; } //call PvE to get EK2 se_static_assert(SK_SIZE == sizeof(prov_get_ek2_output_t)); ret = (ae_error_t)get_ek2(&ek2_input, reinterpret_cast<prov_get_ek2_output_t *>(local_ek2)); if(AE_SUCCESS != ret){ AESM_DBG_ERROR("fail to get EK2:%d",ret); break; } }else{//reuse ek2 generated in processing ProvMsg2 if(0!=memcpy_s(local_ek2, sizeof(local_ek2), ek2, SK_SIZE)){ AESM_DBG_ERROR("fail in memcpy"); ret = PVE_UNEXPECTED_ERROR; break; } } se_static_assert(SK_SIZE==sizeof(sgx_aes_gcm_128bit_key_t)); tlv_msg_t field1 = block_cipher_tlv_get_encrypted_text(MSG4_TOP_FIELD_DATA); decoded_msg4 = reinterpret_cast<uint8_t *>(malloc(field1.msg_size)); if(NULL == decoded_msg4){ AESM_DBG_ERROR("malloc error"); ret = AE_OUT_OF_MEMORY_ERROR; break; } sgx_status_t sgx_status = sgx_rijndael128GCM_decrypt(reinterpret_cast<const sgx_aes_gcm_128bit_key_t *>(local_ek2), field1.msg_buf, field1.msg_size, decoded_msg4, reinterpret_cast<uint8_t *>(block_cipher_tlv_get_iv(MSG4_TOP_FIELD_DATA)), IV_SIZE, reinterpret_cast<const uint8_t *>(msg4_header), PROVISION_RESPONSE_HEADER_SIZE, reinterpret_cast<const sgx_aes_gcm_128bit_tag_t *>(MSG4_TOP_FIELD_MAC.payload)); if(SGX_ERROR_MAC_MISMATCH == sgx_status){ AESM_DBG_ERROR("fail to decrypt ProvMsg4 by EK2"); ret = PVE_INTEGRITY_CHECK_ERROR; break; } if( AE_SUCCESS != (ret = sgx_error_to_ae_error(sgx_status))){ AESM_DBG_ERROR("error in decrypting ProvMsg4:%d",sgx_status); break; } AESM_DBG_TRACE("ProvMsg4 decrypted by EK2 successfully"); ret = check_epid_pve_pg_status_after_mac_verification(msg4_header); if(AE_SUCCESS != ret){ AESM_DBG_ERROR("Backend reported error passed MAC verification:%d",ret); break; } TLVsMsg tlvs_field1; tlv_status = tlvs_field1.init_from_buffer(decoded_msg4, field1.msg_size); ret = tlv_error_2_pve_error(tlv_status); if(AE_SUCCESS != ret){ AESM_DBG_ERROR("ProvMsg4 Field2.1 decoding failed:%d",tlv_status); break; } ret = msg4_field1_msg_checking(tlvs_field1); if( AE_SUCCESS != ret){ AESM_DBG_ERROR("ProvMsg4 Field2.1 invalid:%d",ret); break; } proc_prov_msg4_input_t msg4_input; if(sizeof(proc_prov_msg4_output_t)!=SGX_TRUSTED_EPID_BLOB_SIZE){ AESM_DBG_FATAL("Trusted ProvMsg4 output buffer size error"); ret = PVE_UNEXPECTED_ERROR; break; } tlv_msg_t tcb_data = block_cipher_tlv_get_encrypted_text(MSG4_FIELD1_ENC_TCB); tlv_msg_t Axf_data = block_cipher_tlv_get_encrypted_text(MSG4_FIELD1_ENC_Axf); if(0!=memcpy_s(&msg4_input.group_cert, sizeof(msg4_input.group_cert), MSG4_FIELD1_GROUP_CERT.payload, MSG4_FIELD1_GROUP_CERT.size)|| 0!=memcpy_s(&msg4_input.equivalent_psvn, sizeof(psvn_t), device_id_tlv_get_psvn(MSG4_FIELD1_DEVICE_ID), sizeof(psvn_t))|| 0!=memcpy_s(&msg4_input.fmsp, sizeof(fmsp_t), device_id_tlv_get_fmsp(MSG4_FIELD1_DEVICE_ID), sizeof(fmsp_t))|| 0!=memcpy_s(&msg4_input.tcb_iv, IV_SIZE, block_cipher_tlv_get_iv(MSG4_FIELD1_ENC_TCB), IV_SIZE)|| 0!=memcpy_s(&msg4_input.encrypted_tcb, SK_SIZE, tcb_data.msg_buf, tcb_data.msg_size)|| 0!=memcpy_s(&msg4_input.tcb_mac, MAC_SIZE, MSG4_FIELD1_MAC_TCB.payload, MSG4_FIELD1_MAC_TCB.size)|| 0!=memcpy_s(&msg4_input.member_credential_iv, IV_SIZE, block_cipher_tlv_get_iv(MSG4_FIELD1_ENC_Axf), IV_SIZE)|| 0!=memcpy_s(&msg4_input.encrypted_member_credential, HARD_CODED_EPID_MEMBER_WITH_ESCROW_TLV_SIZE, Axf_data.msg_buf, Axf_data.msg_size)|| 0!=memcpy_s(&msg4_input.member_credential_mac, MAC_SIZE, MSG4_FIELD1_MAC_Axf.payload, MSG4_FIELD1_MAC_Axf.size)){ AESM_DBG_ERROR("memcpy error"); ret = PVE_UNEXPECTED_ERROR; break; } ret = (ae_error_t)proc_prov_msg4_data(&msg4_input, reinterpret_cast<proc_prov_msg4_output_t *>(data_blob)); AESM_DBG_TRACE("PvE return %d in Process ProvMsg4",ret); }while(0); if(decoded_msg4)free(decoded_msg4); return ret; }
//Function to decode ProvMsg4 and generate epid data blob uint32_t CPVEClass::proc_prov_msg4( const pve_data_t &data, const uint8_t *msg4, uint32_t msg4_size, uint8_t *data_blob, uint32_t blob_size) { ae_error_t ret = AE_SUCCESS; uint8_t local_ek2[SK_SIZE]; uint8_t *decoded_msg4 = NULL; uint8_t temp[XID_SIZE+NONCE_SIZE]; sgx_status_t sgx_status; const provision_response_header_t *msg4_header = reinterpret_cast<const provision_response_header_t *>(msg4); if(msg4_size < PROVISION_RESPONSE_HEADER_SIZE) { AESM_DBG_ERROR("invalid msg4 size"); return PVE_MSG_ERROR; } if (blob_size != SGX_TRUSTED_EPID_BLOB_SIZE_PAK) { AESM_DBG_FATAL("invalid input epid blob size"); return PVE_PARAMETER_ERROR; } ret = check_prov_msg4_header(msg4_header, msg4_size); if( AE_SUCCESS != ret) { AESM_DBG_ERROR("Invalid ProvMsg4 Header:(ae%d)",ret); return ret; } if(0!=memcmp(msg4_header->xid, data.xid, XID_SIZE)) { AESM_DBG_ERROR("Invalid XID in msg4 header"); return PVE_MSG_ERROR; } ret = check_epid_pve_pg_status_before_mac_verification(msg4_header); if( AE_SUCCESS != ret) { AESM_DBG_ERROR("Backend return failure in ProvMsg4 Header:(ae%d)",ret); return ret; } do { TLVsMsg tlvs_msg4; uint8_t aad[PROVISION_RESPONSE_HEADER_SIZE+NONCE_SIZE]; tlv_status_t tlv_status; tlv_status = tlvs_msg4.init_from_buffer(msg4+static_cast<uint32_t>(PROVISION_RESPONSE_HEADER_SIZE), msg4_size - static_cast<uint32_t>(PROVISION_RESPONSE_HEADER_SIZE)); ret = tlv_error_2_pve_error(tlv_status); if(AE_SUCCESS!=ret) { AESM_DBG_ERROR("fail to decode ProvMsg4:(ae%d)",ret); break; } ret = msg4_integrity_checking(tlvs_msg4); if(AE_SUCCESS != ret) { AESM_DBG_ERROR("ProvMsg4 integrity checking error:(ae%d)",ret); break; } AESM_DBG_TRACE("ProvMsg4 decoded"); se_static_assert(sizeof(sgx_cmac_128bit_key_t)==SK_SIZE); if(0!=memcpy_s(temp,sizeof(temp), data.xid, XID_SIZE)|| 0!=memcpy_s(temp+XID_SIZE, sizeof(temp)-XID_SIZE, MSG4_TOP_FIELD_NONCE.payload, NONCE_SIZE)) { AESM_DBG_ERROR("Fail in memcpy"); ret = AE_FAILURE; break; } if((sgx_status=sgx_rijndael128_cmac_msg(reinterpret_cast<const sgx_cmac_128bit_key_t *>(data.sk), temp, XID_SIZE+NONCE_SIZE, reinterpret_cast<sgx_cmac_128bit_tag_t *>(local_ek2)))!=SGX_SUCCESS) { AESM_DBG_ERROR("Fail to generate ek2:(sgx0x%x)",sgx_status); ret = AE_FAILURE; break; } se_static_assert(SK_SIZE==sizeof(sgx_aes_gcm_128bit_key_t)); tlv_msg_t field1 = block_cipher_tlv_get_encrypted_text(MSG4_TOP_FIELD_DATA); decoded_msg4 = reinterpret_cast<uint8_t *>(malloc(field1.msg_size)); if(NULL == decoded_msg4) { AESM_DBG_ERROR("malloc error"); ret = AE_OUT_OF_MEMORY_ERROR; break; } if (memcpy_s(aad, sizeof(aad), msg4_header, PROVISION_RESPONSE_HEADER_SIZE) != 0 || memcpy_s(aad + PROVISION_RESPONSE_HEADER_SIZE, sizeof(aad)-PROVISION_RESPONSE_HEADER_SIZE, MSG4_TOP_FIELD_NONCE.payload, MSG4_TOP_FIELD_NONCE.size) != 0) { AESM_DBG_ERROR("memcpy failure"); ret = AE_FAILURE; break; } sgx_status_t sgx_status = sgx_rijndael128GCM_decrypt(reinterpret_cast<const sgx_aes_gcm_128bit_key_t *>(local_ek2), field1.msg_buf, field1.msg_size, decoded_msg4, reinterpret_cast<uint8_t *>(block_cipher_tlv_get_iv(MSG4_TOP_FIELD_DATA)), IV_SIZE, aad, sizeof(aad), reinterpret_cast<const sgx_aes_gcm_128bit_tag_t *>(MSG4_TOP_FIELD_MAC.payload)); if(SGX_ERROR_MAC_MISMATCH == sgx_status) { AESM_DBG_ERROR("fail to decrypt ProvMsg4 by EK2 (sgx0x%x)",sgx_status); ret = PVE_INTEGRITY_CHECK_ERROR; break; } if( AE_SUCCESS != (ret = sgx_error_to_ae_error(sgx_status))) { AESM_DBG_ERROR("error in decrypting ProvMsg4:(sgx0x%x)",sgx_status); break; } AESM_DBG_TRACE("ProvMsg4 decrypted by EK2 successfully"); ret = check_epid_pve_pg_status_after_mac_verification(msg4_header); if(AE_SUCCESS != ret) { AESM_DBG_ERROR("Backend reported error passed MAC verification:(ae%d)",ret); break; } TLVsMsg tlvs_field1; tlv_status = tlvs_field1.init_from_buffer(decoded_msg4, field1.msg_size); ret = tlv_error_2_pve_error(tlv_status); if(AE_SUCCESS != ret) { AESM_DBG_ERROR("ProvMsg4 Field2.1 decoding failed:(ae%d)",ret); break; } ret = msg4_field1_msg_checking(tlvs_field1); if( AE_SUCCESS != ret) { AESM_DBG_ERROR("ProvMsg4 Field2.1 invalid:(ae%d)",ret); break; } proc_prov_msg4_input_t msg4_input; tlv_msg_t Axf_data = block_cipher_tlv_get_encrypted_text(MSG4_FIELD1_ENC_Axf); if(0!=memcpy_s(&msg4_input.group_cert, sizeof(msg4_input.group_cert), MSG4_FIELD1_GROUP_CERT.payload, MSG4_FIELD1_GROUP_CERT.size)|| 0!=memcpy_s(&msg4_input.n2, NONCE_2_SIZE, MSG4_FIELD1_Nonce2.payload, MSG4_FIELD1_Nonce2.size) || 0!=memcpy_s(&msg4_input.equivalent_psvn, sizeof(psvn_t), platform_info_tlv_get_psvn(MSG4_FIELD1_PLATFORM_INFO), sizeof(psvn_t))|| 0!=memcpy_s(&msg4_input.fmsp, sizeof(fmsp_t), platform_info_tlv_get_fmsp(MSG4_FIELD1_PLATFORM_INFO), sizeof(fmsp_t))|| 0!=memcpy_s(&msg4_input.member_credential_iv, IV_SIZE, block_cipher_tlv_get_iv(MSG4_FIELD1_ENC_Axf), IV_SIZE)|| 0!=memcpy_s(&msg4_input.encrypted_member_credential, HARD_CODED_EPID_MEMBER_WITH_ESCROW_TLV_SIZE, Axf_data.msg_buf, Axf_data.msg_size)|| 0!=memcpy_s(&msg4_input.member_credential_mac, MAC_SIZE, MSG4_FIELD1_MAC_Axf.payload, MSG4_FIELD1_MAC_Axf.size)) { AESM_DBG_ERROR("memcpy error"); ret = PVE_UNEXPECTED_ERROR; break; } if (AE_SUCCESS != (ret =XEGDBlob::instance().read(msg4_input.xegb))) { AESM_DBG_ERROR("Fail to read extend epid blob info (ae%d)",ret); return ret; } ret = CPVEClass::instance().load_enclave();//Load PvE enclave now if( ret != AE_SUCCESS) { AESM_DBG_ERROR("Fail to load PvE enclave:(ae%d)\n",ret); break; } ret = (ae_error_t)proc_prov_msg4_data(&msg4_input, reinterpret_cast<proc_prov_msg4_output_t *>(data_blob)); AESM_DBG_TRACE("PvE return (ae%d) in Process ProvMsg4",ret); } while(0); if(decoded_msg4)free(decoded_msg4); return ret; }
file_mht_node_t* protected_fs_file::read_mht_node(uint64_t mht_node_number) { int32_t result32; sgx_status_t status; if (mht_node_number == 0) return &root_mht; uint64_t physical_node_number = 1 + // meta data node mht_node_number * (1 + ATTACHED_DATA_NODES_COUNT); // the '1' is for the mht node preceding every 96 data nodes file_mht_node_t* file_mht_node = (file_mht_node_t*)cache.find(physical_node_number); if (file_mht_node != NULL) return file_mht_node; file_mht_node_t* parent_file_mht_node = read_mht_node((mht_node_number - 1) / CHILD_MHT_NODES_COUNT); if (parent_file_mht_node == NULL) // some error happened return NULL; try { file_mht_node = new file_mht_node_t; } catch (std::bad_alloc& e) { (void)e; // remove warning last_error = ENOMEM; return NULL; } memset(file_mht_node, 0, sizeof(file_mht_node_t)); file_mht_node->type = FILE_MHT_NODE_TYPE; file_mht_node->mht_node_number = mht_node_number; file_mht_node->physical_node_number = physical_node_number; file_mht_node->parent = parent_file_mht_node; status = u_sgxprotectedfs_fread_node(&result32, file, file_mht_node->physical_node_number, file_mht_node->encrypted.cipher, NODE_SIZE); if (status != SGX_SUCCESS || result32 != 0) { delete file_mht_node; last_error = (status != SGX_SUCCESS) ? status : (result32 != -1) ? result32 : EIO; return NULL; } gcm_crypto_data_t* gcm_crypto_data = &file_mht_node->parent->plain.mht_nodes_crypto[(file_mht_node->mht_node_number - 1) % CHILD_MHT_NODES_COUNT]; // this function decrypt the data _and_ checks the integrity of the data against the gmac status = sgx_rijndael128GCM_decrypt(&gcm_crypto_data->key, file_mht_node->encrypted.cipher, NODE_SIZE, (uint8_t*)&file_mht_node->plain, empty_iv, SGX_AESGCM_IV_SIZE, NULL, 0, &gcm_crypto_data->gmac); if (status != SGX_SUCCESS) { delete file_mht_node; last_error = status; if (status == SGX_ERROR_MAC_MISMATCH) { file_status = SGX_FILE_STATUS_CORRUPTED; } return NULL; } if (cache.add(file_mht_node->physical_node_number, file_mht_node) == false) { memset_s(&file_mht_node->plain, sizeof(mht_node_t), 0, sizeof(mht_node_t)); delete file_mht_node; last_error = ENOMEM; return NULL; } return file_mht_node; }
file_data_node_t* protected_fs_file::read_data_node() { uint64_t data_node_number; uint64_t physical_node_number; file_mht_node_t* file_mht_node; int32_t result32; sgx_status_t status; get_node_numbers(offset, NULL, &data_node_number, NULL, &physical_node_number); file_data_node_t* file_data_node = (file_data_node_t*)cache.get(physical_node_number); if (file_data_node != NULL) return file_data_node; // need to read the data node from the disk file_mht_node = get_mht_node(); if (file_mht_node == NULL) // some error happened return NULL; try { file_data_node = new file_data_node_t; } catch (std::bad_alloc& e) { (void)e; // remove warning last_error = ENOMEM; return NULL; } memset(file_data_node, 0, sizeof(file_data_node_t)); file_data_node->type = FILE_DATA_NODE_TYPE; file_data_node->data_node_number = data_node_number; file_data_node->physical_node_number = physical_node_number; file_data_node->parent = file_mht_node; status = u_sgxprotectedfs_fread_node(&result32, file, file_data_node->physical_node_number, file_data_node->encrypted.cipher, NODE_SIZE); if (status != SGX_SUCCESS || result32 != 0) { delete file_data_node; last_error = (status != SGX_SUCCESS) ? status : (result32 != -1) ? result32 : EIO; return NULL; } gcm_crypto_data_t* gcm_crypto_data = &file_data_node->parent->plain.data_nodes_crypto[file_data_node->data_node_number % ATTACHED_DATA_NODES_COUNT]; // this function decrypt the data _and_ checks the integrity of the data against the gmac status = sgx_rijndael128GCM_decrypt(&gcm_crypto_data->key, file_data_node->encrypted.cipher, NODE_SIZE, file_data_node->plain.data, empty_iv, SGX_AESGCM_IV_SIZE, NULL, 0, &gcm_crypto_data->gmac); if (status != SGX_SUCCESS) { delete file_data_node; last_error = status; if (status == SGX_ERROR_MAC_MISMATCH) { file_status = SGX_FILE_STATUS_CORRUPTED; } return NULL; } if (cache.add(file_data_node->physical_node_number, file_data_node) == false) { memset_s(&file_data_node->plain, sizeof(data_node_t), 0, sizeof(data_node_t)); // scrub the plaintext data delete file_data_node; last_error = ENOMEM; return NULL; } return file_data_node; }