1
0
mirror of https://github.com/BitskiCo/jwk-rs synced 2024-11-24 21:09:22 +00:00

Use stable Rust

This commit is contained in:
Nick Hynes 2021-01-25 20:11:01 +03:00
parent 8014d18bf0
commit 9592cad01c
No known key found for this signature in database
GPG Key ID: 5B3463E9F1D73C83
8 changed files with 136 additions and 147 deletions

View File

@ -11,7 +11,7 @@ jobs:
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
profile: minimal profile: minimal
toolchain: nightly toolchain: stable
override: true override: true
components: rustfmt, clippy components: rustfmt, clippy
@ -41,7 +41,7 @@ jobs:
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
profile: minimal profile: minimal
toolchain: nightly toolchain: stable
override: true override: true
- name: Build (release) - name: Build (release)
@ -58,7 +58,7 @@ jobs:
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
profile: minimal profile: minimal
toolchain: nightly toolchain: stable
override: true override: true
- uses: actions-rs/tarpaulin@v0.1 - uses: actions-rs/tarpaulin@v0.1

View File

@ -1,6 +1,6 @@
[package] [package]
name = "jsonwebkey" name = "jsonwebkey"
version = "0.2.0" version = "0.3.0"
authors = ["Nick Hynes <nhynes@nhynes.com>"] authors = ["Nick Hynes <nhynes@nhynes.com>"]
description = "JSON Web Key (JWK) (de)serialization, generation, and conversion." description = "JSON Web Key (JWK) (de)serialization, generation, and conversion."
readme = "README.md" readme = "README.md"
@ -12,6 +12,7 @@ edition = "2018"
base64 = "0.12" base64 = "0.12"
bitflags = "1.2" bitflags = "1.2"
derive_more = "0.99" derive_more = "0.99"
generic-array = "0.14"
jsonwebtoken = { version = "7.2", optional = true } jsonwebtoken = { version = "7.2", optional = true }
num-bigint = { version = "0.2", optional = true } num-bigint = { version = "0.2", optional = true }
p256 = { version = "0.3", optional = true } p256 = { version = "0.3", optional = true }
@ -30,3 +31,6 @@ generate = ["p256", "rand"]
[dev-dependencies] [dev-dependencies]
jsonwebtoken = "7.2" jsonwebtoken = "7.2"
[package.metadata.docs.rs]
all-features = true

View File

