intel / intel/confidential-computing.sgx.sdk

Remote Attestation: isv_app yields 0x2003 (Signature invalid) while processing msg2

Open
#70 5 comments 1 reaction 0 assignees View on GitHub
question
Dominant language
C++
Stars
2
Forks
3
PR merge metrics
No merged PRs in 30d

Description

I would like to implement Service Provider functionality in a Node JS server. However, even though I think I follow the algorithm in the RA Sample, the SGX-enabled client (in my case, isv_app from RemoteAttestation project in sgxsdk SampleCode) does not accept MSG2 from the SP, yielding **0x2003 error** (invalid signature), returned by **sgx_ra_proc_msg2_ex** function.

The following JS code is the SP functionality to process msg1 and generate msg2:

```const crypto = require('crypto');
const aesCmac = require('node-aes-cmac').aesCmac;
const EC = require('elliptic').ec;
const ec = new EC('p256');
const pepECDH = crypto.createECDH('prime256v1');

const SPID = 'C8C56121818E4F00FE4AB868389F3059';
const X_LENGTH = 32;
const Y_LENGTH = 64;

//Utilities
const swap = function(l_index, h_index, array) {
let aux;
while (l_index < h_index) {
aux = array[l_index];
array[l_index] = array[h_index];
array[h_index] = aux;
l_index++;
h_index--;
}
};

const changeKeyEndianess = function(key) {
swap(0, X_LENGTH - 1, key);
swap(X_LENGTH, Y_LENGTH - 1, key);
};

const generateCMAC = function(key, data) {
const options = { returnAsBuffer: true };
return aesCmac(key, data, options);
};

const deriveKey = function(sharedKey, label) {
swap(0, sharedKey.length - 1, sharedKey);
const key0s = Buffer.from('00000000000000000000000000000000', 'hex');

const cmacKey0s = generateCMAC(key0s, Buffer.from(sharedKey));
//0x01 || SMK || 0x00 || 0x80 || 0x00
const auxInfo = Buffer.concat([
Buffer.from('01', 'hex'),
Buffer.from(label),
Buffer.from('008000', 'hex'),
]);
const derivedKey = generateCMAC(cmacKey0s, auxInfo);
return derivedKey;
};

//1. Generate a random EC key using the P-256 curve. This key will become Gb.
pepECDH.generateKeys();
var Gb_BE = pepECDH.getPublicKey().slice(1); //remove 0x04, used only for elliptic library internals
var Gb_LE = Gb_BE;
changeKeyEndianess(Gb_LE); //pep public key now in Little Endian
//2. Derive the key derivation key (KDK) from Ga and Gb:
//2.1. Compute the shared secret using the client's public session key, Ga,
//and the service provider's private session key (obtained from Step 1), Gb.
// The result of this operation will be the x coordinate of Gab, denoted as Gabx.
var Ga_raw = msg.slice(0, 64); //client's public key, Little Endian
var Ga_LE = Buffer.from(Ga_raw);
let Ga_BE = Ga_LE;
changeKeyEndianess(Ga_BE); //done for computeSecret function (to Big Endian)
Ga_BE = [0x04, ...Ga_BE]; //the elliptic lib requires 0x04 at the beginning of the key to work properly
var Gabx_BE = pepECDH.computeSecret(new Buffer(Ga_BE));
//2.2. Convert Gabx to little-endian byte order by reversing its bytes.
var Gabx_LE = Gabx_BE;
changeKeyEndianess(Gabx_LE); //now in little-endian
//2.3. Perform an AES-128 CMAC on the little-endian form of Gabx
//using a block of 0x00 bytes for the key.
//Derive the SMK from the KDK by performing an AES-128 CMAC on the byte sequence:
//0x01 || SMK || 0x00 || 0x80 || 0x00 --> SMK is a literal string without quotes
var derivedKeySMK = deriveKey(Gabx_LE, 'SMK');
//1. Determine the quote type that should be requested from the client
//(0x0 for unlinkable, and 0x1 for linkable). Note that this is a service provider
//policy decision, and the SPID must be associated with the correct quote type.
var quoteType = Buffer.from('0000', 'hex');
//2. Set the KDF_ID. Normally this is 0x1.
var kdfID = Buffer.from('0100', 'hex');
var spid = Buffer.from(SPID, 'hex');
//3. Calculate the ECDSA signature of:
//Gbx || Gby || Gax || Gay
//(traditionally written as r || s) with the service provider's EC private key.
var concatenatedPublicKeys = Buffer.concat([Gb_LE, Ga_LE]);
var pepPrivateKey = pepECDH.getPrivateKey();
var keysHash = crypto
.createHash('sha256')
.update(concatenatedPublicKeys)
.digest();

var SigSP_BE = ec.sign(keysHash, pepPrivateKey, { canonical: true });
var r = SigSP_BE.r.toBuffer('le', 32);
var s = SigSP_BE.s.toBuffer('le', 32);
var SigSP_LE = Buffer.concat([r, s]);
//4. Calculate the AES-128 CMAC of:
//Gb || SPID || Quote_Type || KDF_ID || SigSP
//using the SMK (derived in Step 3) as the key.
var toCmac = Buffer.concat([Gb_LE, spid, quoteType, kdfID, SigSP_LE]);
var Cmaced = generateCMAC(derivedKeySMK, toCmac);```

