Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 43dfc06875 | |||
| e230390e7e | |||
| ef3df46ed2 | |||
| 18e3636161 | |||
| 9d1da84233 | |||
| 37088bba56 | |||
| 3580c45f71 | |||
| 79c289ac00 | |||
| cbca858488 | |||
| 8b896ab4de | |||
| 24b035008c | |||
| 90bc878b21 | |||
| 08185c5ec9 | |||
| 7da2c7ba6a | |||
| fecd786262 | |||
| 1a6d1d0a71 | |||
| f43539088b | |||
| 72f63131ac | |||
| b59856d09e | |||
| 7628ebf605 | |||
| 0688dbf30d | |||
| 7e3d0bc838 |
@@ -3,7 +3,7 @@ name: CI
|
|||||||
on:
|
on:
|
||||||
pull_request: {}
|
pull_request: {}
|
||||||
push:
|
push:
|
||||||
branches: develop
|
branches: main
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CARGO_INCREMENTAL: 0
|
CARGO_INCREMENTAL: 0
|
||||||
@@ -36,13 +36,13 @@ jobs:
|
|||||||
toolchain: stable
|
toolchain: stable
|
||||||
deps: true
|
deps: true
|
||||||
- platform: ubuntu-latest
|
- platform: ubuntu-latest
|
||||||
toolchain: 1.44.0 # MSRV
|
toolchain: 1.46.0 # MSRV
|
||||||
deps: sudo apt-get install libpcsclite-dev
|
deps: sudo apt-get install libpcsclite-dev
|
||||||
- platform: windows-latest
|
- platform: windows-latest
|
||||||
toolchain: 1.44.0 # MSRV
|
toolchain: 1.46.0 # MSRV
|
||||||
deps: true
|
deps: true
|
||||||
- platform: macos-latest
|
- platform: macos-latest
|
||||||
toolchain: 1.44.0 # MSRV
|
toolchain: 1.46.0 # MSRV
|
||||||
deps: true
|
deps: true
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
steps:
|
steps:
|
||||||
@@ -82,7 +82,7 @@ jobs:
|
|||||||
- uses: actions-rs/toolchain@v1
|
- uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
toolchain: 1.44.0 # MSRV
|
toolchain: 1.46.0 # MSRV
|
||||||
components: clippy
|
components: clippy
|
||||||
- run: sudo apt-get install libpcsclite-dev
|
- run: sudo apt-get install libpcsclite-dev
|
||||||
- run: cargo clippy --all --exclude crypto_box --all-features -- -D warnings
|
- run: cargo clippy --all --exclude crypto_box --all-features -- -D warnings
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
paths: Cargo.lock
|
paths: Cargo.lock
|
||||||
push:
|
push:
|
||||||
branches: develop
|
branches: main
|
||||||
paths: Cargo.lock
|
paths: Cargo.lock
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 * * *'
|
- cron: '0 0 * * *'
|
||||||
|
|||||||
+29
-1
@@ -4,7 +4,35 @@ 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/),
|
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).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## 0.1.0 (2020-10-18)
|
## 0.3.0 (2021-03-22)
|
||||||
|
### Added
|
||||||
|
- Typed structs for PIN-protected and admin metadata ([#223])
|
||||||
|
- `MgmKey::set_default`/`MgmKey::set_manual` methods ([#224])
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Have `Transaction::set_mgm_key` take touch requirement as bool ([#224])
|
||||||
|
|
||||||
|
### 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
|
||||||
|
|
||||||
|
## 0.2.0 (2021-01-30)
|
||||||
|
### Changed
|
||||||
|
- Bump `der-parser` to v5.0 ([#194])
|
||||||
|
- Improve self-signed certificates ([#207])
|
||||||
|
- Bump `x509-parser` to v0.9 ([#208])
|
||||||
|
- Bump elliptic-curve to 0.8. Also requires bumping p256 and p384 ([#208])
|
||||||
|
- 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
|
||||||
|
|
||||||
|
## 0.1.0 (2020-10-19)
|
||||||
### Added
|
### Added
|
||||||
- `Certificate::generate_self_signed` ([#80])
|
- `Certificate::generate_self_signed` ([#80])
|
||||||
- `YubiKey::open_by_serial` ([#69])
|
- `YubiKey::open_by_serial` ([#69])
|
||||||
|
|||||||
Generated
+229
-278
File diff suppressed because it is too large
Load Diff
+10
-10
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "yubikey-piv"
|
name = "yubikey-piv"
|
||||||
version = "0.1.0" # Also update html_root_url in lib.rs when bumping this
|
version = "0.3.0" # Also update html_root_url in lib.rs when bumping this
|
||||||
description = """
|
description = """
|
||||||
Pure Rust host-side driver for the YubiKey Personal Identity Verification (PIV)
|
Pure Rust host-side driver for the YubiKey Personal Identity Verification (PIV)
|
||||||
application providing general-purpose public-key signing and encryption
|
application providing general-purpose public-key signing and encryption
|
||||||
@@ -24,19 +24,19 @@ maintenance = { status = "experimental" }
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
cookie-factory = "0.3"
|
cookie-factory = "0.3"
|
||||||
der-parser = "4"
|
der-parser = "5"
|
||||||
des = "0.6"
|
des = "0.6"
|
||||||
elliptic-curve = "0.6"
|
elliptic-curve = "0.8"
|
||||||
getrandom = "0.1"
|
getrandom = "0.1"
|
||||||
hmac = "0.10"
|
hmac = "0.10"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
nom = "5"
|
nom = "6"
|
||||||
num-bigint = { version = "0.6", features = ["rand"], package = "num-bigint-dig" }
|
num-bigint = { version = "0.6", features = ["rand"], package = "num-bigint-dig" }
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
num-integer = "0.1"
|
num-integer = "0.1"
|
||||||
pbkdf2 = "0.6"
|
pbkdf2 = { version = "0.7", default-features = false }
|
||||||
p256 = "0.5"
|
p256 = "0.7"
|
||||||
p384 = "0.4"
|
p384 = "0.6"
|
||||||
pcsc = "2"
|
pcsc = "2"
|
||||||
rsa = "0.3"
|
rsa = "0.3"
|
||||||
secrecy = "0.7"
|
secrecy = "0.7"
|
||||||
@@ -44,13 +44,13 @@ sha-1 = "0.9"
|
|||||||
sha2 = "0.9"
|
sha2 = "0.9"
|
||||||
subtle = "2"
|
subtle = "2"
|
||||||
subtle-encoding = "0.5"
|
subtle-encoding = "0.5"
|
||||||
x509 = "0.1.2"
|
x509 = "0.2"
|
||||||
x509-parser = "0.8"
|
x509-parser = "0.9"
|
||||||
zeroize = "1"
|
zeroize = "1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
||||||
ring = "0.16.15"
|
ring = "0.16.18"
|
||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<img src="https://raw.githubusercontent.com/iqlusioninc/yubikey-piv.rs/develop/img/logo.png" width="150" height="110">
|
<img src="https://raw.githubusercontent.com/iqlusioninc/yubikey-piv.rs/main/img/logo.png" width="150" height="110">
|
||||||
|
|
||||||
# yubikey-piv.rs
|
# yubikey-piv.rs
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ endorsed by Yubico.
|
|||||||
|
|
||||||
## Minimum Supported Rust Version
|
## Minimum Supported Rust Version
|
||||||
|
|
||||||
- Rust **1.44** or newer
|
- Rust **1.46** or newer
|
||||||
|
|
||||||
## Supported YubiKeys
|
## Supported YubiKeys
|
||||||
|
|
||||||
@@ -193,12 +193,12 @@ or conditions.
|
|||||||
[docs-image]: https://docs.rs/yubikey-piv/badge.svg
|
[docs-image]: https://docs.rs/yubikey-piv/badge.svg
|
||||||
[docs-link]: https://docs.rs/yubikey-piv/
|
[docs-link]: https://docs.rs/yubikey-piv/
|
||||||
[license-image]: https://img.shields.io/badge/license-BSD-blue.svg
|
[license-image]: https://img.shields.io/badge/license-BSD-blue.svg
|
||||||
[license-link]: https://github.com/iqlusioninc/yubikey-piv.rs/blob/develop/COPYING
|
[license-link]: https://github.com/iqlusioninc/yubikey-piv.rs/blob/main/COPYING
|
||||||
[rustc-image]: https://img.shields.io/badge/rustc-1.44+-blue.svg
|
[rustc-image]: https://img.shields.io/badge/rustc-1.46+-blue.svg
|
||||||
[maintenance-image]: https://img.shields.io/badge/maintenance-experimental-blue.svg
|
[maintenance-image]: https://img.shields.io/badge/maintenance-experimental-blue.svg
|
||||||
[safety-image]: https://img.shields.io/badge/unsafe-forbidden-success.svg
|
[safety-image]: https://img.shields.io/badge/unsafe-forbidden-success.svg
|
||||||
[safety-link]: https://github.com/rust-secure-code/safety-dance/
|
[safety-link]: https://github.com/rust-secure-code/safety-dance/
|
||||||
[build-image]: https://github.com/iqlusioninc/yubikey-piv.rs/workflows/CI/badge.svg?branch=develop&event=push
|
[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-link]: https://github.com/iqlusioninc/yubikey-piv.rs/actions
|
||||||
[gitter-image]: https://badges.gitter.im/badge.svg
|
[gitter-image]: https://badges.gitter.im/badge.svg
|
||||||
[gitter-link]: https://gitter.im/iqlusioninc/community
|
[gitter-link]: https://gitter.im/iqlusioninc/community
|
||||||
@@ -214,7 +214,7 @@ or conditions.
|
|||||||
[yubico-piv-tool]: https://github.com/Yubico/yubico-piv-tool/
|
[yubico-piv-tool]: https://github.com/Yubico/yubico-piv-tool/
|
||||||
[Corrode]: https://github.com/jameysharp/corrode
|
[Corrode]: https://github.com/jameysharp/corrode
|
||||||
[cc-web]: https://contributor-covenant.org/
|
[cc-web]: https://contributor-covenant.org/
|
||||||
[cc-md]: https://github.com/iqlusioninc/yubikey-piv.rs/blob/develop/CODE_OF_CONDUCT.md
|
[cc-md]: https://github.com/iqlusioninc/yubikey-piv.rs/blob/main/CODE_OF_CONDUCT.md
|
||||||
[BSDL]: https://opensource.org/licenses/BSD-2-Clause
|
[BSDL]: https://opensource.org/licenses/BSD-2-Clause
|
||||||
|
|
||||||
[//]: # (github issues)
|
[//]: # (github issues)
|
||||||
|
|||||||
@@ -4,5 +4,30 @@ 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/),
|
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).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## 0.2.0 (2021-01-30)
|
||||||
|
### Changed
|
||||||
|
- Bump MSRV to 1.46+ ([#208])
|
||||||
|
- Bump `yubikey-piv` dependency to v0.2.0 ([#220])
|
||||||
|
|
||||||
|
[#208]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/208
|
||||||
|
[#220]: https://github.com/iqlusioninc/yubikey-piv.rs/pull/220
|
||||||
|
|
||||||
|
## 0.1.0 (2020-10-19)
|
||||||
|
### Added
|
||||||
|
- `status` command ([#72], [#74])
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Bump `yubikey-piv` to v0.1.0 ([#180])
|
||||||
|
- Bump `x509-parser` to v0.8 ([#181])
|
||||||
|
- 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
|
||||||
|
|
||||||
## 0.0.1 (2019-12-02)
|
## 0.0.1 (2019-12-02)
|
||||||
- Initial release
|
- Initial release
|
||||||
|
|||||||
+4
-4
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "yubikey-cli"
|
name = "yubikey-cli"
|
||||||
version = "0.0.1"
|
version = "0.2.0"
|
||||||
description = """
|
description = """
|
||||||
Command-line interface for performing encryption and signing using RSA/ECC keys
|
Command-line interface for performing encryption and signing using RSA/ECC keys
|
||||||
stored on YubiKey devices.
|
stored on YubiKey devices.
|
||||||
@@ -18,8 +18,8 @@ gumdrop = "0.8"
|
|||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
sha2 = "0.8"
|
sha2 = "0.9"
|
||||||
subtle-encoding = "0.5"
|
subtle-encoding = "0.5"
|
||||||
termcolor = "1"
|
termcolor = "1"
|
||||||
x509-parser = "0.7"
|
x509-parser = "0.9"
|
||||||
yubikey-piv = { version = "0.1", path = ".." }
|
yubikey-piv = { version = "0.3", path = ".." }
|
||||||
|
|||||||
+3
-3
@@ -1,4 +1,4 @@
|
|||||||
<img src="https://raw.githubusercontent.com/iqlusioninc/yubikey-piv.rs/develop/img/logo.png" width="150" height="110">
|
<img src="https://raw.githubusercontent.com/iqlusioninc/yubikey-piv.rs/main/img/logo.png" width="150" height="110">
|
||||||
|
|
||||||
# yubikey-cli.rs
|
# yubikey-cli.rs
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ or conditions.
|
|||||||
[maintenance-image]: https://img.shields.io/badge/maintenance-experimental-blue.svg
|
[maintenance-image]: https://img.shields.io/badge/maintenance-experimental-blue.svg
|
||||||
[safety-image]: https://img.shields.io/badge/unsafe-forbidden-success.svg
|
[safety-image]: https://img.shields.io/badge/unsafe-forbidden-success.svg
|
||||||
[safety-link]: https://github.com/rust-secure-code/safety-dance/
|
[safety-link]: https://github.com/rust-secure-code/safety-dance/
|
||||||
[build-image]: https://github.com/iqlusioninc/yubikey-piv.rs/workflows/Rust/badge.svg?branch=develop&event=push
|
[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-link]: https://github.com/iqlusioninc/yubikey-piv.rs/actions
|
||||||
[gitter-image]: https://badges.gitter.im/badge.svg
|
[gitter-image]: https://badges.gitter.im/badge.svg
|
||||||
[gitter-link]: https://gitter.im/iqlusioninc/community
|
[gitter-link]: https://gitter.im/iqlusioninc/community
|
||||||
@@ -108,5 +108,5 @@ or conditions.
|
|||||||
[yubico-piv-tool]: https://github.com/Yubico/yubico-piv-tool/
|
[yubico-piv-tool]: https://github.com/Yubico/yubico-piv-tool/
|
||||||
[Corrode]: https://github.com/jameysharp/corrode
|
[Corrode]: https://github.com/jameysharp/corrode
|
||||||
[cc-web]: https://contributor-covenant.org/
|
[cc-web]: https://contributor-covenant.org/
|
||||||
[cc-md]: https://github.com/iqlusioninc/yubikey-cli.rs/blob/develop/CODE_OF_CONDUCT.md
|
[cc-md]: https://github.com/iqlusioninc/yubikey-cli.rs/blob/main/CODE_OF_CONDUCT.md
|
||||||
[BSDL]: https://opensource.org/licenses/BSD-2-Clause
|
[BSDL]: https://opensource.org/licenses/BSD-2-Clause
|
||||||
|
|||||||
+4
-4
@@ -19,7 +19,7 @@ use std::io::{self, Write};
|
|||||||
use std::str;
|
use std::str;
|
||||||
use subtle_encoding::hex;
|
use subtle_encoding::hex;
|
||||||
use termcolor::{ColorSpec, StandardStreamLock, WriteColor};
|
use termcolor::{ColorSpec, StandardStreamLock, WriteColor};
|
||||||
use x509_parser::parse_x509_der;
|
use x509_parser::parse_x509_certificate;
|
||||||
use yubikey_piv::{certificate::Certificate, key::*, YubiKey};
|
use yubikey_piv::{certificate::Certificate, key::*, YubiKey};
|
||||||
|
|
||||||
///Write information about certificate found in slot a la yubico-piv-tool output.
|
///Write information about certificate found in slot a la yubico-piv-tool output.
|
||||||
@@ -41,7 +41,7 @@ pub fn print_cert_info(
|
|||||||
let fingerprint = Sha256::digest(&buf);
|
let fingerprint = Sha256::digest(&buf);
|
||||||
let slot_id: u8 = slot.into();
|
let slot_id: u8 = slot.into();
|
||||||
print_cert_attr(stream, "Slot", format!("{:x}", slot_id))?;
|
print_cert_attr(stream, "Slot", format!("{:x}", slot_id))?;
|
||||||
match parse_x509_der(&buf) {
|
match parse_x509_certificate(&buf) {
|
||||||
Ok((_rem, cert)) => {
|
Ok((_rem, cert)) => {
|
||||||
print_cert_attr(
|
print_cert_attr(
|
||||||
stream,
|
stream,
|
||||||
@@ -59,12 +59,12 @@ pub fn print_cert_info(
|
|||||||
print_cert_attr(
|
print_cert_attr(
|
||||||
stream,
|
stream,
|
||||||
"Not Before",
|
"Not Before",
|
||||||
cert.tbs_certificate.validity.not_before.asctime(),
|
cert.tbs_certificate.validity.not_before.to_rfc2822(),
|
||||||
)?;
|
)?;
|
||||||
print_cert_attr(
|
print_cert_attr(
|
||||||
stream,
|
stream,
|
||||||
"Not After",
|
"Not After",
|
||||||
cert.tbs_certificate.validity.not_after.asctime(),
|
cert.tbs_certificate.validity.not_after.to_rfc2822(),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
|||||||
+30
-15
@@ -49,7 +49,8 @@ use sha2::{Digest, Sha256};
|
|||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
use x509_parser::{parse_x509_der, x509::SubjectPublicKeyInfo};
|
use x509::{der::Oid, RelativeDistinguishedName};
|
||||||
|
use x509_parser::{parse_x509_certificate, x509::SubjectPublicKeyInfo};
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
use crate::CB_OBJ_MAX;
|
use crate::CB_OBJ_MAX;
|
||||||
@@ -205,7 +206,13 @@ impl PublicKeyInfo {
|
|||||||
}
|
}
|
||||||
OID_EC_PUBLIC_KEY => {
|
OID_EC_PUBLIC_KEY => {
|
||||||
let key_bytes = &subject_pki.subject_public_key.data;
|
let key_bytes = &subject_pki.subject_public_key.data;
|
||||||
match read_pki::ec_parameters(&subject_pki.algorithm.parameters)? {
|
let algorithm_parameters = subject_pki
|
||||||
|
.algorithm
|
||||||
|
.parameters
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(Error::InvalidObject)?;
|
||||||
|
|
||||||
|
match read_pki::ec_parameters(algorithm_parameters)? {
|
||||||
AlgorithmId::EccP256 => EcPublicKey::from_bytes(key_bytes)
|
AlgorithmId::EccP256 => EcPublicKey::from_bytes(key_bytes)
|
||||||
.map(PublicKeyInfo::EcP256)
|
.map(PublicKeyInfo::EcP256)
|
||||||
.map_err(|_| Error::InvalidObject),
|
.map_err(|_| Error::InvalidObject),
|
||||||
@@ -331,19 +338,21 @@ impl<'a> TryFrom<&'a [u8]> for Certificate {
|
|||||||
impl Certificate {
|
impl Certificate {
|
||||||
/// Creates a new self-signed certificate for the given key. Writes the resulting
|
/// Creates a new self-signed certificate for the given key. Writes the resulting
|
||||||
/// certificate to the slot before returning it.
|
/// certificate to the slot before returning it.
|
||||||
pub fn generate_self_signed(
|
///
|
||||||
|
/// `extensions` is optional; if empty, no extensions will be included. Due to the
|
||||||
|
/// need for an `O: Oid` type parameter, users who do not have any extensions should
|
||||||
|
/// use the workaround `let extensions: &[x509::Extension<'_, &[u64]>] = &[];`.
|
||||||
|
pub fn generate_self_signed<O: Oid>(
|
||||||
yubikey: &mut YubiKey,
|
yubikey: &mut YubiKey,
|
||||||
key: SlotId,
|
key: SlotId,
|
||||||
serial: impl Into<Serial>,
|
serial: impl Into<Serial>,
|
||||||
not_after: Option<DateTime<Utc>>,
|
not_after: Option<DateTime<Utc>>,
|
||||||
subject: String,
|
subject: &[RelativeDistinguishedName<'_>],
|
||||||
subject_pki: PublicKeyInfo,
|
subject_pki: PublicKeyInfo,
|
||||||
|
extensions: &[x509::Extension<'_, O>],
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
let serial = serial.into();
|
let serial = serial.into();
|
||||||
|
|
||||||
// Issuer and subject are the same in self-signed certificates
|
|
||||||
let issuer = subject.clone();
|
|
||||||
|
|
||||||
let mut tbs_cert = Buffer::new(Vec::with_capacity(CB_OBJ_MAX));
|
let mut tbs_cert = Buffer::new(Vec::with_capacity(CB_OBJ_MAX));
|
||||||
|
|
||||||
let signature_algorithm = match subject_pki.algorithm() {
|
let signature_algorithm = match subject_pki.algorithm() {
|
||||||
@@ -355,11 +364,13 @@ impl Certificate {
|
|||||||
x509::write::tbs_certificate(
|
x509::write::tbs_certificate(
|
||||||
&serial.to_bytes(),
|
&serial.to_bytes(),
|
||||||
&signature_algorithm,
|
&signature_algorithm,
|
||||||
&issuer,
|
// Issuer and subject are the same in self-signed certificates.
|
||||||
|
&subject,
|
||||||
Utc::now(),
|
Utc::now(),
|
||||||
not_after,
|
not_after,
|
||||||
&subject,
|
&subject,
|
||||||
&subject_pki,
|
&subject_pki,
|
||||||
|
&extensions,
|
||||||
),
|
),
|
||||||
tbs_cert.deref_mut(),
|
tbs_cert.deref_mut(),
|
||||||
)
|
)
|
||||||
@@ -419,6 +430,15 @@ impl Certificate {
|
|||||||
)
|
)
|
||||||
.expect("can serialize to Vec");
|
.expect("can serialize to Vec");
|
||||||
|
|
||||||
|
let (issuer, subject) = parse_x509_certificate(&data)
|
||||||
|
.map(|(_, cert)| {
|
||||||
|
(
|
||||||
|
cert.tbs_certificate.issuer.to_string(),
|
||||||
|
cert.tbs_certificate.subject.to_string(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.expect("We just serialized this correctly");
|
||||||
|
|
||||||
let cert = Certificate {
|
let cert = Certificate {
|
||||||
serial,
|
serial,
|
||||||
issuer,
|
issuer,
|
||||||
@@ -471,7 +491,7 @@ impl Certificate {
|
|||||||
return Err(Error::SizeError);
|
return Err(Error::SizeError);
|
||||||
}
|
}
|
||||||
|
|
||||||
let parsed_cert = match parse_x509_der(&cert) {
|
let parsed_cert = match parse_x509_certificate(&cert) {
|
||||||
Ok((_, cert)) => cert,
|
Ok((_, cert)) => cert,
|
||||||
_ => return Err(Error::InvalidObject),
|
_ => return Err(Error::InvalidObject),
|
||||||
};
|
};
|
||||||
@@ -631,12 +651,7 @@ mod read_pki {
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub(super) fn ec_parameters(parameters: &DerObject<'_>) -> Result<AlgorithmId, Error> {
|
pub(super) fn ec_parameters(parameters: &DerObject<'_>) -> Result<AlgorithmId, Error> {
|
||||||
let curve_oid = match parameters.as_context_specific() {
|
let curve_oid = parameters.as_oid_val().map_err(|_| Error::InvalidObject)?;
|
||||||
Ok((_, Some(named_curve))) => {
|
|
||||||
named_curve.as_oid_val().map_err(|_| Error::InvalidObject)
|
|
||||||
}
|
|
||||||
_ => Err(Error::InvalidObject),
|
|
||||||
}?;
|
|
||||||
|
|
||||||
match curve_oid.to_string().as_str() {
|
match curve_oid.to_string().as_str() {
|
||||||
OID_NIST_P256 => Ok(AlgorithmId::EccP256),
|
OID_NIST_P256 => Ok(AlgorithmId::EccP256),
|
||||||
|
|||||||
+10
-10
@@ -32,11 +32,11 @@
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::Error,
|
error::Error,
|
||||||
metadata,
|
metadata::{AdminData, ProtectedData},
|
||||||
mgm::{MgmType, ADMIN_FLAGS_1_PROTECTED_MGM},
|
mgm::{MgmType, ADMIN_FLAGS_1_PROTECTED_MGM},
|
||||||
yubikey::{YubiKey, ADMIN_FLAGS_1_PUK_BLOCKED},
|
yubikey::{YubiKey, ADMIN_FLAGS_1_PUK_BLOCKED},
|
||||||
TAG_ADMIN, TAG_ADMIN_FLAGS_1, TAG_ADMIN_SALT, TAG_ADMIN_TIMESTAMP, TAG_PROTECTED,
|
TAG_ADMIN_FLAGS_1, TAG_ADMIN_SALT, TAG_ADMIN_TIMESTAMP, TAG_PROTECTED_FLAGS_1,
|
||||||
TAG_PROTECTED_FLAGS_1, TAG_PROTECTED_MGM,
|
TAG_PROTECTED_MGM,
|
||||||
};
|
};
|
||||||
use log::error;
|
use log::error;
|
||||||
use std::{
|
use std::{
|
||||||
@@ -79,8 +79,8 @@ impl Config {
|
|||||||
|
|
||||||
let txn = yubikey.begin_transaction()?;
|
let txn = yubikey.begin_transaction()?;
|
||||||
|
|
||||||
if let Ok(data) = metadata::read(&txn, TAG_ADMIN) {
|
if let Ok(admin_data) = AdminData::read(&txn) {
|
||||||
if let Ok(item) = metadata::get_item(&data, TAG_ADMIN_FLAGS_1) {
|
if let Ok(item) = admin_data.get_item(TAG_ADMIN_FLAGS_1) {
|
||||||
if item.is_empty() {
|
if item.is_empty() {
|
||||||
error!("empty response for admin flags metadata item! ignoring");
|
error!("empty response for admin flags metadata item! ignoring");
|
||||||
} else {
|
} else {
|
||||||
@@ -94,7 +94,7 @@ impl Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if metadata::get_item(&data, TAG_ADMIN_SALT).is_ok() {
|
if admin_data.get_item(TAG_ADMIN_SALT).is_ok() {
|
||||||
if config.mgm_type != MgmType::Manual {
|
if config.mgm_type != MgmType::Manual {
|
||||||
error!("conflicting types of MGM key administration configured");
|
error!("conflicting types of MGM key administration configured");
|
||||||
} else {
|
} else {
|
||||||
@@ -102,7 +102,7 @@ impl Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(item) = metadata::get_item(&data, TAG_ADMIN_TIMESTAMP) {
|
if let Ok(item) = admin_data.get_item(TAG_ADMIN_TIMESTAMP) {
|
||||||
if item.len() != CB_ADMIN_TIMESTAMP {
|
if item.len() != CB_ADMIN_TIMESTAMP {
|
||||||
error!("pin timestamp in admin metadata is an invalid size");
|
error!("pin timestamp in admin metadata is an invalid size");
|
||||||
} else {
|
} else {
|
||||||
@@ -117,10 +117,10 @@ impl Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(data) = metadata::read(&txn, TAG_PROTECTED) {
|
if let Ok(protected_data) = ProtectedData::read(&txn) {
|
||||||
config.protected_data_available = true;
|
config.protected_data_available = true;
|
||||||
|
|
||||||
if let Ok(item) = metadata::get_item(&data, TAG_PROTECTED_FLAGS_1) {
|
if let Ok(item) = protected_data.get_item(TAG_PROTECTED_FLAGS_1) {
|
||||||
if item.is_empty() {
|
if item.is_empty() {
|
||||||
error!("empty response for protected flags metadata item! ignoring");
|
error!("empty response for protected flags metadata item! ignoring");
|
||||||
} else if item[0] & PROTECTED_FLAGS_1_PUK_NOBLOCK != 0 {
|
} else if item[0] & PROTECTED_FLAGS_1_PUK_NOBLOCK != 0 {
|
||||||
@@ -128,7 +128,7 @@ impl Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if metadata::get_item(&data, TAG_PROTECTED_MGM).is_ok() {
|
if protected_data.get_item(TAG_PROTECTED_MGM).is_ok() {
|
||||||
if config.mgm_type != MgmType::Protected {
|
if config.mgm_type != MgmType::Protected {
|
||||||
error!(
|
error!(
|
||||||
"conflicting types of mgm key administration configured: protected MGM exists"
|
"conflicting types of mgm key administration configured: protected MGM exists"
|
||||||
|
|||||||
+4
-6
@@ -83,12 +83,12 @@
|
|||||||
//! [YubiKey NEO]: https://support.yubico.com/support/solutions/articles/15000006494-yubikey-neo
|
//! [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 4]: https://support.yubico.com/support/solutions/articles/15000006486-yubikey-4
|
||||||
//! [YubiKey 5]: https://www.yubico.com/products/yubikey-5-overview/
|
//! [YubiKey 5]: https://www.yubico.com/products/yubikey-5-overview/
|
||||||
//! [status]: https://github.com/tarcieri/yubikey-piv.rs#status
|
//! [status]: https://github.com/iqlusioninc/yubikey-piv.rs#status
|
||||||
//! [yubico-piv-tool]: https://github.com/Yubico/yubico-piv-tool/
|
//! [yubico-piv-tool]: https://github.com/Yubico/yubico-piv-tool/
|
||||||
//! [Corrode]: https://github.com/jameysharp/corrode
|
//! [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
|
//! [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-web]: https://contributor-covenant.org/
|
||||||
//! [cc-md]: https://github.com/tarcieri/yubikey-piv.rs/blob/develop/CODE_OF_CONDUCT.md
|
//! [cc-md]: https://github.com/iqlusioninc/yubikey-piv.rs/blob/main/CODE_OF_CONDUCT.md
|
||||||
//! [BSDL]: https://opensource.org/licenses/BSD-2-Clause
|
//! [BSDL]: https://opensource.org/licenses/BSD-2-Clause
|
||||||
|
|
||||||
// Adapted from yubico-piv-tool:
|
// Adapted from yubico-piv-tool:
|
||||||
@@ -122,8 +122,8 @@
|
|||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#![doc(
|
#![doc(
|
||||||
html_logo_url = "https://raw.githubusercontent.com/iqlusioninc/yubikey-piv.rs/develop/img/logo.png",
|
html_logo_url = "https://raw.githubusercontent.com/iqlusioninc/yubikey-piv.rs/main/img/logo.png",
|
||||||
html_root_url = "https://docs.rs/yubikey-piv/0.1.0"
|
html_root_url = "https://docs.rs/yubikey-piv/0.3.0"
|
||||||
)]
|
)]
|
||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
#![warn(
|
#![warn(
|
||||||
@@ -178,11 +178,9 @@ pub(crate) const CB_OBJ_TAG_MIN: usize = 2; // 1 byte tag + 1 byte len
|
|||||||
#[cfg(feature = "untested")]
|
#[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 CB_OBJ_TAG_MAX: usize = CB_OBJ_TAG_MIN + 2; // 1 byte tag + 3 bytes len
|
||||||
|
|
||||||
pub(crate) const TAG_ADMIN: u8 = 0x80;
|
|
||||||
pub(crate) const TAG_ADMIN_FLAGS_1: u8 = 0x81;
|
pub(crate) const TAG_ADMIN_FLAGS_1: u8 = 0x81;
|
||||||
pub(crate) const TAG_ADMIN_SALT: u8 = 0x82;
|
pub(crate) const TAG_ADMIN_SALT: u8 = 0x82;
|
||||||
pub(crate) const TAG_ADMIN_TIMESTAMP: u8 = 0x83;
|
pub(crate) const TAG_ADMIN_TIMESTAMP: u8 = 0x83;
|
||||||
pub(crate) const TAG_PROTECTED: u8 = 0x88;
|
|
||||||
pub(crate) const TAG_PROTECTED_FLAGS_1: u8 = 0x81;
|
pub(crate) const TAG_PROTECTED_FLAGS_1: u8 = 0x81;
|
||||||
pub(crate) const TAG_PROTECTED_MGM: u8 = 0x89;
|
pub(crate) const TAG_PROTECTED_MGM: u8 = 0x89;
|
||||||
|
|
||||||
|
|||||||
+180
-122
@@ -30,153 +30,187 @@
|
|||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
use crate::{
|
use std::marker::PhantomData;
|
||||||
error::Error, serialization::*, transaction::Transaction, Buffer, TAG_ADMIN, TAG_PROTECTED,
|
use zeroize::Zeroizing;
|
||||||
};
|
|
||||||
|
use crate::{error::Error, serialization::*, transaction::Transaction, Buffer};
|
||||||
|
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
use crate::{CB_OBJ_MAX, CB_OBJ_TAG_MAX};
|
use crate::{CB_OBJ_MAX, CB_OBJ_TAG_MAX};
|
||||||
|
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
use zeroize::Zeroizing;
|
use std::iter;
|
||||||
|
|
||||||
|
const TAG_ADMIN: u8 = 0x80;
|
||||||
|
const TAG_PROTECTED: u8 = 0x88;
|
||||||
pub const OBJ_ADMIN_DATA: u32 = 0x005f_ff00;
|
pub const OBJ_ADMIN_DATA: u32 = 0x005f_ff00;
|
||||||
pub const OBJ_PRINTED: u32 = 0x005f_c109;
|
pub const OBJ_PRINTED: u32 = 0x005f_c109;
|
||||||
|
|
||||||
/// Get metadata item
|
pub(crate) trait MetadataType: private::Sealed {}
|
||||||
pub(crate) fn get_item(mut data: &[u8], tag: u8) -> Result<&[u8], Error> {
|
|
||||||
while !data.is_empty() {
|
|
||||||
let (remaining, tlv) = Tlv::parse(data)?;
|
|
||||||
data = remaining;
|
|
||||||
|
|
||||||
if tlv.tag == tag {
|
/// A type variable corresponding to PIN-protected metadata.
|
||||||
// found tag
|
pub(crate) enum Protected {}
|
||||||
return Ok(tlv.value);
|
impl MetadataType for Protected {}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(Error::GenericError)
|
/// A type variable corresponding to administrative metadata.
|
||||||
|
pub(crate) enum Admin {}
|
||||||
|
impl MetadataType for Admin {}
|
||||||
|
|
||||||
|
/// Metadata stored in a YubiKey.
|
||||||
|
pub(crate) struct Metadata<T: MetadataType> {
|
||||||
|
inner: Buffer,
|
||||||
|
_marker: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set metadata item
|
/// PIN-protected metadata stored in a YubiKey.
|
||||||
#[cfg(feature = "untested")]
|
pub(crate) type ProtectedData = Metadata<Protected>;
|
||||||
pub(crate) fn set_item(
|
/// Administrative metadata stored in a YubiKey.
|
||||||
data: &mut [u8],
|
pub(crate) type AdminData = Metadata<Admin>;
|
||||||
pcb_data: &mut usize,
|
|
||||||
cb_data_max: usize,
|
|
||||||
tag: u8,
|
|
||||||
p_item: &[u8],
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let mut cb_temp: usize = 0;
|
|
||||||
let mut tag_temp: u8 = 0;
|
|
||||||
let mut cb_len: usize = 0;
|
|
||||||
let cb_item = p_item.len();
|
|
||||||
|
|
||||||
let mut offset = 0;
|
impl<T: MetadataType> Default for Metadata<T> {
|
||||||
|
fn default() -> Self {
|
||||||
while offset < *pcb_data {
|
Metadata {
|
||||||
tag_temp = data[offset];
|
inner: Zeroizing::new(vec![]),
|
||||||
offset += 1;
|
_marker: PhantomData::default(),
|
||||||
|
|
||||||
cb_len = get_length(&data[offset..], &mut cb_temp);
|
|
||||||
offset += cb_len;
|
|
||||||
|
|
||||||
if tag_temp == tag {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
offset += cb_temp;
|
impl<T: MetadataType> Metadata<T> {
|
||||||
|
/// Read metadata
|
||||||
|
pub(crate) fn read(txn: &Transaction<'_>) -> Result<Self, Error> {
|
||||||
|
let data = txn.fetch_object(T::obj_id())?;
|
||||||
|
Ok(Metadata {
|
||||||
|
inner: Tlv::parse_single(data, T::tag())?,
|
||||||
|
_marker: PhantomData::default(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if tag_temp != tag {
|
/// Write metadata
|
||||||
if cb_item == 0 {
|
#[cfg(feature = "untested")]
|
||||||
// We've been asked to delete an existing item that isn't in the blob
|
pub(crate) fn write(&self, txn: &Transaction<'_>) -> Result<(), Error> {
|
||||||
|
if self.inner.len() > CB_OBJ_MAX - CB_OBJ_TAG_MAX {
|
||||||
|
return Err(Error::GenericError);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.inner.is_empty() {
|
||||||
|
return Self::delete(txn);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut buf = Zeroizing::new(vec![0u8; CB_OBJ_MAX]);
|
||||||
|
let len = Tlv::write(&mut buf, T::tag(), &self.inner)?;
|
||||||
|
|
||||||
|
txn.save_object(T::obj_id(), &buf[..len])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete metadata
|
||||||
|
#[cfg(feature = "untested")]
|
||||||
|
pub(crate) fn delete(txn: &Transaction<'_>) -> Result<(), Error> {
|
||||||
|
txn.save_object(T::obj_id(), &[])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get metadata item
|
||||||
|
pub(crate) fn get_item(&self, tag: u8) -> Result<&[u8], Error> {
|
||||||
|
let mut data = &self.inner[..];
|
||||||
|
|
||||||
|
while !data.is_empty() {
|
||||||
|
let (remaining, tlv) = Tlv::parse(data)?;
|
||||||
|
data = remaining;
|
||||||
|
|
||||||
|
if tlv.tag == tag {
|
||||||
|
// found tag
|
||||||
|
return Ok(tlv.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(Error::GenericError)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set metadata item
|
||||||
|
#[cfg(feature = "untested")]
|
||||||
|
pub(crate) fn set_item(&mut self, tag: u8, item: &[u8]) -> Result<(), Error> {
|
||||||
|
let mut cb_temp: usize = 0;
|
||||||
|
let mut tag_temp: u8 = 0;
|
||||||
|
let mut cb_len: usize = 0;
|
||||||
|
|
||||||
|
let mut offset = 0;
|
||||||
|
|
||||||
|
while offset < self.inner.len() {
|
||||||
|
tag_temp = self.inner[offset];
|
||||||
|
offset += 1;
|
||||||
|
|
||||||
|
cb_len = get_length(&self.inner[offset..], &mut cb_temp);
|
||||||
|
offset += cb_len;
|
||||||
|
|
||||||
|
if tag_temp == tag {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += cb_temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if tag_temp != tag {
|
||||||
|
if item.is_empty() {
|
||||||
|
// We've been asked to delete an existing item that isn't in the blob
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// We did not find an existing tag, append
|
||||||
|
assert_eq!(offset, self.inner.len());
|
||||||
|
self.inner
|
||||||
|
.extend(iter::repeat(0).take(1 + get_length_size(item.len()) + item.len()));
|
||||||
|
Tlv::write(&mut self.inner[offset..], tag, item)?;
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// We did not find an existing tag, append
|
// Found tag
|
||||||
*pcb_data += Tlv::write(&mut data[*pcb_data..], tag, p_item)?;
|
|
||||||
|
|
||||||
return Ok(());
|
// Check length, if it matches, overwrite
|
||||||
}
|
if cb_temp == item.len() {
|
||||||
|
self.inner[offset..offset + item.len()].copy_from_slice(item);
|
||||||
// Found tag
|
return Ok(());
|
||||||
|
|
||||||
// Check length, if it matches, overwrite
|
|
||||||
if cb_temp == cb_item {
|
|
||||||
data[offset..offset + cb_item].copy_from_slice(p_item);
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Length doesn't match, expand/shrink to fit
|
|
||||||
let next_offset = offset + cb_temp;
|
|
||||||
// Must be signed to have negative offsets
|
|
||||||
let cb_moved: isize = (cb_item as isize - cb_temp as isize)
|
|
||||||
+ if cb_item != 0 {
|
|
||||||
get_length_size(cb_item) as isize
|
|
||||||
} else {
|
|
||||||
// For tag, if deleting
|
|
||||||
-1
|
|
||||||
}
|
}
|
||||||
// Accounts for different length encoding
|
|
||||||
- cb_len as isize;
|
|
||||||
|
|
||||||
// If length would cause buffer overflow, return error
|
// Length doesn't match, expand/shrink to fit
|
||||||
if (*pcb_data as isize + cb_moved) as usize > cb_data_max {
|
let next_offset = offset + cb_temp;
|
||||||
return Err(Error::GenericError);
|
// Must be signed to have negative offsets
|
||||||
|
let cb_moved: isize = (item.len() as isize - cb_temp as isize)
|
||||||
|
+ if item.is_empty() {
|
||||||
|
// For tag, if deleting
|
||||||
|
-1
|
||||||
|
} else {
|
||||||
|
get_length_size(item.len()) as isize
|
||||||
|
}
|
||||||
|
// Accounts for different length encoding
|
||||||
|
- cb_len as isize;
|
||||||
|
|
||||||
|
// If length would cause buffer overflow, return error
|
||||||
|
if (self.inner.len() as isize + cb_moved) as usize > CB_OBJ_MAX {
|
||||||
|
return Err(Error::GenericError);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move remaining data
|
||||||
|
let orig_len = self.inner.len();
|
||||||
|
if cb_moved > 0 {
|
||||||
|
self.inner.extend(iter::repeat(0).take(cb_moved as usize));
|
||||||
|
}
|
||||||
|
self.inner.copy_within(
|
||||||
|
next_offset..orig_len,
|
||||||
|
(next_offset as isize + cb_moved) as usize,
|
||||||
|
);
|
||||||
|
self.inner
|
||||||
|
.resize((orig_len as isize + cb_moved) as usize, 0);
|
||||||
|
|
||||||
|
// Re-encode item and insert
|
||||||
|
if !item.is_empty() {
|
||||||
|
offset -= cb_len;
|
||||||
|
offset += set_length(&mut self.inner[offset..], item.len())?;
|
||||||
|
self.inner[offset..offset + item.len()].copy_from_slice(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move remaining data
|
|
||||||
data.copy_within(
|
|
||||||
next_offset..*pcb_data,
|
|
||||||
(next_offset as isize + cb_moved) as usize,
|
|
||||||
);
|
|
||||||
*pcb_data = (*pcb_data as isize + cb_moved) as usize;
|
|
||||||
|
|
||||||
// Re-encode item and insert
|
|
||||||
if cb_item != 0 {
|
|
||||||
offset -= cb_len;
|
|
||||||
offset += set_length(&mut data[offset..], cb_item)?;
|
|
||||||
data[offset..offset + cb_item].copy_from_slice(p_item);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read metadata
|
|
||||||
pub(crate) fn read(txn: &Transaction<'_>, tag: u8) -> Result<Buffer, Error> {
|
|
||||||
let obj_id = match tag {
|
|
||||||
TAG_ADMIN => OBJ_ADMIN_DATA,
|
|
||||||
TAG_PROTECTED => OBJ_PRINTED,
|
|
||||||
_ => return Err(Error::InvalidObject),
|
|
||||||
};
|
|
||||||
|
|
||||||
let data = txn.fetch_object(obj_id)?;
|
|
||||||
Tlv::parse_single(data, tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write metadata
|
|
||||||
#[cfg(feature = "untested")]
|
|
||||||
pub(crate) fn write(txn: &Transaction<'_>, tag: u8, data: &[u8]) -> Result<(), Error> {
|
|
||||||
if data.len() > CB_OBJ_MAX - CB_OBJ_TAG_MAX {
|
|
||||||
return Err(Error::GenericError);
|
|
||||||
}
|
|
||||||
|
|
||||||
let obj_id = match tag {
|
|
||||||
TAG_ADMIN => OBJ_ADMIN_DATA,
|
|
||||||
TAG_PROTECTED => OBJ_PRINTED,
|
|
||||||
_ => return Err(Error::InvalidObject),
|
|
||||||
};
|
|
||||||
|
|
||||||
if data.is_empty() {
|
|
||||||
// Deleting metadata
|
|
||||||
return txn.save_object(obj_id, &[]);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut buf = Zeroizing::new(vec![0u8; CB_OBJ_MAX]);
|
|
||||||
let len = Tlv::write(&mut buf, tag, data)?;
|
|
||||||
|
|
||||||
txn.save_object(obj_id, &buf[..len])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the size of a length tag for the given length
|
/// Get the size of a length tag for the given length
|
||||||
@@ -190,3 +224,27 @@ fn get_length_size(length: usize) -> usize {
|
|||||||
3
|
3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod private {
|
||||||
|
use super::*;
|
||||||
|
pub trait Sealed {
|
||||||
|
fn tag() -> u8;
|
||||||
|
fn obj_id() -> u32;
|
||||||
|
}
|
||||||
|
impl Sealed for Protected {
|
||||||
|
fn tag() -> u8 {
|
||||||
|
TAG_PROTECTED
|
||||||
|
}
|
||||||
|
fn obj_id() -> u32 {
|
||||||
|
OBJ_PRINTED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Sealed for Admin {
|
||||||
|
fn tag() -> u8 {
|
||||||
|
TAG_ADMIN
|
||||||
|
}
|
||||||
|
fn obj_id() -> u32 {
|
||||||
|
OBJ_ADMIN_DATA
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+93
-49
@@ -38,8 +38,9 @@ use zeroize::{Zeroize, Zeroizing};
|
|||||||
|
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
use crate::{
|
use crate::{
|
||||||
metadata, yubikey::YubiKey, CB_BUF_MAX, CB_OBJ_MAX, TAG_ADMIN, TAG_ADMIN_FLAGS_1,
|
metadata::{AdminData, ProtectedData},
|
||||||
TAG_ADMIN_SALT, TAG_PROTECTED, TAG_PROTECTED_MGM,
|
yubikey::YubiKey,
|
||||||
|
TAG_ADMIN_FLAGS_1, TAG_ADMIN_SALT, TAG_PROTECTED_MGM,
|
||||||
};
|
};
|
||||||
use des::{
|
use des::{
|
||||||
cipher::{generic_array::GenericArray, BlockCipher, NewBlockCipher},
|
cipher::{generic_array::GenericArray, BlockCipher, NewBlockCipher},
|
||||||
@@ -135,8 +136,8 @@ impl MgmKey {
|
|||||||
let txn = yubikey.begin_transaction()?;
|
let txn = yubikey.begin_transaction()?;
|
||||||
|
|
||||||
// recover management key
|
// recover management key
|
||||||
let data = metadata::read(&txn, TAG_ADMIN)?;
|
let admin_data = AdminData::read(&txn)?;
|
||||||
let salt = metadata::get_item(&data, TAG_ADMIN_SALT)?;
|
let salt = admin_data.get_item(TAG_ADMIN_SALT)?;
|
||||||
|
|
||||||
if salt.len() != CB_ADMIN_SALT {
|
if salt.len() != CB_ADMIN_SALT {
|
||||||
error!(
|
error!(
|
||||||
@@ -159,12 +160,12 @@ impl MgmKey {
|
|||||||
pub fn get_protected(yubikey: &mut YubiKey) -> Result<Self, Error> {
|
pub fn get_protected(yubikey: &mut YubiKey) -> Result<Self, Error> {
|
||||||
let txn = yubikey.begin_transaction()?;
|
let txn = yubikey.begin_transaction()?;
|
||||||
|
|
||||||
let data = metadata::read(&txn, TAG_PROTECTED).map_err(|e| {
|
let protected_data = ProtectedData::read(&txn).map_err(|e| {
|
||||||
error!("could not read protected data (err: {:?})", e);
|
error!("could not read protected data (err: {:?})", e);
|
||||||
e
|
e
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let item = metadata::get_item(&data, TAG_PROTECTED_MGM).map_err(|e| {
|
let item = protected_data.get_item(TAG_PROTECTED_MGM).map_err(|e| {
|
||||||
error!("could not read protected MGM from metadata (err: {:?})", e);
|
error!("could not read protected MGM from metadata (err: {:?})", e);
|
||||||
e
|
e
|
||||||
})?;
|
})?;
|
||||||
@@ -182,21 +183,84 @@ impl MgmKey {
|
|||||||
MgmKey::from_bytes(item)
|
MgmKey::from_bytes(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the management key (MGM)
|
/// Resets the management key for the given YubiKey to the default value.
|
||||||
|
///
|
||||||
|
/// This will wipe any metadata related to derived and PIN-protected management keys.
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
pub fn set(&self, yubikey: &mut YubiKey, touch: Option<u8>) -> Result<(), Error> {
|
pub fn set_default(yubikey: &mut YubiKey) -> Result<(), Error> {
|
||||||
let txn = yubikey.begin_transaction()?;
|
MgmKey::default().set_manual(yubikey, false)
|
||||||
txn.set_mgm_key(&self, touch)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set protected management key (MGM)
|
/// Configures the given YubiKey to use this management key.
|
||||||
|
///
|
||||||
|
/// The management key must be stored by the user, and provided when performing key
|
||||||
|
/// management operations.
|
||||||
|
///
|
||||||
|
/// This will wipe any metadata related to derived and PIN-protected management keys.
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
pub fn set_protected(&self, yubikey: &mut YubiKey) -> Result<(), Error> {
|
pub fn set_manual(&self, yubikey: &mut YubiKey, require_touch: bool) -> Result<(), Error> {
|
||||||
let mut data = Zeroizing::new(vec![0u8; CB_BUF_MAX]);
|
|
||||||
|
|
||||||
let txn = yubikey.begin_transaction()?;
|
let txn = yubikey.begin_transaction()?;
|
||||||
|
|
||||||
txn.set_mgm_key(self, None).map_err(|e| {
|
txn.set_mgm_key(&self, require_touch).map_err(|e| {
|
||||||
|
// Log a warning, since the device mgm key is corrupt or we're in a state
|
||||||
|
// where we can't set the mgm key.
|
||||||
|
error!("could not set new derived mgm key, err = {}", e);
|
||||||
|
e
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// After this point, we've set the mgm key, so the function should succeed,
|
||||||
|
// regardless of being able to set the metadata.
|
||||||
|
|
||||||
|
if let Ok(mut admin_data) = AdminData::read(&txn) {
|
||||||
|
// Clear the protected mgm key bit.
|
||||||
|
if let Ok(item) = admin_data.get_item(TAG_ADMIN_FLAGS_1) {
|
||||||
|
let mut flags_1 = [0u8; 1];
|
||||||
|
if item.len() == flags_1.len() {
|
||||||
|
flags_1.copy_from_slice(item);
|
||||||
|
flags_1[0] &= !ADMIN_FLAGS_1_PROTECTED_MGM;
|
||||||
|
|
||||||
|
if let Err(e) = admin_data.set_item(TAG_ADMIN_FLAGS_1, &flags_1) {
|
||||||
|
error!("could not set admin flags item, err = {}", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error!(
|
||||||
|
"admin data flags are an incorrect size: {} (expected {})",
|
||||||
|
item.len(),
|
||||||
|
flags_1.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove any existing salt for a derived mgm key.
|
||||||
|
if let Err(e) = admin_data.set_item(TAG_ADMIN_SALT, &[]) {
|
||||||
|
error!("could not unset derived mgm salt (err = {})", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = admin_data.write(&txn) {
|
||||||
|
error!("could not write admin data, err = {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear any prior mgm key from protected data.
|
||||||
|
if let Ok(mut protected_data) = ProtectedData::read(&txn) {
|
||||||
|
if let Err(e) = protected_data.set_item(TAG_PROTECTED_MGM, &[]) {
|
||||||
|
error!("could not clear protected mgm item, err = {:?}", e);
|
||||||
|
} else if let Err(e) = protected_data.write(&txn) {
|
||||||
|
error!("could not write protected data, err = {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configures the given YubiKey to use this as a PIN-protected management key.
|
||||||
|
///
|
||||||
|
/// 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> {
|
||||||
|
let txn = yubikey.begin_transaction()?;
|
||||||
|
|
||||||
|
txn.set_mgm_key(self, false).map_err(|e| {
|
||||||
// log a warning, since the device mgm key is corrupt or we're in
|
// log a warning, since the device mgm key is corrupt or we're in
|
||||||
// a state where we can't set the mgm key
|
// a state where we can't set the mgm key
|
||||||
error!("could not set new derived mgm key, err = {}", e);
|
error!("could not set new derived mgm key, err = {}", e);
|
||||||
@@ -206,39 +270,25 @@ impl MgmKey {
|
|||||||
// after this point, we've set the mgm key, so the function should
|
// after this point, we've set the mgm key, so the function should
|
||||||
// succeed, regardless of being able to set the metadata
|
// succeed, regardless of being able to set the metadata
|
||||||
|
|
||||||
// set the new mgm key in protected data
|
// Fetch the current protected data, or start a blank metadata blob.
|
||||||
let buffer = match metadata::read(&txn, TAG_PROTECTED) {
|
let mut protected_data = ProtectedData::read(&txn).unwrap_or_default();
|
||||||
Ok(b) => b,
|
|
||||||
Err(_) => {
|
|
||||||
// set current metadata blob size to zero, we'll add to the blank blob
|
|
||||||
Zeroizing::new(vec![])
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let mut cb_data = buffer.len();
|
|
||||||
data[..cb_data].copy_from_slice(&buffer);
|
|
||||||
|
|
||||||
if let Err(e) = metadata::set_item(
|
// Set the new mgm key in protected data.
|
||||||
data.as_mut_slice(),
|
if let Err(e) = protected_data.set_item(TAG_PROTECTED_MGM, self.as_ref()) {
|
||||||
&mut cb_data,
|
|
||||||
CB_OBJ_MAX,
|
|
||||||
TAG_PROTECTED_MGM,
|
|
||||||
self.as_ref(),
|
|
||||||
) {
|
|
||||||
error!("could not set protected mgm item, err = {:?}", e);
|
error!("could not set protected mgm item, err = {:?}", e);
|
||||||
} else {
|
} else {
|
||||||
metadata::write(&txn, TAG_PROTECTED, &data).map_err(|e| {
|
protected_data.write(&txn).map_err(|e| {
|
||||||
error!("could not write protected data, err = {:?}", e);
|
error!("could not write protected data, err = {:?}", e);
|
||||||
e
|
e
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the protected mgm flag in admin data
|
// set the protected mgm flag in admin data
|
||||||
cb_data = data.len();
|
|
||||||
|
|
||||||
let mut flags_1 = [0u8; 1];
|
let mut flags_1 = [0u8; 1];
|
||||||
|
|
||||||
if let Ok(buffer) = metadata::read(&txn, TAG_ADMIN) {
|
let mut admin_data = if let Ok(mut admin_data) = AdminData::read(&txn) {
|
||||||
if let Ok(item) = metadata::get_item(&buffer, TAG_ADMIN_FLAGS_1) {
|
if let Ok(item) = admin_data.get_item(TAG_ADMIN_FLAGS_1) {
|
||||||
if item.len() == flags_1.len() {
|
if item.len() == flags_1.len() {
|
||||||
flags_1.copy_from_slice(item);
|
flags_1.copy_from_slice(item);
|
||||||
} else {
|
} else {
|
||||||
@@ -254,26 +304,20 @@ impl MgmKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remove any existing salt
|
// remove any existing salt
|
||||||
if let Err(e) =
|
if let Err(e) = admin_data.set_item(TAG_ADMIN_SALT, &[]) {
|
||||||
metadata::set_item(&mut data, &mut cb_data, CB_OBJ_MAX, TAG_ADMIN_SALT, &[])
|
|
||||||
{
|
|
||||||
error!("could not unset derived mgm salt (err = {})", e)
|
error!("could not unset derived mgm salt (err = {})", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
admin_data
|
||||||
} else {
|
} else {
|
||||||
cb_data = 0;
|
AdminData::default()
|
||||||
}
|
};
|
||||||
|
|
||||||
flags_1[0] |= ADMIN_FLAGS_1_PROTECTED_MGM;
|
flags_1[0] |= ADMIN_FLAGS_1_PROTECTED_MGM;
|
||||||
|
|
||||||
if let Err(e) = metadata::set_item(
|
if let Err(e) = admin_data.set_item(TAG_ADMIN_FLAGS_1, &flags_1) {
|
||||||
data.as_mut_slice(),
|
|
||||||
&mut cb_data,
|
|
||||||
CB_OBJ_MAX,
|
|
||||||
TAG_ADMIN_FLAGS_1,
|
|
||||||
&flags_1,
|
|
||||||
) {
|
|
||||||
error!("could not set admin flags item, err = {}", e);
|
error!("could not set admin flags item, err = {}", e);
|
||||||
} else if let Err(e) = metadata::write(&txn, TAG_ADMIN, &data[..cb_data]) {
|
} else if let Err(e) = admin_data.write(&txn) {
|
||||||
error!("could not write admin data, err = {}", e);
|
error!("could not write admin data, err = {}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-8
@@ -229,14 +229,8 @@ impl<'tx> Transaction<'tx> {
|
|||||||
|
|
||||||
/// Set the management key (MGM).
|
/// Set the management key (MGM).
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
pub fn set_mgm_key(&self, new_key: &MgmKey, touch: Option<u8>) -> Result<(), Error> {
|
pub fn set_mgm_key(&self, new_key: &MgmKey, require_touch: bool) -> Result<(), Error> {
|
||||||
let p2 = match touch.unwrap_or_default() {
|
let p2 = if require_touch { 0xfe } else { 0xff };
|
||||||
0 => 0xff,
|
|
||||||
1 => 0xfe,
|
|
||||||
_ => {
|
|
||||||
return Err(Error::GenericError);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut data = [0u8; DES_LEN_3DES + 3];
|
let mut data = [0u8; DES_LEN_3DES + 3];
|
||||||
data[0] = ALGO_3DES;
|
data[0] = ALGO_3DES;
|
||||||
|
|||||||
+21
-33
@@ -50,8 +50,8 @@ use std::{
|
|||||||
|
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
use crate::{
|
use crate::{
|
||||||
apdu::StatusWords, metadata, transaction::ChangeRefAction, Buffer, ObjectId, CB_BUF_MAX,
|
apdu::StatusWords, metadata::AdminData, transaction::ChangeRefAction, Buffer, ObjectId,
|
||||||
CB_OBJ_MAX, MGMT_AID, TAG_ADMIN, TAG_ADMIN_FLAGS_1, TAG_ADMIN_TIMESTAMP,
|
MGMT_AID, TAG_ADMIN_FLAGS_1, TAG_ADMIN_TIMESTAMP,
|
||||||
};
|
};
|
||||||
use getrandom::getrandom;
|
use getrandom::getrandom;
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
@@ -417,12 +417,9 @@ impl YubiKey {
|
|||||||
/// Set PIN last changed
|
/// Set PIN last changed
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
pub fn set_pin_last_changed(yubikey: &mut YubiKey) -> Result<(), Error> {
|
pub fn set_pin_last_changed(yubikey: &mut YubiKey) -> Result<(), Error> {
|
||||||
let mut data = [0u8; CB_BUF_MAX];
|
|
||||||
let txn = yubikey.begin_transaction()?;
|
let txn = yubikey.begin_transaction()?;
|
||||||
|
|
||||||
let buffer = metadata::read(&txn, TAG_ADMIN)?;
|
let mut admin_data = AdminData::read(&txn)?;
|
||||||
let mut cb_data = buffer.len();
|
|
||||||
data[..cb_data].copy_from_slice(&buffer);
|
|
||||||
|
|
||||||
// TODO(tarcieri): double check this is little endian
|
// TODO(tarcieri): double check this is little endian
|
||||||
let tnow = SystemTime::now()
|
let tnow = SystemTime::now()
|
||||||
@@ -431,19 +428,14 @@ impl YubiKey {
|
|||||||
.as_secs()
|
.as_secs()
|
||||||
.to_le_bytes();
|
.to_le_bytes();
|
||||||
|
|
||||||
metadata::set_item(
|
admin_data
|
||||||
&mut data,
|
.set_item(TAG_ADMIN_TIMESTAMP, &tnow)
|
||||||
&mut cb_data,
|
.map_err(|e| {
|
||||||
CB_OBJ_MAX,
|
error!("could not set pin timestamp, err = {}", e);
|
||||||
TAG_ADMIN_TIMESTAMP,
|
e
|
||||||
&tnow,
|
})?;
|
||||||
)
|
|
||||||
.map_err(|e| {
|
|
||||||
error!("could not set pin timestamp, err = {}", e);
|
|
||||||
e
|
|
||||||
})?;
|
|
||||||
|
|
||||||
metadata::write(&txn, TAG_ADMIN, &data).map_err(|e| {
|
admin_data.write(&txn).map_err(|e| {
|
||||||
error!("could not write admin data, err = {}", e);
|
error!("could not write admin data, err = {}", e);
|
||||||
e
|
e
|
||||||
})?;
|
})?;
|
||||||
@@ -494,8 +486,10 @@ impl YubiKey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(data) = metadata::read(&txn, TAG_ADMIN) {
|
// Attempt to set the "PUK blocked" flag in admin data.
|
||||||
if let Ok(item) = metadata::get_item(&data, TAG_ADMIN_FLAGS_1) {
|
|
||||||
|
let mut admin_data = if let Ok(admin_data) = AdminData::read(&txn) {
|
||||||
|
if let Ok(item) = admin_data.get_item(TAG_ADMIN_FLAGS_1) {
|
||||||
if item.len() == flags.len() {
|
if item.len() == flags.len() {
|
||||||
flags.copy_from_slice(item)
|
flags.copy_from_slice(item)
|
||||||
} else {
|
} else {
|
||||||
@@ -506,22 +500,16 @@ impl YubiKey {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
admin_data
|
||||||
|
} else {
|
||||||
|
AdminData::default()
|
||||||
|
};
|
||||||
|
|
||||||
flags[0] |= ADMIN_FLAGS_1_PUK_BLOCKED;
|
flags[0] |= ADMIN_FLAGS_1_PUK_BLOCKED;
|
||||||
let mut data = [0u8; CB_BUF_MAX];
|
|
||||||
let mut cb_data: usize = data.len();
|
|
||||||
|
|
||||||
if metadata::set_item(
|
if admin_data.set_item(TAG_ADMIN_FLAGS_1, &flags).is_ok() {
|
||||||
&mut data,
|
if admin_data.write(&txn).is_err() {
|
||||||
&mut cb_data,
|
|
||||||
CB_OBJ_MAX,
|
|
||||||
TAG_ADMIN_FLAGS_1,
|
|
||||||
&flags,
|
|
||||||
)
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
if metadata::write(&txn, TAG_ADMIN, &data[..cb_data]).is_err() {
|
|
||||||
error!("could not write admin metadata");
|
error!("could not write admin metadata");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+43
-1
@@ -10,6 +10,7 @@ use rsa::{hash::Hash::SHA2_256, PaddingScheme, PublicKey};
|
|||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::{env, sync::Mutex};
|
use std::{env, sync::Mutex};
|
||||||
|
use x509::RelativeDistinguishedName;
|
||||||
use yubikey_piv::{
|
use yubikey_piv::{
|
||||||
certificate::{Certificate, PublicKeyInfo},
|
certificate::{Certificate, PublicKeyInfo},
|
||||||
key::{self, AlgorithmId, Key, RetiredSlotId, SlotId},
|
key::{self, AlgorithmId, Key, RetiredSlotId, SlotId},
|
||||||
@@ -106,6 +107,45 @@ fn test_verify_pin() {
|
|||||||
assert!(yubikey.verify_pin(b"123456").is_ok());
|
assert!(yubikey.verify_pin(b"123456").is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Management key support
|
||||||
|
//
|
||||||
|
|
||||||
|
#[cfg(feature = "untested")]
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn test_set_mgmkey() {
|
||||||
|
let mut yubikey = YUBIKEY.lock().unwrap();
|
||||||
|
|
||||||
|
assert!(yubikey.verify_pin(b"123456").is_ok());
|
||||||
|
assert!(MgmKey::get_protected(&mut yubikey).is_err());
|
||||||
|
assert!(yubikey.authenticate(MgmKey::default()).is_ok());
|
||||||
|
|
||||||
|
// Set a protected management key.
|
||||||
|
assert!(MgmKey::generate()
|
||||||
|
.unwrap()
|
||||||
|
.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();
|
||||||
|
assert!(manual.set_manual(&mut yubikey, false).is_ok());
|
||||||
|
assert!(MgmKey::get_protected(&mut yubikey).is_err());
|
||||||
|
assert!(yubikey.authenticate(MgmKey::default()).is_err());
|
||||||
|
assert!(yubikey.authenticate(protected.clone()).is_err());
|
||||||
|
assert!(yubikey.authenticate(manual.clone()).is_ok());
|
||||||
|
|
||||||
|
// Set back to the default management key.
|
||||||
|
assert!(MgmKey::set_default(&mut yubikey).is_ok());
|
||||||
|
assert!(MgmKey::get_protected(&mut yubikey).is_err());
|
||||||
|
assert!(yubikey.authenticate(protected).is_err());
|
||||||
|
assert!(yubikey.authenticate(manual).is_err());
|
||||||
|
assert!(yubikey.authenticate(MgmKey::default()).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Certificate support
|
// Certificate support
|
||||||
//
|
//
|
||||||
@@ -132,13 +172,15 @@ fn generate_self_signed_cert(algorithm: AlgorithmId) -> Certificate {
|
|||||||
getrandom(&mut serial).unwrap();
|
getrandom(&mut serial).unwrap();
|
||||||
|
|
||||||
// Generate a self-signed certificate for the new key.
|
// Generate a self-signed certificate for the new key.
|
||||||
|
let extensions: &[x509::Extension<'_, &[u64]>] = &[];
|
||||||
let cert_result = Certificate::generate_self_signed(
|
let cert_result = Certificate::generate_self_signed(
|
||||||
&mut yubikey,
|
&mut yubikey,
|
||||||
slot,
|
slot,
|
||||||
serial,
|
serial,
|
||||||
None,
|
None,
|
||||||
"testSubject".to_owned(),
|
&[RelativeDistinguishedName::common_name("testSubject")],
|
||||||
generated,
|
generated,
|
||||||
|
extensions,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(cert_result.is_ok());
|
assert!(cert_result.is_ok());
|
||||||
|
|||||||
Reference in New Issue
Block a user