Skip to content

Commit d173bf4

Browse files
committed
wip
1 parent 49b541e commit d173bf4

File tree

6 files changed

+88
-143
lines changed

6 files changed

+88
-143
lines changed

rust/signed_doc/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,14 @@ license.workspace = true
1111
workspace = true
1212

1313
[dependencies]
14-
rbac-registration = { version = "0.0.2", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "r20250128-01" }
1514
catalyst-types = { version = "0.0.1", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "r20250204-00" }
1615
anyhow = "1.0.95"
1716
serde = { version = "1.0.217", features = ["derive"] }
1817
serde_json = "1.0.134"
1918
coset = "0.3.8"
2019
minicbor = { version = "0.25.1", features = ["half"] }
2120
brotli = "7.0.0"
22-
ed25519-dalek = { version = "2.1.1", features = ["pem", "rand_core"] }
21+
ed25519-dalek = { version = "2.1.1" }
2322
hex = "0.4.3"
2423
strum = { version = "0.26.3", features = ["derive"] }
2524
clap = { version = "4.5.23", features = ["derive", "env"] }
@@ -30,6 +29,7 @@ jsonpath-rust = "0.7.5"
3029
[dev-dependencies]
3130
base64-url = "3.0.0"
3231
rand = "0.8.5"
32+
tokio = "1.42.0"
3333

3434

3535
[[bin]]

rust/signed_doc/examples/mk_signed_doc.rs

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ use std::{
88
path::PathBuf,
99
};
1010

11-
use catalyst_signed_doc::{Builder, CatalystSignedDocument, IdUri, Metadata, SimplePublicKeyType};
11+
use catalyst_signed_doc::{Builder, CatalystSignedDocument, IdUri, Metadata};
1212
use clap::Parser;
13-
use ed25519_dalek::pkcs8::{DecodePrivateKey, DecodePublicKey};
13+
use ed25519_dalek::pkcs8::DecodePrivateKey;
1414

1515
fn main() {
1616
if let Err(err) = Cli::parse().exec() {
@@ -51,16 +51,6 @@ enum Cli {
5151
/// Hex-formatted COSE SIGN Bytes
5252
cose_sign_hex: String,
5353
},
54-
/// Validates a signature by Key ID and verifying key
55-
Verify {
56-
/// Path to the formed (could be empty, without any signatures) COSE document
57-
/// This exact file would be modified and new signature would be added
58-
path: PathBuf,
59-
/// Path to the verifying key in PEM format
60-
pk: PathBuf,
61-
/// Signer kid
62-
kid: IdUri,
63-
},
6454
}
6555

6656
impl Cli {
@@ -99,22 +89,6 @@ impl Cli {
9989
let cose_bytes = hex::decode(&cose_sign_hex)?;
10090
inspect_signed_doc(&cose_bytes)?;
10191
},
102-
Self::Verify { path, pk, kid } => {
103-
let pk = load_public_key_from_file(&pk)
104-
.map_err(|e| anyhow::anyhow!("Failed to load PK FILE {pk:?}: {e}"))?;
105-
let cose_bytes = read_bytes_from_file(&path)?;
106-
let signed_doc = signed_doc_from_bytes(cose_bytes.as_slice())?;
107-
signed_doc
108-
.verify(|k| {
109-
if k.to_string() == kid.to_string() {
110-
SimplePublicKeyType::Ed25519(pk)
111-
} else {
112-
SimplePublicKeyType::Undefined
113-
}
114-
})
115-
.map_err(|e| anyhow::anyhow!("Catalyst Document Verification failed: {e}"))?;
116-
println!("Catalyst Signed Document is Verified.");
117-
},
11892
}
11993
println!("Done");
12094
Ok(())
@@ -171,9 +145,3 @@ fn load_secret_key_from_file(sk_path: &PathBuf) -> anyhow::Result<ed25519_dalek:
171145
let sk = ed25519_dalek::SigningKey::from_pkcs8_pem(&sk_str)?;
172146
Ok(sk)
173147
}
174-
175-
fn load_public_key_from_file(pk_path: &PathBuf) -> anyhow::Result<ed25519_dalek::VerifyingKey> {
176-
let pk_str = read_to_string(pk_path)?;
177-
let pk = ed25519_dalek::VerifyingKey::from_public_key_pem(&pk_str)?;
178-
Ok(pk)
179-
}

rust/signed_doc/src/lib.rs

