Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce image IDs to fold in PCRs and user data details #108

Merged
merged 15 commits into from
Feb 7, 2025
Merged
10 changes: 3 additions & 7 deletions attestation/verifier-risczero/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ cargo build --release

Reproducible builds are enabled for the guest to produce a consistent GUEST_ID.

Expected GUEST_ID: 0x83d32f62832771e9a859d04e7e65b325feced03510cec8ca4b77e70fdaec43ad
Expected GUEST_ID: 0x72b93507835e59e7e2690d93761c8020816dcccb21355b94b7e654ed35b3e17b

## Usage

```bash
$ ./target/release/host --help
GUEST: 0x83d32f62832771e9a859d04e7e65b325feced03510cec8ca4b77e70fdaec43ad
GUEST: 0x72b93507835e59e7e2690d93761c8020816dcccb21355b94b7e654ed35b3e17b
Usage: host --url <URL>

Options:
Expand All @@ -41,14 +41,10 @@ It takes in a URL to an attestation server producing binary attestations.

The journal contains bytes in the following order:
- 8 byte timestamp in milliseconds from the attestation
- 48 byte PCR0
- 48 byte PCR1
- 48 byte PCR2
- 96 byte public key from the root certificate
- 1 byte length of the public key from the attestation
- N byte public key from the attestation
- 2 byte length of the user data
- N byte user data
- 32 byte image id computed by hashing the PCRs and user data from the attestation

## Directory Structure

Expand Down
6 changes: 5 additions & 1 deletion attestation/verifier-risczero/host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@ edition = "2021"

[dependencies]
methods = { path = "../methods" }
risc0-zkvm = { version = "1.2.1", features = ["cuda"] }
risc0-zkvm = { version = "1.2.1" }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
serde = "1.0"
ureq = "2.10.1"
clap = { version = "4.5.20", features = ["derive"] }
hex = "0.4.3"

[features]
default = ["cuda"]
cuda = ["risc0-zkvm/cuda"]
8 changes: 6 additions & 2 deletions attestation/verifier-risczero/host/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use clap::Parser;
use methods::{GUEST_ELF, GUEST_ID};
use risc0_zkvm::{default_prover, ExecutorEnv, ProverOpts};
use risc0_zkvm::{default_prover, is_dev_mode, ExecutorEnv, ProverOpts};

#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
Expand Down Expand Up @@ -49,7 +49,11 @@ fn main() {

println!(
"Seal: {}",
hex::encode(&receipt.inner.groth16().unwrap().seal)
if is_dev_mode() {
"not available in dev mode".to_owned()
} else {
hex::encode(&receipt.inner.groth16().unwrap().seal)
}
);
println!("Journal: {}", hex::encode(&receipt.journal.bytes));
}
20 changes: 15 additions & 5 deletions attestation/verifier-risczero/methods/guest/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion attestation/verifier-risczero/methods/guest/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ edition = "2021"
[dependencies]
p384 = { version = "0.13.0", features = ["ecdsa-core"] }
risc0-zkvm = { version = "1.2.1", default-features = false, features = ['std'] }
sha2 = "0.10.8"
sha2 = { git = "https://github.com/risc0/RustCrypto-hashes", tag = "sha2-v0.10.8-risczero.0" }
x509-cert = "0.2.5"

[dev-dependencies]
Expand Down
43 changes: 18 additions & 25 deletions attestation/verifier-risczero/methods/guest/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use p384::ecdsa::signature::Verifier;
use p384::ecdsa::Signature;
use p384::ecdsa::VerifyingKey;
use sha2::Digest;
use sha2::Sha256;
use x509_cert::der::Decode;

// Design notes:
Expand All @@ -31,6 +32,9 @@ fn main() {
}

fn verify(attestation: &[u8], commit_slice: impl Fn(&[u8])) {
// hasher for accumulating an image id
let mut image_id_hasher = Sha256::new();

// assert initial fields
assert_eq!(
attestation[0..8],
Expand Down Expand Up @@ -85,7 +89,7 @@ fn verify(attestation: &[u8], commit_slice: impl Fn(&[u8])) {
// assert pcrs key
assert_eq!(attestation[offset + 33], 0x64); // text of size 4
assert_eq!(&attestation[offset + 34..offset + 38], b"pcrs");
// commit pcrs 0, 1 and 2
// accumulate pcrs 0, 1 and 2
assert_eq!(attestation[offset + 38], 0xb0); // pcrs is a map of size 16

offset += 39;
Expand All @@ -98,17 +102,17 @@ fn verify(attestation: &[u8], commit_slice: impl Fn(&[u8])) {
]
);
println!("PCR0: {:?}", &attestation[offset + 3..offset + 51]);
commit_slice(&attestation[offset + 3..offset + 51]);
image_id_hasher.update(&attestation[offset + 3..offset + 51]);

offset += 51;
assert_eq!(attestation[offset..offset + 3], [0x01, 0x58, 0x30]);
println!("PCR1: {:?}", &attestation[offset + 3..offset + 51]);
commit_slice(&attestation[offset + 3..offset + 51]);
image_id_hasher.update(&attestation[offset + 3..offset + 51]);

offset += 51;
assert_eq!(attestation[offset..offset + 3], [0x02, 0x58, 0x30]);
println!("PCR2: {:?}", &attestation[offset + 3..offset + 51]);
commit_slice(&attestation[offset + 3..offset + 51]);
image_id_hasher.update(&attestation[offset + 3..offset + 51]);

// skip rest of the pcrs, 3 to 15
offset += 51;
Expand Down Expand Up @@ -378,9 +382,12 @@ fn verify(attestation: &[u8], commit_slice: impl Fn(&[u8])) {
(size, &attestation[offset + 13..offset + 13 + size as usize])
};
println!("User data: {} bytes: {:?}", user_data_size, user_data);
// commit 2 byte length, then data
commit_slice(&user_data_size.to_be_bytes());
commit_slice(user_data);
// accumulate 2 byte length, then data
image_id_hasher.update(&user_data_size.to_be_bytes());
image_id_hasher.update(user_data);

// commit image id
commit_slice(&image_id_hasher.finalize());

// prepare COSE verification hash
let mut hasher = sha2::Sha384::new();
Expand Down Expand Up @@ -460,12 +467,6 @@ mod tests {
let expected_journal = [
// timestamp
"00000193bef3f3b0",
// PCR0
"189038eccf28a3a098949e402f3b3d86a876f4915c5b02d546abb5d8c507ceb1755b8192d8cfca66e8f226160ca4c7a6",
// PCR1
"5d3938eb05288e20a981038b1861062ff4174884968a39aee5982b312894e60561883576cc7381d1a7d05b809936bd16",
// PCR2
"6c3ef363c488a9a86faa63a44653fd806e645d4540b40540876f3b811fc1bceecf036a4703f07587c501ee45bb56a1aa",
// root pubkey
"fc0254eba608c1f36870e29ada90be46383292736e894bfff672d989444b5051e534a4b1f6dbe3c0bc581a32b7b17607",
"0ede12d69a3fea211b66e752cf7dd1dd095f6f1370f4170843d9dc100121e4cf63012809664487c9796284304dc53ff4",
Expand All @@ -474,8 +475,8 @@ mod tests {
// pubkey
"e646f8b0071d5ba75931402522cc6a5c42a84a6fea238864e5ac9a0e12d83bd3",
"6d0c8109d3ca2b699fce8d082bf313f5d2ae249bb275b6b6e91e0fcd9262f4bb",
// userdata len
"0000"
// image id
"10aff51b369137fcb2d71372829300c543b1f8c586d77080f00ba31140621b9c"
].join("");

assert_eq!(
Expand All @@ -499,23 +500,15 @@ mod tests {
let expected_journal = [
// timestamp
"00000193bf444e30",
// PCR0
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
// PCR1
"010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
// PCR2
"020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202",
// root pubkey
"6c79411ebaae7489a4e8355545c0346784b31df5d08cb1f7c0097836a82f67240f2a7201862880a1d09a0bb326637188",
"fbbafab47a10abe3630fcf8c18d35d96532184985e582c0dce3dace8441f37b9cc9211dff935baae69e4872cc3494410",
// pubkey len
"04",
// pubkey
"12345678",
// userdata len
"0003",
// userdata
"abcdef"
// image id
"79fc2e5fd8deb77d38890bdb4e4b1a1bddb08b5854d81d97b24167b449ddd372"
].join("");

assert_eq!(
Expand Down
34 changes: 11 additions & 23 deletions attestation/verifier/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ $ curl <attestation_server_ip:attestation_server_port>/attestation/raw -vs | cur
< 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","user_data":"","timestamp":1712471793488,"verifier_secp256k1_public":"e646f8b0071d5ba75931402522cc6a5c42a84a6fea238864e5ac9a0e12d83bd36d0c8109d3ca2b699fce8d082bf313f5d2ae249bb275b6b6e91e0fcd9262f4bb"}
{"signature":"15383a7af2c33e80eba2637c9e3fb0c246ecedfbb183879f9dc9b18d25635b3871212cfab51db0fd75dded5be870febe19226c92d38425c37689fb7c1e86f74f1c","public_key":"435ed75cf1be0c58b97d372b153ad4e43101895e481e7dd7d27519605e859f34d7b5491586faa887257178bbb6daa1e212f35aa2a60308cd76df5db522abb139","image_id":"3fe8419454c44a36782f2b9a307cad84ee8c226e0351cb0861c50fb6b13a3a3e","timestamp":1738845553218,"verifier_public_key":"60b77877f624b4eaf776a6afef1dd727f969c7557b88b05cc27c51366eafd72a2328b054bd2f6cec47b3b82b61bbfc898b5b32d604446ebf59e50195345a8d98"}
```

