Skip to content

Commit

Permalink
feat(dataverse): add support of Ed25519Signature2018 proof
Browse files Browse the repository at this point in the history
  • Loading branch information
amimart committed Feb 21, 2024
1 parent f660d2e commit ede8320
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 25 deletions.
45 changes: 26 additions & 19 deletions contracts/okp4-dataverse/src/credential/crypto.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use crate::credential::error::VerificationError;
use crate::credential::proof::ProofMaterial;
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,
Expand Down Expand Up @@ -42,15 +42,36 @@ impl CryptoSuite {
deps: &'_ DepsMut<'_>,
unsecured_doc: &[Quad<'_>],
proof_opts: &[Quad<'_>],
proof_value: &[u8],
proof_material: ProofMaterial<'_>,
pub_key: &[u8],
) -> Result<(), VerificationError> {
let unsecured_doc_canon = self.canonicalize(unsecured_doc)?;
let proof_opts_canon = self.canonicalize(proof_opts)?;

let hash = [self.hash(proof_opts_canon), self.hash(unsecured_doc_canon)].concat();

self.verify(deps, &hash, proof_value, pub_key)
match proof_material {
ProofMaterial::Signature(v) => self.verify(deps, &hash, v, pub_key),
ProofMaterial::Jws(jws) => {
let (headers_b64, signature_b64) = Self::explode_jws(jws)?;
let signature = BASE64_URL_SAFE_NO_PAD
.decode(signature_b64)
.map_err(|_| VerificationError::InvalidJws)?;

let signing_input = [headers_b64, b".", &hash].concat();
let signing_input = match self.sign {
SignatureAlg::Ed25519 => signing_input,
SignatureAlg::Secp256k1 => {
let mut hasher = sha2::Sha256::new();
hasher.update(signing_input);

hasher.finalize().to_vec()
}
};

self.verify(deps, &signing_input, &signature, pub_key)
}
}
}

fn canonicalize(&self, unsecured_document: &[Quad<'_>]) -> Result<String, VerificationError> {
Expand Down Expand Up @@ -84,29 +105,15 @@ impl CryptoSuite {
) -> Result<(), VerificationError> {
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();

deps.api
.secp256k1_verify(&signing_input, &signature, pub_key)
}
SignatureAlg::Secp256k1 => deps.api.secp256k1_verify(message, signature, pub_key),
} {
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)?;
fn explode_jws(jws: &str) -> Result<(&[u8], &[u8]), VerificationError> {
let mut parts = jws.split('.');
Ok(
match (parts.next(), parts.next(), parts.next(), parts.next()) {
Expand Down
55 changes: 50 additions & 5 deletions contracts/okp4-dataverse/src/credential/proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,24 @@ use rio_api::model::{GraphName, Literal, NamedNode, Quad, Term};

#[derive(Debug, PartialEq)]
pub enum Proof<'a> {
Ed25519Signature2018(Ed25519Signature2018Proof<'a>),
Ed25519Signature2020(Ed25519Signature2020Proof<'a>),
EcdsaSecp256k1Signature2019(EcdsaSecp256k1Signature2019Proof<'a>),
DataIntegrity(DataIntegrityProof<'a>),
}

#[derive(Debug, PartialEq)]
pub enum ProofMaterial<'a> {
Signature(&'a [u8]),
Jws(&'a str),
}

impl<'a> Proof<'a> {
pub fn suitable(&self, issuer: &str, purpose: ProofPurpose) -> bool {
let (controller, proof_purpose) = match self {
Self::Ed25519Signature2018(proof) => {
(proof.verification_method.controller, proof.purpose)
}
Self::Ed25519Signature2020(proof) => {
(proof.verification_method.controller, proof.purpose)
}
Expand All @@ -33,7 +43,7 @@ impl<'a> Proof<'a> {

pub fn crypto_suite(&self) -> CryptoSuite {
match self {
Proof::Ed25519Signature2020(_) => (
Proof::Ed25519Signature2018(_) | Proof::Ed25519Signature2020(_) => (
CanonicalizationAlg::Urdna2015,
DigestAlg::Sha256,
SignatureAlg::Ed25519,
Expand All @@ -54,22 +64,25 @@ impl<'a> Proof<'a> {

pub fn pub_key(&'a self) -> &'a [u8] {
match self {
Proof::Ed25519Signature2018(p) => &p.verification_method.pub_key,
Proof::Ed25519Signature2020(p) => &p.verification_method.pub_key,
Proof::EcdsaSecp256k1Signature2019(p) => &p.verification_method.pub_key,
Proof::DataIntegrity(p) => &p.verification_method.pub_key,
}
}

pub fn signature(&'a self) -> &'a [u8] {
pub fn proof_material(&'a self) -> ProofMaterial<'a> {
match self {
Proof::Ed25519Signature2020(p) => &p.value,
Proof::EcdsaSecp256k1Signature2019(p) => p.jws.as_bytes(),
Proof::DataIntegrity(p) => &p.value,
Proof::Ed25519Signature2018(p) => ProofMaterial::Jws(p.jws),
Proof::Ed25519Signature2020(p) => ProofMaterial::Signature(p.value.as_slice()),
Proof::EcdsaSecp256k1Signature2019(p) => ProofMaterial::Jws(p.jws),
Proof::DataIntegrity(p) => ProofMaterial::Signature(p.value.as_slice()),
}
}

pub fn options(&'a self) -> &'a [Quad<'a>] {
match self {
Proof::Ed25519Signature2018(p) => p.options.as_ref(),
Proof::Ed25519Signature2020(p) => p.options.as_ref(),
Proof::EcdsaSecp256k1Signature2019(p) => p.options.as_ref(),
Proof::DataIntegrity(p) => p.options.as_ref(),
Expand Down Expand Up @@ -265,6 +278,9 @@ impl<'a> TryFrom<(&'a Dataset<'a>, GraphName<'a>)> for Proof<'a> {
})?;

match proof_type {
"https://w3id.org/security#Ed25519Signature2018" => Ok(Self::Ed25519Signature2018(
Ed25519Signature2018Proof::try_from((dataset, proof_graph))?,
)),
"https://w3id.org/security#Ed25519Signature2020" => Ok(Self::Ed25519Signature2020(
Ed25519Signature2020Proof::try_from((dataset, proof_graph))?,
)),
Expand Down Expand Up @@ -296,6 +312,35 @@ impl<'a> From<&'a str> for ProofPurpose {
}
}

#[derive(Debug, PartialEq)]
pub struct Ed25519Signature2018Proof<'a> {
// The verification method format being the same as the 2020 signature proof we reuse it.
verification_method: Ed25519VerificationKey2020<'a>,
created: &'a str,
purpose: ProofPurpose,
jws: &'a str,
options: Dataset<'a>,
}

impl<'a> TryFrom<(&'a Dataset<'a>, GraphName<'a>)> for Ed25519Signature2018Proof<'a> {
type Error = InvalidProofError;

fn try_from(
(dataset, proof_graph): (&'a Dataset<'a>, GraphName<'a>),
) -> Result<Self, Self::Error> {
let v_method = Proof::extract_verification_method(dataset, proof_graph)?;
let p_purpose = Proof::extract_proof_purpose(dataset, proof_graph)?;

Ok(Self {
verification_method: v_method.try_into()?,
created: Proof::extract_created(dataset, proof_graph)?,
purpose: p_purpose.into(),
jws: Proof::extract_jws(dataset, proof_graph)?,
options: Proof::extract_proof_options(dataset, proof_graph, PROOF_RDF_JWS),
})
}
}

#[derive(Debug, PartialEq)]
pub struct Ed25519Signature2020Proof<'a> {
verification_method: Ed25519VerificationKey2020<'a>,
Expand Down
2 changes: 1 addition & 1 deletion contracts/okp4-dataverse/src/credential/vc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ impl<'a> VerifiableCredential<'a> {
deps,
self.unsecured_document.as_ref(),
proof.options(),
proof.signature(),
proof.proof_material(),
proof.pub_key(),
)
}
Expand Down

0 comments on commit ede8320

Please sign in to comment.