From 61a4fb97846ffcd0617468d321c13eea45868c4b Mon Sep 17 00:00:00 2001 From: Julien Cretin Date: Wed, 4 Mar 2020 18:50:24 +0100 Subject: [PATCH 01/11] Wipe sensitive data on entry deletion When inserting (or replacing) entries in the store, the data may be marked as sensitive. When that entry is deleted, the data is wiped by overwritting it with zeroes. This may cost a few bytes of overhead per entry with sensitive data to satisfy the constraint that words may only be written twice. --- src/ctap/storage.rs | 14 ++- src/embedded_flash/store/bitfield.rs | 5 + src/embedded_flash/store/format.rs | 91 ++++++++++---- src/embedded_flash/store/mod.rs | 172 +++++++++++++++++++++++---- 4 files changed, 233 insertions(+), 49 deletions(-) diff --git a/src/ctap/storage.rs b/src/ctap/storage.rs index 7569c1b..565b0b9 100644 --- a/src/ctap/storage.rs +++ b/src/ctap/storage.rs @@ -198,6 +198,7 @@ impl PersistentStore { .insert(StoreEntry { tag: MASTER_KEYS, data: &master_keys, + sensitive: true, }) .unwrap(); } @@ -206,6 +207,7 @@ impl PersistentStore { .insert(StoreEntry { tag: PIN_RETRIES, data: &[MAX_PIN_RETRIES], + sensitive: false, }) .unwrap(); } @@ -245,6 +247,7 @@ impl PersistentStore { let new_entry = StoreEntry { tag: TAG_CREDENTIAL, data: &credential, + sensitive: true, }; match old_entry { None => self.store.insert(new_entry)?, @@ -299,6 +302,7 @@ impl PersistentStore { .insert(StoreEntry { tag: GLOBAL_SIGNATURE_COUNTER, data: &buffer, + sensitive: false, }) .unwrap(); } @@ -312,6 +316,7 @@ impl PersistentStore { StoreEntry { tag: GLOBAL_SIGNATURE_COUNTER, data: &buffer, + sensitive: false, }, ) .unwrap(); @@ -339,6 +344,7 @@ impl PersistentStore { let entry = StoreEntry { tag: PIN_HASH, data: pin_hash, + sensitive: true, }; match self.store.find_one(&Key::PinHash) { None => self.store.insert(entry).unwrap(), @@ -368,6 +374,7 @@ impl PersistentStore { StoreEntry { tag: PIN_RETRIES, data: &[new_value], + sensitive: false, }, ) .unwrap(); @@ -381,6 +388,7 @@ impl PersistentStore { StoreEntry { tag: PIN_RETRIES, data: &[MAX_PIN_RETRIES], + sensitive: false, }, ) .unwrap(); @@ -465,9 +473,9 @@ mod test { let storage = Storage::new(store, options); let store = embedded_flash::Store::new(storage, Config).unwrap(); // We can replace 3 bytes with minimal overhead. - assert_eq!(store.replace_len(0), 2 * WORD_SIZE); - assert_eq!(store.replace_len(3), 2 * WORD_SIZE); - assert_eq!(store.replace_len(4), 3 * WORD_SIZE); + assert_eq!(store.replace_len(false, 0), 2 * WORD_SIZE); + assert_eq!(store.replace_len(false, 3), 3 * WORD_SIZE); + assert_eq!(store.replace_len(false, 4), 3 * WORD_SIZE); } #[test] diff --git a/src/embedded_flash/store/bitfield.rs b/src/embedded_flash/store/bitfield.rs index 60c6f86..797c78b 100644 --- a/src/embedded_flash/store/bitfield.rs +++ b/src/embedded_flash/store/bitfield.rs @@ -55,6 +55,11 @@ impl ByteGap { bit + 8 * self.length } } + + /// Returns the slice of `data` corresponding to the gap. + pub fn slice(self, data: &[u8]) -> &[u8] { + &data[self.start..self.start + self.length] + } } /// Returns whether a bit is set in a sequence of bits. diff --git a/src/embedded_flash/store/format.rs b/src/embedded_flash/store/format.rs index cbef61f..8308c4a 100644 --- a/src/embedded_flash/store/format.rs +++ b/src/embedded_flash/store/format.rs @@ -59,6 +59,14 @@ pub struct Format { /// - 1 for insert entries. replace_bit: usize, + /// Whether a user entry has sensitive data. + /// + /// When a user entry with sensitive data is deleted, the data is overwritten with zeroes. + /// + /// - 0 for sensitive data. + /// - 1 for non-sensitive data. + sensitive_bit: usize, + /// The data length of a user entry. length_range: bitfield::BitRange, @@ -138,8 +146,9 @@ impl Format { let deleted_bit = present_bit + 1; let internal_bit = deleted_bit + 1; let replace_bit = internal_bit + 1; + let sensitive_bit = replace_bit + 1; let length_range = bitfield::BitRange { - start: replace_bit + 1, + start: sensitive_bit + 1, length: byte_bits, }; let tag_range = bitfield::BitRange { @@ -182,6 +191,7 @@ impl Format { deleted_bit, internal_bit, replace_bit, + sensitive_bit, length_range, tag_range, replace_page_range, @@ -196,10 +206,11 @@ impl Format { // Make sure all the following conditions hold: // - The page header is one word. // - The internal entry is one word. - // - The entry header fits in one word. + // - The entry header fits in one word (which is equivalent to the entry header size being + // exactly one word for sensitive entries). if format.page_header_size() != word_size || format.internal_entry_size() != word_size - || format.header_size() > word_size + || format.header_size(true) != word_size { return None; } @@ -220,28 +231,46 @@ impl Format { /// Returns the entry header length in bytes. /// /// This is the smallest number of bytes necessary to store all fields of the entry info up to - /// and including `length`. - pub fn header_size(&self) -> usize { - self.bits_to_bytes(self.length_range.end()) + /// and including `length`. For sensitive entries, the result is word-aligned. + pub fn header_size(&self, sensitive: bool) -> usize { + let mut size = self.bits_to_bytes(self.length_range.end()); + if sensitive { + // We need to align to the next word boundary so that wiping the user data will not + // count as a write to the header. + size = self.align_word(size); + } + size + } + + /// Returns the entry header length in bytes. + /// + /// This is a convenience function for `header_size` above. + fn header_offset(&self, entry: &[u8]) -> usize { + self.header_size(self.is_sensitive(entry)) } /// Returns the entry info length in bytes. /// /// This is the number of bytes necessary to store all fields of the entry info. This also - /// includes the internal padding to protect the `committed` bit from the `deleted` bit. - fn info_size(&self, is_replace: IsReplace) -> usize { + /// includes the internal padding to protect the `committed` bit from the `deleted` bit and to + /// protect the entry info from the user data for sensitive entries. + fn info_size(&self, is_replace: IsReplace, sensitive: bool) -> usize { let suffix_bits = 2; // committed + complete let info_bits = match is_replace { IsReplace::Replace => self.replace_byte_range.end() + suffix_bits, IsReplace::Insert => self.tag_range.end() + suffix_bits, }; - let info_size = self.bits_to_bytes(info_bits); + let mut info_size = self.bits_to_bytes(info_bits); // If the suffix bits would end up in the header, we need to add one byte for them. - if info_size == self.header_size() { - info_size + 1 - } else { - info_size + let header_size = self.header_size(sensitive); + if info_size <= header_size { + info_size = header_size + 1; } + // If the entry is sensitive, we need to align to the next word boundary. + if sensitive { + info_size = self.align_word(info_size); + } + info_size } /// Returns the length in bytes of an entry. @@ -249,8 +278,8 @@ impl Format { /// This depends on the length of the user data and whether the entry replaces an old entry or /// is an insertion. This also includes the internal padding to protect the `committed` bit from /// the `deleted` bit. - pub fn entry_size(&self, is_replace: IsReplace, length: usize) -> usize { - let mut entry_size = length + self.info_size(is_replace); + pub fn entry_size(&self, is_replace: IsReplace, sensitive: bool, length: usize) -> usize { + let mut entry_size = length + self.info_size(is_replace, sensitive); let word_size = self.word_size; entry_size = self.align_word(entry_size); // The entry must be at least 2 words such that the `committed` and `deleted` bits are on @@ -308,6 +337,14 @@ impl Format { bitfield::set_zero(self.replace_bit, header, bitfield::NO_GAP) } + pub fn is_sensitive(&self, header: &[u8]) -> bool { + bitfield::is_zero(self.sensitive_bit, header, bitfield::NO_GAP) + } + + pub fn set_sensitive(&self, header: &mut [u8]) { + bitfield::set_zero(self.sensitive_bit, header, bitfield::NO_GAP) + } + pub fn get_length(&self, header: &[u8]) -> usize { bitfield::get_range(self.length_range, header, bitfield::NO_GAP) } @@ -317,16 +354,19 @@ impl Format { } pub fn get_data<'a>(&self, entry: &'a [u8]) -> &'a [u8] { - &entry[self.header_size()..][..self.get_length(entry)] + &entry[self.header_offset(entry)..][..self.get_length(entry)] } /// Returns the span of user data in an entry. /// /// The complement of this gap in the entry is exactly the entry info. The header is before the /// gap and the footer is after the gap. - fn entry_gap(&self, entry: &[u8]) -> bitfield::ByteGap { - let start = self.header_size(); - let length = self.get_length(entry); + pub fn entry_gap(&self, entry: &[u8]) -> bitfield::ByteGap { + let start = self.header_offset(entry); + let mut length = self.get_length(entry); + if self.is_sensitive(entry) { + length = self.align_word(length); + } bitfield::ByteGap { start, length } } @@ -406,16 +446,23 @@ impl Format { /// Builds an entry for replace or insert operations. pub fn build_entry(&self, replace: Option, user_entry: StoreEntry) -> Vec { - let StoreEntry { tag, data } = user_entry; + let StoreEntry { + tag, + data, + sensitive, + } = user_entry; let is_replace = match replace { None => IsReplace::Insert, Some(_) => IsReplace::Replace, }; - let entry_len = self.entry_size(is_replace, data.len()); + let entry_len = self.entry_size(is_replace, sensitive, data.len()); let mut entry = Vec::with_capacity(entry_len); // Build the header. - entry.resize(self.header_size(), 0xff); + entry.resize(self.header_size(sensitive), 0xff); self.set_present(&mut entry[..]); + if sensitive { + self.set_sensitive(&mut entry[..]); + } self.set_length(&mut entry[..], data.len()); // Add the data. entry.extend_from_slice(data); diff --git a/src/embedded_flash/store/mod.rs b/src/embedded_flash/store/mod.rs index 0431073..4111068 100644 --- a/src/embedded_flash/store/mod.rs +++ b/src/embedded_flash/store/mod.rs @@ -57,7 +57,7 @@ //! new_page:page_bits //! Padding(word) //! Entry := Header Data Footer -//! // Let X be the byte following `length` in `Info`. +//! // Let X be the byte (word-aligned for sensitive queries) following `length` in `Info`. //! Header := Info[..X] // must fit in one word //! Footer := Info[X..] // must fit in one word //! Info := @@ -65,6 +65,7 @@ //! deleted:1 //! internal=1 //! replace:1 +//! sensitive:1 //! length:byte_bits //! tag:tag_bits //! [ // present if `replace` is 0 @@ -109,15 +110,16 @@ //! 0.1 deleted //! 0.2 internal //! 0.3 replace -//! 0.4 length (9 bits) -//! 1.5 tag (least significant 3 bits out of 5) +//! 0.4 sensitive +//! 0.5 length (9 bits) +//! 1.6 tag (least significant 2 bits out of 5) //! (the header ends at the first byte boundary after `length`) //! 2.0 (2 bytes in this example) //! (the footer starts immediately after the user data) -//! 4.0 tag (most significant 2 bits out of 5) -//! 4.2 replace_page (6 bits) -//! 5.0 replace_byte (9 bits) -//! 6.1 padding (make sure the 2 properties below hold) +//! 4.0 tag (most significant 3 bits out of 5) +//! 4.3 replace_page (6 bits) +//! 5.1 replace_byte (9 bits) +//! 6.2 padding (make sure the 2 properties below hold) //! 7.6 committed //! 7.7 complete (on a different word than `present`) //! 8.0 (word-aligned) @@ -203,6 +205,11 @@ pub struct StoreEntry<'a> { /// The data of the entry. pub data: &'a [u8], + + /// Whether the data is sensitive. + /// + /// Sensitive data is overwritten with zeroes when the entry is deleted. + pub sensitive: bool, } /// Implements a configurable multi-set on top of any storage. @@ -262,6 +269,7 @@ impl Store { StoreEntry { tag: self.format.get_tag(entry), data: self.format.get_data(entry), + sensitive: self.format.is_sensitive(entry), }, )) } else { @@ -326,7 +334,7 @@ impl Store { self.format.validate_entry(new)?; let mut old_index = old.index; // Find a slot. - let entry_len = self.replace_len(new.data.len()); + let entry_len = self.replace_len(new.sensitive, new.data.len()); let index = self.find_slot_for_write(entry_len, Some(&mut old_index))?; // Build a new entry replacing the old one. let entry = self.format.build_entry(Some(old_index), new); @@ -360,17 +368,20 @@ impl Store { /// Returns the byte cost of a replace operation. /// /// Computes the length in bytes that would be used in the storage if a replace operation is - /// executed provided the data of the new entry has `length` bytes. - pub fn replace_len(&self, length: usize) -> usize { - self.format.entry_size(IsReplace::Replace, length) + /// executed provided the data of the new entry has `length` bytes and whether this data is + /// sensitive. + pub fn replace_len(&self, sensitive: bool, length: usize) -> usize { + self.format + .entry_size(IsReplace::Replace, sensitive, length) } /// Returns the byte cost of an insert operation. /// /// Computes the length in bytes that would be used in the storage if an insert operation is - /// executed provided the data of the inserted entry has `length` bytes. - pub fn insert_len(&self, length: usize) -> usize { - self.format.entry_size(IsReplace::Insert, length) + /// executed provided the data of the inserted entry has `length` bytes and whether this data is + /// sensitive. + pub fn insert_len(&self, sensitive: bool, length: usize) -> usize { + self.format.entry_size(IsReplace::Insert, sensitive, length) } /// Returns the erase count of all pages. @@ -410,8 +421,11 @@ impl Store { let entry_index = index; let entry = self.read_entry(index); index.byte += entry.len(); - if !self.format.is_alive(entry) { - // Skip deleted entries (or the page padding). + if !self.format.is_present(entry) { + // Reached the end of the page. + } else if self.format.is_deleted(entry) { + // Wipe sensitive data if needed. + self.wipe_sensitive_data(entry_index); } else if self.format.is_internal(entry) { // Finish page compaction. self.erase_page(entry_index); @@ -449,6 +463,31 @@ impl Store { /// The provided index must point to the beginning of an entry. fn delete_index(&mut self, index: Index) { self.update_word(index, |format, word| format.set_deleted(word)); + self.wipe_sensitive_data(index); + } + + /// Wipes the data of a sensitive entry. + /// + /// If the entry at the provided index is sensitive, overwrites the data with zeroes. Otherwise, + /// does nothing. + fn wipe_sensitive_data(&mut self, mut index: Index) { + let entry = self.read_entry(index); + debug_assert!(self.format.is_present(entry)); + debug_assert!(self.format.is_deleted(entry)); + if self.format.is_internal(entry) || !self.format.is_sensitive(entry) { + // No need to wipe the data. + return; + } + let gap = self.format.entry_gap(entry); + let data = gap.slice(entry); + if data.iter().all(|&byte| byte == 0x00) { + // The data is already wiped. + return; + } + index.byte += gap.start; + self.storage + .write_slice(index, &vec![0; gap.length]) + .unwrap(); } /// Finds a page with enough free space. @@ -555,10 +594,13 @@ impl Store { } else if self.format.is_internal(first_byte) { self.format.internal_entry_size() } else { - let header = self.read_slice(index, self.format.header_size()); + // We don't know if the entry is sensitive or not, but it doesn't matter here. We just + // need to read the replace, sensitive, and length fields. + let header = self.read_slice(index, self.format.header_size(false)); let replace = self.format.is_replace(header); + let sensitive = self.format.is_sensitive(header); let length = self.format.get_length(header); - self.format.entry_size(replace, length) + self.format.entry_size(replace, sensitive, length) }; // Truncate the length to fit the page. This can only happen in case of corruption or // partial writes. @@ -673,7 +715,7 @@ impl Store { // Save the old page index and erase count to the new page. let erase_index = new_index; let erase_entry = self.format.build_erase_entry(old_page, erase_count); - self.storage.write_slice(new_index, &erase_entry).unwrap(); + self.write_entry(new_index, &erase_entry); // Erase the page. self.erase_page(erase_index); // Increase generation. @@ -728,6 +770,22 @@ impl Store { pub fn set_erase_count(&mut self, page: usize, erase_count: usize) { self.initialize_page(page, erase_count); } + + /// Checks whether all deleted sensitive entries have been wiped. + pub fn check_wiped(&self) { + for (_, entry) in Iter::new(self) { + if !self.format.is_present(entry) + || !self.format.is_deleted(entry) + || self.format.is_internal(entry) + || !self.format.is_sensitive(entry) + { + continue; + } + let gap = self.format.entry_gap(entry); + let data = gap.slice(entry); + assert!(data.iter().all(|&byte| byte == 0x00)); + } + } } /// Maps an index from an old page to a new page if needed. @@ -843,7 +901,27 @@ mod tests { let tag = 0; let key = 1; let data = &[key, 2]; - let entry = StoreEntry { tag, data }; + let entry = StoreEntry { + tag, + data, + sensitive: false, + }; + store.insert(entry).unwrap(); + assert_eq!(store.iter().count(), 1); + assert_eq!(store.find_one(&key).unwrap().1, entry); + } + + #[test] + fn insert_sensitive_ok() { + let mut store = new_store(); + let tag = 0; + let key = 1; + let data = &[key, 4]; + let entry = StoreEntry { + tag, + data, + sensitive: true, + }; store.insert(entry).unwrap(); assert_eq!(store.iter().count(), 1); assert_eq!(store.find_one(&key).unwrap().1, entry); @@ -857,6 +935,7 @@ mod tests { let entry = StoreEntry { tag, data: &[key, 2], + sensitive: false, }; store.insert(entry).unwrap(); assert_eq!(store.find_all(&key).count(), 1); @@ -866,6 +945,25 @@ mod tests { assert_eq!(store.iter().count(), 0); } + #[test] + fn delete_sensitive_ok() { + let mut store = new_store(); + let tag = 0; + let key = 1; + let entry = StoreEntry { + tag, + data: &[key, 2], + sensitive: true, + }; + store.insert(entry).unwrap(); + assert_eq!(store.find_all(&key).count(), 1); + let (index, _) = store.find_one(&key).unwrap(); + store.delete(index).unwrap(); + assert_eq!(store.find_all(&key).count(), 0); + assert_eq!(store.iter().count(), 0); + store.check_wiped(); + } + #[test] fn insert_until_full() { let mut store = new_store(); @@ -875,6 +973,7 @@ mod tests { .insert(StoreEntry { tag, data: &[key, 0], + sensitive: false, }) .is_ok() { @@ -892,6 +991,7 @@ mod tests { .insert(StoreEntry { tag, data: &[key, 0], + sensitive: false, }) .is_ok() { @@ -903,6 +1003,7 @@ mod tests { .insert(StoreEntry { tag: 0, data: &[key, 0], + sensitive: false, }) .unwrap(); for k in 1..=key { @@ -916,7 +1017,11 @@ mod tests { let tag = 0; let key = 1; let data = &[key, 2]; - let entry = StoreEntry { tag, data }; + let entry = StoreEntry { + tag, + data, + sensitive: false, + }; store.insert(entry).unwrap(); // Reboot the store. @@ -934,10 +1039,12 @@ mod tests { let old_entry = StoreEntry { tag, data: &[key, 2, 3, 4, 5, 6], + sensitive: false, }; let new_entry = StoreEntry { tag, data: &[key, 7, 8, 9], + sensitive: false, }; let mut delay = 0; loop { @@ -973,6 +1080,7 @@ mod tests { .insert(StoreEntry { tag, data: &[key, 0], + sensitive: false, }) .is_ok() { @@ -983,7 +1091,14 @@ mod tests { let (index, _) = store.find_one(&1).unwrap(); store.arm_snapshot(delay); store - .replace(index, StoreEntry { tag, data: &[1, 1] }) + .replace( + index, + StoreEntry { + tag, + data: &[1, 1], + sensitive: false, + }, + ) .unwrap(); let (complete, store) = match store.get_snapshot() { Err(_) => (true, store.get_storage()), @@ -995,7 +1110,11 @@ mod tests { assert_eq!(store.find_all(&k).count(), 1); assert_eq!( store.find_one(&k).unwrap().1, - StoreEntry { tag, data: &[k, 0] } + StoreEntry { + tag, + data: &[k, 0], + sensitive: false, + } ); } assert_eq!(store.find_all(&1).count(), 1); @@ -1012,7 +1131,11 @@ mod tests { #[test] fn invalid_tag() { let mut store = new_store(); - let entry = StoreEntry { tag: 1, data: &[] }; + let entry = StoreEntry { + tag: 1, + data: &[], + sensitive: false, + }; assert_eq!(store.insert(entry), Err(StoreError::InvalidTag)); } @@ -1022,6 +1145,7 @@ mod tests { let entry = StoreEntry { tag: 0, data: &[0; PAGE_SIZE], + sensitive: false, }; assert_eq!(store.insert(entry), Err(StoreError::StoreFull)); } From 8f4d4c1210e483d209233f67f2c57e9bda0ae2df Mon Sep 17 00:00:00 2001 From: Guillaume Endignoux Date: Fri, 13 Mar 2020 15:59:51 +0100 Subject: [PATCH 02/11] Add a --verbose parameter to print all USB packets at the OpenSK level. --- Cargo.toml | 9 ++++--- deploy.py | 26 ++++++++++++------- run_desktop_tests.sh | 3 ++- src/usb_ctap_hid.rs | 59 ++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 78 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3155106..1d1ad6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,12 +18,13 @@ arrayref = "0.3.6" subtle = { version = "2.2", default-features = false, features = ["nightly"] } [features] -std = ["cbor/std", "crypto/std", "crypto/derive_debug"] -debug_ctap = ["crypto/derive_debug"] -with_ctap1 = ["crypto/with_ctap1"] -panic_console = ["libtock/panic_console"] debug_allocations = ["libtock/debug_allocations"] +debug_ctap = ["crypto/derive_debug"] +panic_console = ["libtock/panic_console"] +std = ["cbor/std", "crypto/std", "crypto/derive_debug"] ram_storage = [] +verbose = ["debug_ctap"] +with_ctap1 = ["crypto/with_ctap1"] [dev-dependencies] elf2tab = "0.4.0" diff --git a/deploy.py b/deploy.py index 8b5ec81..814cb9f 100755 --- a/deploy.py +++ b/deploy.py @@ -701,6 +701,15 @@ if __name__ == "__main__": "output messages before starting blinking the LEDs on the " "board."), ) + main_parser.add_argument( + "--debug", + action="append_const", + const="debug_ctap", + dest="features", + help=("Compiles and installs the OpenSK application in debug mode " + "(i.e. more debug messages will be sent over the console port " + "such as hexdumps of packets)."), + ) main_parser.add_argument( "--debug-allocations", action="append_const", @@ -709,6 +718,14 @@ if __name__ == "__main__": help=("The console will be used to output allocator statistics every " "time an allocation/deallocation happens."), ) + main_parser.add_argument( + "--verbose", + action="append_const", + const="verbose", + dest="features", + help=("The console will be used to output verbose information about the " + "OpenSK application. This also automatically activates --debug."), + ) main_parser.add_argument( "--no-u2f", action=RemoveConstAction, @@ -727,15 +744,6 @@ if __name__ == "__main__": "This is useful to allow flashing multiple OpenSK authenticators " "in a row without them being considered clones."), ) - main_parser.add_argument( - "--debug", - action="append_const", - const="debug_ctap", - dest="features", - help=("Compiles and installs the OpenSK application in debug mode " - "(i.e. more debug messages will be sent over the console port " - "such as hexdumps of packets)."), - ) main_parser.add_argument( "--no-persistent-storage", action="append_const", diff --git a/run_desktop_tests.sh b/run_desktop_tests.sh index 864b8fe..e387353 100755 --- a/run_desktop_tests.sh +++ b/run_desktop_tests.sh @@ -34,8 +34,9 @@ cargo check --release --target=thumbv7em-none-eabi --features debug_ctap cargo check --release --target=thumbv7em-none-eabi --features panic_console cargo check --release --target=thumbv7em-none-eabi --features debug_allocations cargo check --release --target=thumbv7em-none-eabi --features ram_storage +cargo check --release --target=thumbv7em-none-eabi --features verbose cargo check --release --target=thumbv7em-none-eabi --features debug_ctap,with_ctap1 -cargo check --release --target=thumbv7em-none-eabi --features debug_ctap,with_ctap1,panic_console,debug_allocations +cargo check --release --target=thumbv7em-none-eabi --features debug_ctap,with_ctap1,panic_console,debug_allocations,verbose echo "Checking that examples build properly..." cargo check --release --target=thumbv7em-none-eabi --examples diff --git a/src/usb_ctap_hid.rs b/src/usb_ctap_hid.rs index 421e70b..7e63754 100644 --- a/src/usb_ctap_hid.rs +++ b/src/usb_ctap_hid.rs @@ -165,6 +165,57 @@ pub fn send_or_recv(buf: &mut [u8; 64]) -> SendOrRecvStatus { pub fn recv_with_timeout( buf: &mut [u8; 64], timeout_delay: Duration, +) -> Option { + #[cfg(feature = "verbose")] + writeln!( + Console::new(), + "Receiving packet with timeout of {}ms", + timeout_delay.ms(), + ) + .unwrap(); + + let result = recv_with_timeout_detail(buf, timeout_delay); + + #[cfg(feature = "verbose")] + { + if let Some(SendOrRecvStatus::Received) = result { + writeln!(Console::new(), "Received packet = {:02x?}", buf as &[u8]).unwrap(); + } + } + + result +} + +// Same as send_or_recv, but with a timeout. +// If the timeout elapses, return None. +pub fn send_or_recv_with_timeout( + buf: &mut [u8; 64], + timeout_delay: Duration, +) -> Option { + #[cfg(feature = "verbose")] + writeln!( + Console::new(), + "Sending packet with timeout of {}ms = {:02x?}", + timeout_delay.ms(), + buf as &[u8] + ) + .unwrap(); + + let result = send_or_recv_with_timeout_detail(buf, timeout_delay); + + #[cfg(feature = "verbose")] + { + if let Some(SendOrRecvStatus::Received) = result { + writeln!(Console::new(), "Received packet = {:02x?}", buf as &[u8]).unwrap(); + } + } + + result +} + +fn recv_with_timeout_detail( + buf: &mut [u8; 64], + timeout_delay: Duration, ) -> Option { let result = syscalls::allow(DRIVER_NUMBER, allow_nr::RECEIVE, buf); if result.is_err() { @@ -225,7 +276,7 @@ pub fn recv_with_timeout( // Cancel USB transaction if necessary. if status.get().is_none() { - #[cfg(feature = "debug_ctap")] + #[cfg(feature = "verbose")] writeln!(Console::new(), "Cancelling USB receive due to timeout").unwrap(); let result_code = unsafe { syscalls::command(DRIVER_NUMBER, command_nr::CANCEL, 0, 0) }; match result_code { @@ -249,9 +300,7 @@ pub fn recv_with_timeout( status.get() } -// Same as send_or_recv, but with a timeout. -// If the timeout elapses, return None. -pub fn send_or_recv_with_timeout( +fn send_or_recv_with_timeout_detail( buf: &mut [u8; 64], timeout_delay: Duration, ) -> Option { @@ -317,7 +366,7 @@ pub fn send_or_recv_with_timeout( // Cancel USB transaction if necessary. if status.get().is_none() { - #[cfg(feature = "debug_ctap")] + #[cfg(feature = "verbose")] writeln!(Console::new(), "Cancelling USB transaction due to timeout").unwrap(); let result_code = unsafe { syscalls::command(DRIVER_NUMBER, command_nr::CANCEL, 0, 0) }; match result_code { From 4d5ef342dea93d5ee5d0296781811f2c8a301277 Mon Sep 17 00:00:00 2001 From: Guillaume Endignoux Date: Fri, 13 Mar 2020 16:03:31 +0100 Subject: [PATCH 03/11] Update workflows. --- .github/workflows/cargo_check.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cargo_check.yml b/.github/workflows/cargo_check.yml index a6c4c46..c54fc9e 100644 --- a/.github/workflows/cargo_check.yml +++ b/.github/workflows/cargo_check.yml @@ -64,17 +64,23 @@ jobs: command: check args: --target thumbv7em-none-eabi --release --features ram_storage + - name: Check OpenSK verbose + uses: actions-rs/cargo@v1 + with: + command: check + args: --target thumbv7em-none-eabi --release --features verbose + - name: Check OpenSK debug_ctap,with_ctap1 uses: actions-rs/cargo@v1 with: command: check args: --target thumbv7em-none-eabi --release --features debug_ctap,with_ctap1 - - name: Check OpenSK debug_ctap,with_ctap1,panic_console,debug_allocations + - name: Check OpenSK debug_ctap,with_ctap1,panic_console,debug_allocations,verbose uses: actions-rs/cargo@v1 with: command: check - args: --target thumbv7em-none-eabi --release --features debug_ctap,with_ctap1,panic_console,debug_allocations + args: --target thumbv7em-none-eabi --release --features debug_ctap,with_ctap1,panic_console,debug_allocations,verbose - name: Check examples uses: actions-rs/cargo@v1 From b798b195f4ae5980b863e2257bd24197dd2b0b91 Mon Sep 17 00:00:00 2001 From: Julien Cretin Date: Wed, 18 Mar 2020 11:10:01 +0100 Subject: [PATCH 04/11] Return invariant status only and assert in test --- src/embedded_flash/store/mod.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/embedded_flash/store/mod.rs b/src/embedded_flash/store/mod.rs index 4111068..e2ed2fa 100644 --- a/src/embedded_flash/store/mod.rs +++ b/src/embedded_flash/store/mod.rs @@ -771,8 +771,8 @@ impl Store { self.initialize_page(page, erase_count); } - /// Checks whether all deleted sensitive entries have been wiped. - pub fn check_wiped(&self) { + /// Returns whether all deleted sensitive entries have been wiped. + pub fn deleted_entries_are_wiped(&self) -> bool { for (_, entry) in Iter::new(self) { if !self.format.is_present(entry) || !self.format.is_deleted(entry) @@ -783,8 +783,11 @@ impl Store { } let gap = self.format.entry_gap(entry); let data = gap.slice(entry); - assert!(data.iter().all(|&byte| byte == 0x00)); + if !data.iter().all(|&byte| byte == 0x00) { + return false; + } } + true } } @@ -961,7 +964,7 @@ mod tests { store.delete(index).unwrap(); assert_eq!(store.find_all(&key).count(), 0); assert_eq!(store.iter().count(), 0); - store.check_wiped(); + assert!(store.deleted_entries_are_wiped()); } #[test] From 4a9cc3ce2e75f2564dfe7ac16052b7044a3322de Mon Sep 17 00:00:00 2001 From: Julien Cretin Date: Sun, 22 Mar 2020 17:17:21 +0100 Subject: [PATCH 05/11] Add more details to the store documentation --- src/embedded_flash/store/format.rs | 7 +++++-- src/embedded_flash/store/mod.rs | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/embedded_flash/store/format.rs b/src/embedded_flash/store/format.rs index 8308c4a..447506c 100644 --- a/src/embedded_flash/store/format.rs +++ b/src/embedded_flash/store/format.rs @@ -61,10 +61,13 @@ pub struct Format { /// Whether a user entry has sensitive data. /// - /// When a user entry with sensitive data is deleted, the data is overwritten with zeroes. - /// /// - 0 for sensitive data. /// - 1 for non-sensitive data. + /// + /// When a user entry with sensitive data is deleted, the data is overwritten with zeroes. This + /// feature is subject to the same guarantees as all other features of the store, in particular + /// deleting a sensitive entry is atomic. See the store module-level documentation for more + /// information. sensitive_bit: usize, /// The data length of a user entry. diff --git a/src/embedded_flash/store/mod.rs b/src/embedded_flash/store/mod.rs index e2ed2fa..9453372 100644 --- a/src/embedded_flash/store/mod.rs +++ b/src/embedded_flash/store/mod.rs @@ -43,6 +43,28 @@ //! The data-structure can be configured with the `StoreConfig` trait. By implementing this trait, //! the number of possible tags and the association between keys and entries are defined. //! +//! # Properties +//! +//! The data-structure provides the following properties: +//! - When an operation returns success, then the represented multi-set is updated accordingly. For +//! example, an inserted entry can be find with alteration until replaced or deleted. +//! - When an operation returns an error, the resulting multi-set state is described in the error +//! documentation. +//! - When power is lost before an operation returns, the operation will either succeed or be +//! rolled-back on the next initialization. So the multi-set would be either left unchanged or +//! updated accordingly. +//! +//! Those properties rely on the following assumptions: +//! - Writing a word to flash is atomic. When power is lost, the word is either fully written or not +//! written at all. +//! - Reading a word from flash is deterministic. When power is lost while writing or erasing a word +//! (erasing a page containing that word), reading that word repeatedly returns the same result +//! (until it is written or its page is erased). +//! - To decide whether a page has been erased, it is enough to test if all its bits are equal to 1. +//! +//! The properties still hold outside those assumptions but with weaker probabilities as the usage +//! diverges from the assumptions. +//! //! # Implementation //! //! The store is a page-aligned sequence of bits. It matches the following grammar: From 1a337ab90dcf13f37dcf3e2eb1551b3b9503702c Mon Sep 17 00:00:00 2001 From: Julien Cretin Date: Mon, 23 Mar 2020 12:56:46 +0100 Subject: [PATCH 06/11] Fix typos in documentation --- src/embedded_flash/store/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/embedded_flash/store/mod.rs b/src/embedded_flash/store/mod.rs index 9453372..c5cf838 100644 --- a/src/embedded_flash/store/mod.rs +++ b/src/embedded_flash/store/mod.rs @@ -47,7 +47,7 @@ //! //! The data-structure provides the following properties: //! - When an operation returns success, then the represented multi-set is updated accordingly. For -//! example, an inserted entry can be find with alteration until replaced or deleted. +//! example, an inserted entry can be found without alteration until replaced or deleted. //! - When an operation returns an error, the resulting multi-set state is described in the error //! documentation. //! - When power is lost before an operation returns, the operation will either succeed or be @@ -62,8 +62,8 @@ //! (until it is written or its page is erased). //! - To decide whether a page has been erased, it is enough to test if all its bits are equal to 1. //! -//! The properties still hold outside those assumptions but with weaker probabilities as the usage -//! diverges from the assumptions. +//! The properties may still hold outside those assumptions but with weaker probabilities as the +//! usage diverges from the assumptions. //! //! # Implementation //! From 4b9486d0bdbef2c47e0b9d513c1fffcb9f4c08e1 Mon Sep 17 00:00:00 2001 From: Guillaume Endignoux Date: Wed, 8 Apr 2020 15:09:18 +0200 Subject: [PATCH 07/11] Sync with upstream Tock to have reproducible builds. --- .github/workflows/boards_build.yml | 12 +++----- boards/nordic/nrf52840_dongle_dfu/Cargo.toml | 18 +++++++++++ .../{ => nordic}/nrf52840_dongle_dfu/Makefile | 6 ++-- boards/nordic/nrf52840_dongle_dfu/build.rs | 4 +++ .../nrf52840_dongle_dfu}/layout.ld | 2 +- boards/nordic/nrf52840_mdk_dfu/Cargo.toml | 14 +++++++++ boards/{ => nordic}/nrf52840_mdk_dfu/Makefile | 8 ++--- boards/nordic/nrf52840_mdk_dfu/build.rs | 4 +++ .../nrf52840_mdk_dfu}/layout.ld | 2 +- .../{ => nordic}/nrf52840_mdk_dfu/src/io.rs | 0 .../{ => nordic}/nrf52840_mdk_dfu/src/main.rs | 0 boards/nrf52840_dongle_dfu/Cargo.toml | 30 ------------------- boards/nrf52840_dongle_dfu/build.rs | 4 --- boards/nrf52840_mdk_dfu/Cargo.toml | 26 ---------------- boards/nrf52840_mdk_dfu/build.rs | 4 --- deploy.py | 10 +++---- patches/tock/04-additional-boards.patch | 13 ++++++++ patches/tock/04-nrf52-bootloader.patch | 21 ------------- run_desktop_tests.sh | 12 ++++++-- setup.sh | 5 ++++ third_party/tock | 2 +- 21 files changed, 87 insertions(+), 110 deletions(-) create mode 100644 boards/nordic/nrf52840_dongle_dfu/Cargo.toml rename boards/{ => nordic}/nrf52840_dongle_dfu/Makefile (81%) create mode 100644 boards/nordic/nrf52840_dongle_dfu/build.rs rename boards/{nrf52840_mdk_dfu => nordic/nrf52840_dongle_dfu}/layout.ld (76%) create mode 100644 boards/nordic/nrf52840_mdk_dfu/Cargo.toml rename boards/{ => nordic}/nrf52840_mdk_dfu/Makefile (74%) create mode 100644 boards/nordic/nrf52840_mdk_dfu/build.rs rename boards/{nrf52840_dongle_dfu => nordic/nrf52840_mdk_dfu}/layout.ld (76%) rename boards/{ => nordic}/nrf52840_mdk_dfu/src/io.rs (100%) rename boards/{ => nordic}/nrf52840_mdk_dfu/src/main.rs (100%) delete mode 100644 boards/nrf52840_dongle_dfu/Cargo.toml delete mode 100644 boards/nrf52840_dongle_dfu/build.rs delete mode 100644 boards/nrf52840_mdk_dfu/Cargo.toml delete mode 100644 boards/nrf52840_mdk_dfu/build.rs create mode 100644 patches/tock/04-additional-boards.patch delete mode 100644 patches/tock/04-nrf52-bootloader.patch diff --git a/.github/workflows/boards_build.yml b/.github/workflows/boards_build.yml index 0a8fbeb..50a29d5 100644 --- a/.github/workflows/boards_build.yml +++ b/.github/workflows/boards_build.yml @@ -27,15 +27,11 @@ jobs: - name: Set up OpenSK run: ./setup.sh + - name: Building board nrf52840dk + run: ./deploy.py --board=nrf52840dk --no-app --programmer=none + - name: Building board nrf52840_dongle + run: ./deploy.py --board=nrf52840_dongle --no-app --programmer=none - name: Building board nrf52840_dongle_dfu run: ./deploy.py --board=nrf52840_dongle_dfu --no-app --programmer=none - name: Building board nrf52840_mdk_dfu run: ./deploy.py --board=nrf52840_mdk_dfu --no-app --programmer=none - - - name: Create a long build directory - run: mkdir this-is-a-long-build-directory-0123456789abcdefghijklmnopqrstuvwxyz && mv third_party this-is-a-long-build-directory-0123456789abcdefghijklmnopqrstuvwxyz/ - - - name: Building board nrf52840dk - run: make -C this-is-a-long-build-directory-0123456789abcdefghijklmnopqrstuvwxyz/third_party/tock/boards/nordic/nrf52840dk - - name: Building board nrf52840_dongle - run: make -C this-is-a-long-build-directory-0123456789abcdefghijklmnopqrstuvwxyz/third_party/tock/boards/nordic/nrf52840_dongle diff --git a/boards/nordic/nrf52840_dongle_dfu/Cargo.toml b/boards/nordic/nrf52840_dongle_dfu/Cargo.toml new file mode 100644 index 0000000..35eab8a --- /dev/null +++ b/boards/nordic/nrf52840_dongle_dfu/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "nrf52840_dongle_dfu" +version = "0.1.0" +authors = ["Tock Project Developers "] +build = "build.rs" +edition = "2018" + +[[bin]] +path = "../nrf52840_dongle/src/main.rs" +name = "nrf52840_dongle_dfu" + +[dependencies] +components = { path = "../../components" } +cortexm4 = { path = "../../../arch/cortex-m4" } +capsules = { path = "../../../capsules" } +kernel = { path = "../../../kernel" } +nrf52840 = { path = "../../../chips/nrf52840" } +nrf52dk_base = { path = "../nrf52dk_base" } diff --git a/boards/nrf52840_dongle_dfu/Makefile b/boards/nordic/nrf52840_dongle_dfu/Makefile similarity index 81% rename from boards/nrf52840_dongle_dfu/Makefile rename to boards/nordic/nrf52840_dongle_dfu/Makefile index 723ba0a..58771b3 100644 --- a/boards/nrf52840_dongle_dfu/Makefile +++ b/boards/nordic/nrf52840_dongle_dfu/Makefile @@ -4,7 +4,7 @@ TOCK_ARCH=cortex-m4 TARGET=thumbv7em-none-eabi PLATFORM=nrf52840_dongle_dfu -include ../../third_party/tock/boards/Makefile.common +include ../../Makefile.common TOCKLOADER=tockloader @@ -20,10 +20,10 @@ TOCKLOADER_JTAG_FLAGS = --jlink --arch $(TOCK_ARCH) --board $(PLATFORM) --page-s # Upload the kernel over JTAG .PHONY: flash -flash: target/$(TARGET)/release/$(PLATFORM).bin +flash: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).bin $(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) flash --address $(KERNEL_ADDRESS) $(TOCKLOADER_JTAG_FLAGS) $< # Upload the kernel over serial/bootloader .PHONY: program -program: target/$(TARGET)/release/$(PLATFORM).hex +program: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).hex $(error Cannot program nRF52 Dongle over USB. Use \`make flash\` and JTAG) diff --git a/boards/nordic/nrf52840_dongle_dfu/build.rs b/boards/nordic/nrf52840_dongle_dfu/build.rs new file mode 100644 index 0000000..1fdd492 --- /dev/null +++ b/boards/nordic/nrf52840_dongle_dfu/build.rs @@ -0,0 +1,4 @@ +fn main() { + println!("cargo:rerun-if-changed=layout.ld"); + println!("cargo:rerun-if-changed=../../kernel_layout.ld"); +} diff --git a/boards/nrf52840_mdk_dfu/layout.ld b/boards/nordic/nrf52840_dongle_dfu/layout.ld similarity index 76% rename from boards/nrf52840_mdk_dfu/layout.ld rename to boards/nordic/nrf52840_dongle_dfu/layout.ld index 834133c..41ae608 100644 --- a/boards/nrf52840_mdk_dfu/layout.ld +++ b/boards/nordic/nrf52840_dongle_dfu/layout.ld @@ -7,4 +7,4 @@ MEMORY MPU_MIN_ALIGN = 8K; -INCLUDE ../../third_party/tock/boards/kernel_layout.ld +INCLUDE ../../kernel_layout.ld diff --git a/boards/nordic/nrf52840_mdk_dfu/Cargo.toml b/boards/nordic/nrf52840_mdk_dfu/Cargo.toml new file mode 100644 index 0000000..c0cb7af --- /dev/null +++ b/boards/nordic/nrf52840_mdk_dfu/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "nrf52840_mdk_dfu" +version = "0.1.0" +authors = ["Yihui Xiong "] +build = "build.rs" +edition = "2018" + +[dependencies] +components = { path = "../../components" } +cortexm4 = { path = "../../../arch/cortex-m4" } +capsules = { path = "../../../capsules" } +kernel = { path = "../../../kernel" } +nrf52840 = { path = "../../../chips/nrf52840" } +nrf52dk_base = { path = "../nrf52dk_base" } diff --git a/boards/nrf52840_mdk_dfu/Makefile b/boards/nordic/nrf52840_mdk_dfu/Makefile similarity index 74% rename from boards/nrf52840_mdk_dfu/Makefile rename to boards/nordic/nrf52840_mdk_dfu/Makefile index e915141..a179ffb 100644 --- a/boards/nrf52840_mdk_dfu/Makefile +++ b/boards/nordic/nrf52840_mdk_dfu/Makefile @@ -4,7 +4,7 @@ TOCK_ARCH=cortex-m4 TARGET=thumbv7em-none-eabi PLATFORM=nrf52840_mdk_dfu -include ../../third_party/tock/boards/Makefile.common +include ../../Makefile.common TOCKLOADER=tockloader @@ -20,10 +20,10 @@ TOCKLOADER_JTAG_FLAGS = --jlink --arch $(TOCK_ARCH) --board $(PLATFORM) --page-s # Upload the kernel over JTAG .PHONY: flash -flash: target/$(TARGET)/release/$(PLATFORM).bin +flash: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).bin $(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) flash --address $(KERNEL_ADDRESS) $(TOCKLOADER_JTAG_FLAGS) $< # Upload the kernel over serial/bootloader .PHONY: program -program: target/$(TARGET)/release/$(PLATFORM).hex - $(error Cannot program nRF52 Dongle over USB. Use \`make flash\` and JTAG) +program: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).hex + $(error Cannot program nRF52840-MDK over USB. Use \`make flash\` and JTAG) diff --git a/boards/nordic/nrf52840_mdk_dfu/build.rs b/boards/nordic/nrf52840_mdk_dfu/build.rs new file mode 100644 index 0000000..1fdd492 --- /dev/null +++ b/boards/nordic/nrf52840_mdk_dfu/build.rs @@ -0,0 +1,4 @@ +fn main() { + println!("cargo:rerun-if-changed=layout.ld"); + println!("cargo:rerun-if-changed=../../kernel_layout.ld"); +} diff --git a/boards/nrf52840_dongle_dfu/layout.ld b/boards/nordic/nrf52840_mdk_dfu/layout.ld similarity index 76% rename from boards/nrf52840_dongle_dfu/layout.ld rename to boards/nordic/nrf52840_mdk_dfu/layout.ld index 834133c..41ae608 100644 --- a/boards/nrf52840_dongle_dfu/layout.ld +++ b/boards/nordic/nrf52840_mdk_dfu/layout.ld @@ -7,4 +7,4 @@ MEMORY MPU_MIN_ALIGN = 8K; -INCLUDE ../../third_party/tock/boards/kernel_layout.ld +INCLUDE ../../kernel_layout.ld diff --git a/boards/nrf52840_mdk_dfu/src/io.rs b/boards/nordic/nrf52840_mdk_dfu/src/io.rs similarity index 100% rename from boards/nrf52840_mdk_dfu/src/io.rs rename to boards/nordic/nrf52840_mdk_dfu/src/io.rs diff --git a/boards/nrf52840_mdk_dfu/src/main.rs b/boards/nordic/nrf52840_mdk_dfu/src/main.rs similarity index 100% rename from boards/nrf52840_mdk_dfu/src/main.rs rename to boards/nordic/nrf52840_mdk_dfu/src/main.rs diff --git a/boards/nrf52840_dongle_dfu/Cargo.toml b/boards/nrf52840_dongle_dfu/Cargo.toml deleted file mode 100644 index 6944eb3..0000000 --- a/boards/nrf52840_dongle_dfu/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "nrf52840_dongle_dfu" -version = "0.1.0" -authors = ["Tock Project Developers "] -build = "build.rs" -edition = "2018" - -[profile.dev] -panic = "abort" -lto = false -opt-level = "z" -debug = true - -[profile.release] -panic = "abort" -lto = true -opt-level = "z" -debug = true - -[[bin]] -path = "../../third_party/tock/boards/nordic/nrf52840_dongle/src/main.rs" -name = "nrf52840_dongle_dfu" - -[dependencies] -components = { path = "../../third_party/tock/boards/components" } -cortexm4 = { path = "../../third_party/tock/arch/cortex-m4" } -capsules = { path = "../../third_party/tock/capsules" } -kernel = { path = "../../third_party/tock/kernel" } -nrf52840 = { path = "../../third_party/tock/chips/nrf52840" } -nrf52dk_base = { path = "../../third_party/tock/boards/nordic/nrf52dk_base" } diff --git a/boards/nrf52840_dongle_dfu/build.rs b/boards/nrf52840_dongle_dfu/build.rs deleted file mode 100644 index 2631dcc..0000000 --- a/boards/nrf52840_dongle_dfu/build.rs +++ /dev/null @@ -1,4 +0,0 @@ -fn main() { - println!("cargo:rerun-if-changed=layout.ld"); - println!("cargo:rerun-if-changed=../../third_party/tock/boards/kernel_layout.ld"); -} diff --git a/boards/nrf52840_mdk_dfu/Cargo.toml b/boards/nrf52840_mdk_dfu/Cargo.toml deleted file mode 100644 index 1047869..0000000 --- a/boards/nrf52840_mdk_dfu/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "nrf52840_mdk_dfu" -version = "0.1.0" -authors = ["Yihui Xiong "] -build = "build.rs" -edition = "2018" - -[profile.dev] -panic = "abort" -lto = false -opt-level = "z" -debug = true - -[profile.release] -panic = "abort" -lto = true -opt-level = "z" -debug = true - -[dependencies] -components = { path = "../../third_party/tock/boards/components" } -cortexm4 = { path = "../../third_party/tock/arch/cortex-m4" } -capsules = { path = "../../third_party/tock/capsules" } -kernel = { path = "../../third_party/tock/kernel" } -nrf52840 = { path = "../../third_party/tock/chips/nrf52840" } -nrf52dk_base = { path = "../../third_party/tock/boards/nordic/nrf52dk_base" } diff --git a/boards/nrf52840_mdk_dfu/build.rs b/boards/nrf52840_mdk_dfu/build.rs deleted file mode 100644 index 2631dcc..0000000 --- a/boards/nrf52840_mdk_dfu/build.rs +++ /dev/null @@ -1,4 +0,0 @@ -fn main() { - println!("cargo:rerun-if-changed=layout.ld"); - println!("cargo:rerun-if-changed=../../third_party/tock/boards/kernel_layout.ld"); -} diff --git a/deploy.py b/deploy.py index ddbd2e5..3026c7a 100755 --- a/deploy.py +++ b/deploy.py @@ -115,7 +115,7 @@ SUPPORTED_BOARDS = { ), "nrf52840_dongle_dfu": OpenSKBoard( - path="boards/nrf52840_dongle_dfu", + path="third_party/tock/boards/nordic/nrf52840_dongle_dfu", arch="thumbv7em-none-eabi", page_size=4096, kernel_address=0x1000, @@ -132,7 +132,7 @@ SUPPORTED_BOARDS = { ), "nrf52840_mdk_dfu": OpenSKBoard( - path="boards/nrf52840_mdk_dfu", + path="third_party/tock/boards/nordic/nrf52840_mdk_dfu", arch="thumbv7em-none-eabi", page_size=4096, kernel_address=0x1000, @@ -304,7 +304,7 @@ class OpenSKInstaller: def build_tockos(self): info("Building Tock OS for board {}".format(self.args.board)) props = SUPPORTED_BOARDS[self.args.board] - out_directory = os.path.join(props.path, "target", props.arch, "release") + out_directory = os.path.join("third_party", "tock", "target", props.arch, "release") os.makedirs(out_directory, exist_ok=True) self.checked_command_output(["make"], cwd=props.path) @@ -418,7 +418,7 @@ class OpenSKInstaller: def install_tock_os(self): board_props = SUPPORTED_BOARDS[self.args.board] - kernel_file = os.path.join(board_props.path, "target", board_props.arch, + kernel_file = os.path.join("third_party", "tock", "target", board_props.arch, "release", "{}.bin".format(self.args.board)) info("Flashing file {}.".format(kernel_file)) with open(kernel_file, "rb") as f: @@ -481,7 +481,7 @@ class OpenSKInstaller: if self.args.tockos: # Process kernel - kernel_path = os.path.join(board_props.path, "target", board_props.arch, + kernel_path = os.path.join("third_party", "tock", "target", board_props.arch, "release", "{}.bin".format(self.args.board)) with open(kernel_path, "rb") as kernel: kern_hex = intelhex.IntelHex() diff --git a/patches/tock/04-additional-boards.patch b/patches/tock/04-additional-boards.patch new file mode 100644 index 0000000..88cd1c3 --- /dev/null +++ b/patches/tock/04-additional-boards.patch @@ -0,0 +1,13 @@ +diff --git a/Cargo.toml b/Cargo.toml +index 18f4a10d..db88dc1d 100644 +--- a/Cargo.toml ++++ b/Cargo.toml +@@ -13,6 +13,8 @@ members = [ + "boards/launchxl", + "boards/nordic/nrf52840dk", + "boards/nordic/nrf52840_dongle", ++ "boards/nordic/nrf52840_dongle_dfu", ++ "boards/nordic/nrf52840_mdk_dfu", + "boards/nordic/nrf52dk", + "boards/nucleo_f429zi", + "boards/nucleo_f446re", diff --git a/patches/tock/04-nrf52-bootloader.patch b/patches/tock/04-nrf52-bootloader.patch deleted file mode 100644 index 02f57be..0000000 --- a/patches/tock/04-nrf52-bootloader.patch +++ /dev/null @@ -1,21 +0,0 @@ -diff --git a/chips/nrf52/src/crt1.rs b/chips/nrf52/src/crt1.rs -index 9703aac..281ceeb 100644 ---- a/chips/nrf52/src/crt1.rs -+++ b/chips/nrf52/src/crt1.rs -@@ -1,4 +1,4 @@ --use cortexm4::{generic_isr, hard_fault_handler, nvic, svc_handler, systick_handler}; -+use cortexm4::{generic_isr, hard_fault_handler, nvic, scb, svc_handler, systick_handler}; - use tock_rt0; - - /* -@@ -168,5 +168,9 @@ pub unsafe extern "C" fn init() { - tock_rt0::init_data(&mut _etext, &mut _srelocate, &mut _erelocate); - tock_rt0::zero_bss(&mut _szero, &mut _ezero); - -+ // Ensure that we are compatible with a bootloader. -+ // For this we need to offset our vector table -+ scb::set_vector_table_offset(BASE_VECTORS.as_ptr() as *const ()); -+ - nvic::enable_all(); - } - diff --git a/run_desktop_tests.sh b/run_desktop_tests.sh index e387353..36bde36 100755 --- a/run_desktop_tests.sh +++ b/run_desktop_tests.sh @@ -50,8 +50,16 @@ make -C third_party/tock/boards/nordic/nrf52840dk make -C third_party/tock/boards/nordic/nrf52840_dongle echo "Checking that other boards build properly..." -make -C boards/nrf52840_dongle_dfu -make -C boards/nrf52840_mdk_dfu +make -C third_party/tock/boards/nordic/nrf52840_dongle_dfu +make -C third_party/tock/boards/nordic/nrf52840_mdk_dfu + +echo "Checking deployment of supported boards..." +./deploy.py --board=nrf52840dk --no-app --programmer=none +./deploy.py --board=nrf52840_dongle --no-app --programmer=none + +echo "Checking deployment of other boards..." +./deploy.py --board=nrf52840_dongle_dfu --no-app --programmer=none +./deploy.py --board=nrf52840_mdk_dfu --no-app --programmer=none if [ -z "${TRAVIS_OS_NAME}" -o "${TRAVIS_OS_NAME}" = "linux" ] then diff --git a/setup.sh b/setup.sh index 1c6fe37..c4297ad 100755 --- a/setup.sh +++ b/setup.sh @@ -46,6 +46,11 @@ EOF exit 1 } +# Copy additional boards to the kernel. +echo -n '[-] Copying additional boards to Tock... ' +cp -r boards/* third_party/tock/boards +echo $done_text + # Apply patches to kernel. Do that in a sub-shell ( cd third_party/tock/ && \ diff --git a/third_party/tock b/third_party/tock index 3139864..ba44dd6 160000 --- a/third_party/tock +++ b/third_party/tock @@ -1 +1 @@ -Subproject commit 3139864d391ab654bfb9c27ca8dcd3e4e9a2d58e +Subproject commit ba44dd690f2db52ab9c928975f85c34c1c6f5bea From e467ed1344c381398d774b4b9de0718db38ebc3e Mon Sep 17 00:00:00 2001 From: Guillaume Endignoux Date: Wed, 8 Apr 2020 15:55:51 +0200 Subject: [PATCH 08/11] Fix Python formatting. --- deploy.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/deploy.py b/deploy.py index 3026c7a..f519885 100755 --- a/deploy.py +++ b/deploy.py @@ -304,7 +304,8 @@ class OpenSKInstaller: def build_tockos(self): info("Building Tock OS for board {}".format(self.args.board)) props = SUPPORTED_BOARDS[self.args.board] - out_directory = os.path.join("third_party", "tock", "target", props.arch, "release") + out_directory = os.path.join("third_party", "tock", "target", props.arch, + "release") os.makedirs(out_directory, exist_ok=True) self.checked_command_output(["make"], cwd=props.path) @@ -418,8 +419,9 @@ class OpenSKInstaller: def install_tock_os(self): board_props = SUPPORTED_BOARDS[self.args.board] - kernel_file = os.path.join("third_party", "tock", "target", board_props.arch, - "release", "{}.bin".format(self.args.board)) + kernel_file = os.path.join("third_party", "tock", "target", + board_props.arch, "release", + "{}.bin".format(self.args.board)) info("Flashing file {}.".format(kernel_file)) with open(kernel_file, "rb") as f: kernel = f.read() @@ -481,8 +483,9 @@ class OpenSKInstaller: if self.args.tockos: # Process kernel - kernel_path = os.path.join("third_party", "tock", "target", board_props.arch, - "release", "{}.bin".format(self.args.board)) + kernel_path = os.path.join("third_party", "tock", "target", + board_props.arch, "release", + "{}.bin".format(self.args.board)) with open(kernel_path, "rb") as kernel: kern_hex = intelhex.IntelHex() kern_hex.frombytes(kernel.read(), offset=board_props.kernel_address) From 291d3db553482a81d94097db46887feabfc6e297 Mon Sep 17 00:00:00 2001 From: Guillaume Endignoux Date: Thu, 9 Apr 2020 13:38:57 +0200 Subject: [PATCH 09/11] Add rustfmt component to cargo_fmt workflow. --- .github/workflows/cargo_fmt.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/cargo_fmt.yml b/.github/workflows/cargo_fmt.yml index 00589ca..df466b3 100644 --- a/.github/workflows/cargo_fmt.yml +++ b/.github/workflows/cargo_fmt.yml @@ -20,6 +20,7 @@ jobs: - uses: actions-rs/toolchain@v1 with: target: thumbv7em-none-eabi + components: rustfmt - uses: actions/setup-python@v1 with: python-version: 3.7 From a64d5d41d7c37f48946b74c40eba9cb4b4b0c051 Mon Sep 17 00:00:00 2001 From: Fabian Kaczmarczyck Date: Thu, 9 Apr 2020 15:56:23 +0200 Subject: [PATCH 10/11] accept the algorithm identifier used by OpenSSH --- src/ctap/data_formats.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ctap/data_formats.rs b/src/ctap/data_formats.rs index 5049d1c..3a2bed9 100644 --- a/src/ctap/data_formats.rs +++ b/src/ctap/data_formats.rs @@ -462,6 +462,8 @@ pub struct CoseKey(pub BTreeMap); // here: https://www.iana.org/assignments/cose/cose.xhtml#algorithms // In fact, this is just used for compatibility with older specification versions. const ECDH_ALGORITHM: i64 = -25; +// This is the identifier used by OpenSSH. To be compatible, we accept both. +const ES256_ALGORITHM: i64 = -7; const EC2_KEY_TYPE: i64 = 2; const P_256_CURVE: i64 = 1; @@ -497,7 +499,7 @@ impl TryFrom for ecdh::PubKey { return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM); } let algorithm = read_integer(ok_or_missing(cose_key.0.get(&cbor_int!(3)))?)?; - if algorithm != ECDH_ALGORITHM { + if algorithm != ECDH_ALGORITHM && algorithm != ES256_ALGORITHM { return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM); } let curve = read_integer(ok_or_missing(cose_key.0.get(&cbor_int!(-1)))?)?; From 780a5e6a0d5346484a52ab779c36f9518943ccd8 Mon Sep 17 00:00:00 2001 From: Guillaume Endignoux Date: Tue, 14 Apr 2020 16:19:48 +0200 Subject: [PATCH 11/11] More detailed installation instructions. --- docs/install.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/install.md b/docs/install.md index 5b5763b..cf355fa 100644 --- a/docs/install.md +++ b/docs/install.md @@ -50,8 +50,9 @@ In order to compile and flash a working OpenSK firmware, you will need the following: * rustup (can be installed with [Rustup](https://rustup.rs/)) -* python3 and pip -* the OpenSSL command line tool +* python3 and pip (can be installed with the `python3-pip` package on Debian) +* the OpenSSL command line tool (can be installed with the `libssl-dev` + package on Debian) The scripts provided in this project have been tested under Linux and OS X. We haven't tested them on Windows and other platforms.