diff options
author | Neal H. Walfield <neal@pep.foundation> | 2019-11-06 14:42:50 +0100 |
---|---|---|
committer | Neal H. Walfield <neal@pep.foundation> | 2019-11-06 15:21:12 +0100 |
commit | 8693a005c08e1c84d693fe7baa154f8785007520 (patch) | |
tree | d5e65fad35a3137db184562e886e4b2a9fa7111d /openpgp-ffi | |
parent | 0fdb21c2f446234570c8935ada9f29c7f6392b98 (diff) |
openpgp: Replace RFC 2822 parser with a de factor parser
- RFC 4880 says that "by convention, [a User ID Packet] includes an
RFC 2822 [RFC2822] mail name-addr." This is not the actual
convention, and attempting to parse User IDs using an RFC 2822
parser means that many common User IDs cannot be parsed.
- Disparities between the actual convention and the stated
convention include:
- Neither users nor the software they use to create keys
correctly quotes User IDs:
- 'Nachname, Vorname <name@example.org>' is not valid, because
it contains an unquoted comma. It should be 'Nachname\,
Vorname <name@example.org>' or '"Nachname, Vorname"
<name@example.org>'. (The same goes for dots, single
quotes, etc.)
- 'user@example.org <user@example.org>' is not valid, because
it contains an unquoted at symbol.
- 'Bj=?utf-8?q?=C3=B6?=rn <bjoern@example.net>' is encoded
using RFC 2047, which is what RFC 2822 mandates when using
non-ASCII characters, but no OpenPGP software would decode
this User ID. In practice, everyone just uses UTF-8 (in
this case: 'Björn <bjoern@example.net>').
- There are many examples of User IDs containing raw email
addresses ('user@example.org'). But, these are not
"name-addr"s. At best, they are RFC 2822 "mailbox"es.
- Some User IDs only contain a name (e.g, "Frank PGP").
- RFC 2822 also includes a lot of complexity that no one uses or
needs. For instance, CFWS (comments and folding whitespace) can
be placed everywhere, and the rules for parsing them are
complex.
- Instead of continuing to bend the RFC 2822 parser to our will, we
instead accept reality.
- This patch replaces the RFC 2822 parser with a significantly
simpler parser, which is based on actual convention (i.e., User
IDs in the wild).
- This parser is based on dkg's mail to the OpenPGP working group
mailing list.
Message-ID: <87woe7zx7o.fsf@fifthhorseman.net>
https://mailarchive.ietf.org/arch/msg/openpgp/wNo27-0STfGR9JZSlC7s6OYOJkI
- This initial version has one notable regression with respect to
the RFC 2822 parser: it doesn't handle User IDs holding URIs.
Diffstat (limited to 'openpgp-ffi')
-rw-r--r-- | openpgp-ffi/include/sequoia/openpgp.h | 112 | ||||
-rw-r--r-- | openpgp-ffi/src/packet/userid.rs | 187 |
2 files changed, 46 insertions, 253 deletions
diff --git a/openpgp-ffi/include/sequoia/openpgp.h b/openpgp-ffi/include/sequoia/openpgp.h index c6157b70..071d71ff 100644 --- a/openpgp-ffi/include/sequoia/openpgp.h +++ b/openpgp-ffi/include/sequoia/openpgp.h @@ -1063,11 +1063,8 @@ pgp_key_pair_t pgp_key_into_key_pair (pgp_error_t *errp, pgp_key_t key); /*/ /// Constructs a User ID. /// -/// This escapes the name. The comment and address must be well -/// formed according to RFC 2822. Only the address is required. -/// -/// If you already have a full RFC 2822 mailbox, then you can just -/// use `UserID::from()`. +/// This does a basic check and any necessary escaping to form a de +/// factor User ID. Only the address is required. /*/ pgp_packet_t pgp_user_id_from_address (pgp_error_t *errp, const char *name, @@ -1077,14 +1074,11 @@ pgp_packet_t pgp_user_id_from_address (pgp_error_t *errp, /*/ /// Constructs a User ID. /// -/// This escapes the name. The comment must be well formed, the -/// address can be arbitrary. +/// This does a basic check and any necessary escaping to form a de +/// factor User ID. The address is not checked. /// /// This is useful when you want to specify a URI instead of an /// email address. -/// -/// If you have a full RFC 2822 mailbox, then you can just use -/// `UserID::from()`. /*/ pgp_packet_t pgp_user_id_from_unchecked_address (pgp_error_t *errp, const char *name, @@ -1111,44 +1105,29 @@ const uint8_t *pgp_user_id_value (pgp_packet_t uid, size_t *value_len); /*/ -/// Returns the User ID's display name, if any. -/// -/// The User ID is parsed as an [RFC 2822 mailbox], and the display -/// name is extracted. -/// -/// Note: invalid email addresses are accepted in order to support -/// things like URIs of the form `Hostname -/// <ssh://server@example.net>`. +/// Returns the User ID's name component, if any. /// -/// If the User ID is otherwise not a valid RFC 2822 mailbox -/// production, then an error is returned. +/// The User ID is parsed according to de factor convention, and the +/// name component is extracted. /// +/// If the User ID cannot be parsed, then an error is returned. /// -/// If the User ID does not contain a display, *name is set -/// to NULL. -/// -/// [RFC 2822 mailbox]: https://tools.ietf.org/html/rfc2822#section-3.4 +/// If the User ID does not contain a name component, *namep is set to +/// NULL. /*/ pgp_status_t pgp_user_id_name(pgp_error_t *errp, pgp_packet_t uid, char **namep); /*/ -/// Returns the User ID's comment, if any. -/// -/// The User ID is parsed as an [RFC 2822 mailbox], and the first -/// comment is extracted. +/// Returns the User ID's comment field, if any. /// -/// Note: invalid email addresses are accepted in order to support -/// things like URIs of the form `Hostname -/// <ssh://server@example.net>`. +/// The User ID is parsed according to de factor convention, and the +/// comment field is extracted. /// -/// If the User ID is otherwise not a valid RFC 2822 mailbox -/// production, then an error is returned. +/// If the User ID cannot be parsed, then an error is returned. /// /// If the User ID does not contain a comment, *commentp is set /// to NULL. -/// -/// [RFC 2822 mailbox]: https://tools.ietf.org/html/rfc2822#section-3.4 /*/ pgp_status_t pgp_user_id_comment(pgp_error_t *errp, pgp_packet_t uid, char **commentp); @@ -1156,65 +1135,16 @@ pgp_status_t pgp_user_id_comment(pgp_error_t *errp, pgp_packet_t uid, /*/ /// Returns the User ID's email address, if any. /// -/// The User ID is parsed as an [RFC 2822 mailbox], and the email -/// address is extracted. -/// -/// Note: invalid email addresses are accepted in order to support -/// things like URIs of the form `Hostname -/// <ssh://server@example.net>`. +/// The User ID is parsed according to de factor convention, and the +/// email address is extracted. /// -/// If the User ID is otherwise not a valid RFC 2822 mailbox -/// production, then an error is returned. +/// If the User ID cannot be parsed, then an error is returned. /// /// If the User ID does not contain an email address, *addressp is set /// to NULL. -/// -/// [RFC 2822 mailbox]: https://tools.ietf.org/html/rfc2822#section-3.4 -/*/ -pgp_status_t pgp_user_id_address(pgp_error_t *errp, pgp_packet_t uid, - char **addressp); - -/*/ -/// Returns the User ID's invalid address, if any. -/// -/// The User ID is parsed as an [RFC 2822 mailbox], and if the address -/// is invalid, that is returned. -/// -/// Note: invalid email addresses are accepted in order to support -/// things like URIs of the form `Hostname -/// <ssh://server@example.net>`. -/// -/// If the User ID is otherwise not a valid RFC 2822 mailbox -/// production, then an error is returned. -/// -/// If the User ID does not contain an invalid address, *otherp is -/// set to NULL. -/// -/// [RFC 2822 mailbox]: https://tools.ietf.org/html/rfc2822#section-3.4 -/*/ -pgp_status_t pgp_user_id_other(pgp_error_t *errp, pgp_packet_t uid, - char **addressp); - -/*/ -/// Returns the User ID's email address, if any. -/// -/// The User ID is parsed as an [RFC 2822 mailbox], and the email -/// address, whether it is valid or not, is extracted. -/// -/// Note: invalid email addresses are accepted in order to support -/// things like URIs of the form `Hostname -/// <ssh://server@example.net>`. -/// -/// If the User ID is otherwise not a valid RFC 2822 mailbox -/// production, then an error is returned. -/// -/// If the User ID does not contain an address (valid or invalid), -/// *addressp is set to NULL. -/// -/// [RFC 2822 mailbox]: https://tools.ietf.org/html/rfc2822#section-3.4 /*/ -pgp_status_t pgp_user_id_address_or_other(pgp_error_t *errp, pgp_packet_t uid, - char **addressp); +pgp_status_t pgp_user_id_email(pgp_error_t *errp, pgp_packet_t uid, + char **emailp); /*/ /// Returns a normalized version of the UserID's email address. @@ -1236,8 +1166,8 @@ pgp_status_t pgp_user_id_address_or_other(pgp_error_t *errp, pgp_packet_t uid, /// [empty locale]: https://www.w3.org/International/wiki/Case_folding /// [Autocryt]: https://autocrypt.org/level1.html#e-mail-address-canonicalization /*/ -pgp_status_t pgp_user_id_address_normalized(pgp_error_t *errp, pgp_packet_t uid, - char **addressp); +pgp_status_t pgp_user_id_email_normalized(pgp_error_t *errp, pgp_packet_t uid, + char **emailp); /*/ /// Returns the value of the User Attribute Packet. diff --git a/openpgp-ffi/src/packet/userid.rs b/openpgp-ffi/src/packet/userid.rs index 69edf50c..168227c4 100644 --- a/openpgp-ffi/src/packet/userid.rs +++ b/openpgp-ffi/src/packet/userid.rs @@ -27,11 +27,8 @@ fn pgp_user_id_new(value: *const c_char) /// Constructs a User ID. /// -/// This escapes the name. The comment and address must be well -/// formed according to RFC 2822. Only the address is required. -/// -/// If you already have a full RFC 2822 mailbox, then you can just -/// use `UserID::from()`. +/// This does a basic check and any necessary escaping to form a de +/// factor User ID. Only the address is required. #[::sequoia_ffi_macros::extern_fn] #[no_mangle] pub extern "C" fn pgp_user_id_from_address( @@ -63,14 +60,11 @@ fn pgp_user_id_from_address( /// Constructs a User ID. /// -/// This escapes the name. The comment must be well formed, the -/// address can be arbitrary. +/// This does a basic check and any necessary escaping to form a de +/// factor User ID. The address is not checked. /// /// This is useful when you want to specify a URI instead of an /// email address. -/// -/// If you have a full RFC 2822 mailbox, then you can just use -/// `UserID::from()`. #[::sequoia_ffi_macros::extern_fn] #[no_mangle] pub extern "C" fn pgp_user_id_from_unchecked_address( @@ -132,23 +126,15 @@ fn pgp_user_id_value(uid: *const Packet, value_len: Option<&mut size_t>) } } -/// Returns the User ID's display name, if any. -/// -/// The User ID is parsed as an [RFC 2822 mailbox], and the display -/// name is extracted. -/// -/// Note: invalid email addresses are accepted in order to support -/// things like URIs of the form `Hostname -/// <ssh://server@example.net>`. -/// -/// If the User ID is otherwise not a valid RFC 2822 mailbox -/// production, then an error is returned. +/// Returns the User ID's name component, if any. /// +/// The User ID is parsed according to de factor convention, and the +/// name component is extracted. /// -/// If the User ID does not contain a display, *name is set -/// to NULL. +/// If the User ID cannot be parsed, then an error is returned. /// -/// [RFC 2822 mailbox]: https://tools.ietf.org/html/rfc2822#section-3.4 +/// If the User ID does not contain a name component, *namep is set to +/// NULL. #[::sequoia_ffi_macros::extern_fn] #[no_mangle] pub extern "C" fn pgp_user_id_name( @@ -181,22 +167,15 @@ fn pgp_user_id_name( Status::Success } -/// Returns the User ID's comment, if any. -/// -/// The User ID is parsed as an [RFC 2822 mailbox], and the first -/// comment is extracted. +/// Returns the User ID's comment field, if any. /// -/// Note: invalid email addresses are accepted in order to support -/// things like URIs of the form `Hostname -/// <ssh://server@example.net>`. +/// The User ID is parsed according to de factor convention, and the +/// comment field is extracted. /// -/// If the User ID is otherwise not a valid RFC 2822 mailbox -/// production, then an error is returned. +/// If the User ID cannot be parsed, then an error is returned. /// /// If the User ID does not contain a comment, *commentp is set /// to NULL. -/// -/// [RFC 2822 mailbox]: https://tools.ietf.org/html/rfc2822#section-3.4 #[::sequoia_ffi_macros::extern_fn] #[no_mangle] pub extern "C" fn pgp_user_id_comment( @@ -231,23 +210,16 @@ fn pgp_user_id_comment( /// Returns the User ID's email address, if any. /// -/// The User ID is parsed as an [RFC 2822 mailbox], and the email -/// address is extracted. +/// The User ID is parsed according to de factor convention, and the +/// email address is extracted. /// -/// Note: invalid email addresses are accepted in order to support -/// things like URIs of the form `Hostname -/// <ssh://server@example.net>`. -/// -/// If the User ID is otherwise not a valid RFC 2822 mailbox -/// production, then an error is returned. +/// If the User ID cannot be parsed, then an error is returned. /// /// If the User ID does not contain an email address, *addressp is set /// to NULL. -/// -/// [RFC 2822 mailbox]: https://tools.ietf.org/html/rfc2822#section-3.4 #[::sequoia_ffi_macros::extern_fn] #[no_mangle] pub extern "C" -fn pgp_user_id_address( +fn pgp_user_id_email( errp: Option<&mut *mut crate::error::Error>, uid: *const Packet, addressp: &mut *mut c_char) -> Status @@ -256,7 +228,7 @@ fn pgp_user_id_address( let uid = uid.ref_raw(); if let &openpgp::Packet::UserID(ref uid) = uid { - match uid.address() { + match uid.email() { Ok(Some(address)) => *addressp = ffi_return_string!(address), Ok(None) => @@ -277,115 +249,6 @@ fn pgp_user_id_address( Status::Success } -/// Returns the User ID's invalid address, if any. -/// -/// The User ID is parsed as an [RFC 2822 mailbox], and if the address -/// is invalid, that is returned. -/// -/// Note: invalid email addresses are accepted in order to support -/// things like URIs of the form `Hostname -/// <ssh://server@example.net>`. -/// -/// If the User ID is otherwise not a valid RFC 2822 mailbox -/// production, then an error is returned. -/// -/// If the User ID does not contain an invalid address, *otherp is -/// set to NULL. -/// -/// [RFC 2822 mailbox]: https://tools.ietf.org/html/rfc2822#section-3.4 -#[::sequoia_ffi_macros::extern_fn] #[no_mangle] -pub extern "C" -fn pgp_user_id_other( - errp: Option<&mut *mut crate::error::Error>, uid: *const Packet, - otherp: &mut *mut c_char) - -> Status -{ - ffi_make_fry_from_errp!(errp); - let uid = uid.ref_raw(); - - if let &openpgp::Packet::UserID(ref uid) = uid { - match uid.other() { - Ok(Some(other)) => - *otherp = ffi_return_string!(other), - Ok(None) => - *otherp = ::std::ptr::null_mut(), - Err(err) => { - use crate::MoveIntoRaw; - let status = crate::error::Status::from(&err); - if let Some(errp) = errp { - *errp = err.move_into_raw(); - } - return status; - } - } - } else { - panic!("Not a UserID packet"); - } - - Status::Success -} - -/// Returns the User ID's email address, if any. -/// -/// The User ID is parsed as an [RFC 2822 mailbox], and the email -/// address, whether it is valid or not, is extracted. -/// -/// Note: invalid email addresses are accepted in order to support -/// things like URIs of the form `Hostname -/// <ssh://server@example.net>`. -/// -/// If the User ID is otherwise not a valid RFC 2822 mailbox -/// production, then an error is returned. -/// -/// If the User ID does not contain an address (valid or invalid), -/// *addressp is set to NULL. -/// -/// [RFC 2822 mailbox]: https://tools.ietf.org/html/rfc2822#section-3.4 -#[::sequoia_ffi_macros::extern_fn] #[no_mangle] -pub extern "C" -fn pgp_user_id_address_or_other( - errp: Option<&mut *mut crate::error::Error>, uid: *const Packet, - addressp: &mut *mut c_char) - -> Status -{ - ffi_make_fry_from_errp!(errp); - let uid = uid.ref_raw(); - - if let &openpgp::Packet::UserID(ref uid) = uid { - match uid.address() { - Ok(Some(address)) => - *addressp = ffi_return_string!(address), - Ok(None) => - match uid.other() { - Ok(Some(other)) => - *addressp = ffi_return_string!(other), - Ok(None) => - *addressp = ::std::ptr::null_mut(), - Err(err) => { - use crate::MoveIntoRaw; - let status = crate::error::Status::from(&err); - if let Some(errp) = errp { - *errp = err.move_into_raw(); - } - return status; - } - }, - Err(err) => { - use crate::MoveIntoRaw; - let status = crate::error::Status::from(&err); - if let Some(errp) = errp { - *errp = err.move_into_raw(); - } - return status; - } - } - } else { - panic!("Not a UserID packet"); - } - - Status::Success -} - /// Returns a normalized version of the UserID's email address. /// /// Normalized email addresses are primarily needed when email @@ -406,20 +269,20 @@ fn pgp_user_id_address_or_other( /// [Autocrypt]: https://autocrypt.org/level1.html#e-mail-address-canonicalization #[::sequoia_ffi_macros::extern_fn] #[no_mangle] pub extern "C" -fn pgp_user_id_address_normalized( +fn pgp_user_id_email_normalized( errp: Option<&mut *mut crate::error::Error>, uid: *const Packet, - addressp: &mut *mut c_char) + emailp: &mut *mut c_char) -> Status { ffi_make_fry_from_errp!(errp); let uid = uid.ref_raw(); if let &openpgp::Packet::UserID(ref uid) = uid { - match uid.address_normalized() { - Ok(Some(address)) => - *addressp = ffi_return_string!(address), + match uid.email_normalized() { + Ok(Some(email)) => + *emailp = ffi_return_string!(email), Ok(None) => - *addressp = ::std::ptr::null_mut(), + *emailp = ::std::ptr::null_mut(), Err(err) => { use crate::MoveIntoRaw; let status = crate::error::Status::from(&err); |