Extend Test configuration file format.
Run key generation tests for the algorithms specified in the configuration.
This commit is contained in:
parent
2ef6e0442d
commit
3361c8b79d
7 changed files with 163 additions and 210 deletions
|
@ -10,22 +10,27 @@
|
||||||
# You should configure your set of test-card identifiers and store your
|
# You should configure your set of test-card identifiers and store your
|
||||||
# configuration in that location.
|
# configuration in that location.
|
||||||
#
|
#
|
||||||
# Usually you will want to test cards via the pcsc transport, which talks
|
# Usually you will want to test cards via the pcsc backend, which talks
|
||||||
# to smartcards via the standard PCSC lite middleware
|
# to smartcards via the standard PCSC lite middleware
|
||||||
# (https://pcsclite.apdu.fr/).
|
# (https://pcsclite.apdu.fr/).
|
||||||
#
|
#
|
||||||
# An alternative scdc transport is provided. It talks to smartcards via
|
# An alternative scdc backend is provided. It talks to smartcards via
|
||||||
# scdaemon (which is part of the GnuPG system).
|
# scdaemon (which is part of the GnuPG system).
|
||||||
# The scdc transport is not recommended for use by default, it is offered
|
# The scdc backend is not recommended for use by default, it is offered
|
||||||
# for experimental use only.
|
# for experimental use only.
|
||||||
#
|
#
|
||||||
# (However, note that emulated Gnuk can currently only be accessed via scdc)
|
# (However, note that emulated Gnuk can currently only be accessed via scdc)
|
||||||
|
|
||||||
pcsc = [
|
[card.gnuk_emu]
|
||||||
"0006:12345678", # Yubikey 5
|
#backend.pcsc = "FFFE:F1420A7A"
|
||||||
"0005:0000A835", # FLOSS Card 3.4
|
backend.scdc = "D276000124010200FFFEF1420A7A0000"
|
||||||
]
|
config.keygen = ["RSA2k/32", "NIST256", "Curve25519"]
|
||||||
|
|
||||||
scdc = [
|
[card.yubikey5]
|
||||||
"D276000124010200FFFEF1420A7A0000", # Gnuk emulated
|
backend.pcsc = "0006:16019180"
|
||||||
|
#backend.scdc = "D2760001240103040006160191800000"
|
||||||
|
config.keygen = [
|
||||||
|
"RSA2k/17", "RSA3k/17", "RSA4k/17",
|
||||||
|
"NIST256", "NIST384", "NIST521",
|
||||||
|
"Curve25519"
|
||||||
]
|
]
|
|
@ -6,12 +6,78 @@
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use serde_derive::Deserialize;
|
use serde_derive::Deserialize;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use openpgp_card::apdu::PcscClient;
|
use openpgp_card::apdu::PcscClient;
|
||||||
use openpgp_card::card_app::CardApp;
|
use openpgp_card::card_app::CardApp;
|
||||||
use openpgp_card::CardClientBox;
|
use openpgp_card::CardClientBox;
|
||||||
use openpgp_card_scdc::ScdClient;
|
use openpgp_card_scdc::ScdClient;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct TestConfig {
|
||||||
|
card: BTreeMap<String, Card>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Card {
|
||||||
|
backend: BTreeMap<String, String>,
|
||||||
|
config: Config,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
pub struct Config {
|
||||||
|
pub keygen: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An "opened" card, via one particular backend, with test-metadata
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TestCardApp {
|
||||||
|
name: String,
|
||||||
|
tc: TestCard,
|
||||||
|
config: Config,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestCardApp {
|
||||||
|
pub(crate) fn get_card_app(&mut self) -> Result<CardApp> {
|
||||||
|
self.tc.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_config(&self) -> &Config {
|
||||||
|
&self.config
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestConfig {
|
||||||
|
pub fn load(file: &str) -> Result<Self> {
|
||||||
|
let config_file = std::fs::read_to_string(file)?;
|
||||||
|
|
||||||
|
let config: Self = toml::from_str(&config_file)?;
|
||||||
|
Ok(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_cards(self) -> Vec<TestCardApp> {
|
||||||
|
let mut cards = vec![];
|
||||||
|
|
||||||
|
for (name, card) in self.card {
|
||||||
|
for (backend, id) in &card.backend {
|
||||||
|
let tc: TestCard = match backend.as_str() {
|
||||||
|
"pcsc" => TestCard::Pcsc(id.to_string()),
|
||||||
|
"scdc" => TestCard::Scdc(id.to_string()),
|
||||||
|
_ => panic!("unexpected backend {}", backend),
|
||||||
|
};
|
||||||
|
|
||||||
|
cards.push(TestCardApp {
|
||||||
|
name: name.clone(),
|
||||||
|
tc,
|
||||||
|
config: card.config.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cards
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum TestCard {
|
pub enum TestCard {
|
||||||
Pcsc(String),
|
Pcsc(String),
|
||||||
|
@ -65,36 +131,3 @@ impl TestCard {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub struct TestConfig {
|
|
||||||
pcsc: Option<Vec<String>>,
|
|
||||||
scdc: Option<Vec<String>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TestConfig {
|
|
||||||
pub fn open(file: &str) -> Result<Self> {
|
|
||||||
let config_file = std::fs::read_to_string(file)?;
|
|
||||||
|
|
||||||
let config: Self = toml::from_str(&config_file)?;
|
|
||||||
Ok(config)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_cards(&self) -> Vec<TestCard> {
|
|
||||||
let mut cards = vec![];
|
|
||||||
|
|
||||||
if let Some(pcsc) = &self.pcsc {
|
|
||||||
for card in pcsc {
|
|
||||||
cards.push(TestCard::Pcsc(card.to_string()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(scdc) = &self.scdc {
|
|
||||||
for card in scdc {
|
|
||||||
cards.push(TestCard::Scdc(card.to_string()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cards
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ use card_functionality::tests::*;
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
||||||
let config = TestConfig::open("config/test-cards.toml")?;
|
let config = TestConfig::load("config/test-cards.toml")?;
|
||||||
|
|
||||||
let cards = config.get_cards();
|
let cards = config.get_cards();
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ use sequoia_openpgp::Cert;
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
||||||
let config = TestConfig::open("config/test-cards.toml")?;
|
let config = TestConfig::load("config/test-cards.toml")?;
|
||||||
|
|
||||||
let cards = config.get_cards();
|
let cards = config.get_cards();
|
||||||
|
|
||||||
|
@ -31,34 +31,42 @@ fn main() -> Result<()> {
|
||||||
println!("Reset");
|
println!("Reset");
|
||||||
let _ = run_test(&mut card, test_reset, &[])?;
|
let _ = run_test(&mut card, test_reset, &[])?;
|
||||||
|
|
||||||
println!("Algo info");
|
// println!("Algo info");
|
||||||
let _ = run_test(&mut card, test_print_algo_info, &[])?;
|
// let _ = run_test(&mut card, test_print_algo_info, &[])?;
|
||||||
|
|
||||||
// Set user data because keygen expects a name (for the user id)
|
// Set user data because keygen expects a name (for the user id)
|
||||||
println!("Set user data");
|
println!("Set user data");
|
||||||
let _ = run_test(&mut card, test_set_user_data, &[])?;
|
let _ = run_test(&mut card, test_set_user_data, &[])?;
|
||||||
|
|
||||||
println!("Generate key");
|
let algos = {
|
||||||
let res = run_test(&mut card, test_keygen, &[])?;
|
let config = card.get_config();
|
||||||
|
config.keygen.clone()
|
||||||
if let TestResult::Text(cert) = &res[0] {
|
|
||||||
// sign
|
|
||||||
print!(" Sign");
|
|
||||||
let sign_out = run_test(&mut card, test_sign, &[cert])?;
|
|
||||||
println!(" {:x?}", sign_out);
|
|
||||||
|
|
||||||
// decrypt
|
|
||||||
let c = Cert::from_str(cert)?;
|
|
||||||
let ciphertext = util::encrypt_to("Hello world!\n", &c)?;
|
|
||||||
|
|
||||||
print!(" Decrypt");
|
|
||||||
let dec_out =
|
|
||||||
run_test(&mut card, test_decrypt, &[cert, &ciphertext])?;
|
|
||||||
println!(" {:x?}", dec_out);
|
|
||||||
} else {
|
|
||||||
panic!("Didn't get back a Cert from test_keygen");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
for algo in algos {
|
||||||
|
println!("Generate key [{}]", algo);
|
||||||
|
|
||||||
|
let res = run_test(&mut card, test_keygen, &[&algo])?;
|
||||||
|
|
||||||
|
if let TestResult::Text(cert) = &res[0] {
|
||||||
|
// sign
|
||||||
|
print!(" Sign");
|
||||||
|
let sign_out = run_test(&mut card, test_sign, &[cert])?;
|
||||||
|
println!(" {:x?}", sign_out);
|
||||||
|
|
||||||
|
// decrypt
|
||||||
|
let c = Cert::from_str(cert)?;
|
||||||
|
let ciphertext = util::encrypt_to("Hello world!\n", &c)?;
|
||||||
|
|
||||||
|
print!(" Decrypt");
|
||||||
|
let dec_out =
|
||||||
|
run_test(&mut card, test_decrypt, &[cert, &ciphertext])?;
|
||||||
|
println!(" {:x?}", dec_out);
|
||||||
|
} else {
|
||||||
|
panic!("Didn't get back a Cert from test_keygen");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ use card_functionality::tests::*;
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
||||||
let config = TestConfig::open("config/test-cards.toml")?;
|
let config = TestConfig::load("config/test-cards.toml")?;
|
||||||
|
|
||||||
let cards = config.get_cards();
|
let cards = config.get_cards();
|
||||||
|
|
||||||
|
|
|
@ -2,31 +2,23 @@
|
||||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
|
||||||
use anyhow::{Error, Result};
|
use anyhow::{Error, Result};
|
||||||
use std::convert::TryFrom;
|
|
||||||
use std::convert::TryInto;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::string::FromUtf8Error;
|
use std::string::FromUtf8Error;
|
||||||
use std::time::SystemTime;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use sequoia_openpgp::packet::key::{
|
|
||||||
KeyRole, PrimaryRole, PublicParts, SubordinateRole, UnspecifiedRole,
|
|
||||||
};
|
|
||||||
use sequoia_openpgp::packet::signature::SignatureBuilder;
|
|
||||||
use sequoia_openpgp::packet::{Key, UserID};
|
|
||||||
use sequoia_openpgp::parse::Parse;
|
use sequoia_openpgp::parse::Parse;
|
||||||
use sequoia_openpgp::serialize::SerializeInto;
|
use sequoia_openpgp::serialize::SerializeInto;
|
||||||
use sequoia_openpgp::types::{KeyFlags, SignatureType, Timestamp};
|
use sequoia_openpgp::types::Timestamp;
|
||||||
use sequoia_openpgp::{Cert, Packet};
|
use sequoia_openpgp::Cert;
|
||||||
|
|
||||||
use openpgp_card::card_app::CardApp;
|
use openpgp_card::card_app::CardApp;
|
||||||
use openpgp_card::errors::{OcErrorStatus, OpenpgpCardError};
|
use openpgp_card::errors::{OcErrorStatus, OpenpgpCardError};
|
||||||
use openpgp_card::{
|
use openpgp_card::{AlgoSimple, KeyType, Sex};
|
||||||
Algo, Curve, EccAttrs, EccType, KeyType, PublicKeyMaterial, RsaAttrs, Sex,
|
use openpgp_card_sequoia::{
|
||||||
|
make_cert, public_key_material_to_key, public_to_fingerprint,
|
||||||
};
|
};
|
||||||
use openpgp_card_sequoia::{make_cert, signer::CardSigner};
|
|
||||||
|
|
||||||
use crate::cards::TestCard;
|
use crate::cards::TestCardApp;
|
||||||
use crate::util;
|
use crate::util;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -216,151 +208,48 @@ pub fn test_upload_keys(
|
||||||
/// Generate keys for each of the three KeyTypes
|
/// Generate keys for each of the three KeyTypes
|
||||||
pub fn test_keygen(
|
pub fn test_keygen(
|
||||||
ca: &mut CardApp,
|
ca: &mut CardApp,
|
||||||
_param: &[&str],
|
param: &[&str],
|
||||||
) -> Result<TestOutput, TestError> {
|
) -> Result<TestOutput, TestError> {
|
||||||
let verify = ca.verify_pw3("12345678")?;
|
let verify = ca.verify_pw3("12345678")?;
|
||||||
verify.check_ok()?;
|
verify.check_ok()?;
|
||||||
|
|
||||||
// ---------------
|
// Generate all three subkeys on card
|
||||||
|
let algo = param[0];
|
||||||
|
|
||||||
// RSA 1024, e=17
|
let alg = AlgoSimple::from(algo);
|
||||||
let rsa1k = Algo::Rsa(RsaAttrs {
|
|
||||||
len_n: 1024,
|
|
||||||
len_e: 17,
|
|
||||||
import_format: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
// RSA 2048, e=17
|
let (pkm, ts) =
|
||||||
let rsa2k_17 = Algo::Rsa(RsaAttrs {
|
ca.generate_key_simple(public_to_fingerprint, KeyType::Signing, alg)?;
|
||||||
len_n: 2048,
|
let key_sig = public_key_material_to_key(
|
||||||
len_e: 17,
|
|
||||||
import_format: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
// RSA 2048, e=32
|
|
||||||
let rsa2k_32 = Algo::Rsa(RsaAttrs {
|
|
||||||
len_n: 2048,
|
|
||||||
len_e: 32,
|
|
||||||
import_format: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
// RSA 3072, e=17
|
|
||||||
let rsa3k = Algo::Rsa(RsaAttrs {
|
|
||||||
len_n: 3072,
|
|
||||||
len_e: 17,
|
|
||||||
import_format: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
// RSA 4096, e=17
|
|
||||||
let rsa4k_17 = Algo::Rsa(RsaAttrs {
|
|
||||||
len_n: 4096,
|
|
||||||
len_e: 17,
|
|
||||||
import_format: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
// RSA 4096, e=32
|
|
||||||
let rsa4k_32 = Algo::Rsa(RsaAttrs {
|
|
||||||
len_n: 4096,
|
|
||||||
len_e: 32,
|
|
||||||
import_format: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
// ed25519 (sign)
|
|
||||||
let ed25519 = Algo::Ecc(EccAttrs {
|
|
||||||
ecc_type: EccType::EdDSA,
|
|
||||||
curve: Curve::Ed25519,
|
|
||||||
import_format: None,
|
|
||||||
});
|
|
||||||
|
|
||||||
// cv25519 (dec)
|
|
||||||
let cv25519 = Algo::Ecc(EccAttrs {
|
|
||||||
ecc_type: EccType::ECDH,
|
|
||||||
curve: Curve::Cv25519,
|
|
||||||
import_format: None,
|
|
||||||
});
|
|
||||||
|
|
||||||
// nist256 sig, auth
|
|
||||||
let nist256_ecdsa = Algo::Ecc(EccAttrs {
|
|
||||||
ecc_type: EccType::ECDSA,
|
|
||||||
curve: Curve::NistP256r1,
|
|
||||||
import_format: None,
|
|
||||||
});
|
|
||||||
|
|
||||||
// nist256 dec
|
|
||||||
let nist256_ecdh = Algo::Ecc(EccAttrs {
|
|
||||||
ecc_type: EccType::ECDH,
|
|
||||||
curve: Curve::NistP256r1,
|
|
||||||
import_format: None,
|
|
||||||
});
|
|
||||||
|
|
||||||
// ---------------
|
|
||||||
|
|
||||||
// -- RSA (e=17) [YK4, YK5]
|
|
||||||
// let dec_algo = &rsa2k_17;
|
|
||||||
// let sig_algo = &rsa2k_17;
|
|
||||||
|
|
||||||
// let dec_algo = &rsa4k_17;
|
|
||||||
// let sig_algo = &rsa4k_17;
|
|
||||||
|
|
||||||
// -- RSA (e=32) [YK5, Floss3.4, Gnuk1.2]
|
|
||||||
// let dec_algo = &rsa2k_32;
|
|
||||||
// let sig_algo = &rsa2k_32;
|
|
||||||
|
|
||||||
let dec_algo = &rsa4k_32;
|
|
||||||
let sig_algo = &rsa4k_32;
|
|
||||||
|
|
||||||
// -- NIST 256
|
|
||||||
// let dec_algo = &nist256_ecdh;
|
|
||||||
// let sig_algo = &nist256_ecdsa;
|
|
||||||
|
|
||||||
// -- Curve 25519
|
|
||||||
// let dec_algo = &cv25519;
|
|
||||||
// let sig_algo = &ed25519;
|
|
||||||
|
|
||||||
// ---------------
|
|
||||||
|
|
||||||
let fp = |pkm: &PublicKeyMaterial, ts: SystemTime, kt: KeyType| {
|
|
||||||
// FIXME: store creation timestamp
|
|
||||||
|
|
||||||
let key =
|
|
||||||
openpgp_card_sequoia::public_key_material_to_key(pkm, kt, ts)?;
|
|
||||||
|
|
||||||
let fp = key.fingerprint();
|
|
||||||
let fp = fp.as_bytes();
|
|
||||||
assert_eq!(fp.len(), 20);
|
|
||||||
|
|
||||||
Ok(fp.try_into().unwrap())
|
|
||||||
};
|
|
||||||
|
|
||||||
// ------
|
|
||||||
|
|
||||||
let (pkm, ts) = ca.generate_key(fp, KeyType::Signing, Some(sig_algo))?;
|
|
||||||
let key_sig = openpgp_card_sequoia::public_key_material_to_key(
|
|
||||||
&pkm,
|
&pkm,
|
||||||
KeyType::Signing,
|
KeyType::Signing,
|
||||||
SystemTime::from(Timestamp::from(ts)),
|
Timestamp::from(ts).into(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// ------
|
let (pkm, ts) = ca.generate_key_simple(
|
||||||
|
public_to_fingerprint,
|
||||||
let (pkm, ts) =
|
KeyType::Decryption,
|
||||||
ca.generate_key(fp, KeyType::Decryption, Some(dec_algo))?;
|
alg,
|
||||||
let key_dec = openpgp_card_sequoia::public_key_material_to_key(
|
)?;
|
||||||
|
let key_dec = public_key_material_to_key(
|
||||||
&pkm,
|
&pkm,
|
||||||
KeyType::Decryption,
|
KeyType::Decryption,
|
||||||
SystemTime::from(Timestamp::from(ts)),
|
Timestamp::from(ts).into(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// ------
|
let (pkm, ts) = ca.generate_key_simple(
|
||||||
|
public_to_fingerprint,
|
||||||
let (pkm, ts) =
|
KeyType::Authentication,
|
||||||
ca.generate_key(fp, KeyType::Authentication, Some(sig_algo))?;
|
alg,
|
||||||
let key_aut = openpgp_card_sequoia::public_key_material_to_key(
|
)?;
|
||||||
|
let key_aut = public_key_material_to_key(
|
||||||
&pkm,
|
&pkm,
|
||||||
KeyType::Authentication,
|
KeyType::Authentication,
|
||||||
SystemTime::from(Timestamp::from(ts)),
|
Timestamp::from(ts).into(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
// Generate a Cert for this set of generated keys
|
||||||
|
|
||||||
let cert = make_cert(ca, key_sig, key_dec, key_aut)?;
|
let cert = make_cert(ca, key_sig, key_dec, key_aut)?;
|
||||||
let armored = String::from_utf8(cert.armored().to_vec()?)?;
|
let armored = String::from_utf8(cert.armored().to_vec()?)?;
|
||||||
|
|
||||||
|
@ -525,11 +414,11 @@ pub fn test_verify(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_test(
|
pub fn run_test(
|
||||||
card: &mut TestCard,
|
card: &mut TestCardApp,
|
||||||
t: fn(&mut CardApp, &[&str]) -> Result<TestOutput, TestError>,
|
t: fn(&mut CardApp, &[&str]) -> Result<TestOutput, TestError>,
|
||||||
param: &[&str],
|
param: &[&str],
|
||||||
) -> Result<TestOutput, TestError> {
|
) -> Result<TestOutput, TestError> {
|
||||||
let mut ca = card.open()?;
|
let mut ca = card.get_card_app()?;
|
||||||
let ard = ca.get_app_data()?;
|
let ard = ca.get_app_data()?;
|
||||||
let _app_id = CardApp::get_aid(&ard)?;
|
let _app_id = CardApp::get_aid(&ard)?;
|
||||||
|
|
||||||
|
|
|
@ -531,3 +531,21 @@ pub fn sign(
|
||||||
|
|
||||||
String::from_utf8(buffer).context("Failed to convert signature to utf8")
|
String::from_utf8(buffer).context("Failed to convert signature to utf8")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mapping function to get a fingerprint from "PublicKeyMaterial +
|
||||||
|
/// timestamp + KeyType" (intended for use with `CardApp.generate_key()`).
|
||||||
|
pub fn public_to_fingerprint(
|
||||||
|
pkm: &PublicKeyMaterial,
|
||||||
|
ts: SystemTime,
|
||||||
|
kt: KeyType,
|
||||||
|
) -> Result<[u8; 20]> {
|
||||||
|
// Transform PublicKeyMaterial into a Sequoia Key
|
||||||
|
let key = public_key_material_to_key(pkm, kt, ts)?;
|
||||||
|
|
||||||
|
// Get fingerprint from the Sequoia Key
|
||||||
|
let fp = key.fingerprint();
|
||||||
|
let fp = fp.as_bytes();
|
||||||
|
|
||||||
|
assert_eq!(fp.len(), 20);
|
||||||
|
Ok(fp.try_into()?)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue