Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ca1cd587ff | |||
| 0699991f2b | |||
| 76143524ef | |||
| fec9dcc994 | |||
| 8aeb9f3da7 | |||
| 34011088a0 | |||
| 24e6e6ffa6 | |||
| a44859d6b2 | |||
| 4921cbf6ed | |||
| d6729e99ba | |||
| 6452fa0540 | |||
| 71eb71d9c6 | |||
| f0d82245ea | |||
| c7e6fdf58a | |||
| 4a44fb9025 | |||
| c4256dddba | |||
| 5159577135 | |||
| e0b944a524 | |||
| eadebfa2f6 | |||
| 137df751fe | |||
| a24de089e0 | |||
| 982b9d2f95 | |||
| 0ebbc31890 | |||
| d373d531e5 | |||
| 72da2b46c7 | |||
| d31c60f733 | |||
| d7d47e861d | |||
| 9bb39a7b95 | |||
| 7c512d283c | |||
| 351d40e0c7 | |||
| acc783d0d3 | |||
| 379adcb909 |
+65
-17
@@ -1,10 +1,13 @@
|
|||||||
name: CI checks
|
name: CI checks
|
||||||
|
|
||||||
on: [push, pull_request]
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches: main
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test-msrv:
|
||||||
name: Test on ${{ matrix.name }}
|
name: Test MSRV on ${{ matrix.name }}
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
@@ -22,37 +25,83 @@ jobs:
|
|||||||
os: macos-latest
|
os: macos-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install build dependencies
|
- name: Install build dependencies
|
||||||
run: sudo apt install ${{ matrix.build_deps }}
|
run: sudo apt install ${{ matrix.build_deps }}
|
||||||
if: matrix.build_deps != ''
|
if: matrix.build_deps != ''
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
id: stable-toolchain
|
||||||
|
- name: Install test dependencies using latest stable Rust
|
||||||
|
run: cargo +${{steps.stable-toolchain.outputs.name}} install rage
|
||||||
|
- name: Run tests
|
||||||
|
run: cargo test
|
||||||
|
- name: Verify working directory is clean
|
||||||
|
run: git diff --exit-code
|
||||||
|
|
||||||
|
test-latest:
|
||||||
|
name: Test latest stable on ${{ matrix.name }}
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
name: [linux, windows, macos]
|
||||||
|
include:
|
||||||
|
- name: linux
|
||||||
|
os: ubuntu-latest
|
||||||
|
build_deps: >
|
||||||
|
libpcsclite-dev
|
||||||
|
|
||||||
|
- name: windows
|
||||||
|
os: windows-latest
|
||||||
|
|
||||||
|
- name: macos
|
||||||
|
os: macos-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install build dependencies
|
||||||
|
run: sudo apt install ${{ matrix.build_deps }}
|
||||||
|
if: matrix.build_deps != ''
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
id: toolchain
|
||||||
|
- run: rustup override set ${{steps.toolchain.outputs.name}}
|
||||||
- name: Install test dependencies
|
- name: Install test dependencies
|
||||||
run: cargo install rage
|
run: cargo install rage
|
||||||
- run: cargo fetch
|
- name: Remove lockfile to build with latest dependencies
|
||||||
- name: Build tests
|
run: rm Cargo.lock
|
||||||
run: cargo build --verbose --tests
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: cargo test --verbose
|
run: cargo test
|
||||||
|
- name: Verify working directory is clean (excluding lockfile)
|
||||||
|
run: git diff --exit-code ':!Cargo.lock'
|
||||||
|
|
||||||
codecov:
|
codecov:
|
||||||
name: Code coverage
|
name: Code coverage
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: xd009642/tarpaulin:develop-nightly
|
||||||
|
options: --security-opt seccomp=unconfined
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install build dependencies
|
- name: Install build dependencies
|
||||||
run: sudo apt install libpcsclite-dev
|
run: apt update && apt install -y libpcsclite-dev
|
||||||
- name: Install coverage dependencies
|
|
||||||
run: cargo install cargo-tarpaulin
|
|
||||||
- name: Generate coverage report
|
- name: Generate coverage report
|
||||||
run: cargo tarpaulin --engine llvm --all-features --release --timeout 600 --out Xml
|
run: >
|
||||||
|
cargo tarpaulin
|
||||||
|
--engine llvm
|
||||||
|
--timeout 180
|
||||||
|
--out xml
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
uses: codecov/codecov-action@v3.1.1
|
uses: codecov/codecov-action@v4.5.0
|
||||||
|
with:
|
||||||
|
fail_ci_if_error: true
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
||||||
doc-links:
|
doc-links:
|
||||||
name: Intra-doc links
|
name: Intra-doc links
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install build dependencies
|
- name: Install build dependencies
|
||||||
run: sudo apt install libpcsclite-dev
|
run: sudo apt install libpcsclite-dev
|
||||||
- run: cargo fetch
|
- run: cargo fetch
|
||||||
@@ -62,9 +111,8 @@ jobs:
|
|||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
name: Rustfmt
|
name: Rustfmt
|
||||||
timeout-minutes: 30
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Check formatting
|
- name: Check formatting
|
||||||
run: cargo fmt -- --check
|
run: cargo fmt -- --check
|
||||||
|
|||||||
@@ -6,10 +6,9 @@ on: pull_request
|
|||||||
jobs:
|
jobs:
|
||||||
clippy-beta:
|
clippy-beta:
|
||||||
name: Clippy (beta)
|
name: Clippy (beta)
|
||||||
timeout-minutes: 30
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: dtolnay/rust-toolchain@beta
|
- uses: dtolnay/rust-toolchain@beta
|
||||||
id: toolchain
|
id: toolchain
|
||||||
- run: rustup override set ${{steps.toolchain.outputs.name}}
|
- run: rustup override set ${{steps.toolchain.outputs.name}}
|
||||||
|
|||||||
@@ -6,10 +6,9 @@ on: pull_request
|
|||||||
jobs:
|
jobs:
|
||||||
clippy:
|
clippy:
|
||||||
name: Clippy (MSRV)
|
name: Clippy (MSRV)
|
||||||
timeout-minutes: 30
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install build dependencies
|
- name: Install build dependencies
|
||||||
run: sudo apt install libpcsclite-dev
|
run: sudo apt install libpcsclite-dev
|
||||||
- name: Run clippy
|
- name: Run clippy
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ jobs:
|
|||||||
- windows
|
- windows
|
||||||
include:
|
include:
|
||||||
- name: linux
|
- name: linux
|
||||||
os: ubuntu-18.04
|
os: ubuntu-20.04
|
||||||
build_deps: >
|
build_deps: >
|
||||||
libpcsclite-dev
|
libpcsclite-dev
|
||||||
archive_name: age-plugin-yubikey.tar.gz
|
archive_name: age-plugin-yubikey.tar.gz
|
||||||
@@ -48,11 +48,10 @@ jobs:
|
|||||||
asset_suffix: x86_64-darwin.tar.gz
|
asset_suffix: x86_64-darwin.tar.gz
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
id: toolchain
|
||||||
toolchain: stable
|
- run: rustup override set ${{steps.toolchain.outputs.name}}
|
||||||
override: true
|
|
||||||
- name: Add target
|
- name: Add target
|
||||||
run: rustup target add ${{ matrix.target }}
|
run: rustup target add ${{ matrix.target }}
|
||||||
if: matrix.target != ''
|
if: matrix.target != ''
|
||||||
@@ -87,7 +86,7 @@ jobs:
|
|||||||
if: matrix.name == 'windows'
|
if: matrix.name == 'windows'
|
||||||
|
|
||||||
- name: Upload archive to release
|
- name: Upload archive to release
|
||||||
uses: svenstaro/upload-release-action@2.5.0
|
uses: svenstaro/upload-release-action@2.6.1
|
||||||
with:
|
with:
|
||||||
file: ${{ matrix.archive_name }}
|
file: ${{ matrix.archive_name }}
|
||||||
asset_name: age-plugin-yubikey-$tag-${{ matrix.asset_suffix }}
|
asset_name: age-plugin-yubikey-$tag-${{ matrix.asset_suffix }}
|
||||||
@@ -96,7 +95,7 @@ jobs:
|
|||||||
|
|
||||||
deb:
|
deb:
|
||||||
name: Debian ${{ matrix.name }}
|
name: Debian ${{ matrix.name }}
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-20.04
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
name: [linux]
|
name: [linux]
|
||||||
@@ -107,11 +106,10 @@ jobs:
|
|||||||
libpcsclite-dev
|
libpcsclite-dev
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
id: toolchain
|
||||||
toolchain: stable
|
- run: rustup override set ${{steps.toolchain.outputs.name}}
|
||||||
override: true
|
|
||||||
- name: Add target
|
- name: Add target
|
||||||
run: rustup target add ${{ matrix.target }}
|
run: rustup target add ${{ matrix.target }}
|
||||||
- name: cargo install cargo-deb
|
- name: cargo install cargo-deb
|
||||||
@@ -146,7 +144,7 @@ jobs:
|
|||||||
args: --package age-plugin-yubikey --no-build --target ${{ matrix.target }}
|
args: --package age-plugin-yubikey --no-build --target ${{ matrix.target }}
|
||||||
|
|
||||||
- name: Upload Debian package to release
|
- name: Upload Debian package to release
|
||||||
uses: svenstaro/upload-release-action@2.5.0
|
uses: svenstaro/upload-release-action@2.6.1
|
||||||
with:
|
with:
|
||||||
file: target/${{ matrix.target }}/debian/*.deb
|
file: target/${{ matrix.target }}/debian/*.deb
|
||||||
file_glob: true
|
file_glob: true
|
||||||
|
|||||||
+5
-4
@@ -8,11 +8,12 @@ to 0.3.0 are beta releases.
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
## [0.3.4], [0.4.1] - 2026-04-08
|
## [0.5.0] - 2024-08-04
|
||||||
### Fixed
|
### Fixed
|
||||||
- `age-plugin-yubikey` now completely ignores any identity that has unrecognised
|
- `age-plugin-yubikey` can now be compiled with Rust 1.80 and above.
|
||||||
critical extensions in its certificate, to ensure it doesn't misuse a newer
|
|
||||||
identity type.
|
### Changed
|
||||||
|
- MSRV is now 1.67.0.
|
||||||
|
|
||||||
## [0.4.0] - 2023-04-09
|
## [0.4.0] - 2023-04-09
|
||||||
### Changed
|
### Changed
|
||||||
|
|||||||
Generated
+772
-670
File diff suppressed because it is too large
Load Diff
+12
-12
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "age-plugin-yubikey"
|
name = "age-plugin-yubikey"
|
||||||
description = "YubiKey plugin for age clients"
|
description = "YubiKey plugin for age clients"
|
||||||
version = "0.4.1"
|
version = "0.5.0"
|
||||||
authors = ["Jack Grigg <thestr4d@gmail.com>"]
|
authors = ["Jack Grigg <thestr4d@gmail.com>"]
|
||||||
repository = "https://github.com/str4d/age-plugin-yubikey"
|
repository = "https://github.com/str4d/age-plugin-yubikey"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@@ -9,7 +9,7 @@ keywords = ["age", "cli", "encryption", "yubikey"]
|
|||||||
categories = ["command-line-utilities", "cryptography"]
|
categories = ["command-line-utilities", "cryptography"]
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.65" # MSRV
|
rust-version = "1.67" # MSRV
|
||||||
|
|
||||||
[package.metadata.deb]
|
[package.metadata.deb]
|
||||||
extended-description = """\
|
extended-description = """\
|
||||||
@@ -22,12 +22,12 @@ assets = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
age-core = "0.9"
|
age-core = "0.10"
|
||||||
age-plugin = "0.4"
|
age-plugin = "0.5"
|
||||||
base64 = "0.21"
|
base64 = "0.21"
|
||||||
bech32 = "0.9"
|
bech32 = "0.9"
|
||||||
console = { version = "0.15", default-features = false }
|
console = { version = "0.15", default-features = false }
|
||||||
dialoguer = { version = "0.10", default-features = false, features = ["password"] }
|
dialoguer = { version = "0.11", default-features = false, features = ["password"] }
|
||||||
env_logger = "0.10"
|
env_logger = "0.10"
|
||||||
gumdrop = "0.8"
|
gumdrop = "0.8"
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
@@ -36,23 +36,23 @@ p256 = { version = "0.13", features = ["ecdh"] }
|
|||||||
pcsc = "2.4"
|
pcsc = "2.4"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
sha2 = "0.10"
|
sha2 = "0.10"
|
||||||
which = "4.1"
|
which = "5"
|
||||||
x509 = "0.2"
|
x509 = "0.2"
|
||||||
x509-parser = "0.14"
|
x509-parser = "0.14"
|
||||||
yubikey = { version = "=0.8.0-pre.0", features = ["untested"] }
|
yubikey = { version = "=0.8.0-pre.0", features = ["untested"] }
|
||||||
|
|
||||||
# Translations
|
# Translations
|
||||||
i18n-embed = { version = "0.13", features = ["desktop-requester", "fluent-system"] }
|
i18n-embed = { version = "0.14", features = ["desktop-requester", "fluent-system"] }
|
||||||
i18n-embed-fl = "0.6"
|
i18n-embed-fl = "0.8"
|
||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
rust-embed = "6"
|
rust-embed = "8"
|
||||||
|
|
||||||
# GnuPG coexistence
|
# GnuPG coexistence
|
||||||
sysinfo = "0.28"
|
sysinfo = "0.29"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
flate2 = "1"
|
flate2 = "1"
|
||||||
man = "0.3"
|
man = "0.3"
|
||||||
tempfile = "3"
|
tempfile = "3"
|
||||||
test-with = "0.9"
|
test-with = "0.11"
|
||||||
which = "4"
|
which = "5"
|
||||||
|
|||||||
@@ -8,8 +8,13 @@ which enables files to be encrypted to age identities stored on YubiKeys.
|
|||||||
|
|
||||||
| Environment | CLI command |
|
| Environment | CLI command |
|
||||||
|-------------|-------------|
|
|-------------|-------------|
|
||||||
| Cargo (Rust 1.65+) | `cargo install age-plugin-yubikey` |
|
| Cargo (Rust 1.67+) | `cargo install age-plugin-yubikey` |
|
||||||
| Homebrew (macOS or Linux) | `brew install age-plugin-yubikey` |
|
| Homebrew (macOS or Linux) | `brew install age-plugin-yubikey` |
|
||||||
|
| Arch Linux | `pacman -S age-plugin-yubikey` |
|
||||||
|
| Debian | [Debian package](https://github.com/str4d/age-plugin-yubikey/releases) |
|
||||||
|
| NixOS | Add to config:<br>`environment.systemPackages = [`<br>` pkgs.age-plugin-yubikey`<br>`];`<br>Or run `nix-env -i age-plugin-yubikey` |
|
||||||
|
| Ubuntu 20.04+ | [Debian package](https://github.com/str4d/age-plugin-yubikey/releases) |
|
||||||
|
| OpenBSD | `pkg_add age-plugin-yubikey` (security/age-plugin-yubikey) |
|
||||||
|
|
||||||
On Windows, Linux, and macOS, you can use the
|
On Windows, Linux, and macOS, you can use the
|
||||||
[pre-built binaries](https://github.com/str4d/age-plugin-yubikey/releases).
|
[pre-built binaries](https://github.com/str4d/age-plugin-yubikey/releases).
|
||||||
@@ -24,8 +29,10 @@ is installed and running.
|
|||||||
| Environment | CLI command |
|
| Environment | CLI command |
|
||||||
|-------------|-------------|
|
|-------------|-------------|
|
||||||
| Debian or Ubuntu | `sudo apt-get install pcscd` |
|
| Debian or Ubuntu | `sudo apt-get install pcscd` |
|
||||||
|
| Fedora | `sudo dnf install pcsc-lite` |
|
||||||
| OpenBSD | As ```root``` do:<br>`pkg_add pcsc-lite ccid`<br>`rcctl enable pcscd`<br>`rcctl start pcscd` |
|
| OpenBSD | As ```root``` do:<br>`pkg_add pcsc-lite ccid`<br>`rcctl enable pcscd`<br>`rcctl start pcscd` |
|
||||||
| FreeBSD | As ```root``` do:<br>`pkg install pcsc-lite libccid`<br>`service pcscd enable`<br>`service pcscd start` |
|
| FreeBSD | As ```root``` do:<br>`pkg install pcsc-lite libccid`<br>`service pcscd enable`<br>`service pcscd start` |
|
||||||
|
| Arch | `sudo pacman -S pcsclite pcsc-tools yubikey-manager`<br>`sudo systemctl enable pcscd`<br>`sudo systemctl start pcscd`|
|
||||||
|
|
||||||
When installing via Cargo, you also need to ensure that the development headers
|
When installing via Cargo, you also need to ensure that the development headers
|
||||||
for the `pcsc-lite` library are available, so that the `pcsc-sys` crate can be
|
for the `pcsc-lite` library are available, so that the `pcsc-sys` crate can be
|
||||||
@@ -34,6 +41,7 @@ compiled.
|
|||||||
| Environment | CLI command |
|
| Environment | CLI command |
|
||||||
|-------------|-------------|
|
|-------------|-------------|
|
||||||
| Debian or Ubuntu | `sudo apt-get install libpcsclite-dev` |
|
| Debian or Ubuntu | `sudo apt-get install libpcsclite-dev` |
|
||||||
|
| Fedora | `sudo dnf install pcsc-lite-devel` |
|
||||||
|
|
||||||
### Windows Subsystem for Linux (WSL)
|
### Windows Subsystem for Linux (WSL)
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use std::io::prelude::*;
|
|||||||
const MANPAGES_DIR: &str = "./target/manpages";
|
const MANPAGES_DIR: &str = "./target/manpages";
|
||||||
|
|
||||||
fn generate_manpage(page: String, name: &str) {
|
fn generate_manpage(page: String, name: &str) {
|
||||||
let file = File::create(format!("{}/{}.1.gz", MANPAGES_DIR, name))
|
let file = File::create(format!("{MANPAGES_DIR}/{name}.1.gz"))
|
||||||
.expect("Should be able to open file in target directory");
|
.expect("Should be able to open file in target directory");
|
||||||
let mut encoder = GzEncoder::new(file, Compression::best());
|
let mut encoder = GzEncoder::new(file, Compression::best());
|
||||||
encoder
|
encoder
|
||||||
|
|||||||
@@ -204,6 +204,7 @@ err-invalid-flag-tui = Flag '{$flag}' cannot be used with the interactive in
|
|||||||
err-invalid-pin-policy = Invalid PIN policy '{$policy}' (expected [{$expected}]).
|
err-invalid-pin-policy = Invalid PIN policy '{$policy}' (expected [{$expected}]).
|
||||||
err-invalid-slot = Invalid slot '{$slot}' (expected number between 1 and 20).
|
err-invalid-slot = Invalid slot '{$slot}' (expected number between 1 and 20).
|
||||||
err-invalid-touch-policy = Invalid touch policy '{$policy}' (expected [{$expected}]).
|
err-invalid-touch-policy = Invalid touch policy '{$policy}' (expected [{$expected}]).
|
||||||
|
err-io-user = Failed to get input from user: {$err}
|
||||||
err-io = Failed to set up {-yubikey}: {$err}
|
err-io = Failed to set up {-yubikey}: {$err}
|
||||||
err-multiple-commands = Only one of {-cmd-generate}, {-cmd-identity}, {-cmd-list}, {-cmd-list-all} can be specified.
|
err-multiple-commands = Only one of {-cmd-generate}, {-cmd-identity}, {-cmd-list}, {-cmd-list-all} can be specified.
|
||||||
err-multiple-yubikeys = Multiple {-yubikeys} are plugged in. Use {-flag-serial} to select a single {-yubikey}.
|
err-multiple-yubikeys = Multiple {-yubikeys} are plugged in. Use {-flag-serial} to select a single {-yubikey}.
|
||||||
|
|||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "1.65.0"
|
channel = "1.67.0"
|
||||||
components = ["clippy", "rustfmt"]
|
components = ["clippy", "rustfmt"]
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ macro_rules! wlnfl {
|
|||||||
|
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
CustomManagementKey,
|
CustomManagementKey,
|
||||||
|
Dialog(dialoguer::Error),
|
||||||
InvalidFlagCommand(String, String),
|
InvalidFlagCommand(String, String),
|
||||||
InvalidFlagTui(String),
|
InvalidFlagTui(String),
|
||||||
InvalidPinPolicy(String),
|
InvalidPinPolicy(String),
|
||||||
@@ -35,6 +36,12 @@ pub enum Error {
|
|||||||
YubiKey(yubikey::Error),
|
YubiKey(yubikey::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<dialoguer::Error> for Error {
|
||||||
|
fn from(e: dialoguer::Error) -> Self {
|
||||||
|
Error::Dialog(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<io::Error> for Error {
|
impl From<io::Error> for Error {
|
||||||
fn from(e: io::Error) -> Self {
|
fn from(e: io::Error) -> Self {
|
||||||
Error::Io(e)
|
Error::Io(e)
|
||||||
@@ -65,6 +72,7 @@ impl fmt::Debug for Error {
|
|||||||
url = CHANGE_MGMT_KEY_URL
|
url = CHANGE_MGMT_KEY_URL
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
Error::Dialog(e) => wlnfl!(f, "err-io-user", err = e.to_string())?,
|
||||||
Error::InvalidFlagCommand(flag, command) => wlnfl!(
|
Error::InvalidFlagCommand(flag, command) => wlnfl!(
|
||||||
f,
|
f,
|
||||||
"err-invalid-flag-command",
|
"err-invalid-flag-command",
|
||||||
|
|||||||
+10
-37
@@ -15,7 +15,6 @@ use std::io;
|
|||||||
use std::iter;
|
use std::iter;
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::time::{Duration, Instant, SystemTime};
|
use std::time::{Duration, Instant, SystemTime};
|
||||||
use x509_parser::der_parser::oid::Oid;
|
|
||||||
use yubikey::{
|
use yubikey::{
|
||||||
certificate::Certificate,
|
certificate::Certificate,
|
||||||
piv::{decrypt_data, AlgorithmId, RetiredSlotId, SlotId},
|
piv::{decrypt_data, AlgorithmId, RetiredSlotId, SlotId},
|
||||||
@@ -28,16 +27,13 @@ use crate::{
|
|||||||
fl,
|
fl,
|
||||||
format::{RecipientLine, STANZA_KEY_LABEL},
|
format::{RecipientLine, STANZA_KEY_LABEL},
|
||||||
p256::{Recipient, TAG_BYTES},
|
p256::{Recipient, TAG_BYTES},
|
||||||
util::{otp_serial_prefix, Metadata, POLICY_EXTENSION_OID},
|
util::{otp_serial_prefix, Metadata},
|
||||||
IDENTITY_PREFIX,
|
IDENTITY_PREFIX,
|
||||||
};
|
};
|
||||||
|
|
||||||
const ONE_SECOND: Duration = Duration::from_secs(1);
|
const ONE_SECOND: Duration = Duration::from_secs(1);
|
||||||
const FIFTEEN_SECONDS: Duration = Duration::from_secs(15);
|
const FIFTEEN_SECONDS: Duration = Duration::from_secs(15);
|
||||||
|
|
||||||
/// The set of OIDs that we understand and use when parsing YubiKey slot certificates.
|
|
||||||
const KNOWN_OIDS: &[&[u64]] = &[POLICY_EXTENSION_OID];
|
|
||||||
|
|
||||||
pub(crate) fn is_connected(reader: Reader) -> bool {
|
pub(crate) fn is_connected(reader: Reader) -> bool {
|
||||||
filter_connected(&reader)
|
filter_connected(&reader)
|
||||||
}
|
}
|
||||||
@@ -278,10 +274,10 @@ pub(crate) fn disconnect_without_reset(yubikey: YubiKey) {
|
|||||||
let _ = yubikey.disconnect(pcsc::Disposition::LeaveCard);
|
let _ = yubikey.disconnect(pcsc::Disposition::LeaveCard);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn request_pin<E>(
|
fn request_pin<E, E2>(
|
||||||
mut prompt: impl FnMut(Option<String>) -> io::Result<Result<SecretString, E>>,
|
mut prompt: impl FnMut(Option<String>) -> Result<Result<SecretString, E>, E2>,
|
||||||
serial: Serial,
|
serial: Serial,
|
||||||
) -> io::Result<Result<SecretString, E>> {
|
) -> Result<Result<SecretString, E>, E2> {
|
||||||
let mut prev_error = None;
|
let mut prev_error = None;
|
||||||
loop {
|
loop {
|
||||||
prev_error = Some(match prompt(prev_error)? {
|
prev_error = Some(match prompt(prev_error)? {
|
||||||
@@ -330,7 +326,7 @@ pub(crate) fn manage(yubikey: &mut YubiKey) -> Result<(), Error> {
|
|||||||
let pin = request_pin(
|
let pin = request_pin(
|
||||||
|prev_error| {
|
|prev_error| {
|
||||||
if let Some(err) = prev_error {
|
if let Some(err) = prev_error {
|
||||||
eprintln!("{}", err);
|
eprintln!("{err}");
|
||||||
}
|
}
|
||||||
Password::new()
|
Password::new()
|
||||||
.with_prompt(fl!("mgr-choose-new-pin"))
|
.with_prompt(fl!("mgr-choose-new-pin"))
|
||||||
@@ -392,30 +388,6 @@ pub(crate) fn manage(yubikey: &mut YubiKey) -> Result<(), Error> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses the certificate to identify the preferred recipient type it corresponds to.
|
|
||||||
pub(crate) fn identify_recipient(cert: &Certificate) -> Option<Recipient> {
|
|
||||||
let known_oids = KNOWN_OIDS
|
|
||||||
.iter()
|
|
||||||
.map(|oid| Oid::from(oid).unwrap())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
// If the certificate contains any unrecognised critical extensions, reject it: we
|
|
||||||
// don't know how to correctly use the identity. In particular, some identities store
|
|
||||||
// parts of their private key material in certificate extensions to work around
|
|
||||||
// hardware limitations. Not understanding these extensions could lead to encrypting
|
|
||||||
// with the wrong protocol and violating security assumptions.
|
|
||||||
let (_, c) = x509_parser::parse_x509_certificate(cert.as_ref()).ok()?;
|
|
||||||
if c.tbs_certificate
|
|
||||||
.extensions()
|
|
||||||
.iter()
|
|
||||||
.any(|ext| ext.critical && !known_oids.contains(&ext.oid))
|
|
||||||
{
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Recipient::from_certificate(cert)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an iterator of keys that are occupying plugin-compatible slots, along with the
|
/// Returns an iterator of keys that are occupying plugin-compatible slots, along with the
|
||||||
/// corresponding recipient if the key is compatible with this plugin.
|
/// corresponding recipient if the key is compatible with this plugin.
|
||||||
pub(crate) fn list_slots(
|
pub(crate) fn list_slots(
|
||||||
@@ -425,7 +397,8 @@ pub(crate) fn list_slots(
|
|||||||
// We only use the retired slots.
|
// We only use the retired slots.
|
||||||
match key.slot() {
|
match key.slot() {
|
||||||
SlotId::Retired(slot) => {
|
SlotId::Retired(slot) => {
|
||||||
let recipient = identify_recipient(key.certificate());
|
// Only P-256 keys are compatible with us.
|
||||||
|
let recipient = Recipient::from_certificate(key.certificate());
|
||||||
Some((key, slot, recipient))
|
Some((key, slot, recipient))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
@@ -632,9 +605,9 @@ impl Stub {
|
|||||||
let (cert, pk) = match Certificate::read(&mut yubikey, SlotId::Retired(self.slot))
|
let (cert, pk) = match Certificate::read(&mut yubikey, SlotId::Retired(self.slot))
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|cert| {
|
.and_then(|cert| {
|
||||||
identify_recipient(&cert)
|
Recipient::from_certificate(&cert)
|
||||||
.filter(|recipient| recipient.tag() == self.tag)
|
.filter(|pk| pk.tag() == self.tag)
|
||||||
.map(|r| (cert, r))
|
.map(|pk| (cert, pk))
|
||||||
}) {
|
}) {
|
||||||
Some(pk) => pk,
|
Some(pk) => pk,
|
||||||
None => {
|
None => {
|
||||||
|
|||||||
+8
-14
@@ -296,8 +296,8 @@ fn list(flags: PluginFlags, all: bool) -> Result<(), Error> {
|
|||||||
flags,
|
flags,
|
||||||
all,
|
all,
|
||||||
|_, recipient, metadata| {
|
|_, recipient, metadata| {
|
||||||
println!("{}", metadata);
|
println!("{metadata}");
|
||||||
println!("{}", recipient);
|
println!("{recipient}");
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -329,8 +329,8 @@ fn main() -> Result<(), Error> {
|
|||||||
if let Some(state_machine) = opts.age_plugin {
|
if let Some(state_machine) = opts.age_plugin {
|
||||||
run_state_machine(
|
run_state_machine(
|
||||||
&state_machine,
|
&state_machine,
|
||||||
plugin::RecipientPlugin::default,
|
Some(plugin::RecipientPlugin::default),
|
||||||
plugin::IdentityPlugin::default,
|
Some(plugin::IdentityPlugin::default),
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
} else if opts.version {
|
} else if opts.version {
|
||||||
@@ -411,9 +411,9 @@ fn main() -> Result<(), Error> {
|
|||||||
.validity()
|
.validity()
|
||||||
.not_before
|
.not_before
|
||||||
.to_rfc2822()
|
.to_rfc2822()
|
||||||
.unwrap_or_else(|e| format!("Invalid date: {}", e));
|
.unwrap_or_else(|e| format!("Invalid date: {e}"));
|
||||||
|
|
||||||
format!("{}, created: {}", name, created)
|
format!("{name}, created: {created}")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -631,14 +631,8 @@ fn main() -> Result<(), Error> {
|
|||||||
// If `rage` binary is installed, use it in examples. Otherwise default to `age`.
|
// If `rage` binary is installed, use it in examples. Otherwise default to `age`.
|
||||||
let age_binary = which::which("rage").map(|_| "rage").unwrap_or("age");
|
let age_binary = which::which("rage").map(|_| "rage").unwrap_or("age");
|
||||||
|
|
||||||
let encrypt_usage = format!(
|
let encrypt_usage = format!("$ cat foo.txt | {age_binary} -r {recipient} -o foo.txt.age");
|
||||||
"$ cat foo.txt | {} -r {} -o foo.txt.age",
|
let decrypt_usage = format!("$ cat foo.txt.age | {age_binary} -d -i {file_name} > foo.txt");
|
||||||
age_binary, recipient
|
|
||||||
);
|
|
||||||
let decrypt_usage = format!(
|
|
||||||
"$ cat foo.txt.age | {} -d -i {} > foo.txt",
|
|
||||||
age_binary, file_name
|
|
||||||
);
|
|
||||||
let identity_usage = format!(
|
let identity_usage = format!(
|
||||||
"$ age-plugin-yubikey -i --serial {} --slot {} > {}",
|
"$ age-plugin-yubikey -i --serial {} --slot {} > {}",
|
||||||
stub.serial,
|
stub.serial,
|
||||||
|
|||||||
+1
-1
@@ -177,7 +177,7 @@ impl Metadata {
|
|||||||
.validity()
|
.validity()
|
||||||
.not_before
|
.not_before
|
||||||
.to_rfc2822()
|
.to_rfc2822()
|
||||||
.unwrap_or_else(|e| format!("Invalid date: {}", e)),
|
.unwrap_or_else(|e| format!("Invalid date: {e}")),
|
||||||
pin_policy,
|
pin_policy,
|
||||||
touch_policy,
|
touch_policy,
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user