Skip to content

Commit 4455ed2

Browse files
committed
update document content
1 parent 2b9282f commit 4455ed2

File tree

7 files changed

+94
-185
lines changed

7 files changed

+94
-185
lines changed

rust/signed_doc/src/content.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//! Catalyst Signed Document Content Payload
2+
3+
use crate::metadata::{ContentEncoding, ContentType};
4+
5+
/// Decompressed Document Content type bytes.
6+
#[derive(Debug, Clone, PartialEq)]
7+
pub struct Content(Vec<u8>, ContentType);
8+
9+
impl Content {
10+
/// Creates a new `Content` value,
11+
/// verifies a Document's content, that it is correctly encoded and it corresponds and
12+
/// parsed to the specified type
13+
pub fn new(
14+
mut content: Vec<u8>, content_type: ContentType, encoding: Option<ContentEncoding>,
15+
) -> anyhow::Result<Self> {
16+
if let Some(content_encoding) = encoding {
17+
content = content_encoding
18+
.decode(content.as_slice())
19+
.map_err(|e| anyhow::anyhow!("Failed to decode {encoding:?} content: {e}"))?;
20+
}
21+
22+
match content_type {
23+
ContentType::Json => {
24+
serde_json::from_slice::<serde_json::Value>(content.as_slice())?;
25+
},
26+
ContentType::Cbor => {
27+
// TODO impelement a CBOR parsing validation
28+
},
29+
}
30+
31+
Ok(Self(content, content_type))
32+
}
33+
34+
/// Return `true` if Document's content type is Json
35+
#[must_use]
36+
pub fn is_json(&self) -> bool {
37+
matches!(self.1, ContentType::Json)
38+
}
39+
40+
/// Return `true` if Document's content type is Json
41+
#[must_use]
42+
pub fn is_cbor(&self) -> bool {
43+
matches!(self.1, ContentType::Cbor)
44+
}
45+
46+
/// Return content bytes
47+
pub fn bytes(&self) -> Vec<u8> {
48+
self.0.clone()
49+
}
50+
}

rust/signed_doc/src/lib.rs

Lines changed: 41 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,26 @@ use std::{
66
};
77

88
use anyhow::anyhow;
9+
use content::Content;
910
use coset::{CborSerializable, CoseSignature};
1011

12+
mod content;
1113
mod error;
1214
mod metadata;
13-
mod payload;
1415
mod signature;
1516

1617
pub use metadata::{DocumentRef, Metadata, UuidV7};
17-
use payload::JsonContent;
1818
pub use signature::KidUri;
1919
use signature::Signatures;
2020

2121
/// Inner type that holds the Catalyst Signed Document with parsing errors.
22-
#[derive(Default)]
2322
struct InnerCatalystSignedDocument {
2423
/// Document Metadata
2524
metadata: Metadata,
26-
/// Document Payload viewed as JSON Content
27-
payload: JsonContent,
25+
/// Document Content
26+
content: Content,
2827
/// Signatures
2928
signatures: Signatures,
30-
/// Raw COSE Sign data
31-
cose_sign: coset::CoseSign,
3229
}
3330

