Ejemplo n.º 1
0
/*
 * External function used to get quote. Prefix "emp_" means it is a pointer
 * points memory outside enclave.
 *
 * @param p_blob[in, out] Pointer to the EPID Blob.
 * @param blob_size[in] The size of EPID Blob, in bytes.
 * @param p_enclave_report[in] The application enclave's report.
 * @param quote_type[in] The type of quote, random based or name based.
 * @param p_spid[in] Pointer to SPID.
 * @param p_nonce[in] Pointer to nonce.
 * @param emp_sig_rl[in] Pointer to SIG-RL.
 * @param sig_rl_size[in] The size of SIG-RL, in bytes.
 * @param p_qe_report[out] Pointer to QE report, which reportdata is
 *                         sha256(nonce || quote)
 * @param emp_quote[out] Pointer to the output buffer for quote.
 * @param quote_size[in] The size of emp_quote, in bytes.
 * @param pce_isvsvn[in] The ISVSVN of PCE.
 * @return ae_error_t AE_SUCCESS for success, otherwise for errors.
 */
uint32_t get_quote(
    uint8_t *p_blob,
    uint32_t blob_size,
    const sgx_report_t *p_enclave_report,
    sgx_quote_sign_type_t quote_type,
    const sgx_spid_t *p_spid,
    const sgx_quote_nonce_t *p_nonce,
    const uint8_t *emp_sig_rl,
    uint32_t sig_rl_size,
    sgx_report_t *p_qe_report,
    uint8_t *emp_quote,
    uint32_t quote_size,
    sgx_isv_svn_t pce_isvsvn)
{
    ae_error_t ret = AE_SUCCESS;
    EpidStatus epid_ret = kEpidNoErr;
    MemberCtx *p_epid_context = NULL;
    sgx_quote_t quote_body;
    uint8_t is_resealed = 0;
    sgx_basename_t basename = {{0}};
    uint64_t sign_size = 0;
    sgx_status_t se_ret = SGX_SUCCESS;
    sgx_report_t qe_report;
    uint64_t required_buffer_size = 0;
    se_sig_rl_t sig_rl_header;
    se_plaintext_epid_data_sdk_t plaintext;
    sgx_ec256_signature_t ec_signature;
    sgx_cpu_svn_t cpusvn;

    memset(&quote_body, 0, sizeof(quote_body));
    memset(&sig_rl_header, 0, sizeof(sig_rl_header));
    memset(&plaintext, 0, sizeof(plaintext));
    memset(&ec_signature, 0, sizeof(ec_signature));
    memset(&cpusvn, 0, sizeof(cpusvn));


    /* Actually, some cases here will be checked with code generated by
       edger8r. Here we just want to defend in depth. */
    if((NULL == p_blob)
       || (NULL == p_enclave_report)
       || (NULL == p_spid)
       || (NULL == emp_quote)
       || (!quote_size)
       || ((NULL != emp_sig_rl) && (sig_rl_size < sizeof(se_sig_rl_t)
                                                  + 2 * SE_ECDSA_SIGN_SIZE))

		//
		// this size check could mispredict and cause us to
		// overflow, but we have an lfence below
		// that's safe to use for this case
		//

       || ((NULL == emp_sig_rl) && (sig_rl_size != 0)))
        return QE_PARAMETER_ERROR;
    if(SGX_TRUSTED_EPID_BLOB_SIZE_SDK != blob_size)
        return QE_PARAMETER_ERROR;

	//
	// this could mispredict and cause us to
	// overflow, but we have an lfence below
	// that's safe to use for this case
	//

    if(SGX_LINKABLE_SIGNATURE != quote_type
       && SGX_UNLINKABLE_SIGNATURE != quote_type)
        return QE_PARAMETER_ERROR;
    if(!p_nonce && p_qe_report)
        return QE_PARAMETER_ERROR;
    if(p_nonce && !p_qe_report)
        return QE_PARAMETER_ERROR;

    /* To reduce the memory footprint of QE, we should leave sig_rl and
       quote buffer outside enclave. */
    if(!sgx_is_outside_enclave(emp_sig_rl, sig_rl_size))
        return QE_PARAMETER_ERROR;

    //
    // for user_check SigRL input
    // based on quote_size input parameter
    //
    sgx_lfence();

    if(!sgx_is_outside_enclave(emp_quote, quote_size))
        return QE_PARAMETER_ERROR;

    /* Check whether p_blob is copied into EPC. If we want to reduce the
       memory usage, maybe we can leave the p_blob outside EPC. */
    if(!sgx_is_within_enclave(p_blob, blob_size))
        return QE_PARAMETER_ERROR;
    if(!sgx_is_within_enclave(p_enclave_report, sizeof(*p_enclave_report)))
        return QE_PARAMETER_ERROR;
    if(!sgx_is_within_enclave(p_spid, sizeof(*p_spid)))
        return QE_PARAMETER_ERROR;
    /* If the code reach here, if p_nonce is NULL, then p_qe_report will be
       NULL also. So we only check p_nonce here.*/
    if(p_nonce)
    {
        /* Actually Edger8r will alloc the buffer within EPC, this is just kind
           of defense in depth. */
        if(!sgx_is_within_enclave(p_nonce, sizeof(*p_nonce)))
            return QE_PARAMETER_ERROR;
        if(!sgx_is_within_enclave(p_qe_report, sizeof(*p_qe_report)))
            return QE_PARAMETER_ERROR;
    }

    /* Verify the input report. */
    if(SGX_SUCCESS != sgx_verify_report(p_enclave_report))
        return QE_PARAMETER_ERROR;

    /* Verify EPID p_blob and create the context */
    ret = random_stack_advance(verify_blob_internal, p_blob,
        blob_size,
        &is_resealed,
        TRUE,
        plaintext,
        &p_epid_context,
        &cpusvn);
    if(AE_SUCCESS != ret)
        goto CLEANUP;

    /* If SIG-RL is provided, we should check its size. */
    if(emp_sig_rl)
    {
        uint64_t temp_size = 0;
        uint64_t n2 = 0;

        memcpy(&sig_rl_header, emp_sig_rl, sizeof(sig_rl_header));
        if(sig_rl_header.protocol_version != SE_EPID_SIG_RL_VERSION)
        {
            ret = QE_PARAMETER_ERROR;
            goto CLEANUP;
        }

        if(sig_rl_header.epid_identifier != SE_EPID_SIG_RL_ID)
        {
            ret = QE_PARAMETER_ERROR;
            goto CLEANUP;
        }

        if(memcmp(&sig_rl_header.sig_rl.gid, &plaintext.epid_group_cert.gid,
                   sizeof(sig_rl_header.sig_rl.gid)))
        {
            ret = QE_PARAMETER_ERROR;
            goto CLEANUP;
        }
        temp_size = se_get_sig_rl_size(&sig_rl_header);
        if(temp_size != sig_rl_size)
        {
            ret = QE_PARAMETER_ERROR;
            goto CLEANUP;
        }

        se_static_assert(sizeof(ec_signature.x) == SE_ECDSA_SIGN_SIZE);
        se_static_assert(sizeof(ec_signature.y) == SE_ECDSA_SIGN_SIZE);
        memcpy(ec_signature.x,
               emp_sig_rl + sig_rl_size - (SE_ECDSA_SIGN_SIZE * 2),
               sizeof(ec_signature.x));
        SWAP_ENDIAN_32B(ec_signature.x);
        memcpy(ec_signature.y,
               emp_sig_rl + sig_rl_size - (SE_ECDSA_SIGN_SIZE * 1),
               sizeof(ec_signature.y));
        SWAP_ENDIAN_32B(ec_signature.y);

        n2 = SWAP_4BYTES(sig_rl_header.sig_rl.n2);
        temp_size = sizeof(EpidSignature) - sizeof(NrProof)
                    + n2 * sizeof(NrProof);
        if(temp_size > UINT32_MAX)
        {
            ret = QE_PARAMETER_ERROR;
            goto CLEANUP;
        }
        sign_size = temp_size;
    }
    else
    {
        sign_size = sizeof(BasicSignature)
                    + sizeof(uint32_t) // rl_ver
                    + sizeof(uint32_t); // rl_num
    }

    /* Verify sizeof basename is large enough and it should always be true*/
    se_static_assert(sizeof(basename) > sizeof(*p_spid));
    /* Because basename has already been zeroed,
       so we don't need to concatenating with 0s.*/
    memcpy(&basename, p_spid, sizeof(*p_spid));
    if(SGX_UNLINKABLE_SIGNATURE == quote_type)
    {
        uint8_t *p = (uint8_t *)&basename + sizeof(*p_spid);
        se_ret = sgx_read_rand(p, sizeof(basename) - sizeof(*p_spid));
        if(SGX_SUCCESS != se_ret)
        {
            ret = QE_UNEXPECTED_ERROR;
            goto CLEANUP;
        }
    }

    epid_ret = EpidRegisterBasename(p_epid_context, (uint8_t *)&basename,
        sizeof(basename));
    if(kEpidNoErr != epid_ret)
    {
        ret = QE_UNEXPECTED_ERROR;
        goto CLEANUP;
    }

    required_buffer_size = SE_QUOTE_LENGTH_WITHOUT_SIG + sign_size;

    /* We should make sure the buffer size is big enough. */
    if(quote_size < required_buffer_size)
    {
        ret = QE_PARAMETER_ERROR;
        goto CLEANUP;
    }

    //
    // for user_check SigRL input
    // based on n2 field in SigRL
    //
    sgx_lfence();

    /* Copy the data in the report into quote body. */
    memset(emp_quote, 0, quote_size);
    quote_body.version = QE_QUOTE_VERSION;
    quote_body.sign_type = (uint16_t)quote_type;
    quote_body.pce_svn = pce_isvsvn; // Both are little endian
    quote_body.xeid = plaintext.xeid; // Both are little endian
    se_static_assert(sizeof(plaintext.epid_group_cert.gid) == sizeof(OctStr32));
    se_static_assert(sizeof(quote_body.epid_group_id) == sizeof(uint32_t));
    ((uint8_t *)(&quote_body.epid_group_id))[0] = plaintext.epid_group_cert.gid.data[3];
    ((uint8_t *)(&quote_body.epid_group_id))[1] = plaintext.epid_group_cert.gid.data[2];
    ((uint8_t *)(&quote_body.epid_group_id))[2] = plaintext.epid_group_cert.gid.data[1];
    ((uint8_t *)(&quote_body.epid_group_id))[3] = plaintext.epid_group_cert.gid.data[0];
    memcpy(&quote_body.basename, &basename, sizeof(quote_body.basename));

    // Get the QE's report.
    se_ret = sgx_create_report(NULL, NULL, &qe_report);
    if(SGX_SUCCESS != se_ret)
    {
        ret = QE_PARAMETER_ERROR;
        goto CLEANUP;
    }

    // Copy QE's security version in to Quote body.
    quote_body.qe_svn = qe_report.body.isv_svn;

    // Copy the incoming report into Quote body.
    memcpy(&quote_body.report_body, &(p_enclave_report->body),
           sizeof(quote_body.report_body));
    /* Because required_buffer_size is larger than signature_len, so if we
       get here, then no integer overflow will ocur. */
    quote_body.signature_len = (uint32_t)(sizeof(se_wrap_key_t)
                               + QUOTE_IV_SIZE
                               + sizeof(uint32_t)
                               + sign_size
                               + sizeof(sgx_mac_t));

    /* Make the signature. */
    ret = qe_epid_sign(p_epid_context,
                       plaintext,
                       &basename,
                       emp_sig_rl ? ((const se_sig_rl_t *)emp_sig_rl)->sig_rl.bk
                                    : NULL,
                       &sig_rl_header,
                       &ec_signature,
                       p_enclave_report,
                       p_nonce,
                       p_qe_report,
                       emp_quote,
                       &quote_body,
                       (uint32_t)sign_size);
    if(AE_SUCCESS != ret)
    {
        // Only need to clean the buffer after the fixed length part.
        memset_s(emp_quote + sizeof(sgx_quote_t), quote_size - sizeof(sgx_quote_t),
                 0, quote_size - sizeof(sgx_quote_t));
        goto CLEANUP;
    }

    memcpy(emp_quote, &quote_body, sizeof(sgx_quote_t));

CLEANUP:
    if(p_epid_context)
		epid_member_delete(&p_epid_context);
    return ret;
}
Ejemplo n.º 2
0
/*
 * An internal function used to verify EPID Blob, get EPID Group Cert
 * and get EPID context, at the same time, you can check whether EPID blob has
 * been resealed.
 *
 * @param p_blob[in, out] Pointer to EPID Blob.
 * @param p_is_resealed[out] Whether the EPID Blob has been resealed.
 * @param create_context[in] Flag indicates create EPID context or not.
 * @param plaintext_epid_data[out] Used to get the plaintext part of epid blob
 * @param pp_epid_context[out] Used to get the pointer of the EPID context.
 * @param p_cpusvn[out] Return the raw CPUSVN.
 * @return ae_error_t AE_SUCCESS or other error cases.
 */
