Skip to content

Commit 6297bc1

Browse files
committed
[WIP] PKCS#8 support
Adds optional integration with `ed25519::pkcs8` with support for decoding/encoding `Keypair` from/to PKCS#8-encoded documents as well as `PublicKey` from SPKI-encoded documents.
1 parent 7cf7e5f commit 6297bc1

File tree

3 files changed

+146
-1
lines changed

3 files changed

+146
-1
lines changed

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ harness = false
5151
[features]
5252
default = ["std", "rand"]
5353
std = ["curve25519-dalek/std", "ed25519/std", "serde_crate/std", "sha2/std", "rand/std"]
54-
alloc = ["curve25519-dalek/alloc", "rand/alloc", "zeroize/alloc"]
54+
alloc = ["curve25519-dalek/alloc", "ed25519/alloc", "rand/alloc", "zeroize/alloc"]
5555
nightly = ["curve25519-dalek/nightly"]
5656
serde = ["serde_crate", "serde_bytes", "ed25519/serde"]
5757
batch = ["merlin", "rand/std"]
@@ -61,6 +61,9 @@ asm = ["sha2/asm"]
6161
# This features turns off stricter checking for scalar malleability in signatures
6262
legacy_compatibility = []
6363
simd_backend = ["curve25519-dalek/simd_backend"]
64+
pkcs8 = ["ed25519/pkcs8"]
65+
pem = ["alloc", "ed25519/pem", "pkcs8"]
6466

6567
[patch.crates-io]
6668
curve25519-dalek = { git = "https://github.com/dalek-cryptography/curve25519-dalek.git", branch = "release/4.0" }
69+
ed25519 = { git = "https://github.com/RustCrypto/signatures.git" }

src/keypair.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99

1010
//! ed25519 keypairs.
1111
12+
#[cfg(feature = "pkcs8")]
13+
use ed25519::pkcs8::{self, DecodePrivateKey};
14+
1215
#[cfg(feature = "rand")]
1316
use rand::{CryptoRng, RngCore};
1417

@@ -443,6 +446,76 @@ impl Verifier<ed25519::Signature> for Keypair {
443446
}
444447
}
445448

449+
impl TryFrom<&[u8]> for Keypair {
450+
type Error = SignatureError;
451+
452+
fn try_from(bytes: &[u8]) -> Result<Keypair, SignatureError> {
453+
Keypair::from_bytes(bytes)
454+
}
455+
}
456+
457+
#[cfg(feature = "pkcs8")]
458+
#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))]
459+
impl DecodePrivateKey for Keypair {}
460+
461+
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
462+
#[cfg_attr(docsrs, doc(cfg(feature = "alloc", feaure = "pkcs8")))]
463+
impl pkcs8::EncodePrivateKey for Keypair {
464+
fn to_pkcs8_der(&self) -> pkcs8::Result<pkcs8::SecretDocument> {
465+
pkcs8::KeypairBytes::from(self).to_pkcs8_der()
466+
}
467+
}
468+
469+
#[cfg(feature = "pkcs8")]
470+
#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))]
471+
impl TryFrom<pkcs8::KeypairBytes> for Keypair {
472+
type Error = pkcs8::Error;
473+
474+
fn try_from(pkcs8_key: pkcs8::KeypairBytes) -> pkcs8::Result<Self> {
475+
Keypair::try_from(&pkcs8_key)
476+
}
477+
}
478+
479+
#[cfg(feature = "pkcs8")]
480+
#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))]
481+
impl TryFrom<&pkcs8::KeypairBytes> for Keypair {
482+
type Error = pkcs8::Error;
483+
484+
fn try_from(pkcs8_key: &pkcs8::KeypairBytes) -> pkcs8::Result<Self> {
485+
// TODO(tarcieri): validate public key if present?
486+
Keypair::from_bytes(&pkcs8_key.secret_key).map_err(|_| pkcs8::Error::KeyMalformed)
487+
}
488+
}
489+
490+
#[cfg(feature = "pkcs8")]
491+
#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))]
492+
impl From<Keypair> for pkcs8::KeypairBytes {
493+
fn from(keypair: Keypair) -> pkcs8::KeypairBytes {
494+
pkcs8::KeypairBytes::from(&keypair)
495+
}
496+
}
497+
498+
#[cfg(feature = "pkcs8")]
499+
#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))]
500+
impl From<&Keypair> for pkcs8::KeypairBytes {
501+
fn from(keypair: &Keypair) -> pkcs8::KeypairBytes {
502+
pkcs8::KeypairBytes {
503+
secret_key: keypair.secret.to_bytes(),
504+
public_key: Some(pkcs8::PublicKeyBytes(keypair.public.to_bytes()))
505+
}
506+
}
507+
}
508+
509+
#[cfg(feature = "pkcs8")]
510+
#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))]
511+
impl TryFrom<pkcs8::PrivateKeyInfo<'_>> for Keypair {
512+
type Error = pkcs8::Error;
513+
514+
fn try_from(private_key: pkcs8::PrivateKeyInfo<'_>) -> pkcs8::Result<Self> {
515+
pkcs8::KeypairBytes::try_from(private_key)?.try_into()
516+
}
517+
}
518+
446519
#[cfg(feature = "serde")]
447520
impl Serialize for Keypair {
448521
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>

src/public.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ use ed25519::signature::Verifier;
2323

2424
pub use sha2::Sha512;
2525

26+
#[cfg(feature = "pkcs8")]
27+
use ed25519::pkcs8::{self, DecodePublicKey};
28+
2629
#[cfg(feature = "serde")]
2730
use serde::de::Error as SerdeError;
2831
#[cfg(feature = "serde")]
@@ -354,6 +357,72 @@ impl Verifier<ed25519::Signature> for PublicKey {
354357
}
355358
}
356359

