Skip to content

Commit

Permalink
WIP: ic_tee_agent
Browse files Browse the repository at this point in the history
  • Loading branch information
zensh committed Nov 2, 2024
1 parent 0a05896 commit 6e6c69d
Show file tree
Hide file tree
Showing 14 changed files with 1,575 additions and 84 deletions.
1,307 changes: 1,274 additions & 33 deletions Cargo.lock

Large diffs are not rendered by default.

11 changes: 10 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
[workspace]
members = ["src/ic_tee_identity", "src/ic_tee_nitro_attestation"]
members = [
"src/ic_tee_agent",
"src/ic_tee_identity",
"src/ic_tee_nitro_attestation",
"src/ic_tee_sdk",
]
resolver = "2"

[profile.release]
Expand All @@ -24,10 +29,14 @@ serde = "1"
serde_bytes = "0.11"
p384 = { version = "0.13" }
sha2 = "0.10"
sha3 = "0.10"
ic-cdk = "0.16"
ic-stable-structures = "0.6"
ic-canister-sig-creation = "1.1"
ic-certification = "2.6"
ic-agent = "0.39"
getrandom = { version = "0.2", features = ["custom"] }
coset = "0.3.8"
x509-parser = { version = "0.16.0" }
ed25519-consensus = "2.1"
rand = "0.8"
16 changes: 16 additions & 0 deletions src/ic_tee_agent/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "ic_tee_agent"
description = "An agent to interact with the Internet Computer for Trusted Execution Environments (TEEs)"
repository = "https://github.com/ldclabs/ic-tee/tree/main/src/ic_tee_agent"
publish = true
version.workspace = true
edition.workspace = true
keywords.workspace = true
categories.workspace = true
license.workspace = true

