|
| 1 | +/* |
| 2 | + * Copyright (C) 2024 Intel Corporation. |
| 3 | + * Copyright (C) 2024 University of Neuchatel, Switzerland. |
| 4 | + * |
| 5 | + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | + */ |
| 7 | + |
| 8 | +using System.Diagnostics; |
| 9 | +using System.Runtime.InteropServices; |
| 10 | +using System.Security.Cryptography; |
| 11 | +using System.Text; |
| 12 | +using System.Text.Json; |
| 13 | + |
| 14 | +// Set the reference values below |
| 15 | +byte[] mrEnclaveReference = |
| 16 | +{ |
| 17 | + 0xDA, 0xE0, 0xDA, 0x2F, 0x8A, 0x53, 0xA0, 0xB4, 0x8F, 0x92, 0x6A, 0x3B, 0xC0, 0x48, 0xD6, 0xA9, |
| 18 | + 0x67, 0xD4, 0x7C, 0x86, 0x19, 0x86, 0x76, 0x6F, 0x8F, 0x5A, 0xB1, 0xC0, 0xA8, 0xD8, 0x8E, 0x44 |
| 19 | +}; |
| 20 | +byte[] mrSignerReference = |
| 21 | +{ |
| 22 | + 0x83, 0xD7, 0x19, 0xE7, 0x7D, 0xEA, 0xCA, 0x14, 0x70, 0xF6, 0xBA, 0xF6, 0x2A, 0x4D, 0x77, 0x43, |
| 23 | + 0x03, 0xC8, 0x99, 0xDB, 0x69, 0x02, 0x0F, 0x9C, 0x70, 0xEE, 0x1D, 0xFC, 0x08, 0xC7, 0xCE, 0x9E |
| 24 | +}; |
| 25 | +const ushort securityVersionReference = 0; |
| 26 | +const ushort productIdReference = 0; |
| 27 | +string nonce = "This is a sample.\0"; // Notice the \0 at the end, which is mandatory as C-strings are terminated with this char |
| 28 | +string evidenceAsString = """{"type":"sgx_ecdsa","report_base64":"[..]","report_len":[..]}"""; |
| 29 | +string wasmFilePath = "../build/wasm-app/test.wasm"; |
| 30 | + |
| 31 | +// Parse and compute the claims |
| 32 | +EvidenceJson? evidenceAsJson = JsonSerializer.Deserialize<EvidenceJson>(evidenceAsString, new JsonSerializerOptions |
| 33 | +{ |
| 34 | + PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower |
| 35 | +}); |
| 36 | +Debug.Assert(evidenceAsJson != null, "The evidence cannot be parsed."); |
| 37 | + |
| 38 | +byte[] wasmFileContent = await File.ReadAllBytesAsync(wasmFilePath); |
| 39 | +byte[] nonceAsBytes = Encoding.UTF8.GetBytes(nonce); |
| 40 | +byte[] computedUserData = await ComputeUserData(wasmFileContent, nonceAsBytes); |
| 41 | +byte[] evidenceAsBytes = Convert.FromBase64String(evidenceAsJson.ReportBase64); |
| 42 | +Evidence evidence = new(evidenceAsBytes); |
| 43 | +int libRatsReturnValue = LibRats.VerifyEvidenceFromJson(evidenceAsString, await ComputeUserData(wasmFileContent, nonceAsBytes)); |
| 44 | + |
| 45 | +// Compare and display the results |
| 46 | +Console.WriteLine($"User data, evidence: {BitConverter.ToString(evidence.UserData)}"); |
| 47 | +Console.WriteLine($"User Data, computed: {BitConverter.ToString(computedUserData)}"); |
| 48 | +Console.WriteLine($"Do the two user data match? {evidence.UserData.SequenceEqual(computedUserData)}"); |
| 49 | +Console.WriteLine($"MrEnclave: {BitConverter.ToString(evidence.MrEnclave)}"); |
| 50 | +Console.WriteLine($"Do the MrEnclave match? {mrEnclaveReference.SequenceEqual(evidence.MrEnclave)}"); |
| 51 | +Console.WriteLine($"MrSigner: {BitConverter.ToString(evidence.MrSigner)}"); |
| 52 | +Console.WriteLine($"Do the MrSigner match? {mrSignerReference.SequenceEqual(evidence.MrSigner)}"); |
| 53 | +Console.WriteLine($"Security Version: {evidence.SecurityVersion}, expected: {securityVersionReference}"); |
| 54 | +Console.WriteLine($"Product ID: {evidence.ProductId}, expected: {productIdReference}"); |
| 55 | +Console.WriteLine($"VerifyJsonUsingLibrats returned: {libRatsReturnValue:X}"); |
| 56 | + |
| 57 | +// Compute the user data as computed by WAMR |
| 58 | +static async ValueTask<byte[]> ComputeUserData(byte[] wasmFileContent, byte[] nonce) |
| 59 | +{ |
| 60 | + using var sha256 = SHA256.Create(); |
| 61 | + var wasmFileContentHash = sha256.ComputeHash(wasmFileContent); |
| 62 | + |
| 63 | + using MemoryStream stream = new(); |
| 64 | + await stream.WriteAsync(wasmFileContentHash); |
| 65 | + await stream.WriteAsync(nonce); |
| 66 | + stream.Position = 0; |
| 67 | + |
| 68 | + byte[] computedUserData = await sha256.ComputeHashAsync(stream); |
| 69 | + return computedUserData; |
| 70 | +} |
| 71 | + |
| 72 | +/// <summary> |
| 73 | +/// The layout of the JSON is given by librats. |
| 74 | +/// </summary> |
| 75 | +class EvidenceJson |
| 76 | +{ |
| 77 | + public required string Type { get; init; } |
| 78 | + public required string ReportBase64 { get; init; } |
| 79 | + public required int ReportLen { get; init; } |
| 80 | +} |
| 81 | + |
| 82 | +/// <summary> |
| 83 | +/// The start of the _report_body_t struct from Intel SGX is at offset 0x30. |
| 84 | +/// </summary> |
| 85 | +/// <remarks> |
| 86 | +/// _report_body_t struct: https://github.com/intel/linux-sgx/blob/a1eeccba5a72b3b9b342569d2cc469ece106d3e9/common/inc/sgx_report.h#L93-L111 |
| 87 | +/// Attestation flow: https://www.intel.com/content/www/us/en/developer/articles/code-sample/software-guard-extensions-remote-attestation-end-to-end-example.html |
| 88 | +/// </remarks> |
| 89 | +class Evidence(byte[] evidenceAsBytes) |
| 90 | +{ |
| 91 | + public byte[] MrEnclave => evidenceAsBytes[0x70..0x90]; |
| 92 | + public byte[] MrSigner => evidenceAsBytes[0xB0..0xD0]; |
| 93 | + public ushort ProductId => BitConverter.ToUInt16(evidenceAsBytes.AsSpan(0x130, 2)); |
| 94 | + public ushort SecurityVersion => BitConverter.ToUInt16(evidenceAsBytes.AsSpan(0x132, 2)); |
| 95 | + public byte[] UserData => evidenceAsBytes[0x170..0x190]; |
| 96 | +} |
| 97 | + |
| 98 | +static class LibRats |
| 99 | +{ |
| 100 | + /// <summary> |
| 101 | + /// Verifies the evidence using librats native function. |
| 102 | + /// </summary> |
| 103 | + /// <remarks> |
| 104 | + /// Original signature: int librats_verify_evidence_from_json(const char *json_string, const uint8_t *hash); |
| 105 | + /// </remarks> |
| 106 | + [DllImport("/usr/local/lib/librats/librats_lib.so", EntryPoint = "librats_verify_evidence_from_json")] |
| 107 | + public static extern int VerifyEvidenceFromJson(string json, byte[] hash); |
| 108 | +} |
0 commit comments