Compare commits

..

32 Commits

Author SHA1 Message Date
Tony Arcieri (iqlusion) ac72797d1f yubikey v0.4.2 (#291) 2021-07-13 06:35:53 -07:00
Tony Arcieri (iqlusion) fdd3b8730a Make yubikey::Buffer a pub type (#290) 2021-07-13 06:05:24 -07:00
Tony Arcieri (iqlusion) d51ec0a225 Have YubiKey::block_puk take &mut self as argument (#289)
This is effectively the same signature; it just uses `self` instead of a
named argument.
2021-07-13 05:55:24 -07:00
Tony Arcieri (iqlusion) d601c33ba3 yubikey v0.4.1 (#288) 2021-07-12 19:37:12 -07:00
Tony Arcieri (iqlusion) 8e52d75992 Rename Ccc to CccId (#287) 2021-07-12 19:28:46 -07:00
Tony Arcieri (iqlusion) 42ae5fb974 Rename SettingValue to Setting. (#286)
Breaking change, but the crate is fresh and there's time to yank and
republish.
2021-07-12 17:36:42 -07:00
Tony Arcieri (iqlusion) 224d346f09 yubikey-cli v0.4.0 (#284) 2021-07-12 14:33:51 -07:00
Tony Arcieri 01e5bba33f README.md: remove gitter badge 2021-07-12 14:10:11 -07:00
Tony Arcieri 48f42780df README.md: remove maintenance badge 2021-07-12 14:07:08 -07:00
Tony Arcieri (iqlusion) 92f770805f yubikey v0.4.0 (#283) 2021-07-12 14:02:59 -07:00
Tony Arcieri (iqlusion) 563f6f9ccc Extract consts module (#282)
Extracts miscellaneous constants that were floating around in the
toplevel into their own module.
2021-07-12 12:54:54 -07:00
Tony Arcieri (iqlusion) 5f418bbd1d Doc improvements and minor cleanups (#281) 2021-07-12 11:57:42 -07:00
Tony Arcieri (iqlusion) 47776ebf0b Fix parsing local DoS (#279)
Closes #152

Adds additional checks when parsing TLV records to ensure panic-free
operation.
2021-07-12 11:19:26 -07:00
Tony Arcieri (iqlusion) 227518dd1b Rename readers module to reader; Readers => Context (#278)
Renames the `readers` module to be singular: `reader`.

Renames the former `readers::Readers` struct to `reader::Context`.
2021-07-12 11:01:12 -07:00
Tony Arcieri (iqlusion) e6cea2eca6 Rename key module to piv (#277)
Now that the crate is named `yubikey` rather than `yubikey-piv`, it
makes more sense to call this module out as PIV-related functionality.
2021-07-12 10:42:55 -07:00
Tony Arcieri (iqlusion) e249e91297 Replace getrandom with rand_core (#276)
`rand_core::OsRng` provides a facade over `getrandom` which simplifies
error handling.
2021-07-12 09:58:58 -07:00
Tony Arcieri (iqlusion) 1018127843 Fix generate_self_signed_ec_cert integration test (#275)
Unfortunately these tests can't be run in CI as they require a YubiKey
to test against.

The YubiKey generates an ASN.1 DER-encoded ECDSA signature, but the test
was using a fixed-width signature.

The test now passes live against a YubiKey.
2021-07-12 09:05:40 -07:00
Tony Arcieri (iqlusion) 1765e11bc0 Flatten API (#274)
Re-exports types from the toplevel instead of placing them in individual
modules (often which only contain one type).

This makes the API easier for users to navigate, while still retaining
the same module structure internally.

Additionally, this commit uses the `uuid` crate for modeling UUIDs.
2021-07-12 08:40:31 -07:00
Tony Arcieri (iqlusion) 1228d16439 Rename settings::BoolValue => ConfigValue; refactor/cleanup (#272)
Renames the type used for storing a configuration setting.

Also changes the internal functions to use `Option<ConfigValue>` as the
return value, rather than comparing to a default value, which makes them
slightly more idiomatic.
2021-07-11 14:53:54 -07:00
Tony Arcieri (iqlusion) de51b0cc46 Add Result alias (#271)
Adds a `yubikey::Result` alias with `yubikey::Error` as the error type.

Since we only have one `Error` type, this simplifies the return types
where a `Result` is returned.
2021-07-11 09:44:08 -07:00
Tony Arcieri (iqlusion) 1051eaf26d Rename Ccc::cccid => Ccc::card_id (#270)
Better reflects the return type
2021-07-11 09:00:58 -07:00
Tony Arcieri (iqlusion) a1d9c7afc5 Fix clippy::upper_case_acronyms nits; small cleanups (#269)
Renames the following to match Rust idioms:
- `APDU` => `Apdu`
- `CCC` => `Ccc`
- `CHUID` => `ChuId`

Also removes `Copy` from `mscmap::Container`, which fixes a clippy lint
about its usage of `to_bytes`.
2021-07-11 08:51:25 -07:00
Tony Arcieri (iqlusion) 2c06626c25 Bump elliptic-curve to v0.10; MSRV 1.51+ (#268)
Also updates the following:
- `p256` v0.9
- `p384` v0.8
2021-07-11 08:14:14 -07:00
Tony Arcieri (iqlusion) a2a912fc3c Rename to yubikey.rs (#267)
We now have publishing rights to the `yubikey` crate.

This commit renames the project to yubikey.rs

Co-authored-by: Tony Arcieri <bascule@gmail.com>
2021-07-10 17:02:59 -07:00
dependabot[bot] c9e2edc41f Bump sha-1 from 0.9.5 to 0.9.6 (#256)
Bumps [sha-1](https://github.com/RustCrypto/hashes) from 0.9.5 to 0.9.6.
- [Release notes](https://github.com/RustCrypto/hashes/releases)
- [Commits](https://github.com/RustCrypto/hashes/compare/sha-1-v0.9.5...sha-1-v0.9.6)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-19 11:35:31 -07:00
dependabot[bot] 20bf9b0679 Bump sha2 from 0.9.4 to 0.9.5 (#257)
Bumps [sha2](https://github.com/RustCrypto/hashes) from 0.9.4 to 0.9.5.
- [Release notes](https://github.com/RustCrypto/hashes/releases)
- [Commits](https://github.com/RustCrypto/hashes/compare/sha2-v0.9.4...sha2-v0.9.5)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-19 11:17:23 -07:00
dependabot[bot] 0d4d4f7f06 Bump p256 from 0.8.0 to 0.8.1 (#255)
Bumps [p256](https://github.com/RustCrypto/elliptic-curves) from 0.8.0 to 0.8.1.
- [Release notes](https://github.com/RustCrypto/elliptic-curves/releases)
- [Commits](https://github.com/RustCrypto/elliptic-curves/compare/p256/v0.8.0...p256/v0.8.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-17 14:17:17 -07:00
Shella Stephens d31872964d Cargo.lock: bump sha-1 & sha2 (#254) 2021-05-10 09:05:28 -07:00
Tony Arcieri (iqlusion) 865353f4da RustCrypto dependency updates; MSRV 1.47+ (#251)
Updates the following dependencies:
- `des` v0.7
- `elliptic-curve` v0.9
- `hmac` v0.11
- `pbkdf2` v0.8
- `p256` v0.8
- `p384` v0.7
2021-04-30 07:09:24 -07:00
Shella Stephens 1ad17bb025 .github/workflows/ci.yml: fix override: true (#250)
* .github/workflows/ci.yml: fix override: true

* cargo update
2021-04-27 14:36:20 -07:00
Shella Stephens d33e80faea Update rsa to v0.4.0 & fix cargo audit (#246)
* Bump rsa to v0.4.0
2021-03-29 09:12:33 -07:00
Tony Arcieri (iqlusion) e61682be43 yubikey-cli v0.3.0 (#241) 2021-03-22 10:23:15 -07:00
36 changed files with 1273 additions and 1366 deletions
+6 -5
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -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 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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"]
+45 -77
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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()?;
+3 -3
View File
@@ -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()?;
+3 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+20
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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()]),
+15 -15
View File
@@ -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)?)
}
+12 -9
View File
@@ -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);
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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+105 -82
View File
@@ -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,9 +505,9 @@ 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) {
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 {
@@ -501,10 +519,9 @@ impl YubiKey {
}
}
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
View File
@@ -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());
}