Compare commits

...

11 Commits

Author SHA1 Message Date
Jack Hadrill 587c3147d1 Merge pull request 'feature/type-declarations' (#2) from feature/type-declarations into master
continuous-integration/drone/push Build is passing Details
Reviewed-on: #2
2022-02-05 22:57:48 +00:00
Jack Hadrill 5a042757dd Improve code quality and add decrypt status return value
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2022-02-05 22:55:31 +00:00
Jack Hadrill 338cb017f2 Update main and types
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-01-31 01:03:18 +00:00
Jack Hadrill e6568a8b00 Add postinstall
continuous-integration/drone/push Build is passing Details
2022-01-31 01:02:04 +00:00
Jack Hadrill d1769651aa Add prepack script
continuous-integration/drone/push Build is passing Details
2022-01-31 00:57:33 +00:00
Jack Hadrill 360f272323 Update typings and build excludes
continuous-integration/drone/push Build is passing Details
2022-01-31 00:56:04 +00:00
Jack Hadrill 3347c49fa2 Exclude dist from compiler input
continuous-integration/drone/push Build is passing Details
2022-01-31 00:53:10 +00:00
Jack Hadrill d534332ec9 Add prepare script
continuous-integration/drone/push Build is failing Details
2022-01-31 00:50:42 +00:00
Jack Hadrill 8fe7072b83 Update main location
continuous-integration/drone/push Build is passing Details
2022-01-31 00:37:57 +00:00
Jack Hadrill b4858a84d9 Add typings location
continuous-integration/drone/push Build is passing Details
2022-01-31 00:35:30 +00:00
Jack Hadrill 1757d7961a Add declaration setting to tsconfig
continuous-integration/drone/push Build is passing Details
2022-01-31 00:29:14 +00:00
10 changed files with 184 additions and 75 deletions

30
package-lock.json generated
View File

@ -7,9 +7,14 @@
"": {
"name": "romulus-js",
"version": "1.0.0",
"hasInstallScript": true,
"license": "ISC",
"dependencies": {
"uuid": "^8.3.2"
},
"devDependencies": {
"@types/jest": "^27.4.0",
"@types/uuid": "^8.3.4",
"jest": "^27.4.7",
"ts-jest": "^27.1.3",
"ts-standard": "^11.0.0",
@ -1220,6 +1225,12 @@
"integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==",
"dev": true
},
"node_modules/@types/uuid": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz",
"integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==",
"dev": true
},
"node_modules/@types/yargs": {
"version": "16.0.4",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz",
@ -6196,6 +6207,14 @@
"punycode": "^2.1.0"
}
},
"node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/v8-compile-cache": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
@ -7411,6 +7430,12 @@
"integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==",
"dev": true
},
"@types/uuid": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz",
"integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==",
"dev": true
},
"@types/yargs": {
"version": "16.0.4",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz",
@ -11064,6 +11089,11 @@
"punycode": "^2.1.0"
}
},
"uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
},
"v8-compile-cache": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",

View File

@ -17,16 +17,20 @@
"author": "Butlersaurus",
"license": "ISC",
"devDependencies": {
"typescript": "^4.5.5",
"ts-standard": "^11.0.0",
"@types/jest": "^27.4.0",
"@types/uuid": "^8.3.4",
"jest": "^27.4.7",
"ts-jest": "^27.1.3",
"@types/jest": "^27.4.0"
"ts-standard": "^11.0.0",
"typescript": "^4.5.5"
},
"jest": {
"verbose": true,
"transform": {
"^.+\\.ts?$": "ts-jest"
}
},
"dependencies": {
"uuid": "^8.3.2"
}
}

View File

