Skip to content

Commit

Permalink
feat: add more COSE functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
zensh committed Jul 25, 2024
1 parent b44c758 commit 3527ce7
Show file tree
Hide file tree
Showing 22 changed files with 391 additions and 222 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ opt-level = 's'
version = "0.1.0"
edition = "2021"
repository = "https://github.com/ldclabs/ic-cose"
keywords = ["config", "cbor", "canister", "icp"]
keywords = ["config", "cbor", "canister", "icp", "encryption"]
categories = ["web-programming"]
license = "MIT OR Apache-2.0"

Expand Down Expand Up @@ -49,5 +49,5 @@ crc32fast = "1.4"
url = "2.5"
once_cell = "1.19"
getrandom = { version = "0.2", features = ["custom"] }
coset = { git = "https://github.com/ldclabs/coset.git", rev = "50583c5df52b653eb9b17c2940ad4b7fe3d67ff1" }
coset = "0.3.8"
aes-gcm = "0.10"
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# ic-cose
# IC-COSE
⚙️ A decentralized COnfiguration service with Signing and Encryption on the Internet Computer.

## Overview
Expand Down
16 changes: 7 additions & 9 deletions src/ic_cose_canister/src/api_crypto.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
use ic_cose_types::{cose::*, crypto::ecdh_x25519, format_error, mac3_256};
use ic_cose_types::{
cose::{
cose_aes256_key, ecdh::ecdh_x25519, encrypt0::cose_encrypt0, mac3_256, CborSerializable,
},
format_error,
types::{ECDHInput, ECDHOutput, PublicKeyInput, SettingPathInput, SignInput},
};
use serde_bytes::ByteBuf;

use crate::{rand_bytes, store};
Expand All @@ -25,14 +31,6 @@ async fn schnorr_sign(_input: SignInput) -> Result<ByteBuf, String> {
Err("not implemented".to_string())
}

// #[ic_cdk::update]
// async fn ecdh_public_key(path: SettingPathInput, ecdh: ECDHInput) -> Result<ByteN<32>, String> {
// path.validate()?;
// let caller = ic_cdk::caller();
// let spk = store::SettingPathKey::from_path(path.into(), caller);
// store::ns::ecdh_public_key(&caller, &spk, &ecdh).await
// }

