diff --git a/70-solo2.rules b/70-solo2.rules new file mode 100644 index 0000000..51324b2 --- /dev/null +++ b/70-solo2.rules @@ -0,0 +1,8 @@ +# NXP LPC55 ROM bootloader (unmodified) +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1fc9", ATTRS{idProduct}=="0021", TAG+="uaccess" +# NXP LPC55 ROM bootloader (with Solo 2 VID:PID) +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="beee", TAG+="uaccess" +# Solo 2 +SUBSYSTEM=="tty", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="beee", TAG+="uaccess" +# Solo 2 +SUBSYSTEM=="usb", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="beee", TAG+="uaccess" diff --git a/CHANGELOG.md b/CHANGELOG.md index de98cea..f62cb4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.0.6] - 2021-11-06 + +### Changed + +- No more async - we're not a high throughput webserver +- Nicer user dialogs (dialoguer/indicatif) +- Model devices modes (bootloader/card) +- Add udev rules + ## [0.0.5] - 2021-11-06 ### Added diff --git a/Cargo.lock b/Cargo.lock index 5f3df1f..756de09 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1472,7 +1472,7 @@ checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" [[package]] name = "solo2" -version = "0.0.5" +version = "0.0.6" dependencies = [ "anyhow", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 4955d19..f6126ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solo2" -version = "0.0.5" +version = "0.0.6" authors = ["SoloKeys Developers"] edition = "2021" rust-version = "1.56" diff --git a/src/apps.rs b/src/apps.rs index 93c22f3..3493c33 100644 --- a/src/apps.rs +++ b/src/apps.rs @@ -1,7 +1,7 @@ use hex_literal::hex; +use crate::device::{prompt_user_to_select_device, Device}; use crate::{Card, Result, Uuid}; -use crate::device::{Device, prompt_user_to_select_device}; pub mod admin; pub mod ndef; @@ -53,7 +53,6 @@ pub trait App: Sized { fn card(&mut self) -> &mut Card; fn connect(uuid: Option) -> Result { - let mut cards = Card::list(Default::default()); if cards.is_empty() { @@ -73,10 +72,11 @@ pub trait App: Sized { } } - return Err(anyhow::anyhow!("Could not find any Solo 2 device with uuid {}.", uuid.hex())); - + return Err(anyhow::anyhow!( + "Could not find any Solo 2 device with uuid {}.", + uuid.hex() + )); } else { - let devices = cards.into_iter().map(Device::from).collect(); let selected = prompt_user_to_select_device(devices)?; diff --git a/src/apps/oath.rs b/src/apps/oath.rs index 891e244..7324bf5 100644 --- a/src/apps/oath.rs +++ b/src/apps/oath.rs @@ -1,7 +1,4 @@ -use core::{ - convert::{TryFrom, TryInto}, - fmt, -}; +use core::fmt; use anyhow::anyhow; use flexiber::{Encodable, TaggedSlice}; diff --git a/src/apps/provisioner.rs b/src/apps/provisioner.rs index d1e4770..9c1c062 100644 --- a/src/apps/provisioner.rs +++ b/src/apps/provisioner.rs @@ -1,5 +1,3 @@ -use core::convert::TryInto; - use anyhow::anyhow; use iso7816::Instruction; diff --git a/src/bin/solo2/main.rs b/src/bin/solo2/main.rs index a5e895b..bfe8b8d 100644 --- a/src/bin/solo2/main.rs +++ b/src/bin/solo2/main.rs @@ -19,10 +19,10 @@ fn main() { } fn try_main(args: clap::ArgMatches<'_>) -> anyhow::Result<()> { - - let uuid = args.value_of("uuid") + let uuid = args + .value_of("uuid") // if uuid is Some, parse and fail on invalidity (no silent failure) - .map(|uuid| solo2::Uuid::from_hex(&uuid)) + .map(|uuid| solo2::Uuid::from_hex(uuid)) .transpose()?; if let Some(args) = args.subcommand_matches("app") { @@ -229,7 +229,6 @@ fn try_main(args: clap::ArgMatches<'_>) -> anyhow::Result<()> { } if let Some(_args) = args.subcommand_matches("list") { - let devices = solo2::Device::list(); for device in devices { println!("{}", &device); diff --git a/src/dev_pki.rs b/src/dev_pki.rs index 72e7caa..6afaa0d 100644 --- a/src/dev_pki.rs +++ b/src/dev_pki.rs @@ -3,8 +3,6 @@ //! In particular, there is no root CA, no use of hardware keys / PKCS #11, //! no revocation, etc. etc. -use core::convert::TryInto; - use pkcs8::FromPrivateKey as _; use rand_core::{OsRng, RngCore}; diff --git a/src/device.rs b/src/device.rs index 7b9b3dd..67d69f0 100644 --- a/src/device.rs +++ b/src/device.rs @@ -3,8 +3,8 @@ use anyhow::anyhow; use lpc55::bootloader::Bootloader; -use core::fmt; use crate::{Card, Result, Uuid}; +use core::fmt; // #[derive(Debug, Eq, PartialEq)] pub enum Device { @@ -12,12 +12,13 @@ pub enum Device { Card(Card), } - impl fmt::Display for Device { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Device::Bootloader(bootloader) => - f.write_fmt(format_args!("Bootloader UUID: {}", Uuid::from(bootloader.uuid).hex())), + Device::Bootloader(bootloader) => f.write_fmt(format_args!( + "Bootloader UUID: {}", + Uuid::from(bootloader.uuid).hex() + )), Device::Card(card) => card.fmt(f), } } @@ -26,10 +27,11 @@ impl fmt::Display for Device { impl Device { pub fn list() -> Vec { let bootloaders = Bootloader::list().into_iter().map(Device::from); - let cards = Card::list(crate::smartcard::Filter::SoloCards).into_iter().map(Device::from); + let cards = Card::list(crate::smartcard::Filter::SoloCards) + .into_iter() + .map(Device::from); - let devices = bootloaders.chain(cards).collect(); - devices + bootloaders.chain(cards).collect() } /// If this is a Solo device, this will successfully report the UUID. @@ -37,7 +39,9 @@ impl Device { pub fn uuid(&self) -> Result { match self { Device::Bootloader(bootloader) => Ok(bootloader.uuid.into()), - Device::Card(card) => card.uuid.ok_or(anyhow!("Device does not have a UUID")), + Device::Card(card) => card + .uuid + .ok_or_else(|| anyhow!("Device does not have a UUID")), } } @@ -79,7 +83,10 @@ pub fn find_bootloader(uuid: Option) -> Result { return Ok(bootloader); } } - return Err(anyhow!("Could not find any Solo 2 device with uuid {}.", uuid.hex())); + return Err(anyhow!( + "Could not find any Solo 2 device with uuid {}.", + uuid.hex() + )); } else { let mut devices: Vec = Default::default(); for bootloader in bootloaders { @@ -97,35 +104,36 @@ pub fn prompt_user_to_select_device(mut devices: Vec) -> Result return Err(anyhow!("No Solo 2 devices connected")); } - let items: Vec = devices.iter().map(|device| { - match device { - Device::Bootloader(bootloader) => { - format!( - "Bootloader UUID: {}", - hex::encode(bootloader.uuid.to_be_bytes()) - ) - - }, - Device::Card(card) => { - if let Some(uuid) = card.uuid { - // format!("\"{}\" UUID: {}", card.reader_name, hex::encode(uuid.to_be_bytes())) - format!("Solo 2 {}", uuid.hex()) - } else { - format!(" \"{}\"", card.reader_name) + let items: Vec = devices + .iter() + .map(|device| { + match device { + Device::Bootloader(bootloader) => { + format!( + "Bootloader UUID: {}", + hex::encode(bootloader.uuid.to_be_bytes()) + ) + } + Device::Card(card) => { + if let Some(uuid) = card.uuid { + // format!("\"{}\" UUID: {}", card.reader_name, hex::encode(uuid.to_be_bytes())) + format!("Solo 2 {}", uuid.hex()) + } else { + format!(" \"{}\"", card.reader_name) + } } } - } - }).collect(); + }) + .collect(); - use dialoguer::{Select, theme}; + use dialoguer::{theme, Select}; // let selection = Select::with_theme(&theme::SimpleTheme) let selection = Select::with_theme(&theme::ColorfulTheme::default()) .with_prompt("Multiple Solo 2 devices connected, select one or hit Escape key") .items(&items) .default(0) .interact_opt()? - .ok_or(anyhow!("No device selected"))?; + .ok_or_else(|| anyhow!("No device selected"))?; Ok(devices.remove(selection)) - } diff --git a/src/lib.rs b/src/lib.rs index 59d6f2b..6d8f8d0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +#![deny(warnings, trivial_casts, unused_qualifications)] + #[macro_use] extern crate log; diff --git a/src/smartcard.rs b/src/smartcard.rs index 30e7aa9..aa7e462 100644 --- a/src/smartcard.rs +++ b/src/smartcard.rs @@ -1,5 +1,5 @@ use anyhow::anyhow; -use core::{convert::{TryFrom, TryInto}, fmt}; +use core::fmt; use iso7816::Status; use pcsc::{Context, Protocols, Scope, ShareMode}; @@ -44,7 +44,7 @@ impl TryFrom<(&std::ffi::CStr, &Context)> for Card { Ok(Self { card, reader_name: reader.to_str().unwrap().to_owned(), - uuid: uuid_maybe + uuid: uuid_maybe, }) } } @@ -52,12 +52,8 @@ impl TryFrom<(&std::ffi::CStr, &Context)> for Card { impl Card { pub fn list(filter: Filter) -> Vec { let cards = match Self::try_list() { - Ok(cards) => { - cards - } - _ => { - Default::default() - } + Ok(cards) => cards, + _ => Default::default(), }; match filter { Filter::AllCards => cards, @@ -69,7 +65,6 @@ impl Card { } pub fn try_list() -> crate::Result> { - let mut cards_with_trussed: Vec = Default::default(); let context = Context::establish(Scope::User)?; @@ -116,18 +111,11 @@ impl Card { Some(&aid), )?; - let uuid_bytes = Self::call_card( - card, - 0, - apps::admin::App::UUID_COMMAND, - 0x00, - 0x00, - None, - )?; + let uuid_bytes = + Self::call_card(card, 0, apps::admin::App::UUID_COMMAND, 0x00, 0x00, None)?; - Ok(Uuid::try_from(uuid_bytes.as_ref()) - .map_err(|_| anyhow!("Did not read 16 byte uuid from mgmt app."))? - ) + Uuid::try_from(uuid_bytes.as_ref()) + .map_err(|_| anyhow!("Did not read 16 byte uuid from mgmt app.")) } fn call_card( diff --git a/src/update.rs b/src/update.rs index 75859e6..da66c19 100644 --- a/src/update.rs +++ b/src/update.rs @@ -4,10 +4,10 @@ use anyhow::anyhow; use lpc55::bootloader::Bootloader; use serde_json::{from_value, Value}; -use crate::{Card, smartcard, Uuid}; -use crate::apps::App; use crate::apps::admin; -use crate::device::{Device, prompt_user_to_select_device}; +use crate::apps::App; +use crate::device::{prompt_user_to_select_device, Device}; +use crate::{smartcard, Card, Uuid}; pub fn download_latest_solokeys_firmware() -> crate::Result> { println!("Downloading latest release from https://github.com/solokeys/solo2/"); @@ -48,7 +48,7 @@ pub fn download_latest_solokeys_firmware() -> crate::Result> { .set("User-Agent", "solo2-cli") .call()? .into_string()?; - sha256hash = Some(hashfile.split(" ").collect::>()[0].into()); + sha256hash = Some(hashfile.split(' ').collect::>()[0].into()); } } @@ -71,7 +71,7 @@ pub fn download_latest_solokeys_firmware() -> crate::Result> { } // A rather tolerant update function, intended to be used by end users. -pub fn run_update_procedure ( +pub fn run_update_procedure( sbfile: Option, uuid: Option, _skip_major_prompt: bool, @@ -99,13 +99,13 @@ pub fn run_update_procedure ( match device.uuid() { Ok(device_uuid) => { if device_uuid == uuid { - return program_device(device, sbfile) + return program_device(device, sbfile); } } _ => continue, } } - return Err(anyhow!("Cannot find solo2 device with UUID {}", uuid.hex())) + return Err(anyhow!("Cannot find solo2 device with UUID {}", uuid.hex())); } else if update_all { for device in devices { program_device(device, sbfile.clone())?; @@ -128,8 +128,8 @@ pub fn program_device(device: Device, sbfile: Vec) -> crate::Result<()> { let device_version: u32 = admin.version()?.into(); let sb2_product_version = lpc55::secure_binary::Sb2Header::from_bytes(&sbfile.as_slice()[..96]) - .unwrap() - .product_version(); + .unwrap() + .product_version(); // Device stores version as: // major minor patch @@ -139,16 +139,16 @@ pub fn program_device(device: Device, sbfile: Vec) -> crate::Result<()> { info!("new sb2 firmware version: {:?}", sb2_product_version); if device_version_major < sb2_product_version.major as u32 { - use dialoguer::{Confirm, theme}; + use dialoguer::{theme, Confirm}; println!("Warning: This is is major update and it could risk breaking any current credentials on your key."); println!("Check latest release notes here to double check: https://github.com/solokeys/solo2/releases"); - println!("If you haven't used your key for anything yet, you can ignore this."); + println!("If you haven't used your key for anything yet, you can ignore this.\n"); - println!(""); if Confirm::with_theme(&theme::ColorfulTheme::default()) .with_prompt("Continue?") .wait_for_newline(true) - .interact()? { + .interact()? + { println!("Continuing"); } else { return Err(anyhow!("User aborted.")); diff --git a/src/uuid.rs b/src/uuid.rs index a16b303..8c22672 100644 --- a/src/uuid.rs +++ b/src/uuid.rs @@ -1,4 +1,3 @@ -use core::convert::{TryFrom, TryInto}; use crate::{Error, Result}; #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] @@ -46,6 +45,3 @@ impl Uuid { bytes.as_slice().try_into() } } - - -