Skip to content

Commit

Permalink
feat: timingSafeStringEqual
Browse files Browse the repository at this point in the history
  • Loading branch information
kirillgroshkov committed Mar 7, 2024
1 parent 7cfb199 commit c033774
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 0 deletions.
13 changes: 13 additions & 0 deletions src/security/crypto.util.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
encryptObject,
encryptRandomIVBuffer,
encryptString,
timingSafeStringEqual,
} from './crypto.util'

const encKeyBuffer = Buffer.from(TEST_ENC_KEY, 'base64')
Expand Down Expand Up @@ -68,3 +69,15 @@ test('encryptObject, decryptObject', () => {
// Should be deterministic:
expect(encryptObject(obj1, encKeyBuffer)).toEqual(enc)
})

test('timingSafeStringEquals', () => {
const pw = 'hello!@#123'

expect(timingSafeStringEqual(undefined, pw)).toBe(false)
expect(timingSafeStringEqual('', pw)).toBe(false)
expect(timingSafeStringEqual('', '')).toBe(true)
expect(timingSafeStringEqual('abc', 'abc')).toBe(true)
expect(timingSafeStringEqual('abc', 'abd')).toBe(false)
expect(timingSafeStringEqual(pw, pw)).toBe(true)
expect(timingSafeStringEqual(pw, undefined)).toBe(false)
})
17 changes: 17 additions & 0 deletions src/security/crypto.util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,20 @@ function getCryptoParams(secretKeyBuffer: Buffer): { key: Buffer; iv: Buffer } {
const iv = md5AsBuffer(Buffer.concat([secretKeyBuffer, key]))
return { key, iv }
}

/**
* Wraps `crypto.timingSafeEqual` and allows it to be used with String inputs:
*
* 1. Does length check first and short-circuits on length mismatch. Because `crypto.timingSafeEqual` only works with same-length inputs.
*
* Relevant read:
* https://medium.com/nerd-for-tech/checking-api-key-without-shooting-yourself-in-the-foot-javascript-nodejs-f271e47bb428
* https://codahale.com/a-lesson-in-timing-attacks/
* https://github.com/suryagh/tsscmp/blob/master/lib/index.js
*
* Returns true if inputs are equal, false otherwise.
*/
export function timingSafeStringEqual(s1: string | undefined, s2: string | undefined): boolean {
if (s1 === undefined || s2 === undefined || s1.length !== s2.length) return false
return crypto.timingSafeEqual(Buffer.from(s1), Buffer.from(s2))
}

0 comments on commit c033774

Please sign in to comment.