#[ic_cdk::update]
async fn ecdh_encrypted_cose_key(
path: SettingPathInput,
Expand Down
2 changes: 1 addition & 1 deletion src/ic_cose_canister/src/api_query.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use ic_cose_types::{
use ic_cose_types::types::{
namespace::NamespaceInfo,
setting::{SettingInfo, SettingPath},
state::StateInfo,
Expand Down
9 changes: 8 additions & 1 deletion src/ic_cose_canister/src/api_update.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
use ic_cose_types::{
cose::*, crypto::ecdh_x25519, format_error, mac3_256, setting::*, OwnedRef, MILLISECONDS,
cose::{
ecdh::ecdh_x25519,
encrypt0::{cose_decrypt0, cose_encrypt0},
get_cose_key_secret, mac3_256, CborSerializable, CoseKey,
},
format_error,
types::{setting::*, ECDHInput, ECDHOutput},
OwnedRef, MILLISECONDS,
};

use crate::{rand_bytes, store};
Expand Down
2 changes: 1 addition & 1 deletion src/ic_cose_canister/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use candid::Principal;
use ic_cose_types::{cose::*, namespace::*, setting::*, state::StateInfo};
use ic_cose_types::{types::namespace::*, types::setting::*, types::state::StateInfo, types::*};
use serde_bytes::ByteBuf;
use std::collections::BTreeSet;

Expand Down
5 changes: 3 additions & 2 deletions src/ic_cose_canister/src/store.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use candid::Principal;
use ciborium::{from_reader, from_reader_with_buffer, into_writer};
use ic_cose_types::{
cose::try_decode_encrypt0, namespace::NamespaceInfo, setting::*, sha3_256_n, state::StateInfo,
cose::{encrypt0::try_decode_encrypt0, sha3_256_n},
types::{namespace::NamespaceInfo, setting::*, state::StateInfo},
ByteN,
};
use ic_stable_structures::{
Expand Down Expand Up @@ -245,7 +246,7 @@ impl SettingPathKey {
pub fn from_path(val: SettingPath, caller: Principal) -> Self {
Self(
val.ns,
if val.client { 1 } else { 0 },
if val.client_owned { 1 } else { 0 },
val.subject.unwrap_or(caller),
val.key,
val.version,
Expand Down
2 changes: 1 addition & 1 deletion src/ic_cose_types/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ic_cose_types"
description = "A Rust types library used for integrating with ic-cose cluster."
description = "A Rust types library used for integrating with IC-COSE."
publish = true
repository = "https://github.com/ldclabs/ic-cose/tree/main/src/ic_cose_types"
version.workspace = true
Expand Down
143 changes: 0 additions & 143 deletions src/ic_cose_types/src/cose.rs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,17 +1,7 @@
use aes_gcm::{aead::KeyInit, AeadInPlace, Aes256Gcm, Key, Nonce};
use x25519_dalek::{PublicKey, SharedSecret, StaticSecret};

use crate::format_error;

pub fn ecdh_x25519(secret: [u8; 32], their_public: [u8; 32]) -> (SharedSecret, PublicKey) {
let secret = StaticSecret::from(secret);
let public = PublicKey::from(&secret);
(
secret.diffie_hellman(&PublicKey::from(their_public)),
public,
)
}

pub fn aes256_gcm_encrypt(
key: &[u8; 32],
nonce: &[u8; 12],
Expand Down
42 changes: 42 additions & 0 deletions src/ic_cose_types/src/cose/cwt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use coset::{
cwt::{ClaimName, ClaimsSet, Timestamp},
iana, CborSerializable,
};
use num_traits::ToPrimitive;

const CLOCK_SKEW: i64 = 5 * 60; // 5 minutes
pub static SCOPE_NAME: ClaimName = ClaimName::Assigned(iana::CwtClaimName::Scope);

pub fn cwt_from_cwt(data: &[u8], now_sec: i64) -> Result<ClaimsSet, String> {
let claims = ClaimsSet::from_slice(data).map_err(|err| format!("invalid claims: {}", err))?;
if let Some(ref exp) = claims.expiration_time {
let exp = match exp {
Timestamp::WholeSeconds(v) => *v,
Timestamp::FractionalSeconds(v) => (*v).to_i64().unwrap_or_default(),
};
if exp < now_sec - CLOCK_SKEW {
return Err("token expired".to_string());
}
}
if let Some(ref nbf) = claims.not_before {
let nbf = match nbf {
Timestamp::WholeSeconds(v) => *v,
Timestamp::FractionalSeconds(v) => (*v).to_i64().unwrap_or_default(),
};
if nbf > now_sec + CLOCK_SKEW {
return Err("token not yet valid".to_string());
}
}

Ok(claims)
}

pub fn get_scope(claims: &ClaimsSet) -> Result<String, String> {
let scope = claims
.rest
.iter()
.find(|(key, _)| key == &SCOPE_NAME)
.ok_or("missing scope")?;
let scope = scope.1.as_text().ok_or("invalid scope text")?;
Ok(scope.to_string())
}
10 changes: 10 additions & 0 deletions src/ic_cose_types/src/cose/ecdh.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use x25519_dalek::{PublicKey, SharedSecret, StaticSecret};

pub fn ecdh_x25519(secret: [u8; 32], their_public: [u8; 32]) -> (SharedSecret, PublicKey) {
let secret = StaticSecret::from(secret);
let public = PublicKey::from(&secret);
(
secret.diffie_hellman(&PublicKey::from(their_public)),
public,
)
}
29 changes: 29 additions & 0 deletions src/ic_cose_types/src/cose/ecdsa.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use serde_bytes::ByteBuf;

use super::sha256;
use crate::format_error;

pub use k256::ecdsa::{
signature::hazmat::{PrehashSigner, PrehashVerifier},
Signature, SigningKey, VerifyingKey,
};

pub fn secp256k1_verify_any(
public_keys: &[ByteBuf],
message: &[u8],
signature: &[u8],
) -> Result<(), String> {
let keys: Vec<VerifyingKey> = public_keys
.iter()
.map(|key| VerifyingKey::from_sec1_bytes(key).map_err(format_error))
.collect::<Result<_, _>>()?;
let sig = Signature::try_from(signature).map_err(format_error)?;
let digest = sha256(message);
match keys
.iter()
.any(|key| key.verify_prehash(&digest, &sig).is_ok())
{
true => Ok(()),
false => Err("secp256k1 signature verification failed".to_string()),
}
}
23 changes: 23 additions & 0 deletions src/ic_cose_types/src/cose/ed25519.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use crate::{format_error, ByteN};

pub use ed25519_dalek::{Signature, SigningKey, VerifyingKey};

pub fn ed25519_verify_any(
public_keys: &[ByteN<32>],
message: &[u8],
signature: &[u8],
) -> Result<(), String> {
let keys: Vec<VerifyingKey> = public_keys
.iter()
.map(|key| VerifyingKey::from_bytes(key).map_err(format_error))
.collect::<Result<_, _>>()?;
let sig = Signature::from_slice(signature).map_err(format_error)?;

match keys
.iter()
.any(|key| key.verify_strict(message, &sig).is_ok())
{
true => Ok(()),
false => Err("ed25519 signature verification failed".to_string()),
}
}
Loading

0 comments on commit 3527ce7

Please sign in to comment.