Compare commits

..

13 Commits

Author SHA1 Message Date
Tony Arcieri (iqlusion) cafb0b2c18 v0.8.0-pre.0 (#491) 2023-03-14 17:17:07 -06:00
Tony Arcieri (iqlusion) 0c7441a81e Bump asymmetric crypto dependencies; MSRV 1.65 (#490)
Bumps the following dependencies to the latest versions:

- `elliptic-curve` v0.13
- `k256` v0.13
- `p256` v0.13
- `p384` v0.13
- `pbkdf2` v0.12
- `rsa` v0.9.0-pre.0
- `signature` v2
2023-03-14 11:53:00 -06:00
str4d a50addc15b Fix StatusWords::code output for StatusWords::VerifyFailError (#479)
* Fix `StatusWords::code` output for `StatusWords::VerifyFailError`

Closes iqlusioninc/yubikey.rs#473.

* Refactor `Transaction::transfer_data` to match on `StatusWords`

This makes the code more reliable, such that it would have avoided
the bug in iqlusioninc/yubikey.rs#473.
2023-02-12 12:02:22 -07:00
str4d 0809f300b7 Return errors from YubiKey::open_by_serial that indicate a key may exist (#477)
* Return errors from `YubiKey::open_by_serial` that indicate a key may exist

The only such error at the moment is `pcsc::Error::SharingViolation`, which
indicates a transient failure to access a specific reader that could have
been the one we needed (and so a future retry might succeed).

Closes iqlusioninc/yubikey.rs#458.

* Avoid resetting unused devices in YubiKey::open_by_serial

We only connect to readers so that we can determine their serial. We
now try to ensure that the order in which we connect to them doesn't
have an effect on their state after we are done.
2023-02-12 10:22:05 -07:00
str4d d55079f9a6 Enable library users to detect if a smart card doesn't support PIV (#476)
* Enable library users to detect if a smart card doesn't support PIV

Closes iqlusioninc/yubikey.rs#456.

* Avoid resetting the card if we fail to select PIV or fetch version/serial
2023-02-12 10:20:34 -07:00
str4d 10241230b3 Raise minimum pcsc version to remove workaround (#478)
In iqlusioninc/yubikey.rs#88 we added a workaround for what turned out
to be a bug in `pcsc`, where an error was returned if no readers were
available, instead of returning an empty iterator. `pcsc 2.3.1` was
published in 2019, so we can safely rely on it.
2023-02-12 10:18:55 -07:00
dependabot[bot] 1e02f135f0 Bump env_logger from 0.9.3 to 0.10.0 (#452)
Bumps [env_logger](https://github.com/rust-cli/env_logger) from 0.9.3 to 0.10.0.
- [Release notes](https://github.com/rust-cli/env_logger/releases)
- [Changelog](https://github.com/rust-cli/env_logger/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-cli/env_logger/compare/v0.9.3...v0.10.0)

---
updated-dependencies:
- dependency-name: env_logger
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-08 15:24:30 -07:00
Tony Arcieri (iqlusion) 0c2633ab31 transaction: comment cleanup in get_serial (#467)
Moves comments about each YubiKey version number above the arms of the
`match` expression
2023-01-07 13:35:52 -08:00
Tony Arcieri (iqlusion) f49c617a9d Improve parsing of serial numbers (#466)
Checks the length of the data returned when querying the serial number,
returning an error if it's longer than 4 bytes, and left-padding with
zeroes if it's too short.

This fixes some potential panics due to incorrect slice lengths as were
experienced in #465
2023-01-07 08:35:37 -08:00
Tony Arcieri (iqlusion) 1d33ea1747 Cargo.lock: bump dependencies (#463) 2023-01-02 10:22:07 -08:00
str4d 18eb4bf4f4 Add YubiKey::disconnect (#462)
This exposes `pcsc::Card::disconnect` to allow alternate disposition
methods.
2023-01-02 10:15:31 -08:00
str4d 10941bfb5b Add partial Debug impls for Context and YubiKey (#457)
This enables `yubikey::Result<T>` to be debug-formatted, for example
when wrapping the output of an API method in `dbg!()`.
2023-01-01 11:16:10 -07:00
Tony Arcieri (iqlusion) 002491193e Cargo.lock: bump dependencies + audit config (#451)
This should get the security audit passing again
2022-11-28 10:19:39 -08:00
19 changed files with 574 additions and 255 deletions
+3 -3
View File
@@ -1,5 +1,5 @@
[advisories]
ignore = [
"RUSTSEC-2020-0071", # time
"RUSTSEC-2020-0159", # chrono
]
"RUSTSEC-2020-0071", # chrono
"RUSTSEC-2021-0145", # atty
] # advisory IDs to ignore e.g. ["RUSTSEC-2019-0001", ...]
+3 -3
View File
@@ -36,13 +36,13 @@ jobs:
toolchain: stable
deps: true
- platform: ubuntu-latest
toolchain: 1.60.0 # MSRV
toolchain: 1.65.0 # MSRV
deps: sudo apt-get install libpcsclite-dev
- platform: windows-latest
toolchain: 1.60.0 # MSRV
toolchain: 1.65.0 # MSRV
deps: true
- platform: macos-latest
toolchain: 1.60.0 # MSRV
toolchain: 1.65.0 # MSRV
deps: true
runs-on: ${{ matrix.platform }}
steps:
+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
+20
View File
@@ -4,6 +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).
## Unreleased
### Added
- `YubiKey::disconnect`
- `impl Debug for {Context, YubiKey}`
- `Error::AppletNotFound`
### Changed
- `Reader::open` now returns `Error::AppletNotFound` instead of `Error::Generic`
if the PIV applet is not present on the device. This is returned by non-PIV
virtual smart cards like Windows Hello for Business, as well as some smart
card readers when no card is present.
- `Reader::open` now avoids resetting the card if an error occurs (equivalent to
calling `YubiKey::disconnect(pcsc::Disposition::LeaveCard)` if `Reader::open`
succeeds).
### Fixed
- `StatusWords::code` now returns the correct code (including embedded `tries`
count) for `StatusWords::VerifyFailError`. Previously the returned code lost
information and was not round-trip compatible with `StatusWords::from(u16)`.
## 0.7.0 (2022-11-14)
### Added
- Display inner PC/SC errors ([#420])
Generated
+241 -122
View File
@@ -4,9 +4,9 @@ version = 3
[[package]]
name = "aho-corasick"
version = "0.7.19"
version = "0.7.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
dependencies = [
"memchr",
]
@@ -59,17 +59,6 @@ dependencies = [
"syn",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.1.0"
@@ -78,9 +67,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "base16ct"
version = "0.1.1"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce"
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
[[package]]
name = "base64"
@@ -123,9 +112,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cc"
version = "1.0.76"
version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f"
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
[[package]]
name = "cfg-if"
@@ -143,7 +132,7 @@ dependencies = [
"js-sys",
"num-integer",
"num-traits",
"time 0.1.44",
"time 0.1.45",
"wasm-bindgen",
"winapi",
]
@@ -160,14 +149,14 @@ dependencies = [
[[package]]
name = "clap"
version = "4.0.23"
version = "4.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eb41c13df48950b20eb4cd0eefa618819469df1bffc49d11e8487c4ba0037e5"
checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39"
dependencies = [
"atty",
"bitflags",
"clap_derive",
"clap_lex",
"is-terminal",
"once_cell",
"strsim",
"termcolor",
@@ -207,9 +196,9 @@ dependencies = [
[[package]]
name = "const-oid"
version = "0.9.0"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "722e23542a15cea1f65d4a1419c4cfd7a26706c70871a13a04238ca3f40f1661"
checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913"
[[package]]
name = "cookie-factory"
@@ -234,9 +223,9 @@ dependencies = [
[[package]]
name = "crypto-bigint"
version = "0.4.9"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef"
checksum = "7c2538c4e68e52548bacb3e83ac549f903d44f011ac9d5abb5e132e67d0808f7"
dependencies = [
"generic-array",
"rand_core",
@@ -256,9 +245,9 @@ dependencies = [
[[package]]
name = "cxx"
version = "1.0.81"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97abf9f0eca9e52b7f81b945524e76710e6cb2366aead23b7d4fbf72e281f888"
checksum = "5add3fc1717409d029b20c5b6903fc0c0b02fa6741d820054f4a2efa5e5816fd"
dependencies = [
"cc",
"cxxbridge-flags",
@@ -268,9 +257,9 @@ dependencies = [
[[package]]
name = "cxx-build"
version = "1.0.81"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cc32cc5fea1d894b77d269ddb9f192110069a8a9c1f1d441195fba90553dea3"
checksum = "b4c87959ba14bc6fbc61df77c3fcfe180fc32b93538c4f1031dd802ccb5f2ff0"
dependencies = [
"cc",
"codespan-reporting",
@@ -283,15 +272,15 @@ dependencies = [
[[package]]
name = "cxxbridge-flags"
version = "1.0.81"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ca220e4794c934dc6b1207c3b42856ad4c302f2df1712e9f8d2eec5afaacf1f"
checksum = "69a3e162fde4e594ed2b07d0f83c6c67b745e7f28ce58c6df5e6b6bef99dfb59"
[[package]]
name = "cxxbridge-macro"
version = "1.0.81"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b846f081361125bfc8dc9d3940c84e1fd83ba54bbca7b17cd29483c828be0704"
checksum = "3e7e2adeb6a0d4a282e581096b06e1791532b7d576dcde5ccd9382acf55db8e6"
dependencies = [
"proc-macro2",
"quote",
@@ -300,15 +289,15 @@ dependencies = [
[[package]]
name = "data-encoding"
version = "2.3.2"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57"
checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb"
[[package]]
name = "der"
version = "0.6.0"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dd2ae565c0a381dde7fade45fce95984c568bdcb4700a4fdbe3175e0380b2f"
checksum = "bc906908ea6458456e5eaa160a9c08543ec3d1e6f71e2235cedd660cb65f9df0"
dependencies = [
"const-oid",
"pem-rfc7468",
@@ -340,9 +329,9 @@ dependencies = [
[[package]]
name = "digest"
version = "0.10.5"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c"
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
dependencies = [
"block-buffer",
"const-oid",
@@ -363,9 +352,9 @@ dependencies = [
[[package]]
name = "ecdsa"
version = "0.14.8"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c"
checksum = "d1b0a1222f8072619e8a6b667a854020a03d363738303203c09468b3424a420a"
dependencies = [
"der",
"elliptic-curve",
@@ -375,13 +364,12 @@ dependencies = [
[[package]]
name = "elliptic-curve"
version = "0.12.3"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3"
checksum = "6ea5a92946e8614bb585254898bb7dd1ddad241ace60c52149e3765e34cc039d"
dependencies = [
"base16ct",
"crypto-bigint",
"der",
"digest",
"ff",
"generic-array",
@@ -397,22 +385,43 @@ dependencies = [
[[package]]
name = "env_logger"
version = "0.9.3"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7"
checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
dependencies = [
"atty",
"humantime",
"is-terminal",
"log",
"regex",
"termcolor",
]
[[package]]
name = "ff"
version = "0.12.1"
name = "errno"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160"
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
dependencies = [
"errno-dragonfly",
"libc",
"winapi",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "ff"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449"
dependencies = [
"rand_core",
"subtle",
@@ -426,6 +435,7 @@ checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
dependencies = [
"typenum",
"version_check",
"zeroize",
]
[[package]]
@@ -441,9 +451,9 @@ dependencies = [
[[package]]
name = "group"
version = "0.12.1"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7"
checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
dependencies = [
"ff",
"rand_core",
@@ -458,9 +468,9 @@ checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
[[package]]
name = "hermit-abi"
version = "0.1.19"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
dependencies = [
"libc",
]
@@ -523,10 +533,32 @@ dependencies = [
]
[[package]]
name = "itoa"
version = "1.0.4"
name = "io-lifetimes"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "is-terminal"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
dependencies = [
"hermit-abi",
"io-lifetimes",
"rustix",
"windows-sys",
]
[[package]]
name = "itoa"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
[[package]]
name = "js-sys"
@@ -548,9 +580,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.137"
version = "0.2.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
[[package]]
name = "libm"
@@ -560,13 +592,19 @@ checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb"
[[package]]
name = "link-cplusplus"
version = "1.0.7"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369"
checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5"
dependencies = [
"cc",
]
[[package]]
name = "linux-raw-sys"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
[[package]]
name = "log"
version = "0.4.17"
@@ -590,9 +628,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "nom"
version = "7.1.1"
version = "7.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
checksum = "e5507769c4919c998e69e49c839d9dc6e693ede4cc4290d6ad8b41d4f09c548c"
dependencies = [
"memchr",
"minimal-lexical",
@@ -611,9 +649,9 @@ dependencies = [
[[package]]
name = "num-bigint-dig"
version = "0.8.1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "566d173b2f9406afbc5510a90925d5a2cd80cae4605631f1212303df265de011"
checksum = "2399c9463abc5f909349d8aa9ba080e0b88b3ce2885389b60b993f39b1a56905"
dependencies = [
"byteorder",
"lazy_static",
@@ -660,61 +698,64 @@ dependencies = [
[[package]]
name = "oid-registry"
version = "0.6.0"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d4bda43fd1b844cbc6e6e54b5444e2b1bc7838bce59ad205902cccbb26d6761"
checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff"
dependencies = [
"asn1-rs",
]
[[package]]
name = "once_cell"
version = "1.16.0"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
[[package]]
name = "os_str_bytes"
version = "6.4.0"
version = "6.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b5bf27447411e9ee3ff51186bf7a08e16c341efdde93f4d823e8844429bed7e"
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
[[package]]
name = "p256"
version = "0.11.1"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594"
checksum = "7270da3e5caa82afd3deb054cc237905853813aea3859544bc082c3fe55b8d47"
dependencies = [
"ecdsa",
"elliptic-curve",
"primeorder",
"sha2",
]
[[package]]
name = "p384"
version = "0.11.2"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa"
checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209"
dependencies = [
"ecdsa",
"elliptic-curve",
"primeorder",
"sha2",
]
[[package]]
name = "pbkdf2"
version = "0.11.0"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917"
checksum = "f0ca0b5a68607598bf3bad68f32227a8164f6254833f84eafaac409cd6746c31"
dependencies = [
"digest",
"hmac",
]
[[package]]
name = "pcsc"
version = "2.7.0"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e29e4de78a433aeecd06fb5bd55a0f9fde11dc85a14c22d482972c7edc4fdc4"
checksum = "37cab0be9d04e808a8d8059fa54befcd71dc8b168f9f0c04bdb7e59832abbab4"
dependencies = [
"bitflags",
"pcsc-sys",
@@ -731,18 +772,18 @@ dependencies = [
[[package]]
name = "pem-rfc7468"
version = "0.6.0"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d159833a9105500e0398934e205e0773f0b27529557134ecfc51c27646adac"
checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
dependencies = [
"base64ct",
]
[[package]]
name = "pkcs1"
version = "0.4.1"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eff33bdbdfc54cc98a2eca766ebdec3e1b8fb7387523d5c9c9a2891da856f719"
checksum = "178ba28ece1961eafdff1991bd1744c29564cbab5d803f3ccb4a4895a6c550a7"
dependencies = [
"der",
"pkcs8",
@@ -752,9 +793,9 @@ dependencies = [
[[package]]
name = "pkcs8"
version = "0.9.0"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba"
checksum = "3d2820d87d2b008616e5c27212dd9e0e694fb4c6b522de06094106813328cb49"
dependencies = [
"der",
"spki",
@@ -772,6 +813,15 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "primeorder"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7613fdcc0831c10060fa69833ea8fa2caa94b6456f51e25356a885b530a2e3d0"
dependencies = [
"elliptic-curve",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
@@ -798,18 +848,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.47"
version = "1.0.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.21"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
dependencies = [
"proc-macro2",
]
@@ -863,20 +913,19 @@ checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
[[package]]
name = "rfc6979"
version = "0.3.1"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb"
checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
dependencies = [
"crypto-bigint",
"hmac",
"zeroize",
"subtle",
]
[[package]]
name = "rsa"
version = "0.7.1"
version = "0.9.0-pre.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0ecc3307be66bfb3574577895555bacfb9a37a8d5cd959444b72ff02495c618"
checksum = "a7bc1d34159d63536b4d89944e9ab5bb952f45db6fa0b8b03c2f8c09fb5b7171"
dependencies = [
"byteorder",
"digest",
@@ -888,7 +937,6 @@ dependencies = [
"pkcs8",
"rand_core",
"signature",
"smallvec",
"subtle",
"zeroize",
]
@@ -903,16 +951,30 @@ dependencies = [
]
[[package]]
name = "scratch"
version = "1.0.2"
name = "rustix"
version = "0.36.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898"
checksum = "4feacf7db682c6c329c4ede12649cd36ecab0f3be5b7d74e6a20304725db4549"
dependencies = [
"bitflags",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "scratch"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2"
[[package]]
name = "sec1"
version = "0.3.0"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928"
checksum = "48518a2b5775ba8ca5b46596aae011caa431e6ce7e4a67ead66d92f08884220e"
dependencies = [
"base16ct",
"der",
@@ -933,9 +995,9 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.147"
version = "1.0.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965"
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
[[package]]
name = "sha1"
@@ -961,9 +1023,9 @@ dependencies = [
[[package]]
name = "signature"
version = "1.6.4"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c"
checksum = "8fe458c98333f9c8152221191a77e2a44e8325d0193484af2e9421a53019e57d"
dependencies = [
"digest",
"rand_core",
@@ -983,9 +1045,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "spki"
version = "0.6.0"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b"
checksum = "c0445c905640145c7ea8c1993555957f65e7c46d0535b91ba501bc9bfc85522f"
dependencies = [
"base64ct",
"der",
@@ -1005,9 +1067,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "syn"
version = "1.0.103"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d"
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
dependencies = [
"proc-macro2",
"quote",
@@ -1037,18 +1099,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.37"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.37"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
dependencies = [
"proc-macro2",
"quote",
@@ -1057,9 +1119,9 @@ dependencies = [
[[package]]
name = "time"
version = "0.1.44"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
dependencies = [
"libc",
"wasi 0.10.0+wasi-snapshot-preview1",
@@ -1095,15 +1157,15 @@ dependencies = [
[[package]]
name = "typenum"
version = "1.15.0"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
name = "unicode-ident"
version = "1.0.5"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "unicode-width"
@@ -1119,9 +1181,9 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "uuid"
version = "1.2.1"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83"
checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c"
dependencies = [
"getrandom",
]
@@ -1229,6 +1291,63 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
[[package]]
name = "windows_i686_gnu"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
[[package]]
name = "windows_i686_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
[[package]]
name = "x509"
version = "0.2.0"
@@ -1259,7 +1378,7 @@ dependencies = [
[[package]]
name = "yubikey"
version = "0.7.0"
version = "0.8.0-pre.0"
dependencies = [
"base16ct",
"chrono",
+11 -12
View File
@@ -1,6 +1,6 @@
[package]
name = "yubikey"
version = "0.7.0"
version = "0.8.0-pre.0"
description = """
Pure Rust cross-platform host-side driver for YubiKey devices from Yubico with
support for hardware-backed public-key decryption and digital signatures using
@@ -14,7 +14,7 @@ readme = "README.md"
categories = ["api-bindings", "authentication", "cryptography", "hardware-support"]
keywords = ["ecdsa", "encryption", "rsa", "piv", "signature"]
edition = "2021"
rust-version = "1.60"
rust-version = "1.65"
[workspace]
members = [".", "cli"]
@@ -24,20 +24,20 @@ chrono = "0.4.23"
cookie-factory = "0.3"
der-parser = "8"
des = "0.8"
elliptic-curve = "0.12"
hex = { package = "base16ct", version = "0.1", features = ["alloc"] }
elliptic-curve = "0.13"
hex = { package = "base16ct", version = "0.2", features = ["alloc"] }
hmac = "0.12"
log = "0.4"
nom = "7"
num-bigint-dig = { version = "0.8", features = ["rand"] }
num-traits = "0.2"
num-integer = "0.1"
pbkdf2 = { version = "0.11", default-features = false }
p256 = "0.11"
p384 = "0.11"
pcsc = "2"
p256 = "0.13"
p384 = "0.13"
pbkdf2 = { version = "0.12", default-features = false, features = ["hmac"] }
pcsc = "2.3.1"
rand_core = { version = "0.6", features = ["std"] }
rsa = "0.7"
rsa = "=0.9.0-pre.0"
secrecy = "0.8"
sha1 = { version = "0.10", features = ["oid"] }
sha2 = { version = "0.10", features = ["oid"] }
@@ -48,10 +48,9 @@ x509-parser = "0.14"
zeroize = "1"
[dev-dependencies]
env_logger = "0.9"
env_logger = "0.10"
once_cell = "1"
rsa = { version = "0.7.1", features = ["hazmat"] }
signature = { version = "1.6.4", features = ["hazmat-preview"] }
signature = "2"
[features]
untested = []
+2 -2
View File
@@ -172,7 +172,7 @@ 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-2022 Yubico AB, Tony Arcieri
Copyright (c) 2014-2023 Yubico AB, Tony Arcieri
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -214,7 +214,7 @@ or conditions.
[docs-link]: https://docs.rs/yubikey/
[license-image]: https://img.shields.io/badge/license-BSD-blue.svg
[license-link]: https://github.com/iqlusioninc/yubikey.rs/blob/main/COPYING
[msrv-image]: https://img.shields.io/badge/rustc-1.60+-blue.svg
[msrv-image]: https://img.shields.io/badge/rustc-1.65+-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.rs/workflows/CI/badge.svg?branch=main&event=push
+3 -3
View File
@@ -16,11 +16,11 @@ rust-version = "1.56"
[dependencies]
clap = { version = "4", features = ["derive"] }
env_logger = "0.9"
hex = { package = "base16ct", version = "0.1", features = ["alloc"] }
env_logger = "0.10"
hex = { package = "base16ct", version = "0.2", features = ["alloc"] }
log = "0.4"
once_cell = "1"
sha2 = "0.10"
termcolor = "1"
x509-parser = "0.14"
yubikey = { version = "0.7", path = ".." }
yubikey = { version = "0.8.0-pre.0", path = ".." }
+1 -1
View File
@@ -43,7 +43,7 @@ For more information, please see [CODE_OF_CONDUCT.md][cc-md].
## License
Copyright (c) 2014-2022 Yubico AB, Tony Arcieri
Copyright (c) 2014-2023 Yubico AB, Tony Arcieri
All rights reserved.
Redistribution and use in source and binary forms, with or without
+48 -1
View File
@@ -353,6 +353,12 @@ pub(crate) enum StatusWords {
/// Successful execution
Success,
/// The requested data was too large for the response, and there is data remaining.
BytesRemaining {
/// The number of bytes remaining, as indicated in the response.
len: u8,
},
/// https://github.com/Yubico/yubikey-manager/blob/1f22620b623c6b345dd9f9193ec765a542dddc80/ykman/driver_ccid.py#L53
NoInputDataError,
@@ -410,8 +416,9 @@ impl StatusWords {
pub fn code(self) -> u16 {
match self {
StatusWords::None => 0,
StatusWords::BytesRemaining { len } => 0x6100 | len as u16,
StatusWords::NoInputDataError => 0x6285,
StatusWords::VerifyFailError { tries } => 0x63c0 & tries as u16,
StatusWords::VerifyFailError { tries } => 0x63c0 | tries as u16,
StatusWords::WrongLengthError => 0x6700,
StatusWords::SecurityStatusError => 0x6982,
StatusWords::AuthBlockedError => 0x6983,
@@ -439,6 +446,9 @@ impl From<u16> for StatusWords {
fn from(sw: u16) -> Self {
match sw {
0x0000 => StatusWords::None,
sw if sw & 0xff00 == 0x6100 => Self::BytesRemaining {
len: (sw & 0x00ff) as u8,
},
0x6285 => StatusWords::NoInputDataError,
sw if sw & 0xfff0 == 0x63c0 => StatusWords::VerifyFailError {
tries: (sw & 0x000f) as u8,
@@ -466,3 +476,40 @@ impl From<StatusWords> for u16 {
sw.code()
}
}
#[cfg(test)]
mod tests {
use super::StatusWords;
#[test]
fn status_words_round_trip() {
let round_trip = |sw: StatusWords| {
assert_eq!(StatusWords::from(sw.code()), sw);
};
round_trip(StatusWords::None);
round_trip(StatusWords::BytesRemaining { len: 1 });
round_trip(StatusWords::BytesRemaining { len: 10 });
round_trip(StatusWords::BytesRemaining { len: 0xFF });
round_trip(StatusWords::Success);
round_trip(StatusWords::NoInputDataError);
round_trip(StatusWords::VerifyFailError { tries: 0x0F });
round_trip(StatusWords::VerifyFailError { tries: 3 });
round_trip(StatusWords::VerifyFailError { tries: 2 });
round_trip(StatusWords::VerifyFailError { tries: 1 });
round_trip(StatusWords::VerifyFailError { tries: 0 });
round_trip(StatusWords::WrongLengthError);
round_trip(StatusWords::SecurityStatusError);
round_trip(StatusWords::AuthBlockedError);
round_trip(StatusWords::DataInvalidError);
round_trip(StatusWords::ConditionsNotSatisfiedError);
round_trip(StatusWords::CommandNotAllowedError);
round_trip(StatusWords::IncorrectParamError);
round_trip(StatusWords::NotFoundError);
round_trip(StatusWords::NoSpaceError);
round_trip(StatusWords::IncorrectSlotError);
round_trip(StatusWords::NotSupportedError);
round_trip(StatusWords::CommandAbortedError);
round_trip(StatusWords::Other(0x1337));
}
}
+9
View File
@@ -45,6 +45,12 @@ pub enum Error {
/// Applet error
AppletError,
/// We tried to select an applet that could not be found.
AppletNotFound {
/// Human-readable name of the applet.
applet_name: &'static str,
},
/// Argument error
ArgumentError,
@@ -125,6 +131,9 @@ impl Error {
match self {
Error::AlgorithmError => f.write_str("algorithm error"),
Error::AppletError => f.write_str("applet error"),
Error::AppletNotFound { applet_name } => {
f.write_str(&format!("{} applet not found", applet_name))
}
Error::ArgumentError => f.write_str("argument error"),
Error::AuthenticationError => f.write_str("authentication error"),
Error::GenericError => f.write_str("generic error"),
+1
View File
@@ -49,6 +49,7 @@ mod mgm;
mod mscmap;
#[cfg(feature = "untested")]
mod msroots;
mod otp;
pub mod piv;
mod policy;
pub mod reader;
+12 -2
View File
@@ -46,7 +46,17 @@ use des::{
TdesEde3,
};
#[cfg(feature = "untested")]
use {hmac::Hmac, pbkdf2::pbkdf2, sha1::Sha1};
use {pbkdf2::pbkdf2_hmac, sha1::Sha1};
/// YubiKey MGMT Applet Name
#[cfg(feature = "untested")]
pub(crate) const APPLET_NAME: &str = "YubiKey MGMT";
/// MGMT Applet ID.
///
/// <https://developers.yubico.com/PIV/Introduction/Admin_access.html>
#[cfg(feature = "untested")]
pub(crate) const APPLET_ID: &[u8] = &[0xa0, 0x00, 0x00, 0x05, 0x27, 0x47, 0x11, 0x17];
pub(crate) const ADMIN_FLAGS_1_PROTECTED_MGM: u8 = 0x02;
@@ -137,7 +147,7 @@ impl MgmKey {
}
let mut mgm = [0u8; DES_LEN_3DES];
pbkdf2::<Hmac<Sha1>>(pin, salt, ITER_MGM_PBKDF2, &mut mgm);
pbkdf2_hmac::<Sha1>(pin, salt, ITER_MGM_PBKDF2, &mut mgm);
MgmKey::from_bytes(mgm)
}
+5
View File
@@ -0,0 +1,5 @@
/// YubiKey OTP Applet Name
pub(crate) const APPLET_NAME: &str = "YubiKey OTP";
/// YubiKey OTP Applet ID. Needed to query serial on YK4.
pub(crate) const APPLET_ID: &[u8] = &[0xa0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01, 0x01];
+6
View File
@@ -73,6 +73,12 @@ use {
#[cfg(feature = "untested")]
use zeroize::Zeroizing;
/// PIV Applet Name
pub(crate) const APPLET_NAME: &str = "PIV";
/// PIV Applet ID
pub(crate) const APPLET_ID: &[u8] = &[0xa0, 0x00, 0x00, 0x03, 0x08];
const CB_ECC_POINTP256: usize = 65;
const CB_ECC_POINTP384: usize = 97;
+7
View File
@@ -4,6 +4,7 @@ use crate::{Result, YubiKey};
use std::{
borrow::Cow,
ffi::CStr,
fmt,
sync::{Arc, Mutex},
};
@@ -19,6 +20,12 @@ pub struct Context {
reader_names: Vec<u8>,
}
impl fmt::Debug for Context {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Context").finish_non_exhaustive()
}
}
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).
+81 -69
View File
@@ -5,7 +5,8 @@ use crate::{
apdu::{Apdu, Ins, StatusWords},
consts::{CB_BUF_MAX, CB_OBJ_MAX},
error::{Error, Result},
piv::{AlgorithmId, SlotId},
otp,
piv::{self, AlgorithmId, SlotId},
serialization::*,
yubikey::*,
Buffer, ObjectId,
@@ -16,12 +17,6 @@ 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")]
@@ -69,7 +64,7 @@ impl<'tx> Transaction<'tx> {
pub fn select_application(&self) -> Result<()> {
let response = Apdu::new(Ins::SelectApplication)
.p1(0x04)
.data(PIV_AID)
.data(piv::APPLET_ID)
.transmit(self, 0xFF)
.map_err(|e| {
error!("failed communicating with card: '{}'", e);
@@ -81,7 +76,12 @@ impl<'tx> Transaction<'tx> {
"failed selecting application: {:04x}",
response.status_words().code()
);
return Err(Error::GenericError);
return Err(match response.status_words() {
StatusWords::NotFoundError => Error::AppletNotFound {
applet_name: piv::APPLET_NAME,
},
_ => Error::GenericError,
});
}
Ok(())
@@ -105,61 +105,74 @@ impl<'tx> Transaction<'tx> {
/// Get YubiKey device serial number.
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)
.p1(0x04)
.data(YK_AID)
.transmit(self, 0xFF)?
.status_words();
match version.major {
// YK4 requires switching to the YK applet to retrieve the serial
4 => {
let sw = Apdu::new(Ins::SelectApplication)
.p1(0x04)
.data(otp::APPLET_ID)
.transmit(self, 0xFF)?
.status_words();
if !sw.is_success() {
error!("failed selecting yk application: {:04x}", sw.code());
return Err(Error::GenericError);
if !sw.is_success() {
error!("failed selecting yk application: {:04x}", sw.code());
return Err(match sw {
StatusWords::NotFoundError => Error::AppletNotFound {
applet_name: otp::APPLET_NAME,
},
_ => Error::GenericError,
});
}
let response = Apdu::new(0x01).p1(0x10).transmit(self, 0xFF)?;
if !response.is_success() {
// TODO(tarcieri): still reselect the PIV applet in this case?
error!(
"failed retrieving serial number: {:04x}",
response.status_words().code()
);
return Err(Error::GenericError);
}
// reselect the PIV applet
let sw = Apdu::new(Ins::SelectApplication)
.p1(0x04)
.data(piv::APPLET_ID)
.transmit(self, 0xFF)?
.status_words();
if !sw.is_success() {
error!("failed selecting application: {:04x}", sw.code());
return Err(match sw {
StatusWords::NotFoundError => Error::AppletNotFound {
applet_name: piv::APPLET_NAME,
},
_ => Error::GenericError,
});
}
response.data().try_into()
}
let resp = Apdu::new(0x01).p1(0x10).transmit(self, 0xFF)?;
if !resp.is_success() {
error!(
"failed retrieving serial number: {:04x}",
resp.status_words().code()
);
return Err(Error::GenericError);
}
// reselect the PIV applet
let sw = Apdu::new(Ins::SelectApplication)
.p1(0x04)
.data(PIV_AID)
.transmit(self, 0xFF)?
.status_words();
if !sw.is_success() {
error!("failed selecting application: {:04x}", sw.code());
return Err(Error::GenericError);
}
resp
} else {
// YK5 implements getting the serial as a PIV applet command (0xf8)
let resp = Apdu::new(Ins::GetSerial).transmit(self, 0xFF)?;
5 => {
let response = Apdu::new(Ins::GetSerial).transmit(self, 0xFF)?;
if !resp.is_success() {
error!(
"failed retrieving serial number: {:04x}",
resp.status_words().code()
);
return Err(Error::GenericError);
if !response.is_success() {
error!(
"failed retrieving serial number: {:04x}",
response.status_words().code()
);
return Err(Error::GenericError);
}
response.data().try_into()
}
resp
};
response.data()[..4]
.try_into()
.map(|serial| Serial::from(u32::from_be_bytes(serial)))
.map_err(|_| Error::SizeError)
// Other versions unsupported
_ => Err(Error::NotSupported),
}
}
/// Verify device PIN.
@@ -387,11 +400,12 @@ impl<'tx> Transaction<'tx> {
.data(&in_data[in_offset..(in_offset + this_size)])
.transmit(self, 261)?;
sw = response.status_words().code();
sw = response.status_words();
if !response.is_success() && (sw >> 8 != 0x61) {
match sw {
StatusWords::Success | StatusWords::BytesRemaining { .. } => (),
// TODO(tarcieri): is this really OK?
return Ok(Response::new(sw.into(), out_data));
_ => return Ok(Response::new(sw, out_data)),
}
if !out_data.is_empty() && (out_data.len() - response.data().len() > max_out) {
@@ -412,17 +426,15 @@ impl<'tx> Transaction<'tx> {
}
}
while sw >> 8 == 0x61 {
trace!(
"The card indicates there is {} bytes more data for us",
sw & 0xff
);
while let StatusWords::BytesRemaining { len } = sw {
trace!("The card indicates there is {} bytes more data for us", len);
let response = Apdu::new(Ins::GetResponseApdu).transmit(self, 261)?;
sw = response.status_words().code();
sw = response.status_words();
if sw != StatusWords::Success.code() && (sw >> 8 != 0x61) {
return Ok(Response::new(sw.into(), vec![]));
match sw {
StatusWords::Success | StatusWords::BytesRemaining { .. } => (),
_ => return Ok(Response::new(sw, vec![])),
}
if out_data.len() + response.data().len() > max_out {
@@ -438,7 +450,7 @@ impl<'tx> Transaction<'tx> {
out_data.extend_from_slice(&response.data()[..response.data().len()]);
}
Ok(Response::new(sw.into(), out_data))
Ok(Response::new(sw, out_data))
}
/// Fetch an object.
+118 -34
View File
@@ -42,7 +42,7 @@ use crate::{
transaction::Transaction,
};
use log::{error, info};
use pcsc::Card;
use pcsc::{Card, Disposition};
use rand_core::{OsRng, RngCore};
use std::{
fmt::{self, Display},
@@ -55,6 +55,7 @@ use {
apdu::StatusWords,
consts::{TAG_ADMIN_FLAGS_1, TAG_ADMIN_TIMESTAMP},
metadata::AdminData,
mgm,
transaction::ChangeRefAction,
Buffer, ObjectId,
},
@@ -71,12 +72,6 @@ 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.
@@ -98,6 +93,20 @@ impl From<Serial> for u32 {
}
}
impl TryFrom<&[u8]> for Serial {
type Error = Error;
fn try_from(bytes: &[u8]) -> Result<Self> {
if bytes.len() > 4 {
return Err(Error::SizeError);
}
let mut arr = [0u8; 4];
arr[(4 - bytes.len())..].copy_from_slice(bytes);
Ok(Self(u32::from_be_bytes(arr)))
}
}
impl FromStr for Serial {
type Err = Error;
@@ -156,6 +165,16 @@ pub struct YubiKey {
pub(crate) serial: Serial,
}
impl fmt::Debug for YubiKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("YubiKey")
.field("name", &self.name)
.field("version", &self.version)
.field("serial", &self.serial)
.finish_non_exhaustive()
}
}
impl YubiKey {
/// Open a connection to a YubiKey.
///
@@ -166,12 +185,7 @@ impl YubiKey {
/// [`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,
other => other,
})?;
let mut readers = Context::open()?;
let mut reader_iter = readers.iter()?;
if let Some(reader) = reader_iter.next() {
@@ -189,26 +203,42 @@ impl YubiKey {
/// Open a YubiKey with a specific serial number.
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,
other => other,
})?;
let mut readers = Context::open()?;
let mut open_error = None;
for reader in readers.iter()? {
let yubikey = match reader.open() {
Ok(yk) => yk,
Err(_) => continue,
Err(e) => {
// Save the first error we see that indicates we might have been able
// to find a matching YubiKey.
if open_error.is_none() {
if let Error::PcscError {
inner: Some(pcsc::Error::SharingViolation),
} = e
{
open_error = Some(e);
}
}
continue;
}
};
if serial == yubikey.serial() {
return Ok(yubikey);
} else {
// We didn't want this YubiKey; don't reset it.
let _ = yubikey.disconnect(pcsc::Disposition::LeaveCard);
}
}
error!("no YubiKey detected with serial: {}", serial);
Err(Error::NotFound)
Err(if let Some(e) = open_error {
e
} else {
error!("no YubiKey detected with serial: {}", serial);
Error::NotFound
})
}
/// Reconnect to a YubiKey.
@@ -238,6 +268,38 @@ impl YubiKey {
Ok(())
}
/// Disconnect from the YubiKey.
///
/// In case of error, ownership of the YubiKey is returned to the caller.
///
/// # Note
///
/// `YubiKey` implements `Drop` which automatically disconnects the card using
/// `Disposition::ResetCard`; you only need to call this function if you want to
/// handle errors or use a different disposition method.
pub fn disconnect(self, disposition: Disposition) -> core::result::Result<(), (Self, Error)> {
let Self {
card,
name,
pin,
version,
serial,
} = self;
card.disconnect(disposition).map_err(|(card, e)| {
(
Self {
card,
name,
pin,
version,
serial,
},
e.into(),
)
})
}
/// Begin a transaction.
pub(crate) fn begin_transaction(&mut self) -> Result<Transaction<'_>> {
// TODO(tarcieri): reconnect support
@@ -341,7 +403,7 @@ impl YubiKey {
let status_words = Apdu::new(Ins::SelectApplication)
.p1(0x04)
.data(MGMT_AID)
.data(mgm::APPLET_ID)
.transmit(&txn, 255)?
.status_words();
@@ -350,7 +412,12 @@ impl YubiKey {
"Failed selecting mgmt application: {:04x}",
status_words.code()
);
return Err(Error::GenericError);
return Err(match status_words {
StatusWords::NotFoundError => Error::AppletNotFound {
applet_name: mgm::APPLET_NAME,
},
_ => Error::GenericError,
});
}
Ok(())
@@ -636,23 +703,40 @@ impl<'a> TryFrom<&'a Reader<'_>> for YubiKey {
info!("connected to reader: {}", reader.name());
let (version, serial) = {
let mut app_version_serial = || -> Result<(Version, Serial)> {
let txn = Transaction::new(&mut card)?;
txn.select_application()?;
let v = txn.get_version()?;
let s = txn.get_serial(v)?;
(v, s)
Ok((v, s))
};
let yubikey = YubiKey {
card,
name: String::from(reader.name()),
pin: None,
version,
serial,
};
match app_version_serial() {
Err(e) => {
error!("Could not use reader: {}", e);
Ok(yubikey)
// We were unable to use the card, so we've effectively only connected as
// a side-effect of determining this. Avoid disrupting its internal state
// any further (e.g. preserve the PIN cache of whatever applet is selected
// currently).
if let Err((_, e)) = card.disconnect(pcsc::Disposition::LeaveCard) {
error!("Failed to disconnect gracefully from card: {}", e);
}
Err(e)
}
Ok((version, serial)) => {
let yubikey = YubiKey {
card,
name: String::from(reader.name()),
pin: None,
version,
serial,
};
Ok(yubikey)
}
}
}
}
+2 -2
View File
@@ -8,7 +8,7 @@ use once_cell::sync::Lazy;
use rand_core::{OsRng, RngCore};
use rsa::pkcs1v15;
use sha2::{Digest, Sha256};
use signature::{hazmat::PrehashVerifier, Signature as _};
use signature::hazmat::PrehashVerifier;
use std::{env, str::FromStr, sync::Mutex};
use x509::RelativeDistinguishedName;
use yubikey::{
@@ -203,7 +203,7 @@ fn generate_self_signed_rsa_cert() {
let data = cert.as_ref();
let tbs_cert_len = u16::from_be_bytes(data[6..8].try_into().unwrap()) as usize;
let msg = &data[4..8 + tbs_cert_len];
let sig = pkcs1v15::Signature::from_bytes(&data[data.len() - 128..]).unwrap();
let sig = pkcs1v15::Signature::try_from(&data[data.len() - 128..]).unwrap();
let hash = Sha256::digest(msg);
assert!(pubkey.verify_prehash(&hash, &sig).is_ok());