Skip to content

Commit a612907

Browse files
committed
wip(rust/signed_doc): implement verification method
* verify signatures with public (verification) key * todo: verify UUIDs
1 parent 3d34e47 commit a612907

File tree

1 file changed

+70
-0
lines changed

1 file changed

+70
-0
lines changed

rust/signed_doc/src/lib.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub use builder::Builder;
1717
use catalyst_types::problem_report::ProblemReport;
1818
pub use content::Content;
1919
use coset::{CborSerializable, Header};
20+
use ed25519_dalek::VerifyingKey;
2021
use error::CatalystSignedDocError;
2122
pub use metadata::{DocumentRef, ExtraFields, Metadata, UuidV4, UuidV7};
2223
pub use minicbor::{decode, encode, Decode, Decoder, Encode};
@@ -105,6 +106,64 @@ impl CatalystSignedDocument {
105106
pub fn signatures(&self) -> &Signatures {
106107
&self.inner.signatures
107108
}
109+
110+
/// Verify document signatures and `UUID`s.
111+
///
112+
/// # Errors
113+
///
114+
/// Returns a report of verification failures and the source error.
115+
#[allow(clippy::indexing_slicing)]
116+
pub fn verify<P>(&self, pk_getter: P) -> Result<(), CatalystSignedDocError>
117+
where P: Fn(&KidUri) -> VerifyingKey {
118+
let error_report = ProblemReport::new("Catalyst Signed Document Verification");
119+
120+
match coset::CoseSign::try_from(self) {
121+
Ok(cose_sign) => {
122+
let signatures = self.signatures().cose_signatures();
123+
for (idx, kid) in self.signatures().kids().iter().enumerate() {
124+
let pk = pk_getter(kid);
125+
let signature = &signatures[idx];
126+
let tbs_data = cose_sign.tbs_data(&[], signature);
127+
match signature.signature.as_slice().try_into() {
128+
Ok(signature_bytes) => {
129+
let signature = ed25519_dalek::Signature::from_bytes(signature_bytes);
130+
if let Err(e) = pk.verify_strict(&tbs_data, &signature) {
131+
error_report.functional_validation(
132+
&format!(
133+
"Verification failed for signature with Key ID {kid}: {e}"
134+
),
135+
"During signature validation with verifying key",
136+
);
137+
}
138+
},
139+
Err(_) => {
140+
error_report.invalid_value(
141+
"cose signature",
142+
&format!("{}", signature.signature.len()),
143+
&format!("must be {}", ed25519_dalek::Signature::BYTE_SIZE),
144+
"During encoding cose signature to bytes",
145+
);
146+
},
147+
}
148+
}
149+
},
150+
Err(e) => {
151+
error_report.other(
152+
&format!("{e}"),
153+
"During encoding signed document as COSE SIGN",
154+
);
155+
},
156+
}
157+
158+
if error_report.is_problematic() {
159+
return Err(CatalystSignedDocError::new(
160+
error_report,
161+
anyhow::anyhow!("Verification failed for Catalyst Signed Document"),
162+
));
163+
}
164+
165+
Ok(())
166+
}
108167
}
109168

110169
impl TryFrom<&[u8]> for CatalystSignedDocument {
@@ -242,6 +301,17 @@ impl Encode<()> for CatalystSignedDocument {
242301
}
243302
}
244303

304+
impl TryFrom<&CatalystSignedDocument> for coset::CoseSign {
305+
type Error = anyhow::Error;
306+
307+
fn try_from(signed_doc: &CatalystSignedDocument) -> Result<Self, Self::Error> {
308+
let mut cose_bytes: Vec<u8> = Vec::new();
309+
minicbor::encode(signed_doc, &mut cose_bytes)?;
310+
coset::CoseSign::from_slice(&cose_bytes)
311+
.map_err(|e| anyhow::anyhow!("encoding COSE SIGN failed: {e}"))
312+
}
313+
}
314+
245315
#[cfg(test)]
246316
mod tests {
247317
use metadata::{ContentEncoding, ContentType};

0 commit comments

Comments
 (0)