[dependencies]
candid = { workspace = true }
ed25519-consensus = { workspace = true }
ic-agent = { workspace = true }
rand = { workspace = true }
13 changes: 13 additions & 0 deletions src/ic_tee_agent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# `ic_tee_agent`
![License](https://img.shields.io/crates/l/ic_tee_agent.svg)
[![Crates.io](https://img.shields.io/crates/d/ic_tee_agent.svg)](https://crates.io/crates/ic_tee_agent)
[![Test](https://github.com/ldclabs/ic-tee/actions/workflows/test.yml/badge.svg)](https://github.com/ldclabs/ic-tee/actions/workflows/test.yml)
[![Docs.rs](https://img.shields.io/docsrs/ic_tee_agent?label=docs.rs)](https://docs.rs/ic_tee_agent)
[![Latest Version](https://img.shields.io/crates/v/ic_tee_agent.svg)](https://crates.io/crates/ic_tee_agent)

`ic_tee_agent` is an agent to interact with the Internet Computer for Trusted Execution Environments (TEEs).

## License
Copyright © 2024 [LDC Labs](https://github.com/ldclabs).

`ldclabs/ic-tee` is licensed under the MIT License. See [LICENSE](../../LICENSE-MIT) for the full license text.
125 changes: 125 additions & 0 deletions src/ic_tee_agent/src/identity.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
use candid::Principal;
use ed25519_consensus::SigningKey;
use ic_agent::{
identity::{
AnonymousIdentity, BasicIdentity, DelegatedIdentity, Delegation, Identity, SignedDelegation,
},
{agent::EnvelopeContent, Signature},
};
use rand::thread_rng;
use std::time::{Duration, SystemTime, UNIX_EPOCH};

enum InnerIdentity {
Anonymous(AnonymousIdentity),
Delegated(DelegatedIdentity),
}

pub struct TEEIdentity {
identity: InnerIdentity,
signing_key: SigningKey,
user_key: Vec<u8>,
session_key: Vec<u8>,
principal: Principal,
expiration: u64, // ns since UNIX epoch
}

impl Default for TEEIdentity {
fn default() -> Self {
Self::new()
}
}

impl TEEIdentity {
pub fn new() -> Self {
let signing_key = SigningKey::new(thread_rng());
let basic = BasicIdentity::from_signing_key(SigningKey::new(thread_rng()));
Self {
identity: InnerIdentity::Anonymous(AnonymousIdentity),
signing_key,
user_key: vec![],
session_key: basic.public_key().unwrap(),
principal: AnonymousIdentity.sender().unwrap(),
expiration: 0,
}
}

pub fn is_authenticated(&self) -> bool {
match &self.identity {
InnerIdentity::Anonymous(_) => false,
InnerIdentity::Delegated(_) => {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.saturating_sub(Duration::from_secs(300));
!now.is_zero() && now.as_nanos() < self.expiration as u128
}
}
}

pub fn session_key(&self) -> Vec<u8> {
self.session_key.clone()
}

pub fn principal(&self) -> Principal {
self.principal
}

pub fn with_user_key(&mut self, user_key: Vec<u8>) {
self.principal = Principal::self_authenticating(&user_key);
self.user_key = user_key;
}

pub fn with_delegation(&mut self, delegation: SignedDelegation) -> Result<(), String> {
if delegation.delegation.pubkey != self.session_key {
return Err("delegation pubkey does not match".to_string());
}

self.expiration = delegation.delegation.expiration;
let id = DelegatedIdentity::new_unchecked(
self.user_key.clone(),
Box::new(BasicIdentity::from_signing_key(self.signing_key.clone())),
vec![delegation],
);
self.identity = InnerIdentity::Delegated(id);
Ok(())
}
}

impl Identity for TEEIdentity {
fn sender(&self) -> Result<Principal, String> {
match &self.identity {
InnerIdentity::Anonymous(id) => id.sender(),
InnerIdentity::Delegated(id) => id.sender(),
}
}
fn public_key(&self) -> Option<Vec<u8>> {
match &self.identity {
InnerIdentity::Anonymous(id) => id.public_key(),
InnerIdentity::Delegated(id) => id.public_key(),
}
}
fn sign(&self, content: &EnvelopeContent) -> Result<Signature, String> {
match &self.identity {
InnerIdentity::Anonymous(id) => id.sign(content),
InnerIdentity::Delegated(id) => id.sign(content),
}
}
fn sign_delegation(&self, content: &Delegation) -> Result<Signature, String> {
match &self.identity {
InnerIdentity::Anonymous(id) => id.sign_delegation(content),
InnerIdentity::Delegated(id) => id.sign_delegation(content),
}
}
fn sign_arbitrary(&self, content: &[u8]) -> Result<Signature, String> {
match &self.identity {
InnerIdentity::Anonymous(id) => id.sign_arbitrary(content),
InnerIdentity::Delegated(id) => id.sign_arbitrary(content),
}
}
fn delegation_chain(&self) -> Vec<SignedDelegation> {
match &self.identity {
InnerIdentity::Anonymous(id) => id.delegation_chain(),
InnerIdentity::Delegated(id) => id.delegation_chain(),
}
}
}
1 change: 1 addition & 0 deletions src/ic_tee_agent/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod identity;
1 change: 1 addition & 0 deletions src/ic_tee_identity/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ ic-stable-structures = { workspace = true }
ic-canister-sig-creation = { workspace = true }
ic-certification = { workspace = true }
getrandom = { version = "0.2", features = ["custom"] }
ic_tee_sdk = { path = "../ic_tee_sdk", version = "0.1" }
ic_tee_nitro_attestation = { path = "../ic_tee_nitro_attestation", version = "0.1" }
26 changes: 14 additions & 12 deletions src/ic_tee_identity/src/api.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
use candid::Principal;
use ciborium::from_reader;
use ic_canister_sig_creation::{delegation_signature_msg, CanisterSigPublicKey};
use ic_canister_sig_creation::delegation_signature_msg;
use ic_tee_nitro_attestation::parse_and_verify;
use ic_tee_sdk::{
canister_user_key, AttestationUserRequest, Delegation, SignInParams, SignInResponse,
SignedDelegation,
};
use serde_bytes::ByteBuf;

use crate::{store, types};
use crate::store;

const ATTESTATION_EXPIRES_IN_MS: u64 = 1000 * 60 * 5; // 5 minute
const MILLISECONDS: u64 = 1000000;
Expand All @@ -15,25 +19,23 @@ fn get_state() -> Result<store::State, String> {
}

#[ic_cdk::update]
fn sign_in(kind: String, attestation: ByteBuf) -> Result<types::SignInResponse, String> {
fn sign_in(kind: String, attestation: ByteBuf) -> Result<SignInResponse, String> {
let attestation = match kind.as_str() {
"Nitro" => parse_and_verify(attestation.as_slice())?,
_ => Err("unsupported attestation kind".to_string())?,
};

ic_cdk::println!("attestation: {:?}", attestation);

let now_ms = ic_cdk::api::time() / MILLISECONDS;
if now_ms > attestation.timestamp + ATTESTATION_EXPIRES_IN_MS {
return Err("attestation expired".to_string());
}
let pcr0 = attestation.pcrs.get(&0).ok_or_else(|| "missing PCR0")?;
let pcr0 = attestation.pcrs.get(&0).ok_or("missing PCR0")?;
let pubkey: ByteBuf = attestation
.public_key
.ok_or_else(|| "missing public key".to_string())?;

// TODO: check request method and params
let _req: types::AttestationRequest<types::SignInParams> = attestation.user_data.map_or_else(
let _req: AttestationUserRequest<SignInParams> = attestation.user_data.map_or_else(
|| Err("missing user data".to_string()),
|data| from_reader(data.as_slice()).map_err(|err| format!("invalid user data: {:?}", err)),
)?;
Expand All @@ -44,12 +46,12 @@ fn sign_in(kind: String, attestation: ByteBuf) -> Result<types::SignInResponse,
});
let expiration = (now_ms + session_expires_in_ms) * MILLISECONDS;

let user_key = CanisterSigPublicKey::new(ic_cdk::id(), pcr0.to_vec()).to_der();
let user_key = canister_user_key(ic_cdk::id(), &kind, pcr0.as_slice(), None);
let principal = Principal::self_authenticating(&user_key);
let delegation_hash = delegation_signature_msg(pubkey.as_slice(), expiration, None);
store::state::add_signature(principal.as_slice(), delegation_hash.as_slice());

Ok(types::SignInResponse {
Ok(SignInResponse {
expiration,
user_key: user_key.into(),
})
Expand All @@ -60,11 +62,11 @@ fn get_delegation(
principal: Principal,
session_key: ByteBuf,
expiration: u64,
) -> Result<types::SignedDelegation, String> {
) -> Result<SignedDelegation, String> {
let delegation_hash = delegation_signature_msg(session_key.as_slice(), expiration, None);
let signature = store::state::get_signature(principal.as_slice(), delegation_hash.as_slice())?;
Ok(types::SignedDelegation {
delegation: types::Delegation {
Ok(SignedDelegation {
delegation: Delegation {
pubkey: session_key,
expiration,
targets: None,
Expand Down
2 changes: 1 addition & 1 deletion src/ic_tee_identity/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use candid::Principal;
use ic_tee_sdk::{SignInResponse, SignedDelegation};
use serde_bytes::ByteBuf;

mod api;
mod api_init;
mod store;
mod types;

use api_init::ChainArgs;

Expand Down
37 changes: 0 additions & 37 deletions src/ic_tee_identity/src/types.rs

This file was deleted.

17 changes: 17 additions & 0 deletions src/ic_tee_sdk/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "ic_tee_sdk"
description = "A Rust library to make Trusted Execution Environments (TEEs) work with the Internet Computer"
repository = "https://github.com/ldclabs/ic-tee/tree/main/src/ic_tee_sdk"
publish = true
version.workspace = true
edition.workspace = true
keywords.workspace = true
categories.workspace = true
license.workspace = true

[dependencies]
candid = { workspace = true }
serde = { workspace = true }
serde_bytes = { workspace = true }
sha3 = { workspace = true }
ic-canister-sig-creation = { workspace = true }
13 changes: 13 additions & 0 deletions src/ic_tee_sdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# `ic_tee_sdk`
![License](https://img.shields.io/crates/l/ic_tee_sdk.svg)
[![Crates.io](https://img.shields.io/crates/d/ic_tee_sdk.svg)](https://crates.io/crates/ic_tee_sdk)
[![Test](https://github.com/ldclabs/ic-tee/actions/workflows/test.yml/badge.svg)](https://github.com/ldclabs/ic-tee/actions/workflows/test.yml)
[![Docs.rs](https://img.shields.io/docsrs/ic_tee_sdk?label=docs.rs)](https://docs.rs/ic_tee_sdk)
[![Latest Version](https://img.shields.io/crates/v/ic_tee_sdk.svg)](https://crates.io/crates/ic_tee_sdk)

`ic_tee_sdk` is a Rust library to make Trusted Execution Environments (TEEs) work with the Internet Computer.

## License
Copyright © 2024 [LDC Labs](https://github.com/ldclabs).

`ldclabs/ic-tee` is licensed under the MIT License. See [LICENSE](../../LICENSE-MIT) for the full license text.
Loading

0 comments on commit 6e6c69d

Please sign in to comment.