Skip to content

Commit

Permalink
fix: Install code processing (#1317)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nerivec authored Feb 10, 2025
1 parent 877e905 commit 2cf176b
Show file tree
Hide file tree
Showing 13 changed files with 44 additions and 35 deletions.
2 changes: 1 addition & 1 deletion src/adapter/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export abstract class Adapter extends events.EventEmitter<AdapterEventMap> {

public abstract getNetworkParameters(): Promise<TsType.NetworkParameters>;

public abstract addInstallCode(ieeeAddress: string, key: Buffer): Promise<void>;
public abstract addInstallCode(ieeeAddress: string, key: Buffer, hashed: boolean): Promise<void>;

public abstract waitFor(
networkAddress: number | undefined,
Expand Down
4 changes: 2 additions & 2 deletions src/adapter/deconz/adapter/deconzAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,8 +298,8 @@ export class DeconzAdapter extends Adapter {
}
}

public async addInstallCode(ieeeAddress: string, key: Buffer): Promise<void> {
await this.driver.writeLinkKey(ieeeAddress, ZSpec.Utils.aes128MmoHash(key));
public async addInstallCode(ieeeAddress: string, key: Buffer, hashed: boolean): Promise<void> {
await this.driver.writeLinkKey(ieeeAddress, hashed ? key : ZSpec.Utils.aes128MmoHash(key));
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
Expand Down
4 changes: 2 additions & 2 deletions src/adapter/ember/adapter/emberAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1683,11 +1683,11 @@ export class EmberAdapter extends Adapter {
}

// queued
public async addInstallCode(ieeeAddress: string, key: Buffer): Promise<void> {
public async addInstallCode(ieeeAddress: string, key: Buffer, hashed: boolean): Promise<void> {
return await this.queue.execute<void>(async () => {
// Add the key to the transient key table.
// This will be used while the DUT joins.
const impStatus = await this.ezsp.ezspImportTransientKey(ieeeAddress as EUI64, {contents: ZSpec.Utils.aes128MmoHash(key)});
const impStatus = await this.ezsp.ezspImportTransientKey(ieeeAddress as EUI64, {contents: hashed ? key : ZSpec.Utils.aes128MmoHash(key)});

if (impStatus == SLStatus.OK) {
logger.debug(`[ADD INSTALL CODE] Success for '${ieeeAddress}'.`, NS);
Expand Down
4 changes: 2 additions & 2 deletions src/adapter/ezsp/adapter/ezspAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,11 +214,11 @@ export class EZSPAdapter extends Adapter {
return {type: `EZSP v${this.driver.version.product}`, meta: this.driver.version};
}

public async addInstallCode(ieeeAddress: string, key: Buffer): Promise<void> {
public async addInstallCode(ieeeAddress: string, key: Buffer, hashed: boolean): Promise<void> {
if ([8, 10, 14, 16, 18].indexOf(key.length) === -1) {
throw new Error('Wrong install code length');
}
await this.driver.addInstallCode(ieeeAddress, key);
await this.driver.addInstallCode(ieeeAddress, key, hashed);
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
Expand Down
23 changes: 7 additions & 16 deletions src/adapter/ezsp/driver/driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import {
SLStatus,
} from './types/named';
import {
EmberAesMmoHashContext,
EmberApsFrame,
EmberIeeeRawFrame,
EmberInitialSecurityState,
Expand Down Expand Up @@ -879,21 +878,13 @@ export class Driver extends EventEmitter {
}
}

public async addInstallCode(ieeeAddress: string, key: Buffer): Promise<void> {
// Key need to be converted to aes hash string
const hc = new EmberAesMmoHashContext();
hc.result = Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
hc.length = 0;
const hash = await this.ezsp.execCommand('aesMmoHash', {context: hc, finalize: true, data: key});
if (hash.status == EmberStatus.SUCCESS) {
const ieee = new EmberEUI64(ieeeAddress);
const linkKey = new EmberKeyData();
linkKey.contents = hash.returnContext.result;
const result = await this.addTransientLinkKey(ieee, linkKey);
if (result.status !== EmberStatus.SUCCESS) {
throw new Error(`Add install code for '${ieeeAddress}' failed`);
}
} else {
public async addInstallCode(ieeeAddress: string, key: Buffer, hashed: boolean): Promise<void> {
const ieee = new EmberEUI64(ieeeAddress);
const linkKey = new EmberKeyData();
linkKey.contents = hashed ? key : ZSpec.Utils.aes128MmoHash(key);
const result = await this.addTransientLinkKey(ieee, linkKey);

if (result.status !== EmberStatus.SUCCESS) {
throw new Error(`Add install code for '${ieeeAddress}' failed`);
}
}
Expand Down
5 changes: 3 additions & 2 deletions src/adapter/z-stack/adapter/zStackAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -766,9 +766,10 @@ export class ZStackAdapter extends Adapter {
});
}

public async addInstallCode(ieeeAddress: string, key: Buffer): Promise<void> {
public async addInstallCode(ieeeAddress: string, key: Buffer, hashed: boolean): Promise<void> {
assert(this.version.product !== ZnpVersion.zStack12, 'Install code is not supported for ZStack 1.2 adapter');
const payload = {installCodeFormat: key.length === 18 ? 1 : 2, ieeeaddr: ieeeAddress, installCode: key};
// TODO: always use 0x2? => const hashedKey = hashed ? key : ZSpec.Utils.aes128MmoHash(key);
const payload = {installCodeFormat: hashed ? 0x2 : 0x1, ieeeaddr: ieeeAddress, installCode: key};
await this.znp.request(Subsystem.APP_CNF, 'bdbAddInstallCode', payload);
}

Expand Down
4 changes: 2 additions & 2 deletions src/adapter/zboss/adapter/zbossAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,8 @@ export class ZBOSSAdapter extends Adapter {
});
}

public async addInstallCode(ieeeAddress: string, key: Buffer): Promise<void> {
logger.error(`NOT SUPPORTED: sendZclFrameToGroup(${ieeeAddress},${key.toString('hex')}`, NS);
public async addInstallCode(ieeeAddress: string, key: Buffer, hashed: boolean): Promise<void> {
logger.error(`NOT SUPPORTED: sendZclFrameToGroup(${ieeeAddress},${key.toString('hex')},${hashed}`, NS);
throw new Error(`Install code is not supported for 'zboss' yet`);
}

Expand Down
2 changes: 1 addition & 1 deletion src/adapter/zigate/adapter/zigateAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ export class ZiGateAdapter extends Adapter {
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
public async addInstallCode(ieeeAddress: string, key: Buffer): Promise<void> {
public async addInstallCode(ieeeAddress: string, key: Buffer, hashed: boolean): Promise<void> {
throw new Error('Add install code is not supported');
}

Expand Down
8 changes: 7 additions & 1 deletion src/controller/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,13 @@ export class Controller extends events.EventEmitter<ControllerEventMap> {

logger.info(`Adding install code for ${ieeeAddr}.`, NS);

await this.adapter.addInstallCode(ieeeAddr, adjustedKey);
await this.adapter.addInstallCode(ieeeAddr, adjustedKey, false);

if (adjusted === 'missing CRC') {
// in case the CRC was missing, could also be a "already-hashed" key, send both
// XXX: seems to be the case for old HA1.2 devices
await this.adapter.addInstallCode(ieeeAddr, key, true);
}
}

public async permitJoin(time: number, device?: Device): Promise<void> {
Expand Down
2 changes: 1 addition & 1 deletion src/zspec/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ export function aes128MmoHash(data: Buffer): Buffer {
* - If adjust is false, undefined, otherwise, the reason why the code needed adjusting or undefined if not.
* - Throws when adjust=false and invalid, or cannot fix.
*/
export function checkInstallCode(code: Buffer, adjust: boolean = true): [outCode: Buffer, adjusted: string | undefined] {
export function checkInstallCode(code: Buffer, adjust: boolean = true): [outCode: Buffer, adjusted: 'invalid CRC' | 'missing CRC' | undefined] {
const crcLowByteIndex = code.length - INSTALL_CODE_CRC_SIZE;
const crcHighByteIndex = code.length - INSTALL_CODE_CRC_SIZE + 1;

Expand Down
4 changes: 2 additions & 2 deletions test/adapter/ember/emberAdapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2266,7 +2266,7 @@ describe('Ember Adapter Layer', () => {

it('Adapter impl: addInstallCode', async () => {
await expect(
adapter.addInstallCode('0x1122334455667788', Buffer.from('DD7ED5CDAA8E2C708B67D2B1573DB6843A5F', 'hex')),
adapter.addInstallCode('0x1122334455667788', Buffer.from('DD7ED5CDAA8E2C708B67D2B1573DB6843A5F', 'hex'), false),
).resolves.toStrictEqual(undefined);
expect(mockEzspImportTransientKey).toHaveBeenCalledTimes(1);
expect(loggerSpies.debug).toHaveBeenCalledWith(`[ADD INSTALL CODE] Success for '0x1122334455667788'.`, 'zh:ember');
Expand All @@ -2275,7 +2275,7 @@ describe('Ember Adapter Layer', () => {
it('Adapter impl: throw when addInstallCode fails import transient key', async () => {
mockEzspImportTransientKey.mockResolvedValueOnce(SLStatus.FAIL);

await expect(adapter.addInstallCode('0x1122334455667788', Buffer.alloc(16))).rejects.toThrow(
await expect(adapter.addInstallCode('0x1122334455667788', Buffer.alloc(16), true)).rejects.toThrow(
`[ADD INSTALL CODE] Failed for '0x1122334455667788' with status=FAIL.`,
);
expect(mockEzspImportTransientKey).toHaveBeenCalledTimes(1);
Expand Down
6 changes: 4 additions & 2 deletions test/adapter/z-stack/adapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1983,9 +1983,10 @@ describe('zstack-adapter', () => {
await adapter.addInstallCode(
'0x9035EAFFFE424783',
Buffer.from([0xae, 0x3b, 0x28, 0x72, 0x81, 0xcf, 0x16, 0xf5, 0x50, 0x73, 0x3a, 0x0c, 0xec, 0x38, 0xaa, 0x31, 0xe8, 0x02]),
false,
);
const payload = {
installCodeFormat: 1,
installCodeFormat: 0x1,
ieeeaddr: '0x9035EAFFFE424783',
installCode: Buffer.from([0xae, 0x3b, 0x28, 0x72, 0x81, 0xcf, 0x16, 0xf5, 0x50, 0x73, 0x3a, 0x0c, 0xec, 0x38, 0xaa, 0x31, 0xe8, 0x02]),
};
Expand All @@ -1998,9 +1999,10 @@ describe('zstack-adapter', () => {
await adapter.addInstallCode(
'0x9035EAFFFE424783',
Buffer.from([0xae, 0x3b, 0x28, 0x72, 0x81, 0xcf, 0x16, 0xf5, 0x50, 0x73, 0x3a, 0x0c, 0xec, 0x38, 0xaa, 0x31]),
true,
);
const payload = {
installCodeFormat: 2,
installCodeFormat: 0x2,
ieeeaddr: '0x9035EAFFFE424783',
installCode: Buffer.from([0xae, 0x3b, 0x28, 0x72, 0x81, 0xcf, 0x16, 0xf5, 0x50, 0x73, 0x3a, 0x0c, 0xec, 0x38, 0xaa, 0x31]),
};
Expand Down
11 changes: 10 additions & 1 deletion test/controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1825,17 +1825,24 @@ describe('Controller', () => {
expect(mockAddInstallCode).toHaveBeenCalledWith(
'0x9035EAFFFE424783',
Buffer.from([0xae, 0x3b, 0x28, 0x72, 0x81, 0xcf, 0x16, 0xf5, 0x50, 0x73, 0x3a, 0x0c, 0xec, 0x38, 0xaa, 0x31, 0xe8, 0x02]),
false,
);
});

it('Add install code 16 byte - missing CRC is appended', async () => {
await controller.start();
const code = 'RB01SG0D836591B3CC0010000000000000000000000D6F00179F2BC9DLKD0F471C9BBA2C0208608E91EED17E2B1';
await controller.addInstallCode(code);
expect(mockAddInstallCode).toHaveBeenCalledTimes(1);
expect(mockAddInstallCode).toHaveBeenCalledTimes(2);
expect(mockAddInstallCode).toHaveBeenCalledWith(
'0x000D6F00179F2BC9',
Buffer.from([0xd0, 0xf4, 0x71, 0xc9, 0xbb, 0xa2, 0xc0, 0x20, 0x86, 0x08, 0xe9, 0x1e, 0xed, 0x17, 0xe2, 0xb1, 0x9a, 0xec]),
false,
);
expect(mockAddInstallCode).toHaveBeenCalledWith(
'0x000D6F00179F2BC9',
Buffer.from([0xd0, 0xf4, 0x71, 0xc9, 0xbb, 0xa2, 0xc0, 0x20, 0x86, 0x08, 0xe9, 0x1e, 0xed, 0x17, 0xe2, 0xb1]),
true,
);
expect(mockLogger.info).toHaveBeenCalledWith(`Install code was adjusted for reason 'missing CRC'.`, 'zh:controller');
});
Expand All @@ -1848,6 +1855,7 @@ describe('Controller', () => {
expect(mockAddInstallCode).toHaveBeenCalledWith(
'0x54EF44100006E7DF',
Buffer.from([0x33, 0x13, 0xa0, 0x05, 0xe1, 0x77, 0xa6, 0x47, 0xfc, 0x79, 0x25, 0x62, 0x0a, 0xb2, 0x07, 0xc4, 0xbe, 0xf5]),
false,
);
});

Expand All @@ -1859,6 +1867,7 @@ describe('Controller', () => {
expect(mockAddInstallCode).toHaveBeenCalledWith(
'0x54EF44100006E7DF',
Buffer.from([0x33, 0x13, 0xa0, 0x05, 0xe1, 0x77, 0xa6, 0x47, 0xfc, 0x79, 0x25, 0x62, 0x0a, 0xb2, 0x07, 0xc4, 0xbe, 0xf5]),
false,
);
});

Expand Down

0 comments on commit 2cf176b

Please sign in to comment.