Skip to content

Commit

Permalink
Replace secrecy with zeroize; MSRV 1.60 (#54)
Browse files Browse the repository at this point in the history
The `secrecy` version is out-of-date and represents another dependency
which can be eliminated by using a zeroize-on-drop approach.
  • Loading branch information
tarcieri committed Nov 18, 2023
1 parent 46331cb commit e934b82
Show file tree
Hide file tree
Showing 15 changed files with 111 additions and 113 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/cryptouri.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
strategy:
matrix:
include:
- rust: 1.56.0 # MSRV
- rust: 1.60.0 # MSRV
- rust: stable

steps:
Expand Down
13 changes: 2 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ readme = "README.md"
categories = ["cryptography", "encoding"]
keywords = ["bech32", "cryptography", "keys", "security", "uri"]
edition = "2021"
rust-version = "1.56"
rust-version = "1.60"

[badges]
travis-ci = { repository = "cryptouri/cryptouri.rs" }

[dependencies]
secrecy = "0.6"
subtle-encoding = { version = "0.5.1", features = ["bech32-preview"] }
zeroize = "1.7"
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ CryptoURIs which have been mis-transcribed will fail to decode.

## Minimum Supported Rust Version

- Rust **1.56+**
- Rust **1.60+**

## Code of Conduct

Expand Down Expand Up @@ -74,7 +74,7 @@ The **cryptouri** Rust crate is dual licensed under your choice of either of:
[safety-image]: https://img.shields.io/badge/unsafe-forbidden-success.svg
[safety-link]: https://github.com/rust-secure-code/safety-dance/
[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg
[msrv-image]: https://img.shields.io/badge/rustc-1.56+-blue.svg
[msrv-image]: https://img.shields.io/badge/rustc-1.60+-blue.svg

[//]: # (links)

Expand Down
6 changes: 2 additions & 4 deletions src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,21 +107,19 @@ macro_rules! impl_encodable_secret_key {
impl crate::encoding::Encodable for $name {
#[inline]
fn to_uri_string(&self) -> String {
use secrecy::ExposeSecret;
use subtle_encoding::bech32::{self, Bech32};
Bech32::new(
bech32::DEFAULT_CHARSET,
$crate::encoding::URI_ENCODING.delimiter,
)
.encode(
$crate::encoding::URI_ENCODING.secret_key_scheme.to_owned() + $alg,
&self.expose_secret()[..],
&self.as_ref()[..],
)
}

#[inline]
fn to_dasherized_string(&self) -> String {
use secrecy::ExposeSecret;
use subtle_encoding::bech32::{self, Bech32};
Bech32::new(
bech32::DEFAULT_CHARSET,
Expand All @@ -132,7 +130,7 @@ macro_rules! impl_encodable_secret_key {
.secret_key_scheme
.to_owned()
+ $alg,
&self.expose_secret()[..],
&self.as_ref()[..],
)
}
}
Expand Down
1 change: 0 additions & 1 deletion src/hash/sha2.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
//! SHA2 hash types

use crate::{algorithm::SHA256_ALG_ID, error::Error};
use std::convert::{TryFrom, TryInto};

/// Size of a SHA-256 hash
pub const SHA256_HASH_SIZE: usize = 32;
Expand Down
25 changes: 9 additions & 16 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
//! with Bech32 encoding/checksums

#![doc(
html_logo_url = "https://avatars3.githubusercontent.com/u/40766087?u=0267cf8b7fe892bbf35b6114d9eb48adc057d6ff",
html_root_url = "https://docs.rs/cryptouri/0.4.0"
html_logo_url = "https://avatars3.githubusercontent.com/u/40766087?u=0267cf8b7fe892bbf35b6114d9eb48adc057d6ff"
)]
#![forbid(unsafe_code)]
#![warn(missing_docs, rust_2018_idioms, unused_qualifications)]
Expand All @@ -29,15 +28,14 @@ use crate::{
encoding::{Encoding, DASHERIZED_ENCODING, URI_ENCODING},
parts::Parts,
};
use secrecy::{ExposeSecret, SecretString};

/// `CryptoUri`: URI-based format for encoding cryptographic objects
pub struct CryptoUri {
/// Kind of `CryptoUri` (e.g. secret key, public key, hashes, signatures)
kind: CryptoUriKind,

/// URI fragment (i.e. everything after `#`)
fragment: Option<SecretString>,
fragment: Option<String>,
}

/// Kinds of `CryptoUri`s
Expand All @@ -64,38 +62,35 @@ impl CryptoUri {
let kind = if parts.prefix.starts_with(encoding.hash_scheme) {
CryptoUriKind::Hash(Hash::new(
&parts.prefix[encoding.hash_scheme.len()..],
parts.data.expose_secret(),
parts.data.as_ref(),
)?)
} else if parts.prefix.starts_with(encoding.public_key_scheme) {
CryptoUriKind::PublicKey(PublicKey::new(
&parts.prefix[encoding.public_key_scheme.len()..],
parts.data.expose_secret(),
parts.data.as_ref(),
)?)
} else if parts.prefix.starts_with(encoding.secret_key_scheme) {
let alg_id = &parts.prefix[encoding.secret_key_scheme.len()..];

if alg_id.contains(encoding.combine) {
// Multi-algorithm combination (e.g. KDF)
let alg_ids = alg_id.split(encoding.combine).collect::<Vec<_>>();
CryptoUriKind::SecretKey(SecretKey::new_combination(
&alg_ids,
parts.data.expose_secret(),
)?)
CryptoUriKind::SecretKey(SecretKey::new_combination(&alg_ids, parts.data.as_ref())?)
} else {
CryptoUriKind::SecretKey(SecretKey::new(alg_id, parts.data.expose_secret())?)
CryptoUriKind::SecretKey(SecretKey::new(alg_id, parts.data.as_ref())?)
}
} else if parts.prefix.starts_with(encoding.signature_scheme) {
CryptoUriKind::Signature(Signature::new(
&parts.prefix[encoding.signature_scheme.len()..],
parts.data.expose_secret(),
parts.data.as_ref(),
)?)
} else {
return Err(Error::Scheme(parts.prefix.to_owned()));
};

Ok(Self {
kind,
fragment: parts.fragment,
fragment: parts.fragment.clone(),
})
}

Expand Down Expand Up @@ -168,9 +163,7 @@ impl CryptoUri {

/// Obtain the fragment for this URI (i.e. everything after `#`)
pub fn fragment(&self) -> Option<&str> {
self.fragment
.as_ref()
.map(|fragment| fragment.expose_secret().as_ref())
self.fragment.as_ref().map(|fragment| fragment.as_ref())
}
}

Expand Down
19 changes: 13 additions & 6 deletions src/parts.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
//! CryptoURI parts

use crate::{encoding::Encoding, error::Error};
use secrecy::{Secret, SecretString, SecretVec};
use subtle_encoding::bech32::{self, Bech32};
use zeroize::Zeroize;

/// Parts of a CryptoURI
pub(crate) struct Parts {
/// CryptoURI prefix
pub(crate) prefix: String,

/// Data (i.e. public or private key or key fingerprint)
pub(crate) data: SecretVec<u8>,
pub(crate) data: Vec<u8>,

/// URI fragment (i.e. comment)
pub(crate) fragment: Option<SecretString>,
pub(crate) fragment: Option<String>,
}

impl Parts {
Expand All @@ -30,8 +30,8 @@ impl Parts {

return Ok(Self {
prefix,
data: Secret::new(data),
fragment: Some(Secret::new(fragment)),
data,
fragment: Some(fragment),
});
}
}
Expand All @@ -42,8 +42,15 @@ impl Parts {

Ok(Self {
prefix,
data: Secret::new(data),
data,
fragment: None,
})
}
}

impl Drop for Parts {
fn drop(&mut self) {
self.data.zeroize();
self.fragment.zeroize();
}
}
2 changes: 0 additions & 2 deletions src/secret_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ pub use self::{
ed25519::Ed25519SecretKey,
hkdf::HkdfSha256Key,
};
pub use secrecy::ExposeSecret;

use crate::{
algorithm::{
Expand All @@ -22,7 +21,6 @@ use crate::{
error::Error,
};
use std::{
convert::TryInto,
fmt::{self, Display},
str::FromStr,
};
Expand Down
29 changes: 17 additions & 12 deletions src/secret_key/aesgcm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ use crate::{
algorithm::{AES128GCM_ALG_ID, AES256GCM_ALG_ID},
error::Error,
};
use secrecy::{DebugSecret, ExposeSecret, Secret};
use std::convert::{TryFrom, TryInto};
use zeroize::{Zeroize, ZeroizeOnDrop};

/// Size of an AES-128 key in bytes
pub const AES128_KEY_SIZE: usize = 16;
Expand All @@ -15,35 +14,41 @@ pub const AES256_KEY_SIZE: usize = 32;

/// AES-128 in Galois/Counter Mode (GCM)
#[derive(Clone)]
pub struct Aes128GcmKey(Secret<[u8; AES128_KEY_SIZE]>);
pub struct Aes128GcmKey(Box<[u8; AES128_KEY_SIZE]>);

/// AES-256 in Galois/Counter Mode (GCM)
#[derive(Clone)]
pub struct Aes256GcmKey(Secret<[u8; AES256_KEY_SIZE]>);
pub struct Aes256GcmKey(Box<[u8; AES256_KEY_SIZE]>);

macro_rules! impl_aes_gcm_key {
($name:ident, $key_size:expr, $desc:expr) => {
impl AsRef<[u8; $key_size]> for $name {
fn as_ref(&self) -> &[u8; $key_size] {
&self.0
}
}

impl Drop for $name {
fn drop(&mut self) {
self.0.zeroize();
}
}

impl TryFrom<&[u8]> for $name {
type Error = Error;

fn try_from(slice: &[u8]) -> Result<Self, Error> {
slice
.try_into()
.map(|bytes| $name(Secret::new(bytes)))
.map(|bytes| $name(Box::new(bytes)))
.map_err(|_| Error::Length {
actual: slice.len(),
expected: $key_size,
})
}
}

impl DebugSecret for $name {}

impl ExposeSecret<[u8; $key_size]> for $name {
fn expose_secret(&self) -> &[u8; $key_size] {
self.0.expose_secret()
}
}
impl ZeroizeOnDrop for $name {}
};
}

Expand Down
27 changes: 16 additions & 11 deletions src/secret_key/chacha20poly1305.rs
Original file line number Diff line number Diff line change
@@ -1,36 +1,41 @@
//! ChaCha20Poly1305 AEAD (RFC 8439)

use crate::{algorithm::CHACHA20POLY1305_ALG_ID, error::Error};
use secrecy::{DebugSecret, ExposeSecret, Secret};
use std::convert::{TryFrom, TryInto};
use zeroize::{Zeroize, ZeroizeOnDrop};

/// Size of a ChaCha20Poly1305 key in bytes
pub const CHACHA20POLY1305_KEY_SIZE: usize = 32;

/// ChaCha20Poly1305 encryption key
#[derive(Clone)]
pub struct ChaCha20Poly1305Key(Secret<[u8; CHACHA20POLY1305_KEY_SIZE]>);
pub struct ChaCha20Poly1305Key(Box<[u8; CHACHA20POLY1305_KEY_SIZE]>);

impl AsRef<[u8; CHACHA20POLY1305_KEY_SIZE]> for ChaCha20Poly1305Key {
fn as_ref(&self) -> &[u8; CHACHA20POLY1305_KEY_SIZE] {
&self.0
}
}

impl Drop for ChaCha20Poly1305Key {
fn drop(&mut self) {
self.0.zeroize();
}
}

impl TryFrom<&[u8]> for ChaCha20Poly1305Key {
type Error = Error;

fn try_from(slice: &[u8]) -> Result<Self, Error> {
slice
.try_into()
.map(|bytes| ChaCha20Poly1305Key(Secret::new(bytes)))
.map(|bytes| ChaCha20Poly1305Key(Box::new(bytes)))
.map_err(|_| Error::Length {
actual: slice.len(),
expected: 32,
})
}
}

impl DebugSecret for ChaCha20Poly1305Key {}

impl ExposeSecret<[u8; CHACHA20POLY1305_KEY_SIZE]> for ChaCha20Poly1305Key {
fn expose_secret(&self) -> &[u8; CHACHA20POLY1305_KEY_SIZE] {
self.0.expose_secret()
}
}
impl ZeroizeOnDrop for ChaCha20Poly1305Key {}

impl_encodable_secret_key!(ChaCha20Poly1305Key, CHACHA20POLY1305_ALG_ID);

0 comments on commit e934b82

Please sign in to comment.