@@ -135,14 +135,13 @@ 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 text_mode = options. contains ( pkcs7_options. getattr ( crate :: intern!( py, "Text" ) ) ?) ?;
139+ let ( data_with_header, data_without_header) =
140+ if options. contains ( pkcs7_options. getattr ( crate :: intern!( py, "Binary" ) ) ?) ? {
141+ ( Cow :: Borrowed ( raw_data) , Cow :: Borrowed ( raw_data) )
142+ } else {
143+ smime_canonicalize ( raw_data, text_mode)
144+ } ;
146145
147146 let content_type_bytes = asn1:: write_single ( & PKCS7_DATA_OID ) ?;
148147 let signing_time_bytes = asn1:: write_single ( & x509:: certificate:: time_from_chrono (
@@ -179,7 +178,7 @@ fn sign_and_serialize<'p>(
179178 {
180179 (
181180 None ,
182- x509:: sign:: sign_data ( py, py_private_key, py_hash_alg, & data ) ?,
181+ x509:: sign:: sign_data ( py, py_private_key, py_hash_alg, & data_with_header ) ?,
183182 )
184183 } else {
185184 let mut authenticated_attrs = vec ! [ ] ;
@@ -197,7 +196,8 @@ fn sign_and_serialize<'p>(
197196 ] ) ) ,
198197 } ) ;
199198
200- let digest = asn1:: write_single ( & x509:: ocsp:: hash_data ( py, py_hash_alg, & data) ?) ?;
199+ let digest =
200+ asn1:: write_single ( & x509:: ocsp:: hash_data ( py, py_hash_alg, & data_with_header) ?) ?;
201201 // Gross hack: copy to PyBytes to extend the lifetime to 'p
202202 let digest_bytes = pyo3:: types:: PyBytes :: new ( py, & digest) ;
203203 authenticated_attrs. push ( x509:: csr:: Attribute {
@@ -263,7 +263,7 @@ fn sign_and_serialize<'p>(
263263 if options. contains ( pkcs7_options. getattr ( crate :: intern!( py, "DetachedSignature" ) ) ?) ? {
264264 None
265265 } else {
266- data_tlv_bytes = asn1:: write_single ( & data . deref ( ) ) ?;
266+ data_tlv_bytes = asn1:: write_single ( & data_with_header . deref ( ) ) ?;
267267 Some ( asn1:: parse_single ( & data_tlv_bytes) . unwrap ( ) )
268268 } ;
269269
@@ -289,7 +289,7 @@ fn sign_and_serialize<'p>(
289289 content_type : PKCS7_SIGNED_DATA_OID ,
290290 content : Some ( asn1:: parse_single ( & signed_data_bytes) . unwrap ( ) ) ,
291291 } ;
292- let content_info_bytes = asn1:: write_single ( & content_info) ?;
292+ let ci_bytes = asn1:: write_single ( & content_info) ?;
293293
294294 let encoding_class = py
295295 . import ( "cryptography.hazmat.primitives.serialization" ) ?
@@ -301,43 +301,49 @@ fn sign_and_serialize<'p>(
301301 . map ( |d| OIDS_TO_MIC_NAME [ & d. oid ] )
302302 . collect :: < Vec < _ > > ( )
303303 . join ( "," ) ;
304- Ok ( py
304+ let smime_encode = py
305305 . import ( "cryptography.hazmat.primitives.serialization.pkcs7" ) ?
306- . getattr ( crate :: intern!( py, "_smime_encode" ) ) ?
307- . call1 ( (
308- pyo3:: types:: PyBytes :: new ( py, & data) ,
309- pyo3:: types:: PyBytes :: new ( py, & content_info_bytes) ,
310- mic_algs,
311- ) ) ?
306+ . getattr ( crate :: intern!( py, "_smime_encode" ) ) ?;
307+ Ok ( smime_encode
308+ . call1 ( ( & * data_without_header, & * ci_bytes, mic_algs, text_mode) ) ?
312309 . extract ( ) ?)
313310 } else {
314311 // Handles the DER, PEM, and error cases
315- encode_der_data ( py, "PKCS7" . to_string ( ) , content_info_bytes , encoding)
312+ encode_der_data ( py, "PKCS7" . to_string ( ) , ci_bytes , encoding)
316313 }
317314}
318315
319- fn smime_canonicalize ( data : & [ u8 ] , text_mode : bool ) -> Cow < ' _ , [ u8 ] > {
320- let mut new_data = vec ! [ ] ;
316+ fn smime_canonicalize ( data : & [ u8 ] , text_mode : bool ) -> ( Cow < ' _ , [ u8 ] > , Cow < ' _ , [ u8 ] > ) {
317+ let mut new_data_with_header = vec ! [ ] ;
318+ let mut new_data_without_header = vec ! [ ] ;
321319 if text_mode {
322- new_data . extend_from_slice ( b"Content-Type: text/plain\r \n \r \n " ) ;
320+ new_data_with_header . extend_from_slice ( b"Content-Type: text/plain\r \n \r \n " ) ;
323321 }
324322
325323 let mut last_idx = 0 ;
326324 for ( i, c) in data. iter ( ) . copied ( ) . enumerate ( ) {
327325 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' ) ;
326+ new_data_with_header. extend_from_slice ( & data[ last_idx..i] ) ;
327+ new_data_with_header. push ( b'\r' ) ;
328+ new_data_with_header. push ( b'\n' ) ;
329+
330+ new_data_without_header. extend_from_slice ( & data[ last_idx..i] ) ;
331+ new_data_without_header. push ( b'\r' ) ;
332+ new_data_without_header. push ( b'\n' ) ;
331333 last_idx = i + 1 ;
332334 }
333335 }
334336 // If there's stuff in new_data, that means we need to copy the rest of
335337 // data over.
336- if !new_data. is_empty ( ) {
337- new_data. extend_from_slice ( & data[ last_idx..] ) ;
338- Cow :: Owned ( new_data)
338+ if !new_data_with_header. is_empty ( ) {
339+ new_data_with_header. extend_from_slice ( & data[ last_idx..] ) ;
340+ new_data_without_header. extend_from_slice ( & data[ last_idx..] ) ;
341+ (
342+ Cow :: Owned ( new_data_with_header) ,
343+ Cow :: Owned ( new_data_without_header) ,
344+ )
339345 } else {
340- Cow :: Borrowed ( data)
346+ ( Cow :: Borrowed ( data) , Cow :: Borrowed ( data ) )
341347 }
342348}
343349
@@ -358,27 +364,60 @@ mod tests {
358364
359365 #[ test]
360366 fn test_smime_canonicalize ( ) {
361- for ( input, text_mode, expected, expected_is_borrowed) in [
367+ for (
368+ input,
369+ text_mode,
370+ expected_with_header,
371+ expected_without_header,
372+ expected_is_borrowed,
373+ ) in [
362374 // 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 ) ,
375+ ( b"" as & [ u8 ] , false , b"" as & [ u8 ] , b"" as & [ u8 ] , true ) ,
376+ ( b"\n " , false , b"\r \n " , b"\r \n " , false ) ,
377+ ( b"abc" , false , b"abc" , b"abc" , true ) ,
378+ (
379+ b"abc\r \n def\n " ,
380+ false ,
381+ b"abc\r \n def\r \n " ,
382+ b"abc\r \n def\r \n " ,
383+ false ,
384+ ) ,
385+ ( b"abc\r \n " , false , b"abc\r \n " , b"abc\r \n " , true ) ,
386+ (
387+ b"abc\n def\n " ,
388+ false ,
389+ b"abc\r \n def\r \n " ,
390+ b"abc\r \n def\r \n " ,
391+ false ,
392+ ) ,
369393 // 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 ) ,
394+ ( b"" , true , b"Content-Type: text/plain\r \n \r \n " , b"" , false ) ,
395+ (
396+ b"abc" ,
397+ true ,
398+ b"Content-Type: text/plain\r \n \r \n abc" ,
399+ b"abc" ,
400+ false ,
401+ ) ,
372402 (
373403 b"abc\n " ,
374404 true ,
375405 b"Content-Type: text/plain\r \n \r \n abc\r \n " ,
406+ b"abc\r \n " ,
376407 false ,
377408 ) ,
378409 ] {
379- let result = smime_canonicalize ( input, text_mode) ;
380- assert_eq ! ( result. deref( ) , expected) ;
381- assert_eq ! ( matches!( result, Cow :: Borrowed ( _) ) , expected_is_borrowed) ;
410+ let ( result_with_header, result_without_header) = smime_canonicalize ( input, text_mode) ;
411+ assert_eq ! ( result_with_header. deref( ) , expected_with_header) ;
412+ assert_eq ! ( result_without_header. deref( ) , expected_without_header) ;
413+ assert_eq ! (
414+ matches!( result_with_header, Cow :: Borrowed ( _) ) ,
415+ expected_is_borrowed
416+ ) ;
417+ assert_eq ! (
418+ matches!( result_without_header, Cow :: Borrowed ( _) ) ,
419+ expected_is_borrowed
420+ ) ;
382421 }
383422 }
384423}
0 commit comments