Skip to content

pkcs5 encryption + pkcs8 decryption #293

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Feb 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/pkcs5.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ jobs:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- run: cargo build --release --target ${{ matrix.target }}
- run: cargo build --target ${{ matrix.target }} --release
- run: cargo build --target ${{ matrix.target }} --release --features pbes2

test:
runs-on: ubuntu-latest
Expand All @@ -55,3 +56,4 @@ jobs:
toolchain: ${{ matrix.rust }}
override: true
- run: cargo test --release
- run: cargo test --release --all-features
4 changes: 4 additions & 0 deletions .github/workflows/pkcs8.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ jobs:
override: true
- run: cargo build --release --target ${{ matrix.target }} --no-default-features
- run: cargo build --release --target ${{ matrix.target }} --no-default-features --features alloc
- run: cargo build --release --target ${{ matrix.target }} --no-default-features --features encryption
- run: cargo build --release --target ${{ matrix.target }} --no-default-features --features pem
- run: cargo build --release --target ${{ matrix.target }} --no-default-features --features pkcs5

test:
runs-on: ubuntu-latest
Expand All @@ -60,5 +62,7 @@ jobs:
- run: cargo test --release --no-default-features
- run: cargo test --release
- run: cargo test --release --features alloc
- run: cargo test --release --features encryption
- run: cargo test --release --features pem
- run: cargo test --release --features pkcs5
- run: cargo test --release --all-features
2 changes: 1 addition & 1 deletion .github/workflows/workspace.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1
with:
toolchain: 1.46.0 # MSRV
toolchain: 1.47.0 # Highest MSRV in repo
components: clippy
override: true
profile: minimal
Expand Down
147 changes: 146 additions & 1 deletion Cargo.lock

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

9 changes: 9 additions & 0 deletions pkcs5/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,18 @@ readme = "README.md"
der = { version = "0.2.7", features = ["oid"], path = "../der" }
spki = { version = "0.2", path = "../spki" }

aes = { version = "0.6", optional = true }
block-modes = { version = "0.7", optional = true, default-features = false }
hmac = { version = "0.10", optional = true, default-features = false }
pbkdf2 = { version = "0.7", optional = true, default-features = false }
sha2 = { version = "0.9", optional = true, default-features = false }

[dev-dependencies]
hex-literal = "0.3"

[features]
pbes2 = ["aes", "block-modes", "hmac", "pbkdf2", "sha2"]

[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
51 changes: 45 additions & 6 deletions pkcs5/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
#![forbid(unsafe_code)]
#![warn(missing_docs, rust_2018_idioms, unused_qualifications)]

pub use der::{self, Error, ObjectIdentifier, Result};
pub use der::{self, Error, ObjectIdentifier};
pub use spki::AlgorithmIdentifier;

use core::convert::{TryFrom, TryInto};
Expand All @@ -34,6 +34,10 @@ use der::{sequence, Any, Encodable, Encoder, Length};
pub mod pbes1;
pub mod pbes2;

/// Cryptographic errors
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct CryptoError;

/// Supported PKCS#5 password-based encryption schemes.
#[derive(Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
Expand All @@ -51,6 +55,41 @@ pub enum EncryptionScheme<'a> {
}

impl<'a> EncryptionScheme<'a> {
/// Encrypt the given ciphertext in-place using a key derived from the
/// provided password and this scheme's parameters.
#[cfg(feature = "pbes2")]
#[cfg_attr(docsrs, doc(cfg(feature = "pbes2")))]
pub fn encrypt_in_place<'b>(
&self,
password: impl AsRef<[u8]>,
buffer: &'b mut [u8],
pos: usize,
) -> Result<&'b [u8], CryptoError> {
match self {
Self::Pbes2(params) => params.encrypt_in_place(password, buffer, pos),
_ => Err(CryptoError),
}
}

/// Attempt to decrypt the given ciphertext in-place using a key derived
/// from the provided password and this scheme's parameters.
///
/// Returns an error if the algorithm specified in this scheme's parameters
/// is unsupported, or if the ciphertext is malformed (e.g. not a multiple
/// of a block mode's padding)
#[cfg(feature = "pbes2")]
#[cfg_attr(docsrs, doc(cfg(feature = "pbes2")))]
pub fn decrypt_in_place<'b>(
&self,
password: impl AsRef<[u8]>,
buffer: &'b mut [u8],
) -> Result<&'b [u8], CryptoError> {
match self {
Self::Pbes2(params) => params.decrypt_in_place(password, buffer),
_ => Err(CryptoError),
}
}

/// Get the [`ObjectIdentifier`] (a.k.a OID) for this algorithm.
pub fn oid(&self) -> ObjectIdentifier {
match self {
Expand Down Expand Up @@ -79,23 +118,23 @@ impl<'a> EncryptionScheme<'a> {
impl<'a> TryFrom<&'a [u8]> for EncryptionScheme<'a> {
type Error = Error;

fn try_from(bytes: &'a [u8]) -> Result<EncryptionScheme<'a>> {
fn try_from(bytes: &'a [u8]) -> der::Result<EncryptionScheme<'a>> {
AlgorithmIdentifier::try_from(bytes).and_then(TryInto::try_into)
}
}

impl<'a> TryFrom<Any<'a>> for EncryptionScheme<'a> {
type Error = Error;

fn try_from(any: Any<'a>) -> Result<EncryptionScheme<'a>> {
fn try_from(any: Any<'a>) -> der::Result<EncryptionScheme<'a>> {
AlgorithmIdentifier::try_from(any).and_then(TryInto::try_into)
}
}

impl<'a> TryFrom<AlgorithmIdentifier<'a>> for EncryptionScheme<'a> {
type Error = Error;

fn try_from(alg: AlgorithmIdentifier<'a>) -> Result<EncryptionScheme<'_>> {
fn try_from(alg: AlgorithmIdentifier<'a>) -> der::Result<EncryptionScheme<'_>> {
match alg.oid {
pbes2::PBES2_OID => pbes2::Parameters::try_from(alg.parameters_any()?).map(Into::into),
_ => pbes1::Parameters::try_from(alg).map(Into::into),
Expand All @@ -116,14 +155,14 @@ impl<'a> From<pbes2::Parameters<'a>> for EncryptionScheme<'a> {
}

impl<'a> Encodable for EncryptionScheme<'a> {
fn encoded_len(&self) -> Result<Length> {
fn encoded_len(&self) -> der::Result<Length> {
match self {
Self::Pbes1(pbes1) => pbes1.encoded_len(),
Self::Pbes2(pbes2) => sequence::encoded_len(&[&pbes2::PBES2_OID, pbes2]),
}
}

fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> {
fn encode(&self, encoder: &mut Encoder<'_>) -> der::Result<()> {
match self {
Self::Pbes1(pbes1) => pbes1.encode(encoder),
Self::Pbes2(pbes2) => encoder.sequence(&[&pbes2::PBES2_OID, pbes2]),
Expand Down
Loading