static ae_error_t verify_blob_internal(
    uint8_t *p_blob,
    uint32_t blob_size,
    uint8_t *p_is_resealed,
    uint32_t create_context,
    se_plaintext_epid_data_sdk_t& plaintext_epid_data,
    MemberCtx **pp_epid_context,
    sgx_cpu_svn_t *p_cpusvn)
{
    ae_error_t ret = QE_UNEXPECTED_ERROR;
    sgx_status_t se_ret = SGX_SUCCESS;
    uint8_t resealed = FALSE;
    se_secret_epid_data_sdk_t secret_epid_data;
    se_plaintext_epid_data_sik_t plaintext_old_format;
    uint32_t plaintext_length;
    int is_old_format = 0;
    uint32_t decryptedtext_length = sizeof(secret_epid_data);
    sgx_sealed_data_t *p_epid_blob = (sgx_sealed_data_t *)p_blob;
    uint8_t local_epid_blob[sizeof(*p_epid_blob)
                            + sizeof(secret_epid_data)
                            + sizeof(plaintext_epid_data)]
                            = {0};
    MemberCtx *p_ctx = NULL;

    do {
        // We will use plaintext_old_format as buffer to hold the output of sgx_unseal_data.
        // It can be se_plaintext_epid_data_sik_t or se_plaintext_epid_data_sdk_t.
        // We use the static assert to reassure plaintext_old_format is big enough.
        // If someone changed the definition of these 2 structures and break current assumption,
        // it will report error in compile time.
        se_static_assert(sizeof(plaintext_old_format) >= sizeof(plaintext_epid_data));

        if (sgx_get_encrypt_txt_len(p_epid_blob) != sizeof(se_secret_epid_data_sdk_t) &&
            sgx_get_encrypt_txt_len(p_epid_blob) != sizeof(se_secret_epid_data_sik_t)) {
            return QE_EPIDBLOB_ERROR;
        }
        plaintext_length = sgx_get_add_mac_txt_len(p_epid_blob);
        if (plaintext_length != sizeof(se_plaintext_epid_data_sik_t) &&
            plaintext_length != sizeof(se_plaintext_epid_data_sdk_t))
        {
            return QE_EPIDBLOB_ERROR;
        }

        memset(&secret_epid_data, 0, sizeof(secret_epid_data));
        memset(&plaintext_epid_data, 0, sizeof(plaintext_epid_data));
        memset(&plaintext_old_format, 0, sizeof(plaintext_old_format));

        se_ret = sgx_unseal_data(p_epid_blob,
            (uint8_t *)&plaintext_old_format, // The unsealed plaintext can be old or new format, the buffer is defined as old format because it is bigger
            &plaintext_length,
            (uint8_t *)&secret_epid_data,
            &decryptedtext_length);
        BREAK_IF_TRUE(SGX_SUCCESS != se_ret, ret, QE_EPIDBLOB_ERROR);

        //QE will support both epid blob with/without member precomputation
        //If the epid blob without member precomputation is used, QE will generate member precomputation and reseal epid blob
        //blob_type and key_version are always first two fields of plaintext in both format
        BREAK_IF_TRUE((plaintext_old_format.seal_blob_type != PVE_SEAL_EPID_KEY_BLOB)
            || (plaintext_old_format.epid_key_version != EPID_KEY_BLOB_VERSION_SDK&&
                plaintext_old_format.epid_key_version != EPID_KEY_BLOB_VERSION_SIK),
            ret, QE_EPIDBLOB_ERROR);

        // Only 2 combinations are legitimate for the tuple epid_key_version|decryptedtext_length|plaintext_length:
        // EPID_KEY_BLOB_VERSION_SIK|sizeof(se_secret_epid_data_sik_t)|sizeof(se_plaintext_epid_data_sik_t)
        // EPID_KEY_BLOB_VERSION_SDK|sizeof(se_secret_epid_data_sdk_t)|sizeof(se_plaintext_epid_data_sdk_t)
        BREAK_IF_TRUE((plaintext_old_format.epid_key_version == EPID_KEY_BLOB_VERSION_SIK &&
            (decryptedtext_length != sizeof(se_secret_epid_data_sik_t) || plaintext_length != sizeof(se_plaintext_epid_data_sik_t))) ||
            (plaintext_old_format.epid_key_version == EPID_KEY_BLOB_VERSION_SDK &&
            (decryptedtext_length != sizeof(se_secret_epid_data_sdk_t) || plaintext_length != sizeof(se_plaintext_epid_data_sdk_t))),
            ret, QE_EPIDBLOB_ERROR);

        // If the input epid blob is in sik format, we will upgrade it to sdk version
        if (plaintext_old_format.epid_key_version == EPID_KEY_BLOB_VERSION_SIK) {
            plaintext_epid_data.seal_blob_type = PVE_SEAL_EPID_KEY_BLOB;
            plaintext_epid_data.epid_key_version = EPID_KEY_BLOB_VERSION_SDK;
            memcpy(&plaintext_epid_data.equiv_cpu_svn, &plaintext_old_format.equiv_cpu_svn, sizeof(plaintext_old_format.equiv_cpu_svn));
            memcpy(&plaintext_epid_data.equiv_pve_isv_svn, &plaintext_old_format.equiv_pve_isv_svn, sizeof(plaintext_old_format.equiv_pve_isv_svn));
            memcpy(&plaintext_epid_data.epid_group_cert, &plaintext_old_format.epid_group_cert, sizeof(plaintext_old_format.epid_group_cert));
            memcpy(&plaintext_epid_data.qsdk_exp, &plaintext_old_format.qsdk_exp, sizeof(plaintext_old_format.qsdk_exp));
            memcpy(&plaintext_epid_data.qsdk_mod, &plaintext_old_format.qsdk_mod, sizeof(plaintext_old_format.qsdk_mod));
            memcpy(&plaintext_epid_data.epid_sk, &plaintext_old_format.epid_sk, sizeof(plaintext_old_format.epid_sk));
            plaintext_epid_data.xeid = plaintext_old_format.xeid;
            memset(&secret_epid_data.member_precomp_data, 0, sizeof(secret_epid_data.member_precomp_data));
            is_old_format = 1;
            //PrivKey of secret_epid_data are both in offset 0 so that we need not move it
        }
        else {//SDK version format
            memcpy(&plaintext_epid_data, &plaintext_old_format, sizeof(plaintext_epid_data));
        }

        /* Create report to get current cpu_svn and isv_svn. */
        sgx_report_t report;
        memset(&report, 0, sizeof(report));
        se_ret = sgx_create_report(NULL, NULL, &report);
        BREAK_IF_TRUE(SGX_SUCCESS != se_ret, ret, QE_UNEXPECTED_ERROR);

        /* Get the random function pointer. */
        BitSupplier rand_func = epid_random_func;

        /* Create EPID member context if required. PvE is responsible for verifying
        the Cert signature before storing them in the EPID blob. */
        if (create_context || is_old_format)
        {
            EpidStatus epid_ret = kEpidNoErr;
            epid_ret = epid_member_create(rand_func, NULL, NULL, &p_ctx);
            BREAK_IF_TRUE(kEpidNoErr != epid_ret, ret, QE_UNEXPECTED_ERROR);

            epid_ret = EpidProvisionKey(p_ctx,
                &(plaintext_epid_data.epid_group_cert),
                (PrivKey*)&(secret_epid_data.epid_private_key),
                is_old_format ? NULL : &secret_epid_data.member_precomp_data);
            BREAK_IF_TRUE(kEpidNoErr != epid_ret, ret, QE_UNEXPECTED_ERROR);

            // start member
            epid_ret = EpidMemberStartup(p_ctx);
            BREAK_IF_TRUE(kEpidNoErr != epid_ret, ret, QE_UNEXPECTED_ERROR);

            if (is_old_format)
            {
                epid_ret = EpidMemberWritePrecomp(p_ctx, &secret_epid_data.member_precomp_data);
                BREAK_IF_TRUE(kEpidNoErr != epid_ret, ret, QE_UNEXPECTED_ERROR);
            }
        }

        /* Update the Key Blob using the SEAL Key for the current TCB if the TCB is
           upgraded after the Key Blob is generated. Here memcmp cpu_svns might be
           different even though they're actually same, but for defense in depth we
           will keep this comparison here. And we will also upgrade old format EPID
           blob to new format here. */
        if ((memcmp(&report.body.cpu_svn, &p_epid_blob->key_request.cpu_svn,
            sizeof(report.body.cpu_svn)))
            || (report.body.isv_svn != p_epid_blob->key_request.isv_svn)
            || plaintext_old_format.epid_key_version == EPID_KEY_BLOB_VERSION_SIK)
        {
            se_ret = sgx_seal_data(sizeof(plaintext_epid_data),
                (uint8_t *)&plaintext_epid_data,
                sizeof(secret_epid_data),
                (uint8_t *)&secret_epid_data,
                SGX_TRUSTED_EPID_BLOB_SIZE_SDK,
                (sgx_sealed_data_t *)local_epid_blob);
            BREAK_IF_TRUE(SGX_SUCCESS != se_ret, ret, QE_UNEXPECTED_ERROR);

            memcpy(p_epid_blob, local_epid_blob, blob_size);
            resealed = TRUE;
        }
        *p_is_resealed = resealed;
        memcpy(p_cpusvn, &report.body.cpu_svn, sizeof(*p_cpusvn));
        ret = AE_SUCCESS;
    }
    while (false);

    // Clear the output buffer to make sure nothing leaks.
    memset_s(&secret_epid_data, sizeof(secret_epid_data), 0,
        sizeof(secret_epid_data));
    if (AE_SUCCESS != ret) {
        if (p_ctx)
            epid_member_delete(&p_ctx);
    }
    else if (!create_context) {
        if (p_ctx)
            epid_member_delete(&p_ctx);
    }
    else {
        *pp_epid_context = p_ctx;
    }

    return ret;
}
Ejemplo n.º 3
0
//Function to generate Field1_0 of ProvMsg3
//@msg2_blob_input, input decoded ProvMsg2 info
//@join_proof, output the join proof and the escrow data which is encrypted f of Private Key
//@return PVEC_SUCCESS on success and error code on failure
//The function assume all required inputs have been prepared in msg2_blob_input
static pve_status_t gen_msg3_join_proof_escrow_data(const proc_prov_msg2_blob_input_t *msg2_blob_input,
                                                    join_proof_with_escrow_t& join_proof)
{
    pve_status_t ret = PVEC_SUCCESS;
    BitSupplier epid_prng = (BitSupplier) epid_random_func;
    FpElemStr temp_f;
    //first generate private key f randomly before sealing it by PSK
    FpElemStr *f = &temp_f;
    sgx_status_t sgx_status = SGX_SUCCESS;
    JoinRequest *join_r = &join_proof.jr;
    EpidStatus epid_ret  = kEpidNoErr;
    psvn_t psvn;
    MemberCtx* ctx = NULL;
    memset(&temp_f, 0, sizeof(temp_f));

    //randomly generate the private EPID key f, host to network transformation not required since server will not decode it
    ret=sgx_error_to_pve_error(sgx_gen_epid_priv_f((void*)f));
	if(PVEC_SUCCESS != ret){
        goto ret_point;
    }

    //generate JoinP using f before encryption by calling EPID library
    memset(join_r, 0, sizeof(JoinRequest));//first clear to 0
    //generate JoinP to fill it in field1_0_0 by EPID library

    epid_ret = epid_member_create(epid_prng, NULL, f, &ctx);
    if(kEpidNoErr!=epid_ret){
        ret = epid_error_to_pve_error(epid_ret);
        goto ret_point;
    }
    
    epid_ret = EpidCreateJoinRequest(ctx, 
        &msg2_blob_input->group_cert.key, //EPID Group Cert from ProvMsgs2 used
        reinterpret_cast<const IssuerNonce *>(msg2_blob_input->challenge_nonce),
        join_r);
    if(kEpidNoErr != epid_ret){
        ret = epid_error_to_pve_error(epid_ret);
        goto ret_point;
    }

    //get PSK
    sgx_key_128bit_t psk;
    memcpy(&psvn.cpu_svn, &msg2_blob_input->equiv_pi.cpu_svn, sizeof(psvn.cpu_svn));
    memcpy(&psvn.isv_svn, &msg2_blob_input->equiv_pi.pve_svn, sizeof(psvn.isv_svn));
    ret = get_pve_psk(&psvn, &psk);
    if(PVEC_SUCCESS != ret){
        goto ret_point;
    }

    join_proof.escrow.version = 0;//version 0 used for escrow data
    //now we could seal f by PSK
    ret = se_read_rand_error_to_pve_error(sgx_read_rand(join_proof.escrow.iv, IV_SIZE));
    if(PVEC_SUCCESS != ret){
        goto ret_point;
    }

    se_static_assert(sizeof(psk)==sizeof(sgx_aes_gcm_128bit_key_t)); /*sizeof sgx_aes_gcm_128bit_key_t tshould be same as size of psk*/
    se_static_assert(sizeof(sgx_aes_gcm_128bit_tag_t)==sizeof(join_proof.escrow.mac)); /*sizeof sgx_aes_gcm_128bit_tag_t should be same as MAC_SIZE*/
    sgx_status = sgx_rijndael128GCM_encrypt(reinterpret_cast<const sgx_aes_gcm_128bit_key_t *>(&psk),
        reinterpret_cast<uint8_t *>(f), sizeof(*f), reinterpret_cast<uint8_t *>(&join_proof.escrow.f),
        join_proof.escrow.iv, IV_SIZE, NULL, 0, 
        reinterpret_cast<sgx_aes_gcm_128bit_tag_t *>(join_proof.escrow.mac));
    if(SGX_SUCCESS != sgx_status){
        ret = sgx_error_to_pve_error(sgx_status);
    }
ret_point:
    (void)memset_s(&psk, sizeof(psk), 0, sizeof(psk));//clear the key
    (void)memset_s(&temp_f, sizeof(temp_f), 0, sizeof(temp_f));//clear temp f in stack
    if(PVEC_SUCCESS != ret){
        (void)memset_s(&join_proof, sizeof(join_proof), 0, sizeof(join_proof));
    }
    epid_member_delete(&ctx);
    return ret;
}