diff --git a/Cargo.toml b/Cargo.toml index 83fca72cf..a87de92c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ lettre = "0.9.0" imap = "1.0.1" # rental = "0.5.3" mmime = { path = "../mmime" } +base64 = "0.10.1" [dev-dependencies] tempfile = "3.0.7" diff --git a/src/dc_aheader.rs b/src/dc_aheader.rs index 93bbb92b3..5b387f0ab 100644 --- a/src/dc_aheader.rs +++ b/src/dc_aheader.rs @@ -261,12 +261,8 @@ pub unsafe fn dc_aheader_render(mut aheader: *const dc_aheader_t) -> *mut libc:: // TODO replace 78 with enum /rtn /* adds a whitespace every 78 characters, this allows libEtPan to wrap the lines according to RFC 5322 (which may insert a linebreak before every whitespace) */ - keybase64_wrapped = dc_key_render_base64( - (*aheader).public_key, - 78, - b" \x00" as *const u8 as *const libc::c_char, - 0, - ); + keybase64_wrapped = dc_key_render_base64((*aheader).public_key, 78); + if !keybase64_wrapped.is_null() { /*no checksum*/ dc_strbuilder_cat(&mut ret, keybase64_wrapped); diff --git a/src/dc_imex.rs b/src/dc_imex.rs index 862a39a57..4fe31d6ac 100644 --- a/src/dc_imex.rs +++ b/src/dc_imex.rs @@ -315,9 +315,9 @@ pub unsafe extern "C" fn dc_render_setup_file( let mut payload_key_asc: *mut libc::c_char = dc_key_render_asc( curr_private_key, if 0 != e2ee_enabled { - b"Autocrypt-Prefer-Encrypt: mutual\r\n\x00" as *const u8 as *const libc::c_char + Some(("Autocrypt-Prefer-Encrypt", "mutual")) } else { - 0 as *const libc::c_char + None }, ); if !payload_key_asc.is_null() { diff --git a/src/dc_key.rs b/src/dc_key.rs index d04ab933f..061d14738 100644 --- a/src/dc_key.rs +++ b/src/dc_key.rs @@ -1,3 +1,12 @@ +use std::collections::BTreeMap; +use std::ffi::CString; +use std::io::Cursor; +use std::slice; + +use libc; +use pgp::composed::{Deserializable, SignedPublicKey, SignedSecretKey}; +use pgp::ser::Serialize; + use crate::dc_context::dc_context_t; use crate::dc_log::*; use crate::dc_pgp::*; @@ -6,9 +15,7 @@ use crate::dc_strbuilder::*; use crate::dc_tools::*; use crate::types::*; use crate::x::*; -/* * - * Library-internal. - */ + #[derive(Copy, Clone)] #[repr(C)] pub struct dc_key_t { @@ -287,141 +294,78 @@ pub unsafe fn dc_key_load_self_private( } /* the result must be freed */ -pub unsafe fn dc_render_base64( - mut buf: *const libc::c_void, - mut buf_bytes: size_t, - mut break_every: libc::c_int, - mut break_chars: *const libc::c_char, - mut add_checksum: libc::c_int, -) -> *mut libc::c_char { - let mut ret: *mut libc::c_char = 0 as *mut libc::c_char; - if !(buf == 0 as *mut libc::c_void || buf_bytes <= 0) { - ret = encode_base64(buf as *const libc::c_char, buf_bytes as libc::c_int); - if !ret.is_null() { - if break_every > 0i32 { - let mut temp: *mut libc::c_char = ret; - ret = dc_insert_breaks(temp, break_every, break_chars); - free(temp as *mut libc::c_void); - } - if add_checksum == 2i32 { - let mut checksum: libc::c_long = crc_octets(buf as *const libc::c_uchar, buf_bytes); - let mut c: [uint8_t; 3] = [0; 3]; - c[0usize] = (checksum >> 16i32 & 0xffi32 as libc::c_long) as uint8_t; - c[1usize] = (checksum >> 8i32 & 0xffi32 as libc::c_long) as uint8_t; - c[2usize] = (checksum & 0xffi32 as libc::c_long) as uint8_t; - let mut c64: *mut libc::c_char = - encode_base64(c.as_mut_ptr() as *const libc::c_char, 3i32); - let mut temp_0: *mut libc::c_char = ret; - ret = dc_mprintf( - b"%s%s=%s\x00" as *const u8 as *const libc::c_char, - temp_0, - break_chars, - c64, - ); - free(temp_0 as *mut libc::c_void); - free(c64 as *mut libc::c_void); - } - } - } - - ret -} - -/******************************************************************************* - * Render keys - ******************************************************************************/ -unsafe fn crc_octets(mut octets: *const libc::c_uchar, mut len: size_t) -> libc::c_long { - let mut crc: libc::c_long = 0xb704ce; - loop { - let fresh0 = len; - len = len.wrapping_sub(1); - if !(0 != fresh0) { - break; - } - let fresh1 = octets; - octets = octets.offset(1); - crc ^= ((*fresh1 as libc::c_int) << 16i32) as libc::c_long; - let mut i: libc::c_int = 0i32; - while i < 8i32 { - crc <<= 1i32; - if 0 != crc & 0x1000000 as libc::c_long { - crc ^= 0x1864cfb - } - i += 1 - } - } - - crc & 0xffffff -} - -/* the result must be freed */ -pub unsafe fn dc_key_render_base64( - mut key: *const dc_key_t, - mut break_every: libc::c_int, - mut break_chars: *const libc::c_char, - mut add_checksum: libc::c_int, -) -> *mut libc::c_char { +pub fn dc_key_render_base64(key: *const dc_key_t, break_every: usize) -> *mut libc::c_char { if key.is_null() { - return 0 as *mut libc::c_char; + return std::ptr::null_mut(); } - dc_render_base64( - (*key).binary, - (*key).bytes as size_t, - break_every, - break_chars, - add_checksum, - ) + let key = unsafe { *key }; + let bytes = unsafe { slice::from_raw_parts(key.binary as *const u8, key.bytes as usize) }; + assert_eq!(bytes.len(), key.bytes as usize); + + let buf = if key.type_0 == 0 { + // public key + let skey = SignedPublicKey::from_bytes(Cursor::new(bytes)).expect("invalid pub key"); + skey.to_bytes().expect("failed to serialize key") + } else { + // secret key + let skey = SignedSecretKey::from_bytes(Cursor::new(bytes)).expect("invalid sec key"); + skey.to_bytes().expect("failed to serialize key") + }; + + let encoded = base64::encode(&buf); + let res = encoded + .as_bytes() + .chunks(break_every) + .fold(String::new(), |mut res, buf| { + // safe because we are using a base64 encoded string + res += unsafe { std::str::from_utf8_unchecked(buf) }; + res += " "; + res + }); + + let res_c = CString::new(res.trim()).unwrap(); + + // need to use strdup to allocate the result with malloc + // so it can be `free`d later. + unsafe { libc::strdup(res_c.as_ptr()) } } -/* each header line must be terminated by \r\n, the result must be freed */ -pub unsafe fn dc_key_render_asc( - mut key: *const dc_key_t, - mut add_header_lines: *const libc::c_char, -) -> *mut libc::c_char { - /* see RFC 4880, 6.2. Forming ASCII Armor, https://tools.ietf.org/html/rfc4880#section-6.2 */ - let mut base64: *mut libc::c_char = 0 as *mut libc::c_char; - let mut ret: *mut libc::c_char = 0 as *mut libc::c_char; - if !key.is_null() { - base64 = dc_key_render_base64( - key, - 76i32, - b"\r\n\x00" as *const u8 as *const libc::c_char, - 2i32, - ); - if !base64.is_null() { - /*checksum in new line*/ - /* RFC: The encoded output stream must be represented in lines of no more than 76 characters each. */ - ret = - dc_mprintf(b"-----BEGIN PGP %s KEY BLOCK-----\r\n%s\r\n%s\r\n-----END PGP %s KEY BLOCK-----\r\n\x00" - as *const u8 as *const libc::c_char, - if (*key).type_0 == 0i32 { - b"PUBLIC\x00" as *const u8 as - *const libc::c_char - } else { - b"PRIVATE\x00" as *const u8 as - *const libc::c_char - }, - if !add_header_lines.is_null() { - add_header_lines - } else { - b"\x00" as *const u8 as *const libc::c_char - }, base64, - if (*key).type_0 == 0i32 { - b"PUBLIC\x00" as *const u8 as - *const libc::c_char - } else { - b"PRIVATE\x00" as *const u8 as - *const libc::c_char - }) - } +/// each header line must be terminated by `\r\n`, the result must be freed. +pub fn dc_key_render_asc(key: *const dc_key_t, header: Option<(&str, &str)>) -> *mut libc::c_char { + if key.is_null() { + return std::ptr::null_mut(); } - free(base64 as *mut libc::c_void); - ret + let key = unsafe { *key }; + + let headers = header.map(|(key, value)| { + let mut m = BTreeMap::new(); + m.insert(key.to_string(), value.to_string()); + m + }); + + let bytes = unsafe { slice::from_raw_parts(key.binary as *const u8, key.bytes as usize) }; + + let buf = if key.type_0 == 0 { + // public key + let skey = SignedPublicKey::from_bytes(Cursor::new(bytes)).expect("invalid key"); + skey.to_armored_string(headers.as_ref()) + .expect("failed to serialize key") + } else { + // secret key + let skey = SignedSecretKey::from_bytes(Cursor::new(bytes)).expect("invalid key"); + skey.to_armored_string(headers.as_ref()) + .expect("failed to serialize key") + }; + + let buf_c = CString::new(buf).unwrap(); + + // need to use strdup to allocate the result with malloc + // so it can be `free`d later. + unsafe { libc::strdup(buf_c.as_ptr()) } } -// TODO should return bool /rtn pub unsafe fn dc_key_render_asc_to_file( mut key: *const dc_key_t, mut file: *const libc::c_char, @@ -429,8 +373,10 @@ pub unsafe fn dc_key_render_asc_to_file( ) -> libc::c_int { let mut success: libc::c_int = 0i32; let mut file_content: *mut libc::c_char = 0 as *mut libc::c_char; + if !(key.is_null() || file.is_null()) { - file_content = dc_key_render_asc(key, 0 as *const libc::c_char); + file_content = dc_key_render_asc(key, None); + if !file_content.is_null() { if 0 == dc_write_file( context, diff --git a/tests/stress.rs b/tests/stress.rs index a616d543c..d3d4f3294 100644 --- a/tests/stress.rs +++ b/tests/stress.rs @@ -3173,11 +3173,8 @@ unsafe extern "C" fn stress_functions(context: &dc_context_t) { let ah: *mut dc_aheader_t = dc_aheader_new(); let rendered: *mut libc::c_char; let mut ah_ok: libc::c_int; - ah_ok = dc_aheader_set_from_string( - ah, - b"addr=a@b.example.org; prefer-encrypt=mutual; keydata=RGVsdGEgQ2hhdA==\x00" as *const u8 - as *const libc::c_char, - ); + let fixed_header = b"addr=a@b.example.org; prefer-encrypt=mutual; keydata= xsBNBFzG3j0BCAC6iNhT8zydvCXi8LI/gFnkadMbfmSE/rTJskRRra/utGbLyDta/yTrJgWL7O3y/g 4HdDW/dN2z26Y6W13IMzx9gLInn1KQZChtqWAcr/ReUucXcymwcfg1mdkBGk3TSLeLihN6CJx8Wsv8 ig+kgAzte4f5rqEEAJVQ9WZHuti7UiYs6oRzqTo06CRe9owVXxzdMf0VDQtf7ZFm9dpzKKbhH7Lu88 80iiotQ9/yRCkDGp9fNThsrLdZiK6OIAcIBAqi2rI89aS1dAmnRbktQieCx5izzyYkR1KvVL3gTTll HOzfKVEC2asmtWu2e4se/+O4WMIS1eGrn7GeWVb0Vwc5ABEBAAHNETxhQEBiLmV4YW1wbGUuZGU+ws CJBBABCAAzAhkBBQJcxt5FAhsDBAsJCAcGFQgJCgsCAxYCARYhBI4xxYKBgH3ANh5cufaKrc9mtiML AAoJEPaKrc9mtiML938H/18F+3Wf9/JaAy/8hCO1v4S2PVBhxaKCokaNFtkfaMRne2l087LscCFPiF Nyb4mv6Z3YeK8Xpxlp2sI0ecvdiqLUOGfnxS6tQrj+83EjtIrZ/hXOk1h121QFWH9Zg2VNHtODXjAg dLDC0NWUrclR0ZOqEDQHeo0ibTILdokVfXFN25wakPmGaYJP2y729cb1ve7RzvIvwn+Dddfxo3ao72 rBfLi7l4NQ4S0KsY4cw+/6l5bRCKYCP77wZtvCwUvfVVosLdT43agtSiBI49+ayqvZ8OCvSJa61i+v 81brTiEy9GBod4eAp45Ibsuemkw+gon4ZOvUXHTjwFB+h63MrozOwE0EXMbePQEIAL/vauf1zK8JgC u3V+G+SOX0iWw5xUlCPX+ERpBbWfwu3uAqn4wYXD3JDE/fVAF668xiV4eTPtlSUd5h0mn+G7uXMMOt kb+20SoEt50f8zw8TrL9t+ZsV11GKZWJpCar5AhXWsn6EEi8I2hLL5vn55ZZmHuGgN4jjmkRl3ToKC LhaXwTBjCJem7N5EH7F75wErEITa55v4Lb4Nfca7vnvtYrI1OA446xa8gHra0SINelTD09/JM/Fw4s WVPBaRZmJK/Tnu79N23No9XBUubmFPv1pNexZsQclicnTpt/BEWhiun7d6lfGB63K1aoHRTR1pcrWv BuALuuz0gqar2zlI0AEQEAAcLAdgQYAQgAIAUCXMbeRQIbDBYhBI4xxYKBgH3ANh5cufaKrc9mtiML AAoJEPaKrc9mtiMLKSEIAIyLCRO2OyZ0IYRvRPpMn4p7E+7Pfcz/0mSkOy+1hshgJnqivXurm8zwGr wdMqeV4eslKR9H1RUdWGUQJNbtwmmjrt5DHpIhYHl5t3FpCBaGbV20Omo00Q38lBl9MtrmZkZw+ktE k6X+0xCKssMF+2MADkSOIufbR5HrDVB89VZOHCO9DeXvCUUAw2hyJiL/LHmLzJ40zYoTmb+F//f0k0 j+tRdbkefyRoCmwG7YGiT+2hnCdgcezswnzah5J3ZKlrg7jOGo1LxtbvNUzxNBbC6S/aNgwm6qxo7x egRhmEl5uZ16zwyj4qz+xkjGy25Of5mWfUDoNw7OT7sjUbHOOMc="; + ah_ok = dc_aheader_set_from_string(ah, fixed_header as *const u8 as *const libc::c_char); if 0 != !(ah_ok == 1i32) as libc::c_int as libc::c_long { __assert_rtn( (*::std::mem::transmute::<&[u8; 17], &[libc::c_char; 17]>(b"stress_functions\x00")) @@ -3204,21 +3201,9 @@ unsafe extern "C" fn stress_functions(context: &dc_context_t) { ); } else { }; - if 0 != !((*(*ah).public_key).bytes == 10i32 - && strncmp( - (*(*ah).public_key).binary as *mut libc::c_char, - b"Delta Chat\x00" as *const u8 as *const libc::c_char, - 10, - ) == 0i32) as libc::c_int as libc::c_long - { - __assert_rtn((*::std::mem::transmute::<&[u8; 17], - &[libc::c_char; 17]>(b"stress_functions\x00")).as_ptr(), - b"../cmdline/stress.c\x00" as *const u8 as - *const libc::c_char, 771i32, - b"ah->public_key->bytes==10 && strncmp((char*)ah->public_key->binary, \"Delta Chat\", 10)==0\x00" - as *const u8 as *const libc::c_char); - } else { - }; + + assert_eq!((*(*ah).public_key).bytes, 1212); + if 0 != !((*ah).prefer_encrypt == 1i32) as libc::c_int as libc::c_long { __assert_rtn( (*::std::mem::transmute::<&[u8; 17], &[libc::c_char; 17]>(b"stress_functions\x00")) @@ -3229,22 +3214,15 @@ unsafe extern "C" fn stress_functions(context: &dc_context_t) { ); } else { }; + rendered = dc_aheader_render(ah); - if 0 != !(!rendered.is_null() - && strcmp( - rendered, - b"addr=a@b.example.org; prefer-encrypt=mutual; keydata= RGVsdGEgQ2hhdA==\x00" - as *const u8 as *const libc::c_char, - ) == 0i32) as libc::c_int as libc::c_long - { - __assert_rtn((*::std::mem::transmute::<&[u8; 17], - &[libc::c_char; 17]>(b"stress_functions\x00")).as_ptr(), - b"../cmdline/stress.c\x00" as *const u8 as - *const libc::c_char, 775i32, - b"rendered && strcmp(rendered, \"addr=a@b.example.org; prefer-encrypt=mutual; keydata= RGVsdGEgQ2hhdA==\")==0\x00" - as *const u8 as *const libc::c_char); - } else { - }; + + assert!(!rendered.is_null()); + assert_eq!( + std::ffi::CStr::from_ptr(rendered).to_str().unwrap(), + std::str::from_utf8(fixed_header).unwrap() + ); + ah_ok = dc_aheader_set_from_string(ah, b" _foo; __FOO=BAR ;;; addr = a@b.example.org ;\r\n prefer-encrypt = mutual ; keydata = RG VsdGEgQ\r\n2hhdA==\x00" @@ -3275,6 +3253,7 @@ unsafe extern "C" fn stress_functions(context: &dc_context_t) { ); } else { }; + if 0 != !((*(*ah).public_key).bytes == 10i32 && strncmp( (*(*ah).public_key).binary as *mut libc::c_char,