Pad private key scalars of ECC keys.

MPIs can have leading zeros stripped, in OpenPGP, however, e.g. the floss34 card requires the NIST scalar in its non-stripped form.
This commit is contained in:
Heiko Schaefer 2021-09-23 20:54:33 +02:00
parent c73a4fa2f8
commit 1ce74ab8c6
3 changed files with 37 additions and 30 deletions

View file

@ -20,6 +20,9 @@ use openpgp_card::crypto_data::{
CardUploadableKey, EccKey, EccType, PrivateKeyMaterial, RSAKey, CardUploadableKey, EccKey, EccType, PrivateKeyMaterial, RSAKey,
}; };
use openpgp_card::Error; use openpgp_card::Error;
use sequoia_openpgp::types::Curve;
use crate::util;
/// A SequoiaKey represents the private cryptographic key material of an /// A SequoiaKey represents the private cryptographic key material of an
/// OpenPGP (sub)key to be uploaded to an OpenPGP card. /// OpenPGP (sub)key to be uploaded to an OpenPGP card.
@ -83,12 +86,7 @@ impl CardUploadableKey for SequoiaKey {
mpi::PublicKey::ECDH { curve, q, .. }, mpi::PublicKey::ECDH { curve, q, .. },
mpi::SecretKeyMaterial::ECDH { scalar }, mpi::SecretKeyMaterial::ECDH { scalar },
) => { ) => {
let sq_ecc = SqEccKey::new( let sq_ecc = SqEccKey::new(curve, scalar, q, EccType::ECDH);
curve.oid().to_vec(),
scalar,
q,
EccType::ECDH,
);
Ok(PrivateKeyMaterial::E(Box::new(sq_ecc))) Ok(PrivateKeyMaterial::E(Box::new(sq_ecc)))
} }
@ -96,12 +94,7 @@ impl CardUploadableKey for SequoiaKey {
mpi::PublicKey::ECDSA { curve, q, .. }, mpi::PublicKey::ECDSA { curve, q, .. },
mpi::SecretKeyMaterial::ECDSA { scalar }, mpi::SecretKeyMaterial::ECDSA { scalar },
) => { ) => {
let sq_ecc = SqEccKey::new( let sq_ecc = SqEccKey::new(curve, scalar, q, EccType::ECDSA);
curve.oid().to_vec(),
scalar,
q,
EccType::ECDSA,
);
Ok(PrivateKeyMaterial::E(Box::new(sq_ecc))) Ok(PrivateKeyMaterial::E(Box::new(sq_ecc)))
} }
@ -109,19 +102,13 @@ impl CardUploadableKey for SequoiaKey {
mpi::PublicKey::EdDSA { curve, q, .. }, mpi::PublicKey::EdDSA { curve, q, .. },
mpi::SecretKeyMaterial::EdDSA { scalar }, mpi::SecretKeyMaterial::EdDSA { scalar },
) => { ) => {
let sq_ecc = SqEccKey::new( let sq_ecc = SqEccKey::new(curve, scalar, q, EccType::EdDSA);
curve.oid().to_vec(),
scalar,
q,
EccType::EdDSA,
);
Ok(PrivateKeyMaterial::E(Box::new(sq_ecc))) Ok(PrivateKeyMaterial::E(Box::new(sq_ecc)))
} }
(p, s) => { (p, s) => {
unimplemented!( unimplemented!(
"Unexpected algorithms: public {:?}, \ "Unexpected algorithms: public {:?}, secret {:?}",
secret {:?}",
p, p,
s s
); );
@ -208,7 +195,7 @@ impl RSAKey for SqRSA {
/// ECC-specific data-structure to hold private (sub)key material for upload /// ECC-specific data-structure to hold private (sub)key material for upload
/// with the `openpgp-card` crate. /// with the `openpgp-card` crate.
struct SqEccKey { struct SqEccKey {
oid: Vec<u8>, curve: Curve,
private: ProtectedMPI, private: ProtectedMPI,
public: MPI, public: MPI,
ecc_type: EccType, ecc_type: EccType,
@ -216,13 +203,13 @@ struct SqEccKey {
impl SqEccKey { impl SqEccKey {
fn new( fn new(
oid: Vec<u8>, curve: Curve,
private: ProtectedMPI, private: ProtectedMPI,
public: MPI, public: MPI,
ecc_type: EccType, ecc_type: EccType,
) -> Self { ) -> Self {
SqEccKey { SqEccKey {
oid, curve,
private, private,
public, public,
ecc_type, ecc_type,
@ -232,15 +219,22 @@ impl SqEccKey {
impl EccKey for SqEccKey { impl EccKey for SqEccKey {
fn get_oid(&self) -> &[u8] { fn get_oid(&self) -> &[u8] {
&self.oid self.curve.oid()
} }
fn get_private(&self) -> &[u8] { fn get_private(&self) -> Vec<u8> {
self.private.value() // FIXME: padding for 25519?
match self.curve {
Curve::NistP256 => util::left_zero_pad(self.private.value(), 0x20),
Curve::NistP384 => util::left_zero_pad(self.private.value(), 0x30),
Curve::NistP521 => util::left_zero_pad(self.private.value(), 0x42),
_ => self.private.value().to_vec(),
}
} }
fn get_public(&self) -> &[u8] { fn get_public(&self) -> Vec<u8> {
self.public.value() // FIXME: padding?
self.public.value().to_vec()
} }
fn get_type(&self) -> EccType { fn get_type(&self) -> EccType {

View file

@ -305,3 +305,16 @@ pub fn decrypt(
Ok(decrypted) Ok(decrypted)
} }
/// This fn prepends zeros to `value` so that the resulting Vec<u8> has
/// len `size`.
///
/// (Leading zero-bytes may be stripped from MPIs, this fn is a helper for
/// re-creating the non-stripped representation of an MPI)
pub(crate) fn left_zero_pad(value: &[u8], size: usize) -> Vec<u8> {
let pad = size - value.len();
let mut res = vec![0; pad];
res.extend_from_slice(value);
res
}

View file

@ -100,8 +100,8 @@ pub trait RSAKey {
/// card. /// card.
pub trait EccKey { pub trait EccKey {
fn get_oid(&self) -> &[u8]; fn get_oid(&self) -> &[u8];
fn get_private(&self) -> &[u8]; fn get_private(&self) -> Vec<u8>;
fn get_public(&self) -> &[u8]; fn get_public(&self) -> Vec<u8>;
fn get_type(&self) -> EccType; fn get_type(&self) -> EccType;
} }