From b489c7da4dac477143ecd57a143471e8e6283bad Mon Sep 17 00:00:00 2001 From: Nora Widdecke Date: Thu, 27 Oct 2022 21:10:29 +0200 Subject: [PATCH 1/3] opgpcard: Allow sign and decrypt to write to file - Sometimes, it is more convenient to give the target filename as an argument, instead of using pipes. - Add an optional argument -o/--output to opgpcard sign and opgpcard decrypt. --- tools/src/bin/opgpcard/commands/decrypt.rs | 7 ++++++- tools/src/bin/opgpcard/commands/sign.rs | 16 ++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/tools/src/bin/opgpcard/commands/decrypt.rs b/tools/src/bin/opgpcard/commands/decrypt.rs index 462cba2..830809c 100644 --- a/tools/src/bin/opgpcard/commands/decrypt.rs +++ b/tools/src/bin/opgpcard/commands/decrypt.rs @@ -36,6 +36,10 @@ pub struct DecryptCommand { /// Input file (stdin if unset) #[clap(name = "input")] input: Option, + + /// Output file (stdout if unset) + #[clap(name = "output", long = "output", short = 'o')] + pub output: Option, } pub fn decrypt(command: DecryptCommand) -> Result<(), Box> { @@ -59,7 +63,8 @@ pub fn decrypt(command: DecryptCommand) -> Result<(), Box let db = DecryptorBuilder::from_reader(input)?; let mut decryptor = db.with_policy(&p, None, d)?; - std::io::copy(&mut decryptor, &mut std::io::stdout())?; + let mut sink = util::open_or_stdout(command.output.as_deref())?; + std::io::copy(&mut decryptor, &mut sink)?; Ok(()) } diff --git a/tools/src/bin/opgpcard/commands/sign.rs b/tools/src/bin/opgpcard/commands/sign.rs index f891b61..e00e9c1 100644 --- a/tools/src/bin/opgpcard/commands/sign.rs +++ b/tools/src/bin/opgpcard/commands/sign.rs @@ -41,11 +41,20 @@ pub struct SignCommand { /// Input file (stdin if unset) #[clap(name = "input")] pub input: Option, + + /// Output file (stdout if unset) + #[clap(name = "output", long = "output", short = 'o')] + pub output: Option, } pub fn sign(command: SignCommand) -> Result<(), Box> { if command.detached { - sign_detached(&command.ident, command.user_pin, command.input.as_deref()) + sign_detached( + &command.ident, + command.user_pin, + command.input.as_deref(), + command.output.as_deref(), + ) } else { Err(anyhow::anyhow!("Only detached signatures are supported for now").into()) } @@ -55,6 +64,7 @@ pub fn sign_detached( ident: &str, pin_file: Option, input: Option<&Path>, + output: Option<&Path>, ) -> Result<(), Box> { let mut input = util::open_or_stdin(input)?; @@ -71,7 +81,9 @@ pub fn sign_detached( let mut sign = util::verify_to_sign(&mut card, user_pin.as_deref())?; let s = sign.signer(&|| println!("Touch confirmation needed for signing"))?; - let message = Armorer::new(Message::new(std::io::stdout())).build()?; + let sink = util::open_or_stdout(output)?; + + let message = Armorer::new(Message::new(sink)).build()?; let mut signer = Signer::new(message, s).detached().build()?; std::io::copy(&mut input, &mut signer)?; From 09c554affd742f5073141291dbdc7ee23ede3cb7 Mon Sep 17 00:00:00 2001 From: Nora Widdecke Date: Thu, 27 Oct 2022 13:17:05 +0200 Subject: [PATCH 2/3] opgpcard: Test key use. - Add tests for all four combinations of key generation and import, and signing and decrypting. --- .gitlab-ci.yml | 6 +- tools/cargo-test-in-docker | 1 + tools/subplot/opgpcard.md | 124 +++++++++++++++++++++++++++++++++++++ 3 files changed, 130 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f12d583..3619570 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -137,7 +137,11 @@ subplot: before_script: - mkdir -p /run/user/$UID - apt update -y -qq - - apt install -y -qq --no-install-recommends git clang make pkg-config nettle-dev libssl-dev capnproto ca-certificates libpcsclite-dev + - > + apt install -y -qq --no-install-recommends + git clang make pkg-config nettle-dev libssl-dev capnproto ca-certificates + libpcsclite-dev + sq - apt clean - /etc/init.d/pcscd start - su - -c "sh /home/jcardsim/run-card.sh >/dev/null" jcardsim diff --git a/tools/cargo-test-in-docker b/tools/cargo-test-in-docker index 9332cd8..acece79 100755 --- a/tools/cargo-test-in-docker +++ b/tools/cargo-test-in-docker @@ -16,6 +16,7 @@ docker run --rm -it \ -v $(pwd):/src \ -e CARD_BASED_TESTS=true \ registry.gitlab.com/openpgp-card/virtual-cards/smartpgp-builddeps sh -c ' +apt install sq && sed -i "s/timeout=20/timeout=60/" /home/jcardsim/run-card.sh && /etc/init.d/pcscd start && su - -c "sh /home/jcardsim/run-card.sh >/dev/null" jcardsim && diff --git a/tools/subplot/opgpcard.md b/tools/subplot/opgpcard.md index d0729d8..6614596 100644 --- a/tools/subplot/opgpcard.md +++ b/tools/subplot/opgpcard.md @@ -112,3 +112,127 @@ then stdout, as JSON, matches embedded file info.json "ident": "AFAF:00001234" } ~~~ + +## Key generation: `opgpcard generate` and `opgpcard decrypt` + +_Requirement: The tool is able to generate keys and use them for decryption._ + +This is not at all a thorough test, but it exercises the simple happy +paths of the subcommand. + +~~~scenario +given an installed opgpcard +given file admin.pin +given file user.pin +when I run opgpcard admin --card AFAF:00001234 --admin-pin admin.pin generate --user-pin user.pin --output certfile +then file certfile contains "-----BEGIN PGP PUBLIC KEY BLOCK-----" +then file certfile contains "-----END PGP PUBLIC KEY BLOCK-----" + +given file message +when I run sq encrypt message --recipient-cert certfile --output message.enc +and I run opgpcard decrypt --card AFAF:00001234 --user-pin user.pin message.enc --output message.dec +then files message and message.dec match +~~~ + +~~~{#admin.pin .file} +12345678 +~~~ + +~~~{#user.pin .file} +123456 +~~~ + +~~~{#message .file} +Hello World! +~~~ + +## Key generation: `opgpcard generate` and `opgpcard sign` + +_Requirement: The tool is able to generate keys and use them for signing._ + +This is not at all a thorough test, but it exercises the simple happy +paths of the subcommand. + +~~~scenario +given an installed opgpcard +given file admin.pin +given file user.pin +when I run opgpcard admin --card AFAF:00001234 --admin-pin admin.pin generate --user-pin user.pin --output certfile +then file certfile contains "-----BEGIN PGP PUBLIC KEY BLOCK-----" +then file certfile contains "-----END PGP PUBLIC KEY BLOCK-----" + +given file message +when I run opgpcard sign message --card AFAF:00001234 --user-pin user.pin --detached --output message.sig +when I run sq verify message --detached message.sig --signer-cert certfile +then stderr contains "1 good signature." +~~~ + +## Key import: `opgpcard import` and `opgpcard decrypt` + +_Requirement: The tool is able to import keys and use them for decryption._ + +This is not at all a thorough test, but it exercises the simple happy +paths of the subcommand. + +~~~scenario +given an installed opgpcard +given file admin.pin +given file user.pin +given file nist256key +when I run opgpcard admin --card AFAF:00001234 --admin-pin admin.pin import nist256key +then stdout contains "CCCFFFAAC77C9F9D3BB2D2CA3C93515DA813C03F" +then stdout contains "360EC3C59A7D8E51DCE9FA1171858B15EE7F4BCA" +then stdout contains "6D186AC7C6761FC22BE07557D2BE4918C44C74D9" + +given file message +when I run sq encrypt message --recipient-cert nist256key --output message.enc +and I run opgpcard decrypt --card AFAF:00001234 --user-pin user.pin message.enc --output message.dec +then files message and message.dec match +~~~ + +## Key import: `opgpcard import` and `opgpcard sign` + +_Requirement: The tool is able to import keys and use them for signing._ + +This is not at all a thorough test, but it exercises the simple happy +paths of the subcommand. + +~~~scenario +given an installed opgpcard +given file admin.pin +given file nist256key +when I run opgpcard admin --card AFAF:00001234 --admin-pin admin.pin import nist256key +then stdout contains "CCCFFFAAC77C9F9D3BB2D2CA3C93515DA813C03F" +then stdout contains "360EC3C59A7D8E51DCE9FA1171858B15EE7F4BCA" +then stdout contains "6D186AC7C6761FC22BE07557D2BE4918C44C74D9" + +given file user.pin +given file message +when I run opgpcard sign message --card AFAF:00001234 --user-pin user.pin --detached --output message.sig +when I run sq verify message --detached message.sig --signer-cert nist256key +then stderr contains "1 good signature." +~~~ + +~~~{#nist256key .file} +-----BEGIN PGP PRIVATE KEY BLOCK----- + +lHcEYPP/JxMIKoZIzj0DAQcCAwT5a9JIM6BX1zxvFkNr2LMGLyTw72+iXsUZlA8X +w3Bn91jVRpSSIITjibHKliS2e2kZlaoHOZvlXmZ3nqOANjV+AAEAzCPG24MzHigZ +qyoaNr+7o6u/D8DndXHhsrERqm9cCgcOybQfTklTVCBQMjU2IDxuaXN0MjU2QGV4 +YW1wbGUub3JnPoiQBBMTCAA4FiEEzM//qsd8n507stLKPJNRXagTwD8FAmDz/ycC +GwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQPJNRXagTwD+bZAD/fu4NjabH +GKHB1dIpqX+opDt8E3RFes58P+p4wh8W+xEBAMcPs6HLYvcLLkqtpV06wKYngPY+ +Ln/wcpQOagwO+EgfnHsEYPP/JxIIKoZIzj0DAQcCAwTtyP4rOGNlU+Tzpa7UYv5h +jR/T9DzMVUntaFhb3Cm0ung7IEGNAOcbgpCx/fdm7BPL+9MJB+qwpsz8bQa4DfnE +AwEIBwABALvh9XLpqe1MqwPodYlWKgw4me/tR2FNKmLXPC1gl3g7EAeIeAQYEwgA +IBYhBMzP/6rHfJ+dO7LSyjyTUV2oE8A/BQJg8/8nAhsMAAoJEDyTUV2oE8A/SMMA +/3DuQU8hb+U9U2nX93bHwpTBQfAONsEn/vUeZ6u4NdX4AP9ABH//08SFfFttiWHm +TTAR9e57Rw0DhI/wb6qqWABIyZx3BGDz/zkTCCqGSM49AwEHAgMEJz+bbG6RHQag +BoULLuklPRUtQauVTxM9WZZG3PEAnIZuu4LKkHn/JPAN04iSV+K3lBWN+HALVZSV +kFweNSOX6gAA/RD5JKvdwS3CofhQY+czewkb8feXGLQIaPS9rIWP7QX4En2IeAQY +EwgAIBYhBMzP/6rHfJ+dO7LSyjyTUV2oE8A/BQJg8/85AhsgAAoJEDyTUV2oE8A/ +CSkA/2WnUoIwtv4ZBhuCpJY/GIFqRJEPgQ7DW1bXTrYsoTehAQD1wDkG0vD6Jnfu +QIPHexNllmYakW7WNqu1gobPuNEQyw== +=E2Hb +-----END PGP PRIVATE KEY BLOCK----- +~~~ From 8c9d792c42c1c6616016d311a0653d5704c162f4 Mon Sep 17 00:00:00 2001 From: Nora Widdecke Date: Thu, 27 Oct 2022 21:59:25 +0200 Subject: [PATCH 3/3] ci: prevent multithreading for subplot tests - There is only one virtual card, accessing it from multiple programs in parallel leads to undefined behavior. --- .gitlab-ci.yml | 2 +- tools/cargo-test-in-docker | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3619570..8380575 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -149,7 +149,7 @@ subplot: script: # make sure a virtual card is available, so that the subplot tests are # generated - - CARD_BASED_TESTS=true cargo test + - CARD_BASED_TESTS=true cargo test -- --test-threads 1 cache: # inherit all general cache settings <<: *general_cache_config diff --git a/tools/cargo-test-in-docker b/tools/cargo-test-in-docker index acece79..b369ac8 100755 --- a/tools/cargo-test-in-docker +++ b/tools/cargo-test-in-docker @@ -23,4 +23,4 @@ su - -c "sh /home/jcardsim/run-card.sh >/dev/null" jcardsim && cd /src/tools && CARGO_TARGET_DIR=/cargo/ cargo update && CARGO_TARGET_DIR=/cargo/ cargo build -vv && -CARGO_TARGET_DIR=/cargo/ cargo test' +CARGO_TARGET_DIR=/cargo/ cargo test -- --test-threads 1'