@@ -136,14 +136,13 @@ fn sign_and_serialize<'p>(
136
136
. getattr ( crate :: intern!( py, "PKCS7Options" ) ) ?;
137
137
138
138
let raw_data = builder. getattr ( crate :: intern!( py, "_data" ) ) ?. extract ( ) ?;
139
- let data = if options. contains ( pkcs7_options. getattr ( crate :: intern!( py, "Binary" ) ) ?) ? {
140
- Cow :: Borrowed ( raw_data)
141
- } else {
142
- smime_canonicalize (
143
- raw_data,
144
- options. contains ( pkcs7_options. getattr ( crate :: intern!( py, "Text" ) ) ?) ?,
145
- )
146
- } ;
139
+ let text_mode = options. contains ( pkcs7_options. getattr ( crate :: intern!( py, "Text" ) ) ?) ?;
140
+ let ( data_with_header, data_without_header) =
141
+ if options. contains ( pkcs7_options. getattr ( crate :: intern!( py, "Binary" ) ) ?) ? {
142
+ ( Cow :: Borrowed ( raw_data) , Cow :: Borrowed ( raw_data) )
143
+ } else {
144
+ smime_canonicalize ( raw_data, text_mode)
145
+ } ;
147
146
148
147
let content_type_bytes = asn1:: write_single ( & PKCS7_DATA_OID ) ?;
149
148
let signing_time_bytes = asn1:: write_single ( & x509:: certificate:: time_from_chrono (
@@ -180,7 +179,7 @@ fn sign_and_serialize<'p>(
180
179
{
181
180
(
182
181
None ,
183
- x509:: sign:: sign_data ( py, py_private_key, py_hash_alg, & data ) ?,
182
+ x509:: sign:: sign_data ( py, py_private_key, py_hash_alg, & data_with_header ) ?,
184
183
)
185
184
} else {
186
185
let mut authenticated_attrs = vec ! [ ] ;
@@ -198,7 +197,8 @@ fn sign_and_serialize<'p>(
198
197
] ) ) ,
199
198
} ) ;
200
199
201
- let digest = asn1:: write_single ( & x509:: ocsp:: hash_data ( py, py_hash_alg, & data) ?) ?;
200
+ let digest =
201
+ asn1:: write_single ( & x509:: ocsp:: hash_data ( py, py_hash_alg, & data_with_header) ?) ?;
202
202
// Gross hack: copy to PyBytes to extend the lifetime to 'p
203
203
let digest_bytes = pyo3:: types:: PyBytes :: new ( py, & digest) ;
204
204
authenticated_attrs. push ( x509:: csr:: Attribute {
@@ -264,7 +264,7 @@ fn sign_and_serialize<'p>(
264
264
if options. contains ( pkcs7_options. getattr ( crate :: intern!( py, "DetachedSignature" ) ) ?) ? {
265
265
None
266
266
} else {
267
- data_tlv_bytes = asn1:: write_single ( & data . deref ( ) ) ?;
267
+ data_tlv_bytes = asn1:: write_single ( & data_with_header . deref ( ) ) ?;
268
268
Some ( asn1:: parse_single ( & data_tlv_bytes) . unwrap ( ) )
269
269
} ;
270
270
@@ -290,7 +290,7 @@ fn sign_and_serialize<'p>(
290
290
content_type : PKCS7_SIGNED_DATA_OID ,
291
291
content : Some ( asn1:: parse_single ( & signed_data_bytes) . unwrap ( ) ) ,
292
292
} ;
293
- let content_info_bytes = asn1:: write_single ( & content_info) ?;
293
+ let ci_bytes = asn1:: write_single ( & content_info) ?;
294
294
295
295
let encoding_class = py
296
296
. import ( "cryptography.hazmat.primitives.serialization" ) ?
@@ -302,43 +302,49 @@ fn sign_and_serialize<'p>(
302
302
. map ( |d| OIDS_TO_MIC_NAME [ & d. oid ] )
303
303
. collect :: < Vec < _ > > ( )
304
304
. join ( "," ) ;
305
- Ok ( py
305
+ let smime_encode = py
306
306
. import ( "cryptography.hazmat.primitives.serialization.pkcs7" ) ?
307
- . getattr ( crate :: intern!( py, "_smime_encode" ) ) ?
308
- . call1 ( (
309
- pyo3:: types:: PyBytes :: new ( py, & data) ,
310
- pyo3:: types:: PyBytes :: new ( py, & content_info_bytes) ,
311
- mic_algs,
312
- ) ) ?
307
+ . getattr ( crate :: intern!( py, "_smime_encode" ) ) ?;
308
+ Ok ( smime_encode
309
+ . call1 ( ( & * data_without_header, & * ci_bytes, mic_algs, text_mode) ) ?
313
310
. extract ( ) ?)
314
311
} else {
315
312
// Handles the DER, PEM, and error cases
316
- encode_der_data ( py, "PKCS7" . to_string ( ) , content_info_bytes , encoding)
313
+ encode_der_data ( py, "PKCS7" . to_string ( ) , ci_bytes , encoding)
317
314
}
318
315
}
319
316
320
- fn smime_canonicalize ( data : & [ u8 ] , text_mode : bool ) -> Cow < ' _ , [ u8 ] > {
321
- let mut new_data = vec ! [ ] ;
317
+ fn smime_canonicalize ( data : & [ u8 ] , text_mode : bool ) -> ( Cow < ' _ , [ u8 ] > , Cow < ' _ , [ u8 ] > ) {
318
+ let mut new_data_with_header = vec ! [ ] ;
319
+ let mut new_data_without_header = vec ! [ ] ;
322
320
if text_mode {
323
- new_data . extend_from_slice ( b"Content-Type: text/plain\r \n \r \n " ) ;
321
+ new_data_with_header . extend_from_slice ( b"Content-Type: text/plain\r \n \r \n " ) ;
324
322
}
325
323
326
324
let mut last_idx = 0 ;
327
325
for ( i, c) in data. iter ( ) . copied ( ) . enumerate ( ) {
328
326
if c == b'\n' && ( i == 0 || data[ i - 1 ] != b'\r' ) {
329
- new_data. extend_from_slice ( & data[ last_idx..i] ) ;
330
- new_data. push ( b'\r' ) ;
331
- new_data. push ( b'\n' ) ;
327
+ new_data_with_header. extend_from_slice ( & data[ last_idx..i] ) ;
328
+ new_data_with_header. push ( b'\r' ) ;
329
+ new_data_with_header. push ( b'\n' ) ;
330
+
331
+ new_data_without_header. extend_from_slice ( & data[ last_idx..i] ) ;
332
+ new_data_without_header. push ( b'\r' ) ;
333
+ new_data_without_header. push ( b'\n' ) ;
332
334
last_idx = i + 1 ;
333
335
}
334
336
}
335
337
// If there's stuff in new_data, that means we need to copy the rest of
336
338
// data over.
337
- if !new_data. is_empty ( ) {
338
- new_data. extend_from_slice ( & data[ last_idx..] ) ;
339
- Cow :: Owned ( new_data)
339
+ if !new_data_with_header. is_empty ( ) {
340
+ new_data_with_header. extend_from_slice ( & data[ last_idx..] ) ;
341
+ new_data_without_header. extend_from_slice ( & data[ last_idx..] ) ;
342
+ (
343
+ Cow :: Owned ( new_data_with_header) ,
344
+ Cow :: Owned ( new_data_without_header) ,
345
+ )
340
346
} else {
341
- Cow :: Borrowed ( data)
347
+ ( Cow :: Borrowed ( data) , Cow :: Borrowed ( data ) )
342
348
}
343
349
}
344
350
@@ -359,27 +365,60 @@ mod tests {
359
365
360
366
#[ test]
361
367
fn test_smime_canonicalize ( ) {
362
- for ( input, text_mode, expected, expected_is_borrowed) in [
368
+ for (
369
+ input,
370
+ text_mode,
371
+ expected_with_header,
372
+ expected_without_header,
373
+ expected_is_borrowed,
374
+ ) in [
363
375
// Values with text_mode=false
364
- ( b"" as & [ u8 ] , false , b"" as & [ u8 ] , true ) ,
365
- ( b"\n " , false , b"\r \n " , false ) ,
366
- ( b"abc" , false , b"abc" , true ) ,
367
- ( b"abc\r \n def\n " , false , b"abc\r \n def\r \n " , false ) ,
368
- ( b"abc\r \n " , false , b"abc\r \n " , true ) ,
369
- ( b"abc\n def\n " , false , b"abc\r \n def\r \n " , false ) ,
376
+ ( b"" as & [ u8 ] , false , b"" as & [ u8 ] , b"" as & [ u8 ] , true ) ,
377
+ ( b"\n " , false , b"\r \n " , b"\r \n " , false ) ,
378
+ ( b"abc" , false , b"abc" , b"abc" , true ) ,
379
+ (
380
+ b"abc\r \n def\n " ,
381
+ false ,
382
+ b"abc\r \n def\r \n " ,
383
+ b"abc\r \n def\r \n " ,
384
+ false ,
385
+ ) ,
386
+ ( b"abc\r \n " , false , b"abc\r \n " , b"abc\r \n " , true ) ,
387
+ (
388
+ b"abc\n def\n " ,
389
+ false ,
390
+ b"abc\r \n def\r \n " ,
391
+ b"abc\r \n def\r \n " ,
392
+ false ,
393
+ ) ,
370
394
// Values with text_mode=true
371
- ( b"" , true , b"Content-Type: text/plain\r \n \r \n " , false ) ,
372
- ( b"abc" , true , b"Content-Type: text/plain\r \n \r \n abc" , false ) ,
395
+ ( b"" , true , b"Content-Type: text/plain\r \n \r \n " , b"" , false ) ,
396
+ (
397
+ b"abc" ,
398
+ true ,
399
+ b"Content-Type: text/plain\r \n \r \n abc" ,
400
+ b"abc" ,
401
+ false ,
402
+ ) ,
373
403
(
374
404
b"abc\n " ,
375
405
true ,
376
406
b"Content-Type: text/plain\r \n \r \n abc\r \n " ,
407
+ b"abc\r \n " ,
377
408
false ,
378
409
) ,
379
410
] {
380
- let result = smime_canonicalize ( input, text_mode) ;
381
- assert_eq ! ( result. deref( ) , expected) ;
382
- assert_eq ! ( matches!( result, Cow :: Borrowed ( _) ) , expected_is_borrowed) ;
411
+ let ( result_with_header, result_without_header) = smime_canonicalize ( input, text_mode) ;
412
+ assert_eq ! ( result_with_header. deref( ) , expected_with_header) ;
413
+ assert_eq ! ( result_without_header. deref( ) , expected_without_header) ;
414
+ assert_eq ! (
415
+ matches!( result_with_header, Cow :: Borrowed ( _) ) ,
416
+ expected_is_borrowed
417
+ ) ;
418
+ assert_eq ! (
419
+ matches!( result_without_header, Cow :: Borrowed ( _) ) ,
420
+ expected_is_borrowed
421
+ ) ;
383
422
}
384
423
}
385
424
}
0 commit comments