Lines changed: 66 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub mod validator;
1212
use std::{
1313
convert::TryFrom,
1414
fmt::{Display, Formatter},
15+
future::Future,
1516
sync::Arc,
1617
};
1718

@@ -20,11 +21,11 @@ use catalyst_types::problem_report::ProblemReport;
2021
pub use catalyst_types::uuid::{Uuid, UuidV4, UuidV7};
2122
pub use content::Content;
2223
use coset::{CborSerializable, Header};
24+
use ed25519_dalek::VerifyingKey;
2325
use error::CatalystSignedDocError;
2426
use metadata::{ContentEncoding, ContentType};
2527
pub use metadata::{DocumentRef, ExtraFields, Metadata};
2628
pub use minicbor::{decode, encode, Decode, Decoder, Encode, Encoder};
27-
pub use rbac_registration::cardano::cip509::SimplePublicKeyType;
2829
pub use signature::{IdUri, Signatures};
2930
use utils::context::DecodeSignDocCtx;
3031

@@ -141,68 +142,62 @@ impl CatalystSignedDocument {
141142
/// # Errors
142143
///
143144
/// Returns a report of verification failures and the source error.
144-
#[allow(clippy::indexing_slicing)]
145-
pub fn verify<P>(&self, pk_getter: P) -> Result<(), CatalystSignedDocError>
146-
where P: Fn(&IdUri) -> SimplePublicKeyType {
147-
let error_report = ProblemReport::new("Catalyst Signed Document Verification");
148-
149-
match self.as_cose_sign() {
150-
Ok(cose_sign) => {
151-
let signatures = self.signatures().cose_signatures();
152-
for (idx, kid) in self.kids().iter().enumerate() {
153-
match pk_getter(kid) {
154-
SimplePublicKeyType::Ed25519(pk) => {
155-
let signature = &signatures[idx];
156-
let tbs_data = cose_sign.tbs_data(&[], signature);
157-
match signature.signature.as_slice().try_into() {
158-
Ok(signature_bytes) => {
159-
let signature =
160-
ed25519_dalek::Signature::from_bytes(signature_bytes);
161-
if let Err(e) = pk.verify_strict(&tbs_data, &signature) {
162-
error_report.functional_validation(
163-
&format!(
164-
"Verification failed for signature with Key ID {kid}: {e}"
165-
),
166-
"During signature validation with verifying key",
167-
);
168-
}
169-
},
170-
Err(_) => {
171-
error_report.invalid_value(
172-
"cose signature",
173-
&format!("{}", signature.signature.len()),
174-
&format!("must be {}", ed25519_dalek::Signature::BYTE_SIZE),
175-
"During encoding cose signature to bytes",
176-
);
177-
},
145+
pub async fn verify<P, T>(&self, pk_getter: P) -> Result<(), CatalystSignedDocError>
146+
where
147+
P: Fn(&IdUri) -> T,
148+
T: Future<Output = Option<VerifyingKey>>,
149+
{
150+
let report = ProblemReport::new("Catalyst Signed Document Verification");
151+
152+
let cose_sign = match self.as_cose_sign() {
153+
Ok(cose_sign) => cose_sign,
154+
Err(e) => {
155+
report.other(
156+
"Cannot build a COSE sign object",
157+
"During encoding signed document as COSE SIGN",
158+
);
159+
return Err(CatalystSignedDocError::new(report, e));
160+
},
161+
};
162+
163+
for (signature, kid) in self.signatures().cose_signatures_with_kids() {
164+
match pk_getter(kid).await {
165+
Some(pk) => {
166+
let tbs_data = cose_sign.tbs_data(&[], signature);
167+
match signature.signature.as_slice().try_into() {
168+
Ok(signature_bytes) => {
169+
let signature = ed25519_dalek::Signature::from_bytes(signature_bytes);
170+
if let Err(e) = pk.verify_strict(&tbs_data, &signature) {
171+
report.functional_validation(
172+
&format!(
173+
"Verification failed for signature with Key ID {kid}: {e}"
174+
),
175+
"During signature validation with verifying key",
176+
);
178177
}
179178
},
180-
SimplePublicKeyType::Deleted => {
181-
error_report.other(
182-
&format!("Public key for {kid} has been deleted."),
183-
"During public key extraction",
184-
);
185-
},
186-
SimplePublicKeyType::Undefined => {
187-
error_report.other(
188-
&format!("Public key for {kid} is undefined."),
189-
"During public key extraction",
179+
Err(_) => {
180+
report.invalid_value(
181+
"cose signature",
182+
&format!("{}", signature.signature.len()),
183+
&format!("must be {}", ed25519_dalek::Signature::BYTE_SIZE),
184+
"During encoding cose signature to bytes",
190185
);
191186
},
192187
}
193-
}
194-
},
195-
Err(e) => {
196-
error_report.other(
197-
&format!("{e}"),
198-
"During encoding signed document as COSE SIGN",
199-
);
200-
},
188+
},
189+
None => {
190+
report.other(
191+
&format!("Missing public key for {kid}."),
192+
"During public key extraction",
193+
);
194+
},
195+
}
201196
}
202197

203-
if error_report.is_problematic() {
198+
if report.is_problematic() {
204199
return Err(CatalystSignedDocError::new(
205-
error_report,
200+
report,
206201
anyhow::anyhow!("Verification failed for Catalyst Signed Document"),
207202
));
208203
}
@@ -222,10 +217,17 @@ impl CatalystSignedDocument {
222217

223218
/// Convert Catalyst Signed Document into `coset::CoseSign`
224219
fn as_cose_sign(&self) -> anyhow::Result<coset::CoseSign> {
225-
let mut cose_bytes: Vec<u8> = Vec::new();
226-
minicbor::encode(self, &mut cose_bytes)?;
227-
coset::CoseSign::from_slice(&cose_bytes)
228-
.map_err(|e| anyhow::anyhow!("encoding as COSE SIGN failed: {e}"))
220+
let protected_header = Header::try_from(&self.inner.metadata)
221+
.map_err(|e| anyhow::anyhow!("Failed to encode Document Metadata: {e}"))?;
222+
223+
let mut builder = coset::CoseSignBuilder::new()
224+
.protected(protected_header)
225+
.payload(self.inner.content.encoded_bytes()?);
226+
227+
for signature in self.signatures().cose_signatures() {
228+
builder = builder.add_signature(signature);
229+
}
230+
Ok(builder.build())
229231
}
230232
}
231233

@@ -335,24 +337,7 @@ impl Encode<()> for CatalystSignedDocument {
335337
fn encode<W: minicbor::encode::Write>(
336338
&self, e: &mut encode::Encoder<W>, _ctx: &mut (),
337339
) -> Result<(), encode::Error<W::Error>> {
338-
let protected_header = Header::try_from(&self.inner.metadata).map_err(|e| {
339-
minicbor::encode::Error::message(format!("Failed to encode Document Metadata: {e}"))
340-
})?;
341-
342-
let mut builder = coset::CoseSignBuilder::new()
343-
.protected(protected_header)
344-
.payload(
345-
self.inner
346-
.content
347-
.encoded_bytes()
348-
.map_err(encode::Error::message)?,
349-
);
350-
351-
for signature in self.signatures().cose_signatures() {
352-
builder = builder.add_signature(signature);
353-
}
354-
355-
let cose_sign = builder.build();
340+
let cose_sign = self.as_cose_sign().map_err(encode::Error::message)?;
356341

357342
let cose_bytes = cose_sign.to_vec().map_err(|e| {
358343
minicbor::encode::Error::message(format!("Failed to encode COSE Sign document: {e}"))
@@ -423,8 +408,8 @@ mod tests {
423408
assert_eq!(decoded.doc_meta(), metadata.extra());
424409
}
425410

426-
#[test]
427-
fn signature_verification_test() {
411+
#[tokio::test]
412+
async fn signature_verification_test() {
428413
let mut csprng = OsRng;
429414
let sk: SigningKey = SigningKey::generate(&mut csprng);
430415
let content = serde_json::to_vec(&serde_json::Value::Null).unwrap();
@@ -445,16 +430,8 @@ mod tests {
445430
.build()
446431
.unwrap();
447432

448-
assert!(signed_doc
449-
.verify(|_| { SimplePublicKeyType::Ed25519(pk) })
450-
.is_ok());
451-
452-
assert!(signed_doc
453-
.verify(|_| { SimplePublicKeyType::Undefined })
454-
.is_err());
433+
assert!(signed_doc.verify(|_| async { Some(pk) }).await.is_ok());
455434

456-
assert!(signed_doc
457-
.verify(|_| { SimplePublicKeyType::Deleted })
458-
.is_err());
435+
assert!(signed_doc.verify(|_| async { None }).await.is_err());
459436
}
460437
}

rust/signed_doc/src/signature/mod.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,32 +19,32 @@ pub struct Signature {
1919
pub struct Signatures(Vec<Signature>);
2020

2121
impl Signatures {
22-
/// Creates an empty signatures list.
23-
#[must_use]
24-
pub fn new() -> Self {
25-
Self(Vec::new())
26-
}
27-
2822
/// Return a list of author IDs (short form of Catalyst IDs).
2923
#[must_use]
30-
pub fn authors(&self) -> Vec<IdUri> {
24+
pub(crate) fn authors(&self) -> Vec<IdUri> {
3125
self.kids().into_iter().map(|k| k.as_short_id()).collect()
3226
}
3327

3428
/// Return a list of Document's Catalyst IDs.
3529
#[must_use]
36-
pub fn kids(&self) -> Vec<IdUri> {
30+
pub(crate) fn kids(&self) -> Vec<IdUri> {
3731
self.0.iter().map(|sig| sig.kid.clone()).collect()
3832
}
3933

40-
/// List of signatures.
41-
#[must_use]
42-
pub fn cose_signatures(&self) -> Vec<CoseSignature> {
43-
self.0.iter().map(|sig| sig.signature.clone()).collect()
34+
/// Iterator of COSE signatures object with kids.
35+
pub(crate) fn cose_signatures_with_kids(
36+
&self,
37+
) -> impl Iterator<Item = (&CoseSignature, &IdUri)> + use<'_> {
38+
self.0.iter().map(|sig| (&sig.signature, &sig.kid))
39+
}
40+
41+
/// List of COSE signatures object.
42+
pub(crate) fn cose_signatures(&self) -> impl Iterator<Item = CoseSignature> + use<'_> {
43+
self.0.iter().map(|sig| sig.signature.clone())
4444
}
4545

4646
/// Add a new signature
47-
pub fn push(&mut self, kid: IdUri, signature: CoseSignature) {
47+
pub(crate) fn push(&mut self, kid: IdUri, signature: CoseSignature) {
4848
self.0.push(Signature { kid, signature });
4949
}
5050

rust/signed_doc/src/validator/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub trait ValidationDataProvider {
1616
/// Get public keys
1717
fn get_public_key(&self, kid: &IdUri) -> Option<SimplePublicKeyType>;
1818
/// Get signed document by document reference
19-
fn get_doc_ref(&self, doc_ref: &DocumentRef) -> Option<CatalystSignedDocument>;
19+
fn get_doc(&self, doc_ref: &DocumentRef) -> Option<CatalystSignedDocument>;
2020
}
2121

2222
/// Stateless validation function rule type
@@ -59,7 +59,7 @@ where Self: 'static
5959
///
6060
/// Returns a report of validation failures and the source error.
6161
pub fn validate(
62-
doc: &CatalystSignedDocument, provider: &impl ValidationDataProvider,
62+
doc: &CatalystSignedDocument, doc_provider: &impl ValidationDataProvider,
6363
) -> Result<(), CatalystSignedDocError> {
6464
let report = ProblemReport::new("Catalyst Signed Document Validation");
6565

@@ -83,13 +83,13 @@ pub fn validate(
8383
match doc_type {
8484
DocumentType::ProposalDocument => {
8585
if let Ok(proposal_doc) = ProposalDocument::from_signed_doc(doc, &report) {
86-
proposal_doc.statefull_validation(provider, &report);
86+
proposal_doc.statefull_validation(doc_provider, &report);
8787
}
8888
},
8989
DocumentType::ProposalTemplate => {},
9090
DocumentType::CommentDocument => {
9191
if let Ok(comment_doc) = CommentDocument::from_signed_doc(doc, &report) {
92-
comment_doc.statefull_validation(provider, &report);
92+
comment_doc.statefull_validation(doc_provider, &report);
9393
}
9494
},
9595
DocumentType::CommentTemplate => {},

rust/signed_doc/src/validator/utils.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ pub(crate) fn validate_provided_doc(
1111
doc_ref: &DocumentRef, doc_name: &str, provider: &dyn ValidationDataProvider,
1212
report: &ProblemReport, validator: impl Fn(CatalystSignedDocument) -> bool,
1313
) -> bool {
14-
if let Some(doc) = provider.get_doc_ref(doc_ref) {
14+
if let Some(doc) = provider.get_doc(doc_ref) {
1515
validator(doc)
1616
} else {
1717
report.functional_validation(

0 commit comments

Comments
 (0)