3431
/// Keep all the contents private.
@@ -43,15 +40,9 @@ pub struct CatalystSignedDocument {
4340
impl Display for CatalystSignedDocument {
4441
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
4542
writeln!(f, "{}", self.inner.metadata)?;
46-
writeln!(f, "{:#?}\n", self.inner.payload)?;
4743
writeln!(f, "Signature Information [")?;
48-
for signature in &self.inner.cose_sign.signatures {
49-
writeln!(
50-
f,
51-
" {} 0x{:#}",
52-
String::from_utf8_lossy(&signature.protected.header.key_id),
53-
hex::encode(signature.signature.as_slice())
54-
)?;
44+
for kid in &self.inner.signatures.kids() {
45+
writeln!(f, " {kid}")?;
5546
}
5647
writeln!(f, "]\n")
5748
}
@@ -67,44 +58,47 @@ impl TryFrom<&[u8]> for CatalystSignedDocument {
6758

6859
let metadata = Metadata::try_from(&cose_sign.protected)?;
6960

70-
let mut content_errors = Vec::new();
71-
let mut payload = JsonContent::default();
72-
73-
if let Some(bytes) = &cose_sign.payload {
74-
match JsonContent::try_from((bytes.as_ref(), metadata.content_encoding())) {
75-
Ok(c) => payload = c,
76-
Err(e) => {
77-
content_errors.push(anyhow!("Invalid Payload: {e}"));
78-
},
79-
}
80-
} else {
81-
content_errors.push(anyhow!("COSE payload is empty"));
82-
};
61+
let mut errors = Vec::new();
8362

8463
let mut signatures = Signatures::default();
8564
match Signatures::try_from(&cose_sign.signatures) {
8665
Ok(s) => signatures = s,
87-
Err(errors) => {
88-
for e in errors.errors() {
89-
content_errors.push(anyhow!("{e}"));
66+
Err(sign_errors) => {
67+
for e in sign_errors.errors() {
68+
errors.push(anyhow!("{e}"));
9069
}
9170
},
9271
}
9372

94-
let inner = InnerCatalystSignedDocument {
95-
metadata,
96-
payload,
97-
signatures,
98-
cose_sign,
99-
};
100-
101-
if !content_errors.is_empty() {
102-
return Err(error::Error(content_errors));
73+
if let Some(payload) = cose_sign.payload {
74+
match Content::new(
75+
payload,
76+
metadata.content_type(),
77+
metadata.content_encoding(),
78+
) {
79+
Ok(content) => {
80+
if !errors.is_empty() {
81+
return Err(error::Error(errors));
82+
}
83+
84+
Ok(CatalystSignedDocument {
85+
inner: InnerCatalystSignedDocument {
86+
metadata,
87+
content,
88+
signatures,
89+
}
90+
.into(),
91+
})
92+
},
93+
Err(e) => {
94+
errors.push(anyhow::anyhow!("Invalid Document Content: {e}"));
95+
Err(error::Error(errors))
96+
},
97+
}
98+
} else {
99+
errors.push(anyhow!("Document content is missing"));
100+
Err(error::Error(errors))
103101
}
104-
105-
Ok(CatalystSignedDocument {
106-
inner: Arc::new(inner),
107-
})
108102
}
109103
}
110104

@@ -129,34 +123,10 @@ impl CatalystSignedDocument {
129123
self.inner.metadata.doc_ver()
130124
}
131125

132-
/// Return Last Document Reference `Option<DocumentRef>`.
133-
#[must_use]
134-
pub fn doc_ref(&self) -> Option<DocumentRef> {
135-
self.inner.metadata.doc_ref()
136-
}
137-
138-
/// Return Document Template `Option<DocumentRef>`.
139-
#[must_use]
140-
pub fn doc_template(&self) -> Option<DocumentRef> {
141-
self.inner.metadata.doc_template()
142-
}
143-
144-
/// Return Document Reply `Option<DocumentRef>`.
145-
#[must_use]
146-
pub fn doc_reply(&self) -> Option<DocumentRef> {
147-
self.inner.metadata.doc_reply()
148-
}
149-
150-
/// Return Document Reply `Option<DocumentRef>`.
151-
#[must_use]
152-
pub fn doc_section(&self) -> Option<String> {
153-
self.inner.metadata.doc_section()
154-
}
155-
156-
/// Return Raw COSE SIGN bytes.
126+
/// Return document `Content`.
157127
#[must_use]
158-
pub fn cose_sign_bytes(&self) -> Vec<u8> {
159-
self.inner.cose_sign.clone().to_vec().unwrap_or_default()
128+
pub fn document_content(&self) -> &Content {
129+
&self.inner.content
160130
}
161131

162132
/// Return a list of signature KIDs.

rust/signed_doc/src/metadata/content_encoding.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,11 @@ impl TryFrom<&coset::cbor::Value> for ContentEncoding {
5656

5757
impl ContentEncoding {
5858
/// Decompress a Brotli payload
59-
pub fn decode(self, payload: &Vec<u8>) -> anyhow::Result<Vec<u8>> {
59+
pub fn decode(self, mut payload: &[u8]) -> anyhow::Result<Vec<u8>> {
6060
match self {
6161
Self::Brotli => {
6262
let mut buf = Vec::new();
63-
let mut bytes = payload.as_slice();
64-
brotli::BrotliDecompress(&mut bytes, &mut buf)?;
63+
brotli::BrotliDecompress(&mut payload, &mut buf)?;
6564
Ok(buf)
6665
},
6766
}

rust/signed_doc/src/metadata/content_type.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use coset::iana::CoapContentFormat;
99
use serde::{de, Deserialize, Deserializer};
1010

1111
/// Payload Content Type.
12-
#[derive(Copy, Clone, Debug)]
12+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1313
pub enum ContentType {
1414
/// 'application/cbor'
1515
Cbor,

rust/signed_doc/src/metadata/mod.rs

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -43,18 +43,9 @@ pub struct Metadata {
4343
/// Additional Metadata Fields.
4444
#[serde(flatten)]
4545
extra: AdditionalFields,
46-
/// Metadata Content Errors
47-
#[serde(skip)]
48-
content_errors: Vec<String>,
4946
}
5047

5148
impl Metadata {
52-
/// Returns true if metadata has no validation errors.
53-
#[must_use]
54-
pub fn is_valid(&self) -> bool {
55-
self.content_errors.is_empty()
56-
}
57-
5849
/// Return Document Type `UUIDv4`.
5950
#[must_use]
6051
pub fn doc_type(&self) -> uuid::Uuid {
@@ -84,36 +75,6 @@ impl Metadata {
8475
pub fn content_encoding(&self) -> Option<ContentEncoding> {
8576
self.content_encoding
8677
}
87-
88-
/// Return Last Document Reference `Option<DocumentRef>`.
89-
#[must_use]
90-
pub fn doc_ref(&self) -> Option<DocumentRef> {
91-
self.extra.doc_ref
92-
}
93-
94-
/// Return Document Template `Option<DocumentRef>`.
95-
#[must_use]
96-
pub fn doc_template(&self) -> Option<DocumentRef> {
97-
self.extra.template
98-
}
99-
100-
/// Return Document Reply `Option<DocumentRef>`.
101-
#[must_use]
102-
pub fn doc_reply(&self) -> Option<DocumentRef> {
103-
self.extra.reply
104-
}
105-
106-
/// Return Document Section `Option<String>`.
107-
#[must_use]
108-
pub fn doc_section(&self) -> Option<String> {
109-
self.extra.section.clone()
110-
}
111-
112-
/// List of Content Errors.
113-
#[must_use]
114-
pub fn content_errors(&self) -> &Vec<String> {
115-
&self.content_errors
116-
}
11778
}
11879

11980
impl Display for Metadata {
@@ -138,7 +99,6 @@ impl Default for Metadata {
13899
content_type: ContentType::default(),
139100
content_encoding: None,
140101
extra: AdditionalFields::default(),
141-
content_errors: Vec::new(),
142102
}
143103
}
144104
}

rust/signed_doc/src/payload/json.rs

Lines changed: 0 additions & 38 deletions
This file was deleted.

rust/signed_doc/src/payload/mod.rs

Lines changed: 0 additions & 32 deletions
This file was deleted.

0 commit comments

Comments
 (0)