Fix bug in key::generate and document weirdness

Bug was introduced in #73 when starting offsets were overlooked. Digging
into why they were there led to uncovering the weird not-quite-ASN.1
format that the YubiKey returns generated pubkeys in.
This commit is contained in:
Jack Grigg
2019-12-11 02:24:18 +00:00
parent 41b10d1f23
commit 2eff313064
2 changed files with 40 additions and 2 deletions
+38 -2
View File
@@ -548,9 +548,41 @@ pub fn generate(
}
}
// TODO(str4d): Response is wrapped in an ASN.1 TLV:
//
// 0x7f 0x49 -> Application | Constructed | 0x49
match algorithm {
AlgorithmId::Rsa1024 | AlgorithmId::Rsa2048 => {
let (data, modulus_tlv) = Tlv::parse(response.data())?;
// It appears that the inner application-specific value returned by the
// YubiKey is constructed such that RSA pubkeys can be parsed in two ways:
//
// - Use a full ASN.1 parser on the entire datastructure:
//
// RSA 1024:
// [127, 73, 129, 136, 129, 129, 128, [ 128 octets ], 130, 3, 1, 0, 1]
// | tag | len:136 |0x81| len:128 | modulus |0x82|len3| exp |
//
// RSA 2048:
// [127, 73, 130, 1, 9, 129, 130, 1, 0, [ 256 octets ], 130, 3, 1, 0, 1]
// | tag | len:265 |0x81| len:256 | modulus |0x82|len3| exp |
//
// - Skip the first 5 bytes and use crate::serialize::get_length during TLV
// parsing (which treats 128 as a single-byte definite length instead of an
// indefinite length):
//
// RSA 1024:
// [127, 73, 129, 136, 129, 129, 128, [ 128 octets ], 130, 3, 1, 0, 1]
// | |0x81|len128| modulus |0x82|len3| exp |
//
// RSA 2048:
// [127, 73, 130, 1, 9, 129, 130, 1, 0, [ 256 octets ], 130, 3, 1, 0, 1]
// | |0x81| len:256 | modulus |0x82|len3| exp |
//
// Because of the above, treat this for now as a 2-byte ASN.1 tag with a
// 3-byte length.
let data = &response.data()[5..];
let (data, modulus_tlv) = Tlv::parse(data)?;
if modulus_tlv.tag != TAG_RSA_MODULUS {
error!("Failed to parse public key structure (modulus)");
return Err(Error::ParseError);
@@ -574,13 +606,17 @@ pub fn generate(
})
}
AlgorithmId::EccP256 | AlgorithmId::EccP384 => {
// 2-byte ASN.1 tag, 1-byte length (because all supported EC pubkey lengths
// are shorter than 128 bytes, fitting into a definite short ASN.1 length).
let data = &response.data()[3..];
let len = if let AlgorithmId::EccP256 = algorithm {
CB_ECC_POINTP256
} else {
CB_ECC_POINTP384
};
let (_, tlv) = Tlv::parse(response.data())?;
let (_, tlv) = Tlv::parse(data)?;
if tlv.tag != TAG_ECC_POINT {
error!("failed to parse public key structure");