```
Then, the SP contacts IAS and receives code 200 with empty SigRL. Here comes the isv_app output (msg2 printed out after parsing):

```
Sent MSG1 to remote attestation service provider. Received the following MSG2:
176 bytes:
{
0x2, 0x0, 0x0, 0xa8, 0x0, 0x0, 0x0, 0x0,
0x7b, 0x7f, 0xe3, 0x71, 0x6, 0x6f, 0xf2, 0xd4,
0x86, 0x14, 0xa6, 0x87, 0x1a, 0x50, 0x5f, 0x9b,
0x13, 0x11, 0x11, 0x7a, 0x73, 0xaa, 0x8a, 0x80,
0xad, 0xbc, 0xcb, 0x5a, 0xdb, 0xf4, 0x77, 0x2f,
0x5c, 0xb9, 0x12, 0xf3, 0x20, 0x47, 0x34, 0x88,
0x1, 0x75, 0x46, 0xab, 0xb1, 0x24, 0x8d, 0x49,
0xa0, 0xab, 0x73, 0xf9, 0xe6, 0x43, 0x74, 0x4b,
0xe9, 0x1b, 0x5b, 0xba, 0x67, 0xd, 0x5, 0x16,
0xc8, 0xc5, 0x61, 0x21, 0x81, 0x8e, 0x4f, 0x0,
0xfe, 0x4a, 0xb8, 0x68, 0x38, 0x9f, 0x30, 0x59,
0x0, 0x0, 0x1, 0x0, 0xea, 0x50, 0x1c, 0xcb,
0x72, 0xa5, 0x59, 0xb3, 0x35, 0x46, 0xa3, 0x4d,
0xd1, 0xe1, 0xf8, 0xb2, 0x17, 0x8e, 0xea, 0x40,
0xd0, 0x65, 0xc3, 0x54, 0xc, 0x67, 0xc9, 0x10,
0x34, 0xcc, 0x6f, 0x49, 0x11, 0xa9, 0x90, 0x91,
0xba, 0x63, 0x6a, 0x70, 0xd5, 0xed, 0xe2, 0xa4,
0x3c, 0x57, 0x87, 0x84, 0x92, 0x13, 0xc0, 0x5e,
0x28, 0x28, 0x5e, 0x46, 0x31, 0xf8, 0x80, 0x84,
0x7, 0xaa, 0x99, 0x0, 0xb5, 0x3f, 0x5d, 0x23,
0x9f, 0x55, 0x6b, 0x28, 0x9, 0x8, 0xe9, 0x5d,
0xf6, 0x4a, 0x9a, 0xfe, 0x0, 0x0, 0x0, 0x0
}

A more descriptive representation of MSG2:
RESPONSE TYPE: 0x2
RESPONSE STATUS: 0x0 0x0
RESPONSE BODY SIZE: 168
MSG2 gb - 64 bytes:
{
0x7b, 0x7f, 0xe3, 0x71, 0x6, 0x6f, 0xf2, 0xd4,
0x86, 0x14, 0xa6, 0x87, 0x1a, 0x50, 0x5f, 0x9b,
0x13, 0x11, 0x11, 0x7a, 0x73, 0xaa, 0x8a, 0x80,
0xad, 0xbc, 0xcb, 0x5a, 0xdb, 0xf4, 0x77, 0x2f,
0x5c, 0xb9, 0x12, 0xf3, 0x20, 0x47, 0x34, 0x88,
0x1, 0x75, 0x46, 0xab, 0xb1, 0x24, 0x8d, 0x49,
0xa0, 0xab, 0x73, 0xf9, 0xe6, 0x43, 0x74, 0x4b,
0xe9, 0x1b, 0x5b, 0xba, 0x67, 0xd, 0x5, 0x16
}
MSG2 spid - 16 bytes:
{
0xc8, 0xc5, 0x61, 0x21, 0x81, 0x8e, 0x4f, 0x0,
0xfe, 0x4a, 0xb8, 0x68, 0x38, 0x9f, 0x30, 0x59
}
MSG2 quote_type : 0
MSG2 kdf_id : 1
MSG2 sign_gb_ga - 64 bytes:
{
0xea, 0x50, 0x1c, 0xcb, 0x72, 0xa5, 0x59, 0xb3,
0x35, 0x46, 0xa3, 0x4d, 0xd1, 0xe1, 0xf8, 0xb2,
0x17, 0x8e, 0xea, 0x40, 0xd0, 0x65, 0xc3, 0x54,
0xc, 0x67, 0xc9, 0x10, 0x34, 0xcc, 0x6f, 0x49,
0x11, 0xa9, 0x90, 0x91, 0xba, 0x63, 0x6a, 0x70,
0xd5, 0xed, 0xe2, 0xa4, 0x3c, 0x57, 0x87, 0x84,
0x92, 0x13, 0xc0, 0x5e, 0x28, 0x28, 0x5e, 0x46,
0x31, 0xf8, 0x80, 0x84, 0x7, 0xaa, 0x99, 0x0
}
MSG2 mac - 16 bytes:
{
0xb5, 0x3f, 0x5d, 0x23, 0x9f, 0x55, 0x6b, 0x28,
0x9, 0x8, 0xe9, 0x5d, 0xf6, 0x4a, 0x9a, 0xfe
}
MSG2 sig_rl -
( null )

Error, call sgx_ra_proc_msg2_ex fail. p_msg3 = 0x(nil) [main]. Ret: 0x00002003
Call enclave_ra_close success.
Enter a character before exit ...
```

I have already posted this problem on Intel Forum, where I learned that I am preparing the MSG2 correctly and Stack Overflow (no answers so far). I am really out of ideas what to do; the fields are passed correctly, only the signature does not pass the check. Maybe there is a bug in sgx_ra_proc_msg2_ex function?

Thanks for help.
Regards,
Kris

Contributor guide

Open the contributing guide

Research direction

Start with the Node.js MSG2 construction shown in the issue and the RemoteAttestation SampleCode path that calls sgx_ra_proc_msg2_ex. Compare the generated gb, sign_gb_ga, and mac fields with the sample's expected inputs, then reproduce the isv_app run until the signature-invalid error is resolved.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, javascript, node.js
Domain
backend, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.