diff --git a/src/definitions/helpers/non_empty_vec.rs b/src/definitions/helpers/non_empty_vec.rs index 21123f5..949c634 100644 --- a/src/definitions/helpers/non_empty_vec.rs +++ b/src/definitions/helpers/non_empty_vec.rs @@ -58,6 +58,22 @@ impl NonEmptyVec { } } +impl NonEmptyVec { + pub fn try_from_iter>(iter: I) -> Result { + let mut v = Vec::new(); + let mut iter = iter.into_iter(); + if let Some(t) = iter.next() { + v.push(t); + } else { + return Err(Error::Empty); + } + for t in iter { + v.push(t); + } + Ok(NonEmptyVec(v)) + } +} + impl TryFrom> for NonEmptyVec { type Error = Error; diff --git a/src/definitions/x509/x5chain.rs b/src/definitions/x509/x5chain.rs index ee83662..036c344 100644 --- a/src/definitions/x509/x5chain.rs +++ b/src/definitions/x509/x5chain.rs @@ -1,3 +1,4 @@ +use crate::definitions::helpers::non_empty_vec; use crate::definitions::helpers::NonEmptyVec; use crate::definitions::x509::error::Error as X509Error; use crate::definitions::x509::trust_anchor::check_validity_period; @@ -77,6 +78,22 @@ impl From> for X5Chain { } } +impl TryFrom> for X5Chain { + type Error = non_empty_vec::Error; + + fn try_from(v: Vec) -> Result { + NonEmptyVec::try_from_iter(v.into_iter()).map(Self) + } +} + +impl TryFrom>> for X5Chain { + type Error = non_empty_vec::Error; + + fn try_from(v: Vec>) -> Result { + NonEmptyVec::try_from_iter(v.into_iter().map(|bytes| X509 { bytes })).map(Self) + } +} + impl X5Chain { pub fn builder() -> Builder { Builder::default() @@ -124,7 +141,9 @@ impl X5Chain { } } - pub fn get_signer_key(&self) -> Result { + /// Returns the first certificate in the x.509 certificate chain, + /// which is expected be the reader's certificate. + pub fn get_public_key(&self) -> Result { let leaf = self.0.first().ok_or(X509Error::CborDecodingError)?; leaf.public_key().map(|key| key.into()) } diff --git a/src/presentation/mdoc_auth.rs b/src/presentation/mdoc_auth.rs index c8941ed..0fe1f89 100644 --- a/src/presentation/mdoc_auth.rs +++ b/src/presentation/mdoc_auth.rs @@ -19,7 +19,7 @@ use ssi_jwk::Params; use ssi_jwk::JWK as SsiJwk; pub fn issuer_authentication(x5chain: X5Chain, issuer_signed: &IssuerSigned) -> Result<(), Error> { - let signer_key = x5chain.get_signer_key()?; + let signer_key = x5chain.get_public_key()?; let verification_result: cose::sign1::VerificationResult = issuer_signed .issuer_auth diff --git a/src/presentation/mod.rs b/src/presentation/mod.rs index a829514..6cc6a55 100644 --- a/src/presentation/mod.rs +++ b/src/presentation/mod.rs @@ -76,7 +76,7 @@ pub trait Stringify: Serialize + for<'a> Deserialize<'a> { /// Deserialize the object from the [CBOR](https://cbor.io) representation. /// - /// You can call this on something returned by [Stringify::stringify]. + /// You can call this on something returned by [Stringify::stringify]. /// Operation may fail, so it returns a [Result]. /// /// # Example diff --git a/src/presentation/reader.rs b/src/presentation/reader.rs index f5a3308..d89c621 100644 --- a/src/presentation/reader.rs +++ b/src/presentation/reader.rs @@ -36,18 +36,14 @@ use crate::{ }, definitions::{DeviceEngagement, DeviceResponse, SessionData, SessionTranscript180135}, }; -use aes::cipher::{generic_array::GenericArray, typenum::U32}; use anyhow::{anyhow, Result}; use coset::{CoseSign1Builder, Header, Label}; -// use cose_rs::algorithm::Algorithm; -// use cose_rs::sign1::HeaderMap; -// use cose_rs::CoseSign1; -use p256::ecdsa::SigningKey; -use sec1::DecodeEcPrivateKey; use serde::{Deserialize, Serialize}; use serde_json::json; use serde_json::Value; use std::collections::BTreeMap; +use std::future::Future; +use std::pin::Pin; use uuid::Uuid; /// The main state of the reader. @@ -64,7 +60,6 @@ pub struct SessionManager { sk_reader: [u8; 32], reader_message_counter: u32, trust_anchor_registry: Option, - reader_auth_key: [u8; 32], reader_x5chain: X5Chain, } @@ -185,12 +180,12 @@ impl SessionManager { /// Internally it generates the ephemeral keys, /// derives the shared secret, and derives the session keys /// (using **Diffie–Hellman key exchange**). - pub fn establish_session( + pub async fn establish_session( qr_code: String, namespaces: device_request::Namespaces, trust_anchor_registry: Option, reader_x5chain: X5Chain, - reader_key: &str, + reader_signer: impl FnOnce(Vec) -> Pin>> + Send>>, ) -> Result<(Self, Vec, [u8; 16])> { let device_engagement_bytes = Tag24::::from_qr_code_uri(&qr_code).map_err(|e| anyhow!(e))?; @@ -226,9 +221,6 @@ impl SessionManager { let sk_device = derive_session_key(&shared_secret, &session_transcript_bytes, false)?.into(); - let reader_signing_key: SigningKey = ecdsa::SigningKey::from_sec1_pem(reader_key)?; - let reader_auth_key: GenericArray = reader_signing_key.to_bytes(); - let mut session_manager = Self { session_transcript, sk_device, @@ -236,11 +228,14 @@ impl SessionManager { sk_reader, reader_message_counter: 0, trust_anchor_registry, - reader_auth_key: reader_auth_key.into(), + // reader_auth_key: reader_auth_key.into(), reader_x5chain, }; - let request = session_manager.build_request(namespaces)?; + let request = session_manager + .build_request(namespaces, reader_signer) + .await?; + let session = SessionEstablishment { data: request.into(), e_reader_key: e_reader_key_public, @@ -270,8 +265,12 @@ impl SessionManager { } /// Creates a new request with specified elements to request. - pub fn new_request(&mut self, namespaces: device_request::Namespaces) -> Result> { - let request = self.build_request(namespaces)?; + pub async fn new_request( + &mut self, + namespaces: device_request::Namespaces, + reader_signer: impl FnOnce(Vec) -> Pin>> + Send>>, + ) -> Result> { + let request = self.build_request(namespaces, reader_signer).await?; let session = SessionData { data: Some(request.into()), status: None, @@ -279,7 +278,11 @@ impl SessionManager { cbor::to_vec(&session).map_err(Into::into) } - fn build_request(&mut self, namespaces: device_request::Namespaces) -> Result> { + async fn build_request( + &mut self, + namespaces: device_request::Namespaces, + reader_signer: impl FnOnce(Vec) -> Pin>> + Send>>, + ) -> Result> { // if !validate_request(namespaces.clone()).is_ok() { // return Err(anyhow::Error::msg( // "At least one of the namespaces contain an invalid combination of fields to request", @@ -305,12 +308,12 @@ impl SessionManager { Tag24::new(items_request.clone())?, ); - let reader_signing_key = SigningKey::from_slice(&self.reader_auth_key)?; //SigningKey::from_bytes(self.reader_auth_key.to_vec()); - let signature = reader_signing_key.sign_recoverable(&cbor::to_vec(&payload)?)?; + let signature = reader_signer(cbor::to_vec(&payload)?).await?; + let cose_sign1 = CoseSign1Builder::new() .unprotected(header_map) .payload(cbor::to_vec(&payload)?) - .signature(signature.0.to_vec()) + .signature(signature) .build(); let doc_request = DocRequest { diff --git a/tests/simulated_device_and_reader_state.rs b/tests/simulated_device_and_reader_state.rs index 674bf75..afa15a9 100644 --- a/tests/simulated_device_and_reader_state.rs +++ b/tests/simulated_device_and_reader_state.rs @@ -97,7 +97,7 @@ fn establish_reader_session(qr: String) -> Result<(reader::SessionManager, Vec,; + let trust_anchor_registry = None; // Option; let reader_x5chain = X5Chain::builder() .with_der(include_bytes!("../test/issuance/256-cert.der"))? .build()?;