silk.crypto.pkcs8.
Encrypt and decrypt private keys in the PKCS #8 EncryptedPrivateKeyInfo format with PBES2
(RFC 8018, RFC 5958).
When to use#
Use EncryptedPkcs8 for an -----BEGIN ENCRYPTED PRIVATE KEY----- block, such as the input
of createPrivateKey with a passphrase in Node.js, or the output of export with type:
'pkcs8' and a cipher. Find and decode the PEM block with silk.encoding.pem, and read the
decrypted PrivateKeyInfo with the key modules, such as RsaPrivateKey.fromPkcs8Der.
Details#
PBES2 derives the key from the password with PBKDF2 and encrypts the DER with a CBC cipher and
PKCS #7 padding. The PRF is HMAC with SHA-1, SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, or
SHA-512/256 (Pbes2Prf), and the cipher is AES-128, AES-192, or AES-256 in CBC mode, or Triple
DES in CBC mode (Pbes2Cipher). These are the choices of OpenSSL's pkcs8 command. The
caller gives the salt and IV of EncryptedPkcs8.encrypt, so this module needs no random
source. Nothing allocates.
Errors#
The functions return a Pkcs8Error. On an error the output does not change.
Gotchas#
A wrong password usually fails with WrongPassword, but about one wrong password in 256 passes
the padding check and returns garbage, so check that the result parses as a key. PBES1, the
PKCS #12 schemes, and scrypt in PBES2 fail with UnsupportedAlgorithm. The time grows with the
iteration count, which comes from the input: limit it before decrypting a key from outside.
Examples#
Encrypt and decrypt a key#
import silk.crypto.pkcs8 { EncryptedPkcs8, Pbes2Cipher, Pbes2Parameters, Pbes2Prf, Pkcs8Error }
import silk.core.result { Result }
import silk.collections.slice { Slice }
pub fn main() -> i32 {
let key: [u8; 5] = [48, 3, 2, 1, 0]
let parameters = Pbes2Parameters {
cipher: Pbes2Cipher.Aes256Cbc,
prf: Pbes2Prf.HmacSha256,
iterations: 2048,
salt: b"saltsalt",
iv: b"0123456789abcdef",
}
let mut sealed: [u8; 160] = [0; 160]
let length = match EncryptedPkcs8.encrypt(&key, b"secret", ¶meters, &mut sealed) {
Result<usize, Pkcs8Error>.Success { value } => value
Result<usize, Pkcs8Error>.Failure { error: _ } => {
return 1
}
}
let mut opened: [u8; 160] = [0; 160]
let opened_length = match EncryptedPkcs8.decrypt(Slice.view<u8>(&sealed, 0, length), b"secret", &mut opened) {
Result<usize, Pkcs8Error>.Success { value } => value
Result<usize, Pkcs8Error>.Failure { error: _ } => {
return 1
}
}
if opened_length != 5 || opened[0] != 48 {
return 1
}
return 0
}Import as EncryptedPkcs8 with import silk.crypto.pkcs8 { EncryptedPkcs8 }.
Public declarations: 5.
Pkcs8Error#
pub union Pkcs8ErrorThe reason that an EncryptedPkcs8 function rejected a call. The output does not change.
InvalidEncoding#
InvalidEncodingThe input is not strict DER of an EncryptedPrivateKeyInfo with PBES2 parameters.
UnsupportedAlgorithm#
UnsupportedAlgorithmThe scheme, key derivation, PRF, or cipher is not one that this module supports.
WrongPassword#
WrongPasswordThe decrypted data does not end in valid padding: the password is wrong, or the data was changed.
InvalidParameters#
InvalidParametersThe salt is empty, the iteration count is 0, or the IV is not one cipher block.
OutputTooSmall#
OutputTooSmallThe output buffer is shorter than the bytes the call writes.
Implementation Debug for Pkcs8Error#
impl Debug for Pkcs8ErrorOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut WriterWrites the name of the variant, such as WrongPassword.
Pbes2Cipher#
pub enum Pbes2CipherThe CBC ciphers that PBES2 can use here.
Aes128Cbc#
Aes128CbcAES-128 in CBC mode, aes-128-cbc.
Aes192Cbc#
Aes192CbcAES-192 in CBC mode, aes-192-cbc.
Aes256Cbc#
Aes256CbcAES-256 in CBC mode, aes-256-cbc.
TripleDesCbc#
TripleDesCbcTriple DES with a 24-byte key in CBC mode, des-ede3-cbc.
Implementation Pbes2Cipher#
impl Pbes2CipherMethod keyLength#
pub fn keyLength(self: &Self) -> usizeReturns the key length in bytes: 16, 24, 32, or 24.
Method blockLength#
pub fn blockLength(self: &Self) -> usizeReturns the block length in bytes, which is also the IV length: 16 for AES, 8 for Triple DES.
Pbes2Prf#
pub enum Pbes2PrfThe HMAC functions that PBKDF2 can use as its PRF here.
HmacSha1#
HmacSha1HMAC-SHA-1, the default when the parameters name no PRF.
HmacSha224#
HmacSha224HMAC-SHA-224.
HmacSha256#
HmacSha256HMAC-SHA-256, which OpenSSL writes by default.
HmacSha384#
HmacSha384HMAC-SHA-384.
HmacSha512#
HmacSha512HMAC-SHA-512.
HmacSha512_224#
HmacSha512_224HMAC-SHA-512/224.
HmacSha512_256#
HmacSha512_256HMAC-SHA-512/256.
Implementation Pbes2Prf#
impl Pbes2PrfPbes2Parameters#
pub struct Pbes2Parameters<'a>The choices of EncryptedPkcs8.encrypt.
Details#
salt should be at least 8 random bytes and iv one random cipher block. OpenSSL uses a
16-byte salt and 2048 iterations by default; more iterations make a guessed password cost more.
Field cipher#
pub cipher: Pbes2CipherThe cipher that encrypts the key.
Field prf#
pub prf: Pbes2PrfThe PRF of PBKDF2.
Field iterations#
pub iterations: u32The PBKDF2 iteration count, at least 1.
Field salt#
pub salt: &'a [u8]The PBKDF2 salt, not empty.
Field iv#
pub iv: &'a [u8]The CBC IV, one cipher block.
EncryptedPkcs8#
pub struct EncryptedPkcs8Functions that encrypt and decrypt PKCS #8 private keys with PBES2.
Implementation EncryptedPkcs8#
impl EncryptedPkcs8Associated function decrypt#
pub fn decrypt(der: &[u8], password: &[u8], output: &mut [u8]) -> Result<usize, Pkcs8Error>Decrypts the EncryptedPrivateKeyInfo DER der with password into output and returns
the length of the PrivateKeyInfo DER written.
The result is at most EncryptedPkcs8.decryptedLength bytes.
Errors#
InvalidEncodingifderis not strict DER of anEncryptedPrivateKeyInfowith PBES2 parameters, or the PBKDF2 key length does not match the cipher.UnsupportedAlgorithmif the scheme is not PBES2 with PBKDF2, or the PRF or the cipher is not one ofPbes2PrfandPbes2Cipher.InvalidParametersif the iteration count is 0 or the IV is not one block.WrongPasswordif the padding is wrong after decryption.OutputTooSmallifoutputis shorter than the encrypted data.
Associated function decryptedLength#
pub fn decryptedLength(der: &[u8]) -> usizeReturns the most bytes that EncryptedPkcs8.decrypt writes for der: the length of its
encrypted data, or 0 if der does not parse.
Associated function encryptedLength#
pub fn encryptedLength(length: usize, parameters: &Pbes2Parameters) -> usizeReturns the number of bytes that EncryptedPkcs8.encrypt writes for a PrivateKeyInfo of
length bytes with parameters.
Associated function encrypt#
pub fn encrypt(der: &[u8], password: &[u8], parameters: &Pbes2Parameters, output: &mut [u8]) -> Result<usize, Pkcs8Error>Encrypts the PrivateKeyInfo DER der with password and parameters into output as an
EncryptedPrivateKeyInfo, and returns the number of bytes written,
EncryptedPkcs8.encryptedLength.
The PRF is written even when it is HMAC-SHA-1, and the key length is left out, as OpenSSL
does. The function does not check that der is a key.
Errors#
InvalidParametersif the salt is empty, the iteration count is 0, or the IV is not one block of the cipher.OutputTooSmallifoutputis shorter thanEncryptedPkcs8.encryptedLength.
Category: Cryptography.
Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, aarch64-unknown-linux-gnu-no-libc, x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, wasm32-wasip1, wasm32-wasip2, x86_64-pc-windows-msvc, wasm64-unknown-unknown.