Skip to content

Commit

Permalink
Update ssi to latest main (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
sbihel authored Jul 26, 2024
1 parent 12e8999 commit 5ccb842
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 67 deletions.
8 changes: 6 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ repository = "https://github.com/spruceid/oidc4vci-rs/"
# TODO feature-gate
isomdl = { git = "https://github.com/spruceid/isomdl", rev = "90ce218" }

ssi = "0.7.0"
ssi-claims = { git = "https://github.com/spruceid/ssi.git", branch = "main" }
ssi-dids-core = { git = "https://github.com/spruceid/ssi.git", branch = "main" }
ssi-jwk = { git = "https://github.com/spruceid/ssi.git", branch = "main" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_with = "3.3.0"
Expand All @@ -28,5 +30,7 @@ serde_urlencoded = "0.7.1"

[dev-dependencies]
assert-json-diff = "2.0.2"
did-jwk = "0.1.0"
did-jwk = { git = "https://github.com/spruceid/ssi.git", branch = "main" }
did-method-key = { git = "https://github.com/spruceid/ssi.git", branch = "main" }
ssi-verification-methods = { git = "https://github.com/spruceid/ssi.git", branch = "main" }
tokio = { version = "1.25.0", features = ["macros"] }
2 changes: 1 addition & 1 deletion src/core/profiles/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ impl CredentialRequestProfile for CoreProfilesRequest {
type Response = CoreProfilesResponse;
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(tag = "format")]
pub enum CoreProfilesResponse {
#[serde(rename = "jwt_vc_json")]
Expand Down
14 changes: 7 additions & 7 deletions src/core/profiles/w3c/jwt.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use serde::{Deserialize, Serialize};
use ssi::jwk;
use ssi_claims::CompactJWSString;

use crate::profiles::{
AuthorizationDetaislProfile, CredentialMetadataProfile, CredentialOfferProfile,
Expand All @@ -10,7 +10,7 @@ use super::{CredentialDefinition, CredentialOfferDefinition};

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct Metadata {
cryptographic_suites_supported: Option<Vec<jwk::Algorithm>>,
cryptographic_suites_supported: Option<Vec<ssi_jwk::Algorithm>>,
credential_definition: CredentialDefinition,
order: Option<Vec<String>>,
}
Expand All @@ -25,7 +25,7 @@ impl Metadata {
}
field_getters_setters![
pub self [self] ["JWT VC metadata value"] {
set_cryptographic_suites_supported -> cryptographic_suites_supported[Option<Vec<jwk::Algorithm>>],
set_cryptographic_suites_supported -> cryptographic_suites_supported[Option<Vec<ssi_jwk::Algorithm>>],
set_credential_definition -> credential_definition[CredentialDefinition],
set_order -> order[Option<Vec<String>>],
}
Expand Down Expand Up @@ -98,18 +98,18 @@ impl CredentialRequestProfile for Request {
type Response = Response;
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Response {
credential: String,
credential: CompactJWSString,
}

impl Response {
pub fn new(credential: String) -> Self {
pub fn new(credential: CompactJWSString) -> Self {
Self { credential }
}
field_getters_setters![
pub self [self] ["JWT VC response value"] {
set_credential -> credential[String],
set_credential -> credential[CompactJWSString],
}
];
}
Expand Down
15 changes: 8 additions & 7 deletions src/core/profiles/w3c/ldp.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use serde::{Deserialize, Serialize};
use ssi::{ldp::ProofSuiteType, vc::Credential};
use ssi_claims::vc::AnyJsonCredential;

use crate::profiles::{
AuthorizationDetaislProfile, CredentialMetadataProfile, CredentialOfferProfile,
Expand All @@ -10,7 +10,7 @@ use super::{CredentialDefinition, CredentialOfferDefinition};

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct Metadata {
cryptographic_suites_supported: Option<Vec<ProofSuiteType>>,
cryptographic_suites_supported: Option<Vec<String>>,
#[serde(rename = "@context")]
context: Vec<serde_json::Value>,
credentials_definition: CredentialDefinitionLD,
Expand All @@ -31,7 +31,7 @@ impl Metadata {
}
field_getters_setters![
pub self [self] ["LD VC metadata value"] {
set_cryptographic_suites_supported -> cryptographic_suites_supported[Option<Vec<ProofSuiteType>>],
set_cryptographic_suites_supported -> cryptographic_suites_supported[Option<Vec<String>>],
set_context -> context[Vec<serde_json::Value>],
set_credentials_definition -> credentials_definition[CredentialDefinitionLD],
set_order -> order[Option<Vec<String>>],
Expand Down Expand Up @@ -160,19 +160,20 @@ impl CredentialRequestProfile for Request {
type Response = Response;
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
// We might want to make the credential type generic
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Response {
credential: Credential,
credential: AnyJsonCredential,
}

impl Response {
pub fn new(credential: Credential) -> Self {
pub fn new(credential: AnyJsonCredential) -> Self {
Self { credential }
}

field_getters_setters![
pub self [self] ["LD VC credential response value"] {
set_credential -> credential[Credential],
set_credential -> credential[AnyJsonCredential],
}
];
}
Expand Down
2 changes: 1 addition & 1 deletion src/credential.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use openidconnect::{
JweKeyManagementAlgorithm, Nonce,
};
use serde::{Deserialize, Serialize};
use ssi::jwk::JWK;
use ssi_jwk::JWK;

use crate::{
http_utils::{auth_bearer, content_type_has_essence, MIME_TYPE_JSON},
Expand Down
120 changes: 71 additions & 49 deletions src/proof_of_possession.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use openidconnect::Nonce;
use serde::{Deserialize, Serialize};
use ssi::{
did::{Resource, VerificationMethod, DIDURL},
did_resolve::{dereference, Content, DIDResolver, DereferencingInputMetadata},
jwk::{Algorithm, JWK},
use ssi_claims::{
jws::{self, Header},
jwt,
};
use ssi_dids_core::DIDURLBuf;
use ssi_jwk::{Algorithm, JWKResolver, JWK};
use time::{Duration, OffsetDateTime};
use url::Url;

Expand Down Expand Up @@ -61,7 +60,7 @@ pub struct ProofOfPossession {

#[derive(Debug, Clone)]
pub struct ProofOfPossessionController {
pub vm: Option<DIDURL>,
pub vm: Option<DIDURLBuf>,
pub jwk: JWK,
}

Expand All @@ -76,7 +75,7 @@ pub struct ProofOfPossessionVerificationParams {
pub audience: Url,
pub issuer: String,
pub nonce: Nonce,
pub controller_did: Option<String>,
pub controller_did: Option<DIDURLBuf>,
pub controller_jwk: Option<JWK>,
/// Slack in nbf validation to deal with clock synchronisation issues.
pub nbf_tolerance: Option<Duration>,
Expand Down Expand Up @@ -107,15 +106,15 @@ pub enum ConversionError {
#[error(transparent)]
SerializationError(#[from] serde_json::Error),
#[error(transparent)]
SigningError(#[from] ssi::jws::Error),
SigningError(#[from] ssi_claims::jws::Error),
#[error("Unable to select JWT algorithm, please specify in JWK")]
MissingJWKAlg,
}

#[derive(thiserror::Error, Debug)]
pub enum ParsingError {
#[error(transparent)]
InvalidJWS(#[from] ssi::jws::Error),
InvalidJWS(#[from] ssi_claims::jws::Error),
#[error("JWS type header is invalid, expected `{expected}`, found `{actual}`")]
InvalidJWSType { actual: String, expected: String },
#[error("JWS does not specify an algorithm")]
Expand All @@ -127,7 +126,11 @@ pub enum ParsingError {
#[error("Could not retrieve JWK from KID: {0}")]
KIDDereferenceError(String),
#[error(transparent)]
DIDDereferenceError(#[from] ssi::did::Error),
DIDDereferenceError(#[from] ssi_dids_core::resolution::Error),
#[error(transparent)]
InvalidDIDURL(#[from] ssi_dids_core::InvalidDIDURL<String>),
#[error(transparent)]
ProofValidationError(#[from] ssi_claims::ProofValidationError),
}

impl ProofOfPossession {
Expand Down Expand Up @@ -156,7 +159,7 @@ impl ProofOfPossession {
};
let payload = serde_json::to_string(&self.body)?;
let (h_kid, h_jwk) = match (self.controller.vm.clone(), jwk.key_id.clone()) {
(Some(vm), _) => (Some(vm.did), None),
(Some(vm), _) => (Some(vm.to_string()), None),
(None, Some(kid)) => (Some(kid), None),
(None, None) => (None, Some(jwk.to_public())),
};
Expand All @@ -172,15 +175,15 @@ impl ProofOfPossession {

pub async fn from_proof(
proof: &Proof,
resolver: &dyn DIDResolver,
resolver: impl JWKResolver,
) -> Result<Self, ParsingError> {
match proof {
Proof::JWT { jwt } => Self::from_jwt(jwt, resolver).await,
Proof::CWT { .. } => todo!(),
}
}

pub async fn from_jwt(jwt: &str, resolver: &dyn DIDResolver) -> Result<Self, ParsingError> {
pub async fn from_jwt(jwt: &str, resolver: impl JWKResolver) -> Result<Self, ParsingError> {
let header: Header = jws::decode_unverified(jwt)?.0;

if header.type_ != Some(JWS_TYPE.to_string()) {
Expand All @@ -195,9 +198,11 @@ impl ProofOfPossession {
let (controller, jwk) = match (header.key_id, header.jwk, header.x509_certificate_chain) {
(Some(kid), None, None) => {
let vm = kid.parse()?;
get_jwk_from_kid(&kid, resolver)
//get_jwk_from_kid(&kid, resolver)
resolver
.fetch_public_jwk(Some(&kid))
.await
.map(|r| (Some(vm), r))?
.map(|r| (Some(vm), r.into_owned()))?
}
(None, Some(jwk), None) => (None, jwk),
(None, None, Some(_x5c)) => {
Expand Down Expand Up @@ -257,7 +262,7 @@ impl ProofOfPossession {
if let Some(did) = &params.controller_did {
if self.controller.vm.is_none() {
return Err(VerificationError::InvalidDID {
expected: did.clone(),
expected: did.to_string(),
actual: format!("{:?}", self.controller.vm),
});
}
Expand All @@ -267,42 +272,20 @@ impl ProofOfPossession {
}
}

async fn get_jwk_from_kid(kid: &str, resolver: &dyn DIDResolver) -> Result<JWK, ParsingError> {
let (_, content, _) = dereference(resolver, kid, &DereferencingInputMetadata::default()).await;

let vm = match content {
Content::Object(Resource::VerificationMethod(vm)) => Ok(vm),
Content::DIDDocument(document) => {
if let VerificationMethod::Map(vm) =
document.verification_method.unwrap().first().unwrap()
{
Ok(vm.to_owned())
} else {
Err(ParsingError::KIDDereferenceError(
"could not find any verification method".into(),
))
}
}

_ => Err(ParsingError::KIDDereferenceError(
"could not find specified verification method".into(),
)),
}?;

Ok(vm.get_jwk()?)
}

#[cfg(test)]
mod test {
use did_jwk::DIDJWK;
use did_method_key::DIDKey;
use serde_json::json;
use ssi::did::{DIDMethod, Source};
use ssi_dids_core::{DIDResolver, VerificationMethodDIDResolver};
use ssi_jwk::JWK;
use ssi_verification_methods::AnyMethod;

use super::*;

fn generate_pop(expires_in: Duration) -> (ProofOfPossession, String) {
let jwk = serde_json::from_value(json!({"kty":"OKP","crv":"Ed25519","x":"h3GzIK3pU8oTspVBKstiPSHR3VH_USS2FA0NrAOZ51s","d":"pfYMFvJ-LlMO4-EBBsrjpfAVz5UEYNVgbTphLPZypbE"})).unwrap();
let did = DIDJWK.generate(&Source::Key(&jwk)).unwrap();
fn generate_pop(expires_in: Duration) -> (ProofOfPossession, DIDURLBuf) {
let jwk: JWK = serde_json::from_value(json!({"kty":"OKP","crv":"Ed25519","x":"h3GzIK3pU8oTspVBKstiPSHR3VH_USS2FA0NrAOZ51s","d":"pfYMFvJ-LlMO4-EBBsrjpfAVz5UEYNVgbTphLPZypbE"})).unwrap();
let did_url = DIDJWK::generate_url(&jwk);

(
ProofOfPossession::generate(
Expand All @@ -312,12 +295,12 @@ mod test {
nonce: None,
controller: ProofOfPossessionController {
jwk,
vm: Some(did.parse().unwrap()),
vm: Some(did_url.clone()),
},
},
expires_in,
),
did,
did_url,
)
}

Expand All @@ -329,7 +312,8 @@ mod test {

let pop_jwt = pop.to_jwt().unwrap();

let pop = ProofOfPossession::from_jwt(&pop_jwt, &DIDJWK)
let resolver: VerificationMethodDIDResolver<_, AnyMethod> = DIDJWK.into_vm_resolver();
let pop = ProofOfPossession::from_jwt(&pop_jwt, resolver)
.await
.unwrap();

Expand All @@ -346,6 +330,42 @@ mod test {
.unwrap();
}

#[tokio::test]
async fn basic_didkey_p256() {
let expires_in = Duration::minutes(5);
let jwk = JWK::generate_p256();
let did_url = DIDKey::generate_url(&jwk).unwrap();
let pop_jwt = ProofOfPossession::generate(
&ProofOfPossessionParams {
issuer: "test".to_string(),
audience: Url::parse("http://localhost:300").unwrap(),
nonce: None,
controller: ProofOfPossessionController {
jwk,
vm: Some(did_url.clone()),
},
},
expires_in,
)
.to_jwt()
.unwrap();
let resolver: VerificationMethodDIDResolver<_, AnyMethod> = DIDKey.into_vm_resolver();
let pop = ProofOfPossession::from_jwt(&pop_jwt, resolver)
.await
.unwrap();
pop.verify(&ProofOfPossessionVerificationParams {
nonce: pop.body.nonce.clone(),
audience: pop.body.audience.clone(),
issuer: "test".to_string(),
controller_did: Some(did_url),
controller_jwk: None,
nbf_tolerance: None,
exp_tolerance: None,
})
.await
.unwrap();
}

#[tokio::test]
async fn nbf_tolerance() {
let expires_in = Duration::minutes(5);
Expand All @@ -359,7 +379,8 @@ mod test {

let pop_jwt = pop.to_jwt().unwrap();

let pop = ProofOfPossession::from_jwt(&pop_jwt, &DIDJWK)
let resolver: VerificationMethodDIDResolver<_, AnyMethod> = DIDJWK.into_vm_resolver();
let pop = ProofOfPossession::from_jwt(&pop_jwt, resolver)
.await
.unwrap();

Expand Down Expand Up @@ -393,7 +414,8 @@ mod test {

let pop_jwt = pop.to_jwt().unwrap();

let pop = ProofOfPossession::from_jwt(&pop_jwt, &DIDJWK)
let resolver: VerificationMethodDIDResolver<_, AnyMethod> = DIDJWK.into_vm_resolver();
let pop = ProofOfPossession::from_jwt(&pop_jwt, resolver)
.await
.unwrap();

Expand Down

0 comments on commit 5ccb842

Please sign in to comment.