Skip to content

marlinprotocol/oyster-attestation-verifier

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

94 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Marlin Oyster Logo

Attestation Verifier

The attestation verifier verifies attestations provided by the attestation server containing a secp256k1 public key and signs the response using its own secp256k1 key. Intended to be run inside an enclave to provide cheap attestation verification services.

Once the attestation of the verfier is verified on-chain (very expensive), it enables other enclaves, including other verifiers, to get verified by submitting a simple ECDSA signature from the verifier instead (very cheap). The process essentially extends the chain of trust of the attestation verifier enclave instead of trying to verify the full attestation of the other enclave again.

Build

cargo build --release

Prebuilt binaries

amd64: http://public.artifacts.marlin.pro/projects/enclaves/attestation-verifier_v2.0.1_linux_amd64

arm64: http://public.artifacts.marlin.pro/projects/enclaves/attestation-verifier_v2.0.1_linux_arm64

Usage

$ ./target/release/oyster-attestation-verifier --help
Usage: oyster-attestation-verifier --secp256k1-secret <SECP256K1_SECRET> --secp256k1-public <SECP256K1_PUBLIC> --ip <IP> --port <PORT>

Options:
      --secp256k1-secret <SECP256K1_SECRET>
          path to secp256k1 private key file (e.g. /app/secp256k1.sec)
      --secp256k1-public <SECP256K1_PUBLIC>
          path to secp256k1 public key file (e.g. /app/secp256k1.pub)
  -i, --ip <IP>
          server ip (e.g. 127.0.0.1)
  -p, --port <PORT>
          server port (e.g. 1400)
  -h, --help
          Print help
  -V, --version
          Print version

Endpoints

The attestation verifier exposes two verification endpoints which expect the attestation in one of two formats - raw and hex. The formats match the two endpoints of the attestation server and the response of the server can just be sent to the verifier as is.

Raw

Endpoint

/verify/raw

