SequencerInbox

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

Tags:
Proxy, Voting, Upgradeable, Factory, Oracle|addr:0xdf8bb6e0a4a24daccfd8add840f2716bcb67b9df|verified:true|block:23476338|tx:0xa392875b19f32026fd493b5337640f94bd4f96825a86c02105d15a43cac21eac|first_check:1759241768

Submitted on: 2025-09-30 16:16:08

Comments

Log in to comment.

No comments yet.