Skip to content

Commit 79e6014

Browse files
fix: Bring claim_v2 changes from #707 into c2pa_crypto (#811)
* Apply changes from merge commit 13889dd * Introduce `cose::TimeStampStorage` enum * Plumb TimeStampStorage through first layers of SDK * Port `sigTst2` changes from `claim_v2` branch. See 6028853 * Port `sigTst2` validation changes from claim_v2 branch. See 6028853 * Move P1363 format check earlier in validation. See 6028853 * Pick up `claim_v2` changes to ContentInfo parsing. See bfdadc1
1 parent 34b7139 commit 79e6014

File tree

12 files changed

+339
-66
lines changed

12 files changed

+339
-66
lines changed

internal/crypto/src/cose/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,8 @@ pub use sigtst::{
4242
TstToken,
4343
};
4444

45+
mod time_stamp_storage;
46+
pub use time_stamp_storage::TimeStampStorage;
47+
4548
mod verifier;
4649
pub use verifier::Verifier;

internal/crypto/src/cose/sign.rs

Lines changed: 150 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ use coset::{
1818
CoseSign1, CoseSign1Builder, Header, HeaderBuilder, Label, ProtectedHeader,
1919
TaggedCborSerializable,
2020
};
21+
use serde_bytes::ByteBuf;
2122

