diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 79c70f0..9eb5482 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -84,3 +84,64 @@ jobs: tag: ${{ github.ref }} prerelease: true if: github.event.inputs.test != 'true' + + deb: + name: Debian ${{ matrix.name }} + runs-on: ubuntu-latest + strategy: + matrix: + name: [linux] + include: + - name: linux + target: x86_64-unknown-linux-gnu + build_deps: > + libpcsclite-dev + + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + - name: Add target + run: rustup target add ${{ matrix.target }} + - name: cargo install cargo-deb + uses: actions-rs/cargo@v1 + with: + command: install + args: cargo-deb + + - name: Install build dependencies + run: sudo apt install ${{ matrix.build_deps }} + if: matrix.build_deps != '' + + - name: Set up .cargo/config + run: | + mkdir .cargo + echo '${{ matrix.cargo_config }}' >.cargo/config + if: matrix.cargo_config != '' + + - name: cargo build + run: cargo build --release --locked --target ${{ matrix.target }} ${{ matrix.build_flags }} + + - name: Generate manpages + uses: actions-rs/cargo@v1 + with: + command: run + args: --example generate-docs + + - name: cargo deb + uses: actions-rs/cargo@v1 + with: + command: deb + args: --package age-plugin-yubikey --no-build --target ${{ matrix.target }} + + - name: Upload Debian package to release + uses: svenstaro/upload-release-action@2.2.0 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: target/${{ matrix.target }}/debian/*.deb + tag: ${{ github.ref }} + file_glob: true + prerelease: true + if: github.event.inputs.test != 'true' diff --git a/Cargo.lock b/Cargo.lock index 3eb2b67..dbb90d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,11 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aead" version = "0.3.2" @@ -49,9 +55,11 @@ dependencies = [ "console", "dialoguer", "env_logger", + "flate2", "gumdrop", "hex", "log", + "man", "p256", "rand 0.7.3", "secrecy", @@ -244,6 +252,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" +[[package]] +name = "crc32fast" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +dependencies = [ + "cfg-if", +] + [[package]] name = "crypto-mac" version = "0.10.0" @@ -383,6 +400,18 @@ dependencies = [ "subtle", ] +[[package]] +name = "flate2" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0" +dependencies = [ + "cfg-if", + "crc32fast", + "libc", + "miniz_oxide", +] + [[package]] name = "funty" version = "1.1.0" @@ -536,12 +565,31 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "man" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebf5fa795187a80147b1ac10aaedcf5ffd3bbeb1838bda61801a1c9ad700a1c9" +dependencies = [ + "roff", +] + [[package]] name = "memchr" version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg 1.0.1", +] + [[package]] name = "nom" version = "6.1.2" @@ -882,6 +930,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "roff" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33e4fb37ba46888052c763e4ec2acfedd8f00f62897b630cadb6298b833675e" + [[package]] name = "rsa" version = "0.3.0" diff --git a/Cargo.toml b/Cargo.toml index 67e299b..67f00e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,17 @@ categories = ["command-line-utilities", "cryptography"] license = "MIT OR Apache-2.0" edition = "2018" +[package.metadata.deb] +extended-description = """\ +An age plugin adding support for YubiKeys and other PIV hardware tokens. \ +Currently in BETA; we strongly recommend using this with a new YubiKey.""" +section = "utils" +assets = [ + ["target/release/age-plugin-yubikey", "usr/bin/", "755"], + ["target/manpages/age-plugin-yubikey.1.gz", "usr/share/man/man1/", "644"], + ["README.md", "usr/share/doc/age-plugin-yubikey/README.md", "644"], +] + [dependencies] age-core = "0.5" age-plugin = "0.0" @@ -30,6 +41,10 @@ x509 = "0.2" x509-parser = "0.9" yubikey-piv = { version = "0.3", features = ["untested"] } +[dev-dependencies] +flate2 = "1" +man = "0.3" + [patch.crates-io] age-core = { git = "https://github.com/str4d/rage.git", rev = "a9b5e88aa5816b284ebea23fc84d0203a3c4fdbb" } age-plugin = { git = "https://github.com/str4d/rage.git", rev = "a9b5e88aa5816b284ebea23fc84d0203a3c4fdbb" } diff --git a/examples/generate-docs.rs b/examples/generate-docs.rs new file mode 100644 index 0000000..4e5b609 --- /dev/null +++ b/examples/generate-docs.rs @@ -0,0 +1,93 @@ +use flate2::{write::GzEncoder, Compression}; +use man::prelude::*; +use std::fs::{create_dir_all, File}; +use std::io::prelude::*; + +const MANPAGES_DIR: &str = "./target/manpages"; + +fn generate_manpage(page: String, name: &str) { + let file = File::create(format!("{}/{}.1.gz", MANPAGES_DIR, name)) + .expect("Should be able to open file in target directory"); + let mut encoder = GzEncoder::new(file, Compression::best()); + encoder + .write_all(page.as_bytes()) + .expect("Should be able to write to file in target directory"); +} + +fn main() { + // Create the target directory if it does not exist. + let _ = create_dir_all(MANPAGES_DIR); + + let builder = Manual::new("age-plugin-yubikey") + .about("An age plugin adding support for YubiKeys and other PIV hardware tokens") + .author(Author::new("Jack Grigg").email("thestr4d@gmail.com")) + .flag( + Flag::new() + .short("-h") + .long("--help") + .help("Display help text and exit."), + ) + .flag( + Flag::new() + .short("-V") + .long("--version") + .help("Display version info and exit."), + ) + .flag( + Flag::new() + .short("-f") + .long("--force") + .help("Force --generate to overwrite a filled slot."), + ) + .flag( + Flag::new() + .short("-g") + .long("--generate") + .help("Generate a new YubiKey identity."), + ) + .flag( + Flag::new() + .short("-i") + .long("--identity") + .help("Print the identity stored in a YubiKey slot."), + ) + .flag( + Flag::new() + .short("-l") + .long("--list") + .help("List all age identities in connected YubiKeys."), + ) + .flag( + Flag::new() + .long("--list-all") + .help("List all YubiKey keys that are compatible with age."), + ) + .flag( + Flag::new() + .long("--name") + .help("Name for the generated identity. Defaults to 'age identity HEX_TAG'."), + ) + .flag( + Flag::new() + .long("--pin-policy") + .help("One of [always, once, never]. Defaults to 'once'."), + ) + .flag( + Flag::new() + .long("--serial") + .help("Specify which YubiKey to use, if more than one is plugged in."), + ) + .flag( + Flag::new() + .long("--slot") + .help("Specify which slot to use. Defaults to first usable slot."), + ) + .flag( + Flag::new() + .long("--touch-policy") + .help("One of [always, cached, never]. Defaults to 'always'."), + ); + let page = builder.render(); + + generate_manpage(page, "age-plugin-yubikey"); +}