- Implement key generation (without specifying an algorithm so the current algo is used. only supports RSA for now)
- Refactor: rename key_upload.rs -> keys.rs - Fix handling of key timestamps
This commit is contained in:
parent
538bfb51d4
commit
7acc1deb98
7 changed files with 248 additions and 40 deletions
|
@ -26,6 +26,8 @@
|
|||
//! the command data field").
|
||||
|
||||
use anyhow::{Error, Result};
|
||||
use std::convert::TryInto;
|
||||
use std::time::SystemTime;
|
||||
use thiserror::Error;
|
||||
|
||||
use sequoia_openpgp::parse::Parse;
|
||||
|
@ -33,7 +35,7 @@ use sequoia_openpgp::Cert;
|
|||
|
||||
use openpgp_card::card_app::CardApp;
|
||||
use openpgp_card::errors::{OcErrorStatus, OpenpgpCardError};
|
||||
use openpgp_card::Sex;
|
||||
use openpgp_card::{KeyType, PublicKeyMaterial, Sex};
|
||||
|
||||
use crate::cards::{TestCard, TestConfig};
|
||||
|
||||
|
@ -203,10 +205,32 @@ fn test_upload_keys(
|
|||
Ok(vec![])
|
||||
}
|
||||
|
||||
fn test_keygen() {
|
||||
// FIXME
|
||||
// (implementation of this functionality is still missing in openpgp-card)
|
||||
unimplemented!()
|
||||
/// Generate keys for each of the three KeyTypes
|
||||
fn test_keygen(
|
||||
ca: &mut CardApp,
|
||||
_param: &[&str],
|
||||
) -> Result<TestOutput, TestError> {
|
||||
let verify = ca.verify_pw3("12345678")?;
|
||||
verify.check_ok()?;
|
||||
|
||||
let fp = |pkm: &PublicKeyMaterial, ts: SystemTime| {
|
||||
// FIXME: store creation timestamp
|
||||
|
||||
let key = openpgp_card_sequoia::public_key_material_to_key(pkm, ts)?;
|
||||
|
||||
let fp = key.fingerprint();
|
||||
let fp = fp.as_bytes();
|
||||
assert_eq!(fp.len(), 20);
|
||||
|
||||
println!("fp {:?}", fp);
|
||||
Ok(fp.try_into().unwrap())
|
||||
};
|
||||
|
||||
ca.generate_key(fp, KeyType::Signing)?;
|
||||
ca.generate_key(fp, KeyType::Decryption)?;
|
||||
ca.generate_key(fp, KeyType::Authentication)?;
|
||||
|
||||
Ok(vec![])
|
||||
}
|
||||
|
||||
fn test_reset(
|
||||
|
@ -342,6 +366,11 @@ fn main() -> Result<()> {
|
|||
println!("Reset");
|
||||
let _ = run_test(&mut card, test_reset, &[])?;
|
||||
|
||||
println!("Generate key");
|
||||
let _ = run_test(&mut card, test_keygen, &[])?;
|
||||
|
||||
panic!();
|
||||
|
||||
print!("Verify");
|
||||
let verify_out = run_test(&mut card, test_verify, &[])?;
|
||||
println!(" {:x?}", verify_out);
|
||||
|
@ -385,8 +414,6 @@ fn main() -> Result<()> {
|
|||
println!(" {:x?}", sign_out);
|
||||
}
|
||||
|
||||
// FIXME: generate keys
|
||||
|
||||
// FIXME: upload key with password
|
||||
|
||||
println!();
|
||||
|
|
|
@ -4,11 +4,12 @@
|
|||
//! This library supports using openpgp-card functionality with
|
||||
//! sequoia_openpgp data structures.
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::error::Error;
|
||||
use std::io;
|
||||
use std::time::SystemTime;
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use chrono::prelude::*;
|
||||
use openpgp::armor;
|
||||
use openpgp::cert::amalgamation::key::ValidErasedKeyAmalgamation;
|
||||
use openpgp::crypto::mpi;
|
||||
|
@ -23,8 +24,10 @@ use sequoia_openpgp as openpgp;
|
|||
use openpgp_card::card_app::CardApp;
|
||||
use openpgp_card::{
|
||||
errors::OpenpgpCardError, CardAdmin, CardUploadableKey, EccKey, EccType,
|
||||
KeyType, PrivateKeyMaterial, RSAKey,
|
||||
KeyType, PrivateKeyMaterial, PublicKeyMaterial, RSAKey,
|
||||
};
|
||||
use sequoia_openpgp::packet::key::{Key4, PublicParts};
|
||||
use sequoia_openpgp::types::Timestamp;
|
||||
|
||||
mod decryptor;
|
||||
mod signer;
|
||||
|
@ -67,6 +70,22 @@ pub fn vka_as_uploadable_key(
|
|||
Box::new(sqk)
|
||||
}
|
||||
|
||||
/// Helper fn: get a Key<PublicParts, UnspecifiedRole> for a PublicKeyMaterial
|
||||
pub fn public_key_material_to_key(
|
||||
pkm: &PublicKeyMaterial,
|
||||
time: SystemTime,
|
||||
) -> Result<Key<PublicParts, UnspecifiedRole>> {
|
||||
match pkm {
|
||||
PublicKeyMaterial::R(rsa) => {
|
||||
let k4: Key4<key::PublicParts, key::UnspecifiedRole> =
|
||||
Key4::import_public_rsa(&rsa.v, &rsa.n, Some(time))?;
|
||||
|
||||
Ok(Key::from(k4))
|
||||
}
|
||||
_ => unimplemented!("ECC not implemented yet"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Implement the `CardUploadableKey` trait that openpgp-card uses to
|
||||
/// upload (sub)keys to a card.
|
||||
impl CardUploadableKey for SequoiaKey {
|
||||
|
@ -144,9 +163,12 @@ impl CardUploadableKey for SequoiaKey {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_ts(&self) -> u64 {
|
||||
let key_creation: DateTime<Utc> = self.key.creation_time().into();
|
||||
key_creation.timestamp() as u64
|
||||
/// Number of non-leap seconds since January 1, 1970 0:00:00 UTC
|
||||
/// (aka "UNIX timestamp")
|
||||
fn get_ts(&self) -> u32 {
|
||||
let ts: Timestamp = Timestamp::try_from(self.key.creation_time())
|
||||
.expect("Creation time cannot be converted into u32 timestamp");
|
||||
ts.into()
|
||||
}
|
||||
|
||||
fn get_fp(&self) -> Vec<u8> {
|
||||
|
|
|
@ -126,3 +126,8 @@ pub fn decryption(data: Vec<u8>) -> Command {
|
|||
pub fn signature(data: Vec<u8>) -> Command {
|
||||
Command::new(0x00, 0x2A, 0x9e, 0x9a, data)
|
||||
}
|
||||
|
||||
/// Creates new APDU for "GENERATE ASYMMETRIC KEY PAIR"
|
||||
pub fn gen_key(data: Vec<u8>) -> Command {
|
||||
Command::new(0x00, 0x47, 0x80, 0x00, data)
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
use std::borrow::BorrowMut;
|
||||
use std::convert::TryFrom;
|
||||
use std::time::SystemTime;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
|
||||
|
@ -20,13 +21,12 @@ use crate::parse::{
|
|||
algo_attrs::Algo, algo_info::AlgoInfo, application_id::ApplicationId,
|
||||
cardholder::CardHolder, extended_cap::ExtendedCap,
|
||||
extended_length_info::ExtendedLengthInfo, fingerprint,
|
||||
historical::Historical, key_generation_times,
|
||||
key_generation_times::KeyGeneration, pw_status::PWStatus, KeySet,
|
||||
historical::Historical, key_generation_times, pw_status::PWStatus, KeySet,
|
||||
};
|
||||
use crate::tlv::{tag::Tag, Tlv, TlvEntry};
|
||||
use crate::{
|
||||
apdu, key_upload, CardCaps, CardClientBox, CardUploadableKey, DecryptMe,
|
||||
Hash, KeyType, Sex,
|
||||
apdu, keys, CardCaps, CardClientBox, CardUploadableKey, DecryptMe, Hash,
|
||||
KeyGeneration, KeyType, PublicKeyMaterial, Sex,
|
||||
};
|
||||
|
||||
pub struct CardApp {
|
||||
|
@ -524,6 +524,27 @@ impl CardApp {
|
|||
apdu::send_command(&mut self.card_client, put_url, false)
|
||||
}
|
||||
|
||||
pub fn set_creation_time(
|
||||
&mut self,
|
||||
time: u32,
|
||||
key_type: KeyType,
|
||||
) -> Result<Response, OpenpgpCardError> {
|
||||
// Timestamp update
|
||||
let time_value: Vec<u8> = time
|
||||
.to_be_bytes()
|
||||
.iter()
|
||||
.skip_while(|&&e| e == 0)
|
||||
.copied()
|
||||
.collect();
|
||||
|
||||
let time_cmd = commands::put_data(
|
||||
&[key_type.get_timestamp_put_tag()],
|
||||
time_value,
|
||||
);
|
||||
|
||||
apdu::send_command(&mut self.card_client, time_cmd, false)
|
||||
}
|
||||
|
||||
pub fn upload_key(
|
||||
&mut self,
|
||||
key: Box<dyn CardUploadableKey>,
|
||||
|
@ -539,6 +560,16 @@ impl CardApp {
|
|||
None
|
||||
};
|
||||
|
||||
key_upload::upload_key(self, key, key_type, algo_list)
|
||||
keys::upload_key(self, key, key_type, algo_list)
|
||||
}
|
||||
|
||||
// FIXME: use subset of CardUploadableKey to specify algo?
|
||||
pub fn generate_key(
|
||||
&mut self,
|
||||
fp_from_pub: fn(&PublicKeyMaterial, SystemTime) -> Result<[u8; 20]>,
|
||||
key_type: KeyType,
|
||||
) -> Result<(), OpenpgpCardError> {
|
||||
// FIXME: specify algo; pass in algo list?
|
||||
keys::gen_key_with_metadata(self, fp_from_pub, key_type)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
//! Generate and import keys
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use crate::apdu::command::Command;
|
||||
use crate::apdu::commands;
|
||||
|
@ -10,12 +13,109 @@ use crate::errors::OpenpgpCardError;
|
|||
use crate::parse::algo_attrs::{Algo, RsaAttrs};
|
||||
use crate::parse::algo_info::AlgoInfo;
|
||||
use crate::tlv::{tag::Tag, Tlv, TlvEntry};
|
||||
use crate::{apdu, CardClientBox};
|
||||
use crate::{apdu, EccPub, PublicKeyMaterial, RSAPub};
|
||||
use crate::{
|
||||
tlv, CardUploadableKey, EccKey, EccType, KeyType, PrivateKeyMaterial,
|
||||
RSAKey,
|
||||
};
|
||||
|
||||
/// `fp_from_pub` calculates the fingerprint for a public key data object
|
||||
pub(crate) fn gen_key_with_metadata(
|
||||
card_app: &mut CardApp,
|
||||
fp_from_pub: fn(&PublicKeyMaterial, SystemTime) -> Result<[u8; 20]>,
|
||||
key_type: KeyType,
|
||||
) -> Result<(), OpenpgpCardError> {
|
||||
let pubkey = gen_key(card_app, key_type)?;
|
||||
|
||||
// set creation time
|
||||
let time = SystemTime::now();
|
||||
|
||||
// Store creation timestamp (unix time format, limited to u32)
|
||||
let ts = time
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map_err(|e| OpenpgpCardError::InternalError(anyhow!(e)))?
|
||||
.as_secs() as u32;
|
||||
|
||||
card_app.set_creation_time(ts, key_type)?.check_ok()?;
|
||||
|
||||
// calculate/store fingerprint
|
||||
let fp = fp_from_pub(&pubkey, time)?;
|
||||
let fp_cmd =
|
||||
commands::put_data(&[key_type.get_fingerprint_put_tag()], fp.to_vec());
|
||||
|
||||
apdu::send_command(card_app.card(), fp_cmd, true)?.check_ok()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn tlv_to_pubkey(tlv: &Tlv) -> Result<PublicKeyMaterial> {
|
||||
let n = tlv.find(&Tag::new(vec![0x81]));
|
||||
let v = tlv.find(&Tag::new(vec![0x82]));
|
||||
|
||||
let ec = tlv.find(&Tag::new(vec![0x86]));
|
||||
|
||||
match (n, v, ec) {
|
||||
(Some(n), Some(v), None) => {
|
||||
let rsa = RSAPub {
|
||||
n: n.serialize(),
|
||||
v: v.serialize(),
|
||||
};
|
||||
|
||||
Ok(PublicKeyMaterial::R(rsa))
|
||||
}
|
||||
(None, None, Some(ec)) => {
|
||||
let ec = ec.serialize();
|
||||
|
||||
// The public key for ECDSA/DH consists of of two raw
|
||||
// big-endian integers with the same length as a field element
|
||||
// each. In compliance with EN 419212 the format is 04 || x || y
|
||||
// where the first byte (04) indicates an uncompressed raw format.
|
||||
|
||||
assert_eq!(ec[0], 0x4);
|
||||
|
||||
let len = ec.len();
|
||||
assert_eq!(len % 2, 1); // odd number of bytes
|
||||
|
||||
// len 3 -> 4/2 = 2
|
||||
let middle = (len + 1) / 2;
|
||||
let x = ec[1..middle].to_vec();
|
||||
let y = ec[middle..].to_vec();
|
||||
|
||||
let ecc = EccPub { x, y };
|
||||
|
||||
Ok(PublicKeyMaterial::E(ecc))
|
||||
}
|
||||
|
||||
(_, _, _) => {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn gen_key(
|
||||
card_app: &mut CardApp,
|
||||
key_type: KeyType,
|
||||
) -> Result<PublicKeyMaterial, OpenpgpCardError> {
|
||||
println!("gen key for {:?}", key_type);
|
||||
|
||||
// generate key
|
||||
let crt = get_crt(key_type)?;
|
||||
let gen_key_cmd = commands::gen_key(crt.serialize().to_vec());
|
||||
|
||||
let card_client = card_app.card();
|
||||
|
||||
let resp = apdu::send_command(card_client, gen_key_cmd, true)?;
|
||||
resp.check_ok()?;
|
||||
|
||||
let tlv = Tlv::try_from(resp.data()?)?;
|
||||
|
||||
let pubkey = tlv_to_pubkey(&tlv)?;
|
||||
|
||||
println!("public {:x?}", pubkey);
|
||||
|
||||
Ok(pubkey)
|
||||
}
|
||||
|
||||
/// Upload an explicitly selected Key to the card as a specific KeyType.
|
||||
///
|
||||
/// The client needs to make sure that the key is suitable for `key_type`.
|
||||
|
@ -87,7 +187,7 @@ pub(crate) fn upload_key(
|
|||
};
|
||||
|
||||
copy_key_to_card(
|
||||
card_app.card(),
|
||||
card_app,
|
||||
key_type,
|
||||
key.get_ts(),
|
||||
key.get_fp(),
|
||||
|
@ -385,28 +485,17 @@ fn ecc_algo_attrs_cmd(
|
|||
}
|
||||
|
||||
fn copy_key_to_card(
|
||||
card_client: &mut CardClientBox,
|
||||
card_app: &mut CardApp,
|
||||
key_type: KeyType,
|
||||
ts: u64,
|
||||
ts: u32,
|
||||
fp: Vec<u8>,
|
||||
algo_cmd: Command,
|
||||
key_cmd: Command,
|
||||
) -> Result<(), OpenpgpCardError> {
|
||||
let fp_cmd = commands::put_data(&[key_type.get_fingerprint_put_tag()], fp);
|
||||
|
||||
// Timestamp update
|
||||
let time_value: Vec<u8> = ts
|
||||
.to_be_bytes()
|
||||
.iter()
|
||||
.skip_while(|&&e| e == 0)
|
||||
.copied()
|
||||
.collect();
|
||||
|
||||
// Generation date/time
|
||||
let time_cmd =
|
||||
commands::put_data(&[key_type.get_timestamp_put_tag()], time_value);
|
||||
|
||||
// Send all the commands
|
||||
let card_client = card_app.card();
|
||||
|
||||
// FIXME: Only write algo attributes to the card if "extended
|
||||
// capabilities" show that they are changeable!
|
||||
|
@ -414,7 +503,8 @@ fn copy_key_to_card(
|
|||
|
||||
apdu::send_command(card_client, key_cmd, false)?.check_ok()?;
|
||||
apdu::send_command(card_client, fp_cmd, false)?.check_ok()?;
|
||||
apdu::send_command(card_client, time_cmd, false)?.check_ok()?;
|
||||
|
||||
card_app.set_creation_time(ts, key_type)?.check_ok()?;
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -21,7 +21,7 @@ pub mod apdu;
|
|||
mod card;
|
||||
pub mod card_app;
|
||||
pub mod errors;
|
||||
mod key_upload;
|
||||
mod keys;
|
||||
mod parse;
|
||||
mod tlv;
|
||||
|
||||
|
@ -66,6 +66,16 @@ impl CardCaps {
|
|||
}
|
||||
}
|
||||
|
||||
/// An OpenPGP key generation Time
|
||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||
pub struct KeyGeneration(u32);
|
||||
|
||||
impl KeyGeneration {
|
||||
pub fn get(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Container for a hash value.
|
||||
/// These hash values can be signed by the card.
|
||||
pub enum Hash<'a> {
|
||||
|
@ -111,12 +121,37 @@ pub trait CardUploadableKey {
|
|||
fn get_key(&self) -> Result<PrivateKeyMaterial>;
|
||||
|
||||
/// timestamp of (sub)key creation
|
||||
fn get_ts(&self) -> u64;
|
||||
fn get_ts(&self) -> u32;
|
||||
|
||||
/// fingerprint
|
||||
fn get_fp(&self) -> Vec<u8>;
|
||||
}
|
||||
|
||||
/// Algorithm-independent container for public key material retrieved from
|
||||
/// an OpenPGP card
|
||||
#[derive(Debug)]
|
||||
pub enum PublicKeyMaterial {
|
||||
R(RSAPub),
|
||||
E(EccPub),
|
||||
}
|
||||
|
||||
/// RSA-specific container for public key material from an OpenPGP card.
|
||||
#[derive(Debug)]
|
||||
pub struct RSAPub {
|
||||
/// Modulus (a number denoted as n coded on x bytes)
|
||||
pub n: Vec<u8>,
|
||||
|
||||
/// Public exponent (a number denoted as v, e.g. 65537 dec.)
|
||||
pub v: Vec<u8>,
|
||||
}
|
||||
|
||||
/// ECC-specific container for public key material from an OpenPGP card.
|
||||
#[derive(Debug)]
|
||||
pub struct EccPub {
|
||||
pub x: Vec<u8>,
|
||||
pub y: Vec<u8>,
|
||||
}
|
||||
|
||||
/// Algorithm-independent container for private key material to upload to
|
||||
/// an OpenPGP card
|
||||
pub enum PrivateKeyMaterial {
|
||||
|
@ -678,7 +713,7 @@ impl CardAdmin {
|
|||
) -> Result<(), OpenpgpCardError> {
|
||||
let algo_list = self.list_supported_algo()?;
|
||||
|
||||
key_upload::upload_key(&mut self.card_app, key, key_type, algo_list)
|
||||
keys::upload_key(&mut self.card_app, key, key_type, algo_list)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,9 +7,7 @@ use nom::{combinator, number::complete as number, sequence};
|
|||
|
||||
use crate::errors::OpenpgpCardError;
|
||||
use crate::parse::KeySet;
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||
pub struct KeyGeneration(u32);
|
||||
use crate::KeyGeneration;
|
||||
|
||||
impl From<KeyGeneration> for DateTime<Utc> {
|
||||
fn from(kg: KeyGeneration) -> Self {
|
||||
|
|
Loading…
Reference in a new issue