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
+49 -50
View File
@@ -105,61 +105,60 @@ 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 {
// YK4 requires switching to the yk applet to retrieve the serial 4 => {
let sw = Apdu::new(Ins::SelectApplication) // YK4 requires switching to the yk applet to retrieve the serial
.p1(0x04) let sw = Apdu::new(Ins::SelectApplication)
.data(YK_AID) .p1(0x04)
.transmit(self, 0xFF)? .data(YK_AID)
.status_words(); .transmit(self, 0xFF)?
.status_words();
if !sw.is_success() { if !sw.is_success() {
error!("failed selecting yk application: {:04x}", sw.code()); error!("failed selecting yk application: {:04x}", sw.code());
return Err(Error::GenericError); return Err(Error::GenericError);
}
let response = Apdu::new(0x01).p1(0x10).transmit(self, 0xFF)?;
if !response.is_success() {
error!(
"failed retrieving serial number: {:04x}",
response.status_words().code()
);
return Err(Error::GenericError);
}
// reselect the PIV applet
let sw = Apdu::new(Ins::SelectApplication)
.p1(0x04)
.data(PIV_AID)
.transmit(self, 0xFF)?
.status_words();
if !sw.is_success() {
error!("failed selecting application: {:04x}", sw.code());
return Err(Error::GenericError);
}
response.data().try_into()
} }
5 => {
// YK5 implements getting the serial as a PIV applet command (0xf8)
let response = Apdu::new(Ins::GetSerial).transmit(self, 0xFF)?;
let resp = Apdu::new(0x01).p1(0x10).transmit(self, 0xFF)?; if !response.is_success() {
error!(
"failed retrieving serial number: {:04x}",
response.status_words().code()
);
return Err(Error::GenericError);
}
if !resp.is_success() { response.data().try_into()
error!(
"failed retrieving serial number: {:04x}",
resp.status_words().code()
);
return Err(Error::GenericError);
} }
_ => Err(Error::NotSupported),
// reselect the PIV applet }
let sw = Apdu::new(Ins::SelectApplication)
.p1(0x04)
.data(PIV_AID)
.transmit(self, 0xFF)?
.status_words();
if !sw.is_success() {
error!("failed selecting application: {:04x}", sw.code());
return Err(Error::GenericError);
}
resp
} else {
// YK5 implements getting the serial as a PIV applet command (0xf8)
let resp = Apdu::new(Ins::GetSerial).transmit(self, 0xFF)?;
if !resp.is_success() {
error!(
"failed retrieving serial number: {:04x}",
resp.status_words().code()
);
return Err(Error::GenericError);
}
resp
};
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;