//initialize the epid signature header according to sigrl_header static pve_status_t gen_epid_signature_header(const SigRl *sigrl_header, EPIDMember *epid_member, const uint8_t *nonce_challenge, EpidSignature *epid_header, BigNumStr *rnd_bsn) { if(NULL!=sigrl_header){ memcpy(&epid_header->n2, &sigrl_header->n2, sizeof(sigrl_header->n2));//copy size into header in BigEndian memcpy(&epid_header->rl_ver, &sigrl_header->version, sizeof(sigrl_header->version)); //Copy rl_ver in BigEndian }else{ memset(&epid_header->n2, 0, sizeof(epid_header->n2)); //set n2 and rl_ver to 0 if no sigrl provided memset(&epid_header->rl_ver, 0, sizeof(epid_header->rl_ver)); } //challenge nonce value is used as sign message uint32_t msg_len = CHALLENGE_NONCE_SIZE; EpidStatus epid_ret = EpidSignBasic(epid_member, const_cast<uint8_t *>(reinterpret_cast<const uint8_t *>(nonce_challenge)), msg_len, NULL, 0, &epid_header->sigma0, rnd_bsn);//generate EpidSignature Header inside EPC memory if(kEpidNoErr != epid_ret){ return epid_error_to_pve_error(epid_ret); } return PVEC_SUCCESS; }
/* * An internal function used to sign the EPID signature on the quote body. * Prefix "emp_" means it is a pointer points memory outside enclave. * * For quote with SIG-RL * |--------------------------------------------------------------------| * |sgx_quote_t|wrap_key_t|iv|payload_size|basic_sig|rl_ver|n2|nrp..|mac| * |--------------------------------------------------------------------| * For quote without SIG-RL * |--------------------------------------------------------------| * |sgx_quote_t|wrap_key_t|iv|payload_size|basic_sig|rl_ver|n2|mac| * |--------------------------------------------------------------| * * @param p_epid_context[in] Pointer to the EPID context. * @param plaintext[in] Reference to the plain text part of EPID blob. * @param p_basename[in] The pointer to basename. * @param emp_sig_rl_entries[in] The pointer to SIG-RL entries. * @param p_sig_rl_header[in] The header of SIG-RL, within EPC. * @param p_sig_rl_signature[in] The ecdsa signature of SIG-RL, within EPC. * @param p_enclave_report[in] The input isv report. * @param p_nonce[in] The input nonce. * @param p_qe_report[out] The output buffer for qe_report. * @param emp_quote[out] The output buffer for quote. * @param p_quote_body[in] The quote body in EPC. * @param sign_size[in] size of the signature. * @return ae_error_t AE_SUCCESS for success, otherwise for errors. */ static ae_error_t qe_epid_sign( MemberCtx *p_epid_context, const se_plaintext_epid_data_sdk_t& plaintext, const sgx_basename_t *p_basename, const SigRlEntry *emp_sig_rl_entries, se_sig_rl_t *p_sig_rl_header, sgx_ec256_signature_t *p_sig_rl_signature, const sgx_report_t *p_enclave_report, const sgx_quote_nonce_t *p_nonce, sgx_report_t *p_qe_report, uint8_t *emp_quote, const sgx_quote_t *p_quote_body, uint32_t sign_size) { ae_error_t ret = AE_SUCCESS; sgx_status_t se_ret = SGX_SUCCESS; EpidStatus epid_ret = kEpidNoErr; se_wrap_key_t wrap_key; BasicSignature basic_sig; BasicSignature encrypted_basic_sig; uint8_t aes_iv[QUOTE_IV_SIZE] = {0}; uint8_t aes_key[QE_AES_KEY_SIZE] = {0}; uint8_t aes_tag[SGX_SEAL_TAG_SIZE] = {0}; sgx_report_data_t qe_report_data = {{0}}; sgx_target_info_t report_target; sgx_ec256_public_t ec_pub_key; // little endian se_ae_ecdsa_hash_t sig_rl_hash = {{0}}; uint8_t ecc_result = SGX_EC_INVALID_SIGNATURE; sgx_sha_state_handle_t sha_context = NULL; sgx_sha_state_handle_t sha_quote_context = NULL; sgx_aes_state_handle_t aes_gcm_state = NULL; void *pub_key = NULL; size_t pub_key_size = 0; uint8_t* pub_key_buffer = NULL; sgx_ecc_state_handle_t ecc_handle = NULL; memset(&wrap_key, 0, sizeof(wrap_key)); memset(&basic_sig, 0, sizeof(basic_sig)); memset(&encrypted_basic_sig, 0, sizeof(encrypted_basic_sig)); memset(&report_target, 0, sizeof(report_target)); memset(&ec_pub_key, 0, sizeof(ec_pub_key)); se_encrypted_sign_t *emp_p = (se_encrypted_sign_t *) (((sgx_quote_t *)emp_quote)->signature); uint8_t* emp_nr = NULL; uint32_t match = FALSE; /* Sign the quote body and get the basic signature*/ epid_ret = EpidSignBasic(p_epid_context, (uint8_t *)const_cast<sgx_quote_t *>(p_quote_body), (uint32_t)QE_QUOTE_BODY_SIZE, (uint8_t *)const_cast<sgx_basename_t *>(p_basename), sizeof(*p_basename), &basic_sig, NULL); //Random basename, can be NULL if basename is provided if(kEpidNoErr != epid_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } /* Prepare the context for SHA256 of quote. */ if(p_qe_report) { se_ret = sgx_sha256_init(&sha_quote_context); if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } // Update hash for nonce. se_ret = sgx_sha256_update((uint8_t *)const_cast<sgx_quote_nonce_t *>(p_nonce), sizeof(*p_nonce), sha_quote_context); if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } // Update hash for the first part of quote. se_ret = sgx_sha256_update((uint8_t *)const_cast<sgx_quote_t *>(p_quote_body), sizeof(*p_quote_body), sha_quote_context); if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } } /* Prepare the context for SHA256 and start calculate the hash of header * of SIG-RL. */ if(emp_sig_rl_entries) { se_ret = sgx_sha256_init(&sha_context); if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } /* Calculate the hash of SIG-RL header. */ se_ret = sgx_sha256_update((uint8_t *)p_sig_rl_header, (uint32_t)(sizeof(se_sig_rl_t) - sizeof(SigRlEntry)), sha_context); if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } } // Start encrypt the signature. /* Get the random wrap key */ se_ret = sgx_read_rand(aes_key, sizeof(aes_key)); if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } /* Copy the hash of wrap key into output buffer. */ se_static_assert(sizeof(wrap_key.key_hash) == sizeof(sgx_sha256_hash_t)); se_ret = sgx_sha256_msg(aes_key, sizeof(aes_key), (sgx_sha256_hash_t *)wrap_key.key_hash); if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } /* Start encrypt the wrap key by RSA algorithm. */ se_ret = sgx_create_rsa_pub1_key(sizeof(g_qsdk_pub_key_n), sizeof(g_qsdk_pub_key_e), (const unsigned char *)g_qsdk_pub_key_n, (const unsigned char *)g_qsdk_pub_key_e, &pub_key); if(se_ret != SGX_SUCCESS) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } /* Get output buffer size */ se_ret = sgx_rsa_pub_encrypt_sha256(pub_key, NULL, &pub_key_size, aes_key, sizeof(aes_key)); if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } se_ret = sgx_rsa_pub_encrypt_sha256(pub_key, wrap_key.encrypted_key, &pub_key_size, aes_key, sizeof(aes_key)); if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } /* Create the random AES IV. */ se_ret = sgx_read_rand(aes_iv, sizeof(aes_iv)); if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } /* Copy the wrap_key_t into output buffer. */ memcpy(&emp_p->wrap_key, &wrap_key, sizeof(wrap_key)); /* Copy the AES IV into output buffer. */ memcpy(&emp_p->iv, aes_iv, sizeof(aes_iv)); /* Copy the AES Blob payload size into output buffer. */ memcpy(&emp_p->payload_size, &sign_size, sizeof(sign_size)); se_ret = sgx_aes_gcm128_enc_init( aes_key, aes_iv, //input initial vector. randomly generated value and encryption of different msg should use different iv sizeof(aes_iv), //length of initial vector, usually IV_SIZE NULL,//AAD of AES-GCM, it could be NULL 0, //length of bytes of AAD &aes_gcm_state); if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } memset_s(aes_key, sizeof(aes_key), 0, sizeof(aes_key)); /* Encrypt the basic signature. */ se_ret = sgx_aes_gcm128_enc_update( (uint8_t *)&basic_sig, //start address to data before/after encryption sizeof(basic_sig), (uint8_t *)&encrypted_basic_sig, //length of data aes_gcm_state); //pointer to a state if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } /* Copy the encrypted basic signature into output buffer. */ memcpy(&emp_p->basic_sign, &encrypted_basic_sig, sizeof(encrypted_basic_sig)); if(p_qe_report) { se_ret = sgx_sha256_update((uint8_t *)&wrap_key, sizeof(wrap_key), sha_quote_context); if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } se_ret = sgx_sha256_update(aes_iv, sizeof(aes_iv), sha_quote_context); if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } se_ret = sgx_sha256_update((uint8_t *)&sign_size, sizeof(sign_size), sha_quote_context); if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } se_ret = sgx_sha256_update((uint8_t *)&encrypted_basic_sig, sizeof(encrypted_basic_sig), sha_quote_context); if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } } /* Start process the SIG-RL. */ if(emp_sig_rl_entries) { unsigned int entry_count = 0; unsigned int i = 0; RLver_t encrypted_rl_ver = {{0}}; RLCount encrypted_n2 = {{0}}; entry_count = lv_ntohl(p_sig_rl_header->sig_rl.n2);//entry count for big endian to little endian // Continue encrypt the output se_ret = sgx_aes_gcm128_enc_update( (uint8_t *)&(p_sig_rl_header->sig_rl.version), //start address to data before/after encryption sizeof(p_sig_rl_header->sig_rl.version), (uint8_t *)&encrypted_rl_ver, //length of data aes_gcm_state); //pointer to a state if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } se_ret = sgx_aes_gcm128_enc_update( (uint8_t *)&(p_sig_rl_header->sig_rl.n2), //start address to data before/after encryption sizeof(p_sig_rl_header->sig_rl.n2), (uint8_t *)&encrypted_n2, //length of data aes_gcm_state); //pointer to a state if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } memcpy(&(emp_p->rl_ver), &encrypted_rl_ver, sizeof(encrypted_rl_ver)); memcpy(&(emp_p->rl_num), &encrypted_n2, sizeof(encrypted_n2)); if(p_qe_report) { se_ret = sgx_sha256_update((uint8_t *)&encrypted_rl_ver, sizeof(encrypted_rl_ver), sha_quote_context); if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } se_ret = sgx_sha256_update((uint8_t *)&encrypted_n2, sizeof(encrypted_n2), sha_quote_context); if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } } /* Start process the SIG-RL entries one by one. */ emp_nr = emp_p->nrp_mac; for (i = 0; i < entry_count; i++, emp_nr += sizeof(NrProof)) { /* Generate non-revoke prove one by one. */ SigRlEntry entry; NrProof temp_nr; NrProof encrypted_temp_nr; memcpy(&entry, emp_sig_rl_entries + i, sizeof(entry)); memset_s(&temp_nr, sizeof(temp_nr), 0, sizeof(temp_nr)); memset_s(&encrypted_temp_nr, sizeof(encrypted_temp_nr), 0, sizeof(encrypted_temp_nr)); epid_ret = EpidNrProve(p_epid_context, (uint8_t *)const_cast<sgx_quote_t *>(p_quote_body), (uint32_t)QE_QUOTE_BODY_SIZE, (uint8_t *)const_cast<sgx_basename_t *>(p_basename), // basename is required, otherwise it will return kEpidBadArgErr sizeof(*p_basename), &basic_sig, // Basic signature with 'b' and 'k' in it &entry, //Single entry in SigRl composed of 'b' and 'k' &temp_nr); // The generated non-revoked proof if(kEpidNoErr != epid_ret) { if(kEpidSigRevokedInSigRl == epid_ret) match = TRUE; else { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } } /* Update the hash of SIG-RL */ se_ret = sgx_sha256_update((uint8_t *)&entry, sizeof(entry), sha_context); if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } se_ret = sgx_aes_gcm128_enc_update( (uint8_t *)&temp_nr, //start address to data before/after encryption sizeof(encrypted_temp_nr), (uint8_t *)&encrypted_temp_nr, //length of data aes_gcm_state); //pointer to a state if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } memcpy(emp_nr, &encrypted_temp_nr, sizeof(encrypted_temp_nr)); if(p_qe_report) { se_ret = sgx_sha256_update((uint8_t *)&encrypted_temp_nr, sizeof(encrypted_temp_nr), sha_quote_context); if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } } } /* Get the final hash of the whole SIG-RL. */ se_ret = sgx_sha256_get_hash(sha_context, (sgx_sha256_hash_t *)&sig_rl_hash.hash); if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } /* Verify the integrity of SIG-RL by check ECDSA signature. */ se_static_assert(sizeof(ec_pub_key) == sizeof(plaintext.epid_sk)); // Both plaintext.epid_sk and ec_pub_key are little endian memcpy(&ec_pub_key, plaintext.epid_sk, sizeof(ec_pub_key)); se_ret = sgx_ecc256_open_context(&ecc_handle); if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } // sgx_ecdsa_verify_hash will take ec_pub_key as little endian se_ret = sgx_ecdsa_verify_hash((uint8_t*)&(sig_rl_hash.hash), (const sgx_ec256_public_t *)&ec_pub_key, p_sig_rl_signature, &ecc_result, ecc_handle); if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } else if(SGX_EC_VALID != ecc_result) { ret = QE_SIGRL_ERROR; goto CLEANUP; } else if(match) { ret = QE_REVOKED_ERROR; goto CLEANUP; } } else { se_static_assert(sizeof(emp_p->rl_ver) == sizeof(RLver_t)); se_static_assert(sizeof(emp_p->rl_num) == sizeof(RLCount)); uint8_t temp_buf[sizeof(RLver_t) + sizeof(RLCount)] = {0}; uint8_t encrypted_temp_buf[sizeof(temp_buf)] = {0}; se_ret = sgx_aes_gcm128_enc_update( (uint8_t *)&temp_buf, //start address to data before/after encryption sizeof(encrypted_temp_buf), (uint8_t *)&encrypted_temp_buf, //length of data aes_gcm_state); //pointer to a state if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } /* This will copy both encrypted rl_ver and encrypted rl_num into Output buffer. */ memcpy(&emp_p->rl_ver, &encrypted_temp_buf, sizeof(encrypted_temp_buf)); if(p_qe_report) { se_ret = sgx_sha256_update((uint8_t *)&encrypted_temp_buf, sizeof(encrypted_temp_buf), sha_quote_context); if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } } } se_ret = sgx_aes_gcm128_enc_get_mac(aes_tag, aes_gcm_state); if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } memcpy((uint8_t *)&(emp_p->basic_sign) + sign_size, &aes_tag, sizeof(aes_tag)); if(p_qe_report) { se_ret = sgx_sha256_update(aes_tag, sizeof(aes_tag), sha_quote_context); if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } se_ret = sgx_sha256_get_hash(sha_quote_context, (sgx_sha256_hash_t *)&qe_report_data); if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } memcpy(&(report_target.attributes), &(((const sgx_report_t *)p_enclave_report)->body.attributes), sizeof(report_target.attributes)); memcpy(&(report_target.mr_enclave), &(((const sgx_report_t *)p_enclave_report)->body.mr_enclave), sizeof(report_target.mr_enclave)); memcpy(&(report_target.misc_select), &(((const sgx_report_t *)p_enclave_report)->body.misc_select), sizeof(report_target.misc_select)); se_ret = sgx_create_report(&report_target, &qe_report_data, p_qe_report); if(SGX_SUCCESS != se_ret) { ret = QE_PARAMETER_ERROR; goto CLEANUP; } } CLEANUP: memset_s(aes_key, sizeof(aes_key), 0, sizeof(aes_key)); sgx_sha256_close(sha_context); sgx_sha256_close(sha_quote_context); if (aes_gcm_state) sgx_aes_gcm_close(aes_gcm_state); if (pub_key) sgx_free_rsa_key(pub_key, SGX_RSA_PUBLIC_KEY, sizeof(plaintext.qsdk_mod), sizeof(plaintext.qsdk_exp)); if (pub_key_buffer) free(pub_key_buffer); if (ecc_handle) sgx_ecc256_close_context(ecc_handle); return ret; }
EpidStatus EpidSign(MemberCtx const* ctx, void const* msg, size_t msg_len, void const* basename, size_t basename_len, SigRl const* sig_rl, size_t sig_rl_size, EpidSignature* sig, size_t sig_len) { EpidStatus result = kEpidErr; uint32_t num_sig_rl = 0; OctStr32 octstr32_0 = {{0x00, 0x00, 0x00, 0x00}}; if (!ctx || !sig) { return kEpidBadArgErr; } if (!ctx->pub_key) { return kEpidBadArgErr; } if (!msg && (0 != msg_len)) { // if message is non-empty it must have both length and content return kEpidBadArgErr; } if (!basename && (0 != basename_len)) { // if basename is non-empty it must have both length and content return kEpidBadArgErr; } if (sig_rl && (sig_rl_size < sizeof(SigRl) - sizeof(SigRlEntry))) { return kEpidBadArgErr; } if (sig_rl && EpidGetSigSize(sig_rl) > sig_len) { return kEpidBadArgErr; } // 11. The member sets sigma0 = (B, K, T, c, sx, sf, sa, sb). result = EpidSignBasic(ctx, msg, msg_len, basename, basename_len, &sig->sigma0); if (kEpidNoErr != result) { return result; } if (!sig_rl) { // 12. If SigRL is not provided as input, // a. The member sets RLver = 0 and n2 = 0. // b. The member outputs (sigma0, RLver, n2) and returns "succeeded". sig->rl_ver = octstr32_0; sig->n2 = octstr32_0; return kEpidNoErr; } else { uint32_t i = 0; EpidStatus nr_prove_status = kEpidNoErr; // 13. If SigRL is provided as input, the member proceeds with // the following steps: // a. The member verifies that gid in public key and in SigRL // match. if (!IsSigRlValid(&ctx->pub_key->gid, sig_rl, sig_rl_size)) { return kEpidBadArgErr; } // b. The member copies RLver and n2 values in SigRL to the // signature. if (0 != memcpy_S(&sig->rl_ver, sizeof(sig->rl_ver), &sig_rl->version, sizeof(sig_rl->version))) return kEpidBadArgErr; if (0 != memcpy_S(&sig->n2, sizeof(sig->n2), &sig_rl->n2, sizeof(sig_rl->n2))) return kEpidBadArgErr; // c. For i = 0, ..., n2-1, the member computes sigma[i] = // nrProve(f, B, K, B[i], K[i]). The details of nrProve() // will be given in the next subsection. num_sig_rl = ntohl(sig_rl->n2); for (i = 0; i < num_sig_rl; i++) { result = EpidNrProve(ctx, msg, msg_len, &sig->sigma0, &sig_rl->bk[i], &sig->sigma[i]); if (kEpidNoErr != result) { nr_prove_status = result; } } if (kEpidNoErr != nr_prove_status) { memset(&sig->sigma[0], 0, num_sig_rl * sizeof(sig->sigma[0])); return nr_prove_status; } } // d. The member outputs (sigma0, RLver, n2, sigma[0], ..., // sigma[n2-1]). // e. If any of the nrProve() functions outputs "failed", the // member returns "revoked", otherwise returns "succeeded". return kEpidNoErr; }