diff --git a/Cargo.toml b/Cargo.toml index 60109286b..1ea213e65 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" authors = ["dignifiedquire "] edition = "2018" + [build-dependencies] cc = "1.0.35" pkg-config = "0.3" @@ -12,3 +13,9 @@ bindgen = "0.49.0" [dependencies] c2rust-bitfields = "0.1.0" libc = "0.2.51" +pgp = "0.2.0" +failure = "0.1.5" +hex = "0.3.2" +sha2 = "0.8.0" +rand = "0.6.5" +smallvec = "0.6.9" diff --git a/build.rs b/build.rs index 11d2f6922..ed90f7000 100644 --- a/build.rs +++ b/build.rs @@ -21,7 +21,6 @@ fn main() { println!("cargo:rustc-link-lib=dylib=sqlite3"); println!("cargo:rustc-link-lib=dylib=pthread"); - println!("cargo:rustc-link-lib=dylib=rpgp"); println!("cargo:rustc-link-lib=dylib=crypto"); println!("cargo:rustc-link-lib=dylib=tools"); diff --git a/src/dc_pgp.rs b/src/dc_pgp.rs index 99864bbac..fde81ab18 100644 --- a/src/dc_pgp.rs +++ b/src/dc_pgp.rs @@ -9,27 +9,10 @@ use crate::dc_log::*; use crate::dc_lot::dc_lot_t; use crate::dc_sqlite3::*; use crate::dc_tools::*; +use crate::pgp as rpgp; use crate::types::*; use crate::x::*; -pub type rpgp_signed_secret_key = rpgp_SignedSecretKey; -#[derive(Copy, Clone)] -#[repr(C)] -pub struct rpgp_cvec { - pub data: *mut uint8_t, - pub len: size_t, -} -pub type rpgp_message = rpgp_Message; -pub type rpgp_signed_public_key = rpgp_SignedPublicKey; -pub type rpgp_public_or_secret_key = rpgp_PublicOrSecret; -#[derive(Copy, Clone)] -#[repr(C)] -pub struct rpgp_message_decrypt_result { - pub message_ptr: *mut rpgp_message, - pub valid_ids_ptr: *mut *mut libc::c_char, - pub valid_ids_len: size_t, -} - /* ** library-private **********************************************************/ /* validation errors */ /* misc. */ @@ -171,31 +154,31 @@ pub unsafe extern "C" fn dc_pgp_create_keypair( mut ret_private_key: *mut dc_key_t, ) -> libc::c_int { let mut success: libc::c_int = 0i32; - let mut skey: *mut rpgp_signed_secret_key = 0 as *mut rpgp_signed_secret_key; - let mut pkey: *mut rpgp_signed_public_key = 0 as *mut rpgp_signed_public_key; - let mut skey_bytes: *mut rpgp_cvec = 0 as *mut rpgp_cvec; - let mut pkey_bytes: *mut rpgp_cvec = 0 as *mut rpgp_cvec; + let mut skey: *mut rpgp::signed_secret_key = 0 as *mut rpgp::signed_secret_key; + let mut pkey: *mut rpgp::signed_public_key = 0 as *mut rpgp::signed_public_key; + let mut skey_bytes: *mut rpgp::cvec = 0 as *mut rpgp::cvec; + let mut pkey_bytes: *mut rpgp::cvec = 0 as *mut rpgp::cvec; let mut user_id: *mut libc::c_char = 0 as *mut libc::c_char; user_id = dc_mprintf(b"<%s>\x00" as *const u8 as *const libc::c_char, addr); - skey = rpgp_create_rsa_skey(2048i32 as uint32_t, user_id); + skey = rpgp::rpgp_create_rsa_skey(2048i32 as uint32_t, user_id); if !(0 != dc_pgp_handle_rpgp_error(context)) { - skey_bytes = rpgp_skey_to_bytes(skey); + skey_bytes = rpgp::rpgp_skey_to_bytes(skey); if !(0 != dc_pgp_handle_rpgp_error(context)) { - pkey = rpgp_skey_public_key(skey); + pkey = rpgp::rpgp_skey_public_key(skey); if !(0 != dc_pgp_handle_rpgp_error(context)) { - pkey_bytes = rpgp_pkey_to_bytes(pkey); + pkey_bytes = rpgp::rpgp_pkey_to_bytes(pkey); if !(0 != dc_pgp_handle_rpgp_error(context)) { dc_key_set_from_binary( ret_private_key, - rpgp_cvec_data(skey_bytes) as *const libc::c_void, - rpgp_cvec_len(skey_bytes) as libc::c_int, + rpgp::rpgp_cvec_data(skey_bytes) as *const libc::c_void, + rpgp::rpgp_cvec_len(skey_bytes) as libc::c_int, 1i32, ); if !(0 != dc_pgp_handle_rpgp_error(context)) { dc_key_set_from_binary( ret_public_key, - rpgp_cvec_data(pkey_bytes) as *const libc::c_void, - rpgp_cvec_len(pkey_bytes) as libc::c_int, + rpgp::rpgp_cvec_data(pkey_bytes) as *const libc::c_void, + rpgp::rpgp_cvec_len(pkey_bytes) as libc::c_int, 0i32, ); if !(0 != dc_pgp_handle_rpgp_error(context)) { @@ -208,16 +191,16 @@ pub unsafe extern "C" fn dc_pgp_create_keypair( } /* cleanup */ if !skey.is_null() { - rpgp_skey_drop(skey); + rpgp::rpgp_skey_drop(skey); } if !skey_bytes.is_null() { - rpgp_cvec_drop(skey_bytes); + rpgp::rpgp_cvec_drop(skey_bytes); } if !pkey.is_null() { - rpgp_pkey_drop(pkey); + rpgp::rpgp_pkey_drop(pkey); } if !pkey_bytes.is_null() { - rpgp_cvec_drop(pkey_bytes); + rpgp::rpgp_cvec_drop(pkey_bytes); } if !user_id.is_null() { free(user_id as *mut libc::c_void); @@ -230,9 +213,9 @@ pub unsafe extern "C" fn dc_pgp_handle_rpgp_error(mut context: *mut dc_context_t let mut success: libc::c_int = 0i32; let mut len: libc::c_int = 0i32; let mut msg: *mut libc::c_char = 0 as *mut libc::c_char; - len = rpgp_last_error_length(); + len = rpgp::rpgp_last_error_length(); if !(len == 0i32) { - msg = rpgp_last_error_message(); + msg = rpgp::rpgp_last_error_message(); if !context.is_null() { dc_log_info( context, @@ -244,7 +227,7 @@ pub unsafe extern "C" fn dc_pgp_handle_rpgp_error(mut context: *mut dc_context_t success = 1i32 } if !msg.is_null() { - rpgp_string_drop(msg); + rpgp::rpgp_string_drop(msg); } return success; } @@ -254,26 +237,27 @@ pub unsafe extern "C" fn dc_pgp_is_valid_key( mut raw_key: *const dc_key_t, ) -> libc::c_int { let mut key_is_valid: libc::c_int = 0i32; - let mut key: *mut rpgp_public_or_secret_key = 0 as *mut rpgp_public_or_secret_key; + let mut key: *mut rpgp::public_or_secret_key = 0 as *mut rpgp::public_or_secret_key; if !(context.is_null() || raw_key.is_null() || (*raw_key).binary.is_null() || (*raw_key).bytes <= 0i32) { - key = rpgp_key_from_bytes( + key = rpgp::rpgp_key_from_bytes( (*raw_key).binary as *const uint8_t, - (*raw_key).bytes as size_t, + (*raw_key).bytes as usize, ); if !(0 != dc_pgp_handle_rpgp_error(context)) { - if (*raw_key).type_0 == 0i32 && 0 != rpgp_key_is_public(key) as libc::c_int { + if (*raw_key).type_0 == 0i32 && 0 != rpgp::rpgp_key_is_public(key) as libc::c_int { key_is_valid = 1i32 - } else if (*raw_key).type_0 == 1i32 && 0 != rpgp_key_is_secret(key) as libc::c_int { + } else if (*raw_key).type_0 == 1i32 && 0 != rpgp::rpgp_key_is_secret(key) as libc::c_int + { key_is_valid = 1i32 } } } if !key.is_null() { - rpgp_key_drop(key); + rpgp::rpgp_key_drop(key); } return key_is_valid; } @@ -284,8 +268,8 @@ pub unsafe extern "C" fn dc_pgp_calc_fingerprint( mut ret_fingerprint_bytes: *mut size_t, ) -> libc::c_int { let mut success: libc::c_int = 0i32; - let mut key: *mut rpgp_public_or_secret_key = 0 as *mut rpgp_public_or_secret_key; - let mut fingerprint: *mut rpgp_cvec = 0 as *mut rpgp_cvec; + let mut key: *mut rpgp::public_or_secret_key = 0 as *mut rpgp::public_or_secret_key; + let mut fingerprint: *mut rpgp::cvec = 0 as *mut rpgp::cvec; if !(raw_key.is_null() || ret_fingerprint.is_null() || !(*ret_fingerprint).is_null() @@ -294,18 +278,18 @@ pub unsafe extern "C" fn dc_pgp_calc_fingerprint( || (*raw_key).binary.is_null() || (*raw_key).bytes <= 0i32) { - key = rpgp_key_from_bytes( + key = rpgp::rpgp_key_from_bytes( (*raw_key).binary as *const uint8_t, - (*raw_key).bytes as size_t, + (*raw_key).bytes as usize, ); if !(0 != dc_pgp_handle_rpgp_error(0 as *mut dc_context_t)) { - fingerprint = rpgp_key_fingerprint(key); + fingerprint = rpgp::rpgp_key_fingerprint(key); if !(0 != dc_pgp_handle_rpgp_error(0 as *mut dc_context_t)) { - *ret_fingerprint_bytes = rpgp_cvec_len(fingerprint); + *ret_fingerprint_bytes = rpgp::rpgp_cvec_len(fingerprint) as size_t; *ret_fingerprint = malloc(*ret_fingerprint_bytes) as *mut uint8_t; memcpy( *ret_fingerprint as *mut libc::c_void, - rpgp_cvec_data(fingerprint) as *const libc::c_void, + rpgp::rpgp_cvec_data(fingerprint) as *const libc::c_void, *ret_fingerprint_bytes, ); success = 1i32 @@ -313,10 +297,10 @@ pub unsafe extern "C" fn dc_pgp_calc_fingerprint( } } if !key.is_null() { - rpgp_key_drop(key); + rpgp::rpgp_key_drop(key); } if !fingerprint.is_null() { - rpgp_cvec_drop(fingerprint); + rpgp::rpgp_cvec_drop(fingerprint); } return success; } @@ -327,9 +311,9 @@ pub unsafe extern "C" fn dc_pgp_split_key( mut ret_public_key: *mut dc_key_t, ) -> libc::c_int { let mut success: libc::c_int = 0i32; - let mut key: *mut rpgp_signed_secret_key = 0 as *mut rpgp_signed_secret_key; - let mut pub_key: *mut rpgp_signed_public_key = 0 as *mut rpgp_signed_public_key; - let mut buf: *mut rpgp_cvec = 0 as *mut rpgp_cvec; + let mut key: *mut rpgp::signed_secret_key = 0 as *mut rpgp::signed_secret_key; + let mut pub_key: *mut rpgp::signed_public_key = 0 as *mut rpgp::signed_public_key; + let mut buf: *mut rpgp::cvec = 0 as *mut rpgp::cvec; if !(context.is_null() || private_in.is_null() || ret_public_key.is_null()) { if (*private_in).type_0 != 1i32 { dc_log_warning( @@ -338,19 +322,19 @@ pub unsafe extern "C" fn dc_pgp_split_key( b"Split key: Given key is no private key.\x00" as *const u8 as *const libc::c_char, ); } else { - key = rpgp_skey_from_bytes( + key = rpgp::rpgp_skey_from_bytes( (*private_in).binary as *const uint8_t, - (*private_in).bytes as size_t, + (*private_in).bytes as usize, ); if !(0 != dc_pgp_handle_rpgp_error(context)) { - pub_key = rpgp_skey_public_key(key); + pub_key = rpgp::rpgp_skey_public_key(key); if !(0 != dc_pgp_handle_rpgp_error(context)) { - buf = rpgp_pkey_to_bytes(pub_key); + buf = rpgp::rpgp_pkey_to_bytes(pub_key); if !(0 != dc_pgp_handle_rpgp_error(context)) { dc_key_set_from_binary( ret_public_key, - rpgp_cvec_data(buf) as *const libc::c_void, - rpgp_cvec_len(buf) as libc::c_int, + rpgp::rpgp_cvec_data(buf) as *const libc::c_void, + rpgp::rpgp_cvec_len(buf) as libc::c_int, 0i32, ); success = 1i32 @@ -360,13 +344,13 @@ pub unsafe extern "C" fn dc_pgp_split_key( } } if !key.is_null() { - rpgp_skey_drop(key); + rpgp::rpgp_skey_drop(key); } if !pub_key.is_null() { - rpgp_pkey_drop(pub_key); + rpgp::rpgp_pkey_drop(pub_key); } if !buf.is_null() { - rpgp_cvec_drop(buf); + rpgp::rpgp_cvec_drop(buf); } return success; } @@ -385,9 +369,9 @@ pub unsafe extern "C" fn dc_pgp_pk_encrypt( let mut i: libc::c_int = 0i32; let mut success: libc::c_int = 0i32; let mut public_keys_len: libc::c_int = 0i32; - let mut public_keys: *mut *mut rpgp_signed_public_key = 0 as *mut *mut rpgp_signed_public_key; - let mut private_key: *mut rpgp_signed_secret_key = 0 as *mut rpgp_signed_secret_key; - let mut encrypted: *mut rpgp_message = 0 as *mut rpgp_message; + let mut public_keys: *mut *mut rpgp::signed_public_key = 0 as *mut *mut rpgp::signed_public_key; + let mut private_key: *mut rpgp::signed_secret_key = 0 as *mut rpgp::signed_secret_key; + let mut encrypted: *mut rpgp::Message = 0 as *mut rpgp::Message; if !(context.is_null() || plain_text == 0 as *mut libc::c_void || plain_bytes == 0i32 as libc::c_ulong @@ -402,14 +386,14 @@ pub unsafe extern "C" fn dc_pgp_pk_encrypt( *ret_ctext_bytes = 0i32 as size_t; public_keys_len = (*raw_public_keys_for_encryption).count; public_keys = malloc( - (::std::mem::size_of::<*mut rpgp_signed_public_key>() as libc::c_ulong) + (::std::mem::size_of::<*mut rpgp::signed_public_key>() as libc::c_ulong) .wrapping_mul(public_keys_len as libc::c_ulong), - ) as *mut *mut rpgp_signed_public_key; + ) as *mut *mut rpgp::signed_public_key; /* setup secret key for signing */ if !raw_private_key_for_signing.is_null() { - private_key = rpgp_skey_from_bytes( + private_key = rpgp::rpgp_skey_from_bytes( (*raw_private_key_for_signing).binary as *const uint8_t, - (*raw_private_key_for_signing).bytes as size_t, + (*raw_private_key_for_signing).bytes as usize, ); if private_key.is_null() || 0 != dc_pgp_handle_rpgp_error(context) { dc_log_warning( @@ -435,11 +419,11 @@ pub unsafe extern "C" fn dc_pgp_pk_encrypt( break; } let ref mut fresh0 = *public_keys.offset(i as isize); - *fresh0 = rpgp_pkey_from_bytes( + *fresh0 = rpgp::rpgp_pkey_from_bytes( (**(*raw_public_keys_for_encryption).keys.offset(i as isize)).binary as *const uint8_t, (**(*raw_public_keys_for_encryption).keys.offset(i as isize)).bytes - as size_t, + as usize, ); if 0 != dc_pgp_handle_rpgp_error(context) { current_block = 2132137392766895896; @@ -454,11 +438,11 @@ pub unsafe extern "C" fn dc_pgp_pk_encrypt( let mut op_clocks: libc::clock_t = 0i32 as libc::clock_t; let mut start: libc::clock_t = clock(); if private_key.is_null() { - encrypted = rpgp_encrypt_bytes_to_keys( + encrypted = rpgp::rpgp_encrypt_bytes_to_keys( plain_text as *const uint8_t, - plain_bytes, - public_keys as *const *const rpgp_signed_public_key, - public_keys_len as size_t, + plain_bytes as usize, + public_keys as *const *const rpgp::signed_public_key, + public_keys_len as usize, ); if 0 != dc_pgp_handle_rpgp_error(context) { dc_log_warning( @@ -480,11 +464,11 @@ pub unsafe extern "C" fn dc_pgp_pk_encrypt( current_block = 1538046216550696469; } } else { - encrypted = rpgp_sign_encrypt_bytes_to_keys( + encrypted = rpgp::rpgp_sign_encrypt_bytes_to_keys( plain_text as *const uint8_t, - plain_bytes, - public_keys as *const *const rpgp_signed_public_key, - public_keys_len as size_t, + plain_bytes as usize, + public_keys as *const *const rpgp::signed_public_key, + public_keys_len as usize, private_key, ); if 0 != dc_pgp_handle_rpgp_error(context) { @@ -512,10 +496,11 @@ pub unsafe extern "C" fn dc_pgp_pk_encrypt( 2132137392766895896 => {} _ => { /* convert message to armored bytes and return values */ - let mut armored: *mut rpgp_cvec = rpgp_msg_to_armored(encrypted); + let mut armored: *mut rpgp::cvec = + rpgp::rpgp_msg_to_armored(encrypted); if !(0 != dc_pgp_handle_rpgp_error(context)) { - *ret_ctext = rpgp_cvec_data(armored) as *mut libc::c_void; - *ret_ctext_bytes = rpgp_cvec_len(armored); + *ret_ctext = rpgp::rpgp_cvec_data(armored) as *mut libc::c_void; + *ret_ctext_bytes = rpgp::rpgp_cvec_len(armored) as size_t; free(armored as *mut libc::c_void); success = 1i32 } @@ -527,15 +512,15 @@ pub unsafe extern "C" fn dc_pgp_pk_encrypt( } } if !private_key.is_null() { - rpgp_skey_drop(private_key); + rpgp::rpgp_skey_drop(private_key); } i = 0i32; while i < public_keys_len { - rpgp_pkey_drop(*public_keys.offset(i as isize)); + rpgp::rpgp_pkey_drop(*public_keys.offset(i as isize)); i += 1 } if !encrypted.is_null() { - rpgp_msg_drop(encrypted); + rpgp::rpgp_msg_drop(encrypted); } return success; } @@ -554,12 +539,13 @@ pub unsafe extern "C" fn dc_pgp_pk_decrypt( let mut current_block: u64; let mut i: libc::c_int = 0i32; let mut success: libc::c_int = 0i32; - let mut encrypted: *mut rpgp_message = 0 as *mut rpgp_message; - let mut decrypted: *mut rpgp_message_decrypt_result = 0 as *mut rpgp_message_decrypt_result; + let mut encrypted: *mut rpgp::Message = 0 as *mut rpgp::Message; + let mut decrypted: *mut rpgp::message_decrypt_result = 0 as *mut rpgp::message_decrypt_result; let mut private_keys_len: libc::c_int = 0i32; let mut public_keys_len: libc::c_int = 0i32; - let mut private_keys: *mut *mut rpgp_signed_secret_key = 0 as *mut *mut rpgp_signed_secret_key; - let mut public_keys: *mut *mut rpgp_signed_public_key = 0 as *mut *mut rpgp_signed_public_key; + let mut private_keys: *mut *mut rpgp::signed_secret_key = + 0 as *mut *mut rpgp::signed_secret_key; + let mut public_keys: *mut *mut rpgp::signed_public_key = 0 as *mut *mut rpgp::signed_public_key; if !(context.is_null() || ctext == 0 as *mut libc::c_void || ctext_bytes == 0i32 as libc::c_ulong @@ -574,15 +560,15 @@ pub unsafe extern "C" fn dc_pgp_pk_decrypt( *ret_plain_bytes = 0i32 as size_t; private_keys_len = (*raw_private_keys_for_decryption).count; private_keys = malloc( - (::std::mem::size_of::<*mut rpgp_signed_secret_key>() as libc::c_ulong) + (::std::mem::size_of::<*mut rpgp::signed_secret_key>() as libc::c_ulong) .wrapping_mul(private_keys_len as libc::c_ulong), - ) as *mut *mut rpgp_signed_secret_key; + ) as *mut *mut rpgp::signed_secret_key; if !raw_public_keys_for_validation.is_null() { public_keys_len = (*raw_public_keys_for_validation).count; public_keys = malloc( - (::std::mem::size_of::<*mut rpgp_signed_public_key>() as libc::c_ulong) + (::std::mem::size_of::<*mut rpgp::signed_public_key>() as libc::c_ulong) .wrapping_mul(public_keys_len as libc::c_ulong), - ) as *mut *mut rpgp_signed_public_key + ) as *mut *mut rpgp::signed_public_key } /* setup secret keys for decryption */ i = 0i32; @@ -592,10 +578,10 @@ pub unsafe extern "C" fn dc_pgp_pk_decrypt( break; } let ref mut fresh1 = *private_keys.offset(i as isize); - *fresh1 = rpgp_skey_from_bytes( + *fresh1 = rpgp::rpgp_skey_from_bytes( (**(*raw_private_keys_for_decryption).keys.offset(i as isize)).binary as *const uint8_t, - (**(*raw_private_keys_for_decryption).keys.offset(i as isize)).bytes as size_t, + (**(*raw_private_keys_for_decryption).keys.offset(i as isize)).bytes as usize, ); if 0 != dc_pgp_handle_rpgp_error(context) { current_block = 11904635156640512504; @@ -615,11 +601,11 @@ pub unsafe extern "C" fn dc_pgp_pk_decrypt( break; } let ref mut fresh2 = *public_keys.offset(i as isize); - *fresh2 = rpgp_pkey_from_bytes( + *fresh2 = rpgp::rpgp_pkey_from_bytes( (**(*raw_public_keys_for_validation).keys.offset(i as isize)).binary as *const uint8_t, (**(*raw_public_keys_for_validation).keys.offset(i as isize)).bytes - as size_t, + as usize, ); if 0 != dc_pgp_handle_rpgp_error(context) { current_block = 11904635156640512504; @@ -634,22 +620,26 @@ pub unsafe extern "C" fn dc_pgp_pk_decrypt( 11904635156640512504 => {} _ => { /* decrypt */ - encrypted = rpgp_msg_from_armor(ctext as *const uint8_t, ctext_bytes); + encrypted = rpgp::rpgp_msg_from_armor( + ctext as *const uint8_t, + ctext_bytes as usize, + ); if !(0 != dc_pgp_handle_rpgp_error(context)) { - decrypted = rpgp_msg_decrypt_no_pw( + decrypted = rpgp::rpgp_msg_decrypt_no_pw( encrypted, - private_keys as *const *const rpgp_signed_secret_key, - private_keys_len as size_t, - public_keys as *const *const rpgp_signed_public_key, - public_keys_len as size_t, + private_keys as *const *const rpgp::signed_secret_key, + private_keys_len as usize, + public_keys as *const *const rpgp::signed_public_key, + public_keys_len as usize, ); if !(0 != dc_pgp_handle_rpgp_error(context)) { - let mut decrypted_bytes: *mut rpgp_cvec = - rpgp_msg_to_bytes((*decrypted).message_ptr); + let mut decrypted_bytes: *mut rpgp::cvec = + rpgp::rpgp_msg_to_bytes((*decrypted).message_ptr); if !(0 != dc_pgp_handle_rpgp_error(context)) { - *ret_plain_bytes = rpgp_cvec_len(decrypted_bytes); + *ret_plain_bytes = + rpgp::rpgp_cvec_len(decrypted_bytes) as size_t; *ret_plain = - rpgp_cvec_data(decrypted_bytes) as *mut libc::c_void; + rpgp::rpgp_cvec_data(decrypted_bytes) as *mut libc::c_void; free(decrypted_bytes as *mut libc::c_void); if !ret_signature_fingerprints.is_null() { let mut j: uint32_t = 0i32 as uint32_t; @@ -681,19 +671,19 @@ pub unsafe extern "C" fn dc_pgp_pk_decrypt( } i = 0i32; while i < private_keys_len { - rpgp_skey_drop(*private_keys.offset(i as isize)); + rpgp::rpgp_skey_drop(*private_keys.offset(i as isize)); i += 1 } i = 0i32; while i < public_keys_len { - rpgp_pkey_drop(*public_keys.offset(i as isize)); + rpgp::rpgp_pkey_drop(*public_keys.offset(i as isize)); i += 1 } if !encrypted.is_null() { - rpgp_msg_drop(encrypted); + rpgp::rpgp_msg_drop(encrypted); } if !decrypted.is_null() { - rpgp_message_decrypt_result_drop(decrypted); + rpgp::rpgp_message_decrypt_result_drop(decrypted); } return success; } @@ -707,24 +697,27 @@ pub unsafe extern "C" fn dc_pgp_symm_encrypt( mut ret_ctext_armored: *mut *mut libc::c_char, ) -> libc::c_int { let mut success: libc::c_int = 0i32; - let mut decrypted: *mut rpgp_message = 0 as *mut rpgp_message; + let mut decrypted: *mut rpgp::Message = 0 as *mut rpgp::Message; if !(context.is_null() || passphrase.is_null() || plain == 0 as *mut libc::c_void || plain_bytes == 0i32 as libc::c_ulong || ret_ctext_armored.is_null()) { - decrypted = - rpgp_encrypt_bytes_with_password(plain as *const uint8_t, plain_bytes, passphrase); + decrypted = rpgp::rpgp_encrypt_bytes_with_password( + plain as *const uint8_t, + plain_bytes as usize, + passphrase, + ); if !(0 != dc_pgp_handle_rpgp_error(context)) { - *ret_ctext_armored = rpgp_msg_to_armored_str(decrypted); + *ret_ctext_armored = rpgp::rpgp_msg_to_armored_str(decrypted); if !(0 != dc_pgp_handle_rpgp_error(context)) { success = 1i32 } } } if !decrypted.is_null() { - rpgp_msg_drop(decrypted); + rpgp::rpgp_msg_drop(decrypted); } return success; } @@ -737,28 +730,28 @@ pub unsafe extern "C" fn dc_pgp_symm_decrypt( mut ret_plain_text: *mut *mut libc::c_void, mut ret_plain_bytes: *mut size_t, ) -> libc::c_int { - let mut decrypted_bytes: *mut rpgp_cvec = 0 as *mut rpgp_cvec; + let mut decrypted_bytes: *mut rpgp::cvec = 0 as *mut rpgp::cvec; let mut success: libc::c_int = 0i32; - let mut encrypted: *mut rpgp_message = 0 as *mut rpgp_message; - let mut decrypted: *mut rpgp_message = 0 as *mut rpgp_message; - encrypted = rpgp_msg_from_bytes(ctext as *const uint8_t, ctext_bytes); + let mut encrypted: *mut rpgp::Message = 0 as *mut rpgp::Message; + let mut decrypted: *mut rpgp::Message = 0 as *mut rpgp::Message; + encrypted = rpgp::rpgp_msg_from_bytes(ctext as *const uint8_t, ctext_bytes as usize); if !(0 != dc_pgp_handle_rpgp_error(context)) { - decrypted = rpgp_msg_decrypt_with_password(encrypted, passphrase); + decrypted = rpgp::rpgp_msg_decrypt_with_password(encrypted, passphrase); if !(0 != dc_pgp_handle_rpgp_error(context)) { - decrypted_bytes = rpgp_msg_to_bytes(decrypted); + decrypted_bytes = rpgp::rpgp_msg_to_bytes(decrypted); if !(0 != dc_pgp_handle_rpgp_error(context)) { - *ret_plain_text = rpgp_cvec_data(decrypted_bytes) as *mut libc::c_void; - *ret_plain_bytes = rpgp_cvec_len(decrypted_bytes); + *ret_plain_text = rpgp::rpgp_cvec_data(decrypted_bytes) as *mut libc::c_void; + *ret_plain_bytes = rpgp::rpgp_cvec_len(decrypted_bytes) as size_t; free(decrypted_bytes as *mut libc::c_void); success = 1i32 } } } if !encrypted.is_null() { - rpgp_msg_drop(encrypted); + rpgp::rpgp_msg_drop(encrypted); } if !decrypted.is_null() { - rpgp_msg_drop(decrypted); + rpgp::rpgp_msg_drop(decrypted); } return success; } diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index fa5ea7fda..2d7be11af 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -22,6 +22,7 @@ use crate::dc_stock::*; use crate::dc_strbuilder::*; use crate::dc_strencode::*; use crate::dc_tools::*; +use crate::pgp; use crate::types::*; use crate::x::*; @@ -1740,8 +1741,10 @@ unsafe extern "C" fn create_adhoc_grp_id( i += 1 } /* make sha-256 from the string */ - let mut binary_hash: *mut rpgp_cvec = - rpgp_hash_sha256(member_cs.buf as *const uint8_t, strlen(member_cs.buf)); + let mut binary_hash: *mut crate::pgp::cvec = pgp::rpgp_hash_sha256( + member_cs.buf as *const uint8_t, + strlen(member_cs.buf) as usize, + ); if !binary_hash.is_null() { ret = calloc(1i32 as libc::c_ulong, 256i32 as libc::c_ulong) as *mut libc::c_char; if !ret.is_null() { @@ -1750,11 +1753,11 @@ unsafe extern "C" fn create_adhoc_grp_id( sprintf( &mut *ret.offset((i * 2i32) as isize) as *mut libc::c_char, b"%02x\x00" as *const u8 as *const libc::c_char, - *rpgp_cvec_data(binary_hash).offset(i as isize) as libc::c_int, + *pgp::rpgp_cvec_data(binary_hash).offset(i as isize) as libc::c_int, ); i += 1 } - rpgp_cvec_drop(binary_hash); + pgp::rpgp_cvec_drop(binary_hash); } } dc_array_free_ptr(member_addrs); diff --git a/src/lib.rs b/src/lib.rs index ec6404886..8aa48347e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,8 +19,12 @@ ptr_wrapping_offset_from )] -pub mod types; -pub mod x; +#[macro_use] +extern crate failure; + +mod pgp; +mod types; +mod x; pub mod dc_aheader; pub mod dc_apeerstate; diff --git a/src/pgp/c_vec.rs b/src/pgp/c_vec.rs new file mode 100644 index 000000000..96af846de --- /dev/null +++ b/src/pgp/c_vec.rs @@ -0,0 +1,90 @@ +use std::slice; + +/// Represents a vector, that can be passed to C land. +/// Has to be deallocated using [rpgp_cvec_drop], otherwise leaks memory. +#[repr(C)] +#[derive(Debug)] +pub struct cvec { + data: *mut u8, + len: libc::size_t, +} + +impl PartialEq for cvec { + fn eq(&self, other: &cvec) -> bool { + if self.len != other.len { + return false; + } + + unsafe { + slice::from_raw_parts(self.data, self.len) + == slice::from_raw_parts(other.data, other.len) + } + } +} + +impl Eq for cvec {} + +impl Into for Vec { + fn into(mut self) -> cvec { + self.shrink_to_fit(); + assert!(self.len() == self.capacity()); + + let res = cvec { + data: self.as_mut_ptr(), + len: self.len() as libc::size_t, + }; + + // prevent deallocation in Rust + std::mem::forget(self); + res + } +} + +impl Into> for cvec { + fn into(self) -> Vec { + unsafe { Vec::from_raw_parts(self.data, self.len, self.len) } + } +} + +/// Get the length of the data of the given [cvec]. +#[no_mangle] +pub unsafe extern "C" fn rpgp_cvec_len(cvec_ptr: *mut cvec) -> libc::size_t { + assert!(!cvec_ptr.is_null()); + + let cvec = &*cvec_ptr; + cvec.len +} + +/// Get a pointer to the data of the given [cvec]. +#[no_mangle] +pub unsafe extern "C" fn rpgp_cvec_data(cvec_ptr: *mut cvec) -> *const u8 { + assert!(!cvec_ptr.is_null()); + + let cvec = &*cvec_ptr; + cvec.data +} + +/// Free the given [cvec]. +#[no_mangle] +pub unsafe extern "C" fn rpgp_cvec_drop(cvec_ptr: *mut cvec) { + assert!(!cvec_ptr.is_null()); + + let v = &*cvec_ptr; + let _ = Vec::from_raw_parts(v.data, v.len, v.len); + // Drop +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_cvec() { + for i in 0..100 { + let a = vec![i as u8; i * 10]; + let b: cvec = a.clone().into(); + let c: Vec = b.into(); + assert_eq!(a, c); + } + } +} diff --git a/src/pgp/errors.rs b/src/pgp/errors.rs new file mode 100644 index 000000000..d44226aa7 --- /dev/null +++ b/src/pgp/errors.rs @@ -0,0 +1,66 @@ +use std::cell::RefCell; +use std::ffi::CString; +use std::ptr; + +use failure::Error; +use libc::{c_char, c_int}; + +thread_local! { + static LAST_ERROR: RefCell>> = RefCell::new(None); +} + +/// Update the most recent error, clearing whatever may have been there before. +pub fn update_last_error(err: Error) { + { + // Print a pseudo-backtrace for this error, following back each error's + // cause until we reach the root error. + let mut cause = err.as_fail().cause(); + while let Some(parent_err) = cause { + cause = parent_err.cause(); + } + } + + LAST_ERROR.with(|prev| { + *prev.borrow_mut() = Some(Box::new(err)); + }); +} + +/// Retrieve the most recent error, clearing it in the process. +pub fn take_last_error() -> Option> { + LAST_ERROR.with(|prev| prev.borrow_mut().take()) +} + +/// Calculate the number of bytes in the last error's error message **not** +/// including any trailing `null` characters. +#[no_mangle] +pub extern "C" fn rpgp_last_error_length() -> c_int { + LAST_ERROR.with(|prev| match *prev.borrow() { + Some(ref err) => err.to_string().len() as c_int + 1, + None => 0, + }) +} + +/// Write the most recent error message into a caller-provided buffer as a UTF-8 +/// string, returning the number of bytes written. +/// +/// # Note +/// +/// This writes a **UTF-8** string into the buffer. Windows users may need to +/// convert it to a UTF-16 "unicode" afterwards. +/// +/// If there are no recent errors then this returns `0` (because we wrote 0 +/// bytes). `-1` is returned if there are any errors, for example when passed a +/// null pointer or a buffer of insufficient size. +#[no_mangle] +pub unsafe extern "C" fn rpgp_last_error_message() -> *mut c_char { + let last_error = match take_last_error() { + Some(err) => err, + None => return ptr::null_mut(), + }; + + let error_message = last_error.to_string(); + + CString::new(error_message) + .expect("CString alloc failed") + .into_raw() +} diff --git a/src/pgp/hash.rs b/src/pgp/hash.rs new file mode 100644 index 000000000..5d79812c3 --- /dev/null +++ b/src/pgp/hash.rs @@ -0,0 +1,19 @@ +use sha2::{Digest, Sha256}; +use std::slice; + +use crate::pgp::cvec; + +/// Calculate the SHA256 hash of the given bytes. +#[no_mangle] +pub unsafe extern "C" fn rpgp_hash_sha256( + bytes_ptr: *const u8, + bytes_len: libc::size_t, +) -> *mut cvec { + assert!(!bytes_ptr.is_null()); + assert!(bytes_len > 0); + + let bytes = slice::from_raw_parts(bytes_ptr, bytes_len); + let result = Sha256::digest(bytes); + + Box::into_raw(Box::new(result.to_vec().into())) +} diff --git a/src/pgp/key.rs b/src/pgp/key.rs new file mode 100644 index 000000000..474aaae7c --- /dev/null +++ b/src/pgp/key.rs @@ -0,0 +1,151 @@ +use std::ffi::CString; +use std::io::Cursor; +use std::slice; + +use libc::c_char; +use pgp::composed::{from_armor_many, from_bytes_many, PublicOrSecret}; +use pgp::types::KeyTrait; + +use crate::pgp::cvec; + +pub type public_or_secret_key = PublicOrSecret; + +/// Creates an in-memory representation of a PGP key, based on the armor file given. +/// The returned pointer should be stored, and reused when calling methods "on" this key. +/// When done with it [rpgp_key_drop] should be called, to free the memory. +#[no_mangle] +pub unsafe extern "C" fn rpgp_key_from_armor( + raw: *const u8, + len: libc::size_t, +) -> *mut public_or_secret_key { + assert!(!raw.is_null()); + assert!(len > 0); + + let bytes = slice::from_raw_parts(raw, len); + let (mut keys, _headers) = try_ffi!(from_armor_many(Cursor::new(bytes)), "failed to parse"); + + let key = try_ffi!( + try_ffi!( + keys.nth(0).ok_or_else(|| format_err!("no valid key found")), + "failed to parse key" + ), + "failed to parse key" + ); + + try_ffi!(key.verify(), "failed to verify key"); + + Box::into_raw(Box::new(key)) +} + +/// Creates an in-memory representation of a PGP key, based on the serialized bytes given. +#[no_mangle] +pub unsafe extern "C" fn rpgp_key_from_bytes( + raw: *const u8, + len: libc::size_t, +) -> *mut public_or_secret_key { + assert!(!raw.is_null()); + assert!(len > 0); + + let bytes = slice::from_raw_parts(raw, len); + let mut keys = from_bytes_many(Cursor::new(bytes)); + + let key = try_ffi!( + try_ffi!( + keys.nth(0).ok_or_else(|| format_err!("no valid key found")), + "failed to parse key" + ), + "failed to parse key" + ); + + try_ffi!(key.verify(), "failed to verify key"); + + Box::into_raw(Box::new(key)) +} + +/// Returns the KeyID for the passed in key. The caller is responsible to call [rpgp_string_drop] with the returned memory, to free it. +#[no_mangle] +pub unsafe extern "C" fn rpgp_key_id(key_ptr: *mut public_or_secret_key) -> *mut c_char { + assert!(!key_ptr.is_null()); + + let key = &*key_ptr; + let id = try_ffi!( + CString::new(hex::encode(&key.key_id())), + "failed to allocate string" + ); + + id.into_raw() +} + +/// Returns the Fingerprint for the passed in key. The caller is responsible to call [rpgp_cvec_drop] with the returned memory, to free it. +#[no_mangle] +pub unsafe extern "C" fn rpgp_key_fingerprint(key_ptr: *mut public_or_secret_key) -> *mut cvec { + assert!(!key_ptr.is_null()); + + let key = &*key_ptr; + let fingerprint = key.fingerprint(); + + Box::into_raw(Box::new(fingerprint.into())) +} + +/// Returns `true` if this key is a public key, false otherwise. +#[no_mangle] +pub unsafe extern "C" fn rpgp_key_is_public(key_ptr: *mut public_or_secret_key) -> bool { + assert!(!key_ptr.is_null()); + + (&*key_ptr).is_public() +} + +/// Returns `true` if this key is a secret key, false otherwise. +#[no_mangle] +pub unsafe extern "C" fn rpgp_key_is_secret(key_ptr: *mut public_or_secret_key) -> bool { + assert!(!key_ptr.is_null()); + + (&*key_ptr).is_secret() +} + +/// Frees the memory of the passed in key, making the pointer invalid after this method was called. +#[no_mangle] +pub unsafe extern "C" fn rpgp_key_drop(key_ptr: *mut public_or_secret_key) { + assert!(!key_ptr.is_null()); + + let _key = &*key_ptr; + // Drop +} + +#[cfg(test)] +mod tests { + use super::*; + use std::ffi::CStr; + + use crate::pgp::{ + rpgp_create_x25519_skey, rpgp_cvec_data, rpgp_cvec_drop, rpgp_cvec_len, rpgp_skey_to_bytes, + }; + + #[test] + fn test_fingerprint() { + let user_id = CStr::from_bytes_with_nul(b"\0").unwrap(); + + unsafe { + // Create the actual key + let skey = rpgp_create_x25519_skey(user_id.as_ptr()); + + // Serialize secret key into bytes + let skey_bytes = rpgp_skey_to_bytes(skey); + + let key = rpgp_key_from_bytes(rpgp_cvec_data(skey_bytes), rpgp_cvec_len(skey_bytes)); + assert!(rpgp_key_is_secret(key)); + + let fingerprint1 = rpgp_key_fingerprint(key); + + // get fingerprint directly + let mut fingerprint2: cvec = (&*skey).fingerprint().into(); + + assert_eq!(*fingerprint1, fingerprint2); + + // cleanup + rpgp_cvec_drop(skey_bytes); + rpgp_cvec_drop(fingerprint1); + rpgp_cvec_drop(&mut fingerprint2); + } + } +} diff --git a/src/pgp/macros.rs b/src/pgp/macros.rs new file mode 100644 index 000000000..61768194b --- /dev/null +++ b/src/pgp/macros.rs @@ -0,0 +1,12 @@ +#[macro_export] +macro_rules! try_ffi { + ($e:expr, $fmt:expr) => { + match $e { + Ok(v) => v, + Err(err) => { + $crate::pgp::errors::update_last_error(err.into()); + return std::ptr::null_mut(); + } + } + }; +} diff --git a/src/pgp/message.rs b/src/pgp/message.rs new file mode 100644 index 000000000..c0ef5cd1e --- /dev/null +++ b/src/pgp/message.rs @@ -0,0 +1,364 @@ +use std::ffi::{CStr, CString}; +use std::io::Cursor; +use std::slice; + +use libc::c_char; +use pgp::composed::{Deserializable, SignedPublicKey, SignedSecretKey}; +use pgp::types::{CompressionAlgorithm, KeyTrait, StringToKey}; +use rand::thread_rng; + +use crate::pgp::{cvec, signed_public_key, signed_secret_key, update_last_error}; +pub use pgp::composed::Message; +pub type message = Message; + +/// Parse an armored message. +#[no_mangle] +pub unsafe extern "C" fn rpgp_msg_from_armor( + msg_ptr: *const u8, + msg_len: libc::size_t, +) -> *mut message { + assert!(!msg_ptr.is_null()); + assert!(msg_len > 0); + + let enc_msg = slice::from_raw_parts(msg_ptr, msg_len); + + let (msg, _headers) = try_ffi!( + Message::from_armor_single(Cursor::new(enc_msg)), + "invalid message" + ); + + Box::into_raw(Box::new(msg)) +} + +/// Parse a message in bytes format. +#[no_mangle] +pub unsafe extern "C" fn rpgp_msg_from_bytes( + msg_ptr: *const u8, + msg_len: libc::size_t, +) -> *mut message { + assert!(!msg_ptr.is_null()); + assert!(msg_len > 0); + + let enc_msg = slice::from_raw_parts(msg_ptr, msg_len); + + let msg = try_ffi!(Message::from_bytes(Cursor::new(enc_msg)), "invalid message"); + + Box::into_raw(Box::new(msg)) +} + +/// Decrypt the passed in message, using a password. +#[no_mangle] +pub unsafe extern "C" fn rpgp_msg_decrypt_with_password( + msg_ptr: *const message, + password_ptr: *const c_char, +) -> *mut message { + assert!(!msg_ptr.is_null()); + assert!(!password_ptr.is_null()); + + let msg = &*msg_ptr; + let password = CStr::from_ptr(password_ptr); + let password_str = try_ffi!(password.to_str(), "invalid password"); + let mut decryptor = try_ffi!( + msg.decrypt_with_password(|| password_str.into()), + "failed to decrypt message" + ); + let decrypted_msg = try_ffi!( + try_ffi!( + decryptor.next().ok_or_else(|| format_err!("")), + "no message found" + ), + "failed to decrypt message" + ); + + Box::into_raw(Box::new(decrypted_msg)) +} + +/// Decrypt the passed in message, without attempting to use a password. +#[no_mangle] +pub unsafe extern "C" fn rpgp_msg_decrypt_no_pw( + msg_ptr: *const message, + skeys_ptr: *const *const signed_secret_key, + skeys_len: libc::size_t, + pkeys_ptr: *const *const signed_public_key, + pkeys_len: libc::size_t, +) -> *mut message_decrypt_result { + assert!(!msg_ptr.is_null()); + assert!(!skeys_ptr.is_null()); + assert!(skeys_len > 0); + + let msg = &*msg_ptr; + let skeys_raw = slice::from_raw_parts(skeys_ptr, skeys_len); + let skeys = skeys_raw + .iter() + .map(|k| { + let v: &SignedSecretKey = &**k; + v + }) + .collect::>(); + + let pkeys = if pkeys_ptr.is_null() || pkeys_len == 0 { + None + } else { + Some(slice::from_raw_parts(pkeys_ptr, pkeys_len)) + }; + + let (mut decryptor, _) = try_ffi!( + msg.decrypt(|| "".into(), || "".into(), &skeys[..]), + "failed to decrypt message" + ); + + // TODO: how to handle the case when we detect multiple messages? + let dec_msg = try_ffi!( + try_ffi!( + decryptor.next().ok_or_else(|| format_err!("no message")), + "no message found" + ), + "failed to decrypt message" + ); + + let (valid_ids_ptr, valid_ids_len) = if let Some(pkeys) = pkeys { + let mut valid_ids = pkeys + .iter() + .filter_map(|pkey| match dec_msg.verify(&(**pkey).primary_key) { + Ok(_) => Some( + CString::new(hex::encode_upper(&(&**pkey).fingerprint())) + .expect("failed to allocate") + .into_raw(), + ), + Err(_) => None, + }) + .collect::>(); + + valid_ids.shrink_to_fit(); + let res = (valid_ids.as_mut_ptr(), valid_ids.len()); + std::mem::forget(valid_ids); + res + } else { + (std::ptr::null_mut(), 0) + }; + + Box::into_raw(Box::new(message_decrypt_result { + message_ptr: Box::into_raw(Box::new(dec_msg)), + valid_ids_ptr, + valid_ids_len, + })) +} + +/// Message decryption result. +#[repr(C)] +pub struct message_decrypt_result { + /// A pointer to the decrypted message. + pub message_ptr: *mut message, + /// Pointer to a list of fingerprints which verified the signature. + pub valid_ids_ptr: *mut *mut c_char, + pub valid_ids_len: libc::size_t, +} + +/// Free a [message_decrypt_result]. +#[no_mangle] +pub unsafe extern "C" fn rpgp_message_decrypt_result_drop(res_ptr: *mut message_decrypt_result) { + assert!(!res_ptr.is_null()); + + let res = &*res_ptr; + let _msg = &*res.message_ptr; + let _ids = Vec::from_raw_parts(res.valid_ids_ptr, res.valid_ids_len, res.valid_ids_len); + // Drop +} + +/// Returns the underlying data of the given message. +/// Fails when the message is encrypted. Decompresses compressed messages. +#[no_mangle] +pub unsafe extern "C" fn rpgp_msg_to_bytes(msg_ptr: *const message) -> *mut cvec { + assert!(!msg_ptr.is_null()); + + let msg = &*msg_ptr; + + let result = try_ffi!(msg.get_content(), "failed to extract content"); + match result { + Some(data) => Box::into_raw(Box::new(data.into())), + None => { + update_last_error(format_err!("called on encrypted message").into()); + std::ptr::null_mut() + } + } +} + +/// Encodes the message into its ascii armored representation. +#[no_mangle] +pub unsafe extern "C" fn rpgp_msg_to_armored(msg_ptr: *const message) -> *mut cvec { + assert!(!msg_ptr.is_null()); + + let msg = &*msg_ptr; + + let result = try_ffi!( + msg.to_armored_bytes(None), + "failed to encode message to ASCII Armor" + ); + + Box::into_raw(Box::new(result.into())) +} + +/// Encodes the message into its ascii armored representation, returning a string. +#[no_mangle] +pub unsafe extern "C" fn rpgp_msg_to_armored_str(msg_ptr: *const message) -> *mut c_char { + assert!(!msg_ptr.is_null()); + + let msg = &*msg_ptr; + + let result = try_ffi!( + msg.to_armored_string(None), + "failed to encode message to ASCII Armor" + ); + + CString::new(result).expect("allocation failed").into_raw() +} + +/// Free a [message], that was created by rpgp. +#[no_mangle] +pub unsafe extern "C" fn rpgp_msg_drop(msg_ptr: *mut message) { + assert!(!msg_ptr.is_null()); + + let _ = &*msg_ptr; + // Drop +} + +/// Get the number of fingerprints of a given encrypted message. +#[no_mangle] +pub unsafe extern "C" fn rpgp_msg_recipients_len(msg_ptr: *mut message) -> u32 { + assert!(!msg_ptr.is_null()); + + let msg = &*msg_ptr; + + let list = msg.get_recipients(); + + list.len() as u32 +} + +/// Get the fingerprint of a given encrypted message, by index, in hexformat. +#[no_mangle] +pub unsafe extern "C" fn rpgp_msg_recipients_get(msg_ptr: *mut message, i: u32) -> *mut c_char { + assert!(!msg_ptr.is_null()); + + let msg = &*msg_ptr; + + let list = msg.get_recipients(); + if (i as usize) < list.len() { + CString::new(hex::encode(&list[i as usize])) + .expect("allocation failure") + .into_raw() + } else { + std::ptr::null_mut() + } +} + +#[no_mangle] +pub unsafe extern "C" fn rpgp_encrypt_bytes_to_keys( + bytes_ptr: *const u8, + bytes_len: libc::size_t, + pkeys_ptr: *const *const signed_public_key, + pkeys_len: libc::size_t, +) -> *mut message { + assert!(!bytes_ptr.is_null()); + assert!(bytes_len > 0); + assert!(!pkeys_ptr.is_null()); + assert!(pkeys_len > 0); + + let pkeys_raw = slice::from_raw_parts(pkeys_ptr, pkeys_len); + let pkeys = pkeys_raw + .iter() + .map(|k| { + let v: &SignedPublicKey = &**k; + v + }) + .collect::>(); + + let bytes = slice::from_raw_parts(bytes_ptr, bytes_len); + + let mut rng = thread_rng(); + let lit_msg = Message::new_literal_bytes("", bytes); + + let msg = try_ffi!( + lit_msg.encrypt_to_keys(&mut rng, Default::default(), &pkeys), + "failed to encrypt" + ); + + Box::into_raw(Box::new(msg)) +} + +#[no_mangle] +pub unsafe extern "C" fn rpgp_sign_encrypt_bytes_to_keys( + bytes_ptr: *const u8, + bytes_len: libc::size_t, + pkeys_ptr: *const *const signed_public_key, + pkeys_len: libc::size_t, + skey_ptr: *const signed_secret_key, +) -> *mut message { + assert!(!bytes_ptr.is_null()); + assert!(bytes_len > 0); + assert!(!pkeys_ptr.is_null()); + assert!(pkeys_len > 0); + assert!(!skey_ptr.is_null()); + + let pkeys_raw = slice::from_raw_parts(pkeys_ptr, pkeys_len); + let pkeys = pkeys_raw + .iter() + .map(|k| { + let v: &SignedPublicKey = &**k; + v + }) + .collect::>(); + + let skey = &*skey_ptr; + + let bytes = slice::from_raw_parts(bytes_ptr, bytes_len); + + let mut rng = thread_rng(); + + let lit_msg = Message::new_literal_bytes("", bytes); + let signed_msg = try_ffi!( + lit_msg.sign(&skey, || "".into(), Default::default()), + "failed to sign" + ); + + let compressed_msg = try_ffi!( + signed_msg.compress(CompressionAlgorithm::ZLIB), + "failed to compress" + ); + + let encrypted_msg = try_ffi!( + compressed_msg.encrypt_to_keys(&mut rng, Default::default(), &pkeys), + "failed to encrypt" + ); + + Box::into_raw(Box::new(encrypted_msg)) +} + +#[no_mangle] +pub unsafe extern "C" fn rpgp_encrypt_bytes_with_password( + bytes_ptr: *const u8, + bytes_len: libc::size_t, + password_ptr: *const c_char, +) -> *mut message { + assert!(!bytes_ptr.is_null()); + assert!(!password_ptr.is_null()); + assert!(bytes_len > 0); + + let bytes = slice::from_raw_parts(bytes_ptr, bytes_len); + + let mut rng = thread_rng(); + let lit_msg = Message::new_literal_bytes("", bytes); + + let password = CStr::from_ptr(password_ptr); + let password_str = try_ffi!(password.to_str(), "invalid password"); + + let s2k = StringToKey::new_default(&mut rng); + + let msg = try_ffi!( + lit_msg.encrypt_with_password(&mut rng, s2k, Default::default(), || { + password_str.into() + }), + "failed to encrypt" + ); + + Box::into_raw(Box::new(msg)) +} diff --git a/src/pgp/mod.rs b/src/pgp/mod.rs new file mode 100644 index 000000000..39726731b --- /dev/null +++ b/src/pgp/mod.rs @@ -0,0 +1,25 @@ +#[macro_use] +mod macros; + +mod c_vec; +mod errors; +mod hash; +mod key; +mod message; +mod public_key; +mod secret_key; + +pub use self::c_vec::*; +pub use self::errors::*; +pub use self::hash::*; +pub use self::key::*; +pub use self::message::*; +pub use self::public_key::*; +pub use self::secret_key::*; + +/// Free string, that was created by rpgp. +#[no_mangle] +pub unsafe extern "C" fn rpgp_string_drop(p: *mut libc::c_char) { + let _ = std::ffi::CString::from_raw(p); + // Drop +} diff --git a/src/pgp/public_key.rs b/src/pgp/public_key.rs new file mode 100644 index 000000000..03babf42b --- /dev/null +++ b/src/pgp/public_key.rs @@ -0,0 +1,68 @@ +use std::ffi::CString; +use std::io::Cursor; +use std::slice; + +use libc::c_char; +use pgp::composed::{Deserializable, SignedPublicKey}; +use pgp::ser::Serialize; +use pgp::types::KeyTrait; + +use crate::pgp::cvec; + +pub type signed_public_key = SignedPublicKey; + +/// Parse a serialized public key, into the native rPGP memory representation. +#[no_mangle] +pub unsafe extern "C" fn rpgp_pkey_from_bytes( + raw: *const u8, + len: libc::size_t, +) -> *mut signed_public_key { + assert!(!raw.is_null()); + assert!(len > 0); + + let bytes = slice::from_raw_parts(raw, len); + let key = try_ffi!( + SignedPublicKey::from_bytes(Cursor::new(bytes)), + "invalid public key" + ); + + try_ffi!(key.verify(), "failed to verify key"); + + Box::into_raw(Box::new(key)) +} + +/// Serialize the [signed_public_key] to bytes. +#[no_mangle] +pub unsafe extern "C" fn rpgp_pkey_to_bytes(pkey_ptr: *mut signed_public_key) -> *mut cvec { + assert!(!pkey_ptr.is_null()); + + let pkey = &*pkey_ptr; + + let mut res = Vec::new(); + try_ffi!(pkey.to_writer(&mut res), "failed to serialize key"); + + Box::into_raw(Box::new(res.into())) +} + +/// Get the key id of the given [signed_public_key]. +#[no_mangle] +pub unsafe extern "C" fn rpgp_pkey_key_id(pkey_ptr: *mut signed_public_key) -> *mut c_char { + assert!(!pkey_ptr.is_null()); + + let pkey = &*pkey_ptr; + let id = try_ffi!( + CString::new(hex::encode(&pkey.key_id())), + "failed to allocate string" + ); + + id.into_raw() +} + +/// Free the given [signed_public_key]. +#[no_mangle] +pub unsafe extern "C" fn rpgp_pkey_drop(pkey_ptr: *mut signed_public_key) { + assert!(!pkey_ptr.is_null()); + + let _pkey = &*pkey_ptr; + // Drop +} diff --git a/src/pgp/secret_key.rs b/src/pgp/secret_key.rs new file mode 100644 index 000000000..ecca1e375 --- /dev/null +++ b/src/pgp/secret_key.rs @@ -0,0 +1,207 @@ +use std::ffi::{CStr, CString}; +use std::io::Cursor; +use std::slice; + +use libc::c_char; +use pgp::composed::{ + Deserializable, KeyType, SecretKeyParamsBuilder, SignedSecretKey, SubkeyParamsBuilder, +}; +use pgp::crypto::{HashAlgorithm, SymmetricKeyAlgorithm}; +use pgp::errors::Result; +use pgp::ser::Serialize; +use pgp::types::{CompressionAlgorithm, KeyTrait, SecretKeyTrait}; +use smallvec::smallvec; + +use crate::pgp::cvec; +use crate::pgp::signed_public_key; + +pub type signed_secret_key = SignedSecretKey; + +/// Generates a new RSA key. +#[no_mangle] +pub unsafe extern "C" fn rpgp_create_rsa_skey( + bits: u32, + user_id: *const c_char, +) -> *mut signed_secret_key { + assert!(!user_id.is_null()); + + let user_id = CStr::from_ptr(user_id); + let user_id_str = try_ffi!(user_id.to_str(), "invalid user id"); + + let key = try_ffi!( + create_key(KeyType::Rsa(bits), KeyType::Rsa(bits), user_id_str), + "failed to generate key" + ); + + Box::into_raw(Box::new(key)) +} + +/// Generates a new x25519 key. +#[no_mangle] +pub unsafe extern "C" fn rpgp_create_x25519_skey(user_id: *const c_char) -> *mut signed_secret_key { + assert!(!user_id.is_null()); + + let user_id = CStr::from_ptr(user_id); + let user_id_str = try_ffi!(user_id.to_str(), "invalid user id"); + let key = try_ffi!( + create_key(KeyType::EdDSA, KeyType::ECDH, user_id_str), + "failed to generate key" + ); + + Box::into_raw(Box::new(key)) +} + +/// Serialize a secret key into its byte representation. +#[no_mangle] +pub unsafe extern "C" fn rpgp_skey_to_bytes(skey_ptr: *mut signed_secret_key) -> *mut cvec { + assert!(!skey_ptr.is_null()); + + let skey = &*skey_ptr; + + let mut res = Vec::new(); + try_ffi!(skey.to_writer(&mut res), "failed to serialize key"); + + Box::into_raw(Box::new(res.into())) +} + +/// Get the signed public key matching the given private key. Only works for non password protected keys. +#[no_mangle] +pub unsafe extern "C" fn rpgp_skey_public_key( + skey_ptr: *mut signed_secret_key, +) -> *mut signed_public_key { + assert!(!skey_ptr.is_null()); + + let skey = &*skey_ptr; + + let pkey = skey.public_key(); + let signed_pkey = try_ffi!(pkey.sign(&skey, || "".into()), "failed to sign key"); + + Box::into_raw(Box::new(signed_pkey)) +} + +/// Returns the KeyID for the passed in key. +#[no_mangle] +pub unsafe extern "C" fn rpgp_skey_key_id(skey_ptr: *mut signed_secret_key) -> *mut c_char { + assert!(!skey_ptr.is_null()); + + let key = &*skey_ptr; + let id = try_ffi!( + CString::new(hex::encode(&key.key_id())), + "failed to allocate string" + ); + + id.into_raw() +} + +/// Free the memory of a secret key. +#[no_mangle] +pub unsafe extern "C" fn rpgp_skey_drop(skey_ptr: *mut signed_secret_key) { + assert!(!skey_ptr.is_null()); + + let _skey = &*skey_ptr; + // Drop +} + +/// Creates an in-memory representation of a Secret PGP key, based on the serialized bytes given. +#[no_mangle] +pub unsafe extern "C" fn rpgp_skey_from_bytes( + raw: *const u8, + len: libc::size_t, +) -> *mut signed_secret_key { + assert!(!raw.is_null()); + assert!(len > 0); + + let bytes = slice::from_raw_parts(raw, len); + let key = try_ffi!( + SignedSecretKey::from_bytes(Cursor::new(bytes)), + "invalid secret key" + ); + try_ffi!(key.verify(), "failed to verify key"); + + Box::into_raw(Box::new(key)) +} + +fn create_key(typ: KeyType, sub_typ: KeyType, user_id: &str) -> Result { + let key_params = SecretKeyParamsBuilder::default() + .key_type(typ) + .can_create_certificates(true) + .can_sign(true) + .primary_user_id(user_id.into()) + .passphrase(None) + .preferred_symmetric_algorithms(smallvec![ + SymmetricKeyAlgorithm::AES256, + SymmetricKeyAlgorithm::AES192, + SymmetricKeyAlgorithm::AES128, + ]) + .preferred_hash_algorithms(smallvec![ + HashAlgorithm::SHA2_256, + HashAlgorithm::SHA2_384, + HashAlgorithm::SHA2_512, + HashAlgorithm::SHA2_224, + HashAlgorithm::SHA1, + ]) + .preferred_compression_algorithms(smallvec![ + CompressionAlgorithm::ZLIB, + CompressionAlgorithm::ZIP, + ]) + .subkey( + SubkeyParamsBuilder::default() + .key_type(sub_typ) + .can_encrypt(true) + .passphrase(None) + .build() + .unwrap(), + ) + .build()?; + + let key = key_params.generate()?; + + key.sign(|| "".into()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::pgp::*; + use std::ffi::CStr; + use std::slice; + + use pgp::composed::{Deserializable, SignedPublicKey, SignedSecretKey}; + + #[test] + fn test_keygen_rsa() { + let user_id = CStr::from_bytes_with_nul(b"\0").unwrap(); + + unsafe { + /* Create the actual key */ + let skey = rpgp_create_rsa_skey(2048, user_id.as_ptr()); + + /* Serialize secret key into bytes */ + let skey_bytes = rpgp_skey_to_bytes(skey); + + /* Get the public key */ + let pkey = rpgp_skey_public_key(skey); + + /* Serialize public key into bytes */ + let pkey_bytes = rpgp_pkey_to_bytes(pkey); + + let skey_bytes_vec = + slice::from_raw_parts(rpgp_cvec_data(skey_bytes), rpgp_cvec_len(skey_bytes)); + let skey_back = + SignedSecretKey::from_bytes(skey_bytes_vec).expect("invalid secret key"); + assert_eq!(&*skey, &skey_back); + + let pkey_bytes_vec = + slice::from_raw_parts(rpgp_cvec_data(pkey_bytes), rpgp_cvec_len(pkey_bytes)); + let pkey_back = + SignedPublicKey::from_bytes(pkey_bytes_vec).expect("invalid public key"); + assert_eq!(&*pkey, &pkey_back); + + /* cleanup */ + rpgp_skey_drop(skey); + rpgp_cvec_drop(skey_bytes); + rpgp_pkey_drop(pkey); + rpgp_cvec_drop(pkey_bytes); + } + } +} diff --git a/src/types.rs b/src/types.rs index 6d8749b3e..97e2e40fb 100644 --- a/src/types.rs +++ b/src/types.rs @@ -9,11 +9,6 @@ use crate::x::*; extern "C" { pub type __sFILEX; - pub type rpgp_Message; - pub type rpgp_PublicOrSecret; - pub type rpgp_SignedPublicKey; - pub type rpgp_SignedSecretKey; - pub type _telldir; pub type mailstream_cancel; pub type sqlite3; diff --git a/src/x.rs b/src/x.rs index a09a6927a..7babf5edb 100644 --- a/src/x.rs +++ b/src/x.rs @@ -642,70 +642,6 @@ extern "C" { pub fn dc_strbuilder_catf(_: *mut dc_strbuilder_t, format: *const libc::c_char, _: ...); pub fn dc_mprintf(format: *const libc::c_char, _: ...) -> *mut libc::c_char; - // -- rpgp - - pub fn rpgp_create_rsa_skey( - bits: uint32_t, - user_id: *const libc::c_char, - ) -> *mut rpgp_signed_secret_key; - pub fn rpgp_cvec_data(cvec_ptr: *mut rpgp_cvec) -> *const uint8_t; - pub fn rpgp_cvec_drop(cvec_ptr: *mut rpgp_cvec); - pub fn rpgp_cvec_len(cvec_ptr: *mut rpgp_cvec) -> size_t; - pub fn rpgp_encrypt_bytes_to_keys( - bytes_ptr: *const uint8_t, - bytes_len: size_t, - pkeys_ptr: *const *const rpgp_signed_public_key, - pkeys_len: size_t, - ) -> *mut rpgp_message; - pub fn rpgp_encrypt_bytes_with_password( - bytes_ptr: *const uint8_t, - bytes_len: size_t, - password_ptr: *const libc::c_char, - ) -> *mut rpgp_message; - pub fn rpgp_key_drop(key_ptr: *mut rpgp_public_or_secret_key); - pub fn rpgp_key_fingerprint(key_ptr: *mut rpgp_public_or_secret_key) -> *mut rpgp_cvec; - pub fn rpgp_key_from_bytes(raw: *const uint8_t, len: size_t) -> *mut rpgp_public_or_secret_key; - pub fn rpgp_key_is_public(key_ptr: *mut rpgp_public_or_secret_key) -> bool; - pub fn rpgp_key_is_secret(key_ptr: *mut rpgp_public_or_secret_key) -> bool; - pub fn rpgp_last_error_length() -> libc::c_int; - pub fn rpgp_last_error_message() -> *mut libc::c_char; - pub fn rpgp_message_decrypt_result_drop(res_ptr: *mut rpgp_message_decrypt_result); - pub fn rpgp_msg_decrypt_no_pw( - msg_ptr: *const rpgp_message, - skeys_ptr: *const *const rpgp_signed_secret_key, - skeys_len: size_t, - pkeys_ptr: *const *const rpgp_signed_public_key, - pkeys_len: size_t, - ) -> *mut rpgp_message_decrypt_result; - pub fn rpgp_msg_decrypt_with_password( - msg_ptr: *const rpgp_message, - password_ptr: *const libc::c_char, - ) -> *mut rpgp_message; - pub fn rpgp_msg_drop(msg_ptr: *mut rpgp_message); - pub fn rpgp_msg_from_armor(msg_ptr: *const uint8_t, msg_len: size_t) -> *mut rpgp_message; - pub fn rpgp_msg_from_bytes(msg_ptr: *const uint8_t, msg_len: size_t) -> *mut rpgp_message; - pub fn rpgp_msg_to_armored(msg_ptr: *const rpgp_message) -> *mut rpgp_cvec; - pub fn rpgp_msg_to_armored_str(msg_ptr: *const rpgp_message) -> *mut libc::c_char; - pub fn rpgp_msg_to_bytes(msg_ptr: *const rpgp_message) -> *mut rpgp_cvec; - pub fn rpgp_pkey_drop(pkey_ptr: *mut rpgp_signed_public_key); - pub fn rpgp_pkey_from_bytes(raw: *const uint8_t, len: size_t) -> *mut rpgp_signed_public_key; - pub fn rpgp_pkey_to_bytes(pkey_ptr: *mut rpgp_signed_public_key) -> *mut rpgp_cvec; - pub fn rpgp_sign_encrypt_bytes_to_keys( - bytes_ptr: *const uint8_t, - bytes_len: size_t, - pkeys_ptr: *const *const rpgp_signed_public_key, - pkeys_len: size_t, - skey_ptr: *const rpgp_signed_secret_key, - ) -> *mut rpgp_message; - pub fn rpgp_skey_drop(skey_ptr: *mut rpgp_signed_secret_key); - pub fn rpgp_skey_from_bytes(raw: *const uint8_t, len: size_t) -> *mut rpgp_signed_secret_key; - pub fn rpgp_skey_public_key( - skey_ptr: *mut rpgp_signed_secret_key, - ) -> *mut rpgp_signed_public_key; - pub fn rpgp_skey_to_bytes(skey_ptr: *mut rpgp_signed_secret_key) -> *mut rpgp_cvec; - pub fn rpgp_string_drop(p: *mut libc::c_char); - pub fn rpgp_hash_sha256(bytes_ptr: *const uint8_t, bytes_len: size_t) -> *mut rpgp_cvec; - // -- Sqlite3 pub fn sqlite3_bind_blob(