### Hex
Expand Down Expand Up @@ -177,33 +177,27 @@ $ curl <attestation_server_ip:attestation_server_port>/attestation/hex -vs | cur
< 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","user_data":"","timestamp":1712472254392,"verifier_secp256k1_public":"e646f8b0071d5ba75931402522cc6a5c42a84a6fea238864e5ac9a0e12d83bd36d0c8109d3ca2b699fce8d082bf313f5d2ae249bb275b6b6e91e0fcd9262f4bb"}
{"signature":"15383a7af2c33e80eba2637c9e3fb0c246ecedfbb183879f9dc9b18d25635b3871212cfab51db0fd75dded5be870febe19226c92d38425c37689fb7c1e86f74f1c","public_key":"435ed75cf1be0c58b97d372b153ad4e43101895e481e7dd7d27519605e859f34d7b5491586faa887257178bbb6daa1e212f35aa2a60308cd76df5db522abb139","image_id":"3fe8419454c44a36782f2b9a307cad84ee8c226e0351cb0861c50fb6b13a3a3e","timestamp":1738845553218,"verifier_public_key":"60b77877f624b4eaf776a6afef1dd727f969c7557b88b05cc27c51366eafd72a2328b054bd2f6cec47b3b82b61bbfc898b5b32d604446ebf59e50195345a8d98"}
```

## Response format

```json
{
"signature": "...",
"secp256k1_public": "...",
"pcr0": "...",
"pcr1": "...",
"pcr2": "...",
"user_data": "...",
"public_key": "...",
"image_id": "...",
"timestamp": ...,
"verifier_secp256k1_public": "..."
"verifier_public_key": "..."
}
```

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
- `user_data`: user data that was encoded in the attestation
- `public_key`: public key that was encoded in the attestation
- `image_id`: hash of PCRs and user data encoded in the attestation
- `timestamp`: timestamp that was encoded in the attestation
- `verifier_secp256k1_public`: public key of the verifier corresponding to the signature
- `verifier_public_key`: public key of the verifier corresponding to the signature

## Signature format

Expand All @@ -225,10 +219,7 @@ The `chainId`, `verifyingContract` and `salt` fields are omitted because we do n
```typescript
struct Attestation {
bytes enclavePubKey;
bytes PCR0;
bytes PCR1;
bytes PCR2;
bytes userData;
bytes32 imageId;
uint256 timestampInMilliseconds;
}
```
Expand All @@ -248,17 +239,14 @@ bytes32 private constant DOMAIN_SEPARATOR =
);

bytes32 private constant ATTESTATION_TYPEHASH =
keccak256("Attestation(bytes enclavePubKey,bytes PCR0,bytes PCR1,bytes PCR2,bytes userData,uint256 timestampInMilliseconds)");
keccak256("Attestation(bytes enclavePubKey,bytes32 imageId,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),
keccak256(attestation.userData),
attestation.imageId,
attestation.timestampInMilliseconds
)
);
Expand Down
Loading