mirror of
https://github.com/chatmail/relay.git
synced 2026-09-18 05:10:09 +00:00
feat: Support legacy, pre-OpenPGP packet format (#44)
Support legacy packet format for compatibility with GnuPG. Closes: #43 Signed-off-by: Jagoda Ślązak <jslazak@jslazak.com>
This commit is contained in:
committed by
GitHub
parent
c1f728535f
commit
cc1604b0b2
+130
-37
@@ -23,48 +23,122 @@ macro_rules! get_byte {
|
|||||||
///
|
///
|
||||||
/// Returns an [`error::Error::TruncatedHeader`] if the OpenPGP packet header is truncated.
|
/// Returns an [`error::Error::TruncatedHeader`] if the OpenPGP packet header is truncated.
|
||||||
fn check_openpgp_payload(payload: &[u8]) -> Result<bool, error::Error> {
|
fn check_openpgp_payload(payload: &[u8]) -> Result<bool, error::Error> {
|
||||||
|
// From RFC9580; we have to support legacy format too, for compatibility with GnuPG.
|
||||||
|
//
|
||||||
|
// +---------------+
|
||||||
|
// Encoded Packet Type ID: |7 6 5 4 3 2 1 0|
|
||||||
|
// +---------------+
|
||||||
|
// OpenPGP format:
|
||||||
|
// Bit 7 -- always one
|
||||||
|
// Bit 6 -- always one
|
||||||
|
// Bits 5 to 0 -- Packet Type ID
|
||||||
|
//
|
||||||
|
// Legacy format:
|
||||||
|
// Bit 7 -- always one
|
||||||
|
// Bit 6 -- always zero
|
||||||
|
// Bits 5 to 2 -- Packet Type ID
|
||||||
|
// Bits 1 to 0 -- length-type
|
||||||
|
|
||||||
let mut i: usize = 0;
|
let mut i: usize = 0;
|
||||||
while i < payload.len() {
|
while i < payload.len() {
|
||||||
// Only OpenPGP format is allowed.
|
let bits_7_6 = get_byte!(payload, i) & 0xC0;
|
||||||
if (get_byte!(payload, i) & 0xC0) != 0xC0 {
|
// bit 6 is 0 for legacy (GnuPG)
|
||||||
log::debug!("check_openpgp_payload: i={i} Not OpenPGP format");
|
let legacy = if bits_7_6 == 0xC0 {
|
||||||
return Ok(false);
|
false
|
||||||
}
|
} else if bits_7_6 == 0x80 {
|
||||||
|
true
|
||||||
let packet_type_id = get_byte!(payload, i) & 0x3F;
|
|
||||||
i += 1;
|
|
||||||
|
|
||||||
while get_byte!(payload, i) >= 224 && get_byte!(payload, i) < 255 {
|
|
||||||
// Partial body length.
|
|
||||||
let partial_length = 1usize << (get_byte!(payload, i) & 0x1F);
|
|
||||||
i += 1 + partial_length;
|
|
||||||
}
|
|
||||||
|
|
||||||
let body_len: usize;
|
|
||||||
if get_byte!(payload, i) < 192 {
|
|
||||||
// One-octet length.
|
|
||||||
body_len = get_byte!(payload, i) as usize;
|
|
||||||
i += 1;
|
|
||||||
} else if get_byte!(payload, i) < 224 {
|
|
||||||
// Two-octet length.
|
|
||||||
body_len = (((get_byte!(payload, i) as usize) - 192) << 8)
|
|
||||||
+ (get_byte!(payload, i + 1) as usize)
|
|
||||||
+ 192;
|
|
||||||
i += 2;
|
|
||||||
} else if get_byte!(payload, i) == 255 {
|
|
||||||
// Five-octet length.
|
|
||||||
body_len = ((get_byte!(payload, i + 1) as usize) << 24)
|
|
||||||
| ((get_byte!(payload, i + 2) as usize) << 16)
|
|
||||||
| ((get_byte!(payload, i + 3) as usize) << 8)
|
|
||||||
| (get_byte!(payload, i + 4) as usize);
|
|
||||||
i += 5;
|
|
||||||
} else {
|
} else {
|
||||||
// Impossible, partial body length was processed above.
|
log::debug!(
|
||||||
log::debug!("check_openpgp_payload: i={i} Invalid body length");
|
"check_openpgp_payload: i={i} bits 7 and 6 doesn't indicate OpenPGP or legacy format"
|
||||||
|
);
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
};
|
||||||
|
|
||||||
i += body_len;
|
let packet_type_id = if legacy {
|
||||||
|
// bits 5 to 2
|
||||||
|
(get_byte!(payload, i) & 0x3C) >> 2
|
||||||
|
} else {
|
||||||
|
// bits 5 to 0
|
||||||
|
get_byte!(payload, i) & 0x3F
|
||||||
|
};
|
||||||
|
|
||||||
|
if legacy {
|
||||||
|
// Body length calculation - Legacy, RFC9580 4.2.2
|
||||||
|
|
||||||
|
let length_type = get_byte!(payload, i) & 0x03;
|
||||||
|
i += 1;
|
||||||
|
|
||||||
|
let body_len: usize;
|
||||||
|
match length_type {
|
||||||
|
0 => {
|
||||||
|
// One-octet length.
|
||||||
|
body_len = get_byte!(payload, i) as usize;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
// Two-octet length.
|
||||||
|
body_len = ((get_byte!(payload, i) as usize) << 8)
|
||||||
|
+ (get_byte!(payload, i + 1) as usize);
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
// Four-octet length.
|
||||||
|
body_len = ((get_byte!(payload, i) as usize) << 24)
|
||||||
|
+ ((get_byte!(payload, i + 1) as usize) << 16)
|
||||||
|
+ ((get_byte!(payload, i + 2) as usize) << 8)
|
||||||
|
+ (get_byte!(payload, i + 3) as usize);
|
||||||
|
i += 4;
|
||||||
|
}
|
||||||
|
// 3 - indeterminate length, not supported
|
||||||
|
// RFC9580 4.2.2:
|
||||||
|
// "An implementation MUST NOT generate a Legacy format packet with indeterminate length."
|
||||||
|
// We hope they don't.
|
||||||
|
_ => {
|
||||||
|
log::debug!(
|
||||||
|
"check_openpgp_payload: i={i} Indeterminate length (3) length-type in legacy packet format"
|
||||||
|
);
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
i += body_len;
|
||||||
|
} else {
|
||||||
|
// Body length calculation - OpenPGP, RFC9580 4.2.1
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
|
||||||
|
while get_byte!(payload, i) >= 224 && get_byte!(payload, i) < 255 {
|
||||||
|
// Partial body length.
|
||||||
|
let partial_length = 1usize << (get_byte!(payload, i) & 0x1F);
|
||||||
|
i += 1 + partial_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
let body_len: usize;
|
||||||
|
if get_byte!(payload, i) < 192 {
|
||||||
|
// One-octet length.
|
||||||
|
body_len = get_byte!(payload, i) as usize;
|
||||||
|
i += 1;
|
||||||
|
} else if get_byte!(payload, i) < 224 {
|
||||||
|
// Two-octet length.
|
||||||
|
body_len = (((get_byte!(payload, i) as usize) - 192) << 8)
|
||||||
|
+ (get_byte!(payload, i + 1) as usize)
|
||||||
|
+ 192;
|
||||||
|
i += 2;
|
||||||
|
} else if get_byte!(payload, i) == 255 {
|
||||||
|
// Five-octet length.
|
||||||
|
body_len = ((get_byte!(payload, i + 1) as usize) << 24)
|
||||||
|
| ((get_byte!(payload, i + 2) as usize) << 16)
|
||||||
|
| ((get_byte!(payload, i + 3) as usize) << 8)
|
||||||
|
| (get_byte!(payload, i + 4) as usize);
|
||||||
|
i += 5;
|
||||||
|
} else {
|
||||||
|
// Impossible, partial body length was processed above.
|
||||||
|
log::debug!("check_openpgp_payload: i={i} Invalid body length");
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
i += body_len;
|
||||||
|
}
|
||||||
|
|
||||||
if i == payload.len() {
|
if i == payload.len() {
|
||||||
// Last packet should be
|
// Last packet should be
|
||||||
@@ -168,6 +242,25 @@ uj3GwEFrHS+Xuf2UDgpszYT3hI2cL/kUtGakVR7m7vVMZqXBUbZdGAEb1PZNPwsI
|
|||||||
E4aMK02+EVB+tSN4Fzj99N2YD0inVYt+oPjr2tHhUS6aSGBNS/48Ki47DOg4Sxkn
|
E4aMK02+EVB+tSN4Fzj99N2YD0inVYt+oPjr2tHhUS6aSGBNS/48Ki47DOg4Sxkn
|
||||||
lkOWnEbCD+XTnbDd
|
lkOWnEbCD+XTnbDd
|
||||||
=agR5
|
=agR5
|
||||||
|
-----END PGP MESSAGE-----"#, (true, true))]
|
||||||
|
// GnuPG uses a legacy packet format that was obsoleted back in 1998 (RFC2440);
|
||||||
|
// despite RFC9580 stating "The Legacy packet format SHOULD NOT be used to generate new data".
|
||||||
|
#[case::gnupg(r#"-----BEGIN PGP MESSAGE-----
|
||||||
|
|
||||||
|
hF4DFYw3E47UG/ASAQdA3JnkJLJJALitYG/jzzEuF0p+hEBsgLTicaEJkJerInkw
|
||||||
|
SftuCMBjxv4D6rTqNkBn7IQh6gmlUKrUNBVEnq0V1InVF9o0kDpZfUUaB5ajNDMb
|
||||||
|
hF4DMiMKov7/CVgSAQdA7ib6HK7UVW2IdAM5PILbtqzuynukooXdg9YWWamjal8w
|
||||||
|
h/L0BxbtUFzI3U847dvzwtsW7A9CZXpi45OMXRC9mO3G/FAY1fW+C6PwObT7X1qK
|
||||||
|
0sDSAbyYddCihL1UEl6EigtD/YQezCTCydzstD2cRdV75lmUqzMCR8NbfvdttBbl
|
||||||
|
g04ZZtoHm1DSnXIAN4iFzW4EBgNVUDCnqO8wyYmWw4OCOt45IkTXGQUICGxeVh6g
|
||||||
|
4Pdj4OzSrg/mTZGIDjE2h/osVpPEQjwcAus73EZC4sz4n2U+OMHsq0M54Ds7wyGv
|
||||||
|
1KBzcQQ5Rc/DPPT0INnl58datm2HGKtWFWYab9/cw/fnQ+oYhmpkZbdITTmnTAyn
|
||||||
|
CHih86i0w6gXsX99iRLe6yP8BcU3rCCflsCp5zzlcFYViTPgEn1W8H32VOXulfcV
|
||||||
|
rpRFtc5yzjFj8Dvq9gI9yGIgqiS7oEAXdRBYhmBRTydMcbS1bCPDWy5w95Cp2VzR
|
||||||
|
x8cBq5e+27QTl9YNHr4xeric7del79wktJFiINzUCQPwkwM9ud2EJ1zN8Owr2Dp8
|
||||||
|
JvS2ioSEK5J2Lc95vDLmbVnMuKmVW6qD+fEdtYSHnSIc/t+iYG3GlaFyvO8t0Jav
|
||||||
|
cbsijrbYhoSOq67KseQWvfL+gL5u
|
||||||
|
=WWqc
|
||||||
-----END PGP MESSAGE-----"#, (true, true))]
|
-----END PGP MESSAGE-----"#, (true, true))]
|
||||||
#[case::with_comment(r#"-----BEGIN PGP MESSAGE-----
|
#[case::with_comment(r#"-----BEGIN PGP MESSAGE-----
|
||||||
Version: 1
|
Version: 1
|
||||||
|
|||||||
Reference in New Issue
Block a user