Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"src/EspressoTEEVerifier.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/access/Ownable2Step.sol";
import {IEspressoSGXTEEVerifier} from "./interface/IEspressoSGXTEEVerifier.sol";
import {IEspressoNitroTEEVerifier} from "./interface/IEspressoNitroTEEVerifier.sol";
import {IEspressoTEEVerifier} from "./interface/IEspressoTEEVerifier.sol";
/**
* @title EspressoTEEVerifier
* @author Espresso Systems (https://espresso.systems)
* @notice This contract is used to resgister a signer which has been attested by the TEE
*/
contract EspressoTEEVerifier is Ownable2Step, IEspressoTEEVerifier {
IEspressoSGXTEEVerifier public espressoSGXTEEVerifier;
IEspressoNitroTEEVerifier public espressoNitroTEEVerifier;
constructor(
IEspressoSGXTEEVerifier _espressoSGXTEEVerifier,
IEspressoNitroTEEVerifier _espressoNitroTEEVerifier
) Ownable() {
espressoSGXTEEVerifier = _espressoSGXTEEVerifier;
espressoNitroTEEVerifier = _espressoNitroTEEVerifier;
_transferOwnership(msg.sender);
}
/**
* @notice This function is used to verify the signature of the user data
* @param signature The signature of the user data
* @param userDataHash The hash of the user data
*/
function verify(bytes memory signature, bytes32 userDataHash, TeeType teeType)
external
view
returns (bool)
{
address signer = ECDSA.recover(userDataHash, signature);
if (teeType == TeeType.SGX) {
if (!espressoSGXTEEVerifier.registeredSigners(signer)) {
revert InvalidSignature();
}
return true;
}
if (teeType == TeeType.NITRO) {
if (!espressoNitroTEEVerifier.registeredSigners(signer)) {
revert InvalidSignature();
}
return true;
}
revert UnsupportedTeeType();
}
/* @notice Register a new signer by verifying a quote from the TEE
@param attestation The attestation from the TEE
@param data when registering a signer, data can be passed for each TEE type
which can be any additiona data that is required for registering a signer
@param teeType The type of TEE
*/
function registerSigner(bytes calldata attestation, bytes calldata data, TeeType teeType)
external
{
if (teeType == TeeType.SGX) {
espressoSGXTEEVerifier.registerSigner(attestation, data);
return;
}
if (teeType == TeeType.NITRO) {
espressoNitroTEEVerifier.registerSigner(attestation, data);
return;
}
revert UnsupportedTeeType();
}
/**
* @notice This function retrieves whether a signer is registered or not
* @param signer The address of the signer
* @param teeType The type of TEE
*/
function registeredSigners(address signer, TeeType teeType) external view returns (bool) {
if (teeType == TeeType.SGX) {
return espressoSGXTEEVerifier.registeredSigners(signer);
}
if (teeType == TeeType.NITRO) {
return espressoNitroTEEVerifier.registeredSigners(signer);
}
revert UnsupportedTeeType();
}
/**
* @notice This function retrieves whether an enclave hash is registered or not
* @param enclaveHash The hash of the enclave
* @param teeType The type of TEE
*/
function registeredEnclaveHashes(bytes32 enclaveHash, TeeType teeType)
external
view
returns (bool)
{
if (teeType == TeeType.SGX) {
return espressoSGXTEEVerifier.registeredEnclaveHash(enclaveHash);
}
if (teeType == TeeType.NITRO) {
return espressoNitroTEEVerifier.registeredEnclaveHash(enclaveHash);
}
revert UnsupportedTeeType();
}
/*
@notice Set the EspressoSGXTEEVerifier
@param _espressoSGXTEEVerifier The address of the EspressoSGXTEEVerifier
*/
function setEspressoSGXTEEVerifier(IEspressoSGXTEEVerifier _espressoSGXTEEVerifier)
public
onlyOwner
{
espressoSGXTEEVerifier = _espressoSGXTEEVerifier;
}
/*
@notice Set the EspressoNitroTEEVerifier
@param _espressoNitroTEEVerifier The address of the EspressoNitroTEEVerifier
*/
function setEspressoNitroTEEVerifier(IEspressoNitroTEEVerifier _espressoNitroTEEVerifier)
public
onlyOwner
{
espressoNitroTEEVerifier = _espressoNitroTEEVerifier;
}
}
"
},
"lib/openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.0;
import "../Strings.sol";
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV // Deprecated in v4.8
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature` or error string. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength);
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*
* _Available since v4.3._
*/
function tryRecover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address, RecoverError) {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*
* _Available since v4.2._
*/
function recover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*
* _Available since v4.3._
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\
32", hash));
}
/**
* @dev Returns an Ethereum Signed Message, created from `s`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\
", Strings.toString(s.length), s));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
}
}
"
},
"lib/openzeppelin-contracts/contracts/access/Ownable2Step.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (access/Ownable2Step.sol)
pragma solidity ^0.8.0;
import "./Ownable.sol";
/**
* @dev Contract module which provides access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership} and {acceptOwnership}.
*
* This module is used through inheritance. It will make available all functions
* from parent (Ownable).
*/
abstract contract Ownable2Step is Ownable {
address private _pendingOwner;
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
/**
* @dev Returns the address of the pending owner.
*/
function pendingOwner() public view virtual returns (address) {
return _pendingOwner;
}
/**
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual override onlyOwner {
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual override {
delete _pendingOwner;
super._transferOwnership(newOwner);
}
/**
* @dev The new owner accepts the ownership transfer.
*/
function acceptOwnership() external {
address sender = _msgSender();
require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
_transferOwnership(sender);
}
}
"
},
"src/interface/IEspressoSGXTEEVerifier.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Header} from "@automata-network/dcap-attestation/contracts/types/CommonStruct.sol";
import {EnclaveReport} from "@automata-network/dcap-attestation/contracts/types/V3Structs.sol";
interface IEspressoSGXTEEVerifier {
// We only support version 3 for now
error InvalidHeaderVersion();
// This error is thrown when the automata verification fails
error InvalidQuote();
// This error is thrown when the enclave report fails to parse
error FailedToParseEnclaveReport();
// This error is thrown when the mrEnclave don't match
error InvalidEnclaveHash();
// This error is thrown when the reportDataHash doesn't match the hash signed by the TEE
error InvalidReportDataHash();
// This error is thrown when the reportData is too short
error ReportDataTooShort();
// This error is thrown when the data length is invalid
error InvalidDataLength();
// This error is thrown when the signer address is invalid
error InvalidSignerAddress();
// This error is thrown when the quote verifier address is invalid
error InvalidQuoteVerifierAddress();
event EnclaveHashSet(bytes32 enclaveHash, bool valid);
event SignerRegistered(address signer, bytes32 enclaveHash);
event DeletedRegisteredSigner(address signer);
function registeredSigners(address signer) external view returns (bool);
function registeredEnclaveHash(bytes32 enclaveHash) external view returns (bool);
function registerSigner(bytes calldata attestation, bytes calldata data) external;
function verify(bytes calldata rawQuote, bytes32 reportDataHash)
external
view
returns (EnclaveReport memory);
function parseQuoteHeader(bytes calldata rawQuote)
external
pure
returns (Header memory header);
function parseEnclaveReport(bytes memory rawEnclaveReport)
external
pure
returns (bool success, EnclaveReport memory enclaveReport);
function setEnclaveHash(bytes32 enclaveHash, bool valid) external;
function deleteRegisteredSigners(address[] memory signers) external;
}
"
},
"src/interface/IEspressoNitroTEEVerifier.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IEspressoNitroTEEVerifier {
// This error is thrown when the PCR0 values don't match
error InvalidAWSEnclaveHash();
event AWSEnclaveHashSet(bytes32 enclaveHash, bool valid);
event AWSSignerRegistered(address signer, bytes32 enclaveHash);
event DeletedAWSRegisteredSigner(address signer);
function registeredSigners(address signer) external view returns (bool);
function registeredEnclaveHash(bytes32 enclaveHash) external view returns (bool);
function registerSigner(bytes calldata attestation, bytes calldata data) external;
function verifyCACert(bytes calldata certificate, bytes32 parentCertHash) external;
function verifyClientCert(bytes calldata certificate, bytes32 parentCertHash) external;
function certVerified(bytes32 certHash) external view returns (bool);
function setEnclaveHash(bytes32 enclaveHash, bool valid) external;
function deleteRegisteredSigners(address[] memory signers) external;
}
"
},
"src/interface/IEspressoTEEVerifier.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IEspressoSGXTEEVerifier} from "./IEspressoSGXTEEVerifier.sol";
import {IEspressoNitroTEEVerifier} from "./IEspressoNitroTEEVerifier.sol";
interface IEspressoTEEVerifier {
/**
* @notice This enum is used to specify the type of TEE
*/
enum TeeType {
SGX,
NITRO
}
// This error is thrown when the signature is invalid
error InvalidSignature();
// This error is thrown when the TEE type is not supported
error UnsupportedTeeType();
// Get address of Nitro TEE Verifier
function espressoNitroTEEVerifier() external view returns (IEspressoNitroTEEVerifier);
// Get addressof SGX TEE Verifier
function espressoSGXTEEVerifier() external view returns (IEspressoSGXTEEVerifier);
// Function to verify the signature of the user data is from a registered signer
function verify(bytes memory signature, bytes32 userDataHash, TeeType teeType)
external
view
returns (bool);
// Function to register a signer which has been attested by the TEE
function registerSigner(bytes calldata attestation, bytes calldata data, TeeType teeType)
external;
// Function to retrieve whether a signer is registered or not
function registeredSigners(address signer, TeeType teeType) external view returns (bool);
function registeredEnclaveHashes(bytes32 enclaveHash, TeeType teeType)
external
view
returns (bool);
// Function to set the EspressoSGXTEEVerifier
function setEspressoSGXTEEVerifier(IEspressoSGXTEEVerifier _espressoSGXTEEVerifier) external;
// Function to set the EspressoNitroTEEVerifier
function setEspressoNitroTEEVerifier(IEspressoNitroTEEVerifier _espressoNitroTEEVerifier)
external;
}
"
},
"lib/openzeppelin-contracts/contracts/utils/Strings.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
import "./math/Math.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
}
"
},
"lib/openzeppelin-contracts/contracts/access/Ownable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
"
},
"lib/automata-dcap-attestation/contracts/types/CommonStruct.sol": {
"content": "//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {TCBStatus} from "@automata-network/on-chain-pccs/helpers/FmspcTcbHelper.sol";
import {X509CertObj} from "@automata-network/on-chain-pccs/helpers/X509Helper.sol";
/// @dev https://github.com/intel/SGX-TDX-DCAP-QuoteVerificationLibrary/blob/16b7291a7a86e486fdfcf1dfb4be885c0cc00b4e/Src/AttestationLibrary/src/QuoteVerification/QuoteStructures.h#L42-L53
struct Header {
uint16 version;
bytes2 attestationKeyType;
bytes4 teeType;
bytes2 qeSvn;
bytes2 pceSvn;
bytes16 qeVendorId;
bytes20 userData;
}
/// @dev https://github.com/intel/SGX-TDX-DCAP-QuoteVerificationLibrary/blob/16b7291a7a86e486fdfcf1dfb4be885c0cc00b4e/Src/AttestationLibrary/src/QuoteVerification/QuoteStructures.h#L63-L80
struct EnclaveReport {
bytes16 cpuSvn;
bytes4 miscSelect;
bytes28 reserved1;
bytes16 attributes;
bytes32 mrEnclave;
bytes32 reserved2;
bytes32 mrSigner;
bytes reserved3; // 96 bytes
uint16 isvProdId;
uint16 isvSvn;
bytes reserved4; // 60 bytes
bytes reportData; // 64 bytes - For QEReports, this contains the hash of the concatenation of attestation key and QEAuthData
}
/// @dev https://github.com/intel/SGX-TDX-DCAP-QuoteVerificationLibrary/blob/16b7291a7a86e486fdfcf1dfb4be885c0cc00b4e/Src/AttestationLibrary/src/QuoteVerification/QuoteStructures.h#L128-L133
struct QEAuthData {
uint16 parsedDataSize;
bytes data;
}
/// @dev Modified from https://github.com/intel/SGX-TDX-DCAP-QuoteVerificationLibrary/blob/16b7291a7a86e486fdfcf1dfb4be885c0cc00b4e/Src/AttestationLibrary/src/QuoteVerification/QuoteStructures.h#L135-L141
struct CertificationData {
uint16 certType;
uint32 certDataSize;
PCKCollateral pck;
}
/// ========== CUSTOM TYPES ==========
struct PCKCollateral {
X509CertObj[] pckChain; // base64 decoded array containing the PCK chain
PCKCertTCB pckExtension;
}
struct PCKCertTCB {
uint16 pcesvn;
uint8[] cpusvns;
bytes fmspcBytes;
bytes pceidBytes;
}
struct Output {
uint16 quoteVersion; // BE
bytes4 tee; // BE
TCBStatus tcbStatus;
bytes6 fmspcBytes;
bytes quoteBody;
string[] advisoryIDs;
}
"
},
"lib/automata-dcap-attestation/contracts/types/V3Structs.sol": {
"content": "//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./CommonStruct.sol";
/// @dev https://github.com/intel/SGX-TDX-DCAP-QuoteVerificationLibrary/blob/16b7291a7a86e486fdfcf1dfb4be885c0cc00b4e/Src/AttestationLibrary/src/QuoteVerification/QuoteStructures.h#L153-L164
struct ECDSAQuoteV3AuthData {
bytes ecdsa256BitSignature; // 64 bytes
bytes ecdsaAttestationKey; // 64 bytes
EnclaveReport qeReport; // 384 bytes
bytes qeReportSignature; // 64 bytes
QEAuthData qeAuthData;
CertificationData certification;
}
struct V3Quote {
Header header;
EnclaveReport localEnclaveReport;
ECDSAQuoteV3AuthData authData;
}
"
},
"lib/openzeppelin-contracts/contracts/utils/math/Math.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1);
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator,
Rounding rounding
) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10**64) {
value /= 10**64;
result += 64;
}
if (value >= 10**32) {
value /= 10**32;
result += 32;
}
if (value >= 10**16) {
value /= 10**16;
result += 16;
}
if (value >= 10**8) {
value /= 10**8;
result += 8;
}
if (value >= 10**4) {
value /= 10**4;
result += 4;
}
if (value >= 10**2) {
value /= 10**2;
result += 2;
}
if (value >= 10**1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
}
}
}
"
},
"lib/openzeppelin-contracts/contracts/utils/Context.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
"
},
"lib/automata-dcap-attestation/lib/automata-on-chain-pccs/src/helpers/FmspcTcbHelper.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {JSONParserLib} from "solady/utils/JSONParserLib.sol";
import {LibString} from "solady/utils/LibString.sol";
import {DateTimeUtils} from "../utils/DateTimeUtils.sol";
// https://github.com/intel/SGXDataCenterAttestationPrimitives/blob/e7604e02331b3377f3766ed3653250e03af72d45/QuoteVerification/QVL/Src/AttestationLibrary/src/CertVerification/X509Constants.h#L64
uint256 constant TCB_CPUSVN_SIZE = 16;
enum TcbId {
/// the "id" field is absent from TCBInfo V2
/// which defaults TcbId to SGX
/// since TDX TCBInfos are only included in V3 or above
SGX,
TDX
}
/**
* @dev This is a simple representation of the TCBInfo.json in string as a Solidity object.
* @param tcbInfo: tcbInfoJson.tcbInfo string object body
* @param signature The signature to be passed as bytes array
*/
struct TcbInfoJsonObj {
string tcbInfoStr;
bytes signature;
}
/// @dev Solidity object representing TCBInfo.json excluding TCBLevels
struct TcbInfoBasic {
/// the name "tcbType" can be confusing/misleading
/// as the tcbType referred here in this struct is the type
/// of TCB level composition that determines TCB level comparison logic
/// It is not the same as the "type" parameter passed as an argument to the
/// getTcbInfo() API method described in Section 4.2.3 of the Intel PCCS Design Document
/// Instead, getTcbInfo() "type" argument should be checked against the "id" value of this struct
/// which represents the TEE type for the given TCBInfo
uint8 tcbType;
TcbId id;
uint32 version;
uint64 issueDate;
uint64 nextUpdate;
uint32 evaluationDataNumber;
bytes6 fmspc;
bytes2 pceid;
}
struct TCBLevelsObj {
uint16 pcesvn;
uint8[] sgxComponentCpuSvns;
uint8[] tdxSvns;
uint64 tcbDateTimestamp;
TCBStatus status;
string[] advisoryIDs;
}
struct TDXModule {
bytes mrsigner; // 48 bytes
bytes8 attributes;
bytes8 attributesMask;
}
struct TDXModuleIdentity {
string id;
bytes8 attributes;
bytes8 attributesMask;
bytes mrsigner; // 48 bytes
TDXModuleTCBLevelsObj[] tcbLevels;
}
struct TDXModuleTCBLevelsObj {
uint8 isvsvn;
uint64 tcbDateTimestamp;
TCBStatus status;
}
enum TCBStatus {
OK,
TCB_SW_HARDENING_NEEDED,
TCB_CONFIGURATION_AND_SW_HARDENING_NEEDED,
TCB_CONFIGURATION_NEEDED,
TCB_OUT_OF_DATE,
TCB_OUT_OF_DATE_CONFIGURATION_NEEDED,
TCB_REVOKED,
TCB_UNRECOGNIZED
}
/**
* @title FMSPC TCB Helper Contract
* @notice This is a standalone contract that can be used by off-chain applications and smart contracts
* to parse TCBInfo data
*/
contract FmspcTcbHelper {
using JSONParserLib for JSONParserLib.Item;
using LibString for string;
error TCBInfo_Invalid();
error TCB_TDX_Version_Invalid();
error TCB_TDX_ID_Invalid();
function parseTcbString(string calldata tcbInfoStr) external pure returns (TcbInfoBasic memory tcbInfo) {
JSONParserLib.Item memory root = JSONParserLib.parse(tcbInfoStr);
JSONParserLib.Item[] memory tcbInfoObj = root.children();
bool tcbTypeFound;
bool fmspcFound;
bool versionFound;
bool issueDateFound;
bool nextUpdateFound;
bool pceidFound;
bool evaluationFound;
bool idFound;
bool allFound;
TcbInfoBasic memory tcbInfoCopy;
for (uint256 y = 0; y < root.size(); y++) {
JSONParserLib.Item memory current = tcbInfoObj[y];
string memory decodedKey = JSONParserLib.decodeString(current.key());
string memory val = current.value();
if (decodedKey.eq("tcbType")) {
tcbInfoCopy.tcbType = uint8(JSONParserLib.parseUint(val));
tcbTypeFound = true;
} else if (decodedKey.eq("id")) {
string memory idStr = JSONParserLib.decodeString(val);
if (idStr.eq("SGX")) {
tcbInfoCopy.id = TcbId.SGX;
} else if (idStr.eq("TDX")) {
tcbInfoCopy.id = TcbId.TDX;
} else {
revert TCBInfo_Invalid();
}
idFound = true;
} else if (decodedKey.eq("fmspc")) {
tcbInfoCopy.fmspc = bytes6(uint48(JSONParserLib.parseUintFromHex(JSONParserLib.decodeString(val))));
fmspcFound = true;
} else if (decodedKey.eq("version")) {
tcbInfoCopy.version = uint32(JSONParserLib.parseUint(val));
versionFound = true;
} else if (decodedKey.eq("issueDate")) {
tcbInfoCopy.issueDate = uint64(DateTimeUtils.fromISOToTimestamp(JSONParserLib.decodeString(val)));
issueDateFound = true;
} else if (decodedKey.eq("nextUpdate")) {
tcbInfoCopy.nextUpdate = uint64(DateTimeUtils.fromISOToTimestamp(JSONParserLib.decodeString(val)));
nextUpdateFound = true;
} else if (decodedKey.eq("pceId")) {
tcbInfoCopy.pceid = bytes2(uint16(JSONParserLib.parseUintFromHex(JSONParserLib.decodeString(val))));
pceidFound = true;
} else if (decodedKey.eq("tcbEvaluationDataNumber")) {
tcbInfoCopy.evaluationDataNumber = uint32(JSONParserLib.parseUint(val));
evaluationFound = true;
}
if (versionFound) {
allFound =
(tcbTypeFound && fmspcFound && issueDateFound && nextUpdateFound && pceidFound && evaluationFound);
if (tcbInfoCopy.version >= 3) {
allFound = allFound && idFound;
}
if (allFound) {
break;
}
}
}
if (!allFound) {
revert TCBInfo_Invalid();
}
tcbInfo = tcbInfoCopy;
}
function parseTcbLevels(string calldata tcbInfoStr)
external
pure
returns (uint256 version, TCBLevelsObj[] memory tcbLevels)
{
JSONParserLib.Item memory root = JSONParserLib.parse(tcbInfoStr);
JSONParserLib.Item[] memory tcbInfoObj = root.children();
bool versionFound;
bool tcbLevelsFound;
JSONParserLib.Item[] memory tcbLevelsObj;
for (uint256 i = 0; i < root.size(); i++) {
JSONParserLib.Item memory current = tcbInfoObj[i];
string memory decodedKey = JSONParserLib.decodeString(current.key());
if (decodedKey.eq("version")) {
version = JSONParserLib.parseUint(current.value());
versionFound = true;
}
if (decodedKey.eq("tcbLevels")) {
tcbLevelsObj = current.children();
tcbLevelsFound = true;
}
if (versionFound && tcbLevelsFound) {
break;
}
}
if (versionFound && tcbLevelsFound) {
tcbLevels = _parseTCBLevels(version, tcbLevelsObj);
} else {
revert TCBInfo_Invalid();
}
}
function parseTcbTdxModules(string calldata tcbInfoStr)
external
pure
returns (TDXModule memory module, TDXModuleIdentity[] memory moduleIdentities)
{
JSONParserLib.Item memory root = JSONParserLib.parse(tcbInfoStr);
JSONParserLib.Item[] memory tcbInfoObj = root.children();
bool versionFound;
bool idFound;
bool tdxModuleFound;
bool tdxModuleIdentitiesFound;
bool allFound;
for (uint256 i = 0; i < root.size(); i++) {
JSONParserLib.Item memory current = tcbInfoObj[i];
string memory decodedKey = JSONParserLib.decodeString(current.key());
if (decodedKey.eq("version")) {
uint256 version = JSONParserLib.parseUint(current.value());
if (version < 3) {
revert TCB_TDX_Version_Invalid();
}
versionFound = true;
}
if (decodedKey.eq("id")) {
string memory id = JSONParserLib.decodeString(current.value());
if (!id.eq("TDX")) {
revert TCB_TDX_ID_Invalid();
}
idFound = true;
}
if (decodedKey.eq("tdxModule")) {
module = _parseTdxModule(current.children());
tdxModuleFound = true;
}
if (decodedKey.eq("tdxModuleIdentities")) {
moduleIdentities = _parseTdxModuleIdentities(current.children());
tdxModuleIdentitiesFound = true;
}
allFound = versionFound && idFound && tdxModuleFound && tdxModuleIdentitiesFound;
if (allFound) {
break;
}
}
if (!allFound) {
revert TCBInfo_Invalid();
}
}
/// ====== INTERNAL METHODS BELOW ======
function _parseTCBLevels(uint256 version, JSONParserLib.Item[] memory tcbLevelsObj)
private
pure
returns (TCBLevelsObj[] memory tcbLevels)
{
uint256 tcbLevelsSize = tcbLevelsObj.length;
tcbLevels = new TCBLevelsObj[](tcbLevelsSize);
// iterating through the array
for (uint256 i = 0; i < tcbLevelsSize; i++) {
JSONParserLib.Item[] memory tcbObj = tcbLevelsObj[i].children();
// iterating through individual tcb objects
for (uint256 j = 0; j < tcbLevelsObj[i].size(); j++) {
string memory tcbKey = JSONParserLib.decodeString(tcbObj[j].key());
if (tcbKey.eq("tcb")) {
string memory tcbStr = tcbObj[j].value();
JSONParserLib.Item memory tcbParent = JSONParserLib.parse(tcbStr);
JSONParserLib.Item[] memory tcbComponents = tcbParent.children();
if (version == 2) {
(tcbLevels[i].sgxComponentCpuSvns, tcbLevels[i].pcesvn) = _parseV2Tcb(tcbComponents);
} else if (version == 3) {
(tcbLevels[i].sgxComponentCpuSvns, tcbLevels[i].tdxSvns, tcbLevels[i].pcesvn) =
_parseV3Tcb(tcbComponents);
} else {
revert TCBInfo_Invalid();
}
} else if (tcbKey.eq("tcbDate")) {
tcbLevels[i].tcbDateTimestamp =
uint64(DateTimeUtils.fromISOToTimestamp(JSONParserLib.decodeString(tcbObj[j].value())));
} else if (tcbKey.eq("tcbStatus")) {
tcbLevels[i].status = _getTcbStatus(JSONParserLib.decodeString(tcbObj[j].value()));
} else if (tcbKey.eq("advisoryIDs")) {
JSONParserLib.Item[] memory advisoryArr = tcbObj[j].children();
uint256 n = tcbObj[j].size();
tcbLevels[i].advisoryIDs = new string[](n);
for (uint256 k = 0; k < n; k++) {
tcbLevels[i].advisoryIDs[k] = JSONParserLib.decodeString(advisoryArr[k].value());
}
}
}
}
}
function _getTcbStatus(string memory statusStr) private pure returns (TCBStatus status) {
if (statusStr.eq("UpToDate")) {
status = TCBStatus.OK;
} else if (statusStr.eq("OutOfDate")) {
status = TCBStatus.TCB_OUT_OF_DATE;
} else if (statusStr.eq("OutOfDateConfigurationNeeded")) {
status = TCBStatus.TCB_OUT_OF_DATE_CONFIGURATION_NEEDED;
} else if (statusStr.eq("ConfigurationNeeded")) {
status = TCBStatus.TCB_CONFIGURATION_NEEDED;
} else if (statusStr.eq("ConfigurationAndSWHardeningNeeded")) {
status = TCBStatus.TCB_CONFIGURATION_AND_SW_HARDENING_NEEDED;
} else if (statusStr.eq("SWHardeningNeeded")) {
status = TCBStatus.TCB_SW_HARDENING_NEEDED;
} else if (statusStr.eq("Revoked")) {
status = TCBStatus.TCB_REVOKED;
} else {
status = TCBStatus.TCB_UNRECOGNIZED;
}
}
function _parseV2Tcb(JSONParserLib.Item[] memory tcbComponents)
private
pure
returns (uint8[] memory sgxComponentCpuSvns, uint16 pcesvn)
{
sgxComponentCpuSvns = new uint8[](TCB_CPUSVN_SIZE);
uint256 cpusvnCounter = 0;
for (uint256 i = 0; i < tcbComponents.length; i++) {
string memory key = JSONParserLib.decodeString(tcbComponents[i].key());
uint256 value = JSONParserLib.parseUint(tcbComponents[i].value());
if (key.eq("pcesvn")) {
pcesvn = uint16(value);
} else {
sgxComponentCpuSvns[cpusvnCounter++] = uint8(value);
}
}
if (cpusvnCounter != TCB_CPUSVN_SIZE) {
revert TCBInfo_Invalid();
}
}
function _parseV3Tcb(JSONParserLib.Item[] memory tcbComponents)
private
pure
returns (uint8[] memory sgxComponentCpuSvns, uint8[] memory tdxSvns, uint16 pcesvn)
{
sgxComponentCpuSvns = new uint8[](TCB_CPUSVN_SIZE);
tdxSvns = new uint8[](TCB_CPUSVN_SIZE);
for (uint256 i = 0; i < tcbComponents.length; i++) {
string memory key = JSONParserLib.decodeString(tcbComponents[i].key());
if (key.eq("pcesvn")) {
pcesvn = uint16(JSONParserLib.parseUint(tcbComponents[i].value()));
} else {
string memory componentKey = key;
JSONParserLib.Item[] memory componentArr = tcbComponents[i].children();
uint256 cpusvnCounter = 0;
for (uint256 j = 0; j < tcbComponents[i].size(); j++) {
JSONParserLib.Item[] memory component = componentArr[j].children();
for (uint256 k = 0; k < componentArr[j].size(); k++) {
key = JSONParserLib.decodeString(component[k].key());
if (key.eq("svn")) {
if (componentKey.eq("tdxtcbcomponents")) {
tdxSvns[cpusvnCounter++] = uint8(JSONParserLib.parseUint(component[k].value()));
} else {
sgxComponentCpuSvns[cpusvnCounter++] =
uint8(JSONParserLib.parseUint(component[k].value()));
}
}
}
}
if (cpusvnCounter != TCB_CPUSVN_SIZE) {
revert TCBInfo_Invalid();
}
}
}
}
function _parseTdxModule(JSONParserLib.Item[] memory tdxModuleObj) private pure returns (TDXModule memory module) {
for (uint256 i = 0; i < tdxModuleObj.length; i++) {
string memory key = JSONParserLib.decodeString(tdxModuleObj[i].key());
string memory val = JSONParserLib.decodeString(tdxModuleObj[i].value());
if (key.eq("attributes")) {
module.attributes = bytes8(uint64(JSONParserLib.parseUintFromHex(val)));
}
if (key.eq("attributesMask")) {
module.attributesMask = bytes8(uint64(JSONParserLib.parseUintFromHex(val)));
}
if (key.eq("mrsigner")) {
module.mrsigner = _getMrSignerHex(val);
}
}
}
function _parseTdxModuleIdentities(JSONParserLib.Item[] memory tdxModuleIdentitiesArr)
private
pure
returns (TDXModuleIdentity[] memory identities)
{
uint256 n = tdxModuleIdentitiesArr.length;
identities = new TDXModuleIdentity[](n);
for (uint256 i = 0; i < n; i++) {
JSONParserLib.Item[] memory currIdentity = tdxModuleIdentitiesArr[i].children();
for (uint256 j = 0; j < tdxModuleIdentitiesArr[i].size(); j++) {
string memory key = JSONParserLib.decodeString(currIdentity[j].key());
if (key.eq("id")) {
string memory val = JSONParserLib.decodeString(currIdentity[j].value());
identities[i].id = val;
}
if (key.eq("mrsigner")) {
string memory val = JSONParserLib.decodeString(currIdentity[j].value());
identities[i].mrsigner = _getMrSignerHex(val);
}
if (key.eq("attributes")) {
string memory val = JSONParserLib.decodeString(currIdentity[j].value());
identities[i].attributes = bytes8(uint64(JSONParserLib.parseUintFromHex(val)));
}
if (key.eq("attributesMask")) {
string memory val = JSONParserLib.decodeString(currIdentity[j].value());
identities[i].attributesMask = bytes8(uint64(JSONParserLib.parseUintFromHex(val)));
}
if (key.eq("tcbLevels")) {
JSONParserLib.Item[] memory tcbLevelsArr = currIdentity[j].children();
uint256 x = tcbLevelsArr.length;
identities[i].tcbLevels = new TDXModuleTCBLevelsObj[](x);
for (uint256 k = 0; k < x; k++) {
JSONParserLib.Item[] memory tcb = tcbLevelsArr[k].children();
for (uint256 l = 0; l < tcb.length; l++) {
key = JSONParserLib.decodeString(tcb[l].key());
if (key.eq("tcb")) {
JSONParserLib.Item[] memory isvsvnObj = tcb[l].children();
key = JSONParserLib.decodeString(isvsvnObj[0].key());
if (key.eq("isvsvn")) {
identities[i].tcbLevels[k].isvsvn =
uint8(JSONParserLib.parseUint(isvsvnObj[0].value()));
} else {
revert TCBInfo_Invalid();
}
}
if (key.eq("tcbDate")) {
identities[i].tcbLevels[k].tcbDateTimestamp =
uint64(DateTimeUtils.fromISOToTimestamp(JSONParserLib.decodeString(tcb[l].value())));
}
if (key.eq("tcbStatus")) {
identities[i].tcbLevels[k].status =
_getTcbStatus(JSONParserLib.decodeString(tcb[l].value()));
}
}
}
}
}
}
}
function _getMrSignerHex(string memory mrSignerStr) private pure returns (bytes memory mrSignerBytes) {
string memory mrSignerUpper16BytesStr = mrSignerStr.slice(0, 16);
string memory mrSignerLower32BytesStr = mrSignerStr.slice(16, 48);
uint256 mrSignerUpperBytes = JSONParserLib.parseUintFromHex(mrSignerUpper16BytesStr);
uint256 mrSignerLowerBytes = JSONParserLib.parseUintFromHex(mrSignerLower32BytesStr);
mrSignerBytes = abi.encodePacked(uint128(mrSignerUpperBytes), mrSignerLowerBytes);
}
}
"
},
"lib/automata-dcap-attestation/lib/automata-on-chain-pccs/src/helpers/X509Helper.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Asn1Decode, NodePtr} from "../utils/Asn1Decode.sol";
import {BytesUtils} from "../utils/BytesUtils.sol";
import {DateTimeUtils} from "../utils/DateTimeUtils.sol";
/**
* @title Solidity Structure representing X509 Certificates
* @notice This is a simplified structure of a DER-decoded X509 Certificate
*/
struct X509CertObj {
uint256 serialNumber;
string issuerCommonName;
uint256 validityNotBefore;
uint256 validityNotAfter;
string subjectCommonName;
bytes subjectPublicKey;
// the extension needs to be parsed further for PCK Certificates
uint256 extensionPtr;
// for signature verification in the cert chain
bytes signature;
bytes tbs;
}
/**
* @title X509 Certificates Helper Contract
* @notice This is a standalone contract that can be used by off-chain applications and smart contracts
* to parse DER-encoded X509 certificates.
* @dev This parser is only valid for ECDSA signature algorithm and p256 key algorithm.
*/
contract X509Helper {
using Asn1Decode for bytes;
using NodePtr for uint256;
using BytesUtils for bytes;
/// =================================================================================
/// USE THE GETTERS BELOW IF YOU DON'T WANT TO PARSE THE ENTIRE X509 CERTIFICATE
/// =================================================================================
function getTbsAndSig(bytes calldata der) external pure returns (bytes memory tbs, bytes memory sig) {
uint256 root = der.root();
uint256 tbsParentPtr = der.firstChildOf(root);
uint256 sigPtr = der.nextSiblingOf(tbsParentPtr);
sigPtr = der.nextSibling
Submitted on: 2025-09-30 09:57:24
Comments
Log in to comment.
No comments yet.