Skip to content

Commit 9123145

Browse files
authored
Merge pull request #10 from simonratner/issue-5
Allow specifying a custom id field name
2 parents ae417b7 + 1281f23 commit 9123145

File tree

2 files changed

+48
-8
lines changed

2 files changed

+48
-8
lines changed

lib/encrypted-attr.js

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ function EncryptedAttributes (attributes, options) {
1111

1212
let prefix = Buffer.from(`${alg}$`).toString('base64')
1313

14+
// Default to `id` attribute as the object identifier, but allow override.
15+
let verifyId = options.verifyId
16+
if (verifyId && typeof verifyId !== 'string') {
17+
verifyId = 'id'
18+
}
19+
1420
function encryptAttribute (obj, val) {
1521
// Encrypted attributes are prefixed with "aes-256-gcm$", the base64
1622
// encoding of which is in `prefix`. Nulls are not encrypted.
@@ -20,13 +26,13 @@ function EncryptedAttributes (attributes, options) {
2026
if (typeof val !== 'string') {
2127
throw new Error('Encrypted attribute must be a string')
2228
}
23-
if (options.verifyId && !obj.id) {
24-
throw new Error('Cannot encrypt without \'id\' attribute')
29+
if (verifyId && !obj[verifyId]) {
30+
throw new Error(`Cannot encrypt without '${verifyId}'`)
2531
}
2632
// Recommended 96-bit nonce with AES-GCM.
2733
let iv = crypto.randomBytes(12)
2834
let aad = Buffer.from(
29-
`${alg}$${options.verifyId ? obj.id.toString() : ''}$${options.keyId}`)
35+
`${alg}$${verifyId ? obj[verifyId].toString() : ''}$${options.keyId}`)
3036
let key = Buffer.from(options.keys[options.keyId], 'base64')
3137
let gcm = crypto.createCipheriv(alg, key, iv)
3238
gcm.setAAD(aad)
@@ -55,8 +61,8 @@ function EncryptedAttributes (attributes, options) {
5561
if (typeof val !== 'string' || !val.startsWith(prefix)) {
5662
return val
5763
}
58-
if (options.verifyId && !obj.id) {
59-
throw new Error('Cannot decrypt without \'id\' attribute')
64+
if (verifyId && !obj[verifyId]) {
65+
throw new Error(`Cannot decrypt without '${verifyId}'`)
6066
}
6167
let parts = val.split('$').map((x) => Buffer.from(x, 'base64'))
6268
let aad = parts[0]
@@ -68,8 +74,8 @@ function EncryptedAttributes (attributes, options) {
6874
let id = parts[1]
6975
let keyId = parts[2]
7076

71-
if (options.verifyId && (id !== obj.id.toString())) {
72-
throw new Error('Encrypted attribute has invalid id')
77+
if (verifyId && (id !== obj[verifyId].toString())) {
78+
throw new Error(`Encrypted attribute has invalid '${verifyId}'`)
7379
}
7480
if (!options.keys[keyId]) {
7581
throw new Error('Encrypted attribute has invalid key id')

test/encrypted-attr.spec.js

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,18 @@ describe('encrypted attributes', function () {
7878
expect(() => enc.encryptAttribute(obj, 'value'), 'to throw', /cannot encrypt without 'id'/i)
7979
})
8080

81+
it('should throw when encrypting without id (custom field)', function () {
82+
let enc = EncryptedAttributes(['secret'], Object.assign(this.options, {verifyId: '_id'}))
83+
let obj = {}
84+
expect(() => enc.encryptAttribute(obj, 'value'), 'to throw', /cannot encrypt without '_id'/i)
85+
})
86+
87+
it('should encrypt with custom id field name', function () {
88+
let enc = EncryptedAttributes(['secret'], Object.assign(this.options, {verifyId: '_id'}))
89+
let obj = {_id: 1}
90+
expect(() => enc.encryptAttribute(obj, 'value'), 'not to throw')
91+
})
92+
8193
it('should encrypt without id when verify id option is false', function () {
8294
let enc = EncryptedAttributes(['secret'], Object.assign(this.options, {verifyId: false}))
8395
let obj = {}
@@ -142,7 +154,15 @@ describe('encrypted attributes', function () {
142154
let obj = {id: 1}
143155
// aad: aes-256-gcm$02$k1
144156
let invalidId = 'YWVzLTI1Ni1nY20kMiRrMQ==$sK91YfUvv+O8Jx/m$OOQniq8=$WLbWYz7uCQBTNO3Fc+5UvA'
145-
expect(() => enc.decryptAttribute(obj, invalidId), 'to throw', /invalid id/i)
157+
expect(() => enc.decryptAttribute(obj, invalidId), 'to throw', /invalid 'id'/i)
158+
})
159+
160+
it('should throw when decrypting with invalid id (custom field)', function () {
161+
let enc = EncryptedAttributes(['secret'], Object.assign(this.options, {verifyId: '_id'}))
162+
let obj = {_id: 1}
163+
// aad: aes-256-gcm$02$k1
164+
let invalidId = 'YWVzLTI1Ni1nY20kMiRrMQ==$sK91YfUvv+O8Jx/m$OOQniq8=$WLbWYz7uCQBTNO3Fc+5UvA'
165+
expect(() => enc.decryptAttribute(obj, invalidId), 'to throw', /invalid '_id'/i)
146166
})
147167

148168
it('should throw when decrypting with invalid key id', function () {
@@ -176,6 +196,20 @@ describe('encrypted attributes', function () {
176196
expect(() => enc.decryptAttribute(obj, encrypted), 'to throw', /cannot decrypt without 'id'/i)
177197
})
178198

199+
it('should throw when decrypting without id (custom field)', function () {
200+
let enc = EncryptedAttributes(['secret'], Object.assign(this.options, {verifyId: '_id'}))
201+
let obj = {}
202+
let encrypted = 'YWVzLTI1Ni1nY20kMSRrMQ==$sK91YfUvv+O8Jx/m$OOQniq8=$WLbWYz7uCQBTNO3Fc+5UvA'
203+
expect(() => enc.decryptAttribute(obj, encrypted), 'to throw', /cannot decrypt without '_id'/i)
204+
})
205+
206+
it('should decrypt with custom id field name', function () {
207+
let enc = EncryptedAttributes(['secret'], Object.assign(this.options, {verifyId: '_id'}))
208+
let obj = {_id: 1}
209+
let encrypted = 'YWVzLTI1Ni1nY20kMSRrMQ==$sK91YfUvv+O8Jx/m$OOQniq8=$WLbWYz7uCQBTNO3Fc+5UvA'
210+
expect(() => enc.decryptAttribute(obj, encrypted), 'not to throw')
211+
})
212+
179213
it('should decrypt without id when verify id option is false', function () {
180214
let enc = EncryptedAttributes(['secret'], Object.assign(this.options, {verifyId: false}))
181215
let obj = {}

0 commit comments

Comments
 (0)