360+
impl TryFrom<&[u8]> for PublicKey {
361+
type Error = SignatureError;
362+
363+
fn try_from(bytes: &[u8]) -> Result<PublicKey, SignatureError> {
364+
PublicKey::from_bytes(bytes)
365+
}
366+
}
367+
368+
#[cfg(feature = "pkcs8")]
369+
#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))]
370+
impl DecodePublicKey for PublicKey {}
371+
372+
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
373+
#[cfg_attr(docsrs, doc(cfg(feature = "alloc", feaure = "pkcs8")))]
374+
impl pkcs8::EncodePublicKey for PublicKey {
375+
fn to_public_key_der(&self) -> pkcs8::spki::Result<pkcs8::Document> {
376+
pkcs8::PublicKeyBytes::from(self).to_public_key_der()
377+
}
378+
}
379+
380+
#[cfg(feature = "pkcs8")]
381+
#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))]
382+
impl TryFrom<pkcs8::PublicKeyBytes> for PublicKey {
383+
type Error = pkcs8::spki::Error;
384+
385+
fn try_from(pkcs8_key: pkcs8::PublicKeyBytes) -> pkcs8::spki::Result<Self> {
386+
PublicKey::try_from(&pkcs8_key)
387+
}
388+
}
389+
390+
#[cfg(feature = "pkcs8")]
391+
#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))]
392+
impl TryFrom<&pkcs8::PublicKeyBytes> for PublicKey {
393+
type Error = pkcs8::spki::Error;
394+
395+
fn try_from(pkcs8_key: &pkcs8::PublicKeyBytes) -> pkcs8::spki::Result<Self> {
396+
PublicKey::from_bytes(pkcs8_key.as_ref()).map_err(|_| pkcs8::spki::Error::KeyMalformed)
397+
}
398+
}
399+
400+
#[cfg(feature = "pkcs8")]
401+
#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))]
402+
impl From<PublicKey> for pkcs8::PublicKeyBytes {
403+
fn from(public_key: PublicKey) -> pkcs8::PublicKeyBytes {
404+
pkcs8::PublicKeyBytes::from(&public_key)
405+
}
406+
}
407+
408+
#[cfg(feature = "pkcs8")]
409+
#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))]
410+
impl From<&PublicKey> for pkcs8::PublicKeyBytes {
411+
fn from(public_key: &PublicKey) -> pkcs8::PublicKeyBytes {
412+
pkcs8::PublicKeyBytes(public_key.to_bytes())
413+
}
414+
}
415+
416+
#[cfg(feature = "pkcs8")]
417+
#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))]
418+
impl TryFrom<pkcs8::spki::SubjectPublicKeyInfo<'_>> for PublicKey {
419+
type Error = pkcs8::spki::Error;
420+
421+
fn try_from(public_key: pkcs8::spki::SubjectPublicKeyInfo<'_>) -> pkcs8::spki::Result<Self> {
422+
pkcs8::PublicKeyBytes::try_from(public_key)?.try_into()
423+
}
424+
}
425+
357426
#[cfg(feature = "serde")]
358427
impl Serialize for PublicKey {
359428
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>

0 commit comments

Comments
 (0)