@@ -135,14 +135,15 @@ fn sign_and_serialize<'p>(
135135 . getattr ( crate :: intern!( py, "PKCS7Options" ) ) ?;
136136
137137 let raw_data = builder. getattr ( crate :: intern!( py, "_data" ) ) ?. extract ( ) ?;
138- let data = if options. contains ( pkcs7_options. getattr ( crate :: intern!( py, "Binary" ) ) ?) ? {
139- Cow :: Borrowed ( raw_data)
140- } else {
141- smime_canonicalize (
142- raw_data,
143- options. contains ( pkcs7_options. getattr ( crate :: intern!( py, "Text" ) ) ?) ?,
144- )
145- } ;
138+ let ( data_with_header, data_without_header) =
139+ if options. contains ( pkcs7_options. getattr ( crate :: intern!( py, "Binary" ) ) ?) ? {
140+ ( Cow :: Borrowed ( raw_data) , Cow :: Borrowed ( raw_data) )
141+ } else {
142+ smime_canonicalize (
143+ raw_data,
144+ options. contains ( pkcs7_options. getattr ( crate :: intern!( py, "Text" ) ) ?) ?,
145+ )
146+ } ;
146147
147148 let content_type_bytes = asn1:: write_single ( & PKCS7_DATA_OID ) ?;
148149 let signing_time_bytes = asn1:: write_single ( & x509:: certificate:: time_from_chrono (
@@ -179,7 +180,7 @@ fn sign_and_serialize<'p>(
179180 {
180181 (
181182 None ,
182- x509:: sign:: sign_data ( py, py_private_key, py_hash_alg, & data ) ?,
183+ x509:: sign:: sign_data ( py, py_private_key, py_hash_alg, & data_with_header ) ?,
183184 )
184185 } else {
185186 let mut authenticated_attrs = vec ! [ ] ;
@@ -197,7 +198,8 @@ fn sign_and_serialize<'p>(
197198 ] ) ) ,
198199 } ) ;
199200
200- let digest = asn1:: write_single ( & x509:: ocsp:: hash_data ( py, py_hash_alg, & data) ?) ?;
201+ let digest =
202+ asn1:: write_single ( & x509:: ocsp:: hash_data ( py, py_hash_alg, & data_with_header) ?) ?;
201203 // Gross hack: copy to PyBytes to extend the lifetime to 'p
202204 let digest_bytes = pyo3:: types:: PyBytes :: new ( py, & digest) ;
203205 authenticated_attrs. push ( x509:: csr:: Attribute {
@@ -263,7 +265,7 @@ fn sign_and_serialize<'p>(
263265 if options. contains ( pkcs7_options. getattr ( crate :: intern!( py, "DetachedSignature" ) ) ?) ? {
264266 None
265267 } else {
266- data_tlv_bytes = asn1:: write_single ( & data . deref ( ) ) ?;
268+ data_tlv_bytes = asn1:: write_single ( & data_with_header . deref ( ) ) ?;
267269 Some ( asn1:: parse_single ( & data_tlv_bytes) . unwrap ( ) )
268270 } ;
269271
@@ -305,9 +307,10 @@ fn sign_and_serialize<'p>(
305307 . import ( "cryptography.hazmat.primitives.serialization.pkcs7" ) ?
306308 . getattr ( crate :: intern!( py, "_smime_encode" ) ) ?
307309 . call1 ( (
308- pyo3:: types:: PyBytes :: new ( py, & data ) ,
310+ pyo3:: types:: PyBytes :: new ( py, & data_without_header ) ,
309311 pyo3:: types:: PyBytes :: new ( py, & content_info_bytes) ,
310312 mic_algs,
313+ options. contains ( pkcs7_options. getattr ( crate :: intern!( py, "Text" ) ) ?) ?,
311314 ) ) ?
312315 . extract ( ) ?)
313316 } else {
@@ -316,28 +319,37 @@ fn sign_and_serialize<'p>(
316319 }
317320}
318321
319- fn smime_canonicalize ( data : & [ u8 ] , text_mode : bool ) -> Cow < ' _ , [ u8 ] > {
320- let mut new_data = vec ! [ ] ;
322+ fn smime_canonicalize ( data : & [ u8 ] , text_mode : bool ) -> ( Cow < ' _ , [ u8 ] > , Cow < ' _ , [ u8 ] > ) {
323+ let mut new_data_with_header = vec ! [ ] ;
324+ let mut new_data_without_header = vec ! [ ] ;
321325 if text_mode {
322- new_data . extend_from_slice ( b"Content-Type: text/plain\r \n \r \n " ) ;
326+ new_data_with_header . extend_from_slice ( b"Content-Type: text/plain\r \n \r \n " ) ;
323327 }
324328
325329 let mut last_idx = 0 ;
326330 for ( i, c) in data. iter ( ) . copied ( ) . enumerate ( ) {
327331 if c == b'\n' && ( i == 0 || data[ i - 1 ] != b'\r' ) {
328- new_data. extend_from_slice ( & data[ last_idx..i] ) ;
329- new_data. push ( b'\r' ) ;
330- new_data. push ( b'\n' ) ;
332+ new_data_with_header. extend_from_slice ( & data[ last_idx..i] ) ;
333+ new_data_with_header. push ( b'\r' ) ;
334+ new_data_with_header. push ( b'\n' ) ;
335+
336+ new_data_without_header. extend_from_slice ( & data[ last_idx..i] ) ;
337+ new_data_without_header. push ( b'\r' ) ;
338+ new_data_without_header. push ( b'\n' ) ;
331339 last_idx = i + 1 ;
332340 }
333341 }
334342 // If there's stuff in new_data, that means we need to copy the rest of
335343 // data over.
336- if !new_data. is_empty ( ) {
337- new_data. extend_from_slice ( & data[ last_idx..] ) ;
338- Cow :: Owned ( new_data)
344+ if !new_data_with_header. is_empty ( ) {
345+ new_data_with_header. extend_from_slice ( & data[ last_idx..] ) ;
346+ new_data_without_header. extend_from_slice ( & data[ last_idx..] ) ;
347+ (
348+ Cow :: Owned ( new_data_with_header) ,
349+ Cow :: Owned ( new_data_without_header) ,
350+ )
339351 } else {
340- Cow :: Borrowed ( data)
352+ ( Cow :: Borrowed ( data) , Cow :: Borrowed ( data ) )
341353 }
342354}
343355
@@ -358,27 +370,60 @@ mod tests {
358370
359371 #[ test]
360372 fn test_smime_canonicalize ( ) {
361- for ( input, text_mode, expected, expected_is_borrowed) in [
373+ for (
374+ input,
375+ text_mode,
376+ expected_with_header,
377+ expected_without_header,
378+ expected_is_borrowed,
379+ ) in [
362380 // Values with text_mode=false
363- ( b"" as & [ u8 ] , false , b"" as & [ u8 ] , true ) ,
364- ( b"\n " , false , b"\r \n " , false ) ,
365- ( b"abc" , false , b"abc" , true ) ,
366- ( b"abc\r \n def\n " , false , b"abc\r \n def\r \n " , false ) ,
367- ( b"abc\r \n " , false , b"abc\r \n " , true ) ,
368- ( b"abc\n def\n " , false , b"abc\r \n def\r \n " , false ) ,
381+ ( b"" as & [ u8 ] , false , b"" as & [ u8 ] , b"" as & [ u8 ] , true ) ,
382+ ( b"\n " , false , b"\r \n " , b"\r \n " , false ) ,
383+ ( b"abc" , false , b"abc" , b"abc" , true ) ,
384+ (
385+ b"abc\r \n def\n " ,
386+ false ,
387+ b"abc\r \n def\r \n " ,
388+ b"abc\r \n def\r \n " ,
389+ false ,
390+ ) ,
391+ ( b"abc\r \n " , false , b"abc\r \n " , b"abc\r \n " , true ) ,
392+ (
393+ b"abc\n def\n " ,
394+ false ,
395+ b"abc\r \n def\r \n " ,
396+ b"abc\r \n def\r \n " ,
397+ false ,
398+ ) ,
369399 // Values with text_mode=true
370- ( b"" , true , b"Content-Type: text/plain\r \n \r \n " , false ) ,
371- ( b"abc" , true , b"Content-Type: text/plain\r \n \r \n abc" , false ) ,
400+ ( b"" , true , b"Content-Type: text/plain\r \n \r \n " , b"" , false ) ,
401+ (
402+ b"abc" ,
403+ true ,
404+ b"Content-Type: text/plain\r \n \r \n abc" ,
405+ b"abc" ,
406+ false ,
407+ ) ,
372408 (
373409 b"abc\n " ,
374410 true ,
375411 b"Content-Type: text/plain\r \n \r \n abc\r \n " ,
412+ b"abc\r \n " ,
376413 false ,
377414 ) ,
378415 ] {
379- let result = smime_canonicalize ( input, text_mode) ;
380- assert_eq ! ( result. deref( ) , expected) ;
381- assert_eq ! ( matches!( result, Cow :: Borrowed ( _) ) , expected_is_borrowed) ;
416+ let ( result_with_header, result_without_header) = smime_canonicalize ( input, text_mode) ;
417+ assert_eq ! ( result_with_header. deref( ) , expected_with_header) ;
418+ assert_eq ! ( result_without_header. deref( ) , expected_without_header) ;
419+ assert_eq ! (
420+ matches!( result_with_header, Cow :: Borrowed ( _) ) ,
421+ expected_is_borrowed
422+ ) ;
423+ assert_eq ! (
424+ matches!( result_without_header, Cow :: Borrowed ( _) ) ,
425+ expected_is_borrowed
426+ ) ;
382427 }
383428 }
384429}
0 commit comments