@ -6,8 +6,6 @@
*[JSON Web Key (JWK)](https://tools.ietf.org/html/rfc7517#section-4.3) (de)serialization, generation, and conversion.* *[JSON Web Key (JWK)](https://tools.ietf.org/html/rfc7517#section-4.3) (de)serialization, generation, and conversion.*
Note: requires rustc nightly >= 1.45 for conveniences around fixed-size arrays.
**Goals** **Goals**
tl;dr: get keys into a format that can be used by other crates; be as safe as possible while doing so. tl;dr: get keys into a format that can be used by other crates; be as safe as possible while doing so.

View File

@ -1,61 +1,55 @@
use std::{array::FixedSizeArray, fmt}; use derive_more::{AsRef, Deref};
use generic_array::{ArrayLength, GenericArray};
use derive_more::{AsRef, Deref, From}; use serde::de::{self, Deserialize, Deserializer};
use serde::{
de::{self, Deserialize, Deserializer},
ser::{Serialize, Serializer},
};
use zeroize::{Zeroize, Zeroizing}; use zeroize::{Zeroize, Zeroizing};
use crate::utils::{deserialize_base64, serialize_base64};
/// A zeroizing-on-drop container for a `[u8; N]` that deserializes from base64. /// A zeroizing-on-drop container for a `[u8; N]` that deserializes from base64.
#[derive(Clone, Zeroize, Deref, AsRef, From)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deref, AsRef)]
#[zeroize(drop)] #[serde(transparent)]
pub struct ByteArray<const N: usize>(pub [u8; N]); pub struct ByteArray<N: ArrayLength<u8>>(
#[serde(serialize_with = "crate::utils::serde_base64::serialize")] pub GenericArray<u8, N>,
);
impl<const N: usize> fmt::Debug for ByteArray<N> { impl<N: ArrayLength<u8>, T: Into<GenericArray<u8, N>>> From<T> for ByteArray<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn from(arr: T) -> Self {
if cfg!(debug_assertions) { Self(arr.into())
write!(f, "{}", base64::encode(self.0.as_slice()))
} else {
write!(f, "ByteArray<{}>", N)
}
} }
} }
impl<const N: usize> PartialEq for ByteArray<N> { impl<N: ArrayLength<u8>> Drop for ByteArray<N> {
fn eq(&self, other: &Self) -> bool { fn drop(&mut self) {
self.0.as_slice() == other.0.as_slice() Zeroize::zeroize(self.0.as_mut_slice())
} }
} }
impl<const N: usize> Eq for ByteArray<N> {} impl<N: ArrayLength<u8>> ByteArray<N> {
/// An unwrapping version of `try_from_slice`.
pub fn from_slice(bytes: impl AsRef<[u8]>) -> Self {
Self::try_from_slice(bytes).unwrap()
}
impl<const N: usize> ByteArray<N> {
pub fn try_from_slice(bytes: impl AsRef<[u8]>) -> Result<Self, String> { pub fn try_from_slice(bytes: impl AsRef<[u8]>) -> Result<Self, String> {
let mut arr = Self([0u8; N]);
let bytes = bytes.as_ref(); let bytes = bytes.as_ref();
if bytes.len() != N { if bytes.len() != N::USIZE {
Err(format!("expected {} bytes but got {}", N, bytes.len())) Err(format!(
"expected {} bytes but got {}",
N::USIZE,
bytes.len()
))
} else { } else {
arr.0.copy_from_slice(bytes); Ok(Self(GenericArray::clone_from_slice(bytes)))
Ok(arr)
} }
} }
} }
impl<const N: usize> Serialize for ByteArray<N> { impl<'de, N: ArrayLength<u8>> Deserialize<'de> for ByteArray<N> {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
serialize_base64(self.0.as_slice(), s)
}
}
impl<'de, const N: usize> Deserialize<'de> for ByteArray<N> {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> { fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
let bytes = Zeroizing::new(deserialize_base64(d)?); let bytes = Zeroizing::new(crate::utils::serde_base64::deserialize(d)?);
Self::try_from_slice(&*bytes).map_err(|_| { Self::try_from_slice(&*bytes).map_err(|_| {
de::Error::invalid_length(bytes.len(), &format!("{} base64-encoded bytes", N).as_str()) de::Error::invalid_length(
bytes.len(),
&format!("{} base64-encoded bytes", N::USIZE).as_str(),
)
}) })
} }
} }
@ -64,6 +58,8 @@ impl<'de, const N: usize> Deserialize<'de> for ByteArray<N> {
mod tests { mod tests {
use super::*; use super::*;
use generic_array::typenum::*;
static BYTES: &[u8] = &[1, 2, 3, 4, 5, 6, 7]; static BYTES: &[u8] = &[1, 2, 3, 4, 5, 6, 7];
static BASE64_JSON: &str = "\"AQIDBAUGBw==\""; static BASE64_JSON: &str = "\"AQIDBAUGBw==\"";
@ -73,26 +69,26 @@ mod tests {
#[test] #[test]
fn test_serde_byte_array_good() { fn test_serde_byte_array_good() {
let arr = ByteArray::<7>::try_from_slice(BYTES).unwrap(); let arr = ByteArray::<U7>::try_from_slice(BYTES).unwrap();
let b64 = serde_json::to_string(&arr).unwrap(); let b64 = serde_json::to_string(&arr).unwrap();
assert_eq!(b64, BASE64_JSON); assert_eq!(b64, BASE64_JSON);
let bytes: ByteArray<7> = serde_json::from_str(&b64).unwrap(); let bytes: ByteArray<U7> = serde_json::from_str(&b64).unwrap();
assert_eq!(bytes.as_ref(), BYTES); assert_eq!(bytes.as_slice(), BYTES);
} }
#[test] #[test]
fn test_serde_deserialize_byte_array_invalid() { fn test_serde_deserialize_byte_array_invalid() {
let mut de = serde_json::Deserializer::from_str("\"Z\""); let mut de = serde_json::Deserializer::from_str("\"Z\"");
ByteArray::<0>::deserialize(&mut de).unwrap_err(); ByteArray::<U0>::deserialize(&mut de).unwrap_err();
} }
#[test] #[test]
fn test_serde_base64_deserialize_array_long() { fn test_serde_base64_deserialize_array_long() {
ByteArray::<6>::deserialize(&mut get_de()).unwrap_err(); ByteArray::<U6>::deserialize(&mut get_de()).unwrap_err();
} }
#[test] #[test]
fn test_serde_base64_deserialize_array_short() { fn test_serde_base64_deserialize_array_short() {
ByteArray::<8>::deserialize(&mut get_de()).unwrap_err(); ByteArray::<U8>::deserialize(&mut get_de()).unwrap_err();
} }
} }

View File

@ -1,18 +1,13 @@
use std::fmt; use std::fmt;
use derive_more::{AsRef, Deref, From}; use derive_more::{AsRef, Deref, From};
use serde::{
de::{Deserialize, Deserializer},
ser::{Serialize, Serializer},
};
use zeroize::Zeroize; use zeroize::Zeroize;
use crate::utils::{deserialize_base64, serialize_base64};
/// A zeroizing-on-drop container for a `Vec<u8>` that deserializes from base64. /// A zeroizing-on-drop container for a `Vec<u8>` that deserializes from base64.
#[derive(Clone, PartialEq, Eq, Zeroize, Deref, AsRef, From)] #[derive(Clone, PartialEq, Eq, Zeroize, Serialize, Deserialize, Deref, AsRef, From)]
#[zeroize(drop)] #[zeroize(drop)]
pub struct ByteVec(pub Vec<u8>); #[serde(transparent)]
pub struct ByteVec(#[serde(with = "crate::utils::serde_base64")] pub Vec<u8>);
impl fmt::Debug for ByteVec { impl fmt::Debug for ByteVec {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -24,18 +19,6 @@ impl fmt::Debug for ByteVec {
} }
} }
impl Serialize for ByteVec {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
serialize_base64(&self.0, s)
}
}
impl<'de> Deserialize<'de> for ByteVec {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
Ok(Self(deserialize_base64(d)?))
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -1,5 +1,5 @@
#![allow(incomplete_features)] #![deny(rust_2018_idioms, unreachable_pub)]
#![feature(box_syntax, const_generics, fixed_size_array)] #![forbid(unsafe_code)]
//! *[JSON Web Key (JWK)](https://tools.ietf.org/html/rfc7517#section-4.3) (de)serialization, generation, and conversion.* //! *[JSON Web Key (JWK)](https://tools.ietf.org/html/rfc7517#section-4.3) (de)serialization, generation, and conversion.*
//! //!
@ -58,6 +58,9 @@
//! This pulls in the [p256](https://crates.io/crates/p256) and [rand](https://crates.io/crates/rand) crates. //! This pulls in the [p256](https://crates.io/crates/p256) and [rand](https://crates.io/crates/rand) crates.
//! * `jsonwebtoken` - enables conversions to types in the [jsonwebtoken](https://crates.io/crates/jsonwebtoken) crate. //! * `jsonwebtoken` - enables conversions to types in the [jsonwebtoken](https://crates.io/crates/jsonwebtoken) crate.
#[macro_use]
extern crate serde;
mod byte_array; mod byte_array;
mod byte_vec; mod byte_vec;
mod key_ops; mod key_ops;
@ -67,6 +70,7 @@ mod utils;
use std::borrow::Cow; use std::borrow::Cow;
use generic_array::typenum::U32;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
pub use byte_array::ByteArray; pub use byte_array::ByteArray;
@ -94,7 +98,7 @@ pub struct JsonWebKey {
impl JsonWebKey { impl JsonWebKey {
pub fn new(key: Key) -> Self { pub fn new(key: Key) -> Self {
Self { Self {
key: box key, key: Box::new(key),
key_use: None, key_use: None,
key_ops: KeyOps::empty(), key_ops: KeyOps::empty(),
key_id: None, key_id: None,
@ -143,7 +147,7 @@ impl std::str::FromStr for JsonWebKey {
} }
impl std::fmt::Display for JsonWebKey { impl std::fmt::Display for JsonWebKey {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if f.alternate() { if f.alternate() {
write!(f, "{}", serde_json::to_string_pretty(self).unwrap()) write!(f, "{}", serde_json::to_string_pretty(self).unwrap())
} else { } else {
@ -179,7 +183,7 @@ pub enum Key {
impl Key { impl Key {
/// Returns true iff this key only contains private components (i.e. a private asymmetric /// Returns true iff this key only contains private components (i.e. a private asymmetric
/// key or a symmetric key). /// key or a symmetric key).
fn is_private(&self) -> bool { pub fn is_private(&self) -> bool {
match self { match self {
Self::Symmetric { .. } Self::Symmetric { .. }
| Self::EC { | Self::EC {
@ -194,7 +198,7 @@ impl Key {
} }
/// Returns the public part of this key (symmetric keys have no public parts). /// Returns the public part of this key (symmetric keys have no public parts).
pub fn to_public(&self) -> Option<Cow<Self>> { pub fn to_public(&self) -> Option<Cow<'_, Self>> {
if !self.is_private() { if !self.is_private() {
return Some(Cow::Borrowed(self)); return Some(Cow::Borrowed(self));
} }
@ -236,7 +240,7 @@ impl Key {
let prime256v1_oid = ObjectIdentifier::from_slice(&[1, 2, 840, 10045, 3, 1, 7]); let prime256v1_oid = ObjectIdentifier::from_slice(&[1, 2, 840, 10045, 3, 1, 7]);
let oids = &[Some(&ec_public_oid), Some(&prime256v1_oid)]; let oids = &[Some(&ec_public_oid), Some(&prime256v1_oid)];
let write_public = |writer: DERWriter| { let write_public = |writer: DERWriter<'_>| {
let public_bytes: Vec<u8> = [0x04 /* uncompressed */] let public_bytes: Vec<u8> = [0x04 /* uncompressed */]
.iter() .iter()
.chain(x.iter()) .chain(x.iter())
@ -248,7 +252,7 @@ impl Key {
match d { match d {
Some(private_point) => { Some(private_point) => {
pkcs8::write_private(oids, |writer: &mut DERWriterSeq| { pkcs8::write_private(oids, |writer: &mut DERWriterSeq<'_>| {
writer.next().write_i8(1); // version writer.next().write_i8(1); // version
writer.next().write_bytes(&**private_point); writer.next().write_bytes(&**private_point);
// The following tagged value is optional. OpenSSL produces it, // The following tagged value is optional. OpenSSL produces it,
@ -268,17 +272,17 @@ impl Key {
1, 2, 840, 113549, 1, 1, 1, // rsaEncryption 1, 2, 840, 113549, 1, 1, 1, // rsaEncryption
]); ]);
let oids = &[Some(&rsa_encryption_oid), None]; let oids = &[Some(&rsa_encryption_oid), None];
let write_bytevec = |writer: DERWriter, vec: &ByteVec| { let write_bytevec = |writer: DERWriter<'_>, vec: &ByteVec| {
let bigint = BigUint::from_bytes_be(vec.as_slice()); let bigint = BigUint::from_bytes_be(vec.as_slice());
writer.write_biguint(&bigint); writer.write_biguint(&bigint);
}; };
let write_public = |writer: &mut DERWriterSeq| { let write_public = |writer: &mut DERWriterSeq<'_>| {
write_bytevec(writer.next(), &public.n); write_bytevec(writer.next(), &public.n);
writer.next().write_u32(PUBLIC_EXPONENT); writer.next().write_u32(PUBLIC_EXPONENT);
}; };
let write_private = |writer: &mut DERWriterSeq, private: &RsaPrivate| { let write_private = |writer: &mut DERWriterSeq<'_>, private: &RsaPrivate| {
// https://tools.ietf.org/html/rfc3447#appendix-A.1.2 // https://tools.ietf.org/html/rfc3447#appendix-A.1.2
writer.next().write_i8(0); // version (two-prime) writer.next().write_i8(0); // version (two-prime)
write_public(writer); write_public(writer);
@ -389,8 +393,8 @@ impl Key {
Self::EC { Self::EC {
curve: Curve::P256 { curve: Curve::P256 {
d: Some(sk_scalar.to_bytes().into()), d: Some(sk_scalar.to_bytes().into()),
x: ByteArray::try_from_slice(x_bytes).unwrap(), x: ByteArray::from_slice(x_bytes),
y: ByteArray::try_from_slice(y_bytes).unwrap(), y: ByteArray::from_slice(y_bytes),
}, },
} }
} }
@ -404,11 +408,11 @@ pub enum Curve {
P256 { P256 {
/// The private scalar. /// The private scalar.
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
d: Option<ByteArray<32>>, d: Option<ByteArray<U32>>,
/// The curve point x coordinate. /// The curve point x coordinate.
x: ByteArray<32>, x: ByteArray<U32>,
/// The curve point y coordinate. /// The curve point y coordinate.
y: ByteArray<32>, y: ByteArray<U32>,
}, },
} }

View File

@ -2,6 +2,8 @@ use super::*;
use std::str::FromStr; use std::str::FromStr;
use crate::byte_array::ByteArray;
// Generated using https://mkjwk.org // Generated using https://mkjwk.org
static P256_JWK_FIXTURE: &str = r#"{ static P256_JWK_FIXTURE: &str = r#"{
"kty": "EC", "kty": "EC",
@ -40,29 +42,24 @@ fn deserialize_es256() {
assert_eq!( assert_eq!(
jwk, jwk,
JsonWebKey { JsonWebKey {
key: box Key::EC { key: Box::new(Key::EC {
// The parameters were decoded using a 10-liner Rust script. // The parameters were decoded using a 10-liner Rust script.
curve: Curve::P256 { curve: Curve::P256 {
d: Some( d: Some(ByteArray::from_slice(&[
[ 102, 130, 144, 246, 62, 29, 132, 128, 101, 49, 21, 107, 191, 228, 6, 240,
102, 130, 144, 246, 62, 29, 132, 128, 101, 49, 21, 107, 191, 228, 6, 255, 211, 246, 203, 173, 191, 127, 253, 229, 232, 168, 244, 203, 105, 128,
240, 255, 211, 246, 203, 173, 191, 127, 253, 229, 232, 168, 244, 203, 168
105, 128, 168 ])),
] x: ByteArray::from_slice(&[
.into()
),
x: [
64, 227, 7, 154, 255, 122, 181, 89, 73, 191, 235, 141, 170, 154, 231, 13, 64, 227, 7, 154, 255, 122, 181, 89, 73, 191, 235, 141, 170, 154, 231, 13,
34, 136, 143, 144, 34, 45, 53, 202, 70, 137, 151, 98, 118, 175, 208, 221 34, 136, 143, 144, 34, 45, 53, 202, 70, 137, 151, 98, 118, 175, 208, 221
] ]),
.into(), y: ByteArray::from_slice(&[
y: [
78, 54, 25, 160, 121, 220, 181, 171, 68, 19, 163, 66, 172, 169, 151, 65, 78, 54, 25, 160, 121, 220, 181, 171, 68, 19, 163, 66, 172, 169, 151, 65,
210, 73, 62, 115, 115, 100, 69, 252, 156, 25, 153, 117, 237, 192, 99, 137 210, 73, 62, 115, 115, 100, 69, 252, 156, 25, 153, 117, 237, 192, 99, 137
] ])
.into(),
},
}, },
}),
algorithm: Some(Algorithm::ES256), algorithm: Some(Algorithm::ES256),
key_id: Some("a key".into()), key_id: Some("a key".into()),
key_ops: KeyOps::empty(), key_ops: KeyOps::empty(),
@ -74,13 +71,13 @@ fn deserialize_es256() {
#[test] #[test]
fn serialize_es256() { fn serialize_es256() {
let jwk = JsonWebKey { let jwk = JsonWebKey {
key: box Key::EC { key: Box::new(Key::EC {
curve: Curve::P256 { curve: Curve::P256 {
d: None, d: None,
x: [1u8; 32].into(), x: ByteArray::from_slice(&[1u8; 32]),
y: [2u8; 32].into(), y: ByteArray::from_slice(&[2u8; 32]),
},
}, },
}),
key_id: None, key_id: None,
algorithm: None, algorithm: None,
key_ops: KeyOps::empty(), key_ops: KeyOps::empty(),
@ -130,10 +127,10 @@ fn deserialize_hs256() {
assert_eq!( assert_eq!(
jwk, jwk,
JsonWebKey { JsonWebKey {
key: box Key::Symmetric { key: Box::new(Key::Symmetric {
// The parameters were decoded using a 10-liner Rust script. // The parameters were decoded using a 10-liner Rust script.
key: vec![180, 3, 141, 233].into(), key: vec![180, 3, 141, 233].into(),
}, }),
algorithm: Some(Algorithm::HS256), algorithm: Some(Algorithm::HS256),
key_id: None, key_id: None,
key_ops: KeyOps::SIGN | KeyOps::VERIFY, key_ops: KeyOps::SIGN | KeyOps::VERIFY,
@ -145,9 +142,9 @@ fn deserialize_hs256() {
#[test] #[test]
fn serialize_hs256() { fn serialize_hs256() {
let jwk = JsonWebKey { let jwk = JsonWebKey {
key: box Key::Symmetric { key: Box::new(Key::Symmetric {
key: vec![42; 16].into(), key: vec![42; 16].into(),
}, }),
key_id: None, key_id: None,
algorithm: None, algorithm: None,
key_ops: KeyOps::empty(), key_ops: KeyOps::empty(),
@ -165,7 +162,7 @@ fn deserialize_rs256() {
assert_eq!( assert_eq!(
jwk, jwk,
JsonWebKey { JsonWebKey {
key: box Key::RSA { key: Box::new(Key::RSA {
public: RsaPublic { public: RsaPublic {
e: PublicExponent, e: PublicExponent,
n: vec![ n: vec![
@ -223,7 +220,7 @@ fn deserialize_rs256() {
.into() .into()
) )
}) })
}, }),
algorithm: None, algorithm: None,
key_id: None, key_id: None,
key_ops: KeyOps::WRAP_KEY, key_ops: KeyOps::WRAP_KEY,
@ -235,7 +232,7 @@ fn deserialize_rs256() {
#[test] #[test]
fn serialize_rs256() { fn serialize_rs256() {
let jwk = JsonWebKey { let jwk = JsonWebKey {
key: box Key::RSA { key: Box::new(Key::RSA {
public: RsaPublic { public: RsaPublic {
e: PublicExponent, e: PublicExponent,
n: vec![105, 183, 62].into(), n: vec![105, 183, 62].into(),
@ -248,7 +245,7 @@ fn serialize_rs256() {
dq: None, dq: None,
qi: None, qi: None,
}), }),
}, }),
key_id: None, key_id: None,
algorithm: None, algorithm: None,
key_ops: KeyOps::empty(), key_ops: KeyOps::empty(),

View File

@ -16,11 +16,17 @@ fn base64_decode(b64: impl AsRef<[u8]>) -> Result<Vec<u8>, base64::DecodeError>
base64::decode_config(b64, base64_config()) base64::decode_config(b64, base64_config())
} }
pub fn serialize_base64<S: Serializer>(bytes: impl AsRef<[u8]>, s: S) -> Result<S::Ok, S::Error> { pub(crate) mod serde_base64 {
base64_encode(bytes).serialize(s) use super::*;
}
pub fn deserialize_base64<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D::Error> { pub(crate) fn serialize<S: Serializer>(
bytes: impl AsRef<[u8]>,
s: S,
) -> Result<S::Ok, S::Error> {
base64_encode(bytes).serialize(s)
}
pub(crate) fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D::Error> {
let base64_str = Zeroizing::new(String::deserialize(d)?); let base64_str = Zeroizing::new(String::deserialize(d)?);
base64_decode(&*base64_str).map_err(|e| { base64_decode(&*base64_str).map_err(|e| {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
@ -29,16 +35,17 @@ pub fn deserialize_base64<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D:
let err_msg = "invalid base64"; let err_msg = "invalid base64";
de::Error::custom(err_msg.strip_suffix(".").unwrap_or(&err_msg)) de::Error::custom(err_msg.strip_suffix(".").unwrap_or(&err_msg))
}) })
}
} }
#[cfg(feature = "pkcs-convert")] #[cfg(feature = "pkcs-convert")]
pub mod pkcs8 { pub(crate) mod pkcs8 {
use yasna::{ use yasna::{
models::{ObjectIdentifier, TaggedDerValue}, models::{ObjectIdentifier, TaggedDerValue},
DERWriter, DERWriterSeq, DERWriter, DERWriterSeq,
}; };
fn write_oids(writer: &mut DERWriterSeq, oids: &[Option<&ObjectIdentifier>]) { fn write_oids(writer: &mut DERWriterSeq<'_>, oids: &[Option<&ObjectIdentifier>]) {
for oid in oids { for oid in oids {
match oid { match oid {
Some(oid) => writer.next().write_oid(oid), Some(oid) => writer.next().write_oid(oid),
@ -47,9 +54,9 @@ pub mod pkcs8 {
} }
} }
pub fn write_private( pub(crate) fn write_private(
oids: &[Option<&ObjectIdentifier>], oids: &[Option<&ObjectIdentifier>],
body_writer: impl FnOnce(&mut DERWriterSeq), body_writer: impl FnOnce(&mut DERWriterSeq<'_>),
) -> Vec<u8> { ) -> Vec<u8> {
yasna::construct_der(|writer| { yasna::construct_der(|writer| {
writer.write_sequence(|writer| { writer.write_sequence(|writer| {
@ -66,9 +73,9 @@ pub mod pkcs8 {
}) })
} }
pub fn write_public( pub(crate) fn write_public(
oids: &[Option<&ObjectIdentifier>], oids: &[Option<&ObjectIdentifier>],
body_writer: impl FnOnce(DERWriter), body_writer: impl FnOnce(DERWriter<'_>),
) -> Vec<u8> { ) -> Vec<u8> {
yasna::construct_der(|writer| { yasna::construct_der(|writer| {
writer.write_sequence(|writer| { writer.write_sequence(|writer| {