Hackety-hack: implement TOTP-over-CTAP. Also downsize littlefs to "fix" micro-ecc by optimizing less

This commit is contained in:
Nicolas Stalder
2020-05-26 03:25:54 +02:00
parent 0018be868c
commit f4f921614b
6 changed files with 173 additions and 6 deletions
+7 -3
View File
@@ -24,11 +24,12 @@ des = { version = "0.3.0", optional = true }
embedded-hal = { version = "0.2.3", features = ["unproven"] }
generic-array = "0.12.3" # "0.13.2"
# generic-array = { version = "0.13.2", default-features = false }
heapless = "0.5.5"
heapless = { version = "0.5.5", features = ["ufmt"] }
hmac = "0.7.1"
serde = { version = "1.0", default-features = false }
serde_cbor = { version = "0.11.0", default-features = false }
serde-indexed = "0.0.4"
sha-1 = { version = "0.8.2", default-features = false, optional = true }
sha2 = { version = "0.8.0", default-features = false }
ufmt = "0.1.0"
@@ -79,6 +80,7 @@ default-mechanisms = [
"p256",
"sha256",
"tdes",
"totp",
"trng",
]
aes256-cbc = []
@@ -88,9 +90,11 @@ hmac-sha256 = []
p256 = []
sha256 = []
tdes = ["des"]
trng = []
totp = ["sha-1"]
trng = ["sha-1"]
[patch.crates-io]
heapless = { git = "https://github.com/nickray/heapless", branch = "nickray-udebug" }
# heapless = { git = "https://github.com/nicolas-solokeys/heapless", branch = "bytebuf" }
heapless = { path = "../../../heapless" }
ufmt = { git = "https://github.com/nickray/ufmt", branch = "nickray-derive-empty-enums" }
ufmt-macros = { git = "https://github.com/nickray/ufmt", branch = "nickray-derive-empty-enums" }
+21
View File
@@ -521,6 +521,19 @@ impl<Syscall: crate::pipe::Syscall> Client<Syscall> {
self.encrypt(Mechanism::Tdes, key.clone(), message, &[], None)
}
pub fn unsafe_inject_totp_key<'c>(&'c mut self, raw_key: &[u8; 20], persistence: StorageLocation)
-> core::result::Result<FutureResult<'c, reply::UnsafeInjectKey>, ClientError>
{
cortex_m_semihosting::hprintln!("{}B: raw key: {:X?}", raw_key.len(), raw_key).ok();
self.raw.request(request::UnsafeInjectKey {
mechanism: Mechanism::Totp,
raw_key: ShortData::from_slice(raw_key).unwrap(),
attributes: StorageAttributes::new().set_persistence(persistence),
})?;
self.syscall.syscall();
Ok(FutureResult::new(self))
}
pub fn unsafe_inject_tdes_key<'c>(&'c mut self, raw_key: &[u8; 24], persistence: StorageLocation)
-> core::result::Result<FutureResult<'c, reply::UnsafeInjectKey>, ClientError>
{
@@ -593,6 +606,14 @@ impl<Syscall: crate::pipe::Syscall> Client<Syscall> {
self.sign(Mechanism::P256, key.clone(), message, format)
}
pub fn sign_totp<'c>(&'c mut self, key: &ObjectHandle, timestamp: u64)
-> core::result::Result<FutureResult<'c, reply::Sign>, ClientError>
{
self.sign(Mechanism::Totp, key.clone(),
&timestamp.to_le_bytes().as_ref(),
SignatureSerialization::Raw,
)
}
// - mechanism: Mechanism
// - wrapping_key: ObjectHandle
+6 -3
View File
@@ -5,9 +5,6 @@ pub trait MechanismTrait {}
pub struct Aes256Cbc {}
mod aes256cbc;
pub struct Tdes {}
mod tdes;
pub struct Chacha8Poly1305 {}
mod chacha8poly1305;
@@ -24,6 +21,12 @@ mod p256;
pub struct Sha256 {}
mod sha256;
pub struct Totp {}
mod totp;
pub struct Tdes {}
mod tdes;
pub struct Trng {}
mod trng;
+134
View File
@@ -0,0 +1,134 @@
use core::convert::TryInto;
use cortex_m_semihosting::hprintln;
use crate::api::*;
use crate::error::Error;
use crate::service::*;
use crate::store::Store;
use crate::types::*;
// code copied from https://github.com/avacariu/rust-oath
const DIGITS: u32 = 6;
// https://tools.ietf.org/html/rfc4226#section-5.3
fn hotp_raw(key: &[u8], counter: u64, digits: u32) -> u64 {
hmac_and_truncate(key, &counter.to_be_bytes(), digits)
}
fn hmac_and_truncate(key: &[u8], message: &[u8], digits: u32) -> u64 {
use hmac::{Hmac, Mac};
// let mut hmac = Hmac::<D>::new(GenericArray::from_slice(key));
hprintln!("1").ok();
let mut hmac = Hmac::<sha1::Sha1>::new_varkey(key).unwrap();
hprintln!("2").ok();
hmac.input(message);
hprintln!("3").ok();
let result = hmac.result();
hprintln!("4").ok();
// output of `.code()` is GenericArray<u8, OutputSize>, again 20B
// crypto-mac docs warn: "Be very careful using this method,
// since incorrect use of the code value may permit timing attacks
// which defeat the security provided by the Mac trait."
let hs = result.code();
hprintln!("5").ok();
dynamic_truncation(&hs) % 10_u64.pow(digits)
}
#[inline]
fn dynamic_truncation(hs: &[u8]) -> u64 {
// low-order bits of byte 19 (last) of the 20B output
let offset_bits = (*hs.last().unwrap() & 0xf) as usize;
let p = u32::from_be_bytes(hs[offset_bits..][..4].try_into().unwrap()) as u64;
// zero highest bit, avoids signed/unsigned "ambiguity"
p & 0x7fff_ffff
}
#[cfg(feature = "totp")]
impl<R: RngRead, S: Store>
UnsafeInjectKey<R, S> for super::Totp
{
fn unsafe_inject_key(resources: &mut ServiceResources<R, S>, request: request::UnsafeInjectKey)
-> Result<reply::UnsafeInjectKey, Error>
{
// in usual format, secret is a 32B Base32 encoding of 20B actual secret bytes
hprintln!("a").ok();
if request.raw_key.len() != 20 {
hprintln!("{}B: {:X?}", request.raw_key.len(), &request.raw_key).ok();
return Err(Error::WrongMessageLength);
}
hprintln!("b").ok();
// store it
let key_id = resources.store_key(
request.attributes.persistence,
KeyType::Secret,
KeyKind::Symmetric20,
&request.raw_key,
)?;
Ok(reply::UnsafeInjectKey { key: ObjectHandle { object_id: key_id } })
}
}
#[cfg(feature = "totp")]
impl<R: RngRead, S: Store>
Sign<R, S> for super::Totp
{
fn sign(resources: &mut ServiceResources<R, S>, request: request::Sign)
-> Result<reply::Sign, Error>
{
let key_id = request.key.object_id;
let secret: [u8; 20] = resources
.load_key(KeyType::Secret, None, &key_id)?
.value.as_slice().try_into()
.map_err(|_| Error::InternalError)?;
if request.message.len() != 8 {
return Err(Error::InternalError);
}
let timestamp_as_le_bytes = request.message[..].try_into().unwrap();
let timestamp = u64::from_le_bytes(timestamp_as_le_bytes);
let totp_value: u64 = hotp_raw(&secret, timestamp, DIGITS);
// return signature (encode as LE)
Ok(reply::Sign { signature: totp_value.to_le_bytes().as_ref().try_into().unwrap() })
}
}
#[cfg(feature = "totp")]
impl<R: RngRead, S: Store>
Exists<R, S> for super::Totp
{
fn exists(resources: &mut ServiceResources<R, S>, request: request::Exists)
-> Result<reply::Exists, Error>
{
let key_id = request.key.object_id;
let exists = resources.exists_key(KeyType::Secret, Some(KeyKind::Symmetric20), &key_id);
Ok(reply::Exists { exists })
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hotp() {
assert_eq!(hotp_raw(b"\xff", 23, 6), 330795);
// test values from RFC 4226
assert_eq!(hotp_raw(b"12345678901234567890", 0, 6), 755224);
assert_eq!(hotp_raw(b"12345678901234567890", 1, 6), 287082);
assert_ne!(hotp_raw(b"12345678901234567890", 1, 6), 287081);
}
}
+3
View File
@@ -205,6 +205,7 @@ impl<R: RngRead, S: Store> ServiceResources<R, S> {
Mechanism::Ed25519 => mechanisms::Ed25519::exists(self, request),
Mechanism::P256 => mechanisms::P256::exists(self, request),
Mechanism::Totp => mechanisms::Totp::exists(self, request),
_ => Err(Error::MechanismNotAvailable),
}.map(|reply| Reply::Exists(reply))
@@ -223,6 +224,7 @@ impl<R: RngRead, S: Store> ServiceResources<R, S> {
Request::UnsafeInjectKey(request) => {
match request.mechanism {
Mechanism::Tdes => mechanisms::Tdes::unsafe_inject_key(self, request),
Mechanism::Totp => mechanisms::Totp::unsafe_inject_key(self, request),
_ => Err(Error::MechanismNotAvailable),
}.map(|reply| Reply::UnsafeInjectKey(reply))
},
@@ -668,6 +670,7 @@ impl<R: RngRead, S: Store> ServiceResources<R, S> {
Mechanism::HmacSha256 => mechanisms::HmacSha256::sign(self, request),
Mechanism::P256 => mechanisms::P256::sign(self, request),
Mechanism::P256Prehashed => mechanisms::P256Prehashed::sign(self, request),
Mechanism::Totp => mechanisms::Totp::sign(self, request),
_ => Err(Error::MechanismNotAvailable),
}.map(|reply| Reply::Sign(reply))
+2
View File
@@ -157,6 +157,7 @@ pub enum KeyKind {
SymmetricKey32 = 6, // or directly: SharedSecret32 —DeriveKey(HmacSha256)-> SymmetricKey32 —Encrypt(Aes256)-> ...
Symmetric32Nonce12 = 7,
Symmetric24 = 8,
Symmetric20 = 9,
// ThirtytwoByteBuf,
}
@@ -384,6 +385,7 @@ pub enum Mechanism {
// clients can also do hashing by themselves
Sha256,
Tdes,
Totp,
Trng,
X25519,
}