Example
$ curl <attestation_server_ip:attestation_server_port>/attestation/raw -vs | curl -H "Content-Type: application/octet-stream" --data-binary @- <attestation_verifier_ip:attestation_verifier_port>/verify/raw -vs
*   Trying <attestation_server_ip:attestation_server_port>...
* Connected to <attestation_server_ip> (<attestation_server_ip>) port <attestation_server_port> (#0)
> GET /attestation/raw HTTP/1.1
> Host: <attestation_server_ip:attestation_server_port>
> User-Agent: curl/7.81.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< content-type: application/octet-stream
< content-length: 4468
< date: Sun, 07 Apr 2024 06:36:44 GMT
< 
{ [2682 bytes data]
* Connection #0 to host <attestation_server_ip> left intact
*   Trying <attestation_verifier_ip:attestation_verifier_port>...
* Connected to <attestation_verifier_ip> (<attestation_verifier_ip>) port <attestation_verifier_port> (#0)
> POST /verify/raw HTTP/1.1
> Host: <attestation_verifier_ip:attestation_verifier_port>
> User-Agent: curl/7.81.0
> Accept: */*
> Content-Type: application/octet-stream
> Content-Length: 4468
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< content-length: 799
< content-type: application/json
< date: Sun, 07 Apr 2024 06:36:44 GMT
< 
* Connection #0 to host <attestation_verifier_ip> left intact
{"signature":"1aaffb1463cfbeb24401267d2ab2661a9695dd0fb294fc4f4e66ad98efa1ece63b79c0bfc5d79c8515abbfb4fa50994b848132d3374821ff09eb22c7af37395e1b","secp256k1_public":"e646f8b0071d5ba75931402522cc6a5c42a84a6fea238864e5ac9a0e12d83bd36d0c8109d3ca2b699fce8d082bf313f5d2ae249bb275b6b6e91e0fcd9262f4bb","pcr0":"189038eccf28a3a098949e402f3b3d86a876f4915c5b02d546abb5d8c507ceb1755b8192d8cfca66e8f226160ca4c7a6","pcr1":"5d3938eb05288e20a981038b1861062ff4174884968a39aee5982b312894e60561883576cc7381d1a7d05b809936bd16","pcr2":"6c3ef363c488a9a86faa63a44653fd806e645d4540b40540876f3b811fc1bceecf036a4703f07587c501ee45bb56a1aa","timestamp":1712471793488,"verifier_secp256k1_public":"e646f8b0071d5ba75931402522cc6a5c42a84a6fea238864e5ac9a0e12d83bd36d0c8109d3ca2b699fce8d082bf313f5d2ae249bb275b6b6e91e0fcd9262f4bb"}

Hex

Endpoint

/attestation/hex

Example
$ curl <attestation_server_ip:attestation_server_port>/attestation/hex -vs | curl -H "Content-Type: text/plain" -d @- <attestation_verifier_ip:attestation_verifier_port>/verify/hex -vs
*   Trying <attestation_server_ip:attestation_server_port>...
* Connected to <attestation_server_ip> (<attestation_server_ip>) port <attestation_server_port> (#0)
> GET /attestation/hex HTTP/1.1
> Host: <attestation_server_ip:attestation_server_port>
> User-Agent: curl/7.81.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< content-type: text/plain; charset=utf-8
< content-length: 8936
< date: Sun, 07 Apr 2024 06:44:25 GMT
< 
{ [2681 bytes data]
* Connection #0 to host <attestation_server_ip> left intact
*   Trying <attestation_verifier_ip:attestation_verifier_port>...
* Connected to <attestation_verifier_ip> (<attestation_verifier_ip>) port <attestation_verifier_port> (#0)
> POST /verify/hex HTTP/1.1
> Host: <attestation_verifier_ip:attestation_verifier_port>
> User-Agent: curl/7.81.0
> Accept: */*
> Content-Type: text/plain
> Content-Length: 8936
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< content-length: 799
< content-type: application/json
< date: Sun, 07 Apr 2024 06:44:25 GMT
< 
* Connection #0 to host <attestation_verifier_ip> left intact
{"signature":"4ed49c703e8deea8dabccbeeb8fe5625776dbbbef4cffbb9c31f84d21e7a0b6c63707aade102548cc05e6de3a49469b96c700f5b8709e75ec050061ac69dbb621c","secp256k1_public":"e646f8b0071d5ba75931402522cc6a5c42a84a6fea238864e5ac9a0e12d83bd36d0c8109d3ca2b699fce8d082bf313f5d2ae249bb275b6b6e91e0fcd9262f4bb","pcr0":"189038eccf28a3a098949e402f3b3d86a876f4915c5b02d546abb5d8c507ceb1755b8192d8cfca66e8f226160ca4c7a6","pcr1":"5d3938eb05288e20a981038b1861062ff4174884968a39aee5982b312894e60561883576cc7381d1a7d05b809936bd16","pcr2":"6c3ef363c488a9a86faa63a44653fd806e645d4540b40540876f3b811fc1bceecf036a4703f07587c501ee45bb56a1aa","timestamp":1712472254392,"verifier_secp256k1_public":"e646f8b0071d5ba75931402522cc6a5c42a84a6fea238864e5ac9a0e12d83bd36d0c8109d3ca2b699fce8d082bf313f5d2ae249bb275b6b6e91e0fcd9262f4bb"}

Response format

{
    "signature": "...",
    "secp256k1_public": "...",
    "pcr0": "...",
    "pcr1": "...",
    "pcr2": "...",
    "timestamp": ...,
    "verifier_secp256k1_public": "..."
}

The verifier responds with JSON with the following fields:

  • signature: signature provided by the verifier
  • secp256k1_public: public key that was encoded in the attestation
  • pcr0: PCR0 that was encoded in the attestation
  • pcr1: PCR1 that was encoded in the attestation
  • pcr2: PCR2 that was encoded in the attestation
  • timestamp: timestamp that was encoded in the attestation
  • verifier_secp256k1_public: public key of the verifier corresponding to the signature

Signature format

The verifier creates the signature as per the EIP-712 standard.

EIP-712 domain

struct EIP712Domain {
    string name = "marlin.oyster.AttestationVerifier",
    string version = "1",
}

The chainId, verifyingContract and salt fields are omitted because we do not see any significant replay concerns in allowing the signature to be verified on any contract on any chain.

Message struct

struct Attestation {
    bytes enclavePubKey;
    bytes PCR0;
    bytes PCR1;
    bytes PCR2;
    uint256 timestampInMilliseconds;
}

Verification

It is designed to be verified by the following solidity code (taken from the AttestationVerifier contract):

bytes32 private constant DOMAIN_SEPARATOR =
    keccak256(
        abi.encode(
            keccak256("EIP712Domain(string name,string version)"),
            keccak256("marlin.oyster.AttestationVerifier"),
            keccak256("1")
        )
    );

bytes32 private constant ATTESTATION_TYPEHASH =
    keccak256("Attestation(bytes enclavePubKey,bytes PCR0,bytes PCR1,bytes PCR2,uint256 timestampInMilliseconds)");

function _verify(bytes memory signature, Attestation memory attestation) internal view {
    bytes32 hashStruct = keccak256(
        abi.encode(
            ATTESTATION_TYPEHASH,
            keccak256(attestation.enclavePubKey),
            keccak256(attestation.PCR0),
            keccak256(attestation.PCR1),
            keccak256(attestation.PCR2),
            attestation.timestampInMilliseconds
        )
    );
    bytes32 digest = keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, hashStruct));

    address signer = ECDSA.recover(digest, signature);

    ...
}

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages