Skip to content

Commit

Permalink
feat(dataverse): add ecdsa secp 2019 vc proof support
Browse files Browse the repository at this point in the history
  • Loading branch information
amimart committed Feb 9, 2024
1 parent 7b475f5 commit 46cd40c
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 15 deletions.
121 changes: 106 additions & 15 deletions contracts/okp4-dataverse/src/credential/proof.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
use crate::credential::crypto::{CanonicalizationAlg, CryptoSuite, DigestAlg, SignatureAlg};
use crate::credential::error::InvalidProofError;
use crate::credential::rdf_marker::{
PROOF_RDF_CRYPTOSUITE, PROOF_RDF_PROOF_PURPOSE, PROOF_RDF_PROOF_VALUE,
PROOF_RDF_CRYPTOSUITE, PROOF_RDF_JWS, PROOF_RDF_PROOF_PURPOSE, PROOF_RDF_PROOF_VALUE,
PROOF_RDF_PROOF_VALUE_TYPE, PROOF_RDF_VERIFICATION_METHOD, RDF_CREATED, RDF_DATE_TYPE,
RDF_TYPE,
};
use itertools::Itertools;
use okp4_rdf::dataset::{Dataset, QuadIterator};
use rio_api::model::{GraphName, Literal, Quad, Term};
use rio_api::model::{GraphName, Literal, NamedNode, Quad, Term};

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

Expand All @@ -21,6 +22,9 @@ impl<'a> Proof<'a> {
Self::Ed25519Signature2020(proof) => {
(proof.verification_method.controller, proof.purpose)
}
Self::EcdsaSecp256k1Signature2019(proof) => {
(proof.verification_method.controller, proof.purpose)
}
Proof::DataIntegrity(proof) => (proof.verification_method.controller, proof.purpose),
};

Expand All @@ -34,6 +38,11 @@ impl<'a> Proof<'a> {
DigestAlg::Sha256,
SignatureAlg::Ed25519,
),
Proof::EcdsaSecp256k1Signature2019(_) => (
CanonicalizationAlg::Urdna2015,
DigestAlg::Sha256,
SignatureAlg::Secp256k1,
),
Proof::DataIntegrity(p) => (
CanonicalizationAlg::Urdna2015,
DigestAlg::Sha256,
Expand All @@ -46,20 +55,23 @@ impl<'a> Proof<'a> {
pub fn pub_key(&'a self) -> &'a [u8] {
match self {
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] {
match self {
Proof::Ed25519Signature2020(p) => &p.value,
Proof::EcdsaSecp256k1Signature2019(p) => p.jws.as_bytes(),
Proof::DataIntegrity(p) => &p.value,
}
}

pub fn options(&'a self) -> &'a [Quad<'a>] {
match self {
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 @@ -187,11 +199,37 @@ impl<'a> Proof<'a> {
})
}

fn extract_proof_options(dataset: &'a Dataset<'a>, proof_graph: GraphName<'a>) -> Dataset<'a> {
fn extract_jws(
dataset: &'a Dataset<'a>,
proof_graph: GraphName<'a>,
) -> Result<&'a str, InvalidProofError> {
dataset
.match_pattern(None, Some(PROOF_RDF_JWS), None, Some(Some(proof_graph)))
.objects()
.exactly_one()
.map_err(|e| match e.size_hint() {
(_, Some(_)) => InvalidProofError::Malformed(
"Proof cannot have more than one proof jws".to_string(),
),
_ => InvalidProofError::MissingProofValue,
})
.and_then(|o| match o {
Term::Literal(Literal::Simple { value }) => Ok(value),
_ => Err(InvalidProofError::Malformed(
"Proof jws must be a string".to_string(),
)),
})
}

fn extract_proof_options(
dataset: &'a Dataset<'a>,
proof_graph: GraphName<'a>,
value_predicate: NamedNode<'a>,
) -> Dataset<'a> {
Dataset::new(
dataset
.match_pattern(None, None, None, Some(Some(proof_graph)))
.skip_pattern((None, Some(PROOF_RDF_PROOF_VALUE), None, None).into())
.skip_pattern((None, Some(value_predicate), None, None).into())
.map(|quad| Quad {
subject: quad.subject,
predicate: quad.predicate,
Expand Down Expand Up @@ -230,6 +268,11 @@ impl<'a> TryFrom<(&'a Dataset<'a>, GraphName<'a>)> for Proof<'a> {
"https://w3id.org/security#Ed25519Signature2020" => Ok(Self::Ed25519Signature2020(
Ed25519Signature2020Proof::try_from((dataset, proof_graph))?,
)),
"https://w3id.org/security#EcdsaSecp256k1Signature2019" => {
Ok(Self::EcdsaSecp256k1Signature2019(
EcdsaSecp256k1Signature2019Proof::try_from((dataset, proof_graph))?,
))
}
"https://w3id.org/security#DataIntegrityProof" => Ok(Self::DataIntegrity(
DataIntegrityProof::try_from((dataset, proof_graph))?,
)),
Expand All @@ -238,15 +281,6 @@ impl<'a> TryFrom<(&'a Dataset<'a>, GraphName<'a>)> for Proof<'a> {
}
}

#[derive(Debug, PartialEq)]
pub struct Ed25519Signature2020Proof<'a> {
verification_method: Ed25519VerificationKey2020<'a>,
created: &'a str,
purpose: ProofPurpose,
value: Vec<u8>,
options: Dataset<'a>,
}

#[derive(Debug, PartialEq, Copy, Clone)]
pub enum ProofPurpose {
AssertionMethod,
Expand All @@ -262,6 +296,15 @@ impl<'a> From<&'a str> for ProofPurpose {
}
}

#[derive(Debug, PartialEq)]
pub struct Ed25519Signature2020Proof<'a> {
verification_method: Ed25519VerificationKey2020<'a>,
created: &'a str,
purpose: ProofPurpose,
value: Vec<u8>,
options: Dataset<'a>,
}

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

Expand All @@ -278,7 +321,7 @@ impl<'a> TryFrom<(&'a Dataset<'a>, GraphName<'a>)> for Ed25519Signature2020Proof
created: Proof::extract_created(dataset, proof_graph)?,
purpose: p_purpose.into(),
value: p_value,
options: Proof::extract_proof_options(dataset, proof_graph),
options: Proof::extract_proof_options(dataset, proof_graph, PROOF_RDF_PROOF_VALUE),
})
}
}
Expand All @@ -303,6 +346,54 @@ impl<'a> TryFrom<&'a str> for Ed25519VerificationKey2020<'a> {
}
}

