Skip to content

Commit

Permalink
feat: Support sha256 and sha512 oaep algorithms
Browse files Browse the repository at this point in the history
This is particularly useful because CloudFront's Field Level Encryption
uses RSA_OAEP_SHA256_MGF1, which this library doesn't support yet.

Support for oaepHash was added in node 12.9 (nodejs/node#28335), so this
won't work for older node versions. It's still a backwards compatible
change because by default `oaepHash` will be undefined, as before.

I've updated the tests to cover use of the new parameter, but they're
not very strict because they both encrypt and decrypt using the same
parameter.

This means if node silently ignores the oaepHash parameter (as it will
in versions < 12.9) the tests will still pass, which isn't great.

On the other hand, I think this project may still be being tested on an
older version of node, so perhaps the fact the tests won't break is an
unexpected blessing.

I've also tested this manually against AWS CloudFront's Field Level
Encryption and it seems to work.

Resolves aws#198
  • Loading branch information
richardTowers committed Jan 15, 2020
1 parent 0a3107b commit 3065ac0
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 7 deletions.
11 changes: 7 additions & 4 deletions modules/raw-rsa-keyring-node/src/raw_rsa_keyring_node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ import {
constants,
publicEncrypt,
privateDecrypt,
randomBytes
randomBytes,
RsaPublicKey, // eslint-disable-line no-unused-vars
RsaPrivateKey // eslint-disable-line no-unused-vars
} from 'crypto'

import {
Expand Down Expand Up @@ -62,6 +64,7 @@ export type RawRsaKeyringNodeInput = {
keyName: string
rsaKey: RsaKey
padding?: number
oaepHash?: 'sha1'|'sha256'|'sha512'
}

/* Node supports RSA_OAEP_SHA1_MFG1 by default.
Expand All @@ -78,7 +81,7 @@ export class RawRsaKeyringNode extends KeyringNode {
constructor (input: RawRsaKeyringNodeInput) {
super()

const { rsaKey, keyName, keyNamespace, padding = constants.RSA_PKCS1_OAEP_PADDING } = input
const { rsaKey, keyName, keyNamespace, padding = constants.RSA_PKCS1_OAEP_PADDING, oaepHash } = input
const { publicKey, privateKey } = rsaKey
/* Precondition: RsaKeyringNode needs either a public or a private key to operate. */
needs(publicKey || privateKey, 'No Key provided.')
Expand All @@ -90,7 +93,7 @@ export class RawRsaKeyringNode extends KeyringNode {
if (!publicKey) throw new Error('No public key defined in constructor. Encrypt disabled.')
const { buffer, byteOffset, byteLength } = unwrapDataKey(material.getUnencryptedDataKey())
const encryptedDataKey = publicEncrypt(
{ key: publicKey, padding },
{ key: publicKey, padding, oaepHash } as RsaPublicKey,
Buffer.from(buffer, byteOffset, byteLength))
const providerInfo = this.keyName
const providerId = this.keyNamespace
Expand All @@ -112,7 +115,7 @@ export class RawRsaKeyringNode extends KeyringNode {
const { buffer, byteOffset, byteLength } = edk.encryptedDataKey
const encryptedDataKey = Buffer.from(buffer, byteOffset, byteLength)
const unencryptedDataKey = privateDecrypt(
{ key: privateKey, padding },
{ key: privateKey, padding, oaepHash } as RsaPrivateKey,
encryptedDataKey)
return material.setUnencryptedDataKey(unencryptedDataKey, trace)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,15 @@ describe('RawRsaKeyringNode::constructor', () => {
})
})

describe('RawRsaKeyringNode encrypt/decrypt', () => {
const oaepHashOptions: (undefined|'sha1'|'sha256'|'sha512')[] = [undefined, 'sha1', 'sha256', 'sha512']
oaepHashOptions.forEach(oaepHash => describe(`RawRsaKeyringNode encrypt/decrypt for oaepHash=${oaepHash || 'undefined'}`, () => {
const keyNamespace = 'keyNamespace'
const keyName = 'keyName'
const keyring = new RawRsaKeyringNode({
rsaKey: { privateKey: privatePem, publicKey: publicPem },
keyName,
keyNamespace
keyNamespace,
oaepHash
})
let encryptedDataKey: EncryptedDataKey

Expand Down Expand Up @@ -179,4 +181,4 @@ describe('RawRsaKeyringNode encrypt/decrypt', () => {
const material = new NodeDecryptionMaterial(suite, {})
return expect(keyring._unwrapKey(material, encryptedDataKey)).to.rejectedWith(Error)
})
})
}))

0 comments on commit 3065ac0

Please sign in to comment.