diff --git a/Cargo.lock b/Cargo.lock index 84bc47c3..0dd5a9d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -472,9 +472,9 @@ checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2" [[package]] name = "ecdsa" -version = "0.16.8" +version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", "digest 0.10.7", @@ -484,12 +484,6 @@ dependencies = [ "spki", ] -[[package]] -name = "ed25519-compact" -version = "2.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a667e6426df16c2ac478efa4a439d0e674cba769c5556e8cf221739251640c8c" - [[package]] name = "ed25519-zebra" version = "3.0.0" @@ -513,9 +507,9 @@ checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "elliptic-curve" -version = "0.13.5" +version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", @@ -782,7 +776,6 @@ dependencies = [ "cw-storage-plus", "cw-utils", "cw2", - "ed25519-compact", "itertools 0.12.1", "multibase", "okp4-cognitarium", @@ -1327,6 +1320,6 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "zeroize" -version = "1.5.7" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/contracts/okp4-dataverse/Cargo.toml b/contracts/okp4-dataverse/Cargo.toml index f6efd66e..4ee9233d 100644 --- a/contracts/okp4-dataverse/Cargo.toml +++ b/contracts/okp4-dataverse/Cargo.toml @@ -35,9 +35,6 @@ cosmwasm-storage.workspace = true cw-storage-plus.workspace = true cw-utils.workspace = true cw2.workspace = true -ed25519-compact = { version = "2.0.6", default-features = false, features = [ - "std", -] } itertools = "0.12.1" multibase = "0.9.1" okp4-cognitarium.workspace = true diff --git a/contracts/okp4-dataverse/src/contract.rs b/contracts/okp4-dataverse/src/contract.rs index 394f5db0..53ba357d 100644 --- a/contracts/okp4-dataverse/src/contract.rs +++ b/contracts/okp4-dataverse/src/contract.rs @@ -65,7 +65,7 @@ pub fn instantiate( #[cfg_attr(not(feature = "library"), entry_point)] pub fn execute( - _deps: DepsMut<'_>, + deps: DepsMut<'_>, _env: Env, _info: MessageInfo, msg: ExecuteMsg, @@ -74,7 +74,7 @@ pub fn execute( ExecuteMsg::SubmitClaims { metadata, format: _, - } => execute::submit_claims(metadata), + } => execute::submit_claims(deps, metadata), _ => Err(StdError::generic_err("Not implemented").into()), } } @@ -86,13 +86,13 @@ pub mod execute { use okp4_rdf::serde::NQuadsReader; use std::io::BufReader; - pub fn submit_claims(data: Binary) -> Result { + pub fn submit_claims(deps: DepsMut<'_>, data: Binary) -> Result { let buf = BufReader::new(data.as_slice()); let mut reader = NQuadsReader::new(buf); let rdf_quads = reader.read_all()?; let vc_dataset = Dataset::from(rdf_quads.as_slice()); let vc = VerifiableCredential::try_from(&vc_dataset)?; - vc.verify()?; + vc.verify(deps)?; Ok(Response::default()) } diff --git a/contracts/okp4-dataverse/src/credential/crypto.rs b/contracts/okp4-dataverse/src/credential/crypto.rs index 8d774818..2e7271c7 100644 --- a/contracts/okp4-dataverse/src/credential/crypto.rs +++ b/contracts/okp4-dataverse/src/credential/crypto.rs @@ -1,8 +1,11 @@ use crate::credential::error::VerificationError; -use ed25519_compact::{PublicKey, Signature}; +use base64::prelude::BASE64_URL_SAFE_NO_PAD; +use base64::Engine; +use cosmwasm_std::DepsMut; use okp4_rdf::normalize::Normalizer; use rio_api::model::Quad; use sha2::Digest; +use std::str::from_utf8; pub enum CanonicalizationAlg { Urdna2015, @@ -14,6 +17,7 @@ pub enum DigestAlg { pub enum SignatureAlg { Ed25519, + Secp256k1, } pub struct CryptoSuite { @@ -35,6 +39,7 @@ impl From<(CanonicalizationAlg, DigestAlg, SignatureAlg)> for CryptoSuite { impl CryptoSuite { pub fn verify_document( &self, + deps: DepsMut<'_>, unsecured_doc: &[Quad<'_>], proof_opts: &[Quad<'_>], proof_value: &[u8], @@ -45,7 +50,7 @@ impl CryptoSuite { let hash = [self.hash(proof_opts_canon), self.hash(unsecured_doc_canon)].concat(); - self.verify(&hash, proof_value, pub_key) + self.verify(deps, &hash, proof_value, pub_key) } fn canonicalize(&self, unsecured_document: &[Quad<'_>]) -> Result { @@ -72,19 +77,44 @@ impl CryptoSuite { fn verify( &self, + deps: DepsMut<'_>, message: &[u8], signature: &[u8], pub_key: &[u8], ) -> Result<(), VerificationError> { - match self.sign { - SignatureAlg::Ed25519 => { - let pub_key = PublicKey::from_slice(pub_key).map_err(VerificationError::from)?; - let signature = - Signature::from_slice(signature).map_err(VerificationError::from)?; - pub_key - .verify(message, &signature) - .map_err(VerificationError::from) + match match self.sign { + SignatureAlg::Ed25519 => deps.api.ed25519_verify(message, signature, pub_key), + SignatureAlg::Secp256k1 => { + let (headers_b64, signature_b64) = Self::explode_jws(signature)?; + let signature = BASE64_URL_SAFE_NO_PAD + .decode(signature_b64) + .map_err(|_| VerificationError::InvalidJws)?; + + let signing_input = [headers_b64, b".", message].concat(); + let mut hasher = sha2::Sha256::new(); + hasher.update(signing_input); + let signing_input = hasher.finalize().to_vec(); + + let res = deps + .api + .secp256k1_verify(&signing_input, &signature, pub_key); + res } + } { + Ok(true) => Ok(()), + Ok(false) => Err(VerificationError::WrongSignature), + Err(e) => Err(VerificationError::from(e)), } } + + fn explode_jws(jws: &[u8]) -> Result<(&[u8], &[u8]), VerificationError> { + let jws = from_utf8(jws).map_err(|_| VerificationError::InvalidJws)?; + let mut parts = jws.split("."); + Ok( + match (parts.next(), parts.next(), parts.next(), parts.next()) { + (Some(headers), Some(_), Some(sig), None) => (headers.as_bytes(), sig.as_bytes()), + _ => Err(VerificationError::InvalidJws)?, + }, + ) + } } diff --git a/contracts/okp4-dataverse/src/credential/error.rs b/contracts/okp4-dataverse/src/credential/error.rs index fea912c1..161ac07e 100644 --- a/contracts/okp4-dataverse/src/credential/error.rs +++ b/contracts/okp4-dataverse/src/credential/error.rs @@ -39,6 +39,9 @@ pub enum InvalidProofError { #[error("Missing proof value")] MissingProofValue, + #[error("Missing proof cryptosuite")] + MissingProofCryptosuite, + #[error("Malformed proof value: {0}")] MalformedProofValue(#[from] multibase::Error), @@ -59,7 +62,13 @@ pub enum VerificationError { RdfCanonError(#[from] NormalizationError), #[error("Couldn't verify signature: {0}")] - SignatureError(#[from] ed25519_compact::Error), + SignatureError(#[from] cosmwasm_std::VerificationError), + + #[error("Invalid JWS")] + InvalidJws, + + #[error("Signature mismatch")] + WrongSignature, #[error("Couldn't find a suitable proof")] NoSuitableProof, diff --git a/contracts/okp4-dataverse/src/credential/vc.rs b/contracts/okp4-dataverse/src/credential/vc.rs index cbfdec63..183de73d 100644 --- a/contracts/okp4-dataverse/src/credential/vc.rs +++ b/contracts/okp4-dataverse/src/credential/vc.rs @@ -1,6 +1,7 @@ use crate::credential::error::{InvalidCredentialError, InvalidProofError, VerificationError}; use crate::credential::proof::{Proof, ProofPurpose}; use crate::credential::rdf_marker::*; +use cosmwasm_std::DepsMut; use itertools::Itertools; use okp4_rdf::dataset::QuadIterator; use okp4_rdf::dataset::{Dataset, QuadPattern}; @@ -73,7 +74,7 @@ impl<'a> TryFrom<&'a Dataset<'a>> for VerifiableCredential<'a> { } impl<'a> VerifiableCredential<'a> { - pub fn verify(&self) -> Result<(), VerificationError> { + pub fn verify(&self, deps: DepsMut<'_>) -> Result<(), VerificationError> { let proof = self .proof .iter() @@ -82,6 +83,7 @@ impl<'a> VerifiableCredential<'a> { let crypto_suite = proof.crypto_suite(); crypto_suite.verify_document( + deps, self.unsecured_document.as_ref(), proof.options(), proof.signature(),