2223
use crate::{
23-
cose::{add_sigtst_header, add_sigtst_header_async, CoseError},
24+
cose::{add_sigtst_header, add_sigtst_header_async, CoseError, TimeStampStorage},
2425
p1363::{der_to_p1363, parse_ec_der_sig},
2526
raw_signature::{AsyncRawSigner, RawSigner},
2627
SigningAlg,
@@ -76,22 +77,60 @@ use crate::{
7677
#[async_generic(async_signature(
7778
signer: &dyn AsyncRawSigner,
7879
data: &[u8],
79-
box_size: usize
80+
box_size: usize,
81+
tss: TimeStampStorage
8082
))]
81-
pub fn sign(signer: &dyn RawSigner, data: &[u8], box_size: usize) -> Result<Vec<u8>, CoseError> {
83+
pub fn sign(
84+
signer: &dyn RawSigner,
85+
data: &[u8],
86+
box_size: usize,
87+
tss: TimeStampStorage,
88+
) -> Result<Vec<u8>, CoseError> {
89+
if _sync {
90+
match tss {
91+
TimeStampStorage::V1_sigTst => sign_v1(signer, data, box_size, tss),
92+
TimeStampStorage::V2_sigTst2_CTT => sign_v2(signer, data, box_size, tss),
93+
}
94+
} else {
95+
match tss {
96+
TimeStampStorage::V1_sigTst => sign_v1_async(signer, data, box_size, tss).await,
97+
TimeStampStorage::V2_sigTst2_CTT => sign_v2_async(signer, data, box_size, tss).await,
98+
}
99+
}
100+
}
101+
102+
#[async_generic(async_signature(
103+
signer: &dyn AsyncRawSigner,
104+
data: &[u8],
105+
box_size: usize,
106+
tss: TimeStampStorage
107+
))]
108+
pub fn sign_v1(
109+
signer: &dyn RawSigner,
110+
data: &[u8],
111+
box_size: usize,
112+
tss: TimeStampStorage,
113+
) -> Result<Vec<u8>, CoseError> {
82114
let alg = signer.alg();
83115

84-
let (protected_header, unprotected_header) = if _sync {
85-
build_headers(signer, data, alg)?
116+
let protected_header = if _sync {
117+
build_protected_header(signer, alg)?
86118
} else {
87-
build_headers_async(signer, data, alg).await?
119+
build_protected_header_async(signer, alg).await?
88120
};
89121

90122
// We don't use the additional data header.
91123
let aad: &[u8; 0] = b"";
92124

125+
// V1: Generate time stamp then sign.
126+
let unprotected_header = if _sync {
127+
build_unprotected_header(signer, data, &protected_header, tss)?
128+
} else {
129+
build_unprotected_header_async(signer, data, &protected_header, tss).await?
130+
};
131+
93132
let sign1_builder = CoseSign1Builder::new()
94-
.protected(protected_header)
133+
.protected(protected_header.header.clone())
95134
.unprotected(unprotected_header)
96135
.payload(data.to_vec());
97136

@@ -130,12 +169,89 @@ pub fn sign(signer: &dyn RawSigner, data: &[u8], box_size: usize) -> Result<Vec<
130169
pad_cose_sig(&mut sign1, box_size)
131170
}
132171

133-
#[async_generic(async_signature(signer: &dyn AsyncRawSigner, data: &[u8], alg: SigningAlg))]
134-
fn build_headers(
172+
#[async_generic(async_signature(
173+
signer: &dyn AsyncRawSigner,
174+
data: &[u8],
175+
box_size: usize,
176+
tss: TimeStampStorage
177+
))]
178+
pub fn sign_v2(
135179
signer: &dyn RawSigner,
136180
data: &[u8],
181+
box_size: usize,
182+
tss: TimeStampStorage,
183+
) -> Result<Vec<u8>, CoseError> {
184+
let alg = signer.alg();
185+
186+
let protected_header = if _sync {
187+
build_protected_header(signer, alg)?
188+
} else {
189+
build_protected_header_async(signer, alg).await?
190+
};
191+
192+
// We don't use the additional data header.
193+
let aad: &[u8; 0] = b"";
194+
195+
// V2: Sign then generate time stamp.
196+
let sign1_builder = CoseSign1Builder::new()
197+
.protected(protected_header.header.clone())
198+
.payload(data.to_vec());
199+
200+
let mut sign1 = sign1_builder.build();
201+
202+
let tbs = coset::sig_structure_data(
203+
coset::SignatureContext::CoseSign1,
204+
sign1.protected.clone(),
205+
None,
206+
aad,
207+
sign1.payload.as_ref().unwrap_or(&vec![]),
208+
);
209+
210+
let signature = if _sync {
211+
signer.sign(&tbs)?
212+
} else {
213+
signer.sign(tbs).await?
214+
};
215+
216+
// Fix up signatures that may be in the wrong format.
217+
sign1.signature = match alg {
218+
SigningAlg::Es256 | SigningAlg::Es384 | SigningAlg::Es512 => {
219+
if parse_ec_der_sig(&signature).is_ok() {
220+
// Fix up DER signature to be in P1363 format.
221+
der_to_p1363(&signature, alg)?
222+
} else {
223+
signature
224+
}
225+
}
226+
_ => signature,
227+
};
228+
229+
// The payload is provided elsewhere, so we don't need to repeat it in the
230+
// `Cose_Sign1` structure.
231+
sign1.payload = None;
232+
233+
let sig_data = ByteBuf::from(sign1.signature.clone());
234+
let mut sig_data_cbor: Vec<u8> = vec![];
235+
ciborium::into_writer(&sig_data, &mut sig_data_cbor)
236+
.map_err(|e| CoseError::CborGenerationError(e.to_string()))?;
237+
238+
// Fill in the unprotected header with time stamp data.
239+
let unprotected_header = if _sync {
240+
build_unprotected_header(signer, &sig_data_cbor, &protected_header, tss)?
241+
} else {
242+
build_unprotected_header_async(signer, &sig_data_cbor, &protected_header, tss).await?
243+
};
244+
245+
sign1.unprotected = unprotected_header;
246+
247+
pad_cose_sig(&mut sign1, box_size)
248+
}
249+
250+
#[async_generic(async_signature(signer: &dyn AsyncRawSigner, alg: SigningAlg))]
251+
fn build_protected_header(
252+
signer: &dyn RawSigner,
137253
alg: SigningAlg,
138-
) -> Result<(Header, Header), CoseError> {
254+
) -> Result<ProtectedHeader, CoseError> {
139255
let mut protected_h = match alg {
140256
SigningAlg::Ps256 => HeaderBuilder::new().algorithm(iana::Algorithm::PS256),
141257
SigningAlg::Ps384 => HeaderBuilder::new().algorithm(iana::Algorithm::PS384),
@@ -146,14 +262,6 @@ fn build_headers(
146262
SigningAlg::Ed25519 => HeaderBuilder::new().algorithm(iana::Algorithm::EdDSA),
147263
};
148264

149-
let ocsp_val = if _sync {
150-
signer.ocsp_response()
151-
} else {
152-
signer.ocsp_response().await
153-
};
154-
155-
dbg!(&ocsp_val);
156-
157265
let certs = signer.cert_chain()?;
158266

159267
let sc_der_array_or_bytes = match certs.len() {
@@ -173,15 +281,35 @@ fn build_headers(
173281
header: protected_header.clone(),
174282
};
175283

284+
Ok(ph2)
285+
}
286+
287+
#[async_generic(async_signature(signer: &dyn AsyncRawSigner, data: &[u8], p_header: &ProtectedHeader, tss: TimeStampStorage,))]
288+
fn build_unprotected_header(
289+
signer: &dyn RawSigner,
290+
data: &[u8],
291+
p_header: &ProtectedHeader,
292+
tss: TimeStampStorage,
293+
) -> Result<Header, CoseError> {
294+
// signed_data_from_time_stamp_response
295+
296+
// TO DO: Continue with diff here ... (let maybe_cts etc)
297+
176298
let unprotected_h = HeaderBuilder::new();
177299

178300
let mut unprotected_h = if _sync {
179-
add_sigtst_header(signer, data, &ph2, unprotected_h)?
301+
add_sigtst_header(signer, data, p_header, unprotected_h, tss)?
180302
} else {
181-
add_sigtst_header_async(signer, data, &ph2, unprotected_h).await?
303+
add_sigtst_header_async(signer, data, p_header, unprotected_h, tss).await?
182304
};
183305

184306
// Set the OCSP responder response if available.
307+
let ocsp_val = if _sync {
308+
signer.ocsp_response()
309+
} else {
310+
signer.ocsp_response().await
311+
};
312+
185313
if let Some(ocsp) = ocsp_val {
186314
let mut ocsp_vec: Vec<Value> = Vec::new();
187315
let mut r_vals: Vec<(Value, Value)> = vec![];
@@ -192,8 +320,8 @@ fn build_headers(
192320
unprotected_h = unprotected_h.text_value("rVals".to_string(), Value::Map(r_vals));
193321
}
194322

195-
// Build complete header
196-
Ok((protected_header, unprotected_h.build()))
323+
// Build complete header.
324+
Ok(unprotected_h.build())
197325
}
198326

199327
const PAD: &str = "pad";

internal/crypto/src/cose/sigtst.rs

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,19 @@
1111
// specific language governing permissions and limitations under
1212
// each license.
1313

14+
use asn1_rs::nom::AsBytes;
1415
use async_generic::async_generic;
16+
use bcder::decode::Constructed;
1517
use ciborium::value::Value;
1618
use coset::{sig_structure_data, HeaderBuilder, Label, ProtectedHeader, SignatureContext};
1719
use serde::{Deserialize, Serialize};
20+
use serde_bytes::ByteBuf;
1821

1922
use crate::{
20-
asn1::rfc3161::TstInfo,
21-
cose::CoseError,
23+
asn1::rfc3161::{TimeStampResp, TstInfo},
24+
cose::{CoseError, TimeStampStorage},
2225
raw_signature::{AsyncRawSigner, RawSigner},
23-
time_stamp::{verify_time_stamp, verify_time_stamp_async},
26+
time_stamp::{verify_time_stamp, verify_time_stamp_async, ContentInfo, TimeStampResponse},
2427
};
2528

2629
/// Given a COSE signature, retrieve the `sigTst` header from it and validate
@@ -29,13 +32,15 @@ use crate::{
2932
/// Return a [`TstInfo`] struct if available and valid.
3033
#[async_generic]
3134
pub fn validate_cose_tst_info(sign1: &coset::CoseSign1, data: &[u8]) -> Result<TstInfo, CoseError> {
32-
let Some(sigtst) = &sign1
35+
let Some((sigtst, tss)) = &sign1
3336
.unprotected
3437
.rest
3538
.iter()
3639
.find_map(|x: &(Label, Value)| {
37-
if x.0 == Label::Text("sigTst".to_string()) {
38-
Some(x.1.clone())
40+
if x.0 == Label::Text("sigTst2".to_string()) {
41+
Some((x.1.clone(), TimeStampStorage::V2_sigTst2_CTT))
42+
} else if x.0 == Label::Text("sigTst".to_string()) {
43+
Some((x.1.clone(), TimeStampStorage::V1_sigTst))
3944
} else {
4045
None
4146
}
@@ -44,14 +49,27 @@ pub fn validate_cose_tst_info(sign1: &coset::CoseSign1, data: &[u8]) -> Result<T
4449
return Err(CoseError::NoTimeStampToken);
4550
};
4651

52+
// `maybe_sig_data` has to be declared outside the match block below so that the
53+
// slice we return can live long enough.
54+
let mut maybe_sig_data: Vec<u8> = vec![];
55+
let tbs = match tss {
56+
TimeStampStorage::V1_sigTst => data,
57+
TimeStampStorage::V2_sigTst2_CTT => {
58+
let sig_data = ByteBuf::from(sign1.signature.clone());
59+
ciborium::into_writer(&sig_data, &mut maybe_sig_data)
60+
.map_err(|e| CoseError::CborParsingError(e.to_string()))?;
61+
maybe_sig_data.as_slice()
62+
}
63+
};
64+
4765
let mut time_cbor: Vec<u8> = vec![];
4866
ciborium::into_writer(sigtst, &mut time_cbor)
4967
.map_err(|e| CoseError::InternalError(e.to_string()))?;
5068

5169
let tst_infos = if _sync {
52-
parse_and_validate_sigtst(&time_cbor, data, &sign1.protected)?
70+
parse_and_validate_sigtst(&time_cbor, tbs, &sign1.protected)?
5371
} else {
54-
parse_and_validate_sigtst_async(&time_cbor, data, &sign1.protected).await?
72+
parse_and_validate_sigtst_async(&time_cbor, tbs, &sign1.protected).await?
5573
};
5674

5775
// For now, we only pay attention to the first time stamp header.
@@ -148,12 +166,14 @@ impl TstContainer {
148166
data: &[u8],
149167
p_header: &ProtectedHeader,
150168
mut header_builder: HeaderBuilder,
169+
tss: TimeStampStorage,
151170
))]
152171
pub fn add_sigtst_header(
153172
ts_provider: &dyn RawSigner,
154173
data: &[u8],
155174
p_header: &ProtectedHeader,
156175
mut header_builder: HeaderBuilder,
176+
tss: TimeStampStorage,
157177
) -> Result<HeaderBuilder, CoseError> {
158178
let sd = cose_countersign_data(data, p_header);
159179

@@ -164,7 +184,16 @@ pub fn add_sigtst_header(
164184
};
165185

166186
if let Some(cts) = maybe_cts {
167-
let cts = cts?;
187+
let mut cts = cts?;
188+
189+
if tss == TimeStampStorage::V2_sigTst2_CTT {
190+
// In `sigTst2`, we use only the `TimeStampToken` and not `TimeStampRsp` for
191+
// sigTst2
192+
cts = timestamptoken_from_timestamprsp(&cts).ok_or(CoseError::CborGenerationError(
193+
"unable to generate time stamp token".to_string(),
194+
))?;
195+
}
196+
168197
let cts = make_cose_timestamp(&cts);
169198

170199
let mut sigtst_vec: Vec<u8> = vec![];
@@ -174,7 +203,14 @@ pub fn add_sigtst_header(
174203
let sigtst_cbor: Value = ciborium::from_reader(sigtst_vec.as_slice())
175204
.map_err(|e| CoseError::CborGenerationError(e.to_string()))?;
176205

177-
header_builder = header_builder.text_value("sigTst".to_string(), sigtst_cbor);
206+
match tss {
207+
TimeStampStorage::V1_sigTst => {
208+
header_builder = header_builder.text_value("sigTst".to_string(), sigtst_cbor);
209+
}
210+
TimeStampStorage::V2_sigTst2_CTT => {
211+
header_builder = header_builder.text_value("sigTst2".to_string(), sigtst_cbor);
212+
}
213+
}
178214
}
179215

180216
Ok(header_builder)
@@ -191,3 +227,28 @@ fn make_cose_timestamp(ts_data: &[u8]) -> TstContainer {
191227

192228
container
193229
}
230+
231+
// Return timeStampToken used by sigTst2.
232+
fn timestamptoken_from_timestamprsp(ts: &[u8]) -> Option<Vec<u8>> {
233+
let ts_resp = TimeStampResponse(
234+
Constructed::decode(ts, bcder::Mode::Der, TimeStampResp::take_from).ok()?,
235+
);
236+
237+
let tst = ts_resp.0.time_stamp_token?;
238+
239+
let a: Result<Vec<u32>, CoseError> = tst
240+
.content_type
241+
.iter()
242+
.map(|v| {
243+
v.to_u32()
244+
.ok_or(CoseError::InternalError("invalid component".to_string()))
245+
})
246+
.collect();
247+
248+
let ci = ContentInfo {
249+
content_type: rasn::types::ObjectIdentifier::new(a.ok()?)?,
250+
content: rasn::types::Any::new(tst.content.as_bytes().to_vec()),
251+
};
252+
253+
rasn::der::encode(&ci).ok()
254+
}

0 commit comments

Comments
 (0)