From 11f2a812e631b53811d2512c7bc71ad201e1f4fd Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Sat, 11 Jul 2020 22:01:23 -0400 Subject: percent-decode pinentry data responses this is required by the assuan protocol, and breaks responses with percents in them otherwise --- src/pinentry.rs | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/pinentry.rs b/src/pinentry.rs index b9b3b11..b03c94e 100644 --- a/src/pinentry.rs +++ b/src/pinentry.rs @@ -137,9 +137,41 @@ where } } + len = percent_decode(&mut data[..len]); + Ok(len) } +// not using the percent-encoding crate because it doesn't provide a way to do +// this in-place, and we want the password to always live within the locked +// vec. should really move something like this into the percent-encoding crate +// at some point. +fn percent_decode(buf: &mut [u8]) -> usize { + let mut read_idx = 0; + let mut write_idx = 0; + let len = buf.len(); + + while read_idx < len { + let mut c = buf[read_idx]; + + if c == b'%' && read_idx + 2 < len { + if let Some(h) = char::from(buf[read_idx + 1]).to_digit(16) { + #[allow(clippy::cast_possible_truncation)] + if let Some(l) = char::from(buf[read_idx + 2]).to_digit(16) { + c = h as u8 * 0x10 + l as u8; + read_idx += 2; + } + } + } + + buf[write_idx] = c; + read_idx += 1; + write_idx += 1; + } + + write_idx +} + #[test] fn test_read_password() { let good_inputs = &[ @@ -158,11 +190,23 @@ fn test_read_password() { }); } - tokio::runtime::Runtime::new().unwrap().block_on(async { + let match_inputs = &[ + (&b"OK\nOK\nOK\nOK\n"[..], &b""[..]), + (&b"D foo%25bar\n"[..], &b"foo%bar"[..]), + (&b"D foo%0abar\n"[..], &b"foo\nbar"[..]), + (&b"D foo%0Abar\n"[..], &b"foo\nbar"[..]), + (&b"D foo%0Gbar\n"[..], &b"foo%0Gbar"[..]), + (&b"D foo%0\n"[..], &b"foo%0"[..]), + (&b"D foo%\n"[..], &b"foo%"[..]), + (&b"D %25foo\n"[..], &b"%foo"[..]), + (&b"D %25\n"[..], &b"%"[..]), + ]; + + for (input, output) in match_inputs { let mut buf = [0; 64]; - let len = read_password(4, &mut buf, &b"OK\nOK\nOK\nOK\n"[..]) - .await - .unwrap(); - assert_eq!(len, 0); - }) + tokio::runtime::Runtime::new().unwrap().block_on(async { + let len = read_password(4, &mut buf, &input[..]).await.unwrap(); + assert_eq!(&buf[0..len], &output[..]); + }); + } } -- cgit v1.2.3-54-g00ecf