@ -1,6 +1,29 @@
import { cryptoAeadDecrypt } from './romulus-m'
export function decrypt (ciphertext: Buffer, associatedData: Buffer, nonce: Buffer, key: Buffer): Buffer {
const plaintext = cryptoAeadDecrypt(Array.from(ciphertext), Array.from(associatedData), Array.from(nonce), Array.from(key))
return Buffer.from(plaintext)
interface DecryptResult {
success: boolean
plaintext: Buffer
}
/**
* Decrypt a Romulus-M encrypted message.
* N.B. Nonces are handled automatically by this function.
* @param buffer The nonce-prepended data to be decrypted.
* @param associatedData The associated data.
* @param key The encryption key.
* @returns A decrypted DecryptResult object.
*/
export function decrypt (buffer: Buffer, associatedData: Buffer, key: Buffer): DecryptResult {
// Split nonce from ciphertext.
const nonce = Array.from(buffer.slice(0, 16))
const ciphertext = Array.from(buffer.slice(16))
// Decrypt ciphertext using the associated data, nonce and encryption key.
const result = cryptoAeadDecrypt(ciphertext, Array.from(associatedData), nonce, Array.from(key))
// Return the ciphertext and decryption status.
return {
success: result.success,
plaintext: Buffer.from(result.plaintext)
}
}

View File

@ -1,6 +1,22 @@
import { cryptoAeadEncrypt } from './romulus-m'
import { v4 as uuidv4 } from 'uuid'
export function encrypt (message: Buffer, associatedData: Buffer, nonce: Buffer, key: Buffer): Buffer {
const ciphertext = cryptoAeadEncrypt(Array.from(message), Array.from(associatedData), Array.from(nonce), Array.from(key))
return Buffer.from(ciphertext)
/**
* Encrypt a message using the Romulus-M encryption algorithm.
* N.B. A nonce is automatically prepended to the ciphertext using this function.
* @param message The plaintext message to encrypt.
* @param associatedData The associated data.
* @param key The encryption key.
* @returns The nonce-prepended ciphertext.
*/
export function encrypt (message: Buffer, associatedData: Buffer, key: Buffer): Buffer {
// Generate a nonce.
const nonce = Buffer.alloc(16)
uuidv4({}, nonce)
// Encrypt the data using the associated data, newly generated nonce and encryption key.
const ciphertext = Buffer.from(cryptoAeadEncrypt(Array.from(message), Array.from(associatedData), Array.from(nonce), Array.from(key)))
// Return the nonce-prepended ciphertext.
return Buffer.concat([nonce, ciphertext])
}

View File

@ -14,13 +14,13 @@ function parse (message: number[], blockLength: number): number[][] {
// Slice message into blocks.
let ret: number[][] = []
while (message.length - cursor >= blockLength) {
ret.push(...[message.slice(cursor, cursor + blockLength)])
ret.push(message.slice(cursor, cursor + blockLength))
cursor = cursor + blockLength
}
// Append any remaining blocks regardless of block length. These will be padded later.
if (message.length - cursor > 0) {
ret.push(...[message.slice(cursor)])
ret.push(message.slice(cursor))
}
// If no message, return a single block.
@ -42,18 +42,18 @@ function parse (message: number[], blockLength: number): number[][] {
function pad (message: number[], padLength: number): number[] {
// If there is no message, return a fully padded block.
if (message.length === 0) {
return Array(16).fill(0)
return Array(16)
}
// Return a copy of the message if no padding is required.
if (message.length === padLength) {
return [...message]
return Array.from(message)
}
// Pad a copy of the message to padLength.
const ret = [...message]
const ret = Array.from(message)
const requiredPadding = padLength - message.length - 1
ret.push(...Array(requiredPadding).fill(0))
ret.push(...Array(requiredPadding))
// Set the final byte of the padded blocked to the length of the original message.
ret[padLength - 1] = message.length
@ -83,12 +83,12 @@ function rho (state: number[], mBlock: number[]): [number[], number[]] {
const gOfS = g(state)
// C = M ⊕ G(S)
const c = [...Array(16).keys()].map(i => mBlock[i] ^ gOfS[i])
const cBlock = Array.from(Array(16).keys()).map(i => mBlock[i] ^ gOfS[i])
// S' = M ⊕ S
const nextState = [...Array(16).keys()].map(i => state[i] ^ mBlock[i])
const nextState = Array.from(Array(16).keys()).map(i => state[i] ^ mBlock[i])
return [nextState, c]
return [nextState, cBlock]
}
/**
@ -102,10 +102,10 @@ function inverseRoh (state: number[], cBlock: number[]): [number[], number[]] {
const gOfS = g(state)
// M = C ⊕ G(S)
const mBlock = [...Array(16).keys()].map(i => cBlock[i] ^ gOfS[i])
const mBlock = Array.from(Array(16).keys()).map(i => cBlock[i] ^ gOfS[i])
// S' = S ⊕ M
const nextState = [...Array(16).keys()].map(i => state[i] ^ mBlock[i])
const nextState = Array.from(Array(16).keys()).map(i => state[i] ^ mBlock[i])
return [nextState, mBlock]
}
@ -138,20 +138,11 @@ function increaseCounter (counter: number[]): number[] {
* @returns A reset counter.
*/
function resetCounter (): number[] {
const counter = Array(COUNTER_LENGTH).fill(0)
const counter = Array(COUNTER_LENGTH)
counter[0] = 1
return counter
}
/**
* Returns a zeroed buffer.
* @param bufferLength The length of the buffer to return.
* @returns A zeroed buffer.
*/
function zeroedBuffer (bufferLength: number): number[] {
return Array(bufferLength).fill(0)
}
/**
* Calculate the domain separation.
* @param combinedData The parsed and concatenated message and associated data,
@ -182,6 +173,7 @@ function calculateDomainSeparation (combinedData: number[][], parsedMessageLengt
/**
* Encrypt a message using the Romulus-M cryptography specification.
* See https://romulusae.github.io/romulus/docs/Romulusv1.3.pdf for more information.
* @param message The message to encrypt.
* @param associatedData The associated data to encrypt.
* @param nonce A 128 bit nonce.
@ -193,7 +185,7 @@ export function cryptoAeadEncrypt (message: number[], associatedData: number[],
const ciphertext = []
// Reset state and counter.
let state = zeroedBuffer(16)
let state = Array(16)
let counter = resetCounter()
// Carve message and associated data into blocks.
@ -216,10 +208,8 @@ export function cryptoAeadEncrypt (message: number[], associatedData: number[],
combinedDataBlocks[associatedDataBlockCount] = pad(combinedDataBlocks[associatedDataBlockCount], 16)
combinedDataBlocks[associatedDataBlockCount + messageBlockCount] = pad(combinedDataBlocks[associatedDataBlockCount + messageBlockCount], 16)
// Do the encryption.
// See https://romulusae.github.io/romulus/docs/Romulusv1.3.pdf for more information.
// Process the associated data.
let x = 8
for (let i = 1; i < Math.floor((associatedDataBlockCount + messageBlockCount) / 2) + 1; i++) {
[state] = rho(state, combinedDataBlocks[2 * i - 1])
counter = increaseCounter(counter)
@ -231,21 +221,23 @@ export function cryptoAeadEncrypt (message: number[], associatedData: number[],
}
if (associatedDataBlockCount % 2 === messageBlockCount % 2) {
[state] = rho(state, zeroedBuffer(16))
[state] = rho(state, Array(16))
} else {
[state] = rho(state, combinedDataBlocks[associatedDataBlockCount + messageBlockCount])
counter = increaseCounter(counter)
}
const [,authenticationTag] = rho(skinnyEncrypt(state, tweakeyEncode(counter, domainSeparation, nonce, key)), zeroedBuffer(16))
// Generate authentication tag.
const [,authenticationTag] = rho(skinnyEncrypt(state, tweakeyEncode(counter, domainSeparation, nonce, key)), Array(16))
if (message.length === 0) {
return authenticationTag
}
state = [...authenticationTag]
state = Array.from(authenticationTag)
counter = resetCounter()
// Encrypt the message.
const originalFinalMessageBlockLength = messageBlocks[messageBlockCount].length
messageBlocks[messageBlockCount] = pad(messageBlocks[messageBlockCount], 16)
@ -263,35 +255,44 @@ export function cryptoAeadEncrypt (message: number[], associatedData: number[],
}
}
// The authentication tag is stored in the final 16 bytes of the ciphertext.
// Store the authentication tag in the final 16 bytes of the ciphertext.
ciphertext.push(...authenticationTag)
return ciphertext
}
/**
* Return interface for decrypting a message.
*/
export interface DecryptResult {
success: boolean
plaintext: number[]
}
/**
* Decrypt a message using the Romulus-M cryptography specification.
* See https://romulusae.github.io/romulus/docs/Romulusv1.3.pdf for more information.
* @param ciphertext The ciphertext to decrypt.
* @param associatedData The associated data.
* @param nonce The nonce.
* @param key The key.
* @returns The decrypted plaintext.
*/
export function cryptoAeadDecrypt (ciphertext: number[], associatedData: number[], nonce: number[], key: number[]): number[] {
export function cryptoAeadDecrypt (ciphertext: number[], associatedData: number[], nonce: number[], key: number[]): DecryptResult {
// Buffer for decrypted message.
const message = []
const cleartext = []
// The authentication tag is represented by the final 16 bytes of the ciphertext.
const authenticationTag = ciphertext.slice(-16)
ciphertext.length -= 16
// Reset state and counter.
let state = zeroedBuffer(16)
let state = Array(16)
let counter = resetCounter()
if (ciphertext.length !== 0) {
// Combine the ciphertext
state = [...authenticationTag]
// Combine the ciphertext.
state = Array.from(authenticationTag)
const ciphertextBlocks = parse(ciphertext, 16)
const ciphertextBlockCount = ciphertextBlocks.length - 1
const finalCiphertextBlockLength = ciphertextBlocks[ciphertextBlockCount].length
@ -305,9 +306,9 @@ export function cryptoAeadDecrypt (ciphertext: number[], associatedData: number[
counter = increaseCounter(counter)
if (i < ciphertextBlockCount) {
message.push(...mBlock)
cleartext.push(...mBlock)
} else {
message.push(...mBlock.slice(0, finalCiphertextBlockLength))
cleartext.push(...mBlock.slice(0, finalCiphertextBlockLength))
}
}
} else {
@ -315,11 +316,11 @@ export function cryptoAeadDecrypt (ciphertext: number[], associatedData: number[
}
// Reset state and counter.
state = zeroedBuffer(16)
state = Array(16)
counter = resetCounter()
// Carve the message and associated data into blocks.
const messageBlocks = parse(message, 16)
const messageBlocks = parse(cleartext, 16)
const messageBlockLength = messageBlocks.length - 1
const associatedDataBlocks = parse(associatedData, 16)
@ -338,6 +339,7 @@ export function cryptoAeadDecrypt (ciphertext: number[], associatedData: number[
combinedData[associatedDataBlockCount] = pad(combinedData[associatedDataBlockCount], 16)
combinedData[associatedDataBlockCount + messageBlockLength] = pad(combinedData[associatedDataBlockCount + messageBlockLength], 16)
// Verifiy associated data.
let x = 8
for (let i = 1; i < Math.floor((associatedDataBlockCount + messageBlockLength) / 2) + 1; i++) {
[state] = rho(state, combinedData[2 * i - 1])
@ -350,23 +352,32 @@ export function cryptoAeadDecrypt (ciphertext: number[], associatedData: number[
}
if (associatedDataBlockCount % 2 === messageBlockLength % 2) {
[state] = rho(state, zeroedBuffer(16))
[state] = rho(state, Array(16))
} else {
[state] = rho(state, combinedData[associatedDataBlockCount + messageBlockLength])
counter = increaseCounter(counter)
}
// Calculate authentication tag.
const [,computedTag] = rho(skinnyEncrypt(state, tweakeyEncode(counter, domainSeparation, nonce, key)), zeroedBuffer(16))
const [,computedTag] = rho(skinnyEncrypt(state, tweakeyEncode(counter, domainSeparation, nonce, key)), Array(16))
// Validate authentication tag.
let compare = 0
for (let i = 0; i < 16; i++) {
compare |= (authenticationTag[i] ^ computedTag[i])
}
if (compare !== 0) {
return []
// Authentication failed.
return {
success: false,
plaintext: []
}
} else {
return message
// Decrypted successfully.
return {
success: true,
plaintext: cleartext
}
}
}

View File

@ -9,7 +9,7 @@ import { MEMBER_MASK, NB_ROUNDS, TWEAK_LENGTH, PT, LFSR_8_TK2, LFSR_8_TK3, S8, C
* @returns The tweakey.
*/
export function tweakeyEncode (counter: number[], domainSeparation: number, nonce: number[], key: number[]): number[] {
return counter.concat([domainSeparation ^ MEMBER_MASK], Array(8).fill(0), nonce, key)
return counter.concat([domainSeparation ^ MEMBER_MASK], Array(8), nonce, key)
}
/**
@ -21,10 +21,10 @@ export function tweakeyEncode (counter: number[], domainSeparation: number, nonc
export function skinnyEncrypt (plaintext: number[], tweakey: number[]): number[] {
const tk = Array(NB_ROUNDS + 1).fill(Array(TWEAK_LENGTH).fill(0))
tk[0] = [...Array(TWEAK_LENGTH).keys()].map(i => tweakey[i])
tk[0] = Array.from(Array(TWEAK_LENGTH).keys()).map(i => tweakey[i])
for (let i = 0; i < NB_ROUNDS - 1; i++) {
tk[i + 1] = [...tk[i]]
tk[i + 1] = Array.from(tk[i])
for (let j = 0; j < TWEAK_LENGTH; j++) {
tk[i + 1][j] = tk[i][j - j % 16 + PT[j % 16]]
@ -36,7 +36,7 @@ export function skinnyEncrypt (plaintext: number[], tweakey: number[]): number[]
}
}
let s = [...Array(16).keys()].map(i => plaintext[i])
let s = Array.from(Array(16).keys()).map(i => plaintext[i])
for (let i = 0; i < NB_ROUNDS; i++) {
for (let j = 0; j < 16; j++) {
s[j] = S8[s[j]]
@ -53,7 +53,7 @@ export function skinnyEncrypt (plaintext: number[], tweakey: number[]): number[]
s = [s[0], s[1], s[2], s[3], s[7], s[4], s[5], s[6], s[10], s[11], s[8], s[9], s[13], s[14], s[15], s[12]]
for (let j = 0; j < 4; j++) {
const tmp = [...s]
const tmp = Array.from(s)
s[j] = tmp[j] ^ tmp[8 + j] ^ tmp[12 + j]
s[4 + j] = tmp[j]
s[8 + j] = tmp[4 + j] ^ tmp[8 + j]
@ -61,5 +61,5 @@ export function skinnyEncrypt (plaintext: number[], tweakey: number[]): number[]
}
}
return [...Array(16).keys()].map(i => s[i])
return Array.from(Array(16).keys()).map(i => s[i])
}

View File

@ -1,21 +1,44 @@
import { decrypt } from '../src/decrypt'
test('Test buffers are supported by decrypt function.', () => {
test('Test nonce parsing by public decrypt function.', () => {
// Given
const ciphertext = Buffer.from([
// Nonce
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
// Ciphertext
225, 53, 3, 212, 22, 112, 246, 194, 61, 171, 230, 187, 157, 102, 32, 76, 62, 65,
25, 202, 255, 201, 206, 49, 60, 58, 82, 216, 72, 116, 106, 129, 162, 142, 69, 40,
167, 88, 94, 195, 174, 217, 242, 149, 224, 125, 196, 237, 172, 165, 116, 119, 128
])
const associatedData = Buffer.from('Some associated data.')
const nonce = Buffer.from('\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f')
const key = Buffer.from('\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f')
// When
const result = decrypt(ciphertext, associatedData, nonce, key)
const result = decrypt(ciphertext, associatedData, key)
// Then
const expectedResult = Buffer.from('Hello, World! This is a test message.')
expect(result).toMatchObject(expectedResult)
expect(result.success).toBe(true)
expect(result.plaintext).toMatchObject(expectedResult)
})
test('Test decryption with an invalid key.', () => {
// Given
const ciphertext = Buffer.from([
// Nonce
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
// Ciphertext
225, 53, 3, 212, 22, 112, 246, 194, 61, 171, 230, 187, 157, 102, 32, 76, 62, 65,
25, 202, 255, 201, 206, 49, 60, 58, 82, 216, 72, 116, 106, 129, 162, 142, 69, 40,
167, 88, 94, 195, 174, 217, 242, 149, 224, 125, 196, 237, 172, 165, 116, 119, 128
])
const associatedData = Buffer.from('Some associated data.')
const key = Buffer.from('\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00')
// When
const result = decrypt(ciphertext, associatedData, key)
// Then
expect(result.success).toBe(false)
expect(result.plaintext).toMatchObject(Buffer.alloc(0))
})

View File

@ -1,20 +1,17 @@
import { decrypt } from '../src/decrypt'
import { encrypt } from '../src/encrypt'
test('Test buffers are supported by encrypt function.', () => {
test('Test nonce generation by public encrypt function.', () => {
// Given
const message = Buffer.from('Hello, World! This is a test message.')
const associatedData = Buffer.from('Some associated data.')
const nonce = Buffer.from('\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f')
const key = Buffer.from('\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f')
// When
const result = encrypt(message, associatedData, nonce, key)
const ciphertext = encrypt(message, associatedData, key)
const plaintext = decrypt(ciphertext, associatedData, key)
// Then
const expectedResult = Buffer.from([
225, 53, 3, 212, 22, 112, 246, 194, 61, 171, 230, 187, 157, 102, 32, 76, 62, 65,
25, 202, 255, 201, 206, 49, 60, 58, 82, 216, 72, 116, 106, 129, 162, 142, 69, 40,
167, 88, 94, 195, 174, 217, 242, 149, 224, 125, 196, 237, 172, 165, 116, 119, 128
])
expect(result).toMatchObject(expectedResult)
expect(plaintext.success).toBe(true)
expect(plaintext.plaintext).toMatchObject(message)
})

View File

@ -1,5 +1,5 @@
import { referenceTests } from './resources/reference-tests'
import { cryptoAeadDecrypt, cryptoAeadEncrypt } from '../src/romulus-m'
import { cryptoAeadDecrypt, cryptoAeadEncrypt, DecryptResult } from '../src/romulus-m'
function parseHexString (string: string): number[] {
const ret = []
@ -24,6 +24,9 @@ test.each(referenceTests)('Perform decryption using reference test %#.', (key, n
const result = cryptoAeadDecrypt(parseHexString(ciphertext), parseHexString(associatedData), parseHexString(nonce), parseHexString(key))
// Then
const expectedResult = parseHexString(plaintext)
const expectedResult: DecryptResult = {
success: true,
plaintext: parseHexString(plaintext)
}
expect(result).toMatchObject(expectedResult)
})

View File

@ -59,7 +59,8 @@ test('Decrypt a message with no associated data.', () => {
// Then
const expectedResult = stringToArray('Hello, World! This is a test message.')
expect(result).toMatchObject(expectedResult)
expect(result.success).toBe(true)
expect(result.plaintext).toMatchObject(expectedResult)
})
test('Decrypt a message with associated data.', () => {
@ -78,5 +79,6 @@ test('Decrypt a message with associated data.', () => {
// Then
const expectedResult = stringToArray('Hello, World! This is a test message.')
expect(result).toMatchObject(expectedResult)
expect(result.success).toBe(true)
expect(result.plaintext).toMatchObject(expectedResult)
})