Description:
Proxy contract enabling upgradeable smart contract patterns. Delegates calls to an implementation contract.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"@automata-network/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;
}
"
},
"@automata-network/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;
}
"
},
"@automata-network/on-chain-pccs/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;
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")) {
tcbInfo.tcbType = uint8(JSONParserLib.parseUint(val));
tcbTypeFound = true;
} else if (decodedKey.eq("id")) {
string memory idStr = JSONParserLib.decodeString(val);
if (idStr.eq("SGX")) {
tcbInfo.id = TcbId.SGX;
} else if (idStr.eq("TDX")) {
tcbInfo.id = TcbId.TDX;
} else {
revert TCBInfo_Invalid();
}
idFound = true;
} else if (decodedKey.eq("fmspc")) {
tcbInfo.fmspc = bytes6(uint48(JSONParserLib.parseUintFromHex(JSONParserLib.decodeString(val))));
fmspcFound = true;
} else if (decodedKey.eq("version")) {
tcbInfo.version = uint32(JSONParserLib.parseUint(val));
versionFound = true;
} else if (decodedKey.eq("issueDate")) {
tcbInfo.issueDate = uint64(DateTimeUtils.fromISOToTimestamp(JSONParserLib.decodeString(val)));
issueDateFound = true;
} else if (decodedKey.eq("nextUpdate")) {
tcbInfo.nextUpdate = uint64(DateTimeUtils.fromISOToTimestamp(JSONParserLib.decodeString(val)));
nextUpdateFound = true;
} else if (decodedKey.eq("pceId")) {
tcbInfo.pceid = bytes2(uint16(JSONParserLib.parseUintFromHex(JSONParserLib.decodeString(val))));
pceidFound = true;
} else if (decodedKey.eq("tcbEvaluationDataNumber")) {
tcbInfo.evaluationDataNumber = uint32(JSONParserLib.parseUint(val));
evaluationFound = true;
}
if (versionFound) {
allFound =
(tcbTypeFound && fmspcFound && issueDateFound && nextUpdateFound && pceidFound && evaluationFound);
if (tcbInfo.version >= 3) {
allFound = allFound && idFound;
}
if (allFound) {
break;
}
}
}
if (!allFound) {
revert TCBInfo_Invalid();
}
}
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);
}
}
"
},
"@automata-network/on-chain-pccs/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.nextSiblingOf(sigPtr);
tbs = der.allBytesAt(tbsParentPtr);
sig = _getSignature(der, sigPtr);
}
function getSerialNumber(bytes calldata der) external pure returns (uint256 serialNum) {
uint256 root = der.root();
uint256 tbsParentPtr = der.firstChildOf(root);
uint256 tbsPtr = der.firstChildOf(tbsParentPtr);
tbsPtr = der.nextSiblingOf(tbsPtr);
serialNum = _parseSerialNumber(der.bytesAt(tbsPtr));
}
function getIssuerCommonName(bytes calldata der) external pure returns (string memory issuerCommonName) {
uint256 root = der.root();
uint256 tbsParentPtr = der.firstChildOf(root);
uint256 tbsPtr = der.firstChildOf(tbsParentPtr);
tbsPtr = der.nextSiblingOf(tbsPtr);
tbsPtr = der.nextSiblingOf(tbsPtr);
tbsPtr = der.nextSiblingOf(tbsPtr);
issuerCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));
}
function certIsNotExpired(bytes calldata der) external view returns (bool isValid) {
uint256 root = der.root();
uint256 tbsParentPtr = der.firstChildOf(root);
uint256 tbsPtr = der.firstChildOf(tbsParentPtr);
tbsPtr = der.nextSiblingOf(tbsPtr);
tbsPtr = der.nextSiblingOf(tbsPtr);
tbsPtr = der.nextSiblingOf(tbsPtr);
tbsPtr = der.nextSiblingOf(tbsPtr);
(uint256 validityNotBefore, uint256 validityNotAfter) = _getValidity(der, tbsPtr);
isValid = block.timestamp > validityNotBefore && block.timestamp < validityNotAfter;
}
function getSubjectCommonName(bytes calldata der) external pure returns (string memory subjectCommonName) {
uint256 root = der.root();
uint256 tbsParentPtr = der.firstChildOf(root);
uint256 tbsPtr = der.firstChildOf(tbsParentPtr);
tbsPtr = der.nextSiblingOf(tbsPtr);
tbsPtr = der.nextSiblingOf(tbsPtr);
tbsPtr = der.nextSiblingOf(tbsPtr);
tbsPtr = der.nextSiblingOf(tbsPtr);
tbsPtr = der.nextSiblingOf(tbsPtr);
subjectCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));
}
function getSubjectPublicKey(bytes calldata der) external pure returns (bytes memory pubKey) {
uint256 root = der.root();
uint256 tbsParentPtr = der.firstChildOf(root);
uint256 tbsPtr = der.firstChildOf(tbsParentPtr);
tbsPtr = der.nextSiblingOf(tbsPtr);
tbsPtr = der.nextSiblingOf(tbsPtr);
tbsPtr = der.nextSiblingOf(tbsPtr);
tbsPtr = der.nextSiblingOf(tbsPtr);
tbsPtr = der.nextSiblingOf(tbsPtr);
tbsPtr = der.nextSiblingOf(tbsPtr);
pubKey = _getSubjectPublicKey(der, der.firstChildOf(tbsPtr));
}
/// x509 Certificates generally contain a sequence of elements in the following order:
/// 1. tbs
/// - 1a. version
/// - 1b. serial number
/// - 1c. siganture algorithm
/// - 1d. issuer
/// - - 1d(a). common name
/// - - 1d(b). organization name
/// - - 1d(c). locality name
/// - - 1d(d). state or province name
/// - - 1d(e). country name
/// - 1e. validity
/// - - 1e(a) notBefore
/// - - 1e(b) notAfter
/// - 1f. subject
/// - - contains the same set of elements as 1d
/// - 1g. subject public key info
/// - - 1g(a). algorithm
/// - - 1g(b). subject public key
/// - 1h. Extensions
/// 2. Signature Algorithm
/// 3. Signature
function parseX509DER(bytes calldata der) external pure returns (X509CertObj memory cert) {
uint256 root = der.root();
uint256 tbsParentPtr = der.firstChildOf(root);
cert.tbs = der.allBytesAt(tbsParentPtr);
uint256 tbsPtr = der.firstChildOf(tbsParentPtr);
tbsPtr = der.nextSiblingOf(tbsPtr);
cert.serialNumber = _parseSerialNumber(der.bytesAt(tbsPtr));
tbsPtr = der.nextSiblingOf(tbsPtr);
tbsPtr = der.nextSiblingOf(tbsPtr);
cert.issuerCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));
tbsPtr = der.nextSiblingOf(tbsPtr);
(cert.validityNotBefore, cert.validityNotAfter) = _getValidity(der, tbsPtr);
tbsPtr = der.nextSiblingOf(tbsPtr);
cert.subjectCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));
tbsPtr = der.nextSiblingOf(tbsPtr);
cert.subjectPublicKey = _getSubjectPublicKey(der, der.firstChildOf(tbsPtr));
cert.extensionPtr = der.nextSiblingOf(tbsPtr);
// tbs iteration completed
// now we just need to look for the signature
uint256 sigPtr = der.nextSiblingOf(tbsParentPtr);
sigPtr = der.nextSiblingOf(sigPtr);
cert.signature = _getSignature(der, sigPtr);
}
function _getCommonName(bytes calldata der, uint256 commonNameParentPtr)
private
pure
returns (string memory commonName)
{
commonNameParentPtr = der.firstChildOf(commonNameParentPtr);
commonNameParentPtr = der.firstChildOf(commonNameParentPtr);
commonNameParentPtr = der.nextSiblingOf(commonNameParentPtr);
commonName = string(der.bytesAt(commonNameParentPtr));
}
function _getValidity(bytes calldata der, uint256 validityPtr)
private
pure
returns (uint256 notBefore, uint256 notAfter)
{
uint256 notBeforePtr = der.firstChildOf(validityPtr);
uint256 notAfterPtr = der.nextSiblingOf(notBeforePtr);
notBefore = DateTimeUtils.fromDERToTimestamp(der.bytesAt(notBeforePtr));
notAfter = DateTimeUtils.fromDERToTimestamp(der.bytesAt(notAfterPtr));
}
function _getSubjectPublicKey(bytes calldata der, uint256 subjectPublicKeyInfoPtr)
private
pure
returns (bytes memory pubKey)
{
subjectPublicKeyInfoPtr = der.nextSiblingOf(subjectPublicKeyInfoPtr);
pubKey = der.bitstringAt(subjectPublicKeyInfoPtr);
if (pubKey.length != 65) {
// TODO: we need to figure out how to handle key with prefix byte 0x02 or 0x03
revert("compressed public key not supported");
}
pubKey = _trimBytes(pubKey, 64);
}
function _parseSerialNumber(bytes memory serialBytes) private pure returns (uint256 serial) {
uint256 shift = 8 * (32 - serialBytes.length);
serial = uint256(bytes32(serialBytes) >> shift);
}
function _getSignature(bytes calldata der, uint256 sigPtr) private pure returns (bytes memory sig) {
sigPtr = der.rootOfBitStringAt(sigPtr);
sigPtr = der.firstChildOf(sigPtr);
bytes memory r = _trimBytes(der.bytesAt(sigPtr), 32);
sigPtr = der.nextSiblingOf(sigPtr);
bytes memory s = _trimBytes(der.bytesAt(sigPtr), 32);
sig = abi.encodePacked(r, s);
}
/// @dev remove unnecessary prefix from the input
function _trimBytes(bytes memory input, uint256 expectedLength) private pure returns (bytes memory output) {
uint256 n = input.length;
if (n == expectedLength) {
output = input;
} else if (n < expectedLength) {
output = new bytes(expectedLength);
uint256 padLength = expectedLength - n;
for (uint256 i = 0; i < n; i++) {
output[padLength + i] = input[i];
}
} else {
uint256 lengthDiff = n - expectedLength;
output = input.substring(lengthDiff, expectedLength);
}
}
}
"
},
"@automata-network/on-chain-pccs/utils/Asn1Decode.sol": {
"content": "// SPDX-License-Identifier: MIT
// Original source: https://github.com/JonahGroendal/asn1-decode
pragma solidity ^0.8.0;
// Inspired by PufferFinance/rave
// https://github.com/JonahGroendal/asn1-decode/blob/5c2d1469fc678513753786acb441e597969192ec/contracts/Asn1Decode.sol
import "./BytesUtils.sol";
library NodePtr {
// Unpack first byte index
function ixs(uint256 self) internal pure returns (uint256) {
return uint80(self);
}
// Unpack first content byte index
function ixf(uint256 self) internal pure returns (uint256) {
return uint80(self >> 80);
}
// Unpack last content byte index
function ixl(uint256 self) internal pure returns (uint256) {
return uint80(self >> 160);
}
// Pack 3 uint80s into a uint256
function getPtr(uint256 _ixs, uint256 _ixf, uint256 _ixl) internal pure returns (uint256) {
_ixs |= _ixf << 80;
_ixs |= _ixl << 160;
return _ixs;
}
}
library Asn1Decode {
using NodePtr for uint256;
using BytesUtils for bytes;
/*
* @dev Get the root node. First step in traversing an ASN1 structure
* @param der The DER-encoded ASN1 structure
* @return A pointer to the outermost node
*/
function root(bytes memory der) internal pure returns (uint256) {
return readNodeLength(der, 0);
}
/*
* @dev Get the root node of an ASN1 structure that's within a bit string value
* @param der The DER-encoded ASN1 structure
* @return A pointer to the outermost node
*/
function rootOfBitStringAt(bytes memory der, uint256 ptr) internal pure returns (uint256) {
require(der[ptr.ixs()] == 0x03, "Not type BIT STRING");
return readNodeLength(der, ptr.ixf() + 1);
}
/*
* @dev Get the root node of an ASN1 structure that's within an octet string value
* @param der The DER-encoded ASN1 structure
* @return A pointer to the outermost node
*/
function rootOfOctetStringAt(bytes memory der, uint256 ptr) internal pure returns (uint256) {
require(der[ptr.ixs()] == 0x04, "Not type OCTET STRING");
return readNodeLength(der, ptr.ixf());
}
/*
* @dev Get the next sibling node
* @param der The DER-encoded ASN1 structure
* @param ptr Points to the indices of the current node
* @return A pointer to the next sibling node
*/
function nextSiblingOf(bytes memory der, uint256 ptr) internal pure returns (uint256) {
return readNodeLength(der, ptr.ixl() + 1);
}
/*
* @dev Get the first child node of the current node
* @param der The DER-encoded ASN1 structure
* @param ptr Points to the indices of the current node
* @return A pointer to the first child node
*/
function firstChildOf(bytes memory der, uint256 ptr) internal pure returns (uint256) {
require(der[ptr.ixs()] & 0x20 == 0x20, "Not a constructed type");
return readNodeLength(der, ptr.ixf());
}
/*
* @dev Use for looping through children of a node (either i or j).
* @param i Pointer to an ASN1 node
* @param j Pointer to another ASN1 node of the same ASN1 structure
* @return True iff j is child of i or i is child of j.
*/
function isChildOf(uint256 i, uint256 j) internal pure returns (bool) {
return (((i.ixf() <= j.ixs()) && (j.ixl() <= i.ixl())) || ((j.ixf() <= i.ixs()) && (i.ixl() <= j.ixl())));
}
/*
* @dev Extract value of node from DER-encoded structure
* @param der The der-encoded ASN1 structure
* @param ptr Points to the indices of the current node
* @return Value bytes of node
*/
function bytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {
return der.substring(ptr.ixf(), ptr.ixl() + 1 - ptr.ixf());
}
/*
* @dev Extract entire node from DER-encoded structure
* @param der The DER-encoded ASN1 structure
* @param ptr Points to the indices of the current node
* @return All bytes of node
*/
function allBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {
return der.substring(ptr.ixs(), ptr.ixl() + 1 - ptr.ixs());
}
/*
* @dev Extract value of node from DER-encoded structure
* @param der The DER-encoded ASN1 structure
* @param ptr Points to the indices of the current node
* @return Value bytes of node as bytes32
*/
function bytes32At(bytes memory der, uint256 ptr) internal pure returns (bytes32) {
return der.readBytesN(ptr.ixf(), ptr.ixl() + 1 - ptr.ixf());
}
/*
* @dev Extract value of node from DER-encoded structure
* @param der The der-encoded ASN1 structure
* @param ptr Points to the indices of the current node
* @return Uint value of node
*/
function uintAt(bytes memory der, uint256 ptr) internal pure returns (uint256) {
require(der[ptr.ixs()] == 0x02, "Not type INTEGER");
require(der[ptr.ixf()] & 0x80 == 0, "Not positive");
uint256 len = ptr.ixl() + 1 - ptr.ixf();
return uint256(der.readBytesN(ptr.ixf(), len) >> (32 - len) * 8);
}
/*
* @dev Extract value of a positive integer node from DER-encoded structure
* @param der The DER-encoded ASN1 structure
* @param ptr Points to the indices of the current node
* @return Value bytes of a positive integer node
*/
function uintBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {
require(der[ptr.ixs()] == 0x02, "Not type INTEGER");
require(der[ptr.ixf()] & 0x80 == 0, "Not positive");
uint256 valueLength = ptr.ixl() + 1 - ptr.ixf();
if (der[ptr.ixf()] == 0) {
return der.substring(ptr.ixf() + 1, valueLength - 1);
} else {
return der.substring(ptr.ixf(), valueLength);
}
}
function keccakOfBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes32) {
return der.keccak(ptr.ixf(), ptr.ixl() + 1 - ptr.ixf());
}
function keccakOfAllBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes32) {
return der.keccak(ptr.ixs(), ptr.ixl() + 1 - ptr.ixs());
}
/*
* @dev Extract value of bitstring node from DER-encoded structure
* @param der The DER-encoded ASN1 structure
* @param ptr Points to the indices of the current node
* @return Value of bitstring converted to bytes
*/
function bitstringAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {
require(der[ptr.ixs()] == 0x03, "Not type BIT STRING");
// Only 00 padded bitstr can be converted to bytestr!
require(der[ptr.ixf()] == 0x00);
uint256 valueLength = ptr.ixl() + 1 - ptr.ixf();
return der.substring(ptr.ixf() + 1, valueLength - 1);
}
function readNodeLength(bytes memory der, uint256 ix) private pure returns (uint256) {
uint256 length;
uint80 ixFirstContentByte;
uint80 ixLastContentByte;
if ((der[ix + 1] & 0x80) == 0) {
length = uint8(der[ix + 1]);
ixFirstContentByte = uint80(ix + 2);
ixLastContentByte = uint80(ixFirstContentByte + length - 1);
} else {
uint8 lengthbytesLength = uint8(der[ix + 1] & 0x7F);
if (lengthbytesLength == 1) {
length = der.readUint8(ix + 2);
} else if (lengthbytesLength == 2) {
length = der.readUint16(ix + 2);
} else {
length = uint256(der.readBytesN(ix + 2, lengthbytesLength) >> (32 - lengthbytesLength) * 8);
}
ixFirstContentByte = uint80(ix + 2 + lengthbytesLength);
ixLastContentByte = uint80(ixFirstContentByte + length - 1);
}
return NodePtr.getPtr(ix, ixFirstContentByte, ixLastContentByte);
}
}
"
},
"@automata-network/on-chain-pccs/utils/BytesUtils.sol": {
"content": "// SPDX-License-Identifier: BSD 2-Clause License
pragma solidity ^0.8.0;
// Inspired by ensdomains/dnssec-oracle - BSD-2-Clause license
// https://github.com/ensdomains/dnssec-oracle/blob/master/contracts/BytesUtils.sol
library BytesUtils {
/*
* @dev Returns the keccak-256 hash of a byte range.
* @param self The byte string to hash.
* @param offset The position to start hashing at.
* @param len The number of bytes to hash.
* @return The hash of the byte range.
*/
function keccak(bytes memory self, uint256 offset, uint256 len) internal pure returns (bytes32 ret) {
require(offset + len <= self.length);
assembly {
ret := keccak256(add(add(self, 32), offset), len)
}
}
/*
* @dev Returns a positive number if `other` comes lexicographically after
* `self`, a negative number if it comes before, or zero if the
* contents of the two bytes are equal.
* @param self The first bytes to compare.
* @param other The second bytes to compare.
* @return The result of the comparison.
*/
function compare(bytes memory self, bytes memory other) internal pure returns (int256) {
return compare(self, 0, self.length, other, 0, other.length);
}
/*
* @dev Returns a positive number if `other` comes lexicographically after
* `self`, a negative number if it comes before, or zero if the
* contents of the two bytes are equal. Comparison is done per-rune,
* on unicode codepoints.
* @param self The first bytes to compare.
* @param offset The offset of self.
* @param len The length of self.
* @param other The second bytes to compare.
* @param otheroffset The offset of the other string.
* @param otherlen The length of the other string.
* @return The result of the comparison.
*/
function compare(
bytes memory self,
uint256 offset,
uint256 len,
bytes memory other,
uint256 otheroffset,
uint256 otherlen
) internal pure returns (int256) {
uint256 shortest = len;
if (otherlen < len) {
shortest = otherlen;
}
uint256 selfptr;
uint256 otherptr;
assembly {
selfptr := add(self, add(offset, 32))
otherptr := add(other, add(otheroffset, 32))
}
for (uint256 idx = 0; idx < shortest; idx += 32) {
uint256 a;
uint256 b;
assembly {
a := mload(selfptr)
b := mload(otherptr)
}
if (a != b) {
// Mask out irrelevant bytes and check again
uint256 mask;
if (shortest > 32) {
mask = type(uint256).max; // aka 0xffffff....
} else {
mask = ~(2 ** (8 * (32 - shortest + idx)) - 1);
}
uint256 diff = (a & mask) - (b & mask);
if (diff != 0) {
return int256(diff);
}
}
selfptr += 32;
otherptr += 32;
}
return int256(len) - int256(otherlen);
}
/*
* @dev Returns true if the two byte ranges are equal.
* @param self The first byte range to compare.
* @param offset The offset into the first byte range.
* @param other The second byte range to compare.
* @param otherOffset The offset into the second byte range.
* @param len The number of bytes to compare
* @return True if the byte ranges are equal, false otherwise.
*/
function equals(bytes memory self, uint256 offset, bytes memory other, uint256 otherOffset, uint256 len)
internal
pure
returns (bool)
{
return keccak(self, offset, len) == keccak(other, otherOffset, len);
}
/*
* @dev Returns true if the two byte ranges are equal with offsets.
* @param self The first byte range to compare.
* @param offset The offset into the first byte range.
* @param other The second byte range to compare.
* @param otherOffset The offset into the second byte range.
* @return True if the byte ranges are equal, false otherwise.
*/
function equals(bytes memory self, uint256 offset, bytes memory other, uint256 otherOffset)
internal
pure
returns (bool)
{
return keccak(self, offset, self.length - offset) == keccak(other, otherOffset, other.length - otherOffset);
}
/*
* @dev Compares a range of 'self' to all of 'other' and returns True iff
* they are equal.
* @param self The first byte range to compare.
* @param offset The offset into the first byte range.
* @param other The second byte range to compare.
* @return True if the byte ranges are equal, false otherwise.
*/
function equals(bytes memory self, uint256 offset, bytes memory other) internal pure returns (bool) {
return self.length >= offset + other.length && equals(self, offset, other, 0, other.length);
}
/*
* @dev Returns true if the two byte ranges are equal.
* @param self The first byte range to compare.
* @param other The second byte range to compare.
* @return True if the byte ranges are equal, false otherwise.
*/
function equals(bytes memory self, bytes memory other) internal pure returns (bool) {
return self.length == other.length && equals(self, 0, other, 0, self.length);
}
/*
* @dev Returns the 8-bit number at the specified index of self.
* @param self The byte string.
* @param idx The index into the bytes
* @return The specified 8 bits of the string, interpreted as an integer.
*/
function readUint8(bytes memory self, uint256 idx) internal pure returns (uint8 ret) {
return uint8(self[idx]);
}
/*
* @dev Returns the 16-bit number at the specified index of self.
* @param self The byte string.
* @param idx The index into the bytes
* @return The specified 16 bits of the string, interpreted as an integer.
*/
function readUint16(bytes memory self, uint256 idx) internal pure returns (uint16 ret) {
require(idx + 2 <= self.length);
assembly {
ret := and(mload(add(add(self, 2), idx)), 0xFFFF)
}
}
/*
* @dev Returns the 32-bit number at the specified index of self.
* @param self The byte string.
* @param idx The index into the bytes
* @return The specified 32 bits of the string, interpreted as an integer.
*/
function readUint32(bytes memory self, uint256 idx) internal pure returns (uint32 ret) {
require(idx + 4 <= self.length);
assembly {
ret := and(mload(add(add(self, 4), idx)), 0xFFFFFFFF)
}
}
/*
* @dev Returns the 32 byte value at the specified index of self.
* @param self The byte string.
* @param idx The index into the bytes
* @return The specified 32 bytes of the string.
*/
function readBytes32(bytes memory self, uint256 idx) internal pure returns (bytes32 ret) {
require(idx + 32 <= self.length);
assembly {
ret := mload(add(add(self, 32), idx))
}
}
/*
* @dev Returns the 32 byte value at the specified index of self.
* @param self The byte string.
* @param idx The index into the bytes
* @return The specified 32 bytes of the string.
*/
function readBytes20(bytes memory self, uint256 idx) internal pure returns (bytes20 ret) {
require(idx + 20 <= self.length);
assembly {
ret :=
and(mload(add(add(self, 32), idx)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000)
}
}
/*
* @dev Returns the n byte value at the specified index of self.
* @param self The byte string.
* @param idx The index into the bytes.
* @param len The number of bytes.
* @return The specified 32 bytes of the string.
*/
function readBytesN(bytes memory self, uint256 idx, uint256 len) internal pure returns (bytes32 ret) {
require(len <= 32);
require(idx + len <= self.length);
assembly {
let mask := not(sub(exp(256, sub(32, len)), 1))
ret := and(mload(add(add(self, 32), idx)), mask)
}
}
function memcpy(uint256 dest, uint256 src, uint256 len) private pure {
// Copy word-length chunks while possible
for (; len >= 32; len -= 32) {
assembly {
mstore(dest, mload(src))
}
dest += 32;
src += 32;
}
// Copy remaining bytes
uint256 mask;
if (len == 0) {
mask = type(uint256).max; // Set to maximum value of uint256
} else {
mask = 256 ** (32 - len) - 1;
}
assembly {
let srcpart := and(mload(src), not(mask))
let destpart := and(mload(dest), mask)
mstore(dest, or(destpart, srcpart))
}
}
/*
* @dev Copies a substring into a new byte string.
* @param self The byte string to copy from.
* @param offset The offset to start copying at.
* @param len The number of bytes to copy.
*/
function substring(bytes memory self, uint256 offset, uint256 len) internal pure returns (bytes memory) {
require(offset + len <= self.length);
bytes memory ret = new bytes(len);
uint256 dest;
uint256 src;
assembly {
dest := add(ret, 32)
src := add(add(self, 32), offset)
}
memcpy(dest, src, len);
return ret;
}
// Maps characters from 0x30 to 0x7A to their base32 values.
// 0xFF represents invalid characters in that range.
bytes constant base32HexTable =
hex"00010203040506070809FFFFFFFFFFFFFF0A0B0C0D0E0F101112131415161718191A1B1C1D1E1FFFFFFFFFFFFFFFFFFFFF0A0B0C0D0E0F101112131415161718191A1B1C1D1E1F";
/**
* @dev Decodes unpadded base32 data of up to one word in length.
* @param self The data to decode.
* @param off Offset into the string to start at.
* @param len Number of characters to decode.
* @return The decoded data, left aligned.
*/
function base32HexDecodeWord(bytes memory self, uint256 off, uint256 len) internal pure returns (bytes32) {
require(len <= 52);
uint256 ret = 0;
uint8 decoded;
for (uint256 i = 0; i < len; i++) {
bytes1 char = self[off + i];
require(char >= 0x30 && char <= 0x7A);
decoded = uint8(base32HexTable[uint256(uint8(char)) - 0x30]);
require(decoded <= 0x20);
if (i == len - 1) {
break;
}
ret = (ret << 5) | decoded;
}
uint256 bitlen = len * 5;
if (len % 8 == 0) {
// Multiple of 8 characters, no padding
ret = (ret << 5) | decoded;
} else if (len % 8 == 2) {
// Two extra characters - 1 byte
ret = (ret << 3) | (decoded >> 2);
bitlen -= 2;
} else if (len % 8 == 4) {
// Four extra characters - 2 bytes
ret = (ret << 1) | (decoded >> 4);
bitlen -= 4;
} else if (len % 8 == 5) {
// Five extra characters - 3 bytes
ret = (ret << 4) | (decoded >> 1);
bitlen -= 1;
} else if (len % 8 == 7) {
// Seven extra characters - 4 bytes
ret = (ret << 2) | (decoded >> 3);
bitlen -= 3;
} else {
revert();
}
return bytes32(ret << (256 - bitlen));
}
function compareBytes(bytes memory a, bytes memory b) internal pure returns (bool) {
if (a.length != b.length) {
return false;
}
for (uint256 i = 0; i < a.length; i++) {
if (a[i] != b[i]) {
return false;
}
}
return true;
}
}
"
},
"@automata-network/on-chain-pccs/utils/DateTimeUtils.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {DateTimeLib} from "solady/utils/DateTimeLib.sol";
import {LibString} from "solady/utils/LibString.sol";
library DateTimeUtils {
using LibString for string;
/*
* @dev Convert a DER-encoded time to a unix timestamp
* @param x509Time The DER-encoded time
* @return The unix timestamp
*/
function fromDERToTimestamp(bytes memory x509Time) internal pure returns (uint256) {
uint16 yrs;
uint8 mnths;
uint8 dys;
uint8 hrs;
uint8 mins;
uint8 secs;
uint8 offset;
if (x509Time.length == 13) {
if (uint8(x509Time[0]) - 48 < 5) yrs += 2000;
else yrs += 1900;
} else {
yrs += (uint8(x509Time[0]) - 48) * 1000 + (uint8(x509Time[1]) - 48) * 100;
offset = 2;
}
yrs += (uint8(x509Time[offset + 0]) - 48) * 10 + uint8(x509Time[offset + 1]) - 48;
mnths = (uint8(x509Time[offset + 2]) - 48) * 10 + uint8(x509Time[offset + 3]) - 48;
dys += (uint8(x509Time[offset + 4]) - 48) * 10 + uint8(x509Time[offset + 5]) - 48;
hrs += (uint8(x509Time[offset + 6]) - 48) * 10 + uint8(x509Time[offset + 7]) - 48;
mins += (uint8(x509Time[offset + 8]) - 48) * 10 + uint8(x509Time[offset + 9]) - 48;
secs += (uint8(x509Time[offset + 10]) - 48) * 10 + uint8(x509Time[offset + 11]) - 48;
return DateTimeLib.dateTimeToTimestamp(yrs, mnths, dys, hrs, mins, secs);
}
/// @dev iso follows pattern: "YYYY-MM-DDTHH:mm:ssZ"
function fromISOToTimestamp(string memory iso) internal pure returns (uint256) {
require(bytes(iso).length == 20, "invalid iso string length");
uint256 y = stringToUint(iso.slice(0, 4));
uint256 m = stringToUint(iso.slice(5, 7));
uint256 d = stringToUint(iso.slice(8, 10));
uint256 h = stringToUint(iso.slice(11, 13));
uint256 min = stringToUint(iso.slice(14, 16));
uint256 s = stringToUint(iso.slice(17, 19));
return DateTimeLib.dateTimeToTimestamp(y, m, d, h, min, s);
}
// https://ethereum.stackexchange.com/questions/10932/how-to-convert-string-to-int
function stringToUint(string memory s) private pure returns (uint256 result) {
bytes memory b = bytes(s);
result = 0;
for (uint256 i = 0; i < b.length; i++) {
uint256 c = uint256(uint8(b[i]));
if (c >= 48 && c <= 57) {
result = result * 10 + (c - 48);
}
}
}
}
"
},
"espresso-tee-contracts/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;
}
"
},
"espresso-tee-contracts/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;
}
"
},
"espresso-tee-contracts/interface/IEspressoTEEVerifier.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";
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;
}
"
},
"solady/utils/DateTimeLib.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for date time operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/DateTimeLib.sol)
/// @author Modified from BokkyPooBahsDateTimeLibrary (https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary)
/// @dev
/// Conventions:
/// --------------------------------------------------------------------+
/// Unit | Range | Notes |
/// --------------------------------------------------------------------|
/// timestamp | 0..0x1e18549868c76ff | Unix timestamp. |
/// epochDay | 0..0x16d3e098039 | Days since 1970-01-01. |
/// year | 1970..0xffffffff | Gregorian calendar year. |
/// month | 1..12 | Gregorian calendar month. |
/// day | 1..31 | Gregorian calendar day of month. |
/// weekday | 1..7 | The day of the week (1-indexed). |
/// --------------------------------------------------------------------+
/// All timestamps of days are rounded down to 00:00:00 UTC.
library DateTimeLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// Weekdays are 1-indexed, adhering to ISO 8601.
uint256 internal constant MON = 1;
uint256 internal constant TUE = 2;
uint256 internal constant WED = 3;
uint256 internal constant THU = 4;
uint256 internal constant FRI = 5;
uint256 internal constant SAT = 6;
uint256 internal constant SUN = 7;
// Months and days of months are 1-indexed, adhering to ISO 8601.
uint256 internal constant JAN = 1;
uint256 internal constant FEB = 2;
uint256 internal constant MAR = 3;
uint256 internal constant APR = 4;
uint256 internal constant MAY = 5;
uint256 internal constant JUN = 6;
uint256 internal constant JUL = 7;
uint256 internal constant AUG = 8;
uint256 internal constant SEP = 9;
uint256 internal constant OCT = 10;
uint256 internal constant NOV = 11;
uint256 internal constant DEC = 12;
// These limits are large enough for most practical purposes.
// Inputs that exceed these limits result in undefined behavior.
uint256 internal constant MAX_SUPPORTED_YEAR = 0xffffffff;
uint256 internal constant MAX_SUPPORTED_EPOCH_DAY = 0x16d3e098039;
uint256 internal constant MAX_SUPPORTED_TIMESTAMP = 0x1e18549868c76ff;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* DATE TIME OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the number of days since 1970-01-01 from (`year`,`month`,`day`).
/// See: https://howardhinnant.github.io/date_algorithms.html
/// Note: Inputs outside the supported ranges result in undefined behavior.
/// Use {isSupportedDate} to check if the inputs are supported.
function dateToEpochDay(uint256 year, uint256 month, uint256 day)
internal
pure
returns (uint256 epochDay)
{
/// @solidity memory-safe-assembly
assembly {
year := sub(year, lt(month, 3))
let doy := add(shr(11, add(mul(62719, mod(add(month, 9), 12)), 769)), day)
let yoe := mod(year, 400)
let doe := sub(add(add(mul(yoe, 365), shr(2, yoe)), doy), div(yoe, 100))
epochDay := sub(add(mul(div(year, 400), 146097), doe), 719469)
}
}
/// @dev Returns (`year
Submitted on: 2025-09-30 16:16:08
Comments
Log in to comment.
No comments yet.