Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

out-of-tree attester/verifier instances support #25

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 0 additions & 20 deletions src/attesters/api/enclave_attester_register.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,6 @@ enclave_attester_err_t enclave_attester_register(const enclave_attester_opts_t *

RTLS_DEBUG("registering the enclave attester '%s' ...\n", opts->name);

if (opts->flags & ENCLAVE_ATTESTER_OPTS_FLAGS_TDX_GUEST) {
if (!is_tdguest_supported()) {
// clang-format off
RTLS_DEBUG("failed to register the attester '%s' due to lack of TDX Guest capability\n",
opts->type);
// clang-format on
return -ENCLAVE_ATTESTER_ERR_CPU_UNSUPPORTED;
}
}

if (opts->flags & ENCLAVE_ATTESTER_OPTS_FLAGS_SNP_GUEST) {
if (!is_snpguest_supported()) {
// clang-format off
RTLS_DEBUG("failed to register the attester '%s' due to lack of SNP Guest capability\n",
opts->type);
// clang-format on
return -ENCLAVE_ATTESTER_ERR_CPU_UNSUPPORTED;
}
}

enclave_attester_opts_t *new_opts = (enclave_attester_opts_t *)malloc(sizeof(*new_opts));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should move this check to the corresponding instances' pre_init(), instead of simply removing them. In addition, ENCLAVE_ATTESTER_OPTS_FLAGS_SNP_GUEST and ENCLAVE_ATTESTER_OPTS_FLAGS_TDX_GUEST looks useless. You can remove them.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep tee self-detect function in registering phase will block some instance to register. Consider the condition, if an app need to use some key generation function in sev-snp.so, but the app run in intel cpu, user will never have the opportunity to load the sev-snp.so. That's why remove the self-detect function as a registering gate-keeper.
I think user can invoke self-detect function to check the HW environment, but don't use self-detect as forcing gate-keeper in rats-tls.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't get your point. Why an user uses hardware agnostic key generation function from sev-snp? sev-snp only provides the support for its attester role or verifier role. If key generation function is so common that other instance wants to reuse it, does it make sense to move such a general function to the common part? In addition, your scenario actually exists? Does sev-snp instance really show enough versatility on key generation (not just a limited usage for serving itself)?

if (!new_opts)
return -ENCLAVE_ATTESTER_ERR_NO_MEM;
Expand Down
1 change: 1 addition & 0 deletions src/attesters/nullattester/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ static enclave_attester_opts_t nullattester_opts = {
.api_version = ENCLAVE_ATTESTER_API_VERSION_DEFAULT,
.flags = ENCLAVE_ATTESTER_FLAGS_DEFAULT,
.name = "nullattester",
.oid = "",
.priority = 0,
.pre_init = nullattester_pre_init,
.init = nullattester_init,
Expand Down
2 changes: 2 additions & 0 deletions src/attesters/sev-snp/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <stdio.h>
#include <rats-tls/attester.h>
#include <rats-tls/log.h>
#include "sev_snp.h"

extern enclave_attester_err_t enclave_attester_register(enclave_attester_opts_t *opts);
extern enclave_attester_err_t sev_snp_attester_pre_init(void);
Expand All @@ -22,6 +23,7 @@ static enclave_attester_opts_t sev_snp_attester_opts = {
.api_version = ENCLAVE_ATTESTER_API_VERSION_DEFAULT,
.flags = ENCLAVE_ATTESTER_OPTS_FLAGS_SNP_GUEST,
.name = "sev_snp",
.oid = SNP_REPORT_OID,
.priority = 42,
.pre_init = sev_snp_attester_pre_init,
.init = sev_snp_attester_init,
Expand Down
2 changes: 2 additions & 0 deletions src/attesters/sev-snp/sev_snp.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
* https://www.amd.com/system/files/TechDocs/56860.pdf for details.
*/

#define SNP_REPORT_OID "1.2.840.113741.1337.20"

/* 2.2 TCB Version
* A version string that represents the version of the firmware
*/
Expand Down
4 changes: 2 additions & 2 deletions src/attesters/sgx-ecdsa/collect_evidence.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ enclave_attester_err_t sgx_ecdsa_collect_evidence(enclave_attester_ctx_t *ctx,
return SGX_ECDSA_ATTESTER_ERR_CODE((int)qe3_ret);
}

sgx_status = ocall_qe_get_quote(&qe3_ret, &app_report, quote_size, evidence->ecdsa.quote);
sgx_status = ocall_qe_get_quote(&qe3_ret, &app_report, quote_size, evidence->evidence.report);
if (SGX_SUCCESS != sgx_status || ENCLAVE_ATTESTER_ERR_NONE != qe3_ret) {
RTLS_ERR("sgx_qe_get_quote(): 0x%04x, 0x%04x\n", sgx_status, qe3_ret);
return SGX_ECDSA_ATTESTER_ERR_CODE((int)qe3_ret);
Expand All @@ -128,7 +128,7 @@ enclave_attester_err_t sgx_ecdsa_collect_evidence(enclave_attester_ctx_t *ctx,
* format of quote as sgx_ecdsa.
*/
snprintf(evidence->type, sizeof(evidence->type), "%s", "sgx_ecdsa");
evidence->ecdsa.quote_len = quote_size;
evidence->evidence.report_len = quote_size;

return ENCLAVE_ATTESTER_ERR_NONE;
}
2 changes: 2 additions & 0 deletions src/attesters/sgx-ecdsa/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <stdio.h>
#include <rats-tls/attester.h>
#include <rats-tls/log.h>
#include "sgx_ecdsa.h"

extern enclave_attester_err_t enclave_attester_register(enclave_attester_opts_t *opts);
extern enclave_attester_err_t sgx_ecdsa_attester_pre_init(void);
Expand All @@ -24,6 +25,7 @@ static enclave_attester_opts_t sgx_ecdsa_attester_opts = {
.api_version = ENCLAVE_ATTESTER_API_VERSION_DEFAULT,
.flags = ENCLAVE_ATTESTER_OPTS_FLAGS_SGX_ENCLAVE,
.name = "sgx_ecdsa",
.oid = ECDSA_QUOTE_OID,
.priority = 52,
.pre_init = sgx_ecdsa_attester_pre_init,
.init = sgx_ecdsa_attester_init,
Expand Down
4 changes: 3 additions & 1 deletion src/attesters/sgx-ecdsa/sgx_ecdsa.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@

#include <sgx_urts.h>

#define ECDSA_QUOTE_OID "1.2.840.113741.1337.6"

typedef struct {
sgx_enclave_id_t eid;
} sgx_ecdsa_ctx_t;

#endif
#endif
4 changes: 2 additions & 2 deletions src/attesters/sgx-la/collect_evidence.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ enclave_attester_err_t sgx_la_collect_evidence(enclave_attester_ctx_t *ctx,
return SGX_LA_ATTESTER_ERR_CODE((int)generate_evidence_ret);
}

memcpy(evidence->la.report, &isv_report, sizeof(isv_report));
evidence->la.report_len = sizeof(isv_report);
memcpy(evidence->evidence.report, &isv_report, sizeof(isv_report));
evidence->evidence.report_len = sizeof(isv_report);

snprintf(evidence->type, sizeof(evidence->type), "%s", "sgx_la");

Expand Down
2 changes: 2 additions & 0 deletions src/attesters/sgx-la/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <stdio.h>
#include <rats-tls/attester.h>
#include <rats-tls/log.h>
#include "sgx_la.h"

extern enclave_attester_err_t enclave_attester_register(enclave_attester_opts_t *);
extern enclave_attester_err_t sgx_la_attester_pre_init(void);
Expand All @@ -22,6 +23,7 @@ static enclave_attester_opts_t sgx_la_attester_opts = {
.api_version = ENCLAVE_ATTESTER_API_VERSION_DEFAULT,
.flags = ENCLAVE_ATTESTER_OPTS_FLAGS_SGX_ENCLAVE,
.name = "sgx_la",
.oid = LA_REPORT_OID,
.priority = 15,
.pre_init = sgx_la_attester_pre_init,
.init = sgx_la_attester_init,
Expand Down
2 changes: 2 additions & 0 deletions src/attesters/sgx-la/sgx_la.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

#include "sgx_eid.h"

#define LA_REPORT_OID "1.2.840.113741.1337.14"

typedef struct {
sgx_enclave_id_t eid;
} sgx_la_ctx_t;
Expand Down
2 changes: 1 addition & 1 deletion src/attesters/tdx-ecdsa/cleanup.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

#include <rats-tls/log.h>
#include <rats-tls/attester.h>
#include "../../verifiers/tdx-ecdsa/tdx-ecdsa.h"
#include "../../verifiers/tdx-ecdsa/tdx_ecdsa.h"

enclave_attester_err_t tdx_ecdsa_attester_cleanup(enclave_attester_ctx_t *ctx)
{
Expand Down
8 changes: 4 additions & 4 deletions src/attesters/tdx-ecdsa/collect_evidence.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#include <rats-tls/log.h>
#include <rats-tls/attester.h>
#include <stddef.h>
#include "../../verifiers/tdx-ecdsa/tdx-ecdsa.h"
#include "../../verifiers/tdx-ecdsa/tdx_ecdsa.h"

#define VSOCK

Expand Down Expand Up @@ -88,8 +88,8 @@ enclave_attester_err_t tdx_ecdsa_collect_evidence(enclave_attester_ctx_t *ctx,
{
RTLS_DEBUG("ctx %p, evidence %p, algo %d, hash %p\n", ctx, evidence, algo, hash);

evidence->tdx.quote_len = sizeof(evidence->tdx.quote);
if (tdx_gen_quote(hash, evidence->tdx.quote, &evidence->tdx.quote_len)) {
evidence->evidence.report_len = sizeof(evidence->evidence.report);
if (tdx_gen_quote(hash, evidence->evidence.report, &evidence->evidence.report_len)) {
RTLS_ERR("failed to generate quote\n");
return -ENCLAVE_ATTESTER_ERR_INVALID;
}
Expand All @@ -101,7 +101,7 @@ enclave_attester_err_t tdx_ecdsa_collect_evidence(enclave_attester_ctx_t *ctx,
*/
snprintf(evidence->type, sizeof(evidence->type), "tdx_ecdsa");

RTLS_DEBUG("ctx %p, evidence %p, quote_size %d\n", ctx, evidence, evidence->tdx.quote_len);
RTLS_DEBUG("ctx %p, evidence %p, quote_size %d\n", ctx, evidence, evidence->evidence.report_len);

return ENCLAVE_ATTESTER_ERR_NONE;
}
2 changes: 1 addition & 1 deletion src/attesters/tdx-ecdsa/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#include <string.h>
#include <rats-tls/log.h>
#include <rats-tls/attester.h>
#include "../../verifiers/tdx-ecdsa/tdx-ecdsa.h"
#include "../../verifiers/tdx-ecdsa/tdx_ecdsa.h"

enclave_attester_err_t tdx_ecdsa_attester_init(enclave_attester_ctx_t *ctx,
rats_tls_cert_algo_t algo)
Expand Down
2 changes: 2 additions & 0 deletions src/attesters/tdx-ecdsa/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <stdio.h>
#include <rats-tls/attester.h>
#include <rats-tls/log.h>
#include "tdx_ecdsa.h"

extern enclave_attester_err_t enclave_attester_register(enclave_attester_opts_t *opts);
extern enclave_attester_err_t tdx_ecdsa_attester_pre_init(void);
Expand All @@ -22,6 +23,7 @@ static enclave_attester_opts_t tdx_ecdsa_attester_opts = {
.api_version = ENCLAVE_ATTESTER_API_VERSION_DEFAULT,
.flags = ENCLAVE_ATTESTER_OPTS_FLAGS_TDX_GUEST,
.name = "tdx_ecdsa",
.oid = TDX_QUOTE_OID,
.priority = 42,
.pre_init = tdx_ecdsa_attester_pre_init,
.init = tdx_ecdsa_attester_init,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <rats-tls/api.h>

#define TDX_NUM_RTMRS 4
#define TDX_QUOTE_OID "1.2.840.113741.1337.8"

typedef struct {
uint8_t mrowner[SHA384_HASH_SIZE];
Expand Down
32 changes: 15 additions & 17 deletions src/crypto_wrappers/openssl/gen_cert.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
#include <rats-tls/log.h>
#include <rats-tls/crypto_wrapper.h>
#include <rats-tls/oid.h>
#include <rats-tls/attester.h>
#include "openssl.h"
#include "internal/attester.h"

#define CERT_SERIAL_NUMBER 9527

Expand Down Expand Up @@ -145,26 +147,22 @@ crypto_wrapper_err_t openssl_gen_cert(crypto_wrapper_ctx_t *ctx, rats_tls_cert_a
if (!x509_extension_add(cert, ias_report_signature_oid, epid->ias_report_signature,
epid->ias_report_signature_len))
goto err;
} else if (!strcmp(cert_info->evidence.type, "sgx_ecdsa")) {
ecdsa_attestation_evidence_t *ecdsa = &cert_info->evidence.ecdsa;

if (!x509_extension_add(cert, ecdsa_quote_oid, ecdsa->quote, ecdsa->quote_len))
goto err;
} else if (!strcmp(cert_info->evidence.type, "sgx_la")) {
la_attestation_evidence_t *la = &cert_info->evidence.la;

if (!x509_extension_add(cert, la_report_oid, la->report, la->report_len))
goto err;
} else if (!strcmp(cert_info->evidence.type, "tdx_ecdsa")) {
tdx_attestation_evidence_t *tdx = &cert_info->evidence.tdx;
}

if (!x509_extension_add(cert, tdx_quote_oid, tdx->quote, tdx->quote_len))
enclave_attester_opts_t *opts = NULL;
for(int i = 0; i < registerd_enclave_attester_nums; ++i) {
opts = enclave_attesters_opts[i];
if (!opts) {
RTLS_DEBUG("registerd enclave_attesters_opts is null.\n");
goto err;
} else if (!strcmp(cert_info->evidence.type, "sev_snp")) {
snp_attestation_evidence_t *snp = &cert_info->evidence.snp;
}

if (!x509_extension_add(cert, snp_report_oid, snp->report, snp->report_len))
goto err;
if (!strcmp(cert_info->evidence.type, opts->name)) {
tee_attestation_evidence_t *evidence = &cert_info->evidence.evidence;
if (!x509_extension_add(cert, opts->oid, evidence->report, evidence->report_len))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't any places where an OID is referred and then you replace such a loop to find the OID. Actually ctx->attester->opts->oid is the fast path to get it right?

goto err;
break;
}
}

ret = -CRYPTO_WRAPPER_ERR_CERT;
Expand Down
2 changes: 2 additions & 0 deletions src/include/rats-tls/attester.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ typedef struct {
uint8_t api_version;
unsigned long flags;
const char name[ENCLAVE_ATTESTER_TYPE_NAME_SIZE];
const char oid[OID_LENGTH];
/* Different attester instances may generate the same format of attester,
* e.g, sgx_ecdsa and sgx_ecdsa_qve both generate the format "sgx_ecdsa".
* By default, the value of type equals to name.
Expand All @@ -42,6 +43,7 @@ typedef struct {
/* Optional */
enclave_attester_err_t (*pre_init)(void);
enclave_attester_err_t (*init)(enclave_attester_ctx_t *ctx, rats_tls_cert_algo_t algo);
enclave_attester_err_t (*tee_aware)(void);
enclave_attester_err_t (*extend_cert)(enclave_attester_ctx_t *ctx,
const rats_tls_cert_info_t *cert_info);
enclave_attester_err_t (*collect_evidence)(enclave_attester_ctx_t *ctx,
Expand Down
24 changes: 4 additions & 20 deletions src/include/rats-tls/cert.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#ifndef _ENCLAVE_CERT_H
#define _ENCLAVE_CERT_H

#define OID_LENGTH 64

typedef struct {
const unsigned char *organization;
const unsigned char *organization_unit;
Expand All @@ -24,34 +26,16 @@ typedef struct {
uint32_t ias_report_signature_len;
} attestation_verification_report_t;

typedef struct {
uint8_t quote[8192];
uint32_t quote_len;
} ecdsa_attestation_evidence_t;

typedef struct {
uint8_t report[8192];
uint32_t report_len;
} la_attestation_evidence_t;

typedef struct {
uint8_t quote[8192];
uint32_t quote_len;
} tdx_attestation_evidence_t;

typedef struct {
uint8_t report[8192];
uint32_t report_len;
} snp_attestation_evidence_t;
} tee_attestation_evidence_t;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This structure can be removed.


typedef struct {
char type[ENCLAVE_ATTESTER_TYPE_NAME_SIZE];
union {
attestation_verification_report_t epid;
ecdsa_attestation_evidence_t ecdsa;
la_attestation_evidence_t la;
tdx_attestation_evidence_t tdx;
snp_attestation_evidence_t snp;
tee_attestation_evidence_t evidence;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please change to

typedef struct {
	char type[ENCLAVE_ATTESTER_TYPE_NAME_SIZE];
	union {
		attestation_verification_report_t epid;
                struct {
                    uint8_t report[8192];
                    uint32_t report_len;
                };
	};
} attestation_evidence_t;

So we could avoid referring report with evidence->evidence.report. Now just use evidence->report. It looks more clear.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good advice!

};
} attestation_evidence_t;

Expand Down
4 changes: 0 additions & 4 deletions src/include/rats-tls/oid.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,5 @@
#define ias_root_cert_oid "1.2.840.113741.1337.3"
#define ias_leaf_cert_oid "1.2.840.113741.1337.4"
#define ias_report_signature_oid "1.2.840.113741.1337.5"
#define ecdsa_quote_oid "1.2.840.113741.1337.6"
#define la_report_oid "1.2.840.113741.1337.14"
#define tdx_quote_oid "1.2.840.113741.1337.8"
#define snp_report_oid "1.2.840.113741.1337.20"

#endif
1 change: 1 addition & 0 deletions src/include/rats-tls/verifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ typedef struct {
uint8_t api_version;
unsigned long flags;
const char name[ENCLAVE_VERIFIER_TYPE_NAME_SIZE];
const char oid[OID_LENGTH];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a newline here.

/* Different attester instances may generate the same format of verifier,
* e.g, sgx_ecdsa and sgx_ecdsa_qve both generate the format "sgx_ecdsa".
* By default, the value of type equals to name.
Expand Down
6 changes: 3 additions & 3 deletions src/sgx/untrust/sgx_ecdsa_ocall.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ enclave_verifier_err_t ocall_ecdsa_verify_evidence(__attribute__((unused))
return -ENCLAVE_VERIFIER_ERR_NO_MEM;
}

memcpy(pquote, evidence->ecdsa.quote, evidence->ecdsa.quote_len);
memcpy(pquote, evidence->evidence.report, evidence->evidence.report_len);

uint32_t quote_size = (uint32_t)sizeof(sgx_quote3_t) + pquote->signature_data_len;
RTLS_DEBUG("quote size is %d, quote signature_data_len is %d\n", quote_size,
Expand Down Expand Up @@ -141,7 +141,7 @@ enclave_verifier_err_t ocall_ecdsa_verify_evidence(__attribute__((unused))

current_time = time(NULL);

dcap_ret = sgx_qv_verify_quote(evidence->ecdsa.quote, (uint32_t)quote_size, NULL,
dcap_ret = sgx_qv_verify_quote(evidence->evidence.report, (uint32_t)quote_size, NULL,
current_time, &collateral_expiration_status,
&quote_verification_result, qve_report_info,
supplemental_data_size, p_supplemental_data);
Expand All @@ -155,7 +155,7 @@ enclave_verifier_err_t ocall_ecdsa_verify_evidence(__attribute__((unused))

if (!strcmp(name, "sgx_ecdsa_qve")) {
sgx_ret = sgx_tvl_verify_qve_report_and_identity(
enclave_id, &verify_qveid_ret, evidence->ecdsa.quote, (uint32_t)quote_size,
enclave_id, &verify_qveid_ret, evidence->evidence.report, (uint32_t)quote_size,
qve_report_info, current_time, collateral_expiration_status,
quote_verification_result, p_supplemental_data, supplemental_data_size,
qve_isvsvn_threshold);
Expand Down
4 changes: 2 additions & 2 deletions src/sgx/untrust/sgx_la_ocall.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ enclave_verifier_err_t ocall_la_verify_evidence(enclave_verifier_ctx_t *ctx,
RTLS_DEBUG("ctx %p, evidence %p, hash %p\n", ctx, evidence, hash);

/* Firstly verify hash value */
sgx_report_t *lreport = (sgx_report_t *)evidence->la.report;
sgx_report_t *lreport = (sgx_report_t *)evidence->evidence.report;

if (memcmp(hash, lreport->body.report_data.d, hash_len) != 0) {
RTLS_ERR("unmatched hash value in evidence\n");
Expand All @@ -45,7 +45,7 @@ enclave_verifier_err_t ocall_la_verify_evidence(enclave_verifier_ctx_t *ctx,
return SGX_LA_VERIFIER_ERR_CODE((int)qe3_ret);
}

qe3_ret = sgx_qe_get_quote((sgx_report_t *)evidence->la.report, quote_size, quote);
qe3_ret = sgx_qe_get_quote((sgx_report_t *)evidence->evidence.report, quote_size, quote);
if (SGX_QL_SUCCESS != qe3_ret) {
RTLS_ERR("failed to get quote %04x\n", qe3_ret);
return SGX_LA_VERIFIER_ERR_CODE((int)qe3_ret);
Expand Down
Loading