Skip to content
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

Fix PKCS1 handling #929

Merged
merged 4 commits into from
Apr 16, 2024
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
1 change: 1 addition & 0 deletions changelog.d/929.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Track which key was used to encrypt secrets in storage, and encrypt/decrypt secrets in Rust.
17 changes: 14 additions & 3 deletions src/tokens/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand All @@ -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)]
Expand All @@ -31,8 +34,16 @@ enum DecryptError {
impl TokenEncryption {
pub fn new(private_key_data: Vec<u8>) -> Result<Self, TokenEncryptionError> {
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,
Expand Down
21 changes: 21 additions & 0 deletions tests/tokens/tokenencryption.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { expect } from "chai";

describe("TokenEncryption", () => {
let keyPromise: Promise<Buffer>;
let keyPromisePKCS1: Promise<Buffer>;
async function createTokenEncryption() {
return new TokenEncryption(await keyPromise);
}
Expand All @@ -24,6 +25,21 @@ describe("TokenEncryption", () => {
} satisfies RSAKeyPairOptions<"pem", "pem">, (err, _, privateKey) => {
if (err) { reject(err) } else { resolve(Buffer.from(privateKey)) }
}));
keyPromisePKCS1 = new Promise<Buffer>((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();
Expand All @@ -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);
});
});
Loading