Merge branch 'master' into get-next-assertion

This commit is contained in:
kaczmarczyck
2020-11-26 18:51:06 +01:00
committed by GitHub
4 changed files with 106 additions and 54 deletions

View File

@@ -13,7 +13,6 @@
// limitations under the License. // limitations under the License.
use super::hid::ChannelID; use super::hid::ChannelID;
use super::key_material::{ATTESTATION_CERTIFICATE, ATTESTATION_PRIVATE_KEY};
use super::status_code::Ctap2StatusCode; use super::status_code::Ctap2StatusCode;
use super::CtapState; use super::CtapState;
use alloc::vec::Vec; use alloc::vec::Vec;
@@ -36,6 +35,8 @@ pub enum Ctap1StatusCode {
SW_WRONG_LENGTH = 0x6700, SW_WRONG_LENGTH = 0x6700,
SW_CLA_NOT_SUPPORTED = 0x6E00, SW_CLA_NOT_SUPPORTED = 0x6E00,
SW_INS_NOT_SUPPORTED = 0x6D00, SW_INS_NOT_SUPPORTED = 0x6D00,
SW_MEMERR = 0x6501,
SW_COMMAND_ABORTED = 0x6F00,
SW_VENDOR_KEY_HANDLE_TOO_LONG = 0xF000, SW_VENDOR_KEY_HANDLE_TOO_LONG = 0xF000,
} }
@@ -50,6 +51,8 @@ impl TryFrom<u16> for Ctap1StatusCode {
0x6700 => Ok(Ctap1StatusCode::SW_WRONG_LENGTH), 0x6700 => Ok(Ctap1StatusCode::SW_WRONG_LENGTH),
0x6E00 => Ok(Ctap1StatusCode::SW_CLA_NOT_SUPPORTED), 0x6E00 => Ok(Ctap1StatusCode::SW_CLA_NOT_SUPPORTED),
0x6D00 => Ok(Ctap1StatusCode::SW_INS_NOT_SUPPORTED), 0x6D00 => Ok(Ctap1StatusCode::SW_INS_NOT_SUPPORTED),
0x6501 => Ok(Ctap1StatusCode::SW_MEMERR),
0x6F00 => Ok(Ctap1StatusCode::SW_COMMAND_ABORTED),
0xF000 => Ok(Ctap1StatusCode::SW_VENDOR_KEY_HANDLE_TOO_LONG), 0xF000 => Ok(Ctap1StatusCode::SW_VENDOR_KEY_HANDLE_TOO_LONG),
_ => Err(()), _ => Err(()),
} }
@@ -289,20 +292,30 @@ impl Ctap1Command {
let pk = sk.genpk(); let pk = sk.genpk();
let key_handle = ctap_state let key_handle = ctap_state
.encrypt_key_handle(sk, &application, None) .encrypt_key_handle(sk, &application, None)
.map_err(|_| Ctap1StatusCode::SW_VENDOR_KEY_HANDLE_TOO_LONG)?; .map_err(|_| Ctap1StatusCode::SW_COMMAND_ABORTED)?;
if key_handle.len() > 0xFF { if key_handle.len() > 0xFF {
// This is just being defensive with unreachable code. // This is just being defensive with unreachable code.
return Err(Ctap1StatusCode::SW_VENDOR_KEY_HANDLE_TOO_LONG); return Err(Ctap1StatusCode::SW_VENDOR_KEY_HANDLE_TOO_LONG);
} }
let mut response = let certificate = ctap_state
Vec::with_capacity(105 + key_handle.len() + ATTESTATION_CERTIFICATE.len()); .persistent_store
.attestation_certificate()
.map_err(|_| Ctap1StatusCode::SW_MEMERR)?
.ok_or(Ctap1StatusCode::SW_COMMAND_ABORTED)?;
let private_key = ctap_state
.persistent_store
.attestation_private_key()
.map_err(|_| Ctap1StatusCode::SW_MEMERR)?
.ok_or(Ctap1StatusCode::SW_COMMAND_ABORTED)?;
let mut response = Vec::with_capacity(105 + key_handle.len() + certificate.len());
response.push(Ctap1Command::LEGACY_BYTE); response.push(Ctap1Command::LEGACY_BYTE);
let user_pk = pk.to_uncompressed(); let user_pk = pk.to_uncompressed();
response.extend_from_slice(&user_pk); response.extend_from_slice(&user_pk);
response.push(key_handle.len() as u8); response.push(key_handle.len() as u8);
response.extend(key_handle.clone()); response.extend(key_handle.clone());
response.extend_from_slice(&ATTESTATION_CERTIFICATE); response.extend_from_slice(&certificate);
// The first byte is reserved. // The first byte is reserved.
let mut signature_data = Vec::with_capacity(66 + key_handle.len()); let mut signature_data = Vec::with_capacity(66 + key_handle.len());
@@ -312,7 +325,7 @@ impl Ctap1Command {
signature_data.extend(key_handle); signature_data.extend(key_handle);
signature_data.extend_from_slice(&user_pk); signature_data.extend_from_slice(&user_pk);
let attestation_key = crypto::ecdsa::SecKey::from_bytes(ATTESTATION_PRIVATE_KEY).unwrap(); let attestation_key = crypto::ecdsa::SecKey::from_bytes(private_key).unwrap();
let signature = attestation_key.sign_rfc6979::<crypto::sha256::Sha256>(&signature_data); let signature = attestation_key.sign_rfc6979::<crypto::sha256::Sha256>(&signature_data);
response.extend(signature.to_asn1_der()); response.extend(signature.to_asn1_der());
@@ -373,7 +386,7 @@ impl Ctap1Command {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::super::{CREDENTIAL_ID_BASE_SIZE, USE_SIGNATURE_COUNTER}; use super::super::{key_material, CREDENTIAL_ID_BASE_SIZE, USE_SIGNATURE_COUNTER};
use super::*; use super::*;
use crypto::rng256::ThreadRng256; use crypto::rng256::ThreadRng256;
use crypto::Hash256; use crypto::Hash256;
@@ -433,9 +446,30 @@ mod test {
let message = create_register_message(&application); let message = create_register_message(&application);
ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE); ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE);
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE); ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE);
// Certificate and private key are missing
assert_eq!(response, Err(Ctap1StatusCode::SW_COMMAND_ABORTED));
let fake_key = [0x41u8; key_material::ATTESTATION_PRIVATE_KEY_LENGTH];
assert!(ctap_state
.persistent_store
.set_attestation_private_key(&fake_key)
.is_ok());
ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE);
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE);
// Certificate is still missing
assert_eq!(response, Err(Ctap1StatusCode::SW_COMMAND_ABORTED));
let fake_cert = [0x99u8; 100]; // Arbitrary length
assert!(ctap_state
.persistent_store
.set_attestation_certificate(&fake_cert[..])
.is_ok());
ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE);
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
let response = let response =
Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE).unwrap(); Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE).unwrap();
assert_eq!(response[0], Ctap1Command::LEGACY_BYTE); assert_eq!(response[0], Ctap1Command::LEGACY_BYTE);
assert_eq!(response[66], CREDENTIAL_ID_BASE_SIZE as u8); assert_eq!(response[66], CREDENTIAL_ID_BASE_SIZE as u8);
assert!(ctap_state assert!(ctap_state
@@ -447,8 +481,8 @@ mod test {
.is_some()); .is_some());
const CERT_START: usize = 67 + CREDENTIAL_ID_BASE_SIZE; const CERT_START: usize = 67 + CREDENTIAL_ID_BASE_SIZE;
assert_eq!( assert_eq!(
&response[CERT_START..CERT_START + ATTESTATION_CERTIFICATE.len()], &response[CERT_START..CERT_START + fake_cert.len()],
&ATTESTATION_CERTIFICATE[..] &fake_cert[..]
); );
} }