#[derive(Debug, PartialEq)]
pub struct EcdsaSecp256k1Signature2019Proof<'a> {
verification_method: EcdsaSecp256k1VerificationKey2019<'a>,
created: &'a str,
purpose: ProofPurpose,
jws: &'a str,
options: Dataset<'a>,
}

impl<'a> TryFrom<(&'a Dataset<'a>, GraphName<'a>)> for EcdsaSecp256k1Signature2019Proof<'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 EcdsaSecp256k1VerificationKey2019<'a> {
id: &'a str,
controller: &'a str,
pub_key: Vec<u8>,
}

impl<'a> TryFrom<&'a str> for EcdsaSecp256k1VerificationKey2019<'a> {
type Error = InvalidProofError;

fn try_from(value: &'a str) -> Result<Self, Self::Error> {
let (controller, key) = Proof::parse_verification_method(value)?;
Ok(Self {
id: value,
controller,
pub_key: multiformats::decode_secp256k1_key(key)?,
})
}
}

#[derive(Debug, PartialEq)]
pub struct DataIntegrityProof<'a> {
cryptosuite: DataIntegrityCryptoSuite,
Expand Down Expand Up @@ -382,7 +473,7 @@ impl<'a> TryFrom<(&'a Dataset<'a>, GraphName<'a>)> for DataIntegrityProof<'a> {
created: Proof::extract_created(dataset, proof_graph)?,
purpose: p_purpose.into(),
value: p_value,
options: Proof::extract_proof_options(dataset, proof_graph),
options: Proof::extract_proof_options(dataset, proof_graph, PROOF_RDF_PROOF_VALUE),
})
}
}
Expand Down
3 changes: 3 additions & 0 deletions contracts/okp4-dataverse/src/credential/rdf_marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ pub const PROOF_RDF_PROOF_PURPOSE: NamedNode<'_> = NamedNode {
pub const PROOF_RDF_PROOF_VALUE: NamedNode<'_> = NamedNode {
iri: "https://w3id.org/security#proofValue",
};
pub const PROOF_RDF_JWS: NamedNode<'_> = NamedNode {
iri: "https://w3id.org/security#jws",
};
pub const PROOF_RDF_PROOF_VALUE_TYPE: NamedNode<'_> = NamedNode {
iri: "https://w3id.org/security#multibase",
};
Expand Down

0 comments on commit 46cd40c

Please sign in to comment.