Merge branch 'nora/keygen_test' into 'main'
opgpcard: test key generation and use See merge request openpgp-card/openpgp-card!26
This commit is contained in:
commit
6274c87de3
5 changed files with 152 additions and 6 deletions
|
@ -137,7 +137,11 @@ subplot:
|
||||||
before_script:
|
before_script:
|
||||||
- mkdir -p /run/user/$UID
|
- mkdir -p /run/user/$UID
|
||||||
- apt update -y -qq
|
- 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
|
- apt clean
|
||||||
- /etc/init.d/pcscd start
|
- /etc/init.d/pcscd start
|
||||||
- su - -c "sh /home/jcardsim/run-card.sh >/dev/null" jcardsim
|
- su - -c "sh /home/jcardsim/run-card.sh >/dev/null" jcardsim
|
||||||
|
@ -145,7 +149,7 @@ subplot:
|
||||||
script:
|
script:
|
||||||
# make sure a virtual card is available, so that the subplot tests are
|
# make sure a virtual card is available, so that the subplot tests are
|
||||||
# generated
|
# generated
|
||||||
- CARD_BASED_TESTS=true cargo test
|
- CARD_BASED_TESTS=true cargo test -- --test-threads 1
|
||||||
cache:
|
cache:
|
||||||
# inherit all general cache settings
|
# inherit all general cache settings
|
||||||
<<: *general_cache_config
|
<<: *general_cache_config
|
||||||
|
|
|
@ -16,10 +16,11 @@ docker run --rm -it \
|
||||||
-v $(pwd):/src \
|
-v $(pwd):/src \
|
||||||
-e CARD_BASED_TESTS=true \
|
-e CARD_BASED_TESTS=true \
|
||||||
registry.gitlab.com/openpgp-card/virtual-cards/smartpgp-builddeps sh -c '
|
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 &&
|
sed -i "s/timeout=20/timeout=60/" /home/jcardsim/run-card.sh &&
|
||||||
/etc/init.d/pcscd start &&
|
/etc/init.d/pcscd start &&
|
||||||
su - -c "sh /home/jcardsim/run-card.sh >/dev/null" jcardsim &&
|
su - -c "sh /home/jcardsim/run-card.sh >/dev/null" jcardsim &&
|
||||||
cd /src/tools &&
|
cd /src/tools &&
|
||||||
CARGO_TARGET_DIR=/cargo/ cargo update &&
|
CARGO_TARGET_DIR=/cargo/ cargo update &&
|
||||||
CARGO_TARGET_DIR=/cargo/ cargo build -vv &&
|
CARGO_TARGET_DIR=/cargo/ cargo build -vv &&
|
||||||
CARGO_TARGET_DIR=/cargo/ cargo test'
|
CARGO_TARGET_DIR=/cargo/ cargo test -- --test-threads 1'
|
||||||
|
|
|
@ -36,6 +36,10 @@ pub struct DecryptCommand {
|
||||||
/// Input file (stdin if unset)
|
/// Input file (stdin if unset)
|
||||||
#[clap(name = "input")]
|
#[clap(name = "input")]
|
||||||
input: Option<PathBuf>,
|
input: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// Output file (stdout if unset)
|
||||||
|
#[clap(name = "output", long = "output", short = 'o')]
|
||||||
|
pub output: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decrypt(command: DecryptCommand) -> Result<(), Box<dyn std::error::Error>> {
|
pub fn decrypt(command: DecryptCommand) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
@ -59,7 +63,8 @@ pub fn decrypt(command: DecryptCommand) -> Result<(), Box<dyn std::error::Error>
|
||||||
let db = DecryptorBuilder::from_reader(input)?;
|
let db = DecryptorBuilder::from_reader(input)?;
|
||||||
let mut decryptor = db.with_policy(&p, None, d)?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,11 +41,20 @@ pub struct SignCommand {
|
||||||
/// Input file (stdin if unset)
|
/// Input file (stdin if unset)
|
||||||
#[clap(name = "input")]
|
#[clap(name = "input")]
|
||||||
pub input: Option<PathBuf>,
|
pub input: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// Output file (stdout if unset)
|
||||||
|
#[clap(name = "output", long = "output", short = 'o')]
|
||||||
|
pub output: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sign(command: SignCommand) -> Result<(), Box<dyn std::error::Error>> {
|
pub fn sign(command: SignCommand) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
if command.detached {
|
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 {
|
} else {
|
||||||
Err(anyhow::anyhow!("Only detached signatures are supported for now").into())
|
Err(anyhow::anyhow!("Only detached signatures are supported for now").into())
|
||||||
}
|
}
|
||||||
|
@ -55,6 +64,7 @@ pub fn sign_detached(
|
||||||
ident: &str,
|
ident: &str,
|
||||||
pin_file: Option<PathBuf>,
|
pin_file: Option<PathBuf>,
|
||||||
input: Option<&Path>,
|
input: Option<&Path>,
|
||||||
|
output: Option<&Path>,
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut input = util::open_or_stdin(input)?;
|
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 mut sign = util::verify_to_sign(&mut card, user_pin.as_deref())?;
|
||||||
let s = sign.signer(&|| println!("Touch confirmation needed for signing"))?;
|
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()?;
|
let mut signer = Signer::new(message, s).detached().build()?;
|
||||||
|
|
||||||
std::io::copy(&mut input, &mut signer)?;
|
std::io::copy(&mut input, &mut signer)?;
|
||||||
|
|
|
@ -112,3 +112,127 @@ then stdout, as JSON, matches embedded file info.json
|
||||||
"ident": "AFAF:00001234"
|
"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-----
|
||||||
|
~~~
|
||||||
|
|
Loading…
Reference in a new issue