10000 Refactor Certificate handling by Apollon77 · Pull Request #2169 · project-chip/matter.js · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Refactor Certificate handling #2169

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions packages/node/test/node/ServerNodeTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import { OfflineContext } from "#index.js";
import { AccessLevel, BasicInformation, ElementTag, FeatureMap } from "#model";
import { ServerEnvironment } from "#node/server/ServerEnvironment.js";
import { ServerNode } from "#node/ServerNode.js";
import { AttestationCertificateManager, CertificationDeclarationManager, Val } from "#protocol";
import { AttestationCertificateManager, CertificationDeclaration, Val } from "#protocol";
import { FabricIndex, VendorId } from "#types";
import { OccurrenceManager } from "@matter/protocol";
import { MockServerNode } from "./mock-server-node.js";
Expand Down Expand Up @@ -256,11 +256,7 @@ describe("ServerNode", () => {
certification: async () => {
const paa = await AttestationCertificateManager.create(MockCrypto(), vendorId);
const { keyPair: dacKeyPair, dac } = await paa.getDACert(productId);
const declaration = await CertificationDeclarationManager.generate(
MockCrypto(),
vendorId,
productId,
);
const declaration = await CertificationDeclaration.generate(MockCrypto(), vendorId, productId);

commissioningServer2CertificateProviderCalled = true;
return {
Expand Down
4 changes: 2 additions & 2 deletions packages/node/test/node/node-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { Crypto, InternalError } from "#general";
import { CommissioningServer, InteractionServer } from "#index.js";
import { Specification } from "#model";
import {
CertificateManager,
ChannelManager,
Fabric,
FabricManager,
Expand All @@ -35,6 +34,7 @@ import {
TypeFromSchema,
VendorId,
} from "#types";
import { X509Base } from "@matter/protocol";
import { MockServerNode } from "./mock-server-node.js";

export const FAILSAFE_LENGTH_S = 60;
Expand Down Expand Up @@ -137,7 +137,7 @@ export function CommissioningHelper() {
});

const { certSigningRequest } = TlvCertSigningRequest.decode(nocsrElements);
const peerPublicKey = await new CertificateManager(crypto).getPublicKeyFromCsr(certSigningRequest);
const peerPublicKey = await X509Base.getPublicKeyFromCsr(crypto, certSigningRequest);
const noc = await authority.ca.generateNoc(
peerPublicKey,
controllerFabric.fabricId,
Expand Down
36 changes: 20 additions & 16 deletions packages/protocol/src/certificate/AttestationCertificateManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@

import { Bytes, Crypto, PrivateKey, Time, toHex } from "#general";
import { VendorId } from "#types";
import { CertificateManager, jsToMatterDate } from "./CertificateManager.js";
import {
TestCert_PAA_NoVID_PrivateKey,
TestCert_PAA_NoVID_PublicKey,
TestCert_PAA_NoVID_SKID,
} from "./ChipPAAuthorities.js";
import { Dac, Paa, Pai } from "./kinds/AttestationCertificates.js";
import { jsToMatterDate } from "./kinds/definitions/asn.js";

function getPaiCommonName(vendorId: VendorId, productId?: number) {
return `node-matter Dev PAI 0x${vendorId.toString(16).toUpperCase()} ${
Expand All @@ -36,7 +37,7 @@ export class AttestationCertificateManager {
readonly #paaKeyPair = PrivateKey(TestCert_PAA_NoVID_PrivateKey, {
publicKey: TestCert_PAA_NoVID_PublicKey,
});
readonly #certs: CertificateManager;
readonly #crypto: Crypto;
readonly #vendorId: VendorId;
readonly #paiKeyPair: PrivateKey;
readonly #paiKeyIdentifier: Uint8Array;
Expand All @@ -46,7 +47,7 @@ export class AttestationCertificateManager {
#nextCertificateId = 2;

constructor(crypto: Crypto, vendorId: VendorId, paiKeyPair: PrivateKey, paiKeyIdentifier: Uint8Array) {
this.#certs = new CertificateManager(crypto);
this.#crypto = crypto;
this.#vendorId = vendorId;
this.#paiKeyPair = paiKeyPair;
this.#paiKeyIdentifier = paiKeyIdentifier;
Expand All @@ -64,7 +65,7 @@ export class AttestationCertificateManager {
}

async getDACert(productId: number) {
const dacKeyPair = await this.#certs.crypto.createKeyPair();
const dacKeyPair = await this.#crypto.createKeyPair();
return {
keyPair: dacKeyPair,
dac: await this.generateDaCert(dacKeyPair.publicKey, this.#vendorId, productId),
Expand All @@ -74,9 +75,9 @@ export class AttestationCertificateManager {
// Method unused for now because we use the official Matter Test PAA, but is functional
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
private generatePAACert(vendorId?: VendorId) {
private async generatePAACert(vendorId?: VendorId) {
const now = Time.get().now();
const unsignedCertificate = {
const cert = new Paa({
serialNumber: Bytes.fromHex(toHex(this.paaCertId)),
signatureAlgorithm: 1 /* EcdsaWithSHA256 */,
publicKeyAlgorithm: 1 /* EC */,
Expand Down Expand Up @@ -104,13 +105,14 @@ export class AttestationCertificateManager {
subjectKeyIdentifier: this.#paaKeyIdentifier,
authorityKeyIdentifier: this.#paaKeyIdentifier,
},
};
return this.#certs.productAttestationAuthorityCertToAsn1(unsignedCertificate, this.#paaKeyPair);
});
await cert.sign(this.#crypto, this.#paaKeyPair);
return cert.asSignedAsn1();
}

private generatePAICert(vendorId: VendorId, productId?: number) {
private async generatePAICert(vendorId: VendorId, productId?: number) {
const now = Time.get().now();
const unsignedCertificate = {
const cert = new Pai({
serialNumber: Bytes.fromHex(toHex(this.#paiCertId)),
signatureAlgorithm: 1 /* EcdsaWithSHA256 */,
publicKeyAlgorithm: 1 /* EC */,
Expand Down Expand Up @@ -138,14 +140,15 @@ export class AttestationCertificateManager {
subjectKeyIdentifier: this.#paiKeyIdentifier,
authorityKeyIdentifier: this.#paaKeyIdentifier,
},
};
return this.#certs.productAttestationIntermediateCertToAsn1(unsignedCertificate, this.#paaKeyPair);
});
await cert.sign(this.#crypto, this.#paaKeyPair);
return cert.asSignedAsn1();
}

async generateDaCert(publicKey: Uint8Array, vendorId: VendorId, productId: number) {
const now = Time.get().now();
const certId = this.#nextCertificateId++;
const unsignedCertificate = {
const cert = new Dac({
serialNumber: Bytes.fromHex(toHex(certId)),
signatureAlgorithm: 1 /* EcdsaWithSHA256 */,
publicKeyAlgorithm: 1 /* EC */,
Expand All @@ -169,10 +172,11 @@ export class AttestationCertificateManager {
keyUsage: {
digitalSignature: true,
},
subjectKeyIdentifier: (await this.#certs.crypto.computeSha256(publicKey)).slice(0, 20),
subjectKeyIdentifier: (await this.#crypto.computeSha256(publicKey)).slice(0, 20),
authorityKeyIdentifier: this.#paiKeyIdentifier,
},
};
return this.#certs.deviceAttestationCertToAsn1(unsignedCertificate, this.#paiKeyPair);
});
await cert.sign(this.#crypto, this.#paiKeyPair);
return cert.asSignedAsn1();
}
}
53 changes: 18 additions & 35 deletions packages/protocol/src/certificate/CertificateAuthority.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,9 @@ import {
toHex,
} from "#general";
import { CaseAuthenticatedTag, FabricId, NodeId } from "#types";
import {
CertificateManager,
OperationalCertificate,
RootCertificate,
TlvOperationalCertificate,
TlvRootCertificate,
Unsigned,
jsToMatterDate,
} from "./CertificateManager.js";
import { jsToMatterDate } from "./kinds/definitions/asn.js";
import { Noc } from "./kinds/Noc.js";
import { Rcac } from "./kinds/Rcac.js";

const logger = Logger.get("CertificateAuthority");

Expand All @@ -38,16 +32,16 @@ const logger = Logger.get("CertificateAuthority");
* TODO: Add support for (optional) ICACs
*/
export class CertificateAuthority {
#certs: CertificateManager;
#crypto: Crypto;
#rootCertId = BigInt(0);
#rootKeyPair?: PrivateKey;
#rootKeyIdentifier?: Uint8Array<ArrayBufferLike>;
#rootCertBytes?: Uint8Array<ArrayBufferLike>;
#nextCertificateId = BigInt(1);
#construction: Construction<CertificateAuthority>;

get certs() {
return this.#certs;
get crypto() {
return this.#crypto;
}

get construction() {
Expand All @@ -59,16 +53,13 @@ export class CertificateAuthority {
}

constructor(crypto: Crypto, options?: StorageContext | CertificateAuthority.Configuration) {
this.#certs = new CertificateManager(crypto);
this.#crypto = crypto;
this.#construction = Construction(this, async () => {
// Use provided CA config or read from storage, otherwise initialize and store
c 10000 onst certValues = options instanceof StorageContext ? await options.values() : (options ?? {});

this.#rootKeyPair = await this.#certs.crypto.createKeyPair();
this.#rootKeyIdentifier = (await this.#certs.crypto.computeSha256(this.#rootKeyPair.publicKey)).slice(
0,
20,
);
this.#rootKeyPair = await this.#crypto.createKeyPair();
this.#rootKeyIdentifier = (await this.#crypto.computeSha256(this.#rootKeyPair.publicKey)).slice(0, 20);
this.#rootCertBytes = await this.#generateRootCert();

if (
Expand Down Expand Up @@ -124,7 +115,7 @@ export class CertificateAuthority {

async #generateRootCert() {
const now = Time.get().now();
const unsignedCertificate: Unsigned<RootCertificate> = {
const cert = new Rcac({
serialNumber: Bytes.fromHex(toHex(this.#rootCertId)),
signatureAlgorithm: 1 /* EcdsaWithSHA256 */,
publicKeyAlgorithm: 1 /* EC */,
Expand All @@ -143,12 +134,9 @@ export class CertificateAuthority {
subjectKeyIdentifier: this.#initializedRootKeyIdentifier,
authorityKeyIdentifier: this.#initializedRootKeyIdentifier,
},
};
const signature = await this.#certs.crypto.signEcdsa(
this.#initializedRootKeyPair,
this.#certs.rootCertToAsn1(unsignedCertificate),
);
return TlvRootCertificate.encode({ ...unsignedCertificate, signature });
});
await cert.sign(this.#crypto, this.#initializedRootKeyPair);
return cert.asSignedTlv();
}

async generateNoc(
Expand All @@ -159,7 +147,7 @@ export class CertificateAuthority {
) {
const now = Time.get().now();
const certId = this.#nextCertificateId++;
const unsignedCertificate: Unsigned<OperationalCertificate> = {
const cert = new Noc({
serialNumber: Bytes.fromHex(toHex(certId)),
signatureAlgorithm: 1 /* EcdsaWithSHA256 */,
publicKeyAlgorithm: 1 /* EC */,
Expand All @@ -175,17 +163,12 @@ export class CertificateAuthority {
digitalSignature: true,
},
extendedKeyUsage: [2, 1],
subjectKeyIdentifier: (await this.#certs.crypto.computeSha256(publicKey)).slice(0, 20),
subjectKeyIdentifier: (await this.#crypto.computeSha256(publicKey)).slice(0, 20),
authorityKeyIdentifier: this.#initializedRootKeyIdentifier,
},
};

const signature = await this.#certs.crypto.signEcdsa(
this.#initializedRootKeyPair,
this.#certs.nodeOperationalCertToAsn1(unsignedCertificate),
);

return TlvOperationalCertificate.encode({ ...unsignedCertificate, signature });
});
await cert.sign(this.#crypto, this.#initializedRootKeyPair);
return cert.asSignedTlv();
}

get #initializedRootKeyPair() {
Expand Down
Loading
Loading
0