View File

@@ -12,10 +12,14 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
pub const AAGUID: &[u8; 16] = include_bytes!(concat!(env!("OUT_DIR"), "/opensk_aaguid.bin")); pub const ATTESTATION_PRIVATE_KEY_LENGTH: usize = 32;
pub const AAGUID_LENGTH: usize = 16;
pub const AAGUID: &[u8; AAGUID_LENGTH] =
include_bytes!(concat!(env!("OUT_DIR"), "/opensk_aaguid.bin"));
pub const ATTESTATION_CERTIFICATE: &[u8] = pub const ATTESTATION_CERTIFICATE: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/opensk_cert.bin")); include_bytes!(concat!(env!("OUT_DIR"), "/opensk_cert.bin"));
pub const ATTESTATION_PRIVATE_KEY: &[u8; 32] = pub const ATTESTATION_PRIVATE_KEY: &[u8; ATTESTATION_PRIVATE_KEY_LENGTH] =
include_bytes!(concat!(env!("OUT_DIR"), "/opensk_pkey.bin")); include_bytes!(concat!(env!("OUT_DIR"), "/opensk_pkey.bin"));

View File

@@ -573,10 +573,12 @@ where
let mut signature_data = auth_data.clone(); let mut signature_data = auth_data.clone();
signature_data.extend(client_data_hash); signature_data.extend(client_data_hash);
// We currently use the presence of the attestation private key in the persistent storage to
// decide whether batch attestation is needed. let (signature, x5c) = if USE_BATCH_ATTESTATION {
let (signature, x5c) = match self.persistent_store.attestation_private_key()? { let attestation_private_key = self
Some(attestation_private_key) => { .persistent_store
.attestation_private_key()?
.ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?;
let attestation_key = let attestation_key =
crypto::ecdsa::SecKey::from_bytes(attestation_private_key).unwrap(); crypto::ecdsa::SecKey::from_bytes(attestation_private_key).unwrap();
let attestation_certificate = self let attestation_certificate = self
@@ -587,11 +589,11 @@ where
attestation_key.sign_rfc6979::<crypto::sha256::Sha256>(&signature_data), attestation_key.sign_rfc6979::<crypto::sha256::Sha256>(&signature_data),
Some(vec![attestation_certificate]), Some(vec![attestation_certificate]),
) )
} } else {
None => ( (
sk.sign_rfc6979::<crypto::sha256::Sha256>(&signature_data), sk.sign_rfc6979::<crypto::sha256::Sha256>(&signature_data),
None, None,
), )
}; };
let attestation_statement = PackedAttestationStatement { let attestation_statement = PackedAttestationStatement {
alg: SignatureAlgorithm::ES256 as i64, alg: SignatureAlgorithm::ES256 as i64,

View File

@@ -15,9 +15,9 @@
#[cfg(feature = "with_ctap2_1")] #[cfg(feature = "with_ctap2_1")]
use crate::ctap::data_formats::{extract_array, extract_text_string}; use crate::ctap::data_formats::{extract_array, extract_text_string};
use crate::ctap::data_formats::{CredentialProtectionPolicy, PublicKeyCredentialSource}; use crate::ctap::data_formats::{CredentialProtectionPolicy, PublicKeyCredentialSource};
use crate::ctap::key_material;
use crate::ctap::pin_protocol_v1::PIN_AUTH_LENGTH; use crate::ctap::pin_protocol_v1::PIN_AUTH_LENGTH;
use crate::ctap::status_code::Ctap2StatusCode; use crate::ctap::status_code::Ctap2StatusCode;
use crate::ctap::{key_material, USE_BATCH_ATTESTATION};
use crate::embedded_flash::{self, StoreConfig, StoreEntry, StoreError}; use crate::embedded_flash::{self, StoreConfig, StoreEntry, StoreError};
use alloc::string::String; use alloc::string::String;
#[cfg(any(test, feature = "ram_storage", feature = "with_ctap2_1"))] #[cfg(any(test, feature = "ram_storage", feature = "with_ctap2_1"))]
@@ -76,8 +76,6 @@ const MIN_PIN_LENGTH_RP_IDS: usize = 9;
const NUM_TAGS: usize = 10; const NUM_TAGS: usize = 10;
const MAX_PIN_RETRIES: u8 = 8; const MAX_PIN_RETRIES: u8 = 8;
const ATTESTATION_PRIVATE_KEY_LENGTH: usize = 32;
const AAGUID_LENGTH: usize = 16;
#[cfg(feature = "with_ctap2_1")] #[cfg(feature = "with_ctap2_1")]
const DEFAULT_MIN_PIN_LENGTH: u8 = 4; const DEFAULT_MIN_PIN_LENGTH: u8 = 4;
// TODO(kaczmarczyck) use this for the minPinLength extension // TODO(kaczmarczyck) use this for the minPinLength extension
@@ -231,8 +229,19 @@ impl PersistentStore {
}) })
.unwrap(); .unwrap();
} }
// The following 3 entries are meant to be written by vendor-specific commands. // TODO(jmichel): remove this when vendor command is in place
if USE_BATCH_ATTESTATION { #[cfg(not(test))]
self.load_attestation_data_from_firmware();
if self.store.find_one(&Key::Aaguid).is_none() {
self.set_aaguid(key_material::AAGUID).unwrap();
}
}
// TODO(jmichel): remove this function when vendor command is in place.
#[cfg(not(test))]
fn load_attestation_data_from_firmware(&mut self) {
// The following 2 entries are meant to be written by vendor-specific commands.
if self.store.find_one(&Key::AttestationPrivateKey).is_none() { if self.store.find_one(&Key::AttestationPrivateKey).is_none() {
self.set_attestation_private_key(key_material::ATTESTATION_PRIVATE_KEY) self.set_attestation_private_key(key_material::ATTESTATION_PRIVATE_KEY)
.unwrap(); .unwrap();
@@ -242,10 +251,6 @@ impl PersistentStore {
.unwrap(); .unwrap();
} }
} }
if self.store.find_one(&Key::Aaguid).is_none() {
self.set_aaguid(key_material::AAGUID).unwrap();
}
}
pub fn find_credential( pub fn find_credential(
&self, &self,
@@ -545,29 +550,33 @@ impl PersistentStore {
pub fn attestation_private_key( pub fn attestation_private_key(
&self, &self,
) -> Result<Option<&[u8; ATTESTATION_PRIVATE_KEY_LENGTH]>, Ctap2StatusCode> { ) -> Result<Option<&[u8; key_material::ATTESTATION_PRIVATE_KEY_LENGTH]>, Ctap2StatusCode> {
let data = match self.store.find_one(&Key::AttestationPrivateKey) { let data = match self.store.find_one(&Key::AttestationPrivateKey) {
None => return Ok(None), None => return Ok(None),
Some((_, entry)) => entry.data, Some((_, entry)) => entry.data,
}; };
if data.len() != ATTESTATION_PRIVATE_KEY_LENGTH { if data.len() != key_material::ATTESTATION_PRIVATE_KEY_LENGTH {
return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR); return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR);
} }
Ok(Some(array_ref!(data, 0, ATTESTATION_PRIVATE_KEY_LENGTH))) Ok(Some(array_ref!(
data,
0,
key_material::ATTESTATION_PRIVATE_KEY_LENGTH
)))
} }
pub fn set_attestation_private_key( pub fn set_attestation_private_key(
&mut self, &mut self,
attestation_private_key: &[u8; ATTESTATION_PRIVATE_KEY_LENGTH], attestation_private_key: &[u8; key_material::ATTESTATION_PRIVATE_KEY_LENGTH],
) -> Result<(), Ctap2StatusCode> { ) -> Result<(), Ctap2StatusCode> {
let entry = StoreEntry { let entry = StoreEntry {
tag: ATTESTATION_PRIVATE_KEY, tag: ATTESTATION_PRIVATE_KEY,
data: attestation_private_key, data: attestation_private_key,
sensitive: false, sensitive: true,
}; };
match self.store.find_one(&Key::AttestationPrivateKey) { match self.store.find_one(&Key::AttestationPrivateKey) {
None => self.store.insert(entry)?, None => self.store.insert(entry)?,
Some((index, _)) => self.store.replace(index, entry)?, _ => return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR),
} }
Ok(()) Ok(())
} }
@@ -591,24 +600,27 @@ impl PersistentStore {
}; };
match self.store.find_one(&Key::AttestationCertificate) { match self.store.find_one(&Key::AttestationCertificate) {
None => self.store.insert(entry)?, None => self.store.insert(entry)?,
Some((index, _)) => self.store.replace(index, entry)?, _ => return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR),
} }
Ok(()) Ok(())
} }
pub fn aaguid(&self) -> Result<[u8; AAGUID_LENGTH], Ctap2StatusCode> { pub fn aaguid(&self) -> Result<[u8; key_material::AAGUID_LENGTH], Ctap2StatusCode> {
let (_, entry) = self let (_, entry) = self
.store .store
.find_one(&Key::Aaguid) .find_one(&Key::Aaguid)
.ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?; .ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?;
let data = entry.data; let data = entry.data;
if data.len() != AAGUID_LENGTH { if data.len() != key_material::AAGUID_LENGTH {
return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR); return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR);
} }
Ok(*array_ref![data, 0, AAGUID_LENGTH]) Ok(*array_ref![data, 0, key_material::AAGUID_LENGTH])
} }
pub fn set_aaguid(&mut self, aaguid: &[u8; AAGUID_LENGTH]) -> Result<(), Ctap2StatusCode> { pub fn set_aaguid(
&mut self,
aaguid: &[u8; key_material::AAGUID_LENGTH],
) -> Result<(), Ctap2StatusCode> {
let entry = StoreEntry { let entry = StoreEntry {
tag: AAGUID, tag: AAGUID,
data: aaguid, data: aaguid,