Tidy and improve consistency
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
f378a93bae
commit
4241d4545c
138
src/romulus-m.ts
138
src/romulus-m.ts
@ -186,106 +186,125 @@ function calculateDomainSeparation (combinedData: number[][], parsedMessageLengt
|
|||||||
* @param associatedData The associated data to encrypt.
|
* @param associatedData The associated data to encrypt.
|
||||||
* @param nonce A 128 bit nonce.
|
* @param nonce A 128 bit nonce.
|
||||||
* @param key A 128 bit encryption key.
|
* @param key A 128 bit encryption key.
|
||||||
* @returns The ciphertext.
|
* @returns The encrypted ciphertext.
|
||||||
*/
|
*/
|
||||||
export function cryptoAeadEncrypt (message: number[], associatedData: number[], nonce: number[], key: number[]): number[] {
|
export function cryptoAeadEncrypt (message: number[], associatedData: number[], nonce: number[], key: number[]): number[] {
|
||||||
|
// Buffer for ciphertext.
|
||||||
|
const ciphertext = []
|
||||||
|
|
||||||
|
// Reset state and counter.
|
||||||
|
let state = zeroedBuffer(16)
|
||||||
let counter = resetCounter()
|
let counter = resetCounter()
|
||||||
|
|
||||||
// Carve message and associated data into blocks.
|
// Carve message and associated data into blocks.
|
||||||
const parsedMessage = parse(message, 16)
|
const messageBlocks = parse(message, 16)
|
||||||
const parsedMessageLength = parsedMessage.length - 1
|
const messageBlockCount = messageBlocks.length - 1
|
||||||
|
|
||||||
const parsedAssociatedData = parse(associatedData, 16)
|
const associatedDataBlocks = parse(associatedData, 16)
|
||||||
const parsedAssociatedDataLength = parsedAssociatedData.length - 1
|
const associatedDataBlockCount = associatedDataBlocks.length - 1
|
||||||
|
|
||||||
// Concatenate the parsed message and the associated data, excluding each array's first element.
|
// Concatenate the message and associated data blocks, excluding each array's first element.
|
||||||
const combinedData = parsedAssociatedData.slice(1).concat(parsedMessage.slice(1))
|
const combinedDataBlocks = associatedDataBlocks.slice(1).concat(messageBlocks.slice(1))
|
||||||
|
|
||||||
// Insert empty array at position 0.
|
// Insert empty array at position 0.
|
||||||
combinedData.splice(0, 0, [])
|
combinedDataBlocks.splice(0, 0, [])
|
||||||
|
|
||||||
// Calculate domain separation for final encryption stage.
|
// Calculate domain separation for final encryption stage.
|
||||||
const domainSeparation = calculateDomainSeparation(combinedData, parsedMessageLength, parsedAssociatedDataLength)
|
const domainSeparation = calculateDomainSeparation(combinedDataBlocks, messageBlockCount, associatedDataBlockCount)
|
||||||
|
|
||||||
// Pad combined data.
|
// Pad combined data.
|
||||||
combinedData[parsedAssociatedDataLength] = pad(combinedData[parsedAssociatedDataLength], 16)
|
combinedDataBlocks[associatedDataBlockCount] = pad(combinedDataBlocks[associatedDataBlockCount], 16)
|
||||||
combinedData[parsedAssociatedDataLength + parsedMessageLength] = pad(combinedData[parsedAssociatedDataLength + parsedMessageLength], 16)
|
combinedDataBlocks[associatedDataBlockCount + messageBlockCount] = pad(combinedDataBlocks[associatedDataBlockCount + messageBlockCount], 16)
|
||||||
|
|
||||||
// Do the encryption.
|
// Do the encryption.
|
||||||
// See https://romulusae.github.io/romulus/docs/Romulusv1.3.pdf for more information.
|
// See https://romulusae.github.io/romulus/docs/Romulusv1.3.pdf for more information.
|
||||||
let state = zeroedBuffer(16)
|
|
||||||
let c = zeroedBuffer(16)
|
|
||||||
let x = 8
|
let x = 8
|
||||||
for (let i = 1; i < Math.floor((parsedAssociatedDataLength + parsedMessageLength) / 2) + 1; i++) {
|
|
||||||
[state, c] = rho(state, combinedData[2 * i - 1])
|
for (let i = 1; i < Math.floor((associatedDataBlockCount + messageBlockCount) / 2) + 1; i++) {
|
||||||
|
[state] = rho(state, combinedDataBlocks[2 * i - 1])
|
||||||
counter = increaseCounter(counter)
|
counter = increaseCounter(counter)
|
||||||
if (i === Math.floor(parsedAssociatedDataLength / 2) + 1) {
|
if (i === Math.floor(associatedDataBlockCount / 2) + 1) {
|
||||||
x = x ^ 4
|
x = x ^ 4
|
||||||
}
|
}
|
||||||
state = skinnyEncrypt(state, tweakeyEncode(counter, x, combinedData[2 * i], key))
|
state = skinnyEncrypt(state, tweakeyEncode(counter, x, combinedDataBlocks[2 * i], key))
|
||||||
counter = increaseCounter(counter)
|
counter = increaseCounter(counter)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parsedAssociatedDataLength % 2 === parsedMessageLength % 2) {
|
if (associatedDataBlockCount % 2 === messageBlockCount % 2) {
|
||||||
[state, c] = rho(state, zeroedBuffer(16))
|
[state] = rho(state, zeroedBuffer(16))
|
||||||
} else {
|
} else {
|
||||||
[state, c] = rho(state, combinedData[parsedAssociatedDataLength + parsedMessageLength])
|
[state] = rho(state, combinedDataBlocks[associatedDataBlockCount + messageBlockCount])
|
||||||
counter = increaseCounter(counter)
|
counter = increaseCounter(counter)
|
||||||
}
|
}
|
||||||
|
|
||||||
[state, c] = rho(skinnyEncrypt(state, tweakeyEncode(counter, domainSeparation, nonce, key)), zeroedBuffer(16))
|
const [,authenticationTag] = rho(skinnyEncrypt(state, tweakeyEncode(counter, domainSeparation, nonce, key)), zeroedBuffer(16))
|
||||||
|
|
||||||
if (message.length === 0) {
|
if (message.length === 0) {
|
||||||
return c
|
return authenticationTag
|
||||||
}
|
}
|
||||||
|
|
||||||
state = [...c]
|
state = [...authenticationTag]
|
||||||
|
|
||||||
const ciphertext = []
|
|
||||||
const originalLastBlockLength = parsedMessage[parsedMessageLength].length
|
|
||||||
parsedMessage[parsedMessageLength] = pad(parsedMessage[parsedMessageLength], 16)
|
|
||||||
|
|
||||||
counter = resetCounter()
|
counter = resetCounter()
|
||||||
for (let i = 1; i < parsedMessageLength + 1; i++) {
|
|
||||||
|
const originalFinalMessageBlockLength = messageBlocks[messageBlockCount].length
|
||||||
|
messageBlocks[messageBlockCount] = pad(messageBlocks[messageBlockCount], 16)
|
||||||
|
|
||||||
|
for (let i = 1; i < messageBlockCount + 1; i++) {
|
||||||
state = skinnyEncrypt(state, tweakeyEncode(counter, 4, nonce, key))
|
state = skinnyEncrypt(state, tweakeyEncode(counter, 4, nonce, key))
|
||||||
let x
|
|
||||||
[state, x] = rho(state, parsedMessage[i])
|
let cBlock
|
||||||
|
[state, cBlock] = rho(state, messageBlocks[i])
|
||||||
counter = increaseCounter(counter)
|
counter = increaseCounter(counter)
|
||||||
if (i < parsedMessageLength) {
|
|
||||||
ciphertext.push(...x)
|
if (i < messageBlockCount) {
|
||||||
|
ciphertext.push(...cBlock)
|
||||||
} else {
|
} else {
|
||||||
ciphertext.push(...x.slice(0, originalLastBlockLength))
|
ciphertext.push(...cBlock.slice(0, originalFinalMessageBlockLength))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ciphertext.push(...c)
|
// The authentication tag is stored in the final 16 bytes of the ciphertext.
|
||||||
|
ciphertext.push(...authenticationTag)
|
||||||
|
|
||||||
return ciphertext
|
return ciphertext
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypt a message using the Romulus-M cryptography specification.
|
||||||
|
* @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[]): number[] {
|
||||||
// Buffer for decrypted message.
|
// Buffer for decrypted message.
|
||||||
const message = []
|
const message = []
|
||||||
|
|
||||||
// The authentication tag is represented by the last 16 bytes of the ciphertext.
|
// The authentication tag is represented by the final 16 bytes of the ciphertext.
|
||||||
const authenticationTag = ciphertext.slice(-16)
|
const authenticationTag = ciphertext.slice(-16)
|
||||||
ciphertext.length -= 16
|
ciphertext.length -= 16
|
||||||
|
|
||||||
|
// Reset state and counter.
|
||||||
let state = zeroedBuffer(16)
|
let state = zeroedBuffer(16)
|
||||||
let counter = resetCounter()
|
let counter = resetCounter()
|
||||||
|
|
||||||
if (ciphertext.length !== 0) {
|
if (ciphertext.length !== 0) {
|
||||||
|
// Combine the ciphertext
|
||||||
state = [...authenticationTag]
|
state = [...authenticationTag]
|
||||||
const parsedCiphertext = parse(ciphertext, 16)
|
const ciphertextBlocks = parse(ciphertext, 16)
|
||||||
const parsedCiphertextLength = parsedCiphertext.length - 1
|
const ciphertextBlockCount = ciphertextBlocks.length - 1
|
||||||
const finalCiphertextBlockLength = parsedCiphertext[parsedCiphertextLength].length
|
const finalCiphertextBlockLength = ciphertextBlocks[ciphertextBlockCount].length
|
||||||
parsedCiphertext[parsedCiphertextLength] = pad(parsedCiphertext[parsedCiphertextLength], 16)
|
ciphertextBlocks[ciphertextBlockCount] = pad(ciphertextBlocks[ciphertextBlockCount], 16)
|
||||||
|
|
||||||
for (let i = 1; i < parsedCiphertextLength + 1; i++) {
|
for (let i = 1; i < ciphertextBlockCount + 1; i++) {
|
||||||
state = skinnyEncrypt(state, tweakeyEncode(counter, 4, nonce, key))
|
state = skinnyEncrypt(state, tweakeyEncode(counter, 4, nonce, key))
|
||||||
|
|
||||||
let mBlock
|
let mBlock
|
||||||
[state, mBlock] = inverseRoh(state, parsedCiphertext[i])
|
[state, mBlock] = inverseRoh(state, ciphertextBlocks[i])
|
||||||
counter = increaseCounter(counter)
|
counter = increaseCounter(counter)
|
||||||
if (i < parsedCiphertextLength) {
|
|
||||||
|
if (i < ciphertextBlockCount) {
|
||||||
message.push(...mBlock)
|
message.push(...mBlock)
|
||||||
} else {
|
} else {
|
||||||
message.push(...mBlock.slice(0, finalCiphertextBlockLength))
|
message.push(...mBlock.slice(0, finalCiphertextBlockLength))
|
||||||
@ -295,47 +314,50 @@ export function cryptoAeadDecrypt (ciphertext: number[], associatedData: number[
|
|||||||
state = []
|
state = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset state and counter.
|
||||||
state = zeroedBuffer(16)
|
state = zeroedBuffer(16)
|
||||||
counter = resetCounter()
|
counter = resetCounter()
|
||||||
|
|
||||||
const parsedAssociatedData = parse(associatedData, 16)
|
// Carve the message and associated data into blocks.
|
||||||
const parsedAssociatedDataLength = parsedAssociatedData.length - 1
|
const messageBlocks = parse(message, 16)
|
||||||
|
const messageBlockLength = messageBlocks.length - 1
|
||||||
|
|
||||||
const parsedMessage = parse(message, 16)
|
const associatedDataBlocks = parse(associatedData, 16)
|
||||||
const parsedMessageLength = parsedMessage.length - 1
|
const associatedDataBlockCount = associatedDataBlocks.length - 1
|
||||||
|
|
||||||
// Concatenate the parsed message and the associated data, excluding each array's first element.
|
// Concatenate the message and associated data blocks, excluding each array's first element.
|
||||||
const combinedData = parsedAssociatedData.slice(1).concat(parsedMessage.slice(1))
|
const combinedData = associatedDataBlocks.slice(1).concat(messageBlocks.slice(1))
|
||||||
|
|
||||||
// Insert empty array at position 0.
|
// Insert empty array at position 0.
|
||||||
combinedData.splice(0, 0, [])
|
combinedData.splice(0, 0, [])
|
||||||
|
|
||||||
// Calculate domain separation for final decryption stage.
|
// Calculate domain separation for final decryption stage.
|
||||||
const domainSeparation = calculateDomainSeparation(combinedData, parsedMessageLength, parsedAssociatedDataLength)
|
const domainSeparation = calculateDomainSeparation(combinedData, messageBlockLength, associatedDataBlockCount)
|
||||||
|
|
||||||
// Pad combined data.
|
// Pad combined data.
|
||||||
combinedData[parsedAssociatedDataLength] = pad(combinedData[parsedAssociatedDataLength], 16)
|
combinedData[associatedDataBlockCount] = pad(combinedData[associatedDataBlockCount], 16)
|
||||||
combinedData[parsedAssociatedDataLength + parsedMessageLength] = pad(combinedData[parsedAssociatedDataLength + parsedMessageLength], 16)
|
combinedData[associatedDataBlockCount + messageBlockLength] = pad(combinedData[associatedDataBlockCount + messageBlockLength], 16)
|
||||||
|
|
||||||
let x = 8
|
let x = 8
|
||||||
for (let i = 1; i < Math.floor((parsedAssociatedDataLength + parsedMessageLength) / 2) + 1; i++) {
|
for (let i = 1; i < Math.floor((associatedDataBlockCount + messageBlockLength) / 2) + 1; i++) {
|
||||||
[state] = rho(state, combinedData[2 * i - 1])
|
[state] = rho(state, combinedData[2 * i - 1])
|
||||||
counter = increaseCounter(counter)
|
counter = increaseCounter(counter)
|
||||||
if (i === Math.floor(parsedAssociatedDataLength / 2) + 1) {
|
if (i === Math.floor(associatedDataBlockCount / 2) + 1) {
|
||||||
x = x ^ 4
|
x = x ^ 4
|
||||||
}
|
}
|
||||||
state = skinnyEncrypt(state, tweakeyEncode(counter, x, combinedData[2 * i], key))
|
state = skinnyEncrypt(state, tweakeyEncode(counter, x, combinedData[2 * i], key))
|
||||||
counter = increaseCounter(counter)
|
counter = increaseCounter(counter)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parsedAssociatedDataLength % 2 === parsedMessageLength % 2) {
|
if (associatedDataBlockCount % 2 === messageBlockLength % 2) {
|
||||||
[state] = rho(state, zeroedBuffer(16))
|
[state] = rho(state, zeroedBuffer(16))
|
||||||
} else {
|
} else {
|
||||||
[state] = rho(state, combinedData[parsedAssociatedDataLength + parsedMessageLength])
|
[state] = rho(state, combinedData[associatedDataBlockCount + messageBlockLength])
|
||||||
counter = increaseCounter(counter)
|
counter = increaseCounter(counter)
|
||||||
}
|
}
|
||||||
let computedTag
|
|
||||||
[state, computedTag] = rho(skinnyEncrypt(state, tweakeyEncode(counter, domainSeparation, nonce, key)), zeroedBuffer(16))
|
// Calculate authentication tag.
|
||||||
|
const [,computedTag] = rho(skinnyEncrypt(state, tweakeyEncode(counter, domainSeparation, nonce, key)), zeroedBuffer(16))
|
||||||
|
|
||||||
let compare = 0
|
let compare = 0
|
||||||
for (let i = 0; i < 16; i++) {
|
for (let i = 0; i < 16; i++) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user