Improve parsing of serial numbers (#466)

Checks the length of the data returned when querying the serial number,
returning an error if it's longer than 4 bytes, and left-padding with
zeroes if it's too short.

This fixes some potential panics due to incorrect slice lengths as were
experienced in #465
This commit is contained in:
Tony Arcieri (iqlusion)
2023-01-07 09:35:37 -07:00
committed by GitHub
parent 1d33ea1747
commit f49c617a9d
2 changed files with 63 additions and 50 deletions
+15 -16
View File
@@ -105,7 +105,8 @@ impl<'tx> Transaction<'tx> {
/// Get YubiKey device serial number. /// Get YubiKey device serial number.
pub fn get_serial(&self, version: Version) -> Result<Serial> { pub fn get_serial(&self, version: Version) -> Result<Serial> {
let response = if version.major < 5 { match version.major {
4 => {
// YK4 requires switching to the yk applet to retrieve the serial // YK4 requires switching to the yk applet to retrieve the serial
let sw = Apdu::new(Ins::SelectApplication) let sw = Apdu::new(Ins::SelectApplication)
.p1(0x04) .p1(0x04)
@@ -118,12 +119,12 @@ impl<'tx> Transaction<'tx> {
return Err(Error::GenericError); return Err(Error::GenericError);
} }
let resp = Apdu::new(0x01).p1(0x10).transmit(self, 0xFF)?; let response = Apdu::new(0x01).p1(0x10).transmit(self, 0xFF)?;
if !resp.is_success() { if !response.is_success() {
error!( error!(
"failed retrieving serial number: {:04x}", "failed retrieving serial number: {:04x}",
resp.status_words().code() response.status_words().code()
); );
return Err(Error::GenericError); return Err(Error::GenericError);
} }
@@ -140,26 +141,24 @@ impl<'tx> Transaction<'tx> {
return Err(Error::GenericError); return Err(Error::GenericError);
} }
resp response.data().try_into()
} else { }
5 => {
// YK5 implements getting the serial as a PIV applet command (0xf8) // YK5 implements getting the serial as a PIV applet command (0xf8)
let resp = Apdu::new(Ins::GetSerial).transmit(self, 0xFF)?; let response = Apdu::new(Ins::GetSerial).transmit(self, 0xFF)?;
if !resp.is_success() { if !response.is_success() {
error!( error!(
"failed retrieving serial number: {:04x}", "failed retrieving serial number: {:04x}",
resp.status_words().code() response.status_words().code()
); );
return Err(Error::GenericError); return Err(Error::GenericError);
} }
resp response.data().try_into()
}; }
_ => Err(Error::NotSupported),
response.data()[..4] }
.try_into()
.map(|serial| Serial::from(u32::from_be_bytes(serial)))
.map_err(|_| Error::SizeError)
} }
/// Verify device PIN. /// Verify device PIN.
+14
View File
@@ -98,6 +98,20 @@ impl From<Serial> for u32 {
} }
} }
impl TryFrom<&[u8]> for Serial {
type Error = Error;
fn try_from(bytes: &[u8]) -> Result<Self> {
if bytes.len() > 4 {
return Err(Error::SizeError);
}
let mut arr = [0u8; 4];
arr[(4 - bytes.len())..].copy_from_slice(bytes);
Ok(Self(u32::from_be_bytes(arr)))
}
}
impl FromStr for Serial { impl FromStr for Serial {
type Err = Error; type Err = Error;