Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ac72797d1f | |||
| fdd3b8730a | |||
| d51ec0a225 | |||
| d601c33ba3 | |||
| 8e52d75992 | |||
| 42ae5fb974 | |||
| 224d346f09 | |||
| 01e5bba33f | |||
| 48f42780df | |||
| 92f770805f | |||
| 563f6f9ccc | |||
| 5f418bbd1d | |||
| 47776ebf0b | |||
| 227518dd1b | |||
| e6cea2eca6 | |||
| e249e91297 | |||
| 1018127843 | |||
| 1765e11bc0 | |||
| 1228d16439 | |||
| de51b0cc46 | |||
| 1051eaf26d | |||
| a1d9c7afc5 | |||
| 2c06626c25 | |||
| a2a912fc3c | |||
| c9e2edc41f | |||
| 20bf9b0679 | |||
| 0d4d4f7f06 | |||
| d31872964d | |||
| 865353f4da | |||
| 1ad17bb025 | |||
| d33e80faea | |||
| e61682be43 |
@@ -36,13 +36,13 @@ jobs:
|
||||
toolchain: stable
|
||||
deps: true
|
||||
- platform: ubuntu-latest
|
||||
toolchain: 1.46.0 # MSRV
|
||||
toolchain: 1.51.0 # MSRV
|
||||
deps: sudo apt-get install libpcsclite-dev
|
||||
- platform: windows-latest
|
||||
toolchain: 1.46.0 # MSRV
|
||||
toolchain: 1.51.0 # MSRV
|
||||
deps: true
|
||||
- platform: macos-latest
|
||||
toolchain: 1.46.0 # MSRV
|
||||
toolchain: 1.51.0 # MSRV
|
||||
deps: true
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
@@ -82,7 +82,8 @@ jobs:
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: 1.46.0 # MSRV
|
||||
toolchain: 1.51.0 # MSRV
|
||||
components: clippy
|
||||
override: true
|
||||
- run: sudo apt-get install libpcsclite-dev
|
||||
- run: cargo clippy --all --exclude crypto_box --all-features -- -D warnings
|
||||
- run: cargo clippy --all --all-features -- -D warnings
|
||||
|
||||
@@ -42,4 +42,4 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: audit
|
||||
args: --deny-warnings --ignore RUSTSEC-2019-0031 # spin
|
||||
args: --deny warnings --ignore RUSTSEC-2019-0031 # spin
|
||||
|
||||
+109
-49
@@ -4,7 +4,67 @@ 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.3.0 (2021-03-22)
|
||||
## 0.4.2 (2021-07-13)
|
||||
### Added
|
||||
- Make `yubikey::Buffer` a pub type ([#290])
|
||||
|
||||
### Changed
|
||||
- Have `YubiKey::block_puk` take `&mut self` as argument ([#289])
|
||||
|
||||
[#289]: https://github.com/iqlusioninc/yubikey.rs/pull/289
|
||||
[#290]: https://github.com/iqlusioninc/yubikey.rs/pull/290
|
||||
|
||||
## 0.4.1 (2021-07-12)
|
||||
### Changed
|
||||
- Rename `SettingValue` to `Setting` ([#286])
|
||||
- Rename `Ccc` to `CccId` ([#287])
|
||||
|
||||
[#286]: https://github.com/iqlusioninc/yubikey.rs/pull/286
|
||||
[#287]: https://github.com/iqlusioninc/yubikey.rs/pull/287
|
||||
|
||||
## 0.4.0 (2021-07-12) [YANKED]
|
||||
### Added
|
||||
- `Result` alias ([#271])
|
||||
|
||||
### Changed
|
||||
- Renamed crate from `yubikey-piv` => `yubikey` ([#267])
|
||||
- Renamed the following:
|
||||
- `APDU` => `Apdu` ([#269])
|
||||
- `CCC` => `Ccc` ([#269])
|
||||
- `CHUID` => `ChuId` ([#269])
|
||||
- `Ccc::cccid` => `Ccc::card_id` ([#270])
|
||||
- `key` => `piv` ([#277])
|
||||
- `readers` => `reader` ([#278])
|
||||
- `readers::Readers` => `reader::Context` ([#278])
|
||||
- Bumped the following dependencies:
|
||||
- `rsa` => v0.4 ([#246])
|
||||
- `des` => v0.7 ([#251])
|
||||
- `elliptic-curve` => v0.10 ([#268])
|
||||
- `hmac` => v0.11 ([#251])
|
||||
- `pbkdf2` => v0.8 ([#251])
|
||||
- `p256` => v0.9 ([#268])
|
||||
- `p384` => v0.8 ([#268])
|
||||
- MSRV 1.51+ ([#268])
|
||||
- Flatten API ([#274])
|
||||
- Replace `getrandom` with `rand_core` ([#276])
|
||||
|
||||
### Fixed
|
||||
- Potential local DoS in TLV parser ([#279])
|
||||
|
||||
[#246]: https://github.com/iqlusioninc/yubikey.rs/pull/246
|
||||
[#251]: https://github.com/iqlusioninc/yubikey.rs/pull/251
|
||||
[#267]: https://github.com/iqlusioninc/yubikey.rs/pull/267
|
||||
[#268]: https://github.com/iqlusioninc/yubikey.rs/pull/268
|
||||
[#269]: https://github.com/iqlusioninc/yubikey.rs/pull/269
|
||||
[#270]: https://github.com/iqlusioninc/yubikey.rs/pull/270
|
||||
[#271]: https://github.com/iqlusioninc/yubikey.rs/pull/271
|
||||
[#274]: https://github.com/iqlusioninc/yubikey.rs/pull/274
|
||||
[#276]: https://github.com/iqlusioninc/yubikey.rs/pull/276
|
||||
[#277]: https://github.com/iqlusioninc/yubikey.rs/pull/277
|
||||
[#278]: https://github.com/iqlusioninc/yubikey.rs/pull/278
|
||||
[#279]: https://github.com/iqlusioninc/yubikey.rs/pull/279
|
||||
|
||||
## yubikey-piv 0.3.0 (2021-03-22)
|
||||
### Added
|
||||
- Typed structs for PIN-protected and admin metadata ([#223])
|
||||
- `MgmKey::set_default`/`MgmKey::set_manual` methods ([#224])
|
||||
@@ -15,10 +75,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Removed
|
||||
- `MgmKey::set` method ([#224])
|
||||
|
||||
[#223]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/223
|
||||
[#224]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/224
|
||||
[#223]: https://github.com/iqlusioninc/yubikey.rs/pull/223
|
||||
[#224]: https://github.com/iqlusioninc/yubikey.rs/pull/224
|
||||
|
||||
## 0.2.0 (2021-01-30)
|
||||
## yubikey-piv 0.2.0 (2021-01-30)
|
||||
### Changed
|
||||
- Bump `der-parser` to v5.0 ([#194])
|
||||
- Improve self-signed certificates ([#207])
|
||||
@@ -27,12 +87,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Bump MSRV to 1.46+ ([#208])
|
||||
- Bump `pbkdf2` dependency to v0.7 ([#219])
|
||||
|
||||
[#194]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/194
|
||||
[#207]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/207
|
||||
[#208]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/208
|
||||
[#219]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/219
|
||||
[#194]: https://github.com/iqlusioninc/yubikey.rs/pull/194
|
||||
[#207]: https://github.com/iqlusioninc/yubikey.rs/pull/207
|
||||
[#208]: https://github.com/iqlusioninc/yubikey.rs/pull/208
|
||||
[#219]: https://github.com/iqlusioninc/yubikey.rs/pull/219
|
||||
|
||||
## 0.1.0 (2020-10-19)
|
||||
## yubikey-piv 0.1.0 (2020-10-19)
|
||||
### Added
|
||||
- `Certificate::generate_self_signed` ([#80])
|
||||
- `YubiKey::open_by_serial` ([#69])
|
||||
@@ -57,24 +117,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Removed
|
||||
- YubiKey NEO support ([#63])
|
||||
|
||||
[#177]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/177
|
||||
[#175]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/175
|
||||
[#128]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/128
|
||||
[#82]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/82
|
||||
[#73]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/73
|
||||
[#88]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/88
|
||||
[#80]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/80
|
||||
[#69]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/69
|
||||
[#68]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/68
|
||||
[#67]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/67
|
||||
[#65]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/65
|
||||
[#64]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/64
|
||||
[#63]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/63
|
||||
[#62]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/62
|
||||
[#61]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/61
|
||||
[#60]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/60
|
||||
[#177]: https://github.com/iqlusioninc/yubikey.rs/pull/177
|
||||
[#175]: https://github.com/iqlusioninc/yubikey.rs/pull/175
|
||||
[#128]: https://github.com/iqlusioninc/yubikey.rs/pull/128
|
||||
[#82]: https://github.com/iqlusioninc/yubikey.rs/pull/82
|
||||
[#73]: https://github.com/iqlusioninc/yubikey.rs/pull/73
|
||||
[#88]: https://github.com/iqlusioninc/yubikey.rs/pull/88
|
||||
[#80]: https://github.com/iqlusioninc/yubikey.rs/pull/80
|
||||
[#69]: https://github.com/iqlusioninc/yubikey.rs/pull/69
|
||||
[#68]: https://github.com/iqlusioninc/yubikey.rs/pull/68
|
||||
[#67]: https://github.com/iqlusioninc/yubikey.rs/pull/67
|
||||
[#65]: https://github.com/iqlusioninc/yubikey.rs/pull/65
|
||||
[#64]: https://github.com/iqlusioninc/yubikey.rs/pull/64
|
||||
[#63]: https://github.com/iqlusioninc/yubikey.rs/pull/63
|
||||
[#62]: https://github.com/iqlusioninc/yubikey.rs/pull/62
|
||||
[#61]: https://github.com/iqlusioninc/yubikey.rs/pull/61
|
||||
[#60]: https://github.com/iqlusioninc/yubikey.rs/pull/60
|
||||
|
||||
## 0.0.3 (2019-12-02)
|
||||
## yubikey-piv 0.0.3 (2019-12-02)
|
||||
### Added
|
||||
- Initial `Readers` enumerator for detecting YubiKeys ([#51])
|
||||
- Certificate parsing ([#45])
|
||||
@@ -90,19 +150,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- `Ins` (APDU instruction codes) enum ([#33])
|
||||
- Factor `Response` into `apdu` module; improved debugging ([#32])
|
||||
|
||||
[#51]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/51
|
||||
[#45]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/45
|
||||
[#44]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/44
|
||||
[#43]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/43
|
||||
[#42]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/42
|
||||
[#39]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/39
|
||||
[#37]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/37
|
||||
[#36]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/36
|
||||
[#34]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/34
|
||||
[#33]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/33
|
||||
[#32]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/32
|
||||
[#51]: https://github.com/iqlusioninc/yubikey.rs/pull/51
|
||||
[#45]: https://github.com/iqlusioninc/yubikey.rs/pull/45
|
||||
[#44]: https://github.com/iqlusioninc/yubikey.rs/pull/44
|
||||
[#43]: https://github.com/iqlusioninc/yubikey.rs/pull/43
|
||||
[#42]: https://github.com/iqlusioninc/yubikey.rs/pull/42
|
||||
[#39]: https://github.com/iqlusioninc/yubikey.rs/pull/39
|
||||
[#37]: https://github.com/iqlusioninc/yubikey.rs/pull/37
|
||||
[#36]: https://github.com/iqlusioninc/yubikey.rs/pull/36
|
||||
[#34]: https://github.com/iqlusioninc/yubikey.rs/pull/34
|
||||
[#33]: https://github.com/iqlusioninc/yubikey.rs/pull/33
|
||||
[#32]: https://github.com/iqlusioninc/yubikey.rs/pull/32
|
||||
|
||||
## 0.0.2 (2019-11-25)
|
||||
## yubikey-piv 0.0.2 (2019-11-25)
|
||||
### Added
|
||||
- `untested` Cargo feature to mark untested functionality ([#30])
|
||||
- Initial connect test and docs ([#19])
|
||||
@@ -117,16 +177,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Use `log` crate for logging ([#7])
|
||||
- Replace `ErrorKind::Ok` with `Result` ([#6])
|
||||
|
||||
[#30]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/30
|
||||
[#19]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/19
|
||||
[#17]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/17
|
||||
[#15]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/15
|
||||
[#13]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/13
|
||||
[#10]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/10
|
||||
[#9]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/9
|
||||
[#8]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/8
|
||||
[#7]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/7
|
||||
[#6]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/6
|
||||
[#30]: https://github.com/iqlusioninc/yubikey.rs/pull/30
|
||||
[#19]: https://github.com/iqlusioninc/yubikey.rs/pull/19
|
||||
[#17]: https://github.com/iqlusioninc/yubikey.rs/pull/17
|
||||
[#15]: https://github.com/iqlusioninc/yubikey.rs/pull/15
|
||||
[#13]: https://github.com/iqlusioninc/yubikey.rs/pull/13
|
||||
[#10]: https://github.com/iqlusioninc/yubikey.rs/pull/10
|
||||
[#9]: https://github.com/iqlusioninc/yubikey.rs/pull/9
|
||||
[#8]: https://github.com/iqlusioninc/yubikey.rs/pull/8
|
||||
[#7]: https://github.com/iqlusioninc/yubikey.rs/pull/7
|
||||
[#6]: https://github.com/iqlusioninc/yubikey.rs/pull/6
|
||||
|
||||
## 0.0.1 (2019-11-18)
|
||||
## yubikey-piv 0.0.1 (2019-11-18)
|
||||
- Initial release
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2014-2019 Yubico AB, Tony Arcieri
|
||||
Copyright (c) 2014-2021 Yubico AB, Tony Arcieri
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
||||
Generated
+173
-288
@@ -52,17 +52,6 @@ version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "bitvec"
|
||||
version = "0.18.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98fcd36dda4e17b7d7abc64cb549bf0201f4ab71e00700c798ca7e62ed3761fa"
|
||||
dependencies = [
|
||||
"funty",
|
||||
"radium 0.3.0",
|
||||
"wyz",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitvec"
|
||||
version = "0.19.5"
|
||||
@@ -70,7 +59,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321"
|
||||
dependencies = [
|
||||
"funty",
|
||||
"radium 0.5.3",
|
||||
"radium",
|
||||
"tap",
|
||||
"wyz",
|
||||
]
|
||||
@@ -84,24 +73,12 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.67"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
@@ -123,18 +100,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cipher"
|
||||
version = "0.2.5"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801"
|
||||
checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.4.5"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f6b64db6932c7e49332728e3a6bd82c6b7e16016607d20923b537c3bc4c0d5f"
|
||||
checksum = "44c32f031ea41b4291d695026c023b95d59db2d8a2c7640800ed56bc8f510f22"
|
||||
|
||||
[[package]]
|
||||
name = "cookie-factory"
|
||||
@@ -143,16 +120,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "396de984970346b0d9e93d1415082923c679e5ae5c3ee3dcbd104f5610af126b"
|
||||
|
||||
[[package]]
|
||||
name = "cpuid-bool"
|
||||
version = "0.1.2"
|
||||
name = "cpufeatures"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634"
|
||||
checksum = "66c99696f6c9dd7f35d486b9d04d7e6e202aa3e8c40d553f2fdf5e7e0c6a71ef"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-bigint"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b32a398eb1ccfbe7e4f452bc749c44d38dd732e9a253f19da224c416f00ee7f4"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"rand_core",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-mac"
|
||||
version = "0.10.0"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4857fd85a0c34b3c3297875b747c1e02e06b6a0ea32dd892d8192b9ce0813ea6"
|
||||
checksum = "25fab6889090c8133f3deb8f73ba3c65a7f456f66436fc012a1b1e272b1e103e"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"subtle",
|
||||
@@ -166,44 +158,43 @@ checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57"
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.1.0"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51f59c66c30bb7445c8320a5f9233e437e3572368099f25532a59054328899b4"
|
||||
checksum = "49f215f706081a44cb702c71c39a52c05da637822e9c1645a50b7202689e982d"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "der-oid-macro"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd17d13ecf875e704369fdbde242483ac769fc18f6af21e43d5a692a079732fc"
|
||||
checksum = "a4cccf60bb98c0fca115a581f894aed0e43fa55bf289fdac5599bec440bb4fd6"
|
||||
dependencies = [
|
||||
"nom",
|
||||
"num-bigint 0.3.2",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"proc-macro-hack",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "der-parser"
|
||||
version = "5.0.1"
|
||||
version = "5.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13e6cad1223a7b98b59275a56516ed8c40508d21284a32e404ed3fe2ae9a809a"
|
||||
checksum = "120842c2385dea19347e2f6e31caa5dced5ba8afdfacaac16c59465fdd1168f2"
|
||||
dependencies = [
|
||||
"der-oid-macro",
|
||||
"nom",
|
||||
"num-bigint 0.3.2",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"proc-macro-hack",
|
||||
"rusticata-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "des"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b24e7c748888aa2fa8bce21d8c64a52efc810663285315ac7476f7197a982fae"
|
||||
checksum = "ac41dd49fb554432020d52c875fc290e110113f864c6b1b525cd62c7e7747a5d"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"cipher",
|
||||
@@ -221,23 +212,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ecdsa"
|
||||
version = "0.10.2"
|
||||
version = "0.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41fbdb4ff710acb4db8ca29f93b897529ea6d6a45626d5183b47e012aa6ae7e4"
|
||||
checksum = "05cb0ed2d2ce37766ac86c05f66973ace8c51f7f1533bedce8fb79e2b54b3f14"
|
||||
dependencies = [
|
||||
"der",
|
||||
"elliptic-curve",
|
||||
"hmac",
|
||||
"signature",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "elliptic-curve"
|
||||
version = "0.8.5"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2db227e61a43a34915680bdda462ec0e212095518020a88a1f91acd16092c39"
|
||||
checksum = "dd035cb119cbc25e91bb6f1abbfe341388ddb47a1fe5e77ca6bcbe231e87580b"
|
||||
dependencies = [
|
||||
"bitvec 0.18.5",
|
||||
"crypto-bigint",
|
||||
"ff",
|
||||
"funty",
|
||||
"generic-array",
|
||||
"group",
|
||||
"pkcs8",
|
||||
@@ -248,9 +240,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.8.3"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17392a012ea30ef05a610aa97dfb49496e71c9f676b27879922ea5bdf60d9d3f"
|
||||
checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime",
|
||||
@@ -261,11 +253,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ff"
|
||||
version = "0.8.0"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01646e077d4ebda82b73f1bca002ea1e91561a77df2431a9e79729bcc31950ef"
|
||||
checksum = "63eec06c61e487eecf0f7e6e6372e596a81922c28d33e645d6983ca6493a1af0"
|
||||
dependencies = [
|
||||
"bitvec 0.18.5",
|
||||
"rand_core",
|
||||
"subtle",
|
||||
]
|
||||
@@ -288,20 +279,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.16"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
|
||||
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi 0.9.0+wasi-snapshot-preview1",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "group"
|
||||
version = "0.8.0"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc11f9f5fbf1943b48ae7c2bf6846e7d827a512d1be4f23af708f5ca5d01dde1"
|
||||
checksum = "1c363a5301b8f153d80747126a04b3c82073b9fe3130571a9d170cacdeaf7912"
|
||||
dependencies = [
|
||||
"ff",
|
||||
"rand_core",
|
||||
@@ -330,18 +321,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.18"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.10.1"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15"
|
||||
checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b"
|
||||
dependencies = [
|
||||
"crypto-mac",
|
||||
"digest",
|
||||
@@ -353,15 +344,6 @@ version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.49"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc15e39392125075f60c95ba416f5381ff6c3a948ff02ab12464715adf56c821"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
@@ -373,9 +355,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lexical-core"
|
||||
version = "0.7.5"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21f866863575d0e1d654fbeeabdc927292fdf862873dc3c96c6f753357e13374"
|
||||
checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitflags",
|
||||
@@ -386,9 +368,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.90"
|
||||
version = "0.2.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba4aede83fc3617411dc6993bc8c70919750c1c257c6ca6a502aed6e0e2394ae"
|
||||
checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
@@ -413,11 +395,11 @@ checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "6.1.2"
|
||||
version = "6.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2"
|
||||
checksum = "9c5c51b9083a3c620fa67a2a635d1ce7d95b897e957d6b28ff9a5da960a103a6"
|
||||
dependencies = [
|
||||
"bitvec 0.19.5",
|
||||
"bitvec",
|
||||
"funty",
|
||||
"lexical-core",
|
||||
"memchr",
|
||||
@@ -426,20 +408,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.2.6"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304"
|
||||
dependencies = [
|
||||
"autocfg 1.0.1",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d0a3d5e207573f948a9e5376662aa743a2ea13f7c50a554d7af443a73fbfeba"
|
||||
checksum = "4e0d047c1062aa51e256408c560894e5251f08925980e53cf1aa5bd00eec6512"
|
||||
dependencies = [
|
||||
"autocfg 1.0.1",
|
||||
"num-integer",
|
||||
@@ -448,9 +419,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint-dig"
|
||||
version = "0.6.1"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d51546d704f52ef14b3c962b5776e53d5b862e5790e40a350d366c209bd7f7a"
|
||||
checksum = "4547ee5541c18742396ae2c895d0717d0f886d8823b8399cdaf7b07d63ad0480"
|
||||
dependencies = [
|
||||
"autocfg 0.1.7",
|
||||
"byteorder",
|
||||
@@ -493,22 +464,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
||||
dependencies = [
|
||||
"autocfg 1.0.1",
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oid-registry"
|
||||
version = "0.1.1"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2508c8f170e55be68508b1113956a760a82684f42022f8834fb16ca198621211"
|
||||
checksum = "54815ff10e7c65c9b149e677ac20aeaa84e87b3bbcf0bdb5ab0355c568ab47c1"
|
||||
dependencies = [
|
||||
"der-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.7.2"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
|
||||
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
@@ -518,37 +490,38 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||
|
||||
[[package]]
|
||||
name = "p256"
|
||||
version = "0.7.2"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ca0196a204bb3f33305ba4a48b38f6e6e621cba8603a4e0650e6532e0949de4"
|
||||
checksum = "d053368e1bae4c8a672953397bd1bd7183dde1c72b0b7612a15719173148d186"
|
||||
dependencies = [
|
||||
"ecdsa",
|
||||
"elliptic-curve",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "p384"
|
||||
version = "0.6.1"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea33960aac2200d19a5c9ab06a11ebd48a37a23144496632c358182e6765d80b"
|
||||
checksum = "f23bc88c404ccc881c8a1ad62ba5cd7d336a64ecbf46de4874f2ad955f67b157"
|
||||
dependencies = [
|
||||
"elliptic-curve",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pbkdf2"
|
||||
version = "0.7.4"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "297e1dad0e9de7af53235b833761351bf6bda57d6acb4f263b61a2ddf674f1dc"
|
||||
checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa"
|
||||
dependencies = [
|
||||
"crypto-mac",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pcsc"
|
||||
version = "2.4.0"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88e09a8d8705a2c9b1ffe1f9dd9580efe3f8e80c19fc9f99038fe99b7bb56c83"
|
||||
checksum = "d4c82dbce82470474aa850ad01eb104c1b4b06d89ba831ab369f884a8610db18"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"pcsc-sys",
|
||||
@@ -576,11 +549,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pkcs8"
|
||||
version = "0.3.3"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4839a901843f3942576e65857f0ebf2e190ef7024d3c62a94099ba3f819ad1d"
|
||||
checksum = "09d156817ae0125e8aa5067710b0db24f0984830614f99875a70aa5e3b74db69"
|
||||
dependencies = [
|
||||
"der",
|
||||
"spki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -595,17 +569,11 @@ version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.24"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
|
||||
checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
@@ -619,12 +587,6 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "radium"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac"
|
||||
|
||||
[[package]]
|
||||
name = "radium"
|
||||
version = "0.5.3"
|
||||
@@ -633,11 +595,10 @@ checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.7.3"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
||||
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
@@ -646,9 +607,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.2.2"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
@@ -656,27 +617,27 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.2.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
|
||||
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.4.5"
|
||||
version = "1.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19"
|
||||
checksum = "2a26af418b574bd56588335b3a3659a65725d4e636eb1016c2f9e3b38c7cc759"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -685,30 +646,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.23"
|
||||
version = "0.6.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.16.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"spin",
|
||||
"untrusted",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
]
|
||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||
|
||||
[[package]]
|
||||
name = "rsa"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3648b669b10afeab18972c105e284a7b953a669b0be3514c27f9b17acab2f9cd"
|
||||
checksum = "68ef841a26fc5d040ced0417c6c6a64ee851f42489df11cdf0218e545b6f8d28"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"digest",
|
||||
@@ -719,27 +665,25 @@ dependencies = [
|
||||
"num-traits",
|
||||
"pem",
|
||||
"rand",
|
||||
"sha2",
|
||||
"simple_asn1",
|
||||
"subtle",
|
||||
"thiserror",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rusticata-macros"
|
||||
version = "3.0.1"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7390af60e66c44130b4c5ea85f2555b7ace835d73b4b889c704dc3cb4c0468c8"
|
||||
checksum = "d8db3e42c9a4a9479e121c66d4925d15a87734f6fa37f1df0434708718d316ce"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.4"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb5d2a036dc6d2d8fd16fde3498b04306e29bd193bf306a57427019b823d5acd"
|
||||
checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
@@ -758,54 +702,56 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.124"
|
||||
version = "1.0.126"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd761ff957cb2a45fbb9ab3da6512de9de55872866160b23c25f1a841e99d29f"
|
||||
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
|
||||
|
||||
[[package]]
|
||||
name = "sha-1"
|
||||
version = "0.9.4"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfebf75d25bd900fd1e7d11501efab59bc846dbc76196839663e6637bba9f25f"
|
||||
checksum = "8c4cfa741c5832d0ef7fab46cabed29c2aae926db0b11bb2069edd8db5e64e16"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"cfg-if",
|
||||
"cpuid-bool",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.9.3"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa827a14b29ab7f44778d14a88d3cb76e949c45083f7dbfa507d0cb699dc12de"
|
||||
checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"cfg-if",
|
||||
"cpuid-bool",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signature"
|
||||
version = "1.2.2"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29f060a7d147e33490ec10da418795238fd7545bba241504d6b31a409f2e6210"
|
||||
checksum = "c19772be3c4dd2ceaacf03cb41d5885f2a02c4d8804884918e3a258480803335"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simple_asn1"
|
||||
version = "0.4.1"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "692ca13de57ce0613a363c8c2f1de925adebc81b04c923ac60c5488bb44abe4b"
|
||||
checksum = "8eb4ea60fb301dc81dfc113df680571045d375ab7345d171c5dc7d7e13107a80"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"num-bigint 0.2.6",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -820,6 +766,15 @@ version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "987637c5ae6b3121aba9d513f869bd2bff11c4cc086c22473befd6649c0bd521"
|
||||
dependencies = [
|
||||
"der",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
@@ -843,9 +798,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.64"
|
||||
version = "1.0.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f"
|
||||
checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -854,9 +809,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.12.4"
|
||||
version = "0.12.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
|
||||
checksum = "474aaa926faa1603c40b7885a9eaea29b444d1cb2850cb7c0e37bb1a4182f4fa"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -881,18 +836,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.24"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e"
|
||||
checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.24"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0"
|
||||
checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -901,12 +856,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.44"
|
||||
version = "0.1.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
|
||||
checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi 0.10.0+wasi-snapshot-preview1",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
@@ -918,15 +872,18 @@ checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.7.1"
|
||||
name = "uuid"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
@@ -936,79 +893,9 @@ checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.9.0+wasi-snapshot-preview1"
|
||||
version = "0.10.2+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fe8f61dba8e5d645a4d8132dc7a0a66861ed5e1045d2c0ed940fab33bac0fbe"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "046ceba58ff062da072c7cb4ba5b22a37f00a302483f7e2a6cdc18fedbdc1fd3"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ef9aa01d36cda046f797c57959ff5f3c615c9cc63997a8d545831ec7976819b"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96eb45c1b2ee33545a813a92dbb53856418bf7eb54ab34f7f7ff1448a5b3735d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7148f4696fb4960a346eaa60bbfb42a1ac4ebba21f750f75fc1375b098d5ffa"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.49"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59fe19d70f5dacc03f6e46777213facae5ac3801575d56ca6cbd4c93dcd12310"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
@@ -1059,18 +946,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "x509-parser"
|
||||
version = "0.9.1"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db7999ae290e75ec1d4dc8e9ff9870e48e3542a8f2e9c1e2e07d7ca02b459e10"
|
||||
checksum = "64abca276c58f8341ddc13fd4bd6ae75993cc669043f5b34813c90f7dff04771"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"chrono",
|
||||
"data-encoding",
|
||||
"der-oid-macro",
|
||||
"der-parser",
|
||||
"lazy_static",
|
||||
"nom",
|
||||
"num-bigint 0.3.2",
|
||||
"oid-registry",
|
||||
"rusticata-macros",
|
||||
"rustversion",
|
||||
@@ -1078,23 +963,8 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yubikey-cli"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"env_logger",
|
||||
"gumdrop",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"sha2",
|
||||
"subtle-encoding",
|
||||
"termcolor",
|
||||
"x509-parser",
|
||||
"yubikey-piv",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yubikey-piv"
|
||||
version = "0.3.0"
|
||||
name = "yubikey"
|
||||
version = "0.4.2"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"cookie-factory",
|
||||
@@ -1102,7 +972,6 @@ dependencies = [
|
||||
"des",
|
||||
"elliptic-curve",
|
||||
"env_logger",
|
||||
"getrandom",
|
||||
"hmac",
|
||||
"lazy_static",
|
||||
"log",
|
||||
@@ -1114,32 +983,48 @@ dependencies = [
|
||||
"p384",
|
||||
"pbkdf2",
|
||||
"pcsc",
|
||||
"ring",
|
||||
"rand_core",
|
||||
"rsa",
|
||||
"secrecy",
|
||||
"sha-1",
|
||||
"sha2",
|
||||
"subtle",
|
||||
"subtle-encoding",
|
||||
"uuid",
|
||||
"x509",
|
||||
"x509-parser",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yubikey-cli"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"env_logger",
|
||||
"gumdrop",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"sha2",
|
||||
"subtle-encoding",
|
||||
"termcolor",
|
||||
"x509-parser",
|
||||
"yubikey",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.2.0"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81a974bcdd357f0dca4d41677db03436324d45a4c9ed2d0b873a5a360ce41c36"
|
||||
checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd"
|
||||
dependencies = [
|
||||
"zeroize_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize_derive"
|
||||
version = "1.0.1"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3f369ddb18862aba61aa49bf31e74d29f0f162dec753063200e1dc084345d16"
|
||||
checksum = "a2c1e130bebaeab2f23886bf9acbaca14b092408c452543c857f66399cd6dab1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
+19
-21
@@ -1,56 +1,53 @@
|
||||
[package]
|
||||
name = "yubikey-piv"
|
||||
version = "0.3.0" # Also update html_root_url in lib.rs when bumping this
|
||||
name = "yubikey"
|
||||
version = "0.4.2" # Also update html_root_url in lib.rs when bumping this
|
||||
description = """
|
||||
Pure Rust host-side driver for the YubiKey Personal Identity Verification (PIV)
|
||||
application providing general-purpose public-key signing and encryption
|
||||
with hardware-backed private keys for RSA (2048/1024) and ECC (P-256/P-384)
|
||||
algorithms (e.g, PKCS#1v1.5, ECDSA)
|
||||
Pure Rust cross-platform host-side driver for YubiKey devices from Yubico with
|
||||
support for hardware-backed public-key decryption and digital signatures using
|
||||
the Personal Identity Verification (PIV) application. Supports RSA (1024/2048)
|
||||
or ECC (NIST P-256/P-384) algorithms e.g, PKCS#1v1.5, ECDSA
|
||||
"""
|
||||
authors = ["Tony Arcieri <tony@iqlusion.io>", "Yubico AB"]
|
||||
edition = "2018"
|
||||
license = "BSD-2-Clause"
|
||||
repository = "https://github.com/iqlusioninc/yubikey-piv.rs"
|
||||
repository = "https://github.com/iqlusioninc/yubikey.rs"
|
||||
readme = "README.md"
|
||||
categories = ["api-bindings", "cryptography", "hardware-support"]
|
||||
keywords = ["ecdsa", "rsa", "piv", "pcsc", "yubikey"]
|
||||
keywords = ["ecdsa", "encryption", "rsa", "piv", "signature"]
|
||||
|
||||
[workspace]
|
||||
members = [".", "cli"]
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "experimental" }
|
||||
|
||||
[dependencies]
|
||||
chrono = "0.4"
|
||||
cookie-factory = "0.3"
|
||||
der-parser = "5"
|
||||
des = "0.6"
|
||||
elliptic-curve = "0.8"
|
||||
getrandom = "0.1"
|
||||
hmac = "0.10"
|
||||
des = "0.7"
|
||||
elliptic-curve = "0.10"
|
||||
hmac = "0.11"
|
||||
log = "0.4"
|
||||
nom = "6"
|
||||
num-bigint = { version = "0.6", features = ["rand"], package = "num-bigint-dig" }
|
||||
num-bigint-dig = { version = "0.7", features = ["rand"] }
|
||||
num-traits = "0.2"
|
||||
num-integer = "0.1"
|
||||
pbkdf2 = { version = "0.7", default-features = false }
|
||||
p256 = "0.7"
|
||||
p384 = "0.6"
|
||||
pbkdf2 = { version = "0.8", default-features = false }
|
||||
p256 = "0.9"
|
||||
p384 = "0.8"
|
||||
pcsc = "2"
|
||||
rsa = "0.3"
|
||||
rand_core = { version = "0.6", features = ["std"] }
|
||||
rsa = "0.4"
|
||||
secrecy = "0.7"
|
||||
sha-1 = "0.9"
|
||||
sha2 = "0.9"
|
||||
subtle = "2"
|
||||
subtle-encoding = "0.5"
|
||||
uuid = { version = "0.8", features = ["v4"] }
|
||||
x509 = "0.2"
|
||||
x509-parser = "0.9"
|
||||
zeroize = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.8"
|
||||
ring = "0.16.18"
|
||||
lazy_static = "1"
|
||||
|
||||
[features]
|
||||
@@ -58,3 +55,4 @@ untested = []
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
<img src="https://raw.githubusercontent.com/iqlusioninc/yubikey-piv.rs/main/img/logo.png" width="150" height="110">
|
||||
<img src="https://raw.githubusercontent.com/iqlusioninc/yubikey.rs/main/img/logo.png" width="150" height="110">
|
||||
|
||||
# yubikey-piv.rs
|
||||
# yubikey.rs
|
||||
|
||||
[![crate][crate-image]][crate-link]
|
||||
[![Docs][docs-image]][docs-link]
|
||||
[![2-Clause BSD Licensed][license-image]][license-link]
|
||||
![Rust Version][rustc-image]
|
||||
![Maintenance Status: Experimental][maintenance-image]
|
||||
[![Safety Dance][safety-image]][safety-link]
|
||||
[![Build Status][build-image]][build-link]
|
||||
[![Gitter Chat][gitter-image]][gitter-link]
|
||||
|
||||
Pure Rust host-side YubiKey [Personal Identity Verification (PIV)][PIV] driver
|
||||
with general-purpose public-key encryption and signing support.
|
||||
Pure Rust cross-platform host-side driver for [YubiKey] devices from [Yubico]
|
||||
with support for public-key encryption and digital signatures using the
|
||||
[Personal Identity Verification (PIV)][PIV] application.
|
||||
|
||||
[Documentation][docs-link]
|
||||
|
||||
@@ -36,7 +35,7 @@ endorsed by Yubico.
|
||||
|
||||
## Minimum Supported Rust Version
|
||||
|
||||
- Rust **1.46** or newer
|
||||
Rust **1.51** or newer.
|
||||
|
||||
## Supported YubiKeys
|
||||
|
||||
@@ -46,6 +45,12 @@ endorsed by Yubico.
|
||||
NOTE: Nano and USB-C variants of the above are also supported.
|
||||
Pre-YK4 [YubiKey NEO] series is **NOT** supported (see [#18]).
|
||||
|
||||
## Supported Operating Systems
|
||||
|
||||
- Linux
|
||||
- macOS
|
||||
- Windows
|
||||
|
||||
## Security Warning
|
||||
|
||||
No security audits of this crate have ever been performed. Presently it is in
|
||||
@@ -53,41 +58,6 @@ an experimental stage and may still contain high-severity issues.
|
||||
|
||||
USE AT YOUR OWN RISK!
|
||||
|
||||
## Status
|
||||
|
||||
This project is a largely incomplete work-in-progress. So far the only
|
||||
functionality which has actually been tested is connecting to Yubikeys.
|
||||
|
||||
If you're interested helping test functionality, the table below documents
|
||||
the current status of the project and relevant GitHub issues for various
|
||||
functions of the YubiKey:
|
||||
|
||||
| | Module | Issue | Description |
|
||||
|----|---------------|-------|-------------|
|
||||
| 🚧 | `yubikey` | [#20] | Core functionality: auth, keys, PIN/PUK, encrypt, sign, attest |
|
||||
| 🚧 | `cccid` | [#21] | Cardholder Capability Container (CCC) IDs |
|
||||
| 🚧️ | `certificate` | [#22] | Certificates for stored keys |
|
||||
| 🚧 | `chuid` | [#23] | Cardholder Unique Identifier (CHUID) |
|
||||
| ✅️ | `config` | [#24] | Support for reading on-key configuration |
|
||||
| 🚧 | `key` | [#26] | Crypto key management: list, generate, import |
|
||||
| 🚧 | `mgm` | [#26] | Management Key (MGM) support: set, get, derive |
|
||||
| ⚠️ | `mscmap` | [#25] | MS Container Map Records |
|
||||
| ⚠️ | `msroots` | [#28] | `msroots` file: PKCS#7 formatted certificate store for enterprise trusted roots |
|
||||
|
||||
Legend:
|
||||
|
||||
| | Description |
|
||||
|----|------------------------------------|
|
||||
| ✅ | Working |
|
||||
| 🚧 | Testing and validation in progress |
|
||||
| ⚠️ | Untested support |
|
||||
|
||||
NOTE: Commands marked ⚠️ are disabled by default as they have have not been properly tested and may contain bugs or
|
||||
not work at all. USE AT YOUR OWN RISK!
|
||||
|
||||
Enable the `untested` feature in your `Cargo.toml` to enable features marked ⚠️
|
||||
above.
|
||||
|
||||
## Testing
|
||||
|
||||
To run the full test suite, you'll need a connected YubiKey NEO/4/5 device in
|
||||
@@ -115,17 +85,17 @@ Application Protocol Data Unit (APDU) messages, use the `trace` log level:
|
||||
|
||||
```
|
||||
running 1 test
|
||||
[INFO yubikey_piv::yubikey] trying to connect to reader 'Yubico YubiKey OTP+FIDO+CCID'
|
||||
[INFO yubikey_piv::yubikey] connected to 'Yubico YubiKey OTP+FIDO+CCID' successfully
|
||||
[TRACE yubikey_piv::apdu] >>> APDU { cla: 0, ins: SelectApplication, p1: 4, p2: 0, data: [160, 0, 0, 3, 8] }
|
||||
[TRACE yubikey_piv::transaction] >>> [0, 164, 4, 0, 5, 160, 0, 0, 3, 8]
|
||||
[TRACE yubikey_piv::apdu] <<< Response { status_words: Success, data: [97, 17, 79, 6, 0, 0, 16, 0, 1, 0, 121, 7, 79, 5, 160, 0, 0, 3, 8] }
|
||||
[TRACE yubikey_piv::apdu] >>> APDU { cla: 0, ins: GetVersion, p1: 0, p2: 0, data: [] }
|
||||
[TRACE yubikey_piv::transaction] >>> [0, 253, 0, 0, 0]
|
||||
[TRACE yubikey_piv::apdu] <<< Response { status_words: Success, data: [5, 1, 2] }
|
||||
[TRACE yubikey_piv::apdu] >>> APDU { cla: 0, ins: GetSerial, p1: 0, p2: 0, data: [] }
|
||||
[TRACE yubikey_piv::transaction] >>> [0, 248, 0, 0, 0]
|
||||
[TRACE yubikey_piv::apdu] <<< Response { status_words: Success, data: [0, 115, 0, 178] }
|
||||
[INFO yubikey::yubikey] trying to connect to reader 'Yubico YubiKey OTP+FIDO+CCID'
|
||||
[INFO yubikey::yubikey] connected to 'Yubico YubiKey OTP+FIDO+CCID' successfully
|
||||
[TRACE yubikey::apdu] >>> Apdu { cla: 0, ins: SelectApplication, p1: 4, p2: 0, data: [160, 0, 0, 3, 8] }
|
||||
[TRACE yubikey::transaction] >>> [0, 164, 4, 0, 5, 160, 0, 0, 3, 8]
|
||||
[TRACE yubikey::apdu] <<< Response { status_words: Success, data: [97, 17, 79, 6, 0, 0, 16, 0, 1, 0, 121, 7, 79, 5, 160, 0, 0, 3, 8] }
|
||||
[TRACE yubikey::apdu] >>> Apdu { cla: 0, ins: GetVersion, p1: 0, p2: 0, data: [] }
|
||||
[TRACE yubikey::transaction] >>> [0, 253, 0, 0, 0]
|
||||
[TRACE yubikey::apdu] <<< Response { status_words: Success, data: [5, 1, 2] }
|
||||
[TRACE yubikey::apdu] >>> Apdu { cla: 0, ins: GetSerial, p1: 0, p2: 0, data: [] }
|
||||
[TRACE yubikey::transaction] >>> [0, 248, 0, 0, 0]
|
||||
[TRACE yubikey::apdu] <<< Response { status_words: Success, data: [0, 115, 0, 178] }
|
||||
test connect ... ok
|
||||
```
|
||||
|
||||
@@ -147,12 +117,12 @@ For more information, please see [CODE_OF_CONDUCT.md][cc-md].
|
||||
|
||||
## License
|
||||
|
||||
**yubikey-piv.rs** is a fork of and originally a mechanical translation from
|
||||
**yubikey.rs** is a fork of and originally a mechanical translation from
|
||||
Yubico's [yubico-piv-tool], a C library/CLI program. The original library
|
||||
was licensed under a [2-Clause BSD License][BSDL], which this library inherits
|
||||
as a derived work.
|
||||
|
||||
Copyright (c) 2014-2020 Yubico AB, Tony Arcieri
|
||||
Copyright (c) 2014-2021 Yubico AB, Tony Arcieri
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -188,23 +158,21 @@ or conditions.
|
||||
|
||||
[//]: # (badges)
|
||||
|
||||
[crate-image]: https://img.shields.io/crates/v/yubikey-piv.svg
|
||||
[crate-link]: https://crates.io/crates/yubikey-piv
|
||||
[docs-image]: https://docs.rs/yubikey-piv/badge.svg
|
||||
[docs-link]: https://docs.rs/yubikey-piv/
|
||||
[crate-image]: https://img.shields.io/crates/v/yubikey.svg
|
||||
[crate-link]: https://crates.io/crates/yubikey
|
||||
[docs-image]: https://docs.rs/yubikey/badge.svg
|
||||
[docs-link]: https://docs.rs/yubikey/
|
||||
[license-image]: https://img.shields.io/badge/license-BSD-blue.svg
|
||||
[license-link]: https://github.com/iqlusioninc/yubikey-piv.rs/blob/main/COPYING
|
||||
[rustc-image]: https://img.shields.io/badge/rustc-1.46+-blue.svg
|
||||
[maintenance-image]: https://img.shields.io/badge/maintenance-experimental-blue.svg
|
||||
[license-link]: https://github.com/iqlusioninc/yubikey.rs/blob/main/COPYING
|
||||
[rustc-image]: https://img.shields.io/badge/rustc-1.51+-blue.svg
|
||||
[safety-image]: https://img.shields.io/badge/unsafe-forbidden-success.svg
|
||||
[safety-link]: https://github.com/rust-secure-code/safety-dance/
|
||||
[build-image]: https://github.com/iqlusioninc/yubikey-piv.rs/workflows/CI/badge.svg?branch=main&event=push
|
||||
[build-link]: https://github.com/iqlusioninc/yubikey-piv.rs/actions
|
||||
[gitter-image]: https://badges.gitter.im/badge.svg
|
||||
[gitter-link]: https://gitter.im/iqlusioninc/community
|
||||
[build-image]: https://github.com/iqlusioninc/yubikey.rs/workflows/CI/badge.svg?branch=main&event=push
|
||||
[build-link]: https://github.com/iqlusioninc/yubikey.rs/actions
|
||||
|
||||
[//]: # (general links)
|
||||
|
||||
[YubiKey]: https://www.yubico.com/products/yubikey-hardware/
|
||||
[PIV]: https://piv.idmanagement.gov/
|
||||
[yk-guide]: https://developers.yubico.com/PIV/Introduction/YubiKey_and_PIV.html
|
||||
[Yubico]: https://www.yubico.com/
|
||||
@@ -214,18 +182,18 @@ or conditions.
|
||||
[yubico-piv-tool]: https://github.com/Yubico/yubico-piv-tool/
|
||||
[Corrode]: https://github.com/jameysharp/corrode
|
||||
[cc-web]: https://contributor-covenant.org/
|
||||
[cc-md]: https://github.com/iqlusioninc/yubikey-piv.rs/blob/main/CODE_OF_CONDUCT.md
|
||||
[cc-md]: https://github.com/iqlusioninc/yubikey.rs/blob/main/CODE_OF_CONDUCT.md
|
||||
[BSDL]: https://opensource.org/licenses/BSD-2-Clause
|
||||
|
||||
[//]: # (github issues)
|
||||
|
||||
[#18]: https://github.com/iqlusioninc/yubikey-piv.rs/issues/18
|
||||
[#20]: https://github.com/iqlusioninc/yubikey-piv.rs/issues/20
|
||||
[#21]: https://github.com/iqlusioninc/yubikey-piv.rs/issues/21
|
||||
[#22]: https://github.com/iqlusioninc/yubikey-piv.rs/issues/22
|
||||
[#23]: https://github.com/iqlusioninc/yubikey-piv.rs/issues/23
|
||||
[#24]: https://github.com/iqlusioninc/yubikey-piv.rs/issues/24
|
||||
[#25]: https://github.com/iqlusioninc/yubikey-piv.rs/issues/25
|
||||
[#26]: https://github.com/iqlusioninc/yubikey-piv.rs/issues/26
|
||||
[#27]: https://github.com/iqlusioninc/yubikey-piv.rs/issues/27
|
||||
[#28]: https://github.com/iqlusioninc/yubikey-piv.rs/issues/28
|
||||
[#18]: https://github.com/iqlusioninc/yubikey.rs/issues/18
|
||||
[#20]: https://github.com/iqlusioninc/yubikey.rs/issues/20
|
||||
[#21]: https://github.com/iqlusioninc/yubikey.rs/issues/21
|
||||
[#22]: https://github.com/iqlusioninc/yubikey.rs/issues/22
|
||||
[#23]: https://github.com/iqlusioninc/yubikey.rs/issues/23
|
||||
[#24]: https://github.com/iqlusioninc/yubikey.rs/issues/24
|
||||
[#25]: https://github.com/iqlusioninc/yubikey.rs/issues/25
|
||||
[#26]: https://github.com/iqlusioninc/yubikey.rs/issues/26
|
||||
[#27]: https://github.com/iqlusioninc/yubikey.rs/issues/27
|
||||
[#28]: https://github.com/iqlusioninc/yubikey.rs/issues/28
|
||||
|
||||
+22
-9
@@ -4,13 +4,26 @@ 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.4.0 (2021-07-12)
|
||||
### Changed
|
||||
- Switch to renamed `yubikey` crate ([#283])
|
||||
- Bump MSRV to 1.51+ ([#283])
|
||||
|
||||
[#283]: https://github.com/iqlusioninc/yubikey.rs/pull/283
|
||||
|
||||
## 0.3.0 (2021-03-22)
|
||||
### Changed
|
||||
- Bump `yubikey-piv` dependency to v0.3 ([#240])
|
||||
|
||||
[#240]: https://github.com/iqlusioninc/yubikey.rs/pull/240
|
||||
|
||||
## 0.2.0 (2021-01-30)
|
||||
### Changed
|
||||
- Bump MSRV to 1.46+ ([#208])
|
||||
- Bump `yubikey-piv` dependency to v0.2.0 ([#220])
|
||||
- Bump `yubikey-piv` dependency to v0.2 ([#220])
|
||||
|
||||
[#208]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/208
|
||||
[#220]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/220
|
||||
[#208]: https://github.com/iqlusioninc/yubikey.rs/pull/208
|
||||
[#220]: https://github.com/iqlusioninc/yubikey.rs/pull/220
|
||||
|
||||
## 0.1.0 (2020-10-19)
|
||||
### Added
|
||||
@@ -22,12 +35,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Bump `sha2` to v0.9 ([#182])
|
||||
- Rename `list` command to `readers`; improve usage ([#71])
|
||||
|
||||
[#182]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/182
|
||||
[#181]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/181
|
||||
[#180]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/180
|
||||
[#74]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/74
|
||||
[#72]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/72
|
||||
[#71]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/71
|
||||
[#182]: https://github.com/iqlusioninc/yubikey.rs/pull/182
|
||||
[#181]: https://github.com/iqlusioninc/yubikey.rs/pull/181
|
||||
[#180]: https://github.com/iqlusioninc/yubikey.rs/pull/180
|
||||
[#74]: https://github.com/iqlusioninc/yubikey.rs/pull/74
|
||||
[#72]: https://github.com/iqlusioninc/yubikey.rs/pull/72
|
||||
[#71]: https://github.com/iqlusioninc/yubikey.rs/pull/71
|
||||
|
||||
## 0.0.1 (2019-12-02)
|
||||
- Initial release
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "yubikey-cli"
|
||||
version = "0.2.0"
|
||||
version = "0.4.0"
|
||||
description = """
|
||||
Command-line interface for performing encryption and signing using RSA/ECC keys
|
||||
stored on YubiKey devices.
|
||||
@@ -8,7 +8,7 @@ stored on YubiKey devices.
|
||||
authors = ["Tony Arcieri <tony@iqlusion.io>"]
|
||||
edition = "2018"
|
||||
license = "BSD-2-Clause"
|
||||
repository = "https://github.com/iqlusioninc/yubikey-piv.rs"
|
||||
repository = "https://github.com/iqlusioninc/yubikey.rs"
|
||||
readme = "README.md"
|
||||
categories = ["command-line-utilities", "cryptography", "hardware-support"]
|
||||
keywords = ["ecdsa", "rsa", "piv", "pcsc", "yubikey"]
|
||||
@@ -22,4 +22,4 @@ sha2 = "0.9"
|
||||
subtle-encoding = "0.5"
|
||||
termcolor = "1"
|
||||
x509-parser = "0.9"
|
||||
yubikey-piv = { version = "0.3", path = ".." }
|
||||
yubikey = { version = "0.4", path = ".." }
|
||||
|
||||
+5
-5
@@ -1,4 +1,4 @@
|
||||
<img src="https://raw.githubusercontent.com/iqlusioninc/yubikey-piv.rs/main/img/logo.png" width="150" height="110">
|
||||
<img src="https://raw.githubusercontent.com/iqlusioninc/yubikey.rs/main/img/logo.png" width="150" height="110">
|
||||
|
||||
# yubikey-cli.rs
|
||||
|
||||
@@ -18,7 +18,7 @@ utility with general-purpose public-key encryption and signing support.
|
||||
|
||||
## Minimum Supported Rust Version
|
||||
|
||||
- Rust **1.39+**
|
||||
Rust **1.51** or newer.
|
||||
|
||||
## Supported YubiKeys
|
||||
|
||||
@@ -47,7 +47,7 @@ For more information, please see [CODE_OF_CONDUCT.md][cc-md].
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2014-2019 Yubico AB, Tony Arcieri
|
||||
Copyright (c) 2014-2021 Yubico AB, Tony Arcieri
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -92,8 +92,8 @@ or conditions.
|
||||
[maintenance-image]: https://img.shields.io/badge/maintenance-experimental-blue.svg
|
||||
[safety-image]: https://img.shields.io/badge/unsafe-forbidden-success.svg
|
||||
[safety-link]: https://github.com/rust-secure-code/safety-dance/
|
||||
[build-image]: https://github.com/iqlusioninc/yubikey-piv.rs/workflows/CI/badge.svg?branch=main&event=push
|
||||
[build-link]: https://github.com/iqlusioninc/yubikey-piv.rs/actions
|
||||
[build-image]: https://github.com/iqlusioninc/yubikey.rs/workflows/CI/badge.svg?branch=main&event=push
|
||||
[build-link]: https://github.com/iqlusioninc/yubikey.rs/actions
|
||||
[gitter-image]: https://badges.gitter.im/badge.svg
|
||||
[gitter-link]: https://gitter.im/iqlusioninc/community
|
||||
|
||||
|
||||
+2
-2
@@ -12,7 +12,7 @@ use std::{
|
||||
process::exit,
|
||||
};
|
||||
use termcolor::{ColorChoice, ColorSpec, WriteColor};
|
||||
use yubikey_piv::{Serial, YubiKey};
|
||||
use yubikey::{Serial, YubiKey};
|
||||
|
||||
/// The `yubikey` CLI utility
|
||||
#[derive(Debug, Options)]
|
||||
@@ -36,7 +36,7 @@ pub struct YubiKeyCli {
|
||||
|
||||
impl YubiKeyCli {
|
||||
/// Print usage information
|
||||
pub fn print_usage() -> Result<(), io::Error> {
|
||||
pub fn print_usage() -> io::Result<()> {
|
||||
let mut stdout = STDOUT.lock();
|
||||
stdout.reset()?;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ use std::{
|
||||
process::exit,
|
||||
};
|
||||
use termcolor::{ColorSpec, StandardStreamLock, WriteColor};
|
||||
use yubikey_piv::{Readers, Serial};
|
||||
use yubikey::{Context, Serial};
|
||||
|
||||
/// The `readers` subcommand
|
||||
#[derive(Debug, Options)]
|
||||
@@ -16,7 +16,7 @@ pub struct ReadersCmd {}
|
||||
impl ReadersCmd {
|
||||
/// Run the `readers` subcommand
|
||||
pub fn run(&self) {
|
||||
let mut readers = Readers::open().unwrap_or_else(|e| {
|
||||
let mut readers = Context::open().unwrap_or_else(|e| {
|
||||
status_err!("couldn't open PC/SC context: {}", e);
|
||||
exit(1);
|
||||
});
|
||||
@@ -53,7 +53,7 @@ impl ReadersCmd {
|
||||
index: usize,
|
||||
name: &str,
|
||||
serial: Serial,
|
||||
) -> Result<(), io::Error> {
|
||||
) -> io::Result<()> {
|
||||
stream.set_color(ColorSpec::new().set_bold(true))?;
|
||||
write!(stream, "{:>3}:", index)?;
|
||||
stream.reset()?;
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
//! Print device status
|
||||
|
||||
use crate::terminal::STDOUT;
|
||||
use crate::terminal::{print_cert_info, STDOUT};
|
||||
use gumdrop::Options;
|
||||
use std::io::{self, Write};
|
||||
use termcolor::{ColorSpec, StandardStreamLock, WriteColor};
|
||||
use yubikey_piv::{key::*, YubiKey};
|
||||
|
||||
use crate::print_cert_info;
|
||||
use yubikey::{piv::*, YubiKey};
|
||||
|
||||
// String to use for `None`
|
||||
const NONE_STR: &str = "<none>";
|
||||
@@ -51,7 +49,7 @@ impl StatusCmd {
|
||||
stream: &mut StandardStreamLock<'_>,
|
||||
name: &str,
|
||||
value: impl ToString,
|
||||
) -> Result<(), io::Error> {
|
||||
) -> io::Result<()> {
|
||||
stream.set_color(ColorSpec::new().set_bold(true))?;
|
||||
write!(stream, "{:>12}:", name)?;
|
||||
stream.reset()?;
|
||||
|
||||
+7
-86
@@ -1,92 +1,13 @@
|
||||
//! `yubikey` command-line utility
|
||||
//! `yubikey` command-line utility.
|
||||
//!
|
||||
//! The goal of this tool is to provide functionality similar to `yubico-piv-tool`
|
||||
//! but implemented in pure Rust.
|
||||
//!
|
||||
//! It also serves as a demonstration/example of how to use the `yubikey` crate.
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![warn(
|
||||
missing_docs,
|
||||
rust_2018_idioms,
|
||||
unused_lifetimes,
|
||||
unused_qualifications
|
||||
)]
|
||||
#![warn(missing_docs, rust_2018_idioms, unused_qualifications)]
|
||||
|
||||
#[macro_use]
|
||||
pub mod terminal;
|
||||
|
||||
pub mod commands;
|
||||
|
||||
use log::debug;
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::io::{self, Write};
|
||||
use std::str;
|
||||
use subtle_encoding::hex;
|
||||
use termcolor::{ColorSpec, StandardStreamLock, WriteColor};
|
||||
use x509_parser::parse_x509_certificate;
|
||||
use yubikey_piv::{certificate::Certificate, key::*, YubiKey};
|
||||
|
||||
///Write information about certificate found in slot a la yubico-piv-tool output.
|
||||
pub fn print_cert_info(
|
||||
yubikey: &mut YubiKey,
|
||||
slot: SlotId,
|
||||
stream: &mut StandardStreamLock<'_>,
|
||||
) -> Result<(), io::Error> {
|
||||
let cert = match Certificate::read(yubikey, slot) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
debug!("error reading certificate in slot {:?}: {}", slot, e);
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let buf = cert.into_buffer();
|
||||
|
||||
if !buf.is_empty() {
|
||||
let fingerprint = Sha256::digest(&buf);
|
||||
let slot_id: u8 = slot.into();
|
||||
print_cert_attr(stream, "Slot", format!("{:x}", slot_id))?;
|
||||
match parse_x509_certificate(&buf) {
|
||||
Ok((_rem, cert)) => {
|
||||
print_cert_attr(
|
||||
stream,
|
||||
"Algorithm",
|
||||
cert.tbs_certificate.subject_pki.algorithm.algorithm,
|
||||
)?;
|
||||
|
||||
print_cert_attr(stream, "Subject", cert.tbs_certificate.subject)?;
|
||||
print_cert_attr(stream, "Issuer", cert.tbs_certificate.issuer)?;
|
||||
print_cert_attr(
|
||||
stream,
|
||||
"Fingerprint",
|
||||
str::from_utf8(hex::encode(fingerprint).as_slice()).unwrap(),
|
||||
)?;
|
||||
print_cert_attr(
|
||||
stream,
|
||||
"Not Before",
|
||||
cert.tbs_certificate.validity.not_before.to_rfc2822(),
|
||||
)?;
|
||||
print_cert_attr(
|
||||
stream,
|
||||
"Not After",
|
||||
cert.tbs_certificate.validity.not_after.to_rfc2822(),
|
||||
)?;
|
||||
}
|
||||
_ => {
|
||||
println!("Failed to parse certificate");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Print a status attribute
|
||||
fn print_cert_attr(
|
||||
stream: &mut StandardStreamLock<'_>,
|
||||
name: &str,
|
||||
value: impl ToString,
|
||||
) -> Result<(), io::Error> {
|
||||
stream.set_color(ColorSpec::new().set_bold(true))?;
|
||||
write!(stream, "{:>12}:", name)?;
|
||||
stream.reset()?;
|
||||
writeln!(stream, " {}", value.to_string())?;
|
||||
stream.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+81
-4
@@ -1,9 +1,17 @@
|
||||
//! Status messages
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use std::io::{self, Write};
|
||||
use std::sync::Mutex;
|
||||
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
|
||||
use log::debug;
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::{
|
||||
io::{self, Write},
|
||||
str,
|
||||
sync::Mutex,
|
||||
};
|
||||
use subtle_encoding::hex;
|
||||
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, StandardStreamLock, WriteColor};
|
||||
use x509_parser::parse_x509_certificate;
|
||||
use yubikey::{certificate::Certificate, piv::*, YubiKey};
|
||||
|
||||
/// Print a success status message (in green if colors are enabled)
|
||||
#[macro_export]
|
||||
@@ -143,7 +151,7 @@ impl Status {
|
||||
}
|
||||
|
||||
/// Print the given message
|
||||
fn print(self, stream: &StandardStream, msg: impl AsRef<str>) -> Result<(), io::Error> {
|
||||
fn print(self, stream: &StandardStream, msg: impl AsRef<str>) -> io::Result<()> {
|
||||
let mut s = stream.lock();
|
||||
s.reset()?;
|
||||
s.set_color(ColorSpec::new().set_fg(self.color).set_bold(self.bold))?;
|
||||
@@ -163,3 +171,72 @@ impl Status {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Write information about certificate found in slot a la yubico-piv-tool output.
|
||||
pub fn print_cert_info(
|
||||
yubikey: &mut YubiKey,
|
||||
slot: SlotId,
|
||||
stream: &mut StandardStreamLock<'_>,
|
||||
) -> io::Result<()> {
|
||||
let cert = match Certificate::read(yubikey, slot) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
debug!("error reading certificate in slot {:?}: {}", slot, e);
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let buf = cert.into_buffer();
|
||||
|
||||
if !buf.is_empty() {
|
||||
let fingerprint = Sha256::digest(&buf);
|
||||
let slot_id: u8 = slot.into();
|
||||
print_cert_attr(stream, "Slot", format!("{:x}", slot_id))?;
|
||||
match parse_x509_certificate(&buf) {
|
||||
Ok((_rem, cert)) => {
|
||||
print_cert_attr(
|
||||
stream,
|
||||
"Algorithm",
|
||||
cert.tbs_certificate.subject_pki.algorithm.algorithm,
|
||||
)?;
|
||||
|
||||
print_cert_attr(stream, "Subject", cert.tbs_certificate.subject)?;
|
||||
print_cert_attr(stream, "Issuer", cert.tbs_certificate.issuer)?;
|
||||
print_cert_attr(
|
||||
stream,
|
||||
"Fingerprint",
|
||||
str::from_utf8(hex::encode(fingerprint).as_slice()).unwrap(),
|
||||
)?;
|
||||
print_cert_attr(
|
||||
stream,
|
||||
"Not Before",
|
||||
cert.tbs_certificate.validity.not_before.to_rfc2822(),
|
||||
)?;
|
||||
print_cert_attr(
|
||||
stream,
|
||||
"Not After",
|
||||
cert.tbs_certificate.validity.not_after.to_rfc2822(),
|
||||
)?;
|
||||
}
|
||||
_ => {
|
||||
println!("Failed to parse certificate");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Print a status attribute
|
||||
fn print_cert_attr(
|
||||
stream: &mut StandardStreamLock<'_>,
|
||||
name: &str,
|
||||
value: impl ToString,
|
||||
) -> io::Result<()> {
|
||||
stream.set_color(ColorSpec::new().set_bold(true))?;
|
||||
write!(stream, "{:>12}:", name)?;
|
||||
stream.reset()?;
|
||||
writeln!(stream, " {}", value.to_string())?;
|
||||
stream.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+6
-6
@@ -30,7 +30,7 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
use crate::{error::Error, transaction::Transaction, Buffer};
|
||||
use crate::{transaction::Transaction, Buffer, Result};
|
||||
use log::trace;
|
||||
use zeroize::{Zeroize, Zeroizing};
|
||||
|
||||
@@ -41,7 +41,7 @@ const APDU_DATA_MAX: usize = 0xFF;
|
||||
///
|
||||
/// These messages are packets used to communicate with the YubiKey.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub(crate) struct APDU {
|
||||
pub(crate) struct Apdu {
|
||||
/// Instruction class: indicates the type of command (e.g. inter-industry or proprietary)
|
||||
cla: u8,
|
||||
|
||||
@@ -58,7 +58,7 @@ pub(crate) struct APDU {
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl APDU {
|
||||
impl Apdu {
|
||||
/// Create a new APDU with the given instruction code
|
||||
pub fn new(ins: impl Into<Ins>) -> Self {
|
||||
Self {
|
||||
@@ -109,7 +109,7 @@ impl APDU {
|
||||
}
|
||||
|
||||
/// Transmit this APDU using the given card transaction
|
||||
pub fn transmit(&self, txn: &Transaction<'_>, recv_len: usize) -> Result<Response, Error> {
|
||||
pub fn transmit(&self, txn: &Transaction<'_>, recv_len: usize) -> Result<Response> {
|
||||
trace!(">>> {:?}", self);
|
||||
let response = Response::from(txn.transmit(&self.to_bytes(), recv_len)?);
|
||||
trace!("<<< {:?}", &response);
|
||||
@@ -129,13 +129,13 @@ impl APDU {
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for APDU {
|
||||
impl Drop for Apdu {
|
||||
fn drop(&mut self) {
|
||||
self.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl Zeroize for APDU {
|
||||
impl Zeroize for Apdu {
|
||||
fn zeroize(&mut self) {
|
||||
// Only `data` may contain secrets
|
||||
self.data.zeroize();
|
||||
|
||||
+34
-41
@@ -30,17 +30,15 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
use crate::{error::Error, yubikey::YubiKey};
|
||||
use getrandom::getrandom;
|
||||
use std::fmt::{self, Debug, Display};
|
||||
use crate::{Error, Result, YubiKey};
|
||||
use rand_core::{OsRng, RngCore};
|
||||
use std::{
|
||||
convert::TryInto,
|
||||
fmt::{self, Debug, Display},
|
||||
str,
|
||||
};
|
||||
use subtle_encoding::hex;
|
||||
|
||||
/// CCCID size
|
||||
pub const CCCID_SIZE: usize = 14;
|
||||
|
||||
/// CCC size
|
||||
pub const CCC_SIZE: usize = 51;
|
||||
|
||||
/// CCCID offset
|
||||
const CCC_ID_OFFS: usize = 9;
|
||||
|
||||
@@ -62,33 +60,39 @@ const CCC_TMPL: &[u8] = &[
|
||||
0x00, 0xfe, 0x00,
|
||||
];
|
||||
|
||||
/// Cardholder Capability Container (CCC) Identifier Card ID
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct CardId(pub [u8; CCCID_SIZE]);
|
||||
/// Cardholder Capability Container (CCC) Identifier Card ID.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub struct CardId(pub [u8; Self::BYTE_SIZE]);
|
||||
|
||||
impl CardId {
|
||||
/// CCCID size in bytes
|
||||
pub const BYTE_SIZE: usize = 14;
|
||||
|
||||
/// Generate a random CCC Card ID
|
||||
pub fn generate() -> Result<Self, Error> {
|
||||
let mut id = [0u8; CCCID_SIZE];
|
||||
getrandom(&mut id).map_err(|_| Error::RandomnessError)?;
|
||||
Ok(Self(id))
|
||||
pub fn generate() -> Self {
|
||||
let mut id = [0u8; Self::BYTE_SIZE];
|
||||
OsRng.fill_bytes(&mut id);
|
||||
Self(id)
|
||||
}
|
||||
}
|
||||
|
||||
/// Cardholder Capability Container (CCC) Identifier
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct CCC(pub [u8; CCC_SIZE]);
|
||||
/// Cardholder Capability Container (CCC) Identifier.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct CccId(pub [u8; Self::BYTE_SIZE]);
|
||||
|
||||
impl CccId {
|
||||
/// CCC size in bytes
|
||||
pub const BYTE_SIZE: usize = 51;
|
||||
|
||||
impl CCC {
|
||||
/// Return CardId component of CCC
|
||||
pub fn cccid(&self) -> Result<CardId, Error> {
|
||||
let mut cccid = [0u8; CCCID_SIZE];
|
||||
cccid.copy_from_slice(&self.0[CCC_ID_OFFS..(CCC_ID_OFFS + CCCID_SIZE)]);
|
||||
pub fn card_id(&self) -> Result<CardId> {
|
||||
let mut cccid = [0u8; CardId::BYTE_SIZE];
|
||||
cccid.copy_from_slice(&self.0[CCC_ID_OFFS..(CCC_ID_OFFS + CardId::BYTE_SIZE)]);
|
||||
Ok(CardId(cccid))
|
||||
}
|
||||
|
||||
/// Get Cardholder Capability Container (CCC) ID
|
||||
pub fn get(yubikey: &mut YubiKey) -> Result<Self, Error> {
|
||||
pub fn get(yubikey: &mut YubiKey) -> Result<Self> {
|
||||
let txn = yubikey.begin_transaction()?;
|
||||
let response = txn.fetch_object(OBJ_CAPABILITY)?;
|
||||
|
||||
@@ -96,14 +100,13 @@ impl CCC {
|
||||
return Err(Error::GenericError);
|
||||
}
|
||||
|
||||
let mut ccc = [0u8; CCC_SIZE];
|
||||
ccc.copy_from_slice(&response[0..CCC_SIZE]);
|
||||
Ok(Self(ccc))
|
||||
Ok(Self(response[..Self::BYTE_SIZE].try_into().unwrap()))
|
||||
}
|
||||
|
||||
/// Get Cardholder Capability Container (CCC) ID
|
||||
/// Set Cardholder Capability Container (CCC) ID
|
||||
#[cfg(feature = "untested")]
|
||||
pub fn set(&self, yubikey: &mut YubiKey) -> Result<(), Error> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub fn set(&self, yubikey: &mut YubiKey) -> Result<()> {
|
||||
let mut buf = CCC_TMPL.to_vec();
|
||||
buf[0..self.0.len()].copy_from_slice(&self.0);
|
||||
|
||||
@@ -112,18 +115,8 @@ impl CCC {
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for CCC {
|
||||
impl Display for CccId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "CCC({:?})", &self.0[..])
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for CCC {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
String::from_utf8(hex::encode(&self.0[..])).unwrap()
|
||||
)
|
||||
f.write_str(str::from_utf8(&hex::encode(&self.0[..])).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
+22
-27
@@ -1,4 +1,4 @@
|
||||
//! YubiKey Certificates
|
||||
//! X.509 certificate support.
|
||||
|
||||
// Adapted from yubico-piv-tool:
|
||||
// <https://github.com/Yubico/yubico-piv-tool/>
|
||||
@@ -31,8 +31,9 @@
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
use crate::{
|
||||
error::Error,
|
||||
key::{sign_data, AlgorithmId, SlotId},
|
||||
consts::CB_OBJ_MAX,
|
||||
error::{Error, Result},
|
||||
piv::{sign_data, AlgorithmId, SlotId},
|
||||
serialization::*,
|
||||
transaction::Transaction,
|
||||
yubikey::YubiKey,
|
||||
@@ -41,7 +42,7 @@ use crate::{
|
||||
use chrono::{DateTime, Utc};
|
||||
use elliptic_curve::sec1::EncodedPoint as EcPublicKey;
|
||||
use log::error;
|
||||
use num_bigint::BigUint;
|
||||
use num_bigint_dig::BigUint;
|
||||
use p256::NistP256;
|
||||
use p384::NistP384;
|
||||
use rsa::{PublicKeyParts, RSAPublicKey};
|
||||
@@ -53,8 +54,6 @@ use x509::{der::Oid, RelativeDistinguishedName};
|
||||
use x509_parser::{parse_x509_certificate, x509::SubjectPublicKeyInfo};
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
use crate::CB_OBJ_MAX;
|
||||
|
||||
// TODO: Make these der_parser::oid::Oid constants when it has const fn support.
|
||||
const OID_RSA_ENCRYPTION: &str = "1.2.840.113549.1.1.1";
|
||||
const OID_EC_PUBLIC_KEY: &str = "1.2.840.10045.2.1";
|
||||
@@ -82,13 +81,13 @@ impl From<[u8; 20]> for Serial {
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for Serial {
|
||||
type Error = ();
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(bytes: &[u8]) -> Result<Serial, ()> {
|
||||
fn try_from(bytes: &[u8]) -> Result<Serial> {
|
||||
if bytes.len() <= 20 {
|
||||
Ok(Serial(BigUint::from_bytes_be(&bytes)))
|
||||
} else {
|
||||
Err(())
|
||||
Err(Error::ParseError)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,7 +111,7 @@ pub enum CertInfo {
|
||||
impl TryFrom<u8> for CertInfo {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
fn try_from(value: u8) -> Result<Self> {
|
||||
match value {
|
||||
0x00 => Ok(CertInfo::Uncompressed),
|
||||
0x01 => Ok(CertInfo::Gzip),
|
||||
@@ -190,7 +189,7 @@ impl fmt::Debug for PublicKeyInfo {
|
||||
}
|
||||
|
||||
impl PublicKeyInfo {
|
||||
fn parse(subject_pki: &SubjectPublicKeyInfo<'_>) -> Result<Self, Error> {
|
||||
fn parse(subject_pki: &SubjectPublicKeyInfo<'_>) -> Result<Self> {
|
||||
match subject_pki.algorithm.algorithm.to_string().as_str() {
|
||||
OID_RSA_ENCRYPTION => {
|
||||
let pubkey = read_pki::rsa_pubkey(subject_pki.subject_public_key.data)?;
|
||||
@@ -330,7 +329,7 @@ pub struct Certificate {
|
||||
impl<'a> TryFrom<&'a [u8]> for Certificate {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(bytes: &'a [u8]) -> Result<Self, Error> {
|
||||
fn try_from(bytes: &'a [u8]) -> Result<Self> {
|
||||
Self::from_bytes(bytes.to_vec())
|
||||
}
|
||||
}
|
||||
@@ -350,7 +349,7 @@ impl Certificate {
|
||||
subject: &[RelativeDistinguishedName<'_>],
|
||||
subject_pki: PublicKeyInfo,
|
||||
extensions: &[x509::Extension<'_, O>],
|
||||
) -> Result<Self, Error> {
|
||||
) -> Result<Self> {
|
||||
let serial = serial.into();
|
||||
|
||||
let mut tbs_cert = Buffer::new(Vec::with_capacity(CB_OBJ_MAX));
|
||||
@@ -453,7 +452,7 @@ impl Certificate {
|
||||
}
|
||||
|
||||
/// Read a certificate from the given slot in the YubiKey
|
||||
pub fn read(yubikey: &mut YubiKey, slot: SlotId) -> Result<Self, Error> {
|
||||
pub fn read(yubikey: &mut YubiKey, slot: SlotId) -> Result<Self> {
|
||||
let txn = yubikey.begin_transaction()?;
|
||||
let buf = read_certificate(&txn, slot)?;
|
||||
|
||||
@@ -465,25 +464,21 @@ impl Certificate {
|
||||
}
|
||||
|
||||
/// Write this certificate into the YubiKey in the given slot
|
||||
pub fn write(
|
||||
&self,
|
||||
yubikey: &mut YubiKey,
|
||||
slot: SlotId,
|
||||
certinfo: CertInfo,
|
||||
) -> Result<(), Error> {
|
||||
pub fn write(&self, yubikey: &mut YubiKey, slot: SlotId, certinfo: CertInfo) -> Result<()> {
|
||||
let txn = yubikey.begin_transaction()?;
|
||||
write_certificate(&txn, slot, Some(&self.data), certinfo)
|
||||
}
|
||||
|
||||
/// Delete a certificate located at the given slot of the given YubiKey
|
||||
#[cfg(feature = "untested")]
|
||||
pub fn delete(yubikey: &mut YubiKey, slot: SlotId) -> Result<(), Error> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub fn delete(yubikey: &mut YubiKey, slot: SlotId) -> Result<()> {
|
||||
let txn = yubikey.begin_transaction()?;
|
||||
write_certificate(&txn, slot, None, CertInfo::Uncompressed)
|
||||
}
|
||||
|
||||
/// Initialize a local certificate struct from the given bytebuffer
|
||||
pub fn from_bytes(cert: impl Into<Buffer>) -> Result<Self, Error> {
|
||||
pub fn from_bytes(cert: impl Into<Buffer>) -> Result<Self> {
|
||||
let cert = cert.into();
|
||||
|
||||
if cert.is_empty() {
|
||||
@@ -544,7 +539,7 @@ impl AsRef<[u8]> for Certificate {
|
||||
}
|
||||
|
||||
/// Read certificate
|
||||
pub(crate) fn read_certificate(txn: &Transaction<'_>, slot: SlotId) -> Result<Buffer, Error> {
|
||||
pub(crate) fn read_certificate(txn: &Transaction<'_>, slot: SlotId) -> Result<Buffer> {
|
||||
let object_id = slot.object_id();
|
||||
|
||||
let buf = match txn.fetch_object(object_id) {
|
||||
@@ -572,7 +567,7 @@ pub(crate) fn write_certificate(
|
||||
slot: SlotId,
|
||||
data: Option<&[u8]>,
|
||||
certinfo: CertInfo,
|
||||
) -> Result<(), Error> {
|
||||
) -> Result<()> {
|
||||
let object_id = slot.object_id();
|
||||
|
||||
if data.is_none() {
|
||||
@@ -602,7 +597,7 @@ mod read_pki {
|
||||
use rsa::{BigUint, RSAPublicKey};
|
||||
|
||||
use super::{OID_NIST_P256, OID_NIST_P384};
|
||||
use crate::{error::Error, key::AlgorithmId};
|
||||
use crate::{piv::AlgorithmId, Error, Result};
|
||||
|
||||
/// From [RFC 8017](https://tools.ietf.org/html/rfc8017#appendix-A.1.1):
|
||||
/// ```text
|
||||
@@ -611,7 +606,7 @@ mod read_pki {
|
||||
/// publicExponent INTEGER -- e
|
||||
/// }
|
||||
/// ```
|
||||
pub(super) fn rsa_pubkey(encoded: &[u8]) -> Result<RSAPublicKey, Error> {
|
||||
pub(super) fn rsa_pubkey(encoded: &[u8]) -> Result<RSAPublicKey> {
|
||||
fn parse_rsa_pubkey(i: &[u8]) -> IResult<&[u8], DerObject<'_>, BerError> {
|
||||
parse_der_sequence_defined!(i, parse_der_integer >> parse_der_integer)
|
||||
}
|
||||
@@ -650,7 +645,7 @@ mod read_pki {
|
||||
/// -- specifiedCurve SpecifiedECDomain
|
||||
/// }
|
||||
/// ```
|
||||
pub(super) fn ec_parameters(parameters: &DerObject<'_>) -> Result<AlgorithmId, Error> {
|
||||
pub(super) fn ec_parameters(parameters: &DerObject<'_>) -> Result<AlgorithmId> {
|
||||
let curve_oid = parameters.as_oid_val().map_err(|_| Error::InvalidObject)?;
|
||||
|
||||
match curve_oid.to_string().as_str() {
|
||||
|
||||
+34
-63
@@ -30,22 +30,14 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
use crate::{error::Error, yubikey::YubiKey};
|
||||
use getrandom::getrandom;
|
||||
use std::fmt::{self, Debug, Display};
|
||||
use crate::{Error, Result, YubiKey};
|
||||
use std::{
|
||||
convert::TryInto,
|
||||
fmt::{self, Debug, Display},
|
||||
str,
|
||||
};
|
||||
use subtle_encoding::hex;
|
||||
|
||||
/// CHUID size
|
||||
pub const CHUID_SIZE: usize = 59;
|
||||
|
||||
/// CARDID size
|
||||
pub const CARDID_SIZE: usize = 16;
|
||||
|
||||
/// FASC-N component size
|
||||
pub const FASCN_SIZE: usize = 25;
|
||||
|
||||
/// Expiration size
|
||||
pub const EXPIRATION_SIZE: usize = 8;
|
||||
use uuid::Uuid;
|
||||
|
||||
/// FASC-N offset
|
||||
const CHUID_FASCN_OFFS: usize = 2;
|
||||
@@ -81,50 +73,42 @@ const CHUID_TMPL: &[u8] = &[
|
||||
0x30, 0x33, 0x30, 0x30, 0x31, 0x30, 0x31, 0x3e, 0x00, 0xfe, 0x00,
|
||||
];
|
||||
|
||||
/// Cardholder Unique Identifier (CHUID) Card UUID/GUID value
|
||||
/// Cardholder Unique Identifier (CHUID).
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Uuid(pub [u8; CARDID_SIZE]);
|
||||
pub struct ChuId(pub [u8; Self::BYTE_SIZE]);
|
||||
|
||||
impl Uuid {
|
||||
/// Generate a random Cardholder Unique Identifier (CHUID) UUID
|
||||
pub fn generate() -> Result<Self, Error> {
|
||||
let mut id = [0u8; CARDID_SIZE];
|
||||
getrandom(&mut id).map_err(|_| Error::RandomnessError)?;
|
||||
Ok(Self(id))
|
||||
}
|
||||
}
|
||||
impl ChuId {
|
||||
/// CHUID size in bytes
|
||||
pub const BYTE_SIZE: usize = 59;
|
||||
|
||||
/// Cardholder Unique Identifier (CHUID)
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct CHUID(pub [u8; CHUID_SIZE]);
|
||||
/// FASC-N component size
|
||||
pub const FASCN_SIZE: usize = 25;
|
||||
|
||||
/// Expiration size
|
||||
pub const EXPIRATION_SIZE: usize = 8;
|
||||
|
||||
impl CHUID {
|
||||
/// Return FASC-N component of CHUID
|
||||
pub fn fascn(&self) -> Result<[u8; FASCN_SIZE], Error> {
|
||||
let mut fascn = [0u8; FASCN_SIZE];
|
||||
fascn.copy_from_slice(&self.0[CHUID_FASCN_OFFS..(CHUID_FASCN_OFFS + FASCN_SIZE)]);
|
||||
Ok(fascn)
|
||||
pub fn fascn(&self) -> [u8; Self::FASCN_SIZE] {
|
||||
self.0[CHUID_FASCN_OFFS..(CHUID_FASCN_OFFS + Self::FASCN_SIZE)]
|
||||
.try_into()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Return Card UUID/GUID component of CHUID
|
||||
pub fn uuid(&self) -> Result<[u8; CARDID_SIZE], Error> {
|
||||
let mut uuid = [0u8; CARDID_SIZE];
|
||||
uuid.copy_from_slice(&self.0[CHUID_GUID_OFFS..(CHUID_GUID_OFFS + CARDID_SIZE)]);
|
||||
Ok(uuid)
|
||||
pub fn uuid(&self) -> Uuid {
|
||||
Uuid::from_slice(&self.0[CHUID_GUID_OFFS..(CHUID_GUID_OFFS + 16)]).unwrap()
|
||||
}
|
||||
|
||||
/// Return expiration date component of CHUID
|
||||
// TODO(tarcieri): parse expiration?
|
||||
pub fn expiration(&self) -> Result<[u8; EXPIRATION_SIZE], Error> {
|
||||
let mut expiration = [0u8; EXPIRATION_SIZE];
|
||||
expiration.copy_from_slice(
|
||||
&self.0[CHUID_EXPIRATION_OFFS..(CHUID_EXPIRATION_OFFS + EXPIRATION_SIZE)],
|
||||
);
|
||||
Ok(expiration)
|
||||
pub fn expiration(&self) -> [u8; Self::EXPIRATION_SIZE] {
|
||||
self.0[CHUID_EXPIRATION_OFFS..(CHUID_EXPIRATION_OFFS + Self::EXPIRATION_SIZE)]
|
||||
.try_into()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Get Cardholder Unique Identifier (CHUID)
|
||||
pub fn get(yubikey: &mut YubiKey) -> Result<CHUID, Error> {
|
||||
pub fn get(yubikey: &mut YubiKey) -> Result<ChuId> {
|
||||
let txn = yubikey.begin_transaction()?;
|
||||
let response = txn.fetch_object(OBJ_CHUID)?;
|
||||
|
||||
@@ -132,36 +116,23 @@ impl CHUID {
|
||||
return Err(Error::GenericError);
|
||||
}
|
||||
|
||||
let mut chuid = [0u8; CHUID_SIZE];
|
||||
chuid.copy_from_slice(&response[0..CHUID_SIZE]);
|
||||
let retval = CHUID { 0: chuid };
|
||||
Ok(retval)
|
||||
Ok(ChuId(response[..Self::BYTE_SIZE].try_into().unwrap()))
|
||||
}
|
||||
|
||||
/// Set Cardholder Unique Identifier (CHUID)
|
||||
#[cfg(feature = "untested")]
|
||||
|
||||
pub fn set(&self, yubikey: &mut YubiKey) -> Result<(), Error> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub fn set(&self, yubikey: &mut YubiKey) -> Result<()> {
|
||||
let mut buf = CHUID_TMPL.to_vec();
|
||||
buf[0..self.0.len()].copy_from_slice(&self.0);
|
||||
buf[..Self::BYTE_SIZE].copy_from_slice(&self.0);
|
||||
|
||||
let txn = yubikey.begin_transaction()?;
|
||||
txn.save_object(OBJ_CHUID, &buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for CHUID {
|
||||
impl Display for ChuId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
String::from_utf8(hex::encode(&self.0[..])).unwrap()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for CHUID {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "CHUID({:?})", &self.0[..])
|
||||
f.write_str(str::from_utf8(&hex::encode(&self.0[..])).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
+22
-17
@@ -31,12 +31,14 @@
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
use crate::{
|
||||
error::Error,
|
||||
consts::{
|
||||
TAG_ADMIN_FLAGS_1, TAG_ADMIN_SALT, TAG_ADMIN_TIMESTAMP, TAG_PROTECTED_FLAGS_1,
|
||||
TAG_PROTECTED_MGM,
|
||||
},
|
||||
metadata::{AdminData, ProtectedData},
|
||||
mgm::{MgmType, ADMIN_FLAGS_1_PROTECTED_MGM},
|
||||
yubikey::{YubiKey, ADMIN_FLAGS_1_PUK_BLOCKED},
|
||||
TAG_ADMIN_FLAGS_1, TAG_ADMIN_SALT, TAG_ADMIN_TIMESTAMP, TAG_PROTECTED_FLAGS_1,
|
||||
TAG_PROTECTED_MGM,
|
||||
Result,
|
||||
};
|
||||
use log::error;
|
||||
use std::{
|
||||
@@ -47,36 +49,41 @@ use std::{
|
||||
const CB_ADMIN_TIMESTAMP: usize = 0x04;
|
||||
const PROTECTED_FLAGS_1_PUK_NOBLOCK: u8 = 0x01;
|
||||
|
||||
/// Config
|
||||
/// YubiKey configuration.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Config {
|
||||
/// Protected data available
|
||||
protected_data_available: bool,
|
||||
pub protected_data_available: bool,
|
||||
|
||||
/// PUK blocked
|
||||
puk_blocked: bool,
|
||||
pub puk_blocked: bool,
|
||||
|
||||
/// No block on upgrade
|
||||
puk_noblock_on_upgrade: bool,
|
||||
pub puk_noblock_on_upgrade: bool,
|
||||
|
||||
/// PIN last changed
|
||||
pin_last_changed: Option<SystemTime>,
|
||||
pub pin_last_changed: Option<SystemTime>,
|
||||
|
||||
/// MGM type
|
||||
mgm_type: MgmType,
|
||||
pub mgm_type: MgmType,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// Get YubiKey config
|
||||
pub fn get(yubikey: &mut YubiKey) -> Result<Config, Error> {
|
||||
let mut config = Config {
|
||||
impl Default for Config {
|
||||
fn default() -> Config {
|
||||
Config {
|
||||
protected_data_available: false,
|
||||
puk_blocked: false,
|
||||
puk_noblock_on_upgrade: false,
|
||||
pin_last_changed: None,
|
||||
mgm_type: MgmType::Manual,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// Get YubiKey config.
|
||||
pub(crate) fn get(yubikey: &mut YubiKey) -> Result<Config> {
|
||||
let mut config = Self::default();
|
||||
let txn = yubikey.begin_transaction()?;
|
||||
|
||||
if let Ok(admin_data) = AdminData::read(&txn) {
|
||||
@@ -130,9 +137,7 @@ impl Config {
|
||||
|
||||
if protected_data.get_item(TAG_PROTECTED_MGM).is_ok() {
|
||||
if config.mgm_type != MgmType::Protected {
|
||||
error!(
|
||||
"conflicting types of mgm key administration configured: protected MGM exists"
|
||||
);
|
||||
error!("conflicting MGM key types: protected MGM exists");
|
||||
}
|
||||
|
||||
// Always favor protected MGM
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
//! Miscellaneous constant values
|
||||
|
||||
/// YubiKey max buffer size
|
||||
pub(crate) const CB_BUF_MAX: usize = 3072;
|
||||
|
||||
/// YubiKey max object size
|
||||
pub(crate) const CB_OBJ_MAX: usize = CB_BUF_MAX - 9;
|
||||
|
||||
pub(crate) const CB_OBJ_TAG_MIN: usize = 2; // 1 byte tag + 1 byte len
|
||||
#[cfg(feature = "untested")]
|
||||
pub(crate) const CB_OBJ_TAG_MAX: usize = CB_OBJ_TAG_MIN + 2; // 1 byte tag + 3 bytes len
|
||||
|
||||
// Admin tags
|
||||
pub(crate) const TAG_ADMIN_FLAGS_1: u8 = 0x81;
|
||||
pub(crate) const TAG_ADMIN_SALT: u8 = 0x82;
|
||||
pub(crate) const TAG_ADMIN_TIMESTAMP: u8 = 0x83;
|
||||
|
||||
// Protected tags
|
||||
pub(crate) const TAG_PROTECTED_FLAGS_1: u8 = 0x81;
|
||||
pub(crate) const TAG_PROTECTED_MGM: u8 = 0x89;
|
||||
+71
-74
@@ -32,65 +32,66 @@
|
||||
|
||||
use std::fmt::{self, Display};
|
||||
|
||||
/// Kinds of errors
|
||||
/// Result type with [`Error`].
|
||||
pub type Result<T> = core::result::Result<T, Error>;
|
||||
|
||||
/// Kinds of errors.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
/// Algorithm error
|
||||
AlgorithmError,
|
||||
|
||||
/// Applet error
|
||||
AppletError,
|
||||
|
||||
/// Argument error
|
||||
ArgumentError,
|
||||
|
||||
/// Authentication error
|
||||
AuthenticationError,
|
||||
|
||||
/// Generic error
|
||||
GenericError,
|
||||
|
||||
/// Invalid object
|
||||
InvalidObject,
|
||||
|
||||
/// Key error
|
||||
KeyError,
|
||||
|
||||
/// Memory error
|
||||
MemoryError,
|
||||
|
||||
/// Not supported
|
||||
NotSupported,
|
||||
|
||||
/// Not found
|
||||
NotFound,
|
||||
|
||||
/// Parse error
|
||||
ParseError,
|
||||
|
||||
/// PCSC error
|
||||
PcscError {
|
||||
/// Original PC/SC error
|
||||
inner: Option<pcsc::Error>,
|
||||
},
|
||||
|
||||
/// PIN locked
|
||||
PinLocked,
|
||||
|
||||
/// Range error
|
||||
RangeError,
|
||||
|
||||
/// Size error
|
||||
SizeError,
|
||||
|
||||
/// Applet error
|
||||
AppletError,
|
||||
|
||||
/// Authentication error
|
||||
AuthenticationError,
|
||||
|
||||
/// Randomness error
|
||||
RandomnessError,
|
||||
|
||||
/// Generic error
|
||||
GenericError,
|
||||
|
||||
/// Key error
|
||||
KeyError,
|
||||
|
||||
/// Parse error
|
||||
ParseError,
|
||||
|
||||
/// Wrong PIN
|
||||
WrongPin {
|
||||
/// Number of tries remaining
|
||||
tries: u8,
|
||||
},
|
||||
|
||||
/// Invalid object
|
||||
InvalidObject,
|
||||
|
||||
/// Algorithm error
|
||||
AlgorithmError,
|
||||
|
||||
/// PIN locked
|
||||
PinLocked,
|
||||
|
||||
/// Argument error
|
||||
ArgumentError,
|
||||
|
||||
/// Range error
|
||||
RangeError,
|
||||
|
||||
/// Not supported
|
||||
NotSupported,
|
||||
|
||||
/// Not found
|
||||
NotFound,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
@@ -98,48 +99,46 @@ impl Error {
|
||||
///
|
||||
/// These names map to the legacy names from the Yubico C library, to
|
||||
/// assist in web searches for relevant information for these errors.
|
||||
pub fn name(self) -> &'static str {
|
||||
match self {
|
||||
Error::MemoryError => "YKPIV_MEMORY_ERROR",
|
||||
Error::PcscError { .. } => "YKPIV_PCSC_ERROR",
|
||||
Error::SizeError => "YKPIV_SIZE_ERROR",
|
||||
Error::AppletError => "YKPIV_APPLET_ERROR",
|
||||
Error::AuthenticationError => "YKPIV_AUTHENTICATION_ERROR",
|
||||
Error::RandomnessError => "YKPIV_RANDOMNESS_ERROR",
|
||||
Error::GenericError => "YKPIV_GENERIC_ERROR",
|
||||
Error::KeyError => "YKPIV_KEY_ERROR",
|
||||
Error::ParseError => "YKPIV_PARSE_ERROR",
|
||||
Error::WrongPin { .. } => "YKPIV_WRONG_PIN",
|
||||
Error::InvalidObject => "YKPIV_INVALID_OBJECT",
|
||||
pub fn name(self) -> Option<&'static str> {
|
||||
Some(match self {
|
||||
Error::AlgorithmError => "YKPIV_ALGORITHM_ERROR",
|
||||
Error::PinLocked => "YKPIV_PIN_LOCKED",
|
||||
Error::AppletError => "YKPIV_APPLET_ERROR",
|
||||
Error::ArgumentError => "YKPIV_ARGUMENT_ERROR",
|
||||
Error::RangeError => "YKPIV_RANGE_ERROR",
|
||||
Error::AuthenticationError => "YKPIV_AUTHENTICATION_ERROR",
|
||||
Error::GenericError => "YKPIV_GENERIC_ERROR",
|
||||
Error::InvalidObject => "YKPIV_INVALID_OBJECT",
|
||||
Error::KeyError => "YKPIV_KEY_ERROR",
|
||||
Error::MemoryError => "YKPIV_MEMORY_ERROR",
|
||||
Error::NotSupported => "YKPIV_NOT_SUPPORTED",
|
||||
Error::NotFound => "<not found>",
|
||||
}
|
||||
Error::ParseError => "YKPIV_PARSE_ERROR",
|
||||
Error::PcscError { .. } => "YKPIV_PCSC_ERROR",
|
||||
Error::PinLocked => "YKPIV_PIN_LOCKED",
|
||||
Error::RangeError => "YKPIV_RANGE_ERROR",
|
||||
Error::SizeError => "YKPIV_SIZE_ERROR",
|
||||
Error::WrongPin { .. } => "YKPIV_WRONG_PIN",
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Error message
|
||||
pub fn msg(self) -> &'static str {
|
||||
match self {
|
||||
Error::MemoryError => "memory error",
|
||||
Error::PcscError { .. } => "PCSC error",
|
||||
Error::SizeError => "size error",
|
||||
Error::AppletError => "applet error",
|
||||
Error::AuthenticationError => "authentication error",
|
||||
Error::RandomnessError => "randomness error",
|
||||
Error::GenericError => "generic error",
|
||||
Error::KeyError => "key error",
|
||||
Error::ParseError => "parse error",
|
||||
Error::WrongPin { .. } => "wrong pin",
|
||||
Error::InvalidObject => "invalid object",
|
||||
Error::AlgorithmError => "algorithm error",
|
||||
Error::PinLocked => "PIN locked",
|
||||
Error::AppletError => "applet error",
|
||||
Error::ArgumentError => "argument error",
|
||||
Error::RangeError => "range error",
|
||||
Error::AuthenticationError => "authentication error",
|
||||
Error::GenericError => "generic error",
|
||||
Error::InvalidObject => "invalid object",
|
||||
Error::KeyError => "key error",
|
||||
Error::MemoryError => "memory error",
|
||||
Error::NotSupported => "not supported",
|
||||
Error::NotFound => "not found",
|
||||
Error::ParseError => "parse error",
|
||||
Error::PcscError { .. } => "PC/SC error",
|
||||
Error::PinLocked => "PIN locked",
|
||||
Error::RangeError => "range error",
|
||||
Error::SizeError => "size error",
|
||||
Error::WrongPin { .. } => "wrong pin",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -159,10 +158,8 @@ impl From<pcsc::Error> for Error {
|
||||
impl std::error::Error for Error {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
#[allow(trivial_casts)] // why doesn't this work without the cast???
|
||||
Error::PcscError { inner } => inner
|
||||
.as_ref()
|
||||
.map(|err| err as &(dyn std::error::Error + 'static)),
|
||||
#[allow(trivial_casts)]
|
||||
Error::PcscError { inner } => inner.as_ref().map(|err| err as &_),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
+72
-85
@@ -1,10 +1,14 @@
|
||||
//! [YubiKey] PIV: [Personal Identity Verification][PIV] support for
|
||||
//! [Yubico] devices using the Personal Computer/Smart Card ([PC/SC])
|
||||
//! **yubikey.rs**: pure Rust cross-platform host-side driver for [YubiKey]
|
||||
//! devices from [Yubico] using the Personal Computer/Smart Card ([PC/SC])
|
||||
//! interface as provided by the [`pcsc` crate].
|
||||
//!
|
||||
//! **PIV** is a [NIST] standard for both *signing* and *encryption*
|
||||
//! # Features
|
||||
//! ## Personal Identity Verification (PIV)
|
||||
//! [PIV] is a [NIST] standard for both *signing* and *encryption*
|
||||
//! using SmartCards and SmartCard-based hardware tokens like YubiKeys.
|
||||
//!
|
||||
//! PIV-related functionality can be found in the [`piv`] module.
|
||||
//!
|
||||
//! This library natively implements the protocol used to manage and
|
||||
//! utilize PIV encryption and signing keys which can be generated, imported,
|
||||
//! and stored on YubiKey devices.
|
||||
@@ -12,38 +16,43 @@
|
||||
//! See [Yubico's guide to PIV-enabled YubiKeys][yk-guide] for more information
|
||||
//! on which devices support PIV and the available functionality.
|
||||
//!
|
||||
//! ## Minimum Supported Rust Version
|
||||
//!
|
||||
//! Rust 1.44+
|
||||
//!
|
||||
//! ## Supported YubiKeys
|
||||
//! # Minimum Supported Rust Version
|
||||
//! Rust **1.51** or newer.
|
||||
//!
|
||||
//! # Supported YubiKeys
|
||||
//! - [YubiKey 4] series
|
||||
//! - [YubiKey 5] series
|
||||
//!
|
||||
//! NOTE: Nano and USB-C variants of the above are also supported.
|
||||
//! Pre-YK4 [YubiKey NEO] series is **NOT** supported.
|
||||
//!
|
||||
//! ## Supported Algorithms
|
||||
//! # Supported Operating Systems
|
||||
//! - Linux
|
||||
//! - macOS
|
||||
//! - Windows
|
||||
//!
|
||||
//! # Supported Algorithms
|
||||
//! - **Authentication**: `3DES`
|
||||
//! - **Encryption**: `RSA1024`, `RSA2048`, `ECCP256`, `ECCP384`
|
||||
//! - **Encryption**:
|
||||
//! - RSA: `RSA1024`, `RSA2048`
|
||||
//! - ECC: `ECCP256`, `ECCP384` (NIST curves: P-256, P-384)
|
||||
//! - **Signatures**:
|
||||
//! - RSASSA-PKCS#1v1.5: `RSA1024`, `RSA2048`
|
||||
//! - ECDSA: `ECCP256`, `ECCP384`
|
||||
//! - ECDSA: `ECCP256`, `ECCP384` (NIST curves: P-256, P-384)
|
||||
//!
|
||||
//! NOTE: RSASSA-PSS signatures and RSA-OAEP encryption may be supportable (TBD)
|
||||
//!
|
||||
//! ## Status
|
||||
//! # Status
|
||||
//! Functionality which has been successfully tested is available by default.
|
||||
//!
|
||||
//! This is a work-in-progress effort, and while much of the library-level
|
||||
//! code from upstream [yubico-piv-tool] has been translated into Rust
|
||||
//! presenting a safe interface, much of it is still untested.
|
||||
//! Any functionality which is gated on the `untested` feature has not been
|
||||
//! properly tested and is not known to function correctly.
|
||||
//!
|
||||
//! Please see the [project's README.md for a complete status][status].
|
||||
//!
|
||||
//! ## History
|
||||
//! Please see the [`untested` functionality tracking issue] for current status.
|
||||
//! We would appreciate any help testing this functionality and removing the
|
||||
//! `untested` gating as well as writing more automated tests.
|
||||
//!
|
||||
//! # History
|
||||
//! This library is a Rust translation of the [yubico-piv-tool] utility by
|
||||
//! Yubico, which was originally written in C. It was mechanically translated
|
||||
//! from C into Rust using [Corrode], and then subsequently heavily
|
||||
@@ -53,25 +62,23 @@
|
||||
//! the YubiKey implementation of PIV works in general, see the
|
||||
//! [Yubico PIV Tool Command Line Guide][piv-tool-guide].
|
||||
//!
|
||||
//! ## Security Warning
|
||||
//!
|
||||
//! # Security Warning
|
||||
//! No security audits of this crate have ever been performed. Presently it is in
|
||||
//! an experimental stage and may still contain high-severity issues.
|
||||
//!
|
||||
//! USE AT YOUR OWN RISK!
|
||||
//!
|
||||
//! ## Code of Conduct
|
||||
//!
|
||||
//! # Code of Conduct
|
||||
//! We abide by the [Contributor Covenant][cc-md] and ask that you do as well.
|
||||
//!
|
||||
//! For more information, please see [CODE_OF_CONDUCT.md][cc-md].
|
||||
//!
|
||||
//! ## License
|
||||
//! # License
|
||||
//! **yubikey.rs** is a fork of and originally a mechanical translation from
|
||||
//! Yubico's [yubico-piv-tool], a C library/CLI program.
|
||||
//!
|
||||
//! **yubikey-piv.rs** is a fork of and originally a mechanical translation from
|
||||
//! Yubico's [yubico-piv-tool], a C library/CLI program. The original library
|
||||
//! was licensed under a [2-Clause BSD License][BSDL], which this library inherits
|
||||
//! as a derived work.
|
||||
//! The original library was licensed under a [2-Clause BSD License][BSDL],
|
||||
//! which this library inherits as a derived work.
|
||||
//!
|
||||
//! [YubiKey]: https://www.yubico.com/products/yubikey-hardware/
|
||||
//! [PIV]: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-73-4.pdf
|
||||
@@ -83,12 +90,12 @@
|
||||
//! [YubiKey NEO]: https://support.yubico.com/support/solutions/articles/15000006494-yubikey-neo
|
||||
//! [YubiKey 4]: https://support.yubico.com/support/solutions/articles/15000006486-yubikey-4
|
||||
//! [YubiKey 5]: https://www.yubico.com/products/yubikey-5-overview/
|
||||
//! [status]: https://github.com/iqlusioninc/yubikey-piv.rs#status
|
||||
//! [`untested` functionality tracking issue]: https://github.com/iqlusioninc/yubikey.rs/issues/280
|
||||
//! [yubico-piv-tool]: https://github.com/Yubico/yubico-piv-tool/
|
||||
//! [Corrode]: https://github.com/jameysharp/corrode
|
||||
//! [piv-tool-guide]: https://www.yubico.com/wp-content/uploads/2016/05/Yubico_PIV_Tool_Command_Line_Guide_en.pdf
|
||||
//! [cc-web]: https://contributor-covenant.org/
|
||||
//! [cc-md]: https://github.com/iqlusioninc/yubikey-piv.rs/blob/main/CODE_OF_CONDUCT.md
|
||||
//! [cc-md]: https://github.com/iqlusioninc/yubikey.rs/blob/main/CODE_OF_CONDUCT.md
|
||||
//! [BSDL]: https://opensource.org/licenses/BSD-2-Clause
|
||||
|
||||
// Adapted from yubico-piv-tool:
|
||||
@@ -121,76 +128,56 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![doc(
|
||||
html_logo_url = "https://raw.githubusercontent.com/iqlusioninc/yubikey-piv.rs/main/img/logo.png",
|
||||
html_root_url = "https://docs.rs/yubikey-piv/0.3.0"
|
||||
html_logo_url = "https://raw.githubusercontent.com/iqlusioninc/yubikey.rs/main/img/logo.png",
|
||||
html_root_url = "https://docs.rs/yubikey/0.4.2"
|
||||
)]
|
||||
#![forbid(unsafe_code)]
|
||||
#![warn(
|
||||
missing_docs,
|
||||
rust_2018_idioms,
|
||||
trivial_casts,
|
||||
trivial_numeric_casts,
|
||||
unused_lifetimes,
|
||||
unused_qualifications
|
||||
)]
|
||||
#![warn(missing_docs, rust_2018_idioms, trivial_casts, unused_qualifications)]
|
||||
|
||||
mod apdu;
|
||||
pub mod cccid;
|
||||
mod cccid;
|
||||
pub mod certificate;
|
||||
pub mod chuid;
|
||||
pub mod config;
|
||||
pub mod error;
|
||||
pub mod key;
|
||||
mod chuid;
|
||||
mod config;
|
||||
mod consts;
|
||||
mod error;
|
||||
mod metadata;
|
||||
pub mod mgm;
|
||||
mod mgm;
|
||||
#[cfg(feature = "untested")]
|
||||
pub mod mscmap;
|
||||
mod mscmap;
|
||||
#[cfg(feature = "untested")]
|
||||
pub mod msroots;
|
||||
pub mod policy;
|
||||
pub mod readers;
|
||||
mod msroots;
|
||||
pub mod piv;
|
||||
mod policy;
|
||||
pub mod reader;
|
||||
mod serialization;
|
||||
pub mod settings;
|
||||
mod setting;
|
||||
mod transaction;
|
||||
pub mod yubikey;
|
||||
mod yubikey;
|
||||
|
||||
pub use self::{
|
||||
error::Error,
|
||||
key::Key,
|
||||
mgm::MgmKey,
|
||||
readers::Readers,
|
||||
yubikey::{Serial, YubiKey},
|
||||
pub use crate::{
|
||||
cccid::{CardId, CccId},
|
||||
certificate::Certificate,
|
||||
chuid::ChuId,
|
||||
config::Config,
|
||||
error::{Error, Result},
|
||||
mgm::{MgmKey, MgmType},
|
||||
piv::Key,
|
||||
policy::{PinPolicy, TouchPolicy},
|
||||
reader::Context,
|
||||
setting::{Setting, SettingSource},
|
||||
yubikey::{CachedPin, Serial, Version, YubiKey},
|
||||
};
|
||||
|
||||
/// Object identifiers
|
||||
#[cfg(feature = "untested")]
|
||||
pub use crate::{mscmap::MsContainer, msroots::MsRoots};
|
||||
|
||||
pub use uuid::Uuid;
|
||||
|
||||
/// Object identifiers: handles to particular objects stored on a YubiKey.
|
||||
pub type ObjectId = u32;
|
||||
|
||||
/// Buffer type (self-zeroizing byte vector)
|
||||
pub(crate) type Buffer = zeroize::Zeroizing<Vec<u8>>;
|
||||
|
||||
/// YubiKey max buffer size
|
||||
pub(crate) const CB_BUF_MAX: usize = 3072;
|
||||
|
||||
/// YubiKey max object size
|
||||
pub(crate) const CB_OBJ_MAX: usize = CB_BUF_MAX - 9;
|
||||
pub(crate) const CB_OBJ_TAG_MIN: usize = 2; // 1 byte tag + 1 byte len
|
||||
#[cfg(feature = "untested")]
|
||||
pub(crate) const CB_OBJ_TAG_MAX: usize = CB_OBJ_TAG_MIN + 2; // 1 byte tag + 3 bytes len
|
||||
|
||||
pub(crate) const TAG_ADMIN_FLAGS_1: u8 = 0x81;
|
||||
pub(crate) const TAG_ADMIN_SALT: u8 = 0x82;
|
||||
pub(crate) const TAG_ADMIN_TIMESTAMP: u8 = 0x83;
|
||||
pub(crate) const TAG_PROTECTED_FLAGS_1: u8 = 0x81;
|
||||
pub(crate) const TAG_PROTECTED_MGM: u8 = 0x89;
|
||||
|
||||
/// PIV Applet ID
|
||||
pub(crate) const PIV_AID: [u8; 5] = [0xa0, 0x00, 0x00, 0x03, 0x08];
|
||||
|
||||
/// MGMT Applet ID.
|
||||
/// <https://developers.yubico.com/PIV/Introduction/Admin_access.html>
|
||||
#[cfg(feature = "untested")]
|
||||
pub(crate) const MGMT_AID: [u8; 8] = [0xa0, 0x00, 0x00, 0x05, 0x27, 0x47, 0x11, 0x17];
|
||||
|
||||
/// YubiKey OTP Applet ID. Needed to query serial on YK4.
|
||||
pub(crate) const YK_AID: [u8; 8] = [0xa0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01, 0x01];
|
||||
pub type Buffer = zeroize::Zeroizing<Vec<u8>>;
|
||||
|
||||
+10
-7
@@ -33,10 +33,10 @@
|
||||
use std::marker::PhantomData;
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
use crate::{error::Error, serialization::*, transaction::Transaction, Buffer};
|
||||
use crate::{serialization::*, transaction::Transaction, Buffer, Error, Result};
|
||||
|
||||
#[cfg(feature = "untested")]
|
||||
use crate::{CB_OBJ_MAX, CB_OBJ_TAG_MAX};
|
||||
use crate::consts::{CB_OBJ_MAX, CB_OBJ_TAG_MAX};
|
||||
|
||||
#[cfg(feature = "untested")]
|
||||
use std::iter;
|
||||
@@ -78,7 +78,7 @@ impl<T: MetadataType> Default for Metadata<T> {
|
||||
|
||||
impl<T: MetadataType> Metadata<T> {
|
||||
/// Read metadata
|
||||
pub(crate) fn read(txn: &Transaction<'_>) -> Result<Self, Error> {
|
||||
pub(crate) fn read(txn: &Transaction<'_>) -> Result<Self> {
|
||||
let data = txn.fetch_object(T::obj_id())?;
|
||||
Ok(Metadata {
|
||||
inner: Tlv::parse_single(data, T::tag())?,
|
||||
@@ -88,7 +88,8 @@ impl<T: MetadataType> Metadata<T> {
|
||||
|
||||
/// Write metadata
|
||||
#[cfg(feature = "untested")]
|
||||
pub(crate) fn write(&self, txn: &Transaction<'_>) -> Result<(), Error> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub(crate) fn write(&self, txn: &Transaction<'_>) -> Result<()> {
|
||||
if self.inner.len() > CB_OBJ_MAX - CB_OBJ_TAG_MAX {
|
||||
return Err(Error::GenericError);
|
||||
}
|
||||
@@ -105,12 +106,13 @@ impl<T: MetadataType> Metadata<T> {
|
||||
|
||||
/// Delete metadata
|
||||
#[cfg(feature = "untested")]
|
||||
pub(crate) fn delete(txn: &Transaction<'_>) -> Result<(), Error> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub(crate) fn delete(txn: &Transaction<'_>) -> Result<()> {
|
||||
txn.save_object(T::obj_id(), &[])
|
||||
}
|
||||
|
||||
/// Get metadata item
|
||||
pub(crate) fn get_item(&self, tag: u8) -> Result<&[u8], Error> {
|
||||
pub(crate) fn get_item(&self, tag: u8) -> Result<&[u8]> {
|
||||
let mut data = &self.inner[..];
|
||||
|
||||
while !data.is_empty() {
|
||||
@@ -128,7 +130,8 @@ impl<T: MetadataType> Metadata<T> {
|
||||
|
||||
/// Set metadata item
|
||||
#[cfg(feature = "untested")]
|
||||
pub(crate) fn set_item(&mut self, tag: u8, item: &[u8]) -> Result<(), Error> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub(crate) fn set_item(&mut self, tag: u8, item: &[u8]) -> Result<()> {
|
||||
let mut cb_temp: usize = 0;
|
||||
let mut tag_temp: u8 = 0;
|
||||
let mut cb_len: usize = 0;
|
||||
|
||||
+27
-35
@@ -30,39 +30,30 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
use crate::error::Error;
|
||||
use getrandom::getrandom;
|
||||
use crate::{Error, Result};
|
||||
use log::error;
|
||||
use rand_core::{OsRng, RngCore};
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use zeroize::{Zeroize, Zeroizing};
|
||||
|
||||
#[cfg(feature = "untested")]
|
||||
use crate::{
|
||||
consts::{TAG_ADMIN_FLAGS_1, TAG_ADMIN_SALT, TAG_PROTECTED_MGM},
|
||||
metadata::{AdminData, ProtectedData},
|
||||
yubikey::YubiKey,
|
||||
TAG_ADMIN_FLAGS_1, TAG_ADMIN_SALT, TAG_PROTECTED_MGM,
|
||||
};
|
||||
use des::{
|
||||
cipher::{generic_array::GenericArray, BlockCipher, NewBlockCipher},
|
||||
cipher::{generic_array::GenericArray, BlockDecrypt, BlockEncrypt, NewBlockCipher},
|
||||
TdesEde3,
|
||||
};
|
||||
#[cfg(feature = "untested")]
|
||||
use hmac::Hmac;
|
||||
#[cfg(feature = "untested")]
|
||||
use pbkdf2::pbkdf2;
|
||||
#[cfg(feature = "untested")]
|
||||
use sha1::Sha1;
|
||||
use {hmac::Hmac, pbkdf2::pbkdf2, sha1::Sha1};
|
||||
|
||||
pub(crate) const ADMIN_FLAGS_1_PROTECTED_MGM: u8 = 0x02;
|
||||
|
||||
#[cfg(feature = "untested")]
|
||||
const CB_ADMIN_SALT: usize = 16;
|
||||
|
||||
/// Default MGM key configured on all YubiKeys
|
||||
const DEFAULT_MGM_KEY: [u8; DES_LEN_3DES] = [
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8,
|
||||
];
|
||||
|
||||
/// Size of a DES key
|
||||
const DES_LEN_DES: usize = 8;
|
||||
|
||||
@@ -73,7 +64,7 @@ pub(crate) const DES_LEN_3DES: usize = DES_LEN_DES * 3;
|
||||
#[cfg(feature = "untested")]
|
||||
const ITER_MGM_PBKDF2: u32 = 10000;
|
||||
|
||||
/// Management Key (MGM) key types (manual/derived/protected)
|
||||
/// Management Key (MGM) key types (manual/derived/protected).
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum MgmType {
|
||||
/// Manual
|
||||
@@ -97,27 +88,23 @@ pub struct MgmKey([u8; DES_LEN_3DES]);
|
||||
|
||||
impl MgmKey {
|
||||
/// Generate a random MGM key
|
||||
pub fn generate() -> Result<Self, Error> {
|
||||
pub fn generate() -> Self {
|
||||
let mut key_bytes = [0u8; DES_LEN_3DES];
|
||||
|
||||
if getrandom(&mut key_bytes).is_err() {
|
||||
return Err(Error::RandomnessError);
|
||||
}
|
||||
|
||||
MgmKey::new(key_bytes)
|
||||
OsRng.fill_bytes(&mut key_bytes);
|
||||
Self(key_bytes)
|
||||
}
|
||||
|
||||
/// Create an MGM key from byte slice.
|
||||
///
|
||||
/// Returns an error if the slice is the wrong size or the key is weak.
|
||||
pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, Error> {
|
||||
pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self> {
|
||||
bytes.as_ref().try_into()
|
||||
}
|
||||
|
||||
/// Create an MGM key from the given byte array.
|
||||
///
|
||||
/// Returns an error if the key is weak.
|
||||
pub fn new(key_bytes: [u8; DES_LEN_3DES]) -> Result<Self, Error> {
|
||||
pub fn new(key_bytes: [u8; DES_LEN_3DES]) -> Result<Self> {
|
||||
if is_weak_key(&key_bytes) {
|
||||
error!(
|
||||
"blacklisting key '{:?}' since it's weak (with odd parity)",
|
||||
@@ -127,12 +114,13 @@ impl MgmKey {
|
||||
return Err(Error::KeyError);
|
||||
}
|
||||
|
||||
Ok(MgmKey(key_bytes))
|
||||
Ok(Self(key_bytes))
|
||||
}
|
||||
|
||||
/// Get derived management key (MGM)
|
||||
#[cfg(feature = "untested")]
|
||||
pub fn get_derived(yubikey: &mut YubiKey, pin: &[u8]) -> Result<Self, Error> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub fn get_derived(yubikey: &mut YubiKey, pin: &[u8]) -> Result<Self> {
|
||||
let txn = yubikey.begin_transaction()?;
|
||||
|
||||
// recover management key
|
||||
@@ -151,13 +139,13 @@ impl MgmKey {
|
||||
|
||||
let mut mgm = [0u8; DES_LEN_3DES];
|
||||
pbkdf2::<Hmac<Sha1>>(pin, &salt, ITER_MGM_PBKDF2, &mut mgm);
|
||||
|
||||
MgmKey::from_bytes(mgm)
|
||||
}
|
||||
|
||||
/// Get protected management key (MGM)
|
||||
#[cfg(feature = "untested")]
|
||||
pub fn get_protected(yubikey: &mut YubiKey) -> Result<Self, Error> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub fn get_protected(yubikey: &mut YubiKey) -> Result<Self> {
|
||||
let txn = yubikey.begin_transaction()?;
|
||||
|
||||
let protected_data = ProtectedData::read(&txn).map_err(|e| {
|
||||
@@ -187,7 +175,8 @@ impl MgmKey {
|
||||
///
|
||||
/// This will wipe any metadata related to derived and PIN-protected management keys.
|
||||
#[cfg(feature = "untested")]
|
||||
pub fn set_default(yubikey: &mut YubiKey) -> Result<(), Error> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub fn set_default(yubikey: &mut YubiKey) -> Result<()> {
|
||||
MgmKey::default().set_manual(yubikey, false)
|
||||
}
|
||||
|
||||
@@ -198,7 +187,8 @@ impl MgmKey {
|
||||
///
|
||||
/// This will wipe any metadata related to derived and PIN-protected management keys.
|
||||
#[cfg(feature = "untested")]
|
||||
pub fn set_manual(&self, yubikey: &mut YubiKey, require_touch: bool) -> Result<(), Error> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub fn set_manual(&self, yubikey: &mut YubiKey, require_touch: bool) -> Result<()> {
|
||||
let txn = yubikey.begin_transaction()?;
|
||||
|
||||
txn.set_mgm_key(&self, require_touch).map_err(|e| {
|
||||
@@ -257,7 +247,8 @@ impl MgmKey {
|
||||
///
|
||||
/// This enables key management operations to be performed with access to the PIN.
|
||||
#[cfg(feature = "untested")]
|
||||
pub fn set_protected(&self, yubikey: &mut YubiKey) -> Result<(), Error> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub fn set_protected(&self, yubikey: &mut YubiKey) -> Result<()> {
|
||||
let txn = yubikey.begin_transaction()?;
|
||||
|
||||
txn.set_mgm_key(self, false).map_err(|e| {
|
||||
@@ -325,7 +316,6 @@ impl MgmKey {
|
||||
}
|
||||
|
||||
/// Encrypt with 3DES key
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
pub(crate) fn encrypt(&self, input: &[u8; DES_LEN_DES]) -> [u8; DES_LEN_DES] {
|
||||
let mut output = input.to_owned();
|
||||
TdesEde3::new(GenericArray::from_slice(&self.0))
|
||||
@@ -334,7 +324,6 @@ impl MgmKey {
|
||||
}
|
||||
|
||||
/// Decrypt with 3DES key
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
pub(crate) fn decrypt(&self, input: &[u8; DES_LEN_DES]) -> [u8; DES_LEN_DES] {
|
||||
let mut output = input.to_owned();
|
||||
TdesEde3::new(GenericArray::from_slice(&self.0))
|
||||
@@ -349,9 +338,12 @@ impl AsRef<[u8; DES_LEN_3DES]> for MgmKey {
|
||||
}
|
||||
}
|
||||
|
||||
/// Default MGM key configured on all YubiKeys
|
||||
impl Default for MgmKey {
|
||||
fn default() -> Self {
|
||||
MgmKey(DEFAULT_MGM_KEY)
|
||||
MgmKey([
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -364,7 +356,7 @@ impl Drop for MgmKey {
|
||||
impl<'a> TryFrom<&'a [u8]> for MgmKey {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(key_bytes: &'a [u8]) -> Result<Self, Error> {
|
||||
fn try_from(key_bytes: &'a [u8]) -> Result<Self> {
|
||||
Self::new(key_bytes.try_into().map_err(|_| Error::SizeError)?)
|
||||
}
|
||||
}
|
||||
|
||||
+54
-74
@@ -1,7 +1,4 @@
|
||||
//! MS Container Map Records
|
||||
//!
|
||||
//! These appear(?) to be defined in Microsoft's Smart Card Minidriver Specification:
|
||||
//! <https://docs.microsoft.com/en-us/previous-versions/windows/hardware/design/dn631754(v=vs.85)>
|
||||
//! MS Container Map Records.
|
||||
|
||||
// Adapted from yubico-piv-tool:
|
||||
// <https://github.com/Yubico/yubico-piv-tool/>
|
||||
@@ -33,54 +30,58 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
use crate::{error::Error, key::SlotId, serialization::*, yubikey::YubiKey, CB_OBJ_MAX};
|
||||
use crate::{consts::CB_OBJ_MAX, piv::SlotId, serialization::*, Error, Result, YubiKey};
|
||||
use log::error;
|
||||
use std::{
|
||||
convert::{TryFrom, TryInto},
|
||||
fmt::{self, Debug},
|
||||
};
|
||||
|
||||
/// Container name length
|
||||
const CONTAINER_NAME_LEN: usize = 40;
|
||||
|
||||
/// Container record length: 27 = 80 + 1 + 1 + 2 + 1 + 1 + 1 + 20
|
||||
const CONTAINER_REC_LEN: usize = (2 * CONTAINER_NAME_LEN) + 27;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
|
||||
const OBJ_MSCMAP: u32 = 0x005f_ff10;
|
||||
|
||||
const TAG_MSCMAP: u8 = 0x81;
|
||||
|
||||
/// MS Container Map(?) Records
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Container {
|
||||
/// Container name
|
||||
pub name: [u16; CONTAINER_NAME_LEN],
|
||||
/// MS Container Map records.
|
||||
///
|
||||
/// Defined in Microsoft's Smart Card Minidriver Specification:
|
||||
/// <https://docs.microsoft.com/en-us/previous-versions/windows/hardware/design/dn631754(v=vs.85)>
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MsContainer {
|
||||
/// Container name.
|
||||
pub name: [u16; Self::NAME_LEN],
|
||||
|
||||
/// Card slot
|
||||
/// Card slot.
|
||||
pub slot: SlotId,
|
||||
|
||||
/// Key spec
|
||||
/// Key spec.
|
||||
pub key_spec: u8,
|
||||
|
||||
/// Key size in bits
|
||||
/// Key size in bits.
|
||||
pub key_size_bits: u16,
|
||||
|
||||
/// Flags
|
||||
/// Flags.
|
||||
pub flags: u8,
|
||||
|
||||
/// PIN ID
|
||||
/// PIN ID.
|
||||
pub pin_id: u8,
|
||||
|
||||
/// Associated ECHD(?) container (typo of "ecdh" perhaps?)
|
||||
/// Associated ECHD container.
|
||||
pub associated_echd_container: u8,
|
||||
|
||||
/// Cert fingerprint
|
||||
pub cert_fingerprint: [u8; 20],
|
||||
/// Cert fingerprint.
|
||||
pub cert_fingerprint: [u8; Self::CERT_FINGERPRINT_LEN],
|
||||
}
|
||||
|
||||
impl Container {
|
||||
/// Read MS Container Map records
|
||||
pub fn read_mscmap(yubikey: &mut YubiKey) -> Result<Vec<Self>, Error> {
|
||||
impl MsContainer {
|
||||
/// Container name length in UTF-16 chars.
|
||||
const NAME_LEN: usize = 40;
|
||||
|
||||
/// Container record length: 27 = 80 + 1 + 1 + 2 + 1 + 1 + 1 + 20
|
||||
const REC_LEN: usize = (2 * Self::NAME_LEN) + 27;
|
||||
|
||||
/// Length of a certificate fingerprint.
|
||||
const CERT_FINGERPRINT_LEN: usize = 20;
|
||||
|
||||
/// Read MS Container Map records.
|
||||
pub fn read_mscmap(yubikey: &mut YubiKey) -> Result<Vec<Self>> {
|
||||
let txn = yubikey.begin_transaction()?;
|
||||
let response = txn.fetch_object(OBJ_MSCMAP)?;
|
||||
let mut containers = vec![];
|
||||
@@ -98,17 +99,17 @@ impl Container {
|
||||
return Err(Error::InvalidObject);
|
||||
}
|
||||
|
||||
for chunk in tlv.value.chunks_exact(CONTAINER_REC_LEN) {
|
||||
containers.push(Container::new(chunk)?);
|
||||
for chunk in tlv.value.chunks_exact(Self::REC_LEN) {
|
||||
containers.push(MsContainer::new(chunk)?);
|
||||
}
|
||||
|
||||
Ok(containers)
|
||||
}
|
||||
|
||||
/// Write MS Container Map records.
|
||||
pub fn write_mscmap(yubikey: &mut YubiKey, containers: &[Self]) -> Result<(), Error> {
|
||||
pub fn write_mscmap(yubikey: &mut YubiKey, containers: &[Self]) -> Result<()> {
|
||||
let n_containers = containers.len();
|
||||
let data_len = n_containers * CONTAINER_REC_LEN;
|
||||
let data_len = n_containers * Self::REC_LEN;
|
||||
|
||||
let txn = yubikey.begin_transaction()?;
|
||||
|
||||
@@ -118,7 +119,7 @@ impl Container {
|
||||
|
||||
let mut buf = [0u8; CB_OBJ_MAX];
|
||||
let offset = Tlv::write_as(&mut buf, TAG_MSCMAP, data_len, |buf| {
|
||||
for (i, chunk) in buf.chunks_exact_mut(CONTAINER_REC_LEN).enumerate() {
|
||||
for (i, chunk) in buf.chunks_exact_mut(Self::REC_LEN).enumerate() {
|
||||
chunk.copy_from_slice(&containers[i].to_bytes());
|
||||
}
|
||||
})?;
|
||||
@@ -126,19 +127,19 @@ impl Container {
|
||||
txn.save_object(OBJ_MSCMAP, &buf[..offset])
|
||||
}
|
||||
|
||||
/// Parse a container record from a byte slice
|
||||
pub fn new(bytes: &[u8]) -> Result<Self, Error> {
|
||||
if bytes.len() != CONTAINER_REC_LEN {
|
||||
/// Parse a container record from a byte slice.
|
||||
pub fn new(bytes: &[u8]) -> Result<Self> {
|
||||
if bytes.len() != Self::REC_LEN {
|
||||
error!(
|
||||
"couldn't parse PIV container: expected {}-bytes, got {}-bytes",
|
||||
CONTAINER_REC_LEN,
|
||||
Self::REC_LEN,
|
||||
bytes.len()
|
||||
);
|
||||
return Err(Error::ParseError);
|
||||
}
|
||||
|
||||
let mut name = [0u16; CONTAINER_NAME_LEN];
|
||||
let name_bytes_len = CONTAINER_NAME_LEN * 2;
|
||||
let mut name = [0u16; Self::NAME_LEN];
|
||||
let name_bytes_len = Self::NAME_LEN * 2;
|
||||
|
||||
for (i, chunk) in bytes[..name_bytes_len].chunks_exact(2).enumerate() {
|
||||
name[i] = u16::from_le_bytes(chunk.try_into().unwrap());
|
||||
@@ -147,7 +148,7 @@ impl Container {
|
||||
let mut cert_fingerprint = [0u8; 20];
|
||||
cert_fingerprint.copy_from_slice(&bytes[(bytes.len() - 20)..]);
|
||||
|
||||
Ok(Container {
|
||||
Ok(Self {
|
||||
name,
|
||||
slot: bytes[name_bytes_len].try_into()?,
|
||||
key_spec: bytes[name_bytes_len + 1],
|
||||
@@ -163,16 +164,17 @@ impl Container {
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse the container name as a UTF-16 string
|
||||
pub fn parse_name(&self) -> Result<String, Error> {
|
||||
/// Parse the container name as a UTF-16 string.
|
||||
pub fn parse_name(&self) -> Result<String> {
|
||||
String::from_utf16(&self.name).map_err(|_| Error::ParseError)
|
||||
}
|
||||
|
||||
/// Serialize a container record as a byte size
|
||||
pub fn to_bytes(&self) -> [u8; CONTAINER_REC_LEN] {
|
||||
let mut bytes = Vec::with_capacity(CONTAINER_REC_LEN);
|
||||
/// Serialize a container record as a byte size.
|
||||
pub fn to_bytes(&self) -> [u8; Self::REC_LEN] {
|
||||
// TODO(tarcieri): use array instead of `Vec`
|
||||
let mut bytes = Vec::with_capacity(Self::REC_LEN);
|
||||
|
||||
for i in 0..CONTAINER_NAME_LEN {
|
||||
for i in 0..Self::NAME_LEN {
|
||||
bytes.extend_from_slice(&self.name[i].to_le_bytes());
|
||||
}
|
||||
|
||||
@@ -183,36 +185,14 @@ impl Container {
|
||||
bytes.push(self.pin_id);
|
||||
bytes.push(self.associated_echd_container);
|
||||
bytes.extend_from_slice(&self.cert_fingerprint);
|
||||
|
||||
// TODO(tarcieri): use TryInto here when const generics are available
|
||||
let mut result = [0u8; CONTAINER_REC_LEN];
|
||||
result.copy_from_slice(&bytes);
|
||||
result
|
||||
bytes.as_slice().try_into().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Container {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"PivContainer {{ name: {:?}, slot: {:?}, key_spec: {}, key_size_bits: {}, \
|
||||
flags: {}, pin_id: {}, associated_echd_container: {}, cert_fingerprint: {:?} }}",
|
||||
&self.name[..],
|
||||
self.slot,
|
||||
self.key_spec,
|
||||
self.key_size_bits,
|
||||
self.flags,
|
||||
self.pin_id,
|
||||
self.associated_echd_container,
|
||||
&self.cert_fingerprint[..]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a [u8]> for Container {
|
||||
impl<'a> TryFrom<&'a [u8]> for MsContainer {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(bytes: &'a [u8]) -> Result<Self, Error> {
|
||||
fn try_from(bytes: &'a [u8]) -> Result<Self> {
|
||||
Self::new(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
+18
-14
@@ -1,11 +1,4 @@
|
||||
//! `msroots`: PKCS#7 formatted certificate store for enterprise trusted roots.
|
||||
//!
|
||||
//! This `msroots` file contains a bag of certificates with empty content and
|
||||
//! an empty signature, allowing an enterprise root certificate truststore to
|
||||
//! be written to and read from a YubiKey.
|
||||
//!
|
||||
//! For more information, see:
|
||||
//! <https://docs.microsoft.com/en-us/windows-hardware/drivers/smartcard/developer-guidelines#-interoperability-with-msroots>
|
||||
//! PKCS#7 formatted certificate store for enterprise trusted roots.
|
||||
|
||||
// Adapted from yubico-piv-tool:
|
||||
// <https://github.com/Yubico/yubico-piv-tool/>
|
||||
@@ -37,8 +30,11 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
use crate::{error::Error, serialization::*, yubikey::YubiKey};
|
||||
use crate::{CB_OBJ_MAX, CB_OBJ_TAG_MAX};
|
||||
use crate::{
|
||||
consts::{CB_OBJ_MAX, CB_OBJ_TAG_MAX},
|
||||
serialization::*,
|
||||
Error, Result, YubiKey,
|
||||
};
|
||||
use log::error;
|
||||
|
||||
const OBJ_MSROOTS1: u32 = 0x005f_ff11;
|
||||
@@ -53,17 +49,25 @@ const OBJ_MSROOTS5: u32 = 0x005f_ff15;
|
||||
const TAG_MSROOTS_END: u8 = 0x82;
|
||||
const TAG_MSROOTS_MID: u8 = 0x83;
|
||||
|
||||
/// `msroots` file: PKCS#7-formatted certificate store for enterprise trust roots
|
||||
/// PKCS#7-formatted certificate store for enterprise trust roots.
|
||||
///
|
||||
/// The `msroots` file contains a bag of certificates with empty content and
|
||||
/// an empty signature, allowing an enterprise root certificate truststore to
|
||||
/// be written to and read from a YubiKey.
|
||||
///
|
||||
/// For more information, see:
|
||||
/// <https://docs.microsoft.com/en-us/windows-hardware/drivers/smartcard/developer-guidelines#-interoperability-with-msroots>
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub struct MsRoots(Vec<u8>);
|
||||
|
||||
impl MsRoots {
|
||||
/// Initialize a local certificate struct from the given bytebuffer
|
||||
pub fn new(msroots: impl AsRef<[u8]>) -> Result<Self, Error> {
|
||||
pub fn new(msroots: impl AsRef<[u8]>) -> Result<Self> {
|
||||
Ok(MsRoots(msroots.as_ref().into()))
|
||||
}
|
||||
|
||||
/// Read `msroots` file from YubiKey
|
||||
pub fn read(yubikey: &mut YubiKey) -> Result<Option<Self>, Error> {
|
||||
pub fn read(yubikey: &mut YubiKey) -> Result<Option<Self>> {
|
||||
let txn = yubikey.begin_transaction()?;
|
||||
|
||||
// allocate first page
|
||||
@@ -100,7 +104,7 @@ impl MsRoots {
|
||||
}
|
||||
|
||||
/// Write `msroots` file to YubiKey
|
||||
pub fn write(&self, yubikey: &mut YubiKey) -> Result<(), Error> {
|
||||
pub fn write(&self, yubikey: &mut YubiKey) -> Result<()> {
|
||||
let mut buf = [0u8; CB_OBJ_MAX];
|
||||
let mut offset: usize;
|
||||
let mut data_offset: usize = 0;
|
||||
|
||||
+65
-54
@@ -1,11 +1,16 @@
|
||||
//! PIV cryptographic keys stored in a YubiKey.
|
||||
//! Personal Identity Verification (PIV) cryptographic keys stored in a YubiKey.
|
||||
//!
|
||||
//! Support for public-key cryptography using keys stored within the PIV
|
||||
//! slots of a YubiKey.
|
||||
//!
|
||||
//! Supported algorithms:
|
||||
//!
|
||||
//! - **Encryption**: `RSA1024`, `RSA2048`, `ECCP256`, `ECCP384`
|
||||
//! - **Encryption**:
|
||||
//! - RSA: `RSA1024`, `RSA2048`
|
||||
//! - ECC: `ECCP256`, `ECCP384` (NIST curves: P-256, P-384)
|
||||
//! - **Signatures**:
|
||||
//! - RSASSA-PKCS#1v1.5: `RSA1024`, `RSA2048`
|
||||
//! - ECDSA: `ECCP256`, `ECCP384`
|
||||
//! - ECDSA: `ECCP256`, `ECCP384` (NIST curves: P-256, P-384)
|
||||
|
||||
// Adapted from yubico-piv-tool:
|
||||
// <https://github.com/Yubico/yubico-piv-tool/>
|
||||
@@ -39,32 +44,27 @@
|
||||
|
||||
use crate::{
|
||||
apdu::{Ins, StatusWords},
|
||||
certificate::{self, Certificate},
|
||||
error::Error,
|
||||
serialization::*,
|
||||
settings,
|
||||
yubikey::YubiKey,
|
||||
ObjectId,
|
||||
};
|
||||
use log::debug;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[cfg(feature = "untested")]
|
||||
use crate::CB_OBJ_MAX;
|
||||
use crate::{
|
||||
certificate::PublicKeyInfo,
|
||||
certificate::{self, Certificate, PublicKeyInfo},
|
||||
error::{Error, Result},
|
||||
policy::{PinPolicy, TouchPolicy},
|
||||
Buffer,
|
||||
serialization::*,
|
||||
setting,
|
||||
yubikey::YubiKey,
|
||||
Buffer, ObjectId,
|
||||
};
|
||||
use elliptic_curve::sec1::EncodedPoint as EcPublicKey;
|
||||
use log::{error, warn};
|
||||
#[cfg(feature = "untested")]
|
||||
use num_bigint::traits::ModInverse;
|
||||
#[cfg(feature = "untested")]
|
||||
use num_integer::Integer;
|
||||
#[cfg(feature = "untested")]
|
||||
use num_traits::{FromPrimitive, One};
|
||||
use log::{debug, error, warn};
|
||||
use rsa::{BigUint, RSAPublicKey};
|
||||
use std::{convert::TryFrom, str::FromStr};
|
||||
|
||||
#[cfg(feature = "untested")]
|
||||
use {
|
||||
crate::consts::CB_OBJ_MAX,
|
||||
num_bigint_dig::traits::ModInverse,
|
||||
num_integer::Integer,
|
||||
num_traits::{FromPrimitive, One},
|
||||
};
|
||||
|
||||
#[cfg(feature = "untested")]
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
@@ -126,7 +126,7 @@ pub enum SlotId {
|
||||
impl TryFrom<u8> for SlotId {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
fn try_from(value: u8) -> Result<Self> {
|
||||
match value {
|
||||
0x9a => Ok(SlotId::Authentication),
|
||||
0x9c => Ok(SlotId::Signature),
|
||||
@@ -151,17 +151,17 @@ impl From<SlotId> for u8 {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for SlotId {
|
||||
type Error = Error;
|
||||
impl FromStr for SlotId {
|
||||
type Err = Error;
|
||||
|
||||
fn try_from(s: String) -> Result<SlotId, Error> {
|
||||
match s.as_ref() {
|
||||
fn from_str(s: &str) -> Result<SlotId> {
|
||||
match s {
|
||||
"9a" => Ok(SlotId::Authentication),
|
||||
"9c" => Ok(SlotId::Signature),
|
||||
"9d" => Ok(SlotId::KeyManagement),
|
||||
"9e" => Ok(SlotId::CardAuthentication),
|
||||
"f9" => Ok(SlotId::Attestation),
|
||||
_ => RetiredSlotId::try_from(s).map(SlotId::Retired),
|
||||
_ => s.parse().map(SlotId::Retired),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -209,7 +209,7 @@ pub enum RetiredSlotId {
|
||||
impl TryFrom<u8> for RetiredSlotId {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
fn try_from(value: u8) -> Result<Self> {
|
||||
match value {
|
||||
0x82 => Ok(RetiredSlotId::R1),
|
||||
0x83 => Ok(RetiredSlotId::R2),
|
||||
@@ -236,11 +236,11 @@ impl TryFrom<u8> for RetiredSlotId {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for RetiredSlotId {
|
||||
type Error = Error;
|
||||
impl FromStr for RetiredSlotId {
|
||||
type Err = Error;
|
||||
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||
match value.as_ref() {
|
||||
fn from_str(value: &str) -> Result<Self> {
|
||||
match value {
|
||||
"82" => Ok(RetiredSlotId::R1),
|
||||
"83" => Ok(RetiredSlotId::R2),
|
||||
"84" => Ok(RetiredSlotId::R3),
|
||||
@@ -354,10 +354,13 @@ pub const SLOTS: [SlotId; 24] = [
|
||||
pub enum AlgorithmId {
|
||||
/// 1024-bit RSA.
|
||||
Rsa1024,
|
||||
|
||||
/// 2048-bit RSA.
|
||||
Rsa2048,
|
||||
|
||||
/// ECDSA with the NIST P256 curve.
|
||||
EccP256,
|
||||
|
||||
/// ECDSA with the NIST P384 curve.
|
||||
EccP384,
|
||||
}
|
||||
@@ -365,7 +368,7 @@ pub enum AlgorithmId {
|
||||
impl TryFrom<u8> for AlgorithmId {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
fn try_from(value: u8) -> Result<Self> {
|
||||
match value {
|
||||
0x06 => Ok(AlgorithmId::Rsa1024),
|
||||
0x07 => Ok(AlgorithmId::Rsa2048),
|
||||
@@ -389,11 +392,12 @@ impl From<AlgorithmId> for u8 {
|
||||
|
||||
impl AlgorithmId {
|
||||
/// Writes the `AlgorithmId` in the format the YubiKey expects during key generation.
|
||||
pub(crate) fn write(self, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
pub(crate) fn write(self, buf: &mut [u8]) -> Result<usize> {
|
||||
Tlv::write(buf, 0x80, &[self.into()])
|
||||
}
|
||||
|
||||
#[cfg(feature = "untested")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
fn get_elem_len(self) -> usize {
|
||||
match self {
|
||||
AlgorithmId::Rsa1024 => 64,
|
||||
@@ -404,6 +408,7 @@ impl AlgorithmId {
|
||||
}
|
||||
|
||||
#[cfg(feature = "untested")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
fn get_param_tag(self) -> u8 {
|
||||
match self {
|
||||
AlgorithmId::Rsa1024 | AlgorithmId::Rsa2048 => 0x01,
|
||||
@@ -424,7 +429,7 @@ pub struct Key {
|
||||
|
||||
impl Key {
|
||||
/// List Personal Identity Verification (PIV) keys stored in a YubiKey
|
||||
pub fn list(yubikey: &mut YubiKey) -> Result<Vec<Self>, Error> {
|
||||
pub fn list(yubikey: &mut YubiKey) -> Result<Vec<Self>> {
|
||||
let mut keys = vec![];
|
||||
let txn = yubikey.begin_transaction()?;
|
||||
|
||||
@@ -457,15 +462,14 @@ impl Key {
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate key
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
/// Generate new key.
|
||||
pub fn generate(
|
||||
yubikey: &mut YubiKey,
|
||||
slot: SlotId,
|
||||
algorithm: AlgorithmId,
|
||||
pin_policy: PinPolicy,
|
||||
touch_policy: TouchPolicy,
|
||||
) -> Result<PublicKeyInfo, Error> {
|
||||
) -> Result<PublicKeyInfo> {
|
||||
// Keygen messages
|
||||
// TODO(tarcieri): extract these into an I18N-handling type?
|
||||
const SZ_SETTING_ROCA: &str = "Enable_Unsafe_Keygen_ROCA";
|
||||
@@ -477,7 +481,7 @@ pub fn generate(
|
||||
const SZ_ROCA_BLOCK_ADMIN: &str = "was blocked due to an administrator configuration setting.";
|
||||
const SZ_ROCA_DEFAULT: &str = "was permitted by default, but is not recommended. The default behavior will change in a future Yubico release.";
|
||||
|
||||
let setting_roca: settings::BoolValue;
|
||||
let setting_roca: setting::Setting;
|
||||
|
||||
match algorithm {
|
||||
AlgorithmId::Rsa1024 | AlgorithmId::Rsa2048 => {
|
||||
@@ -485,17 +489,17 @@ pub fn generate(
|
||||
&& (yubikey.version.minor < 3
|
||||
|| yubikey.version.minor == 3 && (yubikey.version.patch < 5))
|
||||
{
|
||||
setting_roca = settings::BoolValue::get(SZ_SETTING_ROCA, true);
|
||||
setting_roca = setting::Setting::get(SZ_SETTING_ROCA, true);
|
||||
|
||||
let psz_msg = match setting_roca.source {
|
||||
settings::Source::User => {
|
||||
setting::SettingSource::User => {
|
||||
if setting_roca.value {
|
||||
SZ_ROCA_ALLOW_USER
|
||||
} else {
|
||||
SZ_ROCA_BLOCK_USER
|
||||
}
|
||||
}
|
||||
settings::Source::Admin => {
|
||||
setting::SettingSource::Admin => {
|
||||
if setting_roca.value {
|
||||
SZ_ROCA_ALLOW_ADMIN
|
||||
} else {
|
||||
@@ -664,6 +668,7 @@ pub fn generate(
|
||||
}
|
||||
|
||||
#[cfg(feature = "untested")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
fn write_key(
|
||||
yubikey: &mut YubiKey,
|
||||
slot: SlotId,
|
||||
@@ -671,7 +676,7 @@ fn write_key(
|
||||
pin_policy: PinPolicy,
|
||||
touch_policy: TouchPolicy,
|
||||
algorithm: AlgorithmId,
|
||||
) -> Result<(), Error> {
|
||||
) -> Result<()> {
|
||||
let mut key_data = Buffer::new(vec![0u8; KEYDATA_LEN]);
|
||||
let templ = [0, Ins::ImportKey.code(), algorithm.into(), slot.into()];
|
||||
let mut offset = 0;
|
||||
@@ -712,6 +717,7 @@ fn write_key(
|
||||
|
||||
/// The key data that makes up an RSA key.
|
||||
#[cfg(feature = "untested")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub struct RsaKeyData {
|
||||
/// The secret prime `p`.
|
||||
p: Buffer,
|
||||
@@ -773,6 +779,7 @@ impl RsaKeyData {
|
||||
///
|
||||
/// Errors if `algorithm` isn't `AlgorithmId::Rsa1024` or `AlgorithmId::Rsa2048`.
|
||||
#[cfg(feature = "untested")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub fn import_rsa_key(
|
||||
yubikey: &mut YubiKey,
|
||||
slot: SlotId,
|
||||
@@ -780,7 +787,7 @@ pub fn import_rsa_key(
|
||||
key_data: RsaKeyData,
|
||||
touch_policy: TouchPolicy,
|
||||
pin_policy: PinPolicy,
|
||||
) -> Result<(), Error> {
|
||||
) -> Result<()> {
|
||||
match algorithm {
|
||||
AlgorithmId::Rsa1024 | AlgorithmId::Rsa2048 => (),
|
||||
_ => return Err(Error::AlgorithmError),
|
||||
@@ -807,6 +814,7 @@ pub fn import_rsa_key(
|
||||
///
|
||||
/// Errors if `algorithm` isn't `AlgorithmId::EccP256` or ` AlgorithmId::EccP384`.
|
||||
#[cfg(feature = "untested")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub fn import_ecc_key(
|
||||
yubikey: &mut YubiKey,
|
||||
slot: SlotId,
|
||||
@@ -814,7 +822,7 @@ pub fn import_ecc_key(
|
||||
key_data: &[u8],
|
||||
touch_policy: TouchPolicy,
|
||||
pin_policy: PinPolicy,
|
||||
) -> Result<(), Error> {
|
||||
) -> Result<()> {
|
||||
match algorithm {
|
||||
AlgorithmId::EccP256 | AlgorithmId::EccP384 => (),
|
||||
_ => return Err(Error::AlgorithmError),
|
||||
@@ -832,9 +840,11 @@ pub fn import_ecc_key(
|
||||
}
|
||||
|
||||
/// Generate an attestation certificate for a stored key.
|
||||
///
|
||||
/// <https://developers.yubico.com/PIV/Introduction/PIV_attestation.html>
|
||||
#[cfg(feature = "untested")]
|
||||
pub fn attest(yubikey: &mut YubiKey, key: SlotId) -> Result<Buffer, Error> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub fn attest(yubikey: &mut YubiKey, key: SlotId) -> Result<Buffer> {
|
||||
let templ = [0, Ins::Attest.code(), key.into(), 0];
|
||||
let txn = yubikey.begin_transaction()?;
|
||||
let response = txn.transfer_data(&templ, &[], CB_OBJ_MAX)?;
|
||||
@@ -854,27 +864,28 @@ pub fn attest(yubikey: &mut YubiKey, key: SlotId) -> Result<Buffer, Error> {
|
||||
Ok(Buffer::new(response.data().into()))
|
||||
}
|
||||
|
||||
/// Sign data using a PIV key
|
||||
/// Sign data using a PIV key.
|
||||
pub fn sign_data(
|
||||
yubikey: &mut YubiKey,
|
||||
raw_in: &[u8],
|
||||
algorithm: AlgorithmId,
|
||||
key: SlotId,
|
||||
) -> Result<Buffer, Error> {
|
||||
) -> Result<Buffer> {
|
||||
let txn = yubikey.begin_transaction()?;
|
||||
|
||||
// don't attempt to reselect in crypt operations to avoid problems with PIN_ALWAYS
|
||||
txn.authenticated_command(raw_in, algorithm, key, false)
|
||||
}
|
||||
|
||||
/// Decrypt data using a PIV key
|
||||
/// Decrypt data using a PIV key.
|
||||
#[cfg(feature = "untested")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub fn decrypt_data(
|
||||
yubikey: &mut YubiKey,
|
||||
input: &[u8],
|
||||
algorithm: AlgorithmId,
|
||||
key: SlotId,
|
||||
) -> Result<Buffer, Error> {
|
||||
) -> Result<Buffer> {
|
||||
let txn = yubikey.begin_transaction()?;
|
||||
|
||||
// don't attempt to reselect in crypt operations to avoid problems with PIN_ALWAYS
|
||||
+9
-7
@@ -1,10 +1,11 @@
|
||||
//! Enums representing key policies.
|
||||
|
||||
use crate::{error::Error, serialization::Tlv};
|
||||
use crate::{serialization::Tlv, Result};
|
||||
|
||||
/// Specifies how often the PIN needs to be entered for access to the credential in a
|
||||
/// given slot. This policy must be set upon key generation or importation, and cannot be
|
||||
/// changed later.
|
||||
/// given slot.
|
||||
///
|
||||
/// This policy must be set when keys are generated or imported, and cannot be changed later.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum PinPolicy {
|
||||
/// Use the default PIN policy for the slot. See the slot's documentation for details.
|
||||
@@ -37,7 +38,7 @@ impl From<PinPolicy> for u8 {
|
||||
impl PinPolicy {
|
||||
/// Writes the `PinPolicy` in the format the YubiKey expects during key generation or
|
||||
/// importation.
|
||||
pub(crate) fn write(self, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
pub(crate) fn write(self, buf: &mut [u8]) -> Result<usize> {
|
||||
match self {
|
||||
PinPolicy::Default => Ok(0),
|
||||
_ => Tlv::write(buf, 0xaa, &[self.into()]),
|
||||
@@ -46,8 +47,9 @@ impl PinPolicy {
|
||||
}
|
||||
|
||||
/// Specifies under what conditions a physical touch on the metal contact is required, in
|
||||
/// addition to the [`PinPolicy`]. This policy must be set upon key generation or
|
||||
/// importation, and cannot be changed later.
|
||||
/// addition to the [`PinPolicy`].
|
||||
///
|
||||
/// This policy must be set when keys are generated or imported, and cannot be changed later.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum TouchPolicy {
|
||||
/// Use the default touch policy for the slot.
|
||||
@@ -81,7 +83,7 @@ impl From<TouchPolicy> for u8 {
|
||||
impl TouchPolicy {
|
||||
/// Writes the `TouchPolicy` in the format the YubiKey expects during key generation
|
||||
/// or importation.
|
||||
pub(crate) fn write(self, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
pub(crate) fn write(self, buf: &mut [u8]) -> Result<usize> {
|
||||
match self {
|
||||
TouchPolicy::Default => Ok(0),
|
||||
_ => Tlv::write(buf, 0xab, &[self.into()]),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Support for enumerating available readers
|
||||
//! Support for enumerating available PC/SC card readers.
|
||||
|
||||
use crate::{error::Error, yubikey::YubiKey};
|
||||
use crate::{Result, YubiKey};
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
convert::TryInto,
|
||||
@@ -11,8 +11,8 @@ use std::{
|
||||
/// Iterator over connected readers
|
||||
pub type Iter<'ctx> = std::vec::IntoIter<Reader<'ctx>>;
|
||||
|
||||
/// Enumeration support for available readers
|
||||
pub struct Readers {
|
||||
/// PC/SC reader context: used to enumerate available PC/SC [`Reader`]s.
|
||||
pub struct Context {
|
||||
/// PC/SC context
|
||||
ctx: Arc<Mutex<pcsc::Context>>,
|
||||
|
||||
@@ -20,10 +20,10 @@ pub struct Readers {
|
||||
reader_names: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Readers {
|
||||
impl Context {
|
||||
/// Open a PC/SC context, which can be used to enumerate available PC/SC
|
||||
/// readers (which can be used to connect to YubiKeys).
|
||||
pub fn open() -> Result<Self, Error> {
|
||||
pub fn open() -> Result<Self> {
|
||||
let ctx = pcsc::Context::establish(pcsc::Scope::System)?;
|
||||
let reader_names = vec![0u8; ctx.list_readers_len()?];
|
||||
Ok(Self {
|
||||
@@ -32,8 +32,8 @@ impl Readers {
|
||||
})
|
||||
}
|
||||
|
||||
/// Iterate over the available readers
|
||||
pub fn iter(&mut self) -> Result<Iter<'_>, Error> {
|
||||
/// Iterate over the available readers.
|
||||
pub fn iter(&mut self) -> Result<Iter<'_>> {
|
||||
let Self { ctx, reader_names } = self;
|
||||
|
||||
let reader_cstrs: Vec<_> = {
|
||||
@@ -54,7 +54,7 @@ impl Readers {
|
||||
}
|
||||
}
|
||||
|
||||
/// An individual connected reader
|
||||
/// An individual connected PC/SC card reader.
|
||||
pub struct Reader<'ctx> {
|
||||
/// Name of this reader
|
||||
name: &'ctx CStr,
|
||||
@@ -64,25 +64,25 @@ pub struct Reader<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> Reader<'ctx> {
|
||||
/// Create a new reader from its name and context
|
||||
/// Create a new reader from its name and context.
|
||||
fn new(name: &'ctx CStr, ctx: Arc<Mutex<pcsc::Context>>) -> Self {
|
||||
// TODO(tarcieri): open devices, determine they're YubiKeys, get serial?
|
||||
Self { name, ctx }
|
||||
}
|
||||
|
||||
/// Get this reader's name
|
||||
/// Get this reader's name.
|
||||
pub fn name(&self) -> Cow<'_, str> {
|
||||
// TODO(tarcieri): is lossy ok here? try to avoid lossiness?
|
||||
self.name.to_string_lossy()
|
||||
}
|
||||
|
||||
/// Open a connection to this reader, returning a `YubiKey` if successful
|
||||
pub fn open(&self) -> Result<YubiKey, Error> {
|
||||
/// Open a connection to this reader, returning a `YubiKey` if successful.
|
||||
pub fn open(&self) -> Result<YubiKey> {
|
||||
self.try_into()
|
||||
}
|
||||
|
||||
/// Connect to this reader, returning its `pcsc::Card`
|
||||
pub(crate) fn connect(&self) -> Result<pcsc::Card, Error> {
|
||||
/// Connect to this reader, returning its `pcsc::Card`.
|
||||
pub(crate) fn connect(&self) -> Result<pcsc::Card> {
|
||||
let ctx = self.ctx.lock().unwrap();
|
||||
Ok(ctx.connect(self.name, pcsc::ShareMode::Shared, pcsc::Protocols::T1)?)
|
||||
}
|
||||
+13
-10
@@ -30,7 +30,7 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
use crate::{error::Error, Buffer, ObjectId, CB_OBJ_TAG_MIN};
|
||||
use crate::{consts::CB_OBJ_TAG_MIN, Buffer, Error, ObjectId, Result};
|
||||
|
||||
pub const OBJ_DISCOVERY: u32 = 0x7e;
|
||||
|
||||
@@ -44,24 +44,27 @@ pub(crate) struct Tlv<'a> {
|
||||
|
||||
impl<'a> Tlv<'a> {
|
||||
/// Parses a `Tlv` from a buffer, returning the remainder of the buffer.
|
||||
pub(crate) fn parse(buffer: &'a [u8]) -> Result<(&'a [u8], Self), Error> {
|
||||
pub(crate) fn parse(buffer: &'a [u8]) -> Result<(&'a [u8], Self)> {
|
||||
if buffer.len() < CB_OBJ_TAG_MIN || !has_valid_length(&buffer[1..], buffer.len() - 1) {
|
||||
return Err(Error::SizeError);
|
||||
}
|
||||
|
||||
let tag = buffer[0];
|
||||
|
||||
let mut len = 0;
|
||||
let offset = 1 + get_length(&buffer[1..], &mut len);
|
||||
let buffer = buffer.get(offset..).ok_or(Error::SizeError)?;
|
||||
|
||||
let (value, buffer) = buffer[offset..].split_at(len);
|
||||
|
||||
Ok((buffer, Tlv { tag, value }))
|
||||
if buffer.len() >= len {
|
||||
let (value, buffer) = buffer.split_at(len);
|
||||
Ok((buffer, Tlv { tag, value }))
|
||||
} else {
|
||||
Err(Error::SizeError)
|
||||
}
|
||||
}
|
||||
|
||||
/// Takes a [`Buffer`] containing a single `Tlv` with the given tag, and returns a
|
||||
/// `Buffer` containing only the value part of the `Tlv`.
|
||||
pub(crate) fn parse_single(mut buffer: Buffer, tag: u8) -> Result<Buffer, Error> {
|
||||
pub(crate) fn parse_single(mut buffer: Buffer, tag: u8) -> Result<Buffer> {
|
||||
if buffer.len() < CB_OBJ_TAG_MIN || !has_valid_length(&buffer[1..], buffer.len() - 1) {
|
||||
return Err(Error::SizeError);
|
||||
}
|
||||
@@ -79,7 +82,7 @@ impl<'a> Tlv<'a> {
|
||||
}
|
||||
|
||||
/// Writes a TLV to the given buffer.
|
||||
pub(crate) fn write(buffer: &mut [u8], tag: u8, value: &[u8]) -> Result<usize, Error> {
|
||||
pub(crate) fn write(buffer: &mut [u8], tag: u8, value: &[u8]) -> Result<usize> {
|
||||
if buffer.len() < CB_OBJ_TAG_MIN {
|
||||
return Err(Error::SizeError);
|
||||
}
|
||||
@@ -103,7 +106,7 @@ impl<'a> Tlv<'a> {
|
||||
tag: u8,
|
||||
length: usize,
|
||||
value: Gen,
|
||||
) -> Result<usize, Error>
|
||||
) -> Result<usize>
|
||||
where
|
||||
Gen: FnOnce(&mut [u8]),
|
||||
{
|
||||
@@ -124,7 +127,7 @@ impl<'a> Tlv<'a> {
|
||||
}
|
||||
|
||||
/// Set length
|
||||
pub(crate) fn set_length(buffer: &mut [u8], length: usize) -> Result<usize, Error> {
|
||||
pub(crate) fn set_length(buffer: &mut [u8], length: usize) -> Result<usize> {
|
||||
if length < 0x80 {
|
||||
if buffer.is_empty() {
|
||||
Err(Error::SizeError)
|
||||
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
//! Configuration setting values parsed from the environment and config file:
|
||||
//! `/etc/yubico/yubikeypiv.conf`
|
||||
|
||||
// Adapted from yubico-piv-tool:
|
||||
// <https://github.com/Yubico/yubico-piv-tool/>
|
||||
//
|
||||
// Copyright (c) 2014-2016 Yubico AB
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
/// Default location of the YubiKey PIV configuration file
|
||||
pub const DEFAULT_CONFIG_FILE: &str = "/etc/yubico/yubikeypiv.conf";
|
||||
|
||||
use std::{
|
||||
env,
|
||||
fs::File,
|
||||
io::{BufRead, BufReader},
|
||||
};
|
||||
|
||||
/// Source of how a setting was configured.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum SettingSource {
|
||||
/// User-specified setting: sourced via `YUBIKEY_PIV_*` environment vars.
|
||||
User,
|
||||
|
||||
/// Admin-specified setting: sourced via the `/etc/yubico/yubikeypiv.conf`
|
||||
/// configuration file.
|
||||
Admin,
|
||||
|
||||
/// Default setting.
|
||||
Default,
|
||||
}
|
||||
|
||||
impl Default for SettingSource {
|
||||
fn default() -> Self {
|
||||
Self::Default
|
||||
}
|
||||
}
|
||||
|
||||
/// Setting booleans: configuration values sourced from a file or the environment.
|
||||
///
|
||||
/// These can be configured globally in `/etc/yubico/yubikeypiv.conf` by a
|
||||
/// system administrator, or by the local user via `YUBIKEY_PIV_*` environment
|
||||
/// variables.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Setting {
|
||||
/// Boolean value
|
||||
pub value: bool,
|
||||
|
||||
/// Source of the configuration setting (user, admin, or default)
|
||||
pub source: SettingSource,
|
||||
}
|
||||
|
||||
impl Setting {
|
||||
/// Get a setting by name.
|
||||
pub fn get(key: &str, default: bool) -> Self {
|
||||
Self::from_file(key)
|
||||
.or_else(|| Self::from_env(key))
|
||||
.unwrap_or(Self {
|
||||
value: default,
|
||||
source: SettingSource::Default,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a boolean config value from the provided config file
|
||||
fn from_file(key: &str) -> Option<Self> {
|
||||
if let Ok(file) = File::open(DEFAULT_CONFIG_FILE) {
|
||||
for line in BufReader::new(file).lines() {
|
||||
let line = match line {
|
||||
Ok(line) => line,
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
if line.starts_with('#') || line.starts_with('\r') || line.starts_with('\n') {
|
||||
continue;
|
||||
}
|
||||
|
||||
let (name, value) = {
|
||||
let mut parts = line.splitn(1, '=');
|
||||
let name = parts.next();
|
||||
let value = parts.next();
|
||||
match (name, value, parts.next()) {
|
||||
(Some(name), Some(value), None) => (name.trim(), value.trim()),
|
||||
_ => continue,
|
||||
}
|
||||
};
|
||||
|
||||
if name == key {
|
||||
return Some(Setting {
|
||||
source: SettingSource::Admin,
|
||||
value: value == "1" || value == "true",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Get a setting boolean from an environment variable
|
||||
fn from_env(key: &str) -> Option<Self> {
|
||||
env::var(format!("YUBIKEY_PIV_{}", key))
|
||||
.ok()
|
||||
.map(|value| Setting {
|
||||
source: SettingSource::User,
|
||||
value: value == "1" || value == "true",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Setting {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
value: false,
|
||||
source: SettingSource::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
-138
@@ -1,138 +0,0 @@
|
||||
//! Configuration setting values parsed from the environment and config file:
|
||||
//! `/etc/yubico/yubikeypiv.conf`
|
||||
|
||||
// Adapted from yubico-piv-tool:
|
||||
// <https://github.com/Yubico/yubico-piv-tool/>
|
||||
//
|
||||
// Copyright (c) 2014-2016 Yubico AB
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
/// Default location of the YubiKey PIV configuration file
|
||||
pub const DEFAULT_CONFIG_FILE: &str = "/etc/yubico/yubikeypiv.conf";
|
||||
|
||||
use std::{
|
||||
env,
|
||||
fs::File,
|
||||
io::{BufRead, BufReader},
|
||||
};
|
||||
|
||||
/// Source of how a setting was configured
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Source {
|
||||
/// User-specified setting
|
||||
User,
|
||||
|
||||
/// Admin-specified setting
|
||||
Admin,
|
||||
|
||||
/// Default setting
|
||||
Default,
|
||||
}
|
||||
|
||||
/// Setting booleans
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct BoolValue {
|
||||
/// Boolean value
|
||||
pub value: bool,
|
||||
|
||||
/// Source of the configuration setting (user/admin/default)
|
||||
pub source: Source,
|
||||
}
|
||||
|
||||
impl BoolValue {
|
||||
/// Get a [`BoolValue`] value
|
||||
pub fn get(key: &str, def: bool) -> Self {
|
||||
let mut setting = get_setting_from_file(key);
|
||||
|
||||
if setting.source == Source::Default {
|
||||
setting = get_setting_from_env(key);
|
||||
}
|
||||
|
||||
if setting.source == Source::Default {
|
||||
setting.value = def;
|
||||
}
|
||||
|
||||
setting
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a boolean config value
|
||||
fn get_setting_from_file(key: &str) -> BoolValue {
|
||||
let mut setting: BoolValue = BoolValue {
|
||||
value: false,
|
||||
source: Source::Default,
|
||||
};
|
||||
|
||||
let file = match File::open(DEFAULT_CONFIG_FILE) {
|
||||
Ok(f) => f,
|
||||
Err(_) => return setting,
|
||||
};
|
||||
|
||||
for line in BufReader::new(file).lines() {
|
||||
let line = match line {
|
||||
Ok(line) => line,
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
if line.starts_with('#') || line.starts_with('\r') || line.starts_with('\n') {
|
||||
continue;
|
||||
}
|
||||
|
||||
let (name, value) = {
|
||||
let mut parts = line.splitn(1, '=');
|
||||
let name = parts.next();
|
||||
let value = parts.next();
|
||||
match (name, value, parts.next()) {
|
||||
(Some(name), Some(value), None) => (name.trim(), value.trim()),
|
||||
_ => continue,
|
||||
}
|
||||
};
|
||||
|
||||
if name == key {
|
||||
setting.source = Source::Admin;
|
||||
setting.value = value == "1" || value == "true";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
setting
|
||||
}
|
||||
|
||||
/// Get a setting boolean from an environment variable
|
||||
fn get_setting_from_env(key: &str) -> BoolValue {
|
||||
let mut setting: BoolValue = BoolValue {
|
||||
value: false,
|
||||
source: Source::Default,
|
||||
};
|
||||
|
||||
if let Ok(value) = env::var(format!("YUBIKEY_PIV_{}", key)) {
|
||||
setting.source = Source::User;
|
||||
setting.value = value == "1" || value == "true";
|
||||
}
|
||||
|
||||
setting
|
||||
}
|
||||
+33
-31
@@ -2,12 +2,13 @@
|
||||
|
||||
use crate::{
|
||||
apdu::Response,
|
||||
apdu::{Ins, StatusWords, APDU},
|
||||
error::Error,
|
||||
key::{AlgorithmId, SlotId},
|
||||
apdu::{Apdu, Ins, StatusWords},
|
||||
consts::{CB_BUF_MAX, CB_OBJ_MAX},
|
||||
error::{Error, Result},
|
||||
piv::{AlgorithmId, SlotId},
|
||||
serialization::*,
|
||||
yubikey::*,
|
||||
Buffer, ObjectId, CB_BUF_MAX, CB_OBJ_MAX, PIV_AID, YK_AID,
|
||||
Buffer, ObjectId,
|
||||
};
|
||||
use log::{error, trace};
|
||||
use std::convert::TryInto;
|
||||
@@ -16,6 +17,12 @@ use zeroize::Zeroizing;
|
||||
#[cfg(feature = "untested")]
|
||||
use crate::mgm::{MgmKey, DES_LEN_3DES};
|
||||
|
||||
/// PIV Applet ID
|
||||
const PIV_AID: [u8; 5] = [0xa0, 0x00, 0x00, 0x03, 0x08];
|
||||
|
||||
/// YubiKey OTP Applet ID. Needed to query serial on YK4.
|
||||
const YK_AID: [u8; 8] = [0xa0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01, 0x01];
|
||||
|
||||
const CB_PIN_MAX: usize = 8;
|
||||
|
||||
#[cfg(feature = "untested")]
|
||||
@@ -32,7 +39,7 @@ pub(crate) struct Transaction<'tx> {
|
||||
|
||||
impl<'tx> Transaction<'tx> {
|
||||
/// Create a new transaction with the given card.
|
||||
pub fn new(card: &'tx mut pcsc::Card) -> Result<Self, Error> {
|
||||
pub fn new(card: &'tx mut pcsc::Card) -> Result<Self> {
|
||||
Ok(Transaction {
|
||||
inner: card.transaction()?,
|
||||
})
|
||||
@@ -45,7 +52,7 @@ impl<'tx> Transaction<'tx> {
|
||||
/// single APDU messages at a time. For larger messages that need to be
|
||||
/// split into multiple APDUs, use the [`Transaction::transfer_data`]
|
||||
/// method instead.
|
||||
pub fn transmit(&self, send_buffer: &[u8], recv_len: usize) -> Result<Vec<u8>, Error> {
|
||||
pub fn transmit(&self, send_buffer: &[u8], recv_len: usize) -> Result<Vec<u8>> {
|
||||
trace!(">>> {:?}", send_buffer);
|
||||
|
||||
let mut recv_buffer = vec![0u8; recv_len];
|
||||
@@ -60,8 +67,8 @@ impl<'tx> Transaction<'tx> {
|
||||
}
|
||||
|
||||
/// Select application.
|
||||
pub fn select_application(&self) -> Result<(), Error> {
|
||||
let response = APDU::new(Ins::SelectApplication)
|
||||
pub fn select_application(&self) -> Result<()> {
|
||||
let response = Apdu::new(Ins::SelectApplication)
|
||||
.p1(0x04)
|
||||
.data(&PIV_AID)
|
||||
.transmit(self, 0xFF)
|
||||
@@ -82,9 +89,9 @@ impl<'tx> Transaction<'tx> {
|
||||
}
|
||||
|
||||
/// Get the version of the PIV application installed on the YubiKey.
|
||||
pub fn get_version(&self) -> Result<Version, Error> {
|
||||
pub fn get_version(&self) -> Result<Version> {
|
||||
// get version from device
|
||||
let response = APDU::new(Ins::GetVersion).transmit(self, 261)?;
|
||||
let response = Apdu::new(Ins::GetVersion).transmit(self, 261)?;
|
||||
|
||||
if !response.is_success() {
|
||||
return Err(Error::GenericError);
|
||||
@@ -98,10 +105,10 @@ impl<'tx> Transaction<'tx> {
|
||||
}
|
||||
|
||||
/// Get YubiKey device serial number.
|
||||
pub fn get_serial(&self, version: Version) -> Result<Serial, Error> {
|
||||
pub fn get_serial(&self, version: Version) -> Result<Serial> {
|
||||
let response = if version.major < 5 {
|
||||
// YK4 requires switching to the yk applet to retrieve the serial
|
||||
let sw = APDU::new(Ins::SelectApplication)
|
||||
let sw = Apdu::new(Ins::SelectApplication)
|
||||
.p1(0x04)
|
||||
.data(&YK_AID)
|
||||
.transmit(self, 0xFF)?
|
||||
@@ -112,7 +119,7 @@ impl<'tx> Transaction<'tx> {
|
||||
return Err(Error::GenericError);
|
||||
}
|
||||
|
||||
let resp = APDU::new(0x01).p1(0x10).transmit(self, 0xFF)?;
|
||||
let resp = Apdu::new(0x01).p1(0x10).transmit(self, 0xFF)?;
|
||||
|
||||
if !resp.is_success() {
|
||||
error!(
|
||||
@@ -123,7 +130,7 @@ impl<'tx> Transaction<'tx> {
|
||||
}
|
||||
|
||||
// reselect the PIV applet
|
||||
let sw = APDU::new(Ins::SelectApplication)
|
||||
let sw = Apdu::new(Ins::SelectApplication)
|
||||
.p1(0x04)
|
||||
.data(&PIV_AID)
|
||||
.transmit(self, 0xFF)?
|
||||
@@ -137,7 +144,7 @@ impl<'tx> Transaction<'tx> {
|
||||
resp
|
||||
} else {
|
||||
// YK5 implements getting the serial as a PIV applet command (0xf8)
|
||||
let resp = APDU::new(Ins::GetSerial).transmit(self, 0xFF)?;
|
||||
let resp = Apdu::new(Ins::GetSerial).transmit(self, 0xFF)?;
|
||||
|
||||
if !resp.is_success() {
|
||||
error!(
|
||||
@@ -157,12 +164,12 @@ impl<'tx> Transaction<'tx> {
|
||||
}
|
||||
|
||||
/// Verify device PIN.
|
||||
pub fn verify_pin(&self, pin: &[u8]) -> Result<(), Error> {
|
||||
pub fn verify_pin(&self, pin: &[u8]) -> Result<()> {
|
||||
if pin.len() > CB_PIN_MAX {
|
||||
return Err(Error::SizeError);
|
||||
}
|
||||
|
||||
let mut query = APDU::new(Ins::Verify);
|
||||
let mut query = Apdu::new(Ins::Verify);
|
||||
query.params(0x00, 0x80);
|
||||
|
||||
// Empty pin means we are querying the number of retries. We set no data in this
|
||||
@@ -191,7 +198,7 @@ impl<'tx> Transaction<'tx> {
|
||||
action: ChangeRefAction,
|
||||
current_pin: &[u8],
|
||||
new_pin: &[u8],
|
||||
) -> Result<(), Error> {
|
||||
) -> Result<()> {
|
||||
if current_pin.len() > CB_PIN_MAX || new_pin.len() > CB_PIN_MAX {
|
||||
return Err(Error::SizeError);
|
||||
}
|
||||
@@ -229,7 +236,7 @@ impl<'tx> Transaction<'tx> {
|
||||
|
||||
/// Set the management key (MGM).
|
||||
#[cfg(feature = "untested")]
|
||||
pub fn set_mgm_key(&self, new_key: &MgmKey, require_touch: bool) -> Result<(), Error> {
|
||||
pub fn set_mgm_key(&self, new_key: &MgmKey, require_touch: bool) -> Result<()> {
|
||||
let p2 = if require_touch { 0xfe } else { 0xff };
|
||||
|
||||
let mut data = [0u8; DES_LEN_3DES + 3];
|
||||
@@ -238,7 +245,7 @@ impl<'tx> Transaction<'tx> {
|
||||
data[2] = DES_LEN_3DES as u8;
|
||||
data[3..3 + DES_LEN_3DES].copy_from_slice(new_key.as_ref());
|
||||
|
||||
let status_words = APDU::new(Ins::SetMgmKey)
|
||||
let status_words = Apdu::new(Ins::SetMgmKey)
|
||||
.params(0xff, p2)
|
||||
.data(&data)
|
||||
.transmit(self, 261)?
|
||||
@@ -263,7 +270,7 @@ impl<'tx> Transaction<'tx> {
|
||||
algorithm: AlgorithmId,
|
||||
key: SlotId,
|
||||
decipher: bool,
|
||||
) -> Result<Buffer, Error> {
|
||||
) -> Result<Buffer> {
|
||||
let in_len = sign_in.len();
|
||||
let mut indata = [0u8; 1024];
|
||||
let templ = [0, Ins::Authenticate.code(), algorithm.into(), key.into()];
|
||||
@@ -358,12 +365,7 @@ impl<'tx> Transaction<'tx> {
|
||||
/// messages into smaller APDU-sized messages (using the provided APDU
|
||||
/// template to construct them), and then sending those via
|
||||
/// [`Transaction::transmit`].
|
||||
pub fn transfer_data(
|
||||
&self,
|
||||
templ: &[u8],
|
||||
in_data: &[u8],
|
||||
max_out: usize,
|
||||
) -> Result<Response, Error> {
|
||||
pub fn transfer_data(&self, templ: &[u8], in_data: &[u8], max_out: usize) -> Result<Response> {
|
||||
let mut in_offset = 0;
|
||||
let mut out_data = vec![];
|
||||
let mut sw;
|
||||
@@ -380,7 +382,7 @@ impl<'tx> Transaction<'tx> {
|
||||
|
||||
trace!("going to send {} bytes in this go", this_size);
|
||||
|
||||
let response = APDU::new(templ[1])
|
||||
let response = Apdu::new(templ[1])
|
||||
.cla(cla)
|
||||
.params(templ[2], templ[3])
|
||||
.data(&in_data[in_offset..(in_offset + this_size)])
|
||||
@@ -417,7 +419,7 @@ impl<'tx> Transaction<'tx> {
|
||||
sw & 0xff
|
||||
);
|
||||
|
||||
let response = APDU::new(Ins::GetResponseApdu).transmit(self, 261)?;
|
||||
let response = Apdu::new(Ins::GetResponseApdu).transmit(self, 261)?;
|
||||
sw = response.status_words().code();
|
||||
|
||||
if sw != StatusWords::Success.code() && (sw >> 8 != 0x61) {
|
||||
@@ -441,7 +443,7 @@ impl<'tx> Transaction<'tx> {
|
||||
}
|
||||
|
||||
/// Fetch an object.
|
||||
pub fn fetch_object(&self, object_id: ObjectId) -> Result<Buffer, Error> {
|
||||
pub fn fetch_object(&self, object_id: ObjectId) -> Result<Buffer> {
|
||||
let mut indata = [0u8; 5];
|
||||
let templ = [0, Ins::GetData.code(), 0x3f, 0xff];
|
||||
|
||||
@@ -475,7 +477,7 @@ impl<'tx> Transaction<'tx> {
|
||||
}
|
||||
|
||||
/// Save an object.
|
||||
pub fn save_object(&self, object_id: ObjectId, indata: &[u8]) -> Result<(), Error> {
|
||||
pub fn save_object(&self, object_id: ObjectId, indata: &[u8]) -> Result<()> {
|
||||
let templ = [0, Ins::PutData.code(), 0x3f, 0xff];
|
||||
|
||||
// TODO(tarcieri): replace with vector
|
||||
|
||||
+114
-91
@@ -31,17 +31,19 @@
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
use crate::{
|
||||
apdu::{Ins, APDU},
|
||||
cccid::CCC,
|
||||
chuid::CHUID,
|
||||
apdu::{Apdu, Ins},
|
||||
cccid::CccId,
|
||||
chuid::ChuId,
|
||||
config::Config,
|
||||
error::Error,
|
||||
error::{Error, Result},
|
||||
mgm::MgmKey,
|
||||
readers::{Reader, Readers},
|
||||
piv,
|
||||
reader::{Context, Reader},
|
||||
transaction::Transaction,
|
||||
};
|
||||
use log::{error, info};
|
||||
use pcsc::Card;
|
||||
use rand_core::{OsRng, RngCore};
|
||||
use std::{
|
||||
convert::{TryFrom, TryInto},
|
||||
fmt::{self, Display},
|
||||
@@ -49,15 +51,17 @@ use std::{
|
||||
};
|
||||
|
||||
#[cfg(feature = "untested")]
|
||||
use crate::{
|
||||
apdu::StatusWords, metadata::AdminData, transaction::ChangeRefAction, Buffer, ObjectId,
|
||||
MGMT_AID, TAG_ADMIN_FLAGS_1, TAG_ADMIN_TIMESTAMP,
|
||||
use {
|
||||
crate::{
|
||||
apdu::StatusWords,
|
||||
consts::{TAG_ADMIN_FLAGS_1, TAG_ADMIN_TIMESTAMP},
|
||||
metadata::AdminData,
|
||||
transaction::ChangeRefAction,
|
||||
Buffer, ObjectId,
|
||||
},
|
||||
secrecy::ExposeSecret,
|
||||
std::time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
use getrandom::getrandom;
|
||||
#[cfg(feature = "untested")]
|
||||
use secrecy::ExposeSecret;
|
||||
#[cfg(feature = "untested")]
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
/// Flag for PUK blocked
|
||||
pub(crate) const ADMIN_FLAGS_1_PUK_BLOCKED: u8 = 0x01;
|
||||
@@ -68,12 +72,18 @@ pub(crate) const ALGO_3DES: u8 = 0x03;
|
||||
/// Card management key
|
||||
pub(crate) const KEY_CARDMGM: u8 = 0x9b;
|
||||
|
||||
/// MGMT Applet ID.
|
||||
///
|
||||
/// <https://developers.yubico.com/PIV/Introduction/Admin_access.html>
|
||||
#[cfg(feature = "untested")]
|
||||
const MGMT_AID: [u8; 8] = [0xa0, 0x00, 0x00, 0x05, 0x27, 0x47, 0x11, 0x17];
|
||||
|
||||
const TAG_DYN_AUTH: u8 = 0x7c;
|
||||
|
||||
/// Cached YubiKey PIN
|
||||
/// Cached YubiKey PIN.
|
||||
pub type CachedPin = secrecy::SecretVec<u8>;
|
||||
|
||||
/// YubiKey Serial Number
|
||||
/// YubiKey serial number.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub struct Serial(pub u32);
|
||||
|
||||
@@ -92,8 +102,8 @@ impl From<Serial> for u32 {
|
||||
impl FromStr for Serial {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Error> {
|
||||
u32::from_str(s).map(Serial).map_err(|_| Error::ParseError)
|
||||
fn from_str(s: &str) -> Result<Self> {
|
||||
s.parse().map(Serial).map_err(|_| Error::ParseError)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +113,7 @@ impl Display for Serial {
|
||||
}
|
||||
}
|
||||
|
||||
/// YubiKey Version
|
||||
/// YubiKey version.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Version {
|
||||
/// Major version component
|
||||
@@ -133,8 +143,7 @@ impl Display for Version {
|
||||
}
|
||||
}
|
||||
|
||||
/// YubiKey Device: this is the primary API for opening a session and
|
||||
/// performing various operations.
|
||||
/// YubiKey device: primary API for opening a session and performing various operations.
|
||||
///
|
||||
/// Almost all functionality in this library will require an open session
|
||||
/// with a YubiKey which is represented by this type.
|
||||
@@ -155,9 +164,10 @@ impl YubiKey {
|
||||
///
|
||||
/// If you need to operate in environments with more than one YubiKey
|
||||
/// attached to the same system, use [`YubiKey::open_by_serial`] or
|
||||
///[`yubikey_piv::Readers`] to select from the available PC/SC readers.
|
||||
pub fn open() -> Result<Self, Error> {
|
||||
let mut readers = Readers::open().map_err(|e| match e {
|
||||
/// [`yubikey::reader::Context`][`Context`] to select from the available
|
||||
/// PC/SC readers.
|
||||
pub fn open() -> Result<Self> {
|
||||
let mut readers = Context::open().map_err(|e| match e {
|
||||
Error::PcscError {
|
||||
inner: Some(pcsc::Error::NoReadersAvailable),
|
||||
} => Error::NotFound,
|
||||
@@ -179,8 +189,8 @@ impl YubiKey {
|
||||
}
|
||||
|
||||
/// Open a YubiKey with a specific serial number.
|
||||
pub fn open_by_serial(serial: Serial) -> Result<Self, Error> {
|
||||
let mut readers = Readers::open().map_err(|e| match e {
|
||||
pub fn open_by_serial(serial: Serial) -> Result<Self> {
|
||||
let mut readers = Context::open().map_err(|e| match e {
|
||||
Error::PcscError {
|
||||
inner: Some(pcsc::Error::NoReadersAvailable),
|
||||
} => Error::NotFound,
|
||||
@@ -202,9 +212,10 @@ impl YubiKey {
|
||||
Err(Error::NotFound)
|
||||
}
|
||||
|
||||
/// Reconnect to a YubiKey
|
||||
/// Reconnect to a YubiKey.
|
||||
#[cfg(feature = "untested")]
|
||||
pub fn reconnect(&mut self) -> Result<(), Error> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub fn reconnect(&mut self) -> Result<()> {
|
||||
info!("trying to reconnect to current reader");
|
||||
|
||||
self.card.reconnect(
|
||||
@@ -229,12 +240,12 @@ impl YubiKey {
|
||||
}
|
||||
|
||||
/// Begin a transaction.
|
||||
pub(crate) fn begin_transaction(&mut self) -> Result<Transaction<'_>, Error> {
|
||||
pub(crate) fn begin_transaction(&mut self) -> Result<Transaction<'_>> {
|
||||
// TODO(tarcieri): reconnect support
|
||||
Ok(Transaction::new(&mut self.card)?)
|
||||
Transaction::new(&mut self.card)
|
||||
}
|
||||
|
||||
/// Get the name of the associated PC/SC card reader
|
||||
/// Get the name of the associated PC/SC card reader.
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
@@ -254,26 +265,26 @@ impl YubiKey {
|
||||
}
|
||||
|
||||
/// Get device configuration.
|
||||
pub fn config(&mut self) -> Result<Config, Error> {
|
||||
pub fn config(&mut self) -> Result<Config> {
|
||||
Config::get(self)
|
||||
}
|
||||
|
||||
/// Get CHUID
|
||||
pub fn chuid(&mut self) -> Result<CHUID, Error> {
|
||||
CHUID::get(self)
|
||||
/// Get Cardholder Unique Identifier (CHUID).
|
||||
pub fn chuid(&mut self) -> Result<ChuId> {
|
||||
ChuId::get(self)
|
||||
}
|
||||
|
||||
/// Get CCCID
|
||||
pub fn cccid(&mut self) -> Result<CCC, Error> {
|
||||
CCC::get(self)
|
||||
/// Get Cardholder Capability Container (CCC) Identifier.
|
||||
pub fn cccid(&mut self) -> Result<CccId> {
|
||||
CccId::get(self)
|
||||
}
|
||||
|
||||
/// Authenticate to the card using the provided management key (MGM).
|
||||
pub fn authenticate(&mut self, mgm_key: MgmKey) -> Result<(), Error> {
|
||||
pub fn authenticate(&mut self, mgm_key: MgmKey) -> Result<()> {
|
||||
let txn = self.begin_transaction()?;
|
||||
|
||||
// get a challenge from the card
|
||||
let challenge = APDU::new(Ins::Authenticate)
|
||||
let challenge = Apdu::new(Ins::Authenticate)
|
||||
.params(ALGO_3DES, KEY_CARDMGM)
|
||||
.data(&[TAG_DYN_AUTH, 0x02, 0x80, 0x00])
|
||||
.transmit(&txn, 261)?;
|
||||
@@ -293,16 +304,12 @@ impl YubiKey {
|
||||
data[4..12].copy_from_slice(&response);
|
||||
data[12] = 0x81;
|
||||
data[13] = 8;
|
||||
|
||||
if getrandom(&mut data[14..22]).is_err() {
|
||||
error!("failed getting randomness for authentication");
|
||||
return Err(Error::RandomnessError);
|
||||
}
|
||||
OsRng.fill_bytes(&mut data[14..22]);
|
||||
|
||||
let mut challenge = [0u8; 8];
|
||||
challenge.copy_from_slice(&data[14..22]);
|
||||
|
||||
let authentication = APDU::new(Ins::Authenticate)
|
||||
let authentication = Apdu::new(Ins::Authenticate)
|
||||
.params(ALGO_3DES, KEY_CARDMGM)
|
||||
.data(&data)
|
||||
.transmit(&txn, 261)?;
|
||||
@@ -322,12 +329,18 @@ impl YubiKey {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Deauthenticate
|
||||
/// Get the PIV keys contained in this YubiKey.
|
||||
pub fn piv_keys(&mut self) -> Result<Vec<piv::Key>> {
|
||||
piv::Key::list(self)
|
||||
}
|
||||
|
||||
/// Deauthenticate.
|
||||
#[cfg(feature = "untested")]
|
||||
pub fn deauthenticate(&mut self) -> Result<(), Error> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub fn deauthenticate(&mut self) -> Result<()> {
|
||||
let txn = self.begin_transaction()?;
|
||||
|
||||
let status_words = APDU::new(Ins::SelectApplication)
|
||||
let status_words = Apdu::new(Ins::SelectApplication)
|
||||
.p1(0x04)
|
||||
.data(MGMT_AID)
|
||||
.transmit(&txn, 255)?
|
||||
@@ -345,7 +358,7 @@ impl YubiKey {
|
||||
}
|
||||
|
||||
/// Verify device PIN.
|
||||
pub fn verify_pin(&mut self, pin: &[u8]) -> Result<(), Error> {
|
||||
pub fn verify_pin(&mut self, pin: &[u8]) -> Result<()> {
|
||||
{
|
||||
let txn = self.begin_transaction()?;
|
||||
txn.verify_pin(pin)?;
|
||||
@@ -358,8 +371,8 @@ impl YubiKey {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the number of PIN retries
|
||||
pub fn get_pin_retries(&mut self) -> Result<u8, Error> {
|
||||
/// Get the number of PIN retries.
|
||||
pub fn get_pin_retries(&mut self) -> Result<u8> {
|
||||
let txn = self.begin_transaction()?;
|
||||
|
||||
// Force a re-select to unverify, because once verified the spec dictates that
|
||||
@@ -375,9 +388,10 @@ impl YubiKey {
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the number of PIN retries
|
||||
/// Set the number of PIN retries.
|
||||
#[cfg(feature = "untested")]
|
||||
pub fn set_pin_retries(&mut self, pin_tries: u8, puk_tries: u8) -> Result<(), Error> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub fn set_pin_retries(&mut self, pin_tries: u8, puk_tries: u8) -> Result<()> {
|
||||
// Special case: if either retry count is 0, it's a successful no-op
|
||||
if pin_tries == 0 || puk_tries == 0 {
|
||||
return Ok(());
|
||||
@@ -399,9 +413,10 @@ impl YubiKey {
|
||||
|
||||
/// Change the Personal Identification Number (PIN).
|
||||
///
|
||||
/// The default PIN code is 123456
|
||||
/// The default PIN code is `123456`.
|
||||
#[cfg(feature = "untested")]
|
||||
pub fn change_pin(&mut self, current_pin: &[u8], new_pin: &[u8]) -> Result<(), Error> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub fn change_pin(&mut self, current_pin: &[u8], new_pin: &[u8]) -> Result<()> {
|
||||
{
|
||||
let txn = self.begin_transaction()?;
|
||||
txn.change_ref(ChangeRefAction::ChangePin, current_pin, new_pin)?;
|
||||
@@ -414,9 +429,10 @@ impl YubiKey {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set PIN last changed
|
||||
/// Set PIN last changed.
|
||||
#[cfg(feature = "untested")]
|
||||
pub fn set_pin_last_changed(yubikey: &mut YubiKey) -> Result<(), Error> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub fn set_pin_last_changed(yubikey: &mut YubiKey) -> Result<()> {
|
||||
let txn = yubikey.begin_transaction()?;
|
||||
|
||||
let mut admin_data = AdminData::read(&txn)?;
|
||||
@@ -449,21 +465,23 @@ impl YubiKey {
|
||||
///
|
||||
/// The PUK is part of the PIV standard that the YubiKey follows.
|
||||
///
|
||||
/// The default PUK code is 12345678.
|
||||
/// The default PUK code is `12345678`.
|
||||
#[cfg(feature = "untested")]
|
||||
pub fn change_puk(&mut self, current_puk: &[u8], new_puk: &[u8]) -> Result<(), Error> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub fn change_puk(&mut self, current_puk: &[u8], new_puk: &[u8]) -> Result<()> {
|
||||
let txn = self.begin_transaction()?;
|
||||
txn.change_ref(ChangeRefAction::ChangePuk, current_puk, new_puk)
|
||||
}
|
||||
|
||||
/// Block PUK: permanently prevent the PIN from becoming unblocked
|
||||
/// Block PUK: permanently prevent the PIN from becoming unblocked.
|
||||
#[cfg(feature = "untested")]
|
||||
pub fn block_puk(yubikey: &mut YubiKey) -> Result<(), Error> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub fn block_puk(&mut self) -> Result<()> {
|
||||
let mut puk = [0x30, 0x42, 0x41, 0x44, 0x46, 0x30, 0x30, 0x44];
|
||||
let mut tries_remaining: i32 = -1;
|
||||
let mut flags = [0];
|
||||
|
||||
let txn = yubikey.begin_transaction()?;
|
||||
let txn = self.begin_transaction()?;
|
||||
|
||||
while tries_remaining != 0 {
|
||||
// 2 -> change puk
|
||||
@@ -487,24 +505,23 @@ impl YubiKey {
|
||||
}
|
||||
|
||||
// Attempt to set the "PUK blocked" flag in admin data.
|
||||
|
||||
let mut admin_data = if let Ok(admin_data) = AdminData::read(&txn) {
|
||||
if let Ok(item) = admin_data.get_item(TAG_ADMIN_FLAGS_1) {
|
||||
if item.len() == flags.len() {
|
||||
flags.copy_from_slice(item)
|
||||
} else {
|
||||
error!(
|
||||
"admin flags exist, but are incorrect size: {} (expected {})",
|
||||
item.len(),
|
||||
flags.len()
|
||||
);
|
||||
let mut admin_data = AdminData::read(&txn)
|
||||
.map(|data| {
|
||||
if let Ok(item) = data.get_item(TAG_ADMIN_FLAGS_1) {
|
||||
if item.len() == flags.len() {
|
||||
flags.copy_from_slice(item)
|
||||
} else {
|
||||
error!(
|
||||
"admin flags exist, but are incorrect size: {} (expected {})",
|
||||
item.len(),
|
||||
flags.len()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
admin_data
|
||||
} else {
|
||||
AdminData::default()
|
||||
};
|
||||
data
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
flags[0] |= ADMIN_FLAGS_1_PUK_BLOCKED;
|
||||
|
||||
@@ -522,31 +539,35 @@ impl YubiKey {
|
||||
/// Unblock a Personal Identification Number (PIN) using a previously
|
||||
/// configured PIN Unblocking Key (PUK).
|
||||
#[cfg(feature = "untested")]
|
||||
pub fn unblock_pin(&mut self, puk: &[u8], new_pin: &[u8]) -> Result<(), Error> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub fn unblock_pin(&mut self, puk: &[u8], new_pin: &[u8]) -> Result<()> {
|
||||
let txn = self.begin_transaction()?;
|
||||
txn.change_ref(ChangeRefAction::UnblockPin, puk, new_pin)
|
||||
}
|
||||
|
||||
/// Fetch an object from the YubiKey
|
||||
/// Fetch an object from the YubiKey.
|
||||
#[cfg(feature = "untested")]
|
||||
pub fn fetch_object(&mut self, object_id: ObjectId) -> Result<Buffer, Error> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub fn fetch_object(&mut self, object_id: ObjectId) -> Result<Buffer> {
|
||||
let txn = self.begin_transaction()?;
|
||||
txn.fetch_object(object_id)
|
||||
}
|
||||
|
||||
/// Save an object
|
||||
/// Save an object.
|
||||
#[cfg(feature = "untested")]
|
||||
pub fn save_object(&mut self, object_id: ObjectId, indata: &mut [u8]) -> Result<(), Error> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub fn save_object(&mut self, object_id: ObjectId, indata: &mut [u8]) -> Result<()> {
|
||||
let txn = self.begin_transaction()?;
|
||||
txn.save_object(object_id, indata)
|
||||
}
|
||||
|
||||
/// Get an auth challenge
|
||||
/// Get an auth challenge.
|
||||
#[cfg(feature = "untested")]
|
||||
pub fn get_auth_challenge(&mut self) -> Result<[u8; 8], Error> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub fn get_auth_challenge(&mut self) -> Result<[u8; 8]> {
|
||||
let txn = self.begin_transaction()?;
|
||||
|
||||
let response = APDU::new(Ins::Authenticate)
|
||||
let response = Apdu::new(Ins::Authenticate)
|
||||
.params(ALGO_3DES, KEY_CARDMGM)
|
||||
.data(&[0x7c, 0x02, 0x81, 0x00])
|
||||
.transmit(&txn, 261)?;
|
||||
@@ -558,9 +579,10 @@ impl YubiKey {
|
||||
Ok(response.data()[4..12].try_into().unwrap())
|
||||
}
|
||||
|
||||
/// Verify an auth response
|
||||
/// Verify an auth response.
|
||||
#[cfg(feature = "untested")]
|
||||
pub fn verify_auth_response(&mut self, response: [u8; 8]) -> Result<(), Error> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub fn verify_auth_response(&mut self, response: [u8; 8]) -> Result<()> {
|
||||
let mut data = [0u8; 12];
|
||||
data[0] = 0x7c;
|
||||
data[1] = 0x0a;
|
||||
@@ -571,7 +593,7 @@ impl YubiKey {
|
||||
let txn = self.begin_transaction()?;
|
||||
|
||||
// send the response to the card and a challenge of our own.
|
||||
let status_words = APDU::new(Ins::Authenticate)
|
||||
let status_words = Apdu::new(Ins::Authenticate)
|
||||
.params(ALGO_3DES, KEY_CARDMGM)
|
||||
.data(&data)
|
||||
.transmit(&txn, 261)?
|
||||
@@ -590,7 +612,8 @@ impl YubiKey {
|
||||
///
|
||||
/// The reset function is only available when both pins are blocked.
|
||||
#[cfg(feature = "untested")]
|
||||
pub fn reset_device(&mut self) -> Result<(), Error> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||
pub fn reset_device(&mut self) -> Result<()> {
|
||||
let templ = [0, Ins::Reset.code(), 0, 0];
|
||||
let txn = self.begin_transaction()?;
|
||||
let status_words = txn.transfer_data(&templ, &[], 255)?.status_words();
|
||||
@@ -606,7 +629,7 @@ impl YubiKey {
|
||||
impl<'a> TryFrom<&'a Reader<'_>> for YubiKey {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(reader: &'a Reader<'_>) -> Result<Self, Error> {
|
||||
fn try_from(reader: &'a Reader<'_>) -> Result<Self> {
|
||||
let mut card = reader.connect().map_err(|e| {
|
||||
error!("error connecting to reader '{}': {}", reader.name(), e);
|
||||
e
|
||||
|
||||
+13
-18
@@ -3,19 +3,17 @@
|
||||
#![forbid(unsafe_code)]
|
||||
#![warn(missing_docs, rust_2018_idioms, trivial_casts, unused_qualifications)]
|
||||
|
||||
use getrandom::getrandom;
|
||||
use lazy_static::lazy_static;
|
||||
use log::trace;
|
||||
use rand_core::{OsRng, RngCore};
|
||||
use rsa::{hash::Hash::SHA2_256, PaddingScheme, PublicKey};
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::convert::TryInto;
|
||||
use std::{env, sync::Mutex};
|
||||
use std::{convert::TryInto, env, sync::Mutex};
|
||||
use x509::RelativeDistinguishedName;
|
||||
use yubikey_piv::{
|
||||
use yubikey::{
|
||||
certificate::{Certificate, PublicKeyInfo},
|
||||
key::{self, AlgorithmId, Key, RetiredSlotId, SlotId},
|
||||
policy::{PinPolicy, TouchPolicy},
|
||||
Error, MgmKey, YubiKey,
|
||||
piv::{self, AlgorithmId, Key, RetiredSlotId, SlotId},
|
||||
Error, MgmKey, PinPolicy, TouchPolicy, YubiKey,
|
||||
};
|
||||
|
||||
lazy_static! {
|
||||
@@ -122,16 +120,13 @@ fn test_set_mgmkey() {
|
||||
assert!(yubikey.authenticate(MgmKey::default()).is_ok());
|
||||
|
||||
// Set a protected management key.
|
||||
assert!(MgmKey::generate()
|
||||
.unwrap()
|
||||
.set_protected(&mut yubikey)
|
||||
.is_ok());
|
||||
assert!(MgmKey::generate().set_protected(&mut yubikey).is_ok());
|
||||
let protected = MgmKey::get_protected(&mut yubikey).unwrap();
|
||||
assert!(yubikey.authenticate(MgmKey::default()).is_err());
|
||||
assert!(yubikey.authenticate(protected.clone()).is_ok());
|
||||
|
||||
// Set a manual management key.
|
||||
let manual = MgmKey::generate().unwrap();
|
||||
let manual = MgmKey::generate();
|
||||
assert!(manual.set_manual(&mut yubikey, false).is_ok());
|
||||
assert!(MgmKey::get_protected(&mut yubikey).is_err());
|
||||
assert!(yubikey.authenticate(MgmKey::default()).is_err());
|
||||
@@ -159,7 +154,7 @@ fn generate_self_signed_cert(algorithm: AlgorithmId) -> Certificate {
|
||||
let slot = SlotId::Retired(RetiredSlotId::R1);
|
||||
|
||||
// Generate a new key in the selected slot.
|
||||
let generated = key::generate(
|
||||
let generated = piv::generate(
|
||||
&mut yubikey,
|
||||
slot,
|
||||
algorithm,
|
||||
@@ -169,7 +164,7 @@ fn generate_self_signed_cert(algorithm: AlgorithmId) -> Certificate {
|
||||
.unwrap();
|
||||
|
||||
let mut serial = [0u8; 20];
|
||||
getrandom(&mut serial).unwrap();
|
||||
OsRng.fill_bytes(&mut serial);
|
||||
|
||||
// Generate a self-signed certificate for the new key.
|
||||
let extensions: &[x509::Extension<'_, &[u64]>] = &[];
|
||||
@@ -240,9 +235,9 @@ fn generate_self_signed_ec_cert() {
|
||||
let sig_algo_len = data[7 + tbs_cert_len + 1] as usize;
|
||||
let sig_start = 7 + tbs_cert_len + 2 + sig_algo_len + 3;
|
||||
let msg = &data[4..7 + tbs_cert_len];
|
||||
let sig = &data[sig_start..];
|
||||
let sig = p256::ecdsa::Signature::from_der(&data[sig_start..]).unwrap();
|
||||
let vk = p256::ecdsa::VerifyingKey::from_sec1_bytes(pubkey.as_bytes()).unwrap();
|
||||
|
||||
use ring::signature::{UnparsedPublicKey, ECDSA_P256_SHA256_ASN1};
|
||||
let ring_pk = UnparsedPublicKey::new(&ECDSA_P256_SHA256_ASN1, pubkey.as_bytes());
|
||||
assert!(ring_pk.verify(msg, sig).is_ok());
|
||||
use p256::ecdsa::signature::Verifier;
|
||||
assert!(vk.verify(msg, &sig).is_ok());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user