diff --git a/changelog.d/929.bugfix b/changelog.d/929.bugfix new file mode 100644 index 00000000..7261adb4 --- /dev/null +++ b/changelog.d/929.bugfix @@ -0,0 +1 @@ +Track which key was used to encrypt secrets in storage, and encrypt/decrypt secrets in Rust. diff --git a/src/tokens/mod.rs b/src/tokens/mod.rs index 6aa0cc17..60e42941 100644 --- a/src/tokens/mod.rs +++ b/src/tokens/mod.rs @@ -3,6 +3,7 @@ use std::string::FromUtf8Error; use base64ct::{Base64, Encoding}; use napi::bindgen_prelude::Buffer; use napi::Error; +use rsa::pkcs1::DecodeRsaPrivateKey; use rsa::pkcs8::DecodePrivateKey; use rsa::{Pkcs1v15Encrypt, RsaPrivateKey, RsaPublicKey}; @@ -17,7 +18,9 @@ struct TokenEncryption { #[allow(dead_code)] enum TokenEncryptionError { FromUtf8(FromUtf8Error), - PrivateKey(rsa::pkcs8::Error), + UnknownFormat, + PrivateKey8(rsa::pkcs8::Error), + PrivateKey1(rsa::pkcs1::Error), } #[derive(Debug)] @@ -31,8 +34,16 @@ enum DecryptError { impl TokenEncryption { pub fn new(private_key_data: Vec) -> Result { let data = String::from_utf8(private_key_data).map_err(TokenEncryptionError::FromUtf8)?; - let private_key = RsaPrivateKey::from_pkcs8_pem(data.as_str()) - .map_err(TokenEncryptionError::PrivateKey)?; + let private_key: RsaPrivateKey; + if data.starts_with("-----BEGIN PRIVATE KEY-----") { + private_key = RsaPrivateKey::from_pkcs8_pem(data.as_str()) + .map_err(TokenEncryptionError::PrivateKey8)?; + } else if data.starts_with("-----BEGIN RSA PRIVATE KEY-----") { + private_key = RsaPrivateKey::from_pkcs1_pem(data.as_str()) + .map_err(TokenEncryptionError::PrivateKey1)?; + } else { + return Err(TokenEncryptionError::UnknownFormat); + } let public_key = private_key.to_public_key(); Ok(TokenEncryption { private_key, diff --git a/tests/tokens/tokenencryption.spec.ts b/tests/tokens/tokenencryption.spec.ts index bd969447..d57aec9c 100644 --- a/tests/tokens/tokenencryption.spec.ts +++ b/tests/tokens/tokenencryption.spec.ts @@ -4,6 +4,7 @@ import { expect } from "chai"; describe("TokenEncryption", () => { let keyPromise: Promise; + let keyPromisePKCS1: Promise; async function createTokenEncryption() { return new TokenEncryption(await keyPromise); } @@ -24,6 +25,21 @@ describe("TokenEncryption", () => { } satisfies RSAKeyPairOptions<"pem", "pem">, (err, _, privateKey) => { if (err) { reject(err) } else { resolve(Buffer.from(privateKey)) } })); + keyPromisePKCS1 = new Promise((resolve, reject) => generateKeyPair("rsa", { + // Deliberately shorter length to speed up test + modulusLength: 2048, + privateKeyEncoding: { + type: "pkcs1", + format: "pem", + }, + publicKeyEncoding: { + format: "pem", + type: "pkcs1", + } + } satisfies RSAKeyPairOptions<"pem", "pem">, (err, _, privateKey) => { + if (err) { reject(err) } else { resolve(Buffer.from(privateKey)) } + })); + }, ); it('should be able to encrypt a string into a single part', async() => { const tokenEncryption = await createTokenEncryption(); @@ -45,4 +61,9 @@ describe("TokenEncryption", () => { const result = tokenEncryption.decrypt(value); expect(result).to.equal(plaintext); }); + it('should support pkcs1 format keys', async() => { + const tokenEncryption = new TokenEncryption(await keyPromisePKCS1); + const result = tokenEncryption.encrypt('hello world'); + expect(result).to.have.lengthOf(1); + }); });