Skip to content

Commit

Permalink
fix: AES-128-MMO incorrect calculation (#1292)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nerivec authored Jan 12, 2025
1 parent 801212f commit 705f765
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 21 deletions.
42 changes: 21 additions & 21 deletions src/zspec/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,37 +185,37 @@ export function crc16CCITTFALSE(data: number[] | Uint8Array | Buffer): number {
return calcCRC(data, 16, 0x1021, 0xffff);
}

function aes128MmoHashUpdate(result: Buffer, data: Buffer, dataSize: number): void {
while (dataSize >= AES_MMO_128_BLOCK_SIZE) {
const cipher = createCipheriv('aes-128-ecb', result, null);
const block = data.subarray(0, AES_MMO_128_BLOCK_SIZE);
const encryptedBlock = Buffer.concat([cipher.update(block), cipher.final()]);

// XOR encrypted and plaintext
for (let i = 0; i < AES_MMO_128_BLOCK_SIZE; i++) {
result[i] = encryptedBlock[i] ^ block[i];
}

data = data.subarray(AES_MMO_128_BLOCK_SIZE);
dataSize -= AES_MMO_128_BLOCK_SIZE;
}
}

/**
* AES-128-MMO (Matyas-Meyer-Oseas) hashing (using node 'crypto' built-in with 'aes-128-ecb')
*
* Used for Install Codes - see Document 13-0402-13 - 10.1
*/
export function aes128MmoHash(data: Buffer): Buffer {
const update = (result: Buffer, data: Buffer, dataSize: number): boolean => {
while (dataSize >= AES_MMO_128_BLOCK_SIZE) {
const cipher = createCipheriv('aes-128-ecb', result, null);
const block = data.subarray(0, AES_MMO_128_BLOCK_SIZE);
const encryptedBlock = Buffer.concat([cipher.update(block), cipher.final()]);

// XOR encrypted and plaintext
for (let i = 0; i < AES_MMO_128_BLOCK_SIZE; i++) {
result[i] = encryptedBlock[i] ^ block[i];
}

data = data.subarray(AES_MMO_128_BLOCK_SIZE);
dataSize -= AES_MMO_128_BLOCK_SIZE;
}

return true;
};

const hashResult = Buffer.alloc(AES_MMO_128_BLOCK_SIZE);
const temp = Buffer.alloc(AES_MMO_128_BLOCK_SIZE);
let remainingLength = data.length;
let position = 0;

for (position; remainingLength >= AES_MMO_128_BLOCK_SIZE; ) {
update(hashResult, data.subarray(position, position + AES_MMO_128_BLOCK_SIZE), data.length);
const chunk = data.subarray(position, position + AES_MMO_128_BLOCK_SIZE);

aes128MmoHashUpdate(hashResult, chunk, chunk.length);

position += AES_MMO_128_BLOCK_SIZE;
remainingLength -= AES_MMO_128_BLOCK_SIZE;
Expand All @@ -230,14 +230,14 @@ export function aes128MmoHash(data: Buffer): Buffer {

// if appending the bit string will push us beyond the 16-byte boundary, hash that block and append another 16-byte block
if (AES_MMO_128_BLOCK_SIZE - remainingLength < 3) {
update(hashResult, temp, AES_MMO_128_BLOCK_SIZE);
aes128MmoHashUpdate(hashResult, temp, AES_MMO_128_BLOCK_SIZE);
temp.fill(0);
}

temp[AES_MMO_128_BLOCK_SIZE - 2] = (data.length >> 5) & 0xff;
temp[AES_MMO_128_BLOCK_SIZE - 1] = (data.length << 3) & 0xff;

update(hashResult, temp, AES_MMO_128_BLOCK_SIZE);
aes128MmoHashUpdate(hashResult, temp, AES_MMO_128_BLOCK_SIZE);

const result = Buffer.alloc(AES_MMO_128_BLOCK_SIZE);

Expand Down
6 changes: 6 additions & 0 deletions test/zspec/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,15 @@ describe('ZSpec Utils', () => {
const val1 = Buffer.from('83FED3407A939723A5C639FF4C12', 'hex');
// Example from Zigbee spec
const val2 = Buffer.from('83FED3407A939723A5C639B26916D505C3B5', 'hex');
// Example from Zigbee spec C.6.1
const val3 = Buffer.from('76777475727370717E7F7C7D7A7B7879C0', 'hex');
// Example from Zigbee spec C.6.1
const val4 = Buffer.from('1C1D1E1F18191A1B14151617101112133C3D537529A7A9A03F669DCD886CB52C', 'hex');

expect(ZSpec.Utils.aes128MmoHash(val1)).toStrictEqual(Buffer.from('58C1828CF7F1C3FE29E7B1024AD84BFA', 'hex'));
expect(ZSpec.Utils.aes128MmoHash(val2)).toStrictEqual(Buffer.from('66B6900981E1EE3CA4206B6B861C02BB', 'hex'));
expect(ZSpec.Utils.aes128MmoHash(val3)).toStrictEqual(Buffer.from('3C3D537529A7A9A03F669DCD886CB52C', 'hex'));
expect(ZSpec.Utils.aes128MmoHash(val4)).toStrictEqual(Buffer.from('4512807BF94CB3400F0E2C25FB76E999', 'hex'));
});

it('Checks install codes of all lengths', () => {
Expand Down

0 comments on commit 705f765

Please sign in to comment.