Ed25519::{from_pkcs8, from_pkcs8_maybe_unchecked}: Also accept correct tagging public key.
See the added API documentation for more details. Also update the test private key to the standard format.
This commit is contained in:
parent
ff03b734f9
commit
8dc1b93b24
@ -71,11 +71,18 @@ impl Ed25519KeyPair {
|
|||||||
/// verify that the public key and the private key are consistent with each
|
/// verify that the public key and the private key are consistent with each
|
||||||
/// other.
|
/// other.
|
||||||
///
|
///
|
||||||
|
/// Some early implementations of PKCS#8 v2, including earlier versions of
|
||||||
|
/// *ring* and other implementations, wrapped the public key in the wrong
|
||||||
|
/// ASN.1 tags. Both that incorrect form and the standardized form are
|
||||||
|
/// accepted.
|
||||||
|
///
|
||||||
/// If you need to parse PKCS#8 v1 files (without the public key) then use
|
/// If you need to parse PKCS#8 v1 files (without the public key) then use
|
||||||
/// `Ed25519KeyPair::from_pkcs8_maybe_unchecked()` instead.
|
/// `Ed25519KeyPair::from_pkcs8_maybe_unchecked()` instead.
|
||||||
pub fn from_pkcs8(pkcs8: &[u8]) -> Result<Self, error::KeyRejected> {
|
pub fn from_pkcs8(pkcs8: &[u8]) -> Result<Self, error::KeyRejected> {
|
||||||
let (seed, public_key) =
|
let version = pkcs8::Version::V2Only(pkcs8::PublicKeyOptions {
|
||||||
unwrap_pkcs8(pkcs8::Version::V2Only, untrusted::Input::from(pkcs8))?;
|
accept_legacy_ed25519_public_key_tag: true,
|
||||||
|
});
|
||||||
|
let (seed, public_key) = unwrap_pkcs8(version, untrusted::Input::from(pkcs8))?;
|
||||||
Self::from_seed_and_public_key(
|
Self::from_seed_and_public_key(
|
||||||
seed.as_slice_less_safe(),
|
seed.as_slice_less_safe(),
|
||||||
public_key.unwrap().as_slice_less_safe(),
|
public_key.unwrap().as_slice_less_safe(),
|
||||||
@ -95,10 +102,17 @@ impl Ed25519KeyPair {
|
|||||||
/// computed from the private key, and there will be no consistency check
|
/// computed from the private key, and there will be no consistency check
|
||||||
/// between the public key and the private key.
|
/// between the public key and the private key.
|
||||||
///
|
///
|
||||||
|
/// Some early implementations of PKCS#8 v2, including earlier versions of
|
||||||
|
/// *ring* and other implementations, wrapped the public key in the wrong
|
||||||
|
/// ASN.1 tags. Both that incorrect form and the standardized form are
|
||||||
|
/// accepted.
|
||||||
|
///
|
||||||
/// PKCS#8 v2 files are parsed exactly like `Ed25519KeyPair::from_pkcs8()`.
|
/// PKCS#8 v2 files are parsed exactly like `Ed25519KeyPair::from_pkcs8()`.
|
||||||
pub fn from_pkcs8_maybe_unchecked(pkcs8: &[u8]) -> Result<Self, error::KeyRejected> {
|
pub fn from_pkcs8_maybe_unchecked(pkcs8: &[u8]) -> Result<Self, error::KeyRejected> {
|
||||||
let (seed, public_key) =
|
let version = pkcs8::Version::V1OrV2(pkcs8::PublicKeyOptions {
|
||||||
unwrap_pkcs8(pkcs8::Version::V1OrV2, untrusted::Input::from(pkcs8))?;
|
accept_legacy_ed25519_public_key_tag: true,
|
||||||
|
});
|
||||||
|
let (seed, public_key) = unwrap_pkcs8(version, untrusted::Input::from(pkcs8))?;
|
||||||
if let Some(public_key) = public_key {
|
if let Some(public_key) = public_key {
|
||||||
Self::from_seed_and_public_key(
|
Self::from_seed_and_public_key(
|
||||||
seed.as_slice_less_safe(),
|
seed.as_slice_less_safe(),
|
||||||
|
@ -35,6 +35,8 @@ pub enum Tag {
|
|||||||
UTCTime = 0x17,
|
UTCTime = 0x17,
|
||||||
GeneralizedTime = 0x18,
|
GeneralizedTime = 0x18,
|
||||||
|
|
||||||
|
ContextSpecific1 = CONTEXT_SPECIFIC | 1,
|
||||||
|
|
||||||
ContextSpecificConstructed0 = CONTEXT_SPECIFIC | CONSTRUCTED | 0,
|
ContextSpecificConstructed0 = CONTEXT_SPECIFIC | CONSTRUCTED | 0,
|
||||||
ContextSpecificConstructed1 = CONTEXT_SPECIFIC | CONSTRUCTED | 1,
|
ContextSpecificConstructed1 = CONTEXT_SPECIFIC | CONSTRUCTED | 1,
|
||||||
ContextSpecificConstructed3 = CONTEXT_SPECIFIC | CONSTRUCTED | 3,
|
ContextSpecificConstructed3 = CONTEXT_SPECIFIC | CONSTRUCTED | 3,
|
||||||
@ -101,10 +103,18 @@ pub fn read_tag_and_get_value<'a>(
|
|||||||
Ok((tag, inner))
|
Ok((tag, inner))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn bit_string_with_no_unused_bits<'a>(
|
pub fn bit_string_with_no_unused_bits<'a>(
|
||||||
input: &mut untrusted::Reader<'a>,
|
input: &mut untrusted::Reader<'a>,
|
||||||
) -> Result<untrusted::Input<'a>, error::Unspecified> {
|
) -> Result<untrusted::Input<'a>, error::Unspecified> {
|
||||||
nested(input, Tag::BitString, error::Unspecified, |value| {
|
bit_string_tagged_with_no_unused_bits(Tag::BitString, input)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn bit_string_tagged_with_no_unused_bits<'a>(
|
||||||
|
tag: Tag,
|
||||||
|
input: &mut untrusted::Reader<'a>,
|
||||||
|
) -> Result<untrusted::Input<'a>, error::Unspecified> {
|
||||||
|
nested(input, tag, error::Unspecified, |value| {
|
||||||
let unused_bits_at_end = value.read_byte().map_err(|_| error::Unspecified)?;
|
let unused_bits_at_end = value.read_byte().map_err(|_| error::Unspecified)?;
|
||||||
if unused_bits_at_end != 0 {
|
if unused_bits_at_end != 0 {
|
||||||
return Err(error::Unspecified);
|
return Err(error::Unspecified);
|
||||||
|
42
src/pkcs8.rs
42
src/pkcs8.rs
@ -18,10 +18,16 @@
|
|||||||
|
|
||||||
use crate::{ec, error, io::der};
|
use crate::{ec, error, io::der};
|
||||||
|
|
||||||
|
pub(crate) struct PublicKeyOptions {
|
||||||
|
/// Should the wrong public key ASN.1 tagging used by early implementations
|
||||||
|
/// of PKCS#8 v2 (including earlier versions of *ring*) be accepted?
|
||||||
|
pub accept_legacy_ed25519_public_key_tag: bool,
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) enum Version {
|
pub(crate) enum Version {
|
||||||
V1Only,
|
V1Only,
|
||||||
V1OrV2,
|
V1OrV2(PublicKeyOptions),
|
||||||
V2Only,
|
V2Only(PublicKeyOptions),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A template for constructing PKCS#8 documents.
|
/// A template for constructing PKCS#8 documents.
|
||||||
@ -123,10 +129,10 @@ fn unwrap_key__<'a>(
|
|||||||
return Err(error::KeyRejected::wrong_algorithm());
|
return Err(error::KeyRejected::wrong_algorithm());
|
||||||
}
|
}
|
||||||
|
|
||||||
let require_public_key = match (actual_version, version) {
|
let public_key_options = match (actual_version, version) {
|
||||||
(0, Version::V1Only) => false,
|
(0, Version::V1Only) => None,
|
||||||
(0, Version::V1OrV2) => false,
|
(0, Version::V1OrV2(_)) => None,
|
||||||
(1, Version::V1OrV2) | (1, Version::V2Only) => true,
|
(1, Version::V1OrV2(options)) | (1, Version::V2Only(options)) => Some(options),
|
||||||
_ => {
|
_ => {
|
||||||
return Err(error::KeyRejected::version_not_supported());
|
return Err(error::KeyRejected::version_not_supported());
|
||||||
}
|
}
|
||||||
@ -141,17 +147,25 @@ fn unwrap_key__<'a>(
|
|||||||
.map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?;
|
.map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let public_key = if require_public_key {
|
let public_key = if let Some(options) = public_key_options {
|
||||||
if input.at_end() {
|
if input.at_end() {
|
||||||
return Err(error::KeyRejected::public_key_is_missing());
|
return Err(error::KeyRejected::public_key_is_missing());
|
||||||
}
|
}
|
||||||
let public_key = der::nested(
|
|
||||||
input,
|
const INCORRECT_LEGACY: der::Tag = der::Tag::ContextSpecificConstructed1;
|
||||||
der::Tag::ContextSpecificConstructed1,
|
let result =
|
||||||
error::Unspecified,
|
if options.accept_legacy_ed25519_public_key_tag && input.peek(INCORRECT_LEGACY as u8) {
|
||||||
der::bit_string_with_no_unused_bits,
|
der::nested(
|
||||||
)
|
input,
|
||||||
.map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?;
|
INCORRECT_LEGACY,
|
||||||
|
error::Unspecified,
|
||||||
|
der::bit_string_with_no_unused_bits,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
der::bit_string_tagged_with_no_unused_bits(der::Tag::ContextSpecific1, input)
|
||||||
|
};
|
||||||
|
let public_key =
|
||||||
|
result.map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?;
|
||||||
Some(public_key)
|
Some(public_key)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user