Opcode/Instruction | Op/En | 64/32 bit Mode Support | CPUID Feature Flag | Description |
---|---|---|---|---|
EAX = 02H ENCLS[EINIT] | IR | V/V | SGX1 | This leaf function initializes the enclave and makes it ready to execute enclave code. |
Op/En | EAX | RBX | RCX | RDX | |
IR | EINIT (In) | Error code (Out) | Address of SIGSTRUCT (In) | Address of SECS (In) | Address of EINITTOKEN (In) |
This leaf function is the final instruction executed in the enclave build process. After EINIT, the MRENCLAVE measurement is complete, and the enclave is ready to start user code execution using the EENTER instruction.
EINIT takes the effective address of a SIGSTRUCT and EINITTOKEN. The SIGSTRUCT describes the enclave including MRENCLAVE, ATTRIBUTES, ISVSVN, a 3072 bit RSA key, and a signature using the included key. SIGSTRUCT must be populated with two values, q1 and q2. These are calculated using the formulas shown below:
q1 = floor(Signature2 / Modulus);
q2 = floor((Signature3 - q1 * Signature * Modulus) / Modulus);
The EINITTOKEN contains the MRENCLAVE, MRSIGNER, and ATTRIBUTES. These values must match the corresponding values in the SECS. If the EINITTOKEN was created with a debug launch key, the enclave must be in debug mode as well.
SIGSTRUCT
SECS | EINITTOKEN |
Read/Write access by Enclave | Access by non-Enclave |
Access by non-Enclave
EINIT performs the following steps, which can be seen in Figure 38-1:
1. Validates that SIGSTRUCT is signed using the enclosed public key.
2. Checks that the completed computation of SECS.MRENCLAVE equals SIGSTRUCT.HASHENCLAVE.
3. Checks that no controlled ATTRIBUTES bits are set in SIGSTRUCT.ATTRIBUTES unless the SHA256 digest of SIGSTRUCT.MODULUS equals IA32_SGX_LEPUBKEYHASH.
4. Checks that the result of bitwise and-ing SIGSTRUCT.ATTRIBUTEMASK with SIGSTRUCT.ATTRIBUTES equals the result of bitwise and-ing SIGSTRUCT.ATTRIBUTEMASK with SECS.ATTRIBUTES.
5. If EINITTOKEN.VALID is 0, checks that the SHA256 digest of SIGSTRUCT.MODULUS equals IA32_SGX_LEPUBKEYHASH.
6. If EINITTOKEN.VALID is 1, checks the validity of EINITTOKEN.
7. If EINITTOKEN.VALID is 1, checks that EINITTOKEN.MRENCLAVE equals SECS.MRENCLAVE.
8. If EINITTOKEN.VALID is 1 and EINITTOKEN.ATTRIBUTES.DEBUG is 1, SECS.ATTRIBUTES.DEBUG must be 1.
9. Commits SECS.MRENCLAVE, and sets SECS.MRSIGNER, SECS.ISVSVN, and SECS.ISVPRODID based on SIGSTRUCT.
10. Update the SECS as Initialized.
Periodically, EINIT polls for certain asynchronous events. If such an event is detected, it completes with failure code (ZF=1 and RAX = SGX_UNMASKED_EVENT), and RIP is incremented to point to the next instruction. These events includes external interrupts, non-maskable interrupts, system-management interrupts, machine checks, INIT signals, and the VMX-preemption timer. EINIT does not fail if the pending event is inhibited (e.g., external interrupts could be inhibited due to blocking by MOV SS blocking or by STI).
The following bits in RFLAGS are cleared: CF, PF, AF, OF, and SF. When the instruction completes with an error, RFLAGS.ZF is set to 1, and the corresponding error bit is set in RAX. If no error occurs, RFLAGS.ZF is cleared and RAX is set to 0.
The error codes are:
Error Code (see Table 38-4) | Description |
---|---|
No Error | EINIT successful. |
SGX_INVALID_SIG_STRUCT | If SIGSTRUCT contained an invalid value. |
SGX_INVALID_ATTRIBUTE | If SIGSTRUCT contains an unauthorized attributes mask. |
SGX_INVALID_MEASUREMENT | If SIGSTRUCT contains an incorrect measurement. If EINITTOKEN contains an incorrect measurement. |
SGX_INVALID_SIGNATURE | If signature does not validate with enclosed public key. |
SGX_INVALID_LICENSE | If license is invalid. |
SGX_INVALID_CPUSVN | If license SVN is unsupported. |
SGX_UNMASKED_EVENT | If an unmasked event is received before the instruction completes its operation. |
Leaf | Parameter | Base Concurrency Restrictions | ||
---|---|---|---|---|
On Conflict | ||||
EINIT EINIT SECS [DS:RCX] Shared EINIT SECS [DS:RCX] | SECS [DS:RCX] |
Leaf Access On Conflict EINIT SECS [DS:RCX] Concurrent Exclusive Access On Conflict EINIT SECS [DS:RCX] Concurrent | Parameter | Additional Concurrency Restrictions | |||||
vs. EACCEPT, EACCEPTCOPY, vs. EADD, EEXTEND, EINIT vs. ETRACK, ETRACKC Access vs. ETRACK, ETRACKC Access On Conflict Access vs. ETRACK, ETRACKC Access On Conflict EMODPE, EMODPR, EMODT | vs. EADD, EEXTEND, EINIT vs. EADD, EEXTEND, EINIT vs. ETRACK, ETRACKC | vs. ETRACK, ETRACKC | |||||
Access On Conflict Access On Conflict Access Access On Conflict Access On Conflict | |||||||
EINIT | SECS [DS:RCX] | Concurrent | Concurrent |
Name | Type | Size | Description |
---|---|---|---|
TMP_SIG | SIGSTRUCT | 1808Bytes | Temp space for SIGSTRUCT. |
TMP_TOKEN | EINITTOKEN | 304Bytes | Temp space for EINITTOKEN. |
TMP_MRENCLAVE | 32Bytes | Temp space for calculating MRENCLAVE. | |
TMP_MRSIGNER | 32Bytes | Temp space for calculating MRSIGNER. | |
CONTROLLED_ATTRIBU TES | ATTRIBUTES | 16Bytes | Constant mask of all ATTRIBUTE bits that can only be set for authorized enclaves. |
TMP_KEYDEPENDENCIE S | Buffer | 224Bytes | Temp space for key derivation. |
TMP_EINITTOKENKEY | 16Bytes | Temp space for the derived EINITTOKEN Key. | |
TMP_SIG_PADDING | PKCS Padding Buffer | 352Bytes | The value of the top 352 bytes from the computation of Signature3 modulo MRSIGNER. |
(* make sure SIGSTRUCT and SECS are aligned *)
IF ( (DS:RBX is not 4KByte Aligned) or (DS:RCX is not 4KByte Aligned) )
THEN #GP(0); FI;
(* make sure the EINITTOKEN is aligned *)
IF (DS:RDX is not 512Byte Aligned)
THEN #GP(0); FI;
(* make sure the SECS is inside the EPC *)
IF (DS:RCX does not resolve within an EPC)
THEN #PF(DS:RCX); FI;
TMP_SIG[14463:0] := DS:RBX[14463:0]; // 1808 bytes
TMP_TOKEN[2423:0] := DS:RDX[2423:0]; // 304 bytes
(* Verify SIGSTRUCT Header. *)
IF ( (TMP_SIG.HEADER ≠ 06000000E10000000000010000000000h) or
((TMP_SIG.VENDOR ≠ 0) and (TMP_SIG.VENDOR ≠ 00008086h) ) or
(TMP_SIG HEADER2 ≠ 01010000600000006000000001000000h) or
(TMP_SIG.EXPONENT ≠ 00000003h) or (Reserved space is not 0’s) )
THEN
RFLAGS.ZF := 1;
RAX := SGX_INVALID_SIG_STRUCT;
GOTO EXIT;
FI;
(* Open “Event Window” Check for Interrupts. Verify signature using embedded public key, q1, and q2. Save upper 352 bytes of the PKCS1.5 encoded message into the TMP_SIG_PADDING*)
IF (interrupt was pending) THEN
RFLAGS.ZF := 1;
RAX := SGX_UNMASKED_EVENT;
GOTO EXIT;
FI
IF (signature failed to verify) THEN
RFLAGS.ZF := 1;
RAX := SGX_INVALID_SIGNATURE;
GOTO EXIT;
FI;
(*Close “Event Window” *)
(* make sure no other Intel SGX instruction is modifying SECS*)
IF (Other instructions modifying SECS)
THEN #GP(0); FI;
IF ( (EPCM(DS:RCX). VALID = 0) or (EPCM(DS:RCX).PT ≠ PT_SECS) )
THEN #PF(DS:RCX); FI;
(* Verify ISVFAMILYID is not used on an enclave with KSS disabled *)
IF ((TMP_SIG.ISVFAMILYID != 0) AND (DS:RCX.ATTRIBUTES.KSS == 0))
THEN
RFLAGS.ZF := 1;
RAX := SGX_INVALID_SIG_STRUCT;
GOTO EXIT;
FI;
(* make sure no other instruction is accessing MRENCLAVE or ATTRIBUTES.INIT *)
IF ( (Other instruction modifying MRENCLAVE) or (Other instructions modifying the SECS’s Initialized state))
THEN #GP(0); FI;
(* Calculate finalized version of MRENCLAVE *)
(* SHA256 algorithm requires one last update that compresses the length of the hashed message into the output SHA256 digest *)
TMP_ENCLAVE := SHA256FINAL( (DS:RCX).MRENCLAVE, enclave’s MRENCLAVE update count *512);
(* Verify MRENCLAVE from SIGSTRUCT *)
IF (TMP_SIG.ENCLAVEHASH ≠ TMP_MRENCLAVE)
RFLAGS.ZF := 1;
RAX := SGX_INVALID_MEASUREMENT;
GOTO EXIT;
TMP_MRSIGNER := SHA256(TMP_SIG.MODULUS)
(* if controlled ATTRIBUTES are set, SIGSTRUCT must be signed using an authorized key *)
CONTROLLED_ATTRIBUTES := 0000000000000020H;
IF ( ( (DS:RCX.ATTRIBUTES & CONTROLLED_ATTRIBUTES) ≠ 0) and (TMP_MRSIGNER ≠ IA32_SGXLEPUBKEYHASH) )
RFLAGS.ZF := 1;
RAX := SGX_INVALID_ATTRIBUTE;
GOTO EXIT;
FI;
(* Verify SIGSTRUCT.ATTRIBUTE requirements are met *)
IF ( (DS:RCX.ATTRIBUTES & TMP_SIG.ATTRIBUTEMASK) ≠ (TMP_SIG.ATTRIBUTE & TMP_SIG.ATTRIBUTEMASK) )
RFLAGS.ZF := 1;
RAX := SGX_INVALID_ATTRIBUTE;
GOTO EXIT;
FI;
( *Verify SIGSTRUCT.MISCSELECT requirements are met *)
IF ( (DS:RCX.MISCSELECT & TMP_SIG.MISCMASK) ≠ (TMP_SIG.MISCSELECT & TMP_SIG.MISCMASK) )
THEN
RFLAGS.ZF := 1;
RAX := SGX_INVALID_ATTRIBUTE;
GOTO EXIT
FI;
IF (CPUID.(EAX=12H, ECX=1):EAX[6] = 1)
IF ( DS:RCX.CET_ATTRIBUTES & TMP_SIG.CET_ATTRIBUTES_MASK ≠ TMP_SIG.CET_ATTRIBUTES &
TMP_SIG.CET_ATTRIB-UTES_MASK )
THEN
RFLAGS.ZF := 1;
RAX := SGX_INVALID_ATTRIBUTE;
GOTO EXIT
FI;
FI;
(* If EINITTOKEN.VALID[0] is 0, verify the enclave is signed by an authorized key *)
IF (TMP_TOKEN.VALID[0] = 0)
IF (TMP_MRSIGNER ≠ IA32_SGXLEPUBKEYHASH)
RFLAGS.ZF := 1;
RAX := SGX_INVALID_EINITTOKEN;
GOTO EXIT;
FI;
GOTO COMMIT;
FI;
(* Debug Launch Enclave cannot launch Production Enclaves *)
IF ( (DS:RDX.MASKEDATTRIBUTESLE.DEBUG = 1) and (DS:RCX.ATTRIBUTES.DEBUG = 0) )
RFLAGS.ZF := 1;
RAX := SGX_INVALID_EINITTOKEN;
GOTO EXIT;
(* Check reserve space in EINIT token includes reserved regions and upper bits in valid field *)
IF (TMP_TOKEN reserved space is not clear)
RFLAGS.ZF := 1;
RAX := SGX_INVALID_EINITTOKEN;
GOTO EXIT;
FI;
(* EINIT token must not have been created by a configuration beyond the current CPU configuration *)
IF (TMP_TOKEN.CPUSVN must not be a configuration beyond CR_CPUSVN)
RFLAGS.ZF := 1;
RAX := SGX_INVALID_CPUSVN;
GOTO EXIT;
FI;
(* Derive Launch key used to calculate EINITTOKEN.MAC *)
HARDCODED_PKCS1_5_PADDING[15:0] := 0100H;
HARDCODED_PKCS1_5_PADDING[2655:16] := SignExtend330Byte(-1); // 330 bytes of 0FFH
HARDCODED_PKCS1_5_PADDING[2815:2656] := 2004000501020403650148866009060D30313000H;
TMP_KEYDEPENDENCIES.KEYNAME := EINITTOKEN_KEY;
TMP_KEYDEPENDENCIES.ISVFAMILYID := 0;
TMP_KEYDEPENDENCIES.ISVEXTPRODID := 0;
TMP_KEYDEPENDENCIES.ISVPRODID := TMP_TOKEN.ISVPRODIDLE;
TMP_KEYDEPENDENCIES.ISVSVN := TMP_TOKEN.ISVSVNLE;
TMP_KEYDEPENDENCIES.SGXOWNEREPOCH := CR_SGXOWNEREPOCH;
TMP_KEYDEPENDENCIES.ATTRIBUTES := TMP_TOKEN.MASKEDATTRIBUTESLE;
TMP_KEYDEPENDENCIES.ATTRIBUTESMASK := 0;
TMP_KEYDEPENDENCIES.MRENCLAVE := 0;
TMP_KEYDEPENDENCIES.MRSIGNER := IA32_SGXLEPUBKEYHASH;
TMP_KEYDEPENDENCIES.KEYID := TMP_TOKEN.KEYID;
TMP_KEYDEPENDENCIES.SEAL_KEY_FUSES := CR_SEAL_FUSES;
TMP_KEYDEPENDENCIES.CPUSVN := TMP_TOKEN.CPUSVNLE;
TMP_KEYDEPENDENCIES.MISCSELECT := TMP_TOKEN.MASKEDMISCSELECTLE;
TMP_KEYDEPENDENCIES.MISCMASK := 0;
TMP_KEYDEPENDENCIES.PADDING := HARDCODED_PKCS1_5_PADDING;
TMP_KEYDEPENDENCIES.KEYPOLICY := 0;
TMP_KEYDEPENDENCIES.CONFIGID := 0;
TMP_KEYDEPENDENCIES.CONFIGSVN := 0;
IF (CPUID.(EAX=12H, ECX=1):EAX[6] = 1))
TMP_KEYDEPENDENCIES.CET_ATTRIBUTES := TMP_TOKEN.CET_MASKED_ATTRIBUTES_ LE;
TMP_KEYDEPENDENCIES.CET_ATTRIBUTES_MASK := 0;
FI;
(* Calculate the derived key*)
TMP_EINITTOKENKEY := derivekey(TMP_KEYDEPENDENCIES);
(* Verify EINITTOKEN was generated using this CPU's Launch key and that it has not been modified since issuing by the Launch Enclave. Only 192 bytes of EINITTOKEN are CMACed *)
IF (TMP_TOKEN.MAC ≠ CMAC(TMP_EINITTOKENKEY, TMP_TOKEN[1535:0] ) )
RFLAGS.ZF := 1;
RAX := SGX_INVALID_EINITTOKEN;
GOTO EXIT;
FI;
(* Verify EINITTOKEN (RDX) is for this enclave *)
IF ( (TMP_TOKEN.MRENCLAVE ≠ TMP_MRENCLAVE) or (TMP_TOKEN.MRSIGNER ≠ TMP_MRSIGNER) )
RFLAGS.ZF := 1;
RAX := SGX_INVALID_MEASUREMENT;
GOTO EXIT;
FI;
(* Verify ATTRIBUTES in EINITTOKEN are the same as the enclave’s *)
IF (TMP_TOKEN.ATTRIBUTES ≠ DS:RCX.ATTRIBUTES)
RFLAGS.ZF := 1;
RAX := SGX_INVALID_EINIT_ATTRIBUTE;
GOTO EXIT;
FI;
COMMIT:
(* Commit changes to the SECS; Set ISVPRODID, ISVSVN, MRSIGNER, INIT ATTRIBUTE fields in SECS (RCX) *)
DS:RCX.MRENCLAVE := TMP_MRENCLAVE;
(* MRSIGNER stores a SHA256 in little endian implemented natively on x86 *)
DS:RCX.MRSIGNER := TMP_MRSIGNER;
DS:RCX.ISVEXTPRODID := TMP_SIG.ISVEXTPRODID;
DS:RCX.ISVPRODID := TMP_SIG.ISVPRODID;
DS:RCX.ISVSVN := TMP_SIG.ISVSVN;
DS:RCX.ISVFAMILYID := TMP_SIG.ISVFAMILYID;
DS:RCX.PADDING := TMP_SIG_PADDING;
(* Mark the SECS as initialized *)
Update DS:RCX to initialized;
(* Set RAX and ZF for success*)
RFLAGS.ZF := 0;
RAX := 0;
EXIT:
RFLAGS.CF,PF,AF,OF,SF := 0;
ZF is cleared if successful, otherwise ZF is set and RAX contains the error code. CF, PF, AF, OF, SF are cleared.
#GP(0) | If a memory operand is not properly aligned. |
If another instruction is modifying the SECS. | |
If the enclave is already initialized. | |
If the SECS.MRENCLAVE is in use. | |
#PF(error | code) If a page fault occurs in accessing memory operands. |
If RCX does not resolve in an EPC page. | |
If the memory address is not a valid, uninitialized SECS. |
#GP(0) | If a memory operand is not properly aligned. |
If another instruction is modifying the SECS. | |
If the enclave is already initialized. | |
If the SECS.MRENCLAVE is in use. | |
#PF(error | code) If a page fault occurs in accessing memory operands. |
If RCX does not resolve in an EPC page. | |
If the memory address is not a valid, uninitialized SECS. |