Merge pull request #173 from deltachat/actests

test and fix Autocrypt Setup Message flow
This commit is contained in:
holger krekel
2019-07-06 12:32:56 +02:00
committed by GitHub
8 changed files with 248 additions and 157 deletions

View File

@@ -1,4 +1,4 @@
use std::ffi::{CStr, CString};
use std::ffi::CString;
use mmime::mailmime_content::*;
use mmime::mmapstring::*;
@@ -362,11 +362,7 @@ pub unsafe fn dc_continue_key_transfer(
}
|| *filename.offset(0isize) as libc::c_int == 0i32
{
dc_log_error(
context,
0i32,
b"Message is no Autocrypt Setup Message.\x00" as *const u8 as *const libc::c_char,
);
error!(context, 0, "Message is no Autocrypt Setup Message.",);
} else if 0
== dc_read_file(
context,
@@ -377,29 +373,15 @@ pub unsafe fn dc_continue_key_transfer(
|| filecontent.is_null()
|| filebytes <= 0
{
dc_log_error(
context,
0i32,
b"Cannot read Autocrypt Setup Message file.\x00" as *const u8
as *const libc::c_char,
);
error!(context, 0, "Cannot read Autocrypt Setup Message file.",);
} else {
norm_sc = dc_normalize_setup_code(context, setup_code);
if norm_sc.is_null() {
dc_log_warning(
context,
0i32,
b"Cannot normalize Setup Code.\x00" as *const u8 as *const libc::c_char,
);
warn!(context, 0, "Cannot normalize Setup Code.",);
} else {
armored_key = dc_decrypt_setup_file(context, norm_sc, filecontent);
if armored_key.is_null() {
dc_log_warning(
context,
0i32,
b"Cannot decrypt Autocrypt Setup Message.\x00" as *const u8
as *const libc::c_char,
);
warn!(context, 0, "Cannot decrypt Autocrypt Setup Message.",);
} else if !(0 == set_self_key(context, armored_key, 1i32)) {
/*set default*/
/* error already logged */
@@ -420,136 +402,96 @@ pub unsafe fn dc_continue_key_transfer(
// TODO should return bool /rtn
unsafe fn set_self_key(
context: &Context,
armored: *const libc::c_char,
armored_c: *const libc::c_char,
set_default: libc::c_int,
) -> libc::c_int {
let mut success: libc::c_int = 0i32;
let buf: *mut libc::c_char;
// pointer inside buf, MUST NOT be free()'d
let mut buf_headerline: *const libc::c_char = 0 as *const libc::c_char;
// - " -
let mut buf_preferencrypt: *const libc::c_char = 0 as *const libc::c_char;
// - " -
let mut buf_base64: *const libc::c_char = 0 as *const libc::c_char;
let mut success = 0;
let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt;
let mut self_addr: *mut libc::c_char = 0 as *mut libc::c_char;
buf = dc_strdup(armored);
if 0 == dc_split_armored_data(
buf,
&mut buf_headerline,
0 as *mut *const libc::c_char,
&mut buf_preferencrypt,
&mut buf_base64,
) || strcmp(
buf_headerline,
b"-----BEGIN PGP PRIVATE KEY BLOCK-----\x00" as *const u8 as *const libc::c_char,
) != 0i32
|| buf_base64.is_null()
assert!(!armored_c.is_null(), "invalid buffer");
let armored = as_str(armored_c);
if let Some((private_key, public_key, header)) =
Key::from_armored_string(armored, KeyType::Private)
.and_then(|(k, h)| if k.verify() { Some((k, h)) } else { None })
.and_then(|(k, h)| k.split_key().map(|pub_key| (k, pub_key, h)))
{
dc_log_warning(
stmt = dc_sqlite3_prepare(
context,
0i32,
b"File does not contain a private key.\x00" as *const u8 as *const libc::c_char,
&context.sql,
b"DELETE FROM keypairs WHERE public_key=? OR private_key=?;\x00" as *const u8
as *const libc::c_char,
);
} else {
if let Some((private_key, public_key)) = Key::from_base64(
CStr::from_ptr(buf_base64).to_str().unwrap(),
KeyType::Private,
)
.and_then(|k| if k.verify() { Some(k) } else { None })
.and_then(|k| k.split_key().map(|pub_key| (k, pub_key)))
{
stmt = dc_sqlite3_prepare(
let pub_bytes = public_key.to_bytes();
sqlite3_bind_blob(
stmt,
1,
pub_bytes.as_ptr() as *const _,
pub_bytes.len() as libc::c_int,
None,
);
let priv_bytes = private_key.to_bytes();
sqlite3_bind_blob(
stmt,
2,
priv_bytes.as_ptr() as *const _,
priv_bytes.len() as libc::c_int,
None,
);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
stmt = 0 as *mut sqlite3_stmt;
if 0 != set_default {
dc_sqlite3_execute(
context,
&context.sql,
b"DELETE FROM keypairs WHERE public_key=? OR private_key=?;\x00" as *const u8
as *const libc::c_char,
);
let pub_bytes = public_key.to_bytes();
sqlite3_bind_blob(
stmt,
1,
pub_bytes.as_ptr() as *const _,
pub_bytes.len() as libc::c_int,
None,
);
let priv_bytes = private_key.to_bytes();
sqlite3_bind_blob(
stmt,
2,
priv_bytes.as_ptr() as *const _,
priv_bytes.len() as libc::c_int,
None,
);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
stmt = 0 as *mut sqlite3_stmt;
if 0 != set_default {
dc_sqlite3_execute(
context,
&context.sql,
b"UPDATE keypairs SET is_default=0;\x00" as *const u8 as *const libc::c_char,
);
}
self_addr = dc_sqlite3_get_config(
context,
&context.sql,
b"configured_addr\x00" as *const u8 as *const libc::c_char,
0 as *const libc::c_char,
);
if !dc_key_save_self_keypair(
context,
&public_key,
&private_key,
self_addr,
set_default,
&context.sql,
) {
dc_log_error(
context,
0i32,
b"Cannot save keypair.\x00" as *const u8 as *const libc::c_char,
);
} else {
if !buf_preferencrypt.is_null() {
if strcmp(
buf_preferencrypt,
b"nopreference\x00" as *const u8 as *const libc::c_char,
) == 0i32
{
dc_sqlite3_set_config_int(
context,
&context.sql,
b"e2ee_enabled\x00" as *const u8 as *const libc::c_char,
0i32,
);
} else if strcmp(
buf_preferencrypt,
b"mutual\x00" as *const u8 as *const libc::c_char,
) == 0i32
{
dc_sqlite3_set_config_int(
context,
&context.sql,
b"e2ee_enabled\x00" as *const u8 as *const libc::c_char,
1i32,
);
}
}
success = 1;
}
} else {
dc_log_error(
context,
0i32,
b"File does not contain a valid private key.\x00" as *const u8
as *const libc::c_char,
b"UPDATE keypairs SET is_default=0;\x00" as *const u8 as *const libc::c_char,
);
}
self_addr = dc_sqlite3_get_config(
context,
&context.sql,
b"configured_addr\x00" as *const u8 as *const libc::c_char,
0 as *const libc::c_char,
);
if !dc_key_save_self_keypair(
context,
&public_key,
&private_key,
self_addr,
set_default,
&context.sql,
) {
error!(context, 0, "Cannot save keypair.",);
} else {
let prefer_encrypt = header.get("Autocrypt-Prefer-Encrypt");
if let Some(prefer_encrypt) = prefer_encrypt {
if prefer_encrypt == "nopreference" {
dc_sqlite3_set_config_int(
context,
&context.sql,
b"e2ee_enabled\x00" as *const u8 as *const libc::c_char,
0i32,
);
} else if prefer_encrypt == "mutual" {
dc_sqlite3_set_config_int(
context,
&context.sql,
b"e2ee_enabled\x00" as *const u8 as *const libc::c_char,
1i32,
);
}
}
success = 1;
}
} else {
error!(context, 0, "File does not contain a private key.",);
}
sqlite3_finalize(stmt);
free(buf as *mut libc::c_void);
free(self_addr as *mut libc::c_void);
success

View File

@@ -126,11 +126,31 @@ impl Key {
Self::from_binary(data, len, key_type)
}
pub fn from_armored_string(
data: &str,
key_type: KeyType,
) -> Option<(Self, BTreeMap<String, String>)> {
let bytes = data.as_bytes();
let res: Result<(Key, _), _> = match key_type {
KeyType::Public => SignedPublicKey::from_armor_single(Cursor::new(bytes))
.map(|(k, h)| (Into::into(k), h)),
KeyType::Private => SignedSecretKey::from_armor_single(Cursor::new(bytes))
.map(|(k, h)| (Into::into(k), h)),
};
match res {
Ok(res) => Some(res),
Err(err) => {
eprintln!("Invalid key bytes: {:?}", err);
None
}
}
}
pub fn from_base64(encoded_data: &str, key_type: KeyType) -> Option<Self> {
// strip newlines and other whitespace
let cleaned: String = encoded_data.trim().split_whitespace().collect();
let bytes = cleaned.as_bytes();
base64::decode(bytes)
.ok()
.and_then(|decoded| Self::from_slice(&decoded, key_type))
@@ -226,16 +246,6 @@ impl Key {
.to_string()
}
/// the result must be freed
pub fn to_base64_c(&self, break_every: usize) -> *mut libc::c_char {
let res = self.to_base64(break_every);
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 { strdup(res_c.as_ptr()) }
}
pub fn to_armored_string(
&self,
headers: Option<&BTreeMap<String, String>>,
@@ -436,6 +446,73 @@ mod tests {
assert_eq!(fingerprint, "1234567890ABCDABCDEFABCDEF");
}
#[test]
fn test_from_armored_string() {
let (private_key, _) = Key::from_armored_string(
"-----BEGIN PGP PRIVATE KEY BLOCK-----
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
7yPJeQ==
=KZk/
-----END PGP PRIVATE KEY BLOCK-----",
KeyType::Private,
)
.expect("failed to decode"); // NOTE: if you take out the ===GU1/ part, everything passes!
let binary = private_key.to_bytes();
Key::from_slice(&binary, KeyType::Private).expect("invalid private key");
}
#[test]
fn test_format_fingerprint() {
let fingerprint = dc_format_fingerprint("1234567890ABCDABCDEFABCDEF1234567890ABCD");
@@ -459,4 +536,21 @@ mod tests {
let private_key2 = Key::from_slice(&binary, KeyType::Private).expect("invalid private key");
assert_eq!(private_key, private_key2);
}
#[test]
fn test_ascii_roundtrip() {
let (public_key, private_key) =
crate::pgp::dc_pgp_create_keypair(CString::new("hello").unwrap().as_ptr()).unwrap();
let s = public_key.to_armored_string(None).unwrap();
let (public_key2, _) =
Key::from_armored_string(&s, KeyType::Public).expect("invalid public key");
assert_eq!(public_key, public_key2);
let s = private_key.to_armored_string(None).unwrap();
println!("{}", &s);
let (private_key2, _) =
Key::from_armored_string(&s, KeyType::Private).expect("invalid private key");
assert_eq!(private_key, private_key2);
}
}