@@ -136,14 +136,13 @@ fn sign_and_serialize<'p>(
136136 . getattr ( crate :: intern!( py, "PKCS7Options" ) ) ?;
137137
138138 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+ } ;
147146
148147 let content_type_bytes = asn1:: write_single ( & PKCS7_DATA_OID ) ?;
149148 let signing_time_bytes = asn1:: write_single ( & x509:: certificate:: time_from_chrono (
@@ -180,7 +179,7 @@ fn sign_and_serialize<'p>(
180179 {
181180 (
182181 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 ) ?,
184183 )
185184 } else {
186185 let mut authenticated_attrs = vec ! [ ] ;
@@ -198,7 +197,8 @@ fn sign_and_serialize<'p>(
198197 ] ) ) ,
199198 } ) ;
200199
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) ?) ?;
202202 // Gross hack: copy to PyBytes to extend the lifetime to 'p
203203 let digest_bytes = pyo3:: types:: PyBytes :: new ( py, & digest) ;
204204 authenticated_attrs. push ( x509:: csr:: Attribute {
@@ -264,7 +264,7 @@ fn sign_and_serialize<'p>(
264264 if options. contains ( pkcs7_options. getattr ( crate :: intern!( py, "DetachedSignature" ) ) ?) ? {
265265 None
266266 } else {
267- data_tlv_bytes = asn1:: write_single ( & data . deref ( ) ) ?;
267+ data_tlv_bytes = asn1:: write_single ( & data_with_header . deref ( ) ) ?;
268268 Some ( asn1:: parse_single ( & data_tlv_bytes) . unwrap ( ) )
269269 } ;
270270
@@ -290,7 +290,7 @@ fn sign_and_serialize<'p>(
290290 content_type : PKCS7_SIGNED_DATA_OID ,
291291 content : Some ( asn1:: parse_single ( & signed_data_bytes) . unwrap ( ) ) ,
292292 } ;
293- let content_info_bytes = asn1:: write_single ( & content_info) ?;
293+ let ci_bytes = asn1:: write_single ( & content_info) ?;
294294
295295 let encoding_class = py
296296 . import ( "cryptography.hazmat.primitives.serialization" ) ?
@@ -302,43 +302,49 @@ fn sign_and_serialize<'p>(
302302 . map ( |d| OIDS_TO_MIC_NAME [ & d. oid ] )
303303 . collect :: < Vec < _ > > ( )
304304 . join ( "," ) ;
305- Ok ( py
305+ let smime_encode = py
306306 . 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) ) ?
313310 . extract ( ) ?)
314311 } else {
315312 // 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)
317314 }
318315}
319316
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 ! [ ] ;
322320 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 " ) ;
324322 }
325323
326324 let mut last_idx = 0 ;
327325 for ( i, c) in data. iter ( ) . copied ( ) . enumerate ( ) {
328326 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' ) ;
332334 last_idx = i + 1 ;
333335 }
334336 }
335337 // If there's stuff in new_data, that means we need to copy the rest of
336338 // 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+ )
340346 } else {
341- Cow :: Borrowed ( data)
347+ ( Cow :: Borrowed ( data) , Cow :: Borrowed ( data ) )
342348 }
343349}
344350
@@ -359,27 +365,60 @@ mod tests {
359365
360366 #[ test]
361367 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 [
363375 // 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+ ) ,
370394 // 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+ ) ,
373403 (
374404 b"abc\n " ,
375405 true ,
376406 b"Content-Type: text/plain\r \n \r \n abc\r \n " ,
407+ b"abc\r \n " ,
377408 false ,
378409 ) ,
379410 ] {
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+ ) ;
383422 }
384423 }
385424}
0 commit comments