Skip to content

Commit

Permalink
Merge b46501b into f0acd09
Browse files Browse the repository at this point in the history
  • Loading branch information
karrui committed Jun 11, 2020
2 parents f0acd09 + b46501b commit c1dcd4c
Show file tree
Hide file tree
Showing 15 changed files with 497 additions and 313 deletions.
84 changes: 82 additions & 2 deletions spec/init.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,87 @@
import formsg from '../src/index'
import {
getVerificationPublicKey,
getSigningPublicKey,
} from '../src/util/publicKey'
import { VERIFICATION_KEYS } from '../src/resource/verification-keys'
import { SIGNING_KEYS } from '../src/resource/signing-keys'
import Webhooks from '../src/webhooks'
import Crypto from '../src/crypto'
import Verification from '../src/verification'

const TEST_PUBLIC_KEY = SIGNING_KEYS.test.publicKey

describe('FormSG SDK', () => {
it('should be able to initialise without arguments', () => {
expect(() => formsg()).not.toThrow()
describe('Initialisation', () => {
it('should be able to initialise without arguments', () => {
expect(() => formsg()).not.toThrow()
})

it('should be able to initialise with valid verification options', () => {
// Arrange
const TEST_TRANSACTION_EXPIRY = 10000
const sdk = formsg({
mode: 'test',
verificationOptions: {
secretKey: VERIFICATION_KEYS.test.secretKey,
transactionExpiry: TEST_TRANSACTION_EXPIRY,
},
})

expect(sdk.verification.verificationPublicKey).toEqual(
VERIFICATION_KEYS.test.publicKey
)
expect(sdk.verification.verificationSecretKey).toEqual(
VERIFICATION_KEYS.test.secretKey
)
expect(sdk.verification.transactionExpiry).toEqual(
TEST_TRANSACTION_EXPIRY
)
})
})

describe('Public keys', () => {
it('should get the correct verification public key given a mode', () => {
expect(getVerificationPublicKey('test')).toBe(
VERIFICATION_KEYS.test.publicKey
)
expect(getVerificationPublicKey('staging')).toBe(
VERIFICATION_KEYS.staging.publicKey
)
expect(getVerificationPublicKey('development')).toBe(
VERIFICATION_KEYS.development.publicKey
)
expect(getVerificationPublicKey('production')).toBe(
VERIFICATION_KEYS.production.publicKey
)
expect(getVerificationPublicKey()).toBe(
VERIFICATION_KEYS.production.publicKey
)
})
})

it('should get the correct signing key given a mode', () => {
expect(getSigningPublicKey('test')).toBe(SIGNING_KEYS.test.publicKey)
expect(getSigningPublicKey('staging')).toBe(SIGNING_KEYS.staging.publicKey)
expect(getSigningPublicKey('development')).toBe(
SIGNING_KEYS.development.publicKey
)
expect(getSigningPublicKey('production')).toBe(
SIGNING_KEYS.production.publicKey
)
expect(getSigningPublicKey()).toBe(SIGNING_KEYS.production.publicKey)
})

it('should be able to initialise with given publicKey init param', () => {
// Act
const userSpecifiedKey = TEST_PUBLIC_KEY
// Create SDK with a specified public key
const sdk = formsg({ publicKey: userSpecifiedKey })

// Assert
// All various keys used by subpackages should be the given public key.
expect(sdk.crypto.publicSigningKey).toEqual(userSpecifiedKey)
expect(sdk.verification.verificationPublicKey).toEqual(userSpecifiedKey)
expect(sdk.webhooks.publicKey).toEqual(userSpecifiedKey)
})
})
102 changes: 0 additions & 102 deletions spec/verification.spec.ts

This file was deleted.

46 changes: 46 additions & 0 deletions spec/verification/utils.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {
formatToBaseString,
isSignatureTimeValid,
} from '../../src/verification/utils'

describe('Verification Utils', () => {
const TEST_TRANSACTION_EXPIRY = 10000
const TIME = 1588658696255
const PARAMS = {
transactionId: 'transactionId',
formId: 'formId',
fieldId: 'fieldId',
answer: 'answer',
}

describe('formatToBaseString', () => {
it('should construct a basestring', () => {
// Act
const baseString = formatToBaseString({
time: TIME,
...PARAMS,
})

// Assert
const expectedBaseString = `${PARAMS.transactionId}.${PARAMS.formId}.${PARAMS.fieldId}.${PARAMS.answer}.${TIME}`
expect(baseString).toBe(expectedBaseString)
})
})

describe('isSignatureTimeValid', () => {
it('should return true if time is valid', () => {
// Valid time less than the TEST_TRANSACTION EXPIRY
const validTime = TIME + 1
expect(
isSignatureTimeValid(TIME, validTime, TEST_TRANSACTION_EXPIRY)
).toBe(true)
})

it('should return false if time is invalid (expired)', () => {
const expiredTime = TIME + TEST_TRANSACTION_EXPIRY * 2000
expect(
isSignatureTimeValid(TIME, expiredTime, TEST_TRANSACTION_EXPIRY)
).toBe(false)
})
})
})
124 changes: 124 additions & 0 deletions spec/verification/verification.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { VERIFICATION_KEYS } from '../../src/resource/verification-keys'
import Verification from '../../src/verification'
import { MissingSecretKeyError, MissingPublicKeyError } from '../../src/errors'

const publicKey = VERIFICATION_KEYS.test.publicKey
const secretKey = VERIFICATION_KEYS.test.secretKey

const TEST_TRANSACTION_EXPIRY = 10000
const TEST_PARAMS = {
transactionId: 'transactionId',
formId: 'formId',
fieldId: 'fieldId',
answer: 'answer',
}
const TIME = 1588658696255
const VALID_SIGNATURE = `f=formId,v=transactionId,t=${TIME},s=XLF1V4RDu8dEJLq1yK3UN92TwiekVoif7PX4V8cXr5ERfIQXlOcO+ZOFAawawKWhFSqScg5z1Ro+Y+bMeNmRAg==`
const INVALID_SIGNATURE = `f=formId,v=transactionId,t=${TIME},s=InvalidSignatureyK3UN92TwiekVoif7PX4V8cXr5ERfIQXlOcO+ZOFAawawKWhFSqScg5z1Ro+Y+bMeNmRAg==`
const DEFORMED_SIGNATURE = `abcdefg`

const VALID_AUTH_PAYLOAD = {
signatureString: VALID_SIGNATURE,
submissionCreatedAt: TIME + 1,
fieldId: TEST_PARAMS.fieldId,
answer: TEST_PARAMS.answer,
}

describe('Verification', () => {
describe('Initialization', () => {
it('should not generate signatures if secret key is not provided', () => {
// Arrange
const verification = new Verification({
// No secret key provided.
transactionExpiry: TEST_TRANSACTION_EXPIRY,
})

// Act
expect(() => verification.generateSignature(TEST_PARAMS)).toThrow(
MissingSecretKeyError
)
})

it('should not authenticate if public key is not provided', () => {
const verification = new Verification({
// No public key provided.
transactionExpiry: TEST_TRANSACTION_EXPIRY,
verificationSecretKey: secretKey,
})

expect(() => verification.authenticate(VALID_AUTH_PAYLOAD)).toThrow(
MissingPublicKeyError
)
})

it('should not authenticate if transaction expiry is not provided', () => {
const verification = new Verification({
// No transaction expiry provided.
verificationPublicKey: publicKey,
verificationSecretKey: secretKey,
})

expect(() => verification.authenticate(VALID_AUTH_PAYLOAD)).toThrow(
'Provide a transaction expiry when when initializing the FormSG SDK to use this function.'
)
})
})

describe('Usage', () => {
const verification = new Verification({
transactionExpiry: TEST_TRANSACTION_EXPIRY,
verificationSecretKey: secretKey,
verificationPublicKey: publicKey,
})

let now: jest.MockInstance<number, any>

beforeAll(() => {
now = jest.spyOn(Date, 'now').mockImplementation(() => {
return TIME
})
})

afterAll(() => {
now.mockRestore()
})

it('should generate a signature', () => {
expect(verification.generateSignature(TEST_PARAMS)).toBe(VALID_SIGNATURE)
})

it('should successfully authenticate a valid signature', () => {
expect(verification.authenticate(VALID_AUTH_PAYLOAD)).toBe(true)
})

it('should fail to authenticate a valid signature if it is expired', () => {
const payload = {
signatureString: VALID_SIGNATURE,
submissionCreatedAt: TIME + TEST_TRANSACTION_EXPIRY * 2000,
fieldId: TEST_PARAMS.fieldId,
answer: TEST_PARAMS.answer,
}
expect(verification.authenticate(payload)).toBe(false)
})

it('should fail to authenticate an invalid signature', () => {
const payload = {
signatureString: INVALID_SIGNATURE,
submissionCreatedAt: TIME + 1,
fieldId: TEST_PARAMS.fieldId,
answer: TEST_PARAMS.answer,
}
expect(verification.authenticate(payload)).toBe(false)
})

it('should fail to authenticate a deformed signature', () => {
const payload = {
signatureString: DEFORMED_SIGNATURE,
submissionCreatedAt: TIME + 1,
fieldId: TEST_PARAMS.fieldId,
answer: TEST_PARAMS.answer,
}
expect(verification.authenticate(payload)).toBe(false)
})
})
})
4 changes: 2 additions & 2 deletions src/errors.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
class MissingSecretKeyError extends Error {
constructor(
message = 'Provide a secret key when when initializing the FormSG SDK to use this function.'
message = 'Provide a secret key when initializing the FormSG SDK to use this function.'
) {
super(message)
this.name = this.constructor.name
Expand All @@ -12,7 +12,7 @@ class MissingSecretKeyError extends Error {
}
class MissingPublicKeyError extends Error {
constructor(
message = 'Provide a public key when when initializing the FormSG SDK to use this function.'
message = 'Provide a public key when initializing the FormSG SDK to use this function.'
) {
super(message)
this.name = this.constructor.name
Expand Down

0 comments on commit c1dcd4c

Please sign in to comment.