@matter/protocol 0.15.0-alpha.0-20250616-4b3754906 → 0.15.0-alpha.0-20250619-df2264f15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/certificate/AttestationCertificateManager.d.ts.map +1 -1
- package/dist/cjs/certificate/AttestationCertificateManager.js +26 -22
- package/dist/cjs/certificate/AttestationCertificateManager.js.map +1 -1
- package/dist/cjs/certificate/CertificateAuthority.d.ts +1 -2
- package/dist/cjs/certificate/CertificateAuthority.d.ts.map +1 -1
- package/dist/cjs/certificate/CertificateAuthority.js +22 -29
- package/dist/cjs/certificate/CertificateAuthority.js.map +1 -1
- package/dist/cjs/certificate/DeviceCertification.d.ts.map +1 -1
- package/dist/cjs/certificate/DeviceCertification.js +2 -6
- package/dist/cjs/certificate/DeviceCertification.js.map +1 -1
- package/dist/cjs/certificate/index.d.ts +7 -2
- package/dist/cjs/certificate/index.d.ts.map +1 -1
- package/dist/cjs/certificate/index.js +14 -2
- package/dist/cjs/certificate/index.js.map +1 -1
- package/dist/cjs/certificate/kinds/AttestationCertificates.d.ts +34 -0
- package/dist/cjs/certificate/kinds/AttestationCertificates.d.ts.map +1 -0
- package/dist/cjs/certificate/kinds/AttestationCertificates.js +64 -0
- package/dist/cjs/certificate/kinds/AttestationCertificates.js.map +6 -0
- package/dist/cjs/certificate/kinds/CertificationDeclaration.d.ts +23 -0
- package/dist/cjs/certificate/kinds/CertificationDeclaration.d.ts.map +1 -0
- package/dist/cjs/certificate/kinds/CertificationDeclaration.js +86 -0
- package/dist/cjs/certificate/kinds/CertificationDeclaration.js.map +6 -0
- package/dist/cjs/certificate/kinds/Icac.d.ts +29 -0
- package/dist/cjs/certificate/kinds/Icac.d.ts.map +1 -0
- package/dist/cjs/certificate/kinds/Icac.js +138 -0
- package/dist/cjs/certificate/kinds/Icac.js.map +6 -0
- package/dist/cjs/certificate/kinds/Noc.d.ts +27 -0
- package/dist/cjs/certificate/kinds/Noc.d.ts.map +1 -0
- package/dist/cjs/certificate/kinds/Noc.js +148 -0
- package/dist/cjs/certificate/kinds/Noc.js.map +6 -0
- package/dist/cjs/certificate/kinds/OperationalBase.d.ts +24 -0
- package/dist/cjs/certificate/kinds/OperationalBase.d.ts.map +1 -0
- package/dist/cjs/certificate/kinds/OperationalBase.js +68 -0
- package/dist/cjs/certificate/kinds/OperationalBase.js.map +6 -0
- package/dist/cjs/certificate/kinds/Rcac.d.ts +25 -0
- package/dist/cjs/certificate/kinds/Rcac.d.ts.map +1 -0
- package/dist/cjs/certificate/kinds/Rcac.js +119 -0
- package/dist/cjs/certificate/kinds/Rcac.js.map +6 -0
- package/dist/cjs/certificate/kinds/X509Base.d.ts +92 -0
- package/dist/cjs/certificate/kinds/X509Base.d.ts.map +1 -0
- package/dist/cjs/certificate/kinds/X509Base.js +344 -0
- package/dist/cjs/certificate/kinds/X509Base.js.map +6 -0
- package/dist/cjs/certificate/kinds/common.d.ts +18 -0
- package/dist/cjs/certificate/kinds/common.d.ts.map +1 -0
- package/dist/cjs/certificate/kinds/common.js +42 -0
- package/dist/cjs/certificate/kinds/common.js.map +6 -0
- package/dist/cjs/certificate/kinds/definitions/asn.d.ts +25 -0
- package/dist/cjs/certificate/kinds/definitions/asn.d.ts.map +1 -0
- package/dist/cjs/certificate/kinds/definitions/asn.js +83 -0
- package/dist/cjs/certificate/kinds/definitions/asn.js.map +6 -0
- package/dist/cjs/certificate/kinds/definitions/attestation.d.ts +44 -0
- package/dist/cjs/certificate/kinds/definitions/attestation.d.ts.map +1 -0
- package/dist/cjs/certificate/kinds/definitions/attestation.js +22 -0
- package/dist/cjs/certificate/kinds/definitions/attestation.js.map +6 -0
- package/dist/cjs/certificate/kinds/definitions/base.d.ts +52 -0
- package/dist/cjs/certificate/kinds/definitions/base.d.ts.map +1 -0
- package/dist/cjs/certificate/kinds/definitions/base.js +43 -0
- package/dist/cjs/certificate/kinds/definitions/base.js.map +6 -0
- package/dist/cjs/certificate/kinds/definitions/certification-declaration.d.ts +18 -0
- package/dist/cjs/certificate/kinds/definitions/certification-declaration.d.ts.map +1 -0
- package/dist/cjs/certificate/kinds/definitions/certification-declaration.js +50 -0
- package/dist/cjs/certificate/kinds/definitions/certification-declaration.js.map +6 -0
- package/dist/cjs/certificate/kinds/definitions/operational.d.ts +368 -0
- package/dist/cjs/certificate/kinds/definitions/operational.d.ts.map +1 -0
- package/dist/cjs/certificate/kinds/definitions/operational.js +149 -0
- package/dist/cjs/certificate/kinds/definitions/operational.js.map +6 -0
- package/dist/cjs/certificate/kinds/index.d.ts +12 -0
- package/dist/cjs/certificate/kinds/index.d.ts.map +1 -0
- package/dist/cjs/certificate/kinds/index.js +29 -0
- package/dist/cjs/certificate/kinds/index.js.map +6 -0
- package/dist/cjs/fabric/Fabric.d.ts +1 -2
- package/dist/cjs/fabric/Fabric.d.ts.map +1 -1
- package/dist/cjs/fabric/Fabric.js +28 -31
- package/dist/cjs/fabric/Fabric.js.map +1 -1
- package/dist/cjs/peer/ControllerCommissioningFlow.d.ts.map +1 -1
- package/dist/cjs/peer/ControllerCommissioningFlow.js +2 -1
- package/dist/cjs/peer/ControllerCommissioningFlow.js.map +1 -1
- package/dist/cjs/session/case/CaseClient.d.ts.map +1 -1
- package/dist/cjs/session/case/CaseClient.js +3 -3
- package/dist/cjs/session/case/CaseClient.js.map +1 -1
- package/dist/cjs/session/case/CaseServer.d.ts.map +1 -1
- package/dist/cjs/session/case/CaseServer.js +2 -2
- package/dist/cjs/session/case/CaseServer.js.map +1 -1
- package/dist/esm/certificate/AttestationCertificateManager.d.ts.map +1 -1
- package/dist/esm/certificate/AttestationCertificateManager.js +20 -16
- package/dist/esm/certificate/AttestationCertificateManager.js.map +1 -1
- package/dist/esm/certificate/CertificateAuthority.d.ts +1 -2
- package/dist/esm/certificate/CertificateAuthority.d.ts.map +1 -1
- package/dist/esm/certificate/CertificateAuthority.js +18 -30
- package/dist/esm/certificate/CertificateAuthority.js.map +1 -1
- package/dist/esm/certificate/DeviceCertification.d.ts.map +1 -1
- package/dist/esm/certificate/DeviceCertification.js +2 -6
- package/dist/esm/certificate/DeviceCertification.js.map +1 -1
- package/dist/esm/certificate/index.d.ts +7 -2
- package/dist/esm/certificate/index.d.ts.map +1 -1
- package/dist/esm/certificate/index.js +10 -2
- package/dist/esm/certificate/index.js.map +1 -1
- package/dist/esm/certificate/kinds/AttestationCertificates.d.ts +34 -0
- package/dist/esm/certificate/kinds/AttestationCertificates.d.ts.map +1 -0
- package/dist/esm/certificate/kinds/AttestationCertificates.js +44 -0
- package/dist/esm/certificate/kinds/AttestationCertificates.js.map +6 -0
- package/dist/esm/certificate/kinds/CertificationDeclaration.d.ts +23 -0
- package/dist/esm/certificate/kinds/CertificationDeclaration.d.ts.map +1 -0
- package/dist/esm/certificate/kinds/CertificationDeclaration.js +66 -0
- package/dist/esm/certificate/kinds/CertificationDeclaration.js.map +6 -0
- package/dist/esm/certificate/kinds/Icac.d.ts +29 -0
- package/dist/esm/certificate/kinds/Icac.d.ts.map +1 -0
- package/dist/esm/certificate/kinds/Icac.js +118 -0
- package/dist/esm/certificate/kinds/Icac.js.map +6 -0
- package/dist/esm/certificate/kinds/Noc.d.ts +27 -0
- package/dist/esm/certificate/kinds/Noc.d.ts.map +1 -0
- package/dist/esm/certificate/kinds/Noc.js +128 -0
- package/dist/esm/certificate/kinds/Noc.js.map +6 -0
- package/dist/esm/certificate/kinds/OperationalBase.d.ts +24 -0
- package/dist/esm/certificate/kinds/OperationalBase.d.ts.map +1 -0
- package/dist/esm/certificate/kinds/OperationalBase.js +48 -0
- package/dist/esm/certificate/kinds/OperationalBase.js.map +6 -0
- package/dist/esm/certificate/kinds/Rcac.d.ts +25 -0
- package/dist/esm/certificate/kinds/Rcac.d.ts.map +1 -0
- package/dist/esm/certificate/kinds/Rcac.js +99 -0
- package/dist/esm/certificate/kinds/Rcac.js.map +6 -0
- package/dist/esm/certificate/kinds/X509Base.d.ts +92 -0
- package/dist/esm/certificate/kinds/X509Base.d.ts.map +1 -0
- package/dist/esm/certificate/kinds/X509Base.js +347 -0
- package/dist/esm/certificate/kinds/X509Base.js.map +6 -0
- package/dist/esm/certificate/kinds/common.d.ts +18 -0
- package/dist/esm/certificate/kinds/common.d.ts.map +1 -0
- package/dist/esm/certificate/kinds/common.js +22 -0
- package/dist/esm/certificate/kinds/common.js.map +6 -0
- package/dist/esm/certificate/kinds/definitions/asn.d.ts +25 -0
- package/dist/esm/certificate/kinds/definitions/asn.d.ts.map +1 -0
- package/dist/esm/certificate/kinds/definitions/asn.js +63 -0
- package/dist/esm/certificate/kinds/definitions/asn.js.map +6 -0
- package/dist/esm/certificate/kinds/definitions/attestation.d.ts +44 -0
- package/dist/esm/certificate/kinds/definitions/attestation.d.ts.map +1 -0
- package/dist/esm/certificate/kinds/definitions/attestation.js +6 -0
- package/dist/esm/certificate/kinds/definitions/attestation.js.map +6 -0
- package/dist/esm/certificate/kinds/definitions/base.d.ts +52 -0
- package/dist/esm/certificate/kinds/definitions/base.d.ts.map +1 -0
- package/dist/esm/certificate/kinds/definitions/base.js +23 -0
- package/dist/esm/certificate/kinds/definitions/base.js.map +6 -0
- package/dist/esm/certificate/kinds/definitions/certification-declaration.d.ts +18 -0
- package/dist/esm/certificate/kinds/definitions/certification-declaration.d.ts.map +1 -0
- package/dist/esm/certificate/kinds/definitions/certification-declaration.js +41 -0
- package/dist/esm/certificate/kinds/definitions/certification-declaration.js.map +6 -0
- package/dist/esm/certificate/kinds/definitions/operational.d.ts +368 -0
- package/dist/esm/certificate/kinds/definitions/operational.d.ts.map +1 -0
- package/dist/esm/certificate/kinds/definitions/operational.js +148 -0
- package/dist/esm/certificate/kinds/definitions/operational.js.map +6 -0
- package/dist/esm/certificate/kinds/index.d.ts +12 -0
- package/dist/esm/certificate/kinds/index.d.ts.map +1 -0
- package/dist/esm/certificate/kinds/index.js +12 -0
- package/dist/esm/certificate/kinds/index.js.map +6 -0
- package/dist/esm/fabric/Fabric.d.ts +1 -2
- package/dist/esm/fabric/Fabric.d.ts.map +1 -1
- package/dist/esm/fabric/Fabric.js +28 -36
- package/dist/esm/fabric/Fabric.js.map +1 -1
- package/dist/esm/peer/ControllerCommissioningFlow.d.ts.map +1 -1
- package/dist/esm/peer/ControllerCommissioningFlow.js +2 -1
- package/dist/esm/peer/ControllerCommissioningFlow.js.map +1 -1
- package/dist/esm/session/case/CaseClient.d.ts.map +1 -1
- package/dist/esm/session/case/CaseClient.js +3 -3
- package/dist/esm/session/case/CaseClient.js.map +1 -1
- package/dist/esm/session/case/CaseServer.d.ts.map +1 -1
- package/dist/esm/session/case/CaseServer.js +2 -2
- package/dist/esm/session/case/CaseServer.js.map +1 -1
- package/package.json +6 -6
- package/src/certificate/AttestationCertificateManager.ts +20 -16
- package/src/certificate/CertificateAuthority.ts +18 -35
- package/src/certificate/DeviceCertification.ts +2 -6
- package/src/certificate/index.ts +7 -2
- package/src/certificate/kinds/AttestationCertificates.ts +48 -0
- package/src/certificate/kinds/CertificationDeclaration.ts +91 -0
- package/src/certificate/kinds/Icac.ts +156 -0
- package/src/certificate/kinds/Noc.ts +164 -0
- package/src/certificate/kinds/OperationalBase.ts +72 -0
- package/src/certificate/kinds/Rcac.ts +126 -0
- package/src/certificate/kinds/X509Base.ts +380 -0
- package/src/certificate/kinds/common.ts +24 -0
- package/src/certificate/kinds/definitions/asn.ts +97 -0
- package/src/certificate/kinds/definitions/attestation.ts +46 -0
- package/src/certificate/kinds/definitions/base.ts +43 -0
- package/src/certificate/kinds/definitions/certification-declaration.ts +38 -0
- package/src/certificate/kinds/definitions/operational.ts +179 -0
- package/src/certificate/kinds/index.ts +12 -0
- package/src/fabric/Fabric.ts +28 -40
- package/src/peer/ControllerCommissioningFlow.ts +2 -1
- package/src/session/case/CaseClient.ts +3 -3
- package/src/session/case/CaseServer.ts +2 -2
- package/dist/cjs/certificate/CertificateManager.d.ts +0 -578
- package/dist/cjs/certificate/CertificateManager.d.ts.map +0 -1
- package/dist/cjs/certificate/CertificateManager.js +0 -843
- package/dist/cjs/certificate/CertificateManager.js.map +0 -6
- package/dist/cjs/certificate/CertificationDeclarationManager.d.ts +0 -11
- package/dist/cjs/certificate/CertificationDeclarationManager.d.ts.map +0 -1
- package/dist/cjs/certificate/CertificationDeclarationManager.js +0 -54
- package/dist/cjs/certificate/CertificationDeclarationManager.js.map +0 -6
- package/dist/esm/certificate/CertificateManager.d.ts +0 -578
- package/dist/esm/certificate/CertificateManager.d.ts.map +0 -1
- package/dist/esm/certificate/CertificateManager.js +0 -870
- package/dist/esm/certificate/CertificateManager.js.map +0 -6
- package/dist/esm/certificate/CertificationDeclarationManager.d.ts +0 -11
- package/dist/esm/certificate/CertificationDeclarationManager.d.ts.map +0 -1
- package/dist/esm/certificate/CertificationDeclarationManager.js +0 -34
- package/dist/esm/certificate/CertificationDeclarationManager.js.map +0 -6
- package/src/certificate/CertificateManager.ts +0 -1176
- package/src/certificate/CertificationDeclarationManager.ts +0 -52
|
@@ -1,1176 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright 2022-2025 Matter.js Authors
|
|
4
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
Bytes,
|
|
9
|
-
ContextTagged,
|
|
10
|
-
ContextTaggedBytes,
|
|
11
|
-
Crypto,
|
|
12
|
-
DatatypeOverride,
|
|
13
|
-
DerBitString,
|
|
14
|
-
DerCodec,
|
|
15
|
-
DerKey,
|
|
16
|
-
DerObject,
|
|
17
|
-
DerType,
|
|
18
|
-
Diagnostic,
|
|
19
|
-
ImplementationError,
|
|
20
|
-
Key,
|
|
21
|
-
Logger,
|
|
22
|
-
MatterError,
|
|
23
|
-
Pkcs7,
|
|
24
|
-
PublicKey,
|
|
25
|
-
RawBytes,
|
|
26
|
-
SHA256_CMS,
|
|
27
|
-
Time,
|
|
28
|
-
X509,
|
|
29
|
-
X520,
|
|
30
|
-
X962,
|
|
31
|
-
} from "#general";
|
|
32
|
-
import {
|
|
33
|
-
BitFlag,
|
|
34
|
-
BitmapSchema,
|
|
35
|
-
CaseAuthenticatedTag,
|
|
36
|
-
FabricId,
|
|
37
|
-
NodeId,
|
|
38
|
-
TlvArray,
|
|
39
|
-
TlvBitmap,
|
|
40
|
-
TlvBoolean,
|
|
41
|
-
TlvByteString,
|
|
42
|
-
TlvCaseAuthenticatedTag,
|
|
43
|
-
TlvFabricId,
|
|
44
|
-
TlvField,
|
|
45
|
-
TlvNodeId,
|
|
46
|
-
TlvObject,
|
|
47
|
-
TlvObjectWithMaxSize,
|
|
48
|
-
TlvOptionalField,
|
|
49
|
-
TlvOptionalRepeatedField,
|
|
50
|
-
TlvString,
|
|
51
|
-
TlvTaggedList,
|
|
52
|
-
TlvUInt16,
|
|
53
|
-
TlvUInt32,
|
|
54
|
-
TlvUInt64,
|
|
55
|
-
TlvUInt8,
|
|
56
|
-
TlvVendorId,
|
|
57
|
-
TypeFromPartialBitSchema,
|
|
58
|
-
TypeFromSchema,
|
|
59
|
-
VendorId,
|
|
60
|
-
} from "#types";
|
|
61
|
-
|
|
62
|
-
const logger = Logger.get("CertificateManager");
|
|
63
|
-
|
|
64
|
-
export class CertificateError extends MatterError {}
|
|
65
|
-
|
|
66
|
-
const YEAR_S = 365 * 24 * 60 * 60;
|
|
67
|
-
const EPOCH_OFFSET_S = 10957 * 24 * 60 * 60;
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Matter specific Certificate Sizes
|
|
71
|
-
* @see {@link MatterSpecification.v13.Core} 6.1.3.
|
|
72
|
-
*/
|
|
73
|
-
const MAX_DER_CERTIFICATE_SIZE = 600;
|
|
74
|
-
const MAX_TLV_CERTIFICATE_SIZE = 400;
|
|
75
|
-
|
|
76
|
-
// TODO replace usage of Date by abstraction
|
|
77
|
-
|
|
78
|
-
export function matterToJsDate(date: number) {
|
|
79
|
-
return date === 0 ? X520.NON_WELL_DEFINED_DATE : new Date((date + EPOCH_OFFSET_S) * 1000);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export function jsToMatterDate(date: Date, addYears = 0) {
|
|
83
|
-
return date.getTime() === X520.NON_WELL_DEFINED_DATE.getTime()
|
|
84
|
-
? 0
|
|
85
|
-
: Math.floor(date.getTime() / 1000) - EPOCH_OFFSET_S + addYears * YEAR_S;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function intTo16Chars(value: bigint | number) {
|
|
89
|
-
const byteArray = new Uint8Array(8);
|
|
90
|
-
const dataView = Bytes.dataViewOf(byteArray);
|
|
91
|
-
dataView.setBigUint64(0, typeof value === "bigint" ? value : BigInt(value));
|
|
92
|
-
return Bytes.toHex(byteArray).toUpperCase();
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function uInt16To8Chars(value: number) {
|
|
96
|
-
const byteArray = new Uint8Array(4);
|
|
97
|
-
const dataView = Bytes.dataViewOf(byteArray);
|
|
98
|
-
dataView.setUint32(0, value);
|
|
99
|
-
return Bytes.toHex(byteArray).toUpperCase();
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function uInt16To4Chars(value: number) {
|
|
103
|
-
const byteArray = new Uint8Array(2);
|
|
104
|
-
const dataView = Bytes.dataViewOf(byteArray);
|
|
105
|
-
dataView.setUint16(0, value);
|
|
106
|
-
return Bytes.toHex(byteArray).toUpperCase();
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Matter specific ASN.1 OIDs
|
|
111
|
-
* @see {@link MatterSpecification.v12.Core} Appendix E
|
|
112
|
-
*/
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Generator function to create a specific ASN field for a Matter OpCert DN with the OID base 1.3.6.1.4.1.37244.1.*.
|
|
116
|
-
* The returned function takes the value and returns the ASN.1 DER object.
|
|
117
|
-
*/
|
|
118
|
-
const GenericMatterOpCertObject =
|
|
119
|
-
<T>(id: number, valueConverter?: (value: T) => string) =>
|
|
120
|
-
(value: T) => [
|
|
121
|
-
DerObject(`2b0601040182a27c01${id.toString(16).padStart(2, "0")}`, {
|
|
122
|
-
value: (valueConverter ?? intTo16Chars)(value as any),
|
|
123
|
-
}),
|
|
124
|
-
];
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Generator function to create a specific ASN field for a Matter AttCert DN with the OID base 1.3.6.1.4.1.37244.2.*.
|
|
128
|
-
* The returned function takes the value and returns the ASN.1 DER object.
|
|
129
|
-
*/
|
|
130
|
-
const GenericMatterAttCertObject =
|
|
131
|
-
<T>(id: number, valueConverter?: (value: T) => string) =>
|
|
132
|
-
(value: T) => [
|
|
133
|
-
DerObject(`2b0601040182a27c02${id.toString(16).padStart(2, "0")}`, {
|
|
134
|
-
value: (valueConverter ?? intTo16Chars)(value as any),
|
|
135
|
-
}),
|
|
136
|
-
];
|
|
137
|
-
|
|
138
|
-
/** matter-node-id = ASN.1 OID 1.3.6.1.4.1.37244.1.1 */
|
|
139
|
-
export const NodeId_Matter = GenericMatterOpCertObject<NodeId>(1);
|
|
140
|
-
|
|
141
|
-
/** matter-firmware-signing-id = ASN.1 OID 1.3.6.1.4.1.37244.1.2 */
|
|
142
|
-
export const FirmwareSigningId_Matter = GenericMatterOpCertObject<number>(2);
|
|
143
|
-
|
|
144
|
-
/** matter-icac-id = ASN.1 OID 1.3.6.1.4.1.37244.1.3 */
|
|
145
|
-
export const IcacId_Matter = GenericMatterOpCertObject<bigint | number>(3);
|
|
146
|
-
|
|
147
|
-
/** matter-rcac-id = ASN.1 OID 1.3.6.1.4.1.37244.1.4 */
|
|
148
|
-
export const RcacId_Matter = GenericMatterOpCertObject<bigint | number>(4);
|
|
149
|
-
|
|
150
|
-
/** matter-fabric-id = ASN.1 OID 1.3.6.1.4.1.37244.1.5 */
|
|
151
|
-
export const FabricId_Matter = GenericMatterOpCertObject<FabricId>(5);
|
|
152
|
-
|
|
153
|
-
/** matter-noc-cat = ASN.1 OID 1.3.6.1.4.1.37244.1.6 */
|
|
154
|
-
export const NocCat_Matter = GenericMatterOpCertObject<number>(6, uInt16To8Chars);
|
|
155
|
-
|
|
156
|
-
/** matter-oid-vid = ASN.1 OID 1.3.6.1.4.1.37244.2.1 */
|
|
157
|
-
export const VendorId_Matter = GenericMatterAttCertObject<VendorId>(1, uInt16To4Chars);
|
|
158
|
-
|
|
159
|
-
/** matter-oid-pid = ASN.1 OID 1.3.6.1.4.1.37244.2.2 */
|
|
160
|
-
export const ProductId_Matter = GenericMatterAttCertObject<number>(2, uInt16To4Chars);
|
|
161
|
-
|
|
162
|
-
/** All defined Matter fields for subject and issuer that we always allow optionally to be encoded */
|
|
163
|
-
const AllowedSubjectAndIssuerMatterFields = {
|
|
164
|
-
nodeId: TlvOptionalField(17, TlvNodeId),
|
|
165
|
-
firmwareSigningId: TlvOptionalField(18, TlvUInt32),
|
|
166
|
-
icacId: TlvOptionalField(19, TlvUInt64),
|
|
167
|
-
rcacId: TlvOptionalField(20, TlvUInt64),
|
|
168
|
-
fabricId: TlvOptionalField(21, TlvFabricId),
|
|
169
|
-
caseAuthenticatedTags: TlvOptionalRepeatedField(22, TlvCaseAuthenticatedTag, { maxLength: 3 }),
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* TLV schema for a generic subject or issuer field in a certificate. We handle all fields as optional here for the TLV
|
|
174
|
-
* parsing and check required fields in the logic to make sure we return the correct errors.
|
|
175
|
-
*/
|
|
176
|
-
const TlvGenericMatterSubjectOrIssuerTaggedList = <T>(matterFields: T) => {
|
|
177
|
-
const fields = {
|
|
178
|
-
// Standard DNs
|
|
179
|
-
commonName: TlvOptionalField(1, TlvString),
|
|
180
|
-
sureName: TlvOptionalField(2, TlvString),
|
|
181
|
-
serialNum: TlvOptionalField(3, TlvString),
|
|
182
|
-
countryName: TlvOptionalField(4, TlvString),
|
|
183
|
-
localityName: TlvOptionalField(5, TlvString),
|
|
184
|
-
stateOrProvinceName: TlvOptionalField(6, TlvString),
|
|
185
|
-
orgName: TlvOptionalField(7, TlvString),
|
|
186
|
-
orgUnitName: TlvOptionalField(8, TlvString),
|
|
187
|
-
title: TlvOptionalField(9, TlvString),
|
|
188
|
-
name: TlvOptionalField(10, TlvString),
|
|
189
|
-
givenName: TlvOptionalField(11, TlvString),
|
|
190
|
-
initials: TlvOptionalField(12, TlvString),
|
|
191
|
-
genQualifier: TlvOptionalField(13, TlvString),
|
|
192
|
-
dnQualifier: TlvOptionalField(14, TlvString),
|
|
193
|
-
pseudonym: TlvOptionalField(15, TlvString),
|
|
194
|
-
domainComponent: TlvOptionalField(16, TlvString),
|
|
195
|
-
|
|
196
|
-
// Matter specific DNs
|
|
197
|
-
...matterFields,
|
|
198
|
-
|
|
199
|
-
// Standard DNs when encoded as Printable String
|
|
200
|
-
commonNamePs: TlvOptionalField(129, TlvString),
|
|
201
|
-
sureNamePs: TlvOptionalField(130, TlvString),
|
|
202
|
-
serialNumPs: TlvOptionalField(131, TlvString),
|
|
203
|
-
countryNamePs: TlvOptionalField(132, TlvString),
|
|
204
|
-
localityNamePs: TlvOptionalField(133, TlvString),
|
|
205
|
-
stateOrProvinceNamePs: TlvOptionalField(134, TlvString),
|
|
206
|
-
orgNamePs: TlvOptionalField(135, TlvString),
|
|
207
|
-
orgUnitNamePs: TlvOptionalField(136, TlvString),
|
|
208
|
-
titlePs: TlvOptionalField(137, TlvString),
|
|
209
|
-
namePs: TlvOptionalField(138, TlvString),
|
|
210
|
-
givenNamePs: TlvOptionalField(139, TlvString),
|
|
211
|
-
initialsPs: TlvOptionalField(140, TlvString),
|
|
212
|
-
genQualifierPs: TlvOptionalField(141, TlvString),
|
|
213
|
-
dnQualifierPs: TlvOptionalField(142, TlvString),
|
|
214
|
-
pseudonymPs: TlvOptionalField(143, TlvString),
|
|
215
|
-
};
|
|
216
|
-
return TlvTaggedList(fields);
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
const ExtensionKeyUsageBitmap = {
|
|
220
|
-
digitalSignature: BitFlag(0),
|
|
221
|
-
nonRepudiation: BitFlag(1),
|
|
222
|
-
keyEncipherment: BitFlag(2),
|
|
223
|
-
dataEncipherment: BitFlag(3),
|
|
224
|
-
keyAgreement: BitFlag(4),
|
|
225
|
-
keyCertSign: BitFlag(5),
|
|
226
|
-
cRLSign: BitFlag(6),
|
|
227
|
-
encipherOnly: BitFlag(7),
|
|
228
|
-
decipherOnly: BitFlag(8),
|
|
229
|
-
};
|
|
230
|
-
const ExtensionKeyUsageSchema = BitmapSchema(ExtensionKeyUsageBitmap);
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* This generator enhances the generic Matter Certificate definition by allowing to override the subject and issuer
|
|
234
|
-
* fields. The overriding serves two needs:
|
|
235
|
-
* 1. to make some fields mandatory for the Tlv parsing and definition for the typescript types
|
|
236
|
-
* 2. have typing guidance when generating certificates ourself in code
|
|
237
|
-
*
|
|
238
|
-
* On Tlv definition level also all not specified allowed Matter Fields are optionally allowed and are decoded,
|
|
239
|
-
* re-encoded into Tlv and also encoded into ASN if the certificate is converted. Just the typing system do not know
|
|
240
|
-
* about them.
|
|
241
|
-
*/
|
|
242
|
-
const BaseMatterCertificate = <S, I>(matterFields?: { subject?: S; issuer?: I }) =>
|
|
243
|
-
TlvObjectWithMaxSize(
|
|
244
|
-
{
|
|
245
|
-
serialNumber: TlvField(1, TlvByteString.bound({ maxLength: 20 })),
|
|
246
|
-
signatureAlgorithm: TlvField(2, TlvUInt8),
|
|
247
|
-
issuer: TlvField(
|
|
248
|
-
3,
|
|
249
|
-
TlvGenericMatterSubjectOrIssuerTaggedList<I>({
|
|
250
|
-
...AllowedSubjectAndIssuerMatterFields,
|
|
251
|
-
...(matterFields?.issuer ?? {}),
|
|
252
|
-
} as I),
|
|
253
|
-
),
|
|
254
|
-
notBefore: TlvField(4, TlvUInt32),
|
|
255
|
-
notAfter: TlvField(5, TlvUInt32),
|
|
256
|
-
subject: TlvField(
|
|
257
|
-
6,
|
|
258
|
-
TlvGenericMatterSubjectOrIssuerTaggedList<S>({
|
|
259
|
-
...AllowedSubjectAndIssuerMatterFields,
|
|
260
|
-
...(matterFields?.subject ?? {}),
|
|
261
|
-
} as S),
|
|
262
|
-
),
|
|
263
|
-
publicKeyAlgorithm: TlvField(7, TlvUInt8),
|
|
264
|
-
ellipticCurveIdentifier: TlvField(8, TlvUInt8),
|
|
265
|
-
ellipticCurvePublicKey: TlvField(9, TlvByteString),
|
|
266
|
-
extensions: TlvField(
|
|
267
|
-
10,
|
|
268
|
-
TlvTaggedList({
|
|
269
|
-
basicConstraints: TlvField(
|
|
270
|
-
1,
|
|
271
|
-
TlvObject({
|
|
272
|
-
isCa: TlvField(1, TlvBoolean),
|
|
273
|
-
pathLen: TlvOptionalField(2, TlvUInt8),
|
|
274
|
-
}),
|
|
275
|
-
),
|
|
276
|
-
keyUsage: TlvField(2, TlvBitmap(TlvUInt16, ExtensionKeyUsageBitmap)),
|
|
277
|
-
extendedKeyUsage: TlvOptionalField(3, TlvArray(TlvUInt8)),
|
|
278
|
-
subjectKeyIdentifier: TlvField(4, TlvByteString.bound({ length: 20 })),
|
|
279
|
-
authorityKeyIdentifier: TlvField(5, TlvByteString.bound({ length: 20 })),
|
|
280
|
-
futureExtension: TlvOptionalRepeatedField(6, TlvByteString),
|
|
281
|
-
}),
|
|
282
|
-
),
|
|
283
|
-
signature: TlvField(11, TlvByteString),
|
|
284
|
-
},
|
|
285
|
-
MAX_TLV_CERTIFICATE_SIZE,
|
|
286
|
-
);
|
|
287
|
-
|
|
288
|
-
export const TlvRootCertificate = BaseMatterCertificate({
|
|
289
|
-
subject: {
|
|
290
|
-
rcacId: TlvField(20, TlvUInt64),
|
|
291
|
-
fabricId: TlvOptionalField(21, TlvFabricId),
|
|
292
|
-
},
|
|
293
|
-
issuer: AllowedSubjectAndIssuerMatterFields,
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
export const TlvOperationalCertificate = BaseMatterCertificate({
|
|
297
|
-
subject: {
|
|
298
|
-
nodeId: TlvField(17, TlvNodeId),
|
|
299
|
-
fabricId: TlvField(21, TlvFabricId),
|
|
300
|
-
caseAuthenticatedTags: TlvOptionalRepeatedField(22, TlvCaseAuthenticatedTag, { maxLength: 3 }),
|
|
301
|
-
},
|
|
302
|
-
issuer: AllowedSubjectAndIssuerMatterFields,
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
export const TlvIntermediateCertificate = BaseMatterCertificate({
|
|
306
|
-
subject: {
|
|
307
|
-
icacId: TlvField(19, TlvUInt64),
|
|
308
|
-
fabricId: TlvOptionalField(21, TlvFabricId),
|
|
309
|
-
},
|
|
310
|
-
issuer: AllowedSubjectAndIssuerMatterFields,
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
const TlvBaseCertificate = BaseMatterCertificate();
|
|
314
|
-
|
|
315
|
-
interface AttestationCertificateBase {
|
|
316
|
-
serialNumber: Uint8Array;
|
|
317
|
-
signatureAlgorithm: number;
|
|
318
|
-
issuer: {};
|
|
319
|
-
notBefore: number;
|
|
320
|
-
notAfter: number;
|
|
321
|
-
subject: {};
|
|
322
|
-
publicKeyAlgorithm: number;
|
|
323
|
-
ellipticCurveIdentifier: number;
|
|
324
|
-
ellipticCurvePublicKey: Uint8Array;
|
|
325
|
-
extensions: {
|
|
326
|
-
basicConstraints: {
|
|
327
|
-
isCa: boolean;
|
|
328
|
-
pathLen?: number;
|
|
329
|
-
};
|
|
330
|
-
keyUsage: TypeFromPartialBitSchema<typeof ExtensionKeyUsageBitmap>;
|
|
331
|
-
extendedKeyUsage?: number[];
|
|
332
|
-
subjectKeyIdentifier: Uint8Array;
|
|
333
|
-
authorityKeyIdentifier: Uint8Array;
|
|
334
|
-
futureExtension?: Uint8Array[];
|
|
335
|
-
};
|
|
336
|
-
signature: Uint8Array;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
export interface DeviceAttestationCertificate extends AttestationCertificateBase {
|
|
340
|
-
issuer: {
|
|
341
|
-
commonName: string;
|
|
342
|
-
productId?: number;
|
|
343
|
-
vendorId: VendorId;
|
|
344
|
-
};
|
|
345
|
-
subject: {
|
|
346
|
-
commonName: string;
|
|
347
|
-
productId: number;
|
|
348
|
-
vendorId: VendorId;
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
export interface ProductAttestationIntermediateCertificate extends AttestationCertificateBase {
|
|
353
|
-
issuer: {
|
|
354
|
-
commonName: string;
|
|
355
|
-
vendorId?: VendorId;
|
|
356
|
-
};
|
|
357
|
-
subject: {
|
|
358
|
-
commonName: string;
|
|
359
|
-
productId?: number;
|
|
360
|
-
vendorId: VendorId;
|
|
361
|
-
};
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
export interface ProductAttestationAuthorityCertificate extends AttestationCertificateBase {
|
|
365
|
-
issuer: {
|
|
366
|
-
commonName: string;
|
|
367
|
-
vendorId?: VendorId;
|
|
368
|
-
};
|
|
369
|
-
subject: {
|
|
370
|
-
commonName: string;
|
|
371
|
-
vendorId?: VendorId;
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
export const TlvCertificationDeclaration = TlvObject({
|
|
376
|
-
formatVersion: TlvField(0, TlvUInt16),
|
|
377
|
-
vendorId: TlvField(1, TlvVendorId),
|
|
378
|
-
produceIdArray: TlvField(2, TlvArray(TlvUInt16, { minLength: 1, maxLength: 100 })),
|
|
379
|
-
deviceTypeId: TlvField(3, TlvUInt32),
|
|
380
|
-
certificateId: TlvField(4, TlvString.bound({ length: 19 })),
|
|
381
|
-
securityLevel: TlvField(5, TlvUInt8),
|
|
382
|
-
securityInformation: TlvField(6, TlvUInt16),
|
|
383
|
-
versionNumber: TlvField(7, TlvUInt16),
|
|
384
|
-
certificationType: TlvField(8, TlvUInt8),
|
|
385
|
-
dacOriginVendorId: TlvOptionalField(9, TlvVendorId),
|
|
386
|
-
dacOriginProductId: TlvOptionalField(10, TlvUInt16),
|
|
387
|
-
authorizedPaaList: TlvOptionalField(
|
|
388
|
-
11,
|
|
389
|
-
TlvArray(TlvByteString.bound({ length: 20 }), { minLength: 1, maxLength: 10 }),
|
|
390
|
-
),
|
|
391
|
-
});
|
|
392
|
-
|
|
393
|
-
export type BaseCertificate = TypeFromSchema<typeof TlvBaseCertificate>;
|
|
394
|
-
export type RootCertificate = TypeFromSchema<typeof TlvRootCertificate>;
|
|
395
|
-
export type IntermediateCertificate = TypeFromSchema<typeof TlvIntermediateCertificate>;
|
|
396
|
-
export type OperationalCertificate = TypeFromSchema<typeof TlvOperationalCertificate>;
|
|
397
|
-
export type Unsigned<Type> = { [Property in keyof Type as Exclude<Property, "signature">]: Type[Property] };
|
|
398
|
-
|
|
399
|
-
/**
|
|
400
|
-
* Preserve order of keys from original subject and also copy potential custom elements
|
|
401
|
-
* @param data
|
|
402
|
-
*/
|
|
403
|
-
function subjectOrIssuerToAsn1(data: { [field: string]: any }) {
|
|
404
|
-
const asn = {} as { [field: string]: any[] };
|
|
405
|
-
Object.entries(data).forEach(([key, value]) => {
|
|
406
|
-
if (value === undefined) {
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
|
-
switch (key) {
|
|
410
|
-
case "commonName":
|
|
411
|
-
asn.commonName = X520.CommonName(value as string);
|
|
412
|
-
break;
|
|
413
|
-
case "sureName":
|
|
414
|
-
asn.sureName = X520.SurName(value as string);
|
|
415
|
-
break;
|
|
416
|
-
case "serialNum":
|
|
417
|
-
asn.serialNum = X520.SerialNumber(value as string);
|
|
418
|
-
break;
|
|
419
|
-
case "countryName":
|
|
420
|
-
asn.countryName = X520.CountryName(value as string);
|
|
421
|
-
break;
|
|
422
|
-
case "localityName":
|
|
423
|
-
asn.localityName = X520.LocalityName(value as string);
|
|
424
|
-
break;
|
|
425
|
-
case "stateOrProvinceName":
|
|
426
|
-
asn.stateOrProvinceName = X520.StateOrProvinceName(value as string);
|
|
427
|
-
break;
|
|
428
|
-
case "orgName":
|
|
429
|
-
asn.orgName = X520.OrganisationName(value as string);
|
|
430
|
-
break;
|
|
431
|
-
case "orgUnitName":
|
|
432
|
-
asn.orgUnitName = X520.OrganizationalUnitName(value as string);
|
|
433
|
-
break;
|
|
434
|
-
case "title":
|
|
435
|
-
asn.title = X520.Title(value as string);
|
|
436
|
-
break;
|
|
437
|
-
case "name":
|
|
438
|
-
asn.name = X520.Name(value as string);
|
|
439
|
-
break;
|
|
440
|
-
case "givenName":
|
|
441
|
-
asn.givenName = X520.GivenName(value as string);
|
|
442
|
-
break;
|
|
443
|
-
case "initials":
|
|
444
|
-
asn.initials = X520.Initials(value as string);
|
|
445
|
-
break;
|
|
446
|
-
case "genQualifier":
|
|
447
|
-
asn.genQualifier = X520.GenerationQualifier(value as string);
|
|
448
|
-
break;
|
|
449
|
-
case "dnQualifier":
|
|
450
|
-
asn.dnQualifier = X520.DnQualifier(value as string);
|
|
451
|
-
break;
|
|
452
|
-
case "pseudonym":
|
|
453
|
-
asn.pseudonym = X520.Pseudonym(value as string);
|
|
454
|
-
break;
|
|
455
|
-
case "domainComponent":
|
|
456
|
-
asn.domainComponent = X520.DomainComponent(value as string);
|
|
457
|
-
break;
|
|
458
|
-
case "nodeId":
|
|
459
|
-
asn.nodeId = NodeId_Matter(value as NodeId);
|
|
460
|
-
break;
|
|
461
|
-
case "firmwareSigningId":
|
|
462
|
-
asn.firmwareSigningId = FirmwareSigningId_Matter(value as number);
|
|
463
|
-
break;
|
|
464
|
-
case "icacId":
|
|
465
|
-
asn.icacId = IcacId_Matter(value as number | bigint);
|
|
466
|
-
break;
|
|
467
|
-
case "rcacId":
|
|
468
|
-
asn.rcacId = RcacId_Matter(value as number | bigint);
|
|
469
|
-
break;
|
|
470
|
-
case "fabricId":
|
|
471
|
-
asn.fabricId = FabricId_Matter(value as FabricId);
|
|
472
|
-
break;
|
|
473
|
-
case "caseAuthenticatedTags":
|
|
474
|
-
// In theory if someone mixes multiple caseAuthenticatedTag fields with other fields we currently would
|
|
475
|
-
// code them in ASN.1 as fields at the first position from the original data which might fail
|
|
476
|
-
// certificate validation. Changing this would require to change Tlv decoding, so lets try that way for now.
|
|
477
|
-
const caseAuthenticatedTags = value as CaseAuthenticatedTag[];
|
|
478
|
-
CaseAuthenticatedTag.validateNocTagList(caseAuthenticatedTags);
|
|
479
|
-
|
|
480
|
-
const cat0 = caseAuthenticatedTags[0];
|
|
481
|
-
const cat1 = caseAuthenticatedTags[1];
|
|
482
|
-
const cat2 = caseAuthenticatedTags[2];
|
|
483
|
-
if (cat0 !== undefined) {
|
|
484
|
-
asn.caseAuthenticatedTag0 = NocCat_Matter(cat0);
|
|
485
|
-
}
|
|
486
|
-
if (cat1 !== undefined) {
|
|
487
|
-
asn.caseAuthenticatedTag1 = NocCat_Matter(cat1);
|
|
488
|
-
}
|
|
489
|
-
if (cat2 !== undefined) {
|
|
490
|
-
asn.caseAuthenticatedTag2 = NocCat_Matter(cat2);
|
|
491
|
-
}
|
|
492
|
-
break;
|
|
493
|
-
case "vendorId": // Only relevant for ASN.1 encoding of DAC/PAA/PAI certificates
|
|
494
|
-
asn.vendorId = VendorId_Matter(value as VendorId);
|
|
495
|
-
break;
|
|
496
|
-
case "productId": // Only relevant for ASN.1 encoding of DAC/PAA/PAI certificates
|
|
497
|
-
asn.productId = ProductId_Matter(value as number);
|
|
498
|
-
break;
|
|
499
|
-
case "commonNamePs":
|
|
500
|
-
asn.commonNamePs = X520.CommonName(value as string, true);
|
|
501
|
-
break;
|
|
502
|
-
case "sureNamePs":
|
|
503
|
-
asn.sureNamePs = X520.SurName(value as string, true);
|
|
504
|
-
break;
|
|
505
|
-
case "serialNumPs":
|
|
506
|
-
asn.serialNumPs = X520.SerialNumber(value as string, true);
|
|
507
|
-
break;
|
|
508
|
-
case "countryNamePs":
|
|
509
|
-
asn.countryNamePs = X520.CountryName(value as string, true);
|
|
510
|
-
break;
|
|
511
|
-
case "localityNamePs":
|
|
512
|
-
asn.localityNamePs = X520.LocalityName(value as string, true);
|
|
513
|
-
break;
|
|
514
|
-
case "stateOrProvinceNamePs":
|
|
515
|
-
asn.stateOrProvinceNamePs = X520.StateOrProvinceName(value as string, true);
|
|
516
|
-
break;
|
|
517
|
-
case "orgNamePs":
|
|
518
|
-
asn.orgNamePs = X520.OrganisationName(value as string, true);
|
|
519
|
-
break;
|
|
520
|
-
case "orgUnitNamePs":
|
|
521
|
-
asn.orgUnitNamePs = X520.OrganizationalUnitName(value as string, true);
|
|
522
|
-
break;
|
|
523
|
-
case "titlePs":
|
|
524
|
-
asn.titlePs = X520.Title(value as string, true);
|
|
525
|
-
break;
|
|
526
|
-
case "namePs":
|
|
527
|
-
asn.namePs = X520.Name(value as string, true);
|
|
528
|
-
break;
|
|
529
|
-
case "givenNamePs":
|
|
530
|
-
asn.givenNamePs = X520.GivenName(value as string, true);
|
|
531
|
-
break;
|
|
532
|
-
case "initialsPs":
|
|
533
|
-
asn.initialsPs = X520.Initials(value as string, true);
|
|
534
|
-
break;
|
|
535
|
-
case "genQualifierPs":
|
|
536
|
-
asn.genQualifierPs = X520.GenerationQualifier(value as string, true);
|
|
537
|
-
break;
|
|
538
|
-
case "dnQualifierPs":
|
|
539
|
-
asn.dnQualifierPs = X520.DnQualifier(value as string, true);
|
|
540
|
-
break;
|
|
541
|
-
case "pseudonymPs":
|
|
542
|
-
asn.pseudonymPs = X520.Pseudonym(value as string, true);
|
|
543
|
-
break;
|
|
544
|
-
}
|
|
545
|
-
});
|
|
546
|
-
return asn;
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
function extensionsToAsn1(extensions: BaseCertificate["extensions"]) {
|
|
550
|
-
const asn = {} as { [field: string]: any[] | any };
|
|
551
|
-
Object.entries(extensions).forEach(([key, value]) => {
|
|
552
|
-
if (value === undefined) {
|
|
553
|
-
return;
|
|
554
|
-
}
|
|
555
|
-
switch (key) {
|
|
556
|
-
case "basicConstraints":
|
|
557
|
-
asn.basicConstraints = X509.BasicConstraints(value);
|
|
558
|
-
break;
|
|
559
|
-
case "keyUsage":
|
|
560
|
-
asn.keyUsage = X509.KeyUsage(
|
|
561
|
-
ExtensionKeyUsageSchema.encode(value as TypeFromPartialBitSchema<typeof ExtensionKeyUsageBitmap>),
|
|
562
|
-
);
|
|
563
|
-
break;
|
|
564
|
-
case "extendedKeyUsage":
|
|
565
|
-
asn.extendedKeyUsage = X509.ExtendedKeyUsage(value as number[] | undefined);
|
|
566
|
-
break;
|
|
567
|
-
case "subjectKeyIdentifier":
|
|
568
|
-
asn.subjectKeyIdentifier = X509.SubjectKeyIdentifier(value as Uint8Array);
|
|
569
|
-
break;
|
|
570
|
-
case "authorityKeyIdentifier":
|
|
571
|
-
asn.authorityKeyIdentifier = X509.AuthorityKeyIdentifier(value as Uint8Array);
|
|
572
|
-
break;
|
|
573
|
-
case "futureExtension":
|
|
574
|
-
asn.futureExtension = RawBytes(Bytes.concat(...((value as Uint8Array[] | undefined) ?? [])));
|
|
575
|
-
break;
|
|
576
|
-
}
|
|
577
|
-
});
|
|
578
|
-
return asn;
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
function genericBuildAsn1Structure({
|
|
582
|
-
serialNumber,
|
|
583
|
-
notBefore,
|
|
584
|
-
notAfter,
|
|
585
|
-
issuer,
|
|
586
|
-
subject,
|
|
587
|
-
ellipticCurvePublicKey,
|
|
588
|
-
extensions,
|
|
589
|
-
}: Unsigned<BaseCertificate>) {
|
|
590
|
-
const {
|
|
591
|
-
basicConstraints: { isCa, pathLen },
|
|
592
|
-
} = extensions;
|
|
593
|
-
if (!isCa && pathLen !== undefined) {
|
|
594
|
-
throw new CertificateError("Path length must be undefined for non-CA certificates.");
|
|
595
|
-
}
|
|
596
|
-
return {
|
|
597
|
-
version: ContextTagged(0, 2), // v3
|
|
598
|
-
serialNumber: DatatypeOverride(DerType.Integer, serialNumber),
|
|
599
|
-
signatureAlgorithm: X962.EcdsaWithSHA256,
|
|
600
|
-
issuer: subjectOrIssuerToAsn1(issuer),
|
|
601
|
-
validity: {
|
|
602
|
-
notBefore: matterToJsDate(notBefore),
|
|
603
|
-
notAfter: matterToJsDate(notAfter),
|
|
604
|
-
},
|
|
605
|
-
subject: subjectOrIssuerToAsn1(subject),
|
|
606
|
-
publicKey: X962.PublicKeyEcPrime256v1(ellipticCurvePublicKey),
|
|
607
|
-
extensions: ContextTagged(3, extensionsToAsn1(extensions)),
|
|
608
|
-
};
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
function genericCertToAsn1(cert: Unsigned<BaseCertificate>) {
|
|
612
|
-
const certBytes = DerCodec.encode(genericBuildAsn1Structure(cert));
|
|
613
|
-
assertCertificateDerSize(certBytes);
|
|
614
|
-
return certBytes;
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
function assertCertificateDerSize(certBytes: Uint8Array) {
|
|
618
|
-
if (certBytes.length > MAX_DER_CERTIFICATE_SIZE) {
|
|
619
|
-
throw new ImplementationError(
|
|
620
|
-
`Certificate to generate is too big: ${certBytes.length} bytes instead of max ${MAX_DER_CERTIFICATE_SIZE} bytes`,
|
|
621
|
-
);
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
export class CertificateManager {
|
|
626
|
-
#crypto: Crypto;
|
|
627
|
-
|
|
628
|
-
constructor(crypto: Crypto) {
|
|
629
|
-
this.#crypto = crypto;
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
get crypto() {
|
|
633
|
-
return this.#crypto;
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
rootCertToAsn1(cert: Unsigned<RootCertificate>) {
|
|
637
|
-
const {
|
|
638
|
-
extensions: {
|
|
639
|
-
basicConstraints: { isCa },
|
|
640
|
-
},
|
|
641
|
-
} = cert;
|
|
642
|
-
if (!isCa) {
|
|
643
|
-
throw new CertificateError("Root certificate must be a CA.");
|
|
644
|
-
}
|
|
645
|
-
return genericCertToAsn1(cert);
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
intermediateCaCertToAsn1(cert: Unsigned<IntermediateCertificate>) {
|
|
649
|
-
const {
|
|
650
|
-
extensions: {
|
|
651
|
-
basicConstraints: { isCa },
|
|
652
|
-
},
|
|
653
|
-
} = cert;
|
|
654
|
-
if (!isCa) {
|
|
655
|
-
throw new CertificateError("Intermediate certificate must be a CA.");
|
|
656
|
-
}
|
|
657
|
-
return genericCertToAsn1(cert);
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
nodeOperationalCertToAsn1(cert: Unsigned<OperationalCertificate>) {
|
|
661
|
-
const {
|
|
662
|
-
issuer: { icacId, rcacId },
|
|
663
|
-
extensions: {
|
|
664
|
-
basicConstraints: { isCa },
|
|
665
|
-
},
|
|
666
|
-
} = cert;
|
|
667
|
-
if (icacId === undefined && rcacId === undefined) {
|
|
668
|
-
throw new CertificateError("Issuer RCAC or ICAC ID must be defined for an operational certificate.");
|
|
669
|
-
}
|
|
670
|
-
if (isCa) {
|
|
671
|
-
throw new CertificateError("Node operational certificate must not be a CA.");
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
return genericCertToAsn1(cert);
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
async deviceAttestationCertToAsn1(cert: Unsigned<DeviceAttestationCertificate>, key: Key) {
|
|
678
|
-
const certificate = genericBuildAsn1Structure(cert);
|
|
679
|
-
const signature = await this.#crypto.signEcdsa(key, DerCodec.encode(certificate), "der");
|
|
680
|
-
const certBytes = DerCodec.encode({
|
|
681
|
-
certificate,
|
|
682
|
-
signAlgorithm: X962.EcdsaWithSHA256,
|
|
683
|
-
signature: DerBitString(signature),
|
|
684
|
-
});
|
|
685
|
-
assertCertificateDerSize(certBytes);
|
|
686
|
-
return certBytes;
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
async productAttestationIntermediateCertToAsn1(
|
|
690
|
-
cert: Unsigned<ProductAttestationIntermediateCertificate>,
|
|
691
|
-
key: Key,
|
|
692
|
-
) {
|
|
693
|
-
const certificate = genericBuildAsn1Structure(cert);
|
|
694
|
-
const signature = await this.#crypto.signEcdsa(key, DerCodec.encode(certificate), "der");
|
|
695
|
-
const certBytes = DerCodec.encode({
|
|
696
|
-
certificate,
|
|
697
|
-
signAlgorithm: X962.EcdsaWithSHA256,
|
|
698
|
-
signature: DerBitString(signature),
|
|
699
|
-
});
|
|
700
|
-
assertCertificateDerSize(certBytes);
|
|
701
|
-
return certBytes;
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
async productAttestationAuthorityCertToAsn1(cert: Unsigned<ProductAttestationAuthorityCertificate>, key: Key) {
|
|
705
|
-
const certificate = genericBuildAsn1Structure(cert);
|
|
706
|
-
const certBytes = DerCodec.encode({
|
|
707
|
-
certificate,
|
|
708
|
-
signAlgorithm: X962.EcdsaWithSHA256,
|
|
709
|
-
signature: DerBitString(await this.#crypto.signEcdsa(key, DerCodec.encode(certificate), "der")),
|
|
710
|
-
});
|
|
711
|
-
assertCertificateDerSize(certBytes);
|
|
712
|
-
return certBytes;
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
async certificationDeclarationToAsn1(
|
|
716
|
-
eContent: Uint8Array,
|
|
717
|
-
subjectKeyIdentifier: Uint8Array,
|
|
718
|
-
privateKey: JsonWebKey,
|
|
719
|
-
) {
|
|
720
|
-
const certificate = {
|
|
721
|
-
version: 3,
|
|
722
|
-
digestAlgorithm: [SHA256_CMS],
|
|
723
|
-
encapContentInfo: Pkcs7.Data(eContent),
|
|
724
|
-
signerInfo: [
|
|
725
|
-
{
|
|
726
|
-
version: 3,
|
|
727
|
-
subjectKeyIdentifier: ContextTaggedBytes(0, subjectKeyIdentifier),
|
|
728
|
-
digestAlgorithm: SHA256_CMS,
|
|
729
|
-
signatureAlgorithm: X962.EcdsaWithSHA256,
|
|
730
|
-
signature: await this.#crypto.signEcdsa(privateKey, eContent, "der"),
|
|
731
|
-
},
|
|
732
|
-
],
|
|
733
|
-
};
|
|
734
|
-
|
|
735
|
-
const certBytes = DerCodec.encode(Pkcs7.SignedData(certificate));
|
|
736
|
-
assertCertificateDerSize(certBytes);
|
|
737
|
-
return certBytes;
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
/**
|
|
741
|
-
* Validate general requirements a Matter certificate fields must fulfill.
|
|
742
|
-
* Rules for this are listed in @see {@link MatterSpecification.v12.Core} §6.5.x
|
|
743
|
-
*/
|
|
744
|
-
validateGeneralCertificateFields(cert: RootCertificate | OperationalCertificate | IntermediateCertificate) {
|
|
745
|
-
if (cert.serialNumber.length > 20)
|
|
746
|
-
throw new CertificateError(
|
|
747
|
-
`Serial number must not be longer then 20 octets. Current serial number has ${cert.serialNumber.length} octets.`,
|
|
748
|
-
);
|
|
749
|
-
|
|
750
|
-
if (cert.signatureAlgorithm !== 1) {
|
|
751
|
-
// ecdsa-with-sha256
|
|
752
|
-
throw new CertificateError(`Unsupported signature algorithm: ${cert.signatureAlgorithm}`);
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
if (cert.publicKeyAlgorithm !== 1) {
|
|
756
|
-
// ec-pub-key
|
|
757
|
-
throw new CertificateError(`Unsupported public key algorithm: ${cert.publicKeyAlgorithm}`);
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
if (cert.ellipticCurveIdentifier !== 1) {
|
|
761
|
-
// prime256v1
|
|
762
|
-
throw new CertificateError(`Unsupported elliptic curve identifier: ${cert.ellipticCurveIdentifier}`);
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
// All implementations SHALL reject Matter certificates with more than 5 RDNs in a single DN.
|
|
766
|
-
if (Object.keys(cert.subject).length > 5) {
|
|
767
|
-
throw new CertificateError(`Certificate subject must not contain more than 5 RDNs.`);
|
|
768
|
-
}
|
|
769
|
-
if (Object.keys(cert.issuer).length > 5) {
|
|
770
|
-
throw new CertificateError(`Certificate issuer must not contain more than 5 RDNs.`);
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
// notBefore date should be already reached, notAfter is not checked right now
|
|
774
|
-
// TODO: implement real checks when we add "Last known Good UTC time"
|
|
775
|
-
if (cert.notBefore * 1000 > Time.nowMs()) {
|
|
776
|
-
logger.warn(`Certificate notBefore date is in the future: ${cert.notBefore * 1000} vs ${Time.nowMs()}`);
|
|
777
|
-
/*throw new CertificateError(
|
|
778
|
-
`Certificate notBefore date is in the future: ${cert.notBefore * 1000} vs ${Time.nowMs()}`,
|
|
779
|
-
);*/
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
/**
|
|
784
|
-
* Verify requirements a Matter Root certificate must fulfill.
|
|
785
|
-
* Rules for this are listed in @see {@link MatterSpecification.v12.Core} §6.5.x
|
|
786
|
-
*/
|
|
787
|
-
async verifyRootCertificate(rootCert: RootCertificate) {
|
|
788
|
-
this.validateGeneralCertificateFields(rootCert);
|
|
789
|
-
|
|
790
|
-
// The subject DN SHALL NOT encode any matter-node-id attribute.
|
|
791
|
-
if ("nodeId" in rootCert.subject) {
|
|
792
|
-
throw new CertificateError(`Root certificate must not contain a nodeId.`);
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
// The subject DN MAY encode at most one matter-fabric-id attribute.
|
|
796
|
-
if (rootCert.subject.fabricId !== undefined) {
|
|
797
|
-
if (Array.isArray(rootCert.subject.fabricId)) {
|
|
798
|
-
throw new CertificateError(
|
|
799
|
-
`Invalid fabricId in NoC certificate: ${Diagnostic.json(rootCert.subject.fabricId)}`,
|
|
800
|
-
);
|
|
801
|
-
}
|
|
802
|
-
// If present, the matter-fabric-id attribute’s value SHALL NOT be 0
|
|
803
|
-
if (rootCert.subject.fabricId === FabricId(0)) {
|
|
804
|
-
throw new CertificateError(
|
|
805
|
-
`Invalid fabricId in NoC certificate: ${Diagnostic.json(rootCert.subject.fabricId)}`,
|
|
806
|
-
);
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
// The subject DN SHALL NOT encode any matter-icac-id attribute.
|
|
811
|
-
if ("icacId" in rootCert.subject) {
|
|
812
|
-
throw new CertificateError(`Root certificate must not contain an icacId.`);
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
// The subject DN SHALL encode exactly one matter-rcac-id attribute.
|
|
816
|
-
if (rootCert.subject.rcacId === undefined || Array.isArray(rootCert.subject.rcacId)) {
|
|
817
|
-
throw new CertificateError(
|
|
818
|
-
`Invalid rcacId in Root certificate: ${Diagnostic.json(rootCert.subject.rcacId)}`,
|
|
819
|
-
);
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
// The subject DN SHALL NOT encode any matter-noc-cat attribute.
|
|
823
|
-
if ("caseAuthenticatedTags" in rootCert.subject) {
|
|
824
|
-
throw new CertificateError(`Root certificate must not contain a caseAuthenticatedTags.`);
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
// The basic constraints extension SHALL be encoded with is-ca set to true.
|
|
828
|
-
if (rootCert.extensions.basicConstraints.isCa !== true) {
|
|
829
|
-
throw new CertificateError(`Root certificate must have isCa set to true.`);
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
// The key usage extension SHALL be encoded with at least two flags: keyCertSign (0x0020) and CRLSign (0x0040)
|
|
833
|
-
// and optionally with digitalSignature (0x0001).
|
|
834
|
-
if (
|
|
835
|
-
ExtensionKeyUsageSchema.encode(rootCert.extensions.keyUsage) !== 0x0060 &&
|
|
836
|
-
ExtensionKeyUsageSchema.encode(rootCert.extensions.keyUsage) !== 0x0061
|
|
837
|
-
) {
|
|
838
|
-
throw new CertificateError(
|
|
839
|
-
`Root certificate keyUsage must have keyCertSign and CRLSign and optionally digitalSignature set.`,
|
|
840
|
-
);
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
// The extended key usage extension SHALL NOT be present.
|
|
844
|
-
if (rootCert.extensions.extendedKeyUsage !== undefined) {
|
|
845
|
-
throw new CertificateError(`Root certificate must not have extendedKeyUsage set.`);
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
// The subject key identifier extension SHALL be present and 160 bit long.
|
|
849
|
-
if (rootCert.extensions.subjectKeyIdentifier === undefined) {
|
|
850
|
-
throw new CertificateError(`Root certificate must have subjectKeyIdentifier set.`);
|
|
851
|
-
}
|
|
852
|
-
if (rootCert.extensions.subjectKeyIdentifier.length !== 20) {
|
|
853
|
-
throw new CertificateError(`Root certificate subjectKeyIdentifier must be 160 bit.`);
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
// The authority key identifier extension SHALL be present and 160 bit long.
|
|
857
|
-
if (rootCert.extensions.authorityKeyIdentifier === undefined) {
|
|
858
|
-
throw new CertificateError(`Root certificate must have authorityKeyIdentifier set.`);
|
|
859
|
-
}
|
|
860
|
-
if (rootCert.extensions.authorityKeyIdentifier.length !== 20) {
|
|
861
|
-
throw new CertificateError(`Root certificate authorityKeyIdentifier must be 160 bit.`);
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
// The authority key identifier extension SHALL be equal to the subject key identifier extension.
|
|
865
|
-
if (!Bytes.areEqual(rootCert.extensions.authorityKeyIdentifier, rootCert.extensions.subjectKeyIdentifier)) {
|
|
866
|
-
throw new CertificateError(
|
|
867
|
-
`Root certificate authorityKeyIdentifier must be equal to subjectKeyIdentifier.`,
|
|
868
|
-
);
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
await this.#crypto.verifyEcdsa(
|
|
872
|
-
PublicKey(rootCert.ellipticCurvePublicKey),
|
|
873
|
-
this.rootCertToAsn1(rootCert),
|
|
874
|
-
rootCert.signature,
|
|
875
|
-
);
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
/**
|
|
879
|
-
* Verify requirements a Matter Node Operational certificate must fulfill.
|
|
880
|
-
* Rules for this are listed in @see {@link MatterSpecification.v12.Core} §6.5.x
|
|
881
|
-
*/
|
|
882
|
-
async verifyNodeOperationalCertificate(
|
|
883
|
-
nocCert: OperationalCertificate,
|
|
884
|
-
rootCert: RootCertificate,
|
|
885
|
-
icaCert?: IntermediateCertificate,
|
|
886
|
-
) {
|
|
887
|
-
this.validateGeneralCertificateFields(nocCert);
|
|
888
|
-
|
|
889
|
-
// The subject DN SHALL encode exactly one matter-node-id attribute.
|
|
890
|
-
if (nocCert.subject.nodeId === undefined || Array.isArray(nocCert.subject.nodeId)) {
|
|
891
|
-
throw new CertificateError(`Invalid nodeId in NoC certificate: ${Diagnostic.json(nocCert.subject.nodeId)}`);
|
|
892
|
-
}
|
|
893
|
-
// The matter-node-id attribute’s value SHALL be in the Operational Node ID
|
|
894
|
-
if (!NodeId.isOperationalNodeId(nocCert.subject.nodeId)) {
|
|
895
|
-
throw new CertificateError(`Invalid nodeId in NoC certificate: ${Diagnostic.json(nocCert.subject.nodeId)}`);
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
// The subject DN SHALL encode exactly one matter-fabric-id attribute.
|
|
899
|
-
if (nocCert.subject.fabricId === undefined || Array.isArray(nocCert.subject.fabricId)) {
|
|
900
|
-
throw new CertificateError(
|
|
901
|
-
`Invalid fabricId in NoC certificate: ${Diagnostic.json(nocCert.subject.fabricId)}`,
|
|
902
|
-
);
|
|
903
|
-
}
|
|
904
|
-
// The matter-fabric-id attribute’s value SHALL NOT be 0
|
|
905
|
-
if (nocCert.subject.fabricId === FabricId(0)) {
|
|
906
|
-
throw new CertificateError(
|
|
907
|
-
`Invalid fabricId in NoC certificate: ${Diagnostic.json(nocCert.subject.fabricId)}`,
|
|
908
|
-
);
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
// The subject DN SHALL NOT encode any matter-icac-id attribute.
|
|
912
|
-
if ("icacId" in nocCert.subject) {
|
|
913
|
-
throw new CertificateError(`Noc certificate must not contain an icacId.`);
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
// The subject DN SHALL NOT encode any matter-rcac-id attribute.
|
|
917
|
-
if ("rcacId" in nocCert.subject) {
|
|
918
|
-
throw new CertificateError(`Noc certificate must not contain an rcacId.`);
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
// The subject DN MAY encode at most three matter-noc-cat attributes.
|
|
922
|
-
if (nocCert.subject.caseAuthenticatedTags !== undefined) {
|
|
923
|
-
CaseAuthenticatedTag.validateNocTagList(nocCert.subject.caseAuthenticatedTags); // throws ValidationError
|
|
924
|
-
}
|
|
925
|
-
|
|
926
|
-
// When any matter-fabric-id attributes are present in either the Matter Root CA Certificate or the Matter ICA
|
|
927
|
-
// Certificate, the value SHALL match the one present in the Matter Node Operational Certificate (NOC) within
|
|
928
|
-
// the same certificate chain.
|
|
929
|
-
if (rootCert.subject.fabricId !== undefined && rootCert.subject.fabricId !== nocCert.subject.fabricId) {
|
|
930
|
-
throw new CertificateError(
|
|
931
|
-
`FabricId in NoC certificate does not match the fabricId in the parent certificate. ${Diagnostic.json(
|
|
932
|
-
rootCert.subject.fabricId,
|
|
933
|
-
)} !== ${Diagnostic.json(nocCert.subject.fabricId)}`,
|
|
934
|
-
);
|
|
935
|
-
}
|
|
936
|
-
if (
|
|
937
|
-
icaCert !== undefined &&
|
|
938
|
-
icaCert.subject.fabricId !== undefined &&
|
|
939
|
-
icaCert.subject.fabricId !== nocCert.subject.fabricId
|
|
940
|
-
) {
|
|
941
|
-
throw new CertificateError(
|
|
942
|
-
`FabricId in NoC certificate does not match the fabricId in the parent certificate. ${Diagnostic.json(
|
|
943
|
-
icaCert.subject.fabricId,
|
|
944
|
-
)} !== ${Diagnostic.json(nocCert.subject.fabricId)}`,
|
|
945
|
-
);
|
|
946
|
-
}
|
|
947
|
-
|
|
948
|
-
// The basic constraints extension SHALL be encoded with is-ca set to false.
|
|
949
|
-
if (nocCert.extensions.basicConstraints.isCa) {
|
|
950
|
-
throw new CertificateError(`Noc certificate must not have isCa set to true.`);
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
// The key usage extension SHALL be encoded with exactly two flags: keyCertSign (0x0020) and CRLSign (0x0040).
|
|
954
|
-
// Formally the check should be the following line but Amazon uses a wrong Root cert which also has
|
|
955
|
-
// digitalCertificate set, so we just check the the two needed are set and ignore additionally set parameters.
|
|
956
|
-
//if (ExtensionKeyUsageSchema.encode(nocCert.extensions.keyUsage) !== 1) {
|
|
957
|
-
if (!nocCert.extensions.keyUsage.digitalSignature) {
|
|
958
|
-
throw new CertificateError(`Noc certificate must have keyUsage set to digitalSignature.`);
|
|
959
|
-
}
|
|
960
|
-
|
|
961
|
-
// The extended key usage extension SHALL be encoded with exactly two key-purpose-id values: serverAuth and clientAuth.
|
|
962
|
-
if (
|
|
963
|
-
nocCert.extensions.extendedKeyUsage === undefined ||
|
|
964
|
-
(!nocCert.extensions.extendedKeyUsage.includes(1) && !nocCert.extensions.extendedKeyUsage.includes(2))
|
|
965
|
-
) {
|
|
966
|
-
throw new CertificateError(
|
|
967
|
-
`Noc certificate must have extendedKeyUsage with serverAuth and clientAuth: ${Diagnostic.json(nocCert.extensions.extendedKeyUsage)}`,
|
|
968
|
-
);
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
// The subject key identifier extension SHALL be present and 160 bit long.
|
|
972
|
-
if (nocCert.extensions.subjectKeyIdentifier === undefined) {
|
|
973
|
-
throw new CertificateError(`Noc certificate must have subjectKeyIdentifier set.`);
|
|
974
|
-
}
|
|
975
|
-
if (nocCert.extensions.subjectKeyIdentifier.length !== 20) {
|
|
976
|
-
throw new CertificateError(`Noc certificate subjectKeyIdentifier must be 160 bit.`);
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
// The authority key identifier extension SHALL be present and 160 bit long.
|
|
980
|
-
if (nocCert.extensions.authorityKeyIdentifier === undefined) {
|
|
981
|
-
throw new CertificateError(`Noc certificate must have authorityKeyIdentifier set.`);
|
|
982
|
-
}
|
|
983
|
-
if (nocCert.extensions.authorityKeyIdentifier.length !== 20) {
|
|
984
|
-
throw new CertificateError(`Noc certificate authorityKeyIdentifier must be 160 bit.`);
|
|
985
|
-
}
|
|
986
|
-
|
|
987
|
-
// Validate authority key identifier against subject key identifier
|
|
988
|
-
if (
|
|
989
|
-
!Bytes.areEqual(
|
|
990
|
-
nocCert.extensions.authorityKeyIdentifier,
|
|
991
|
-
(icaCert ?? rootCert).extensions.subjectKeyIdentifier,
|
|
992
|
-
)
|
|
993
|
-
) {
|
|
994
|
-
throw new CertificateError(
|
|
995
|
-
`Noc certificate authorityKeyIdentifier must be equal to Root/Ica subjectKeyIdentifier.`,
|
|
996
|
-
);
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
await this.#crypto.verifyEcdsa(
|
|
1000
|
-
PublicKey((icaCert ?? rootCert).ellipticCurvePublicKey),
|
|
1001
|
-
this.nodeOperationalCertToAsn1(nocCert),
|
|
1002
|
-
nocCert.signature,
|
|
1003
|
-
);
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
/**
|
|
1007
|
-
* Verify requirements a Matter Intermediate CA certificate must fulfill.
|
|
1008
|
-
* Rules for this are listed in @see {@link MatterSpecification.v12.Core} §6.5.x
|
|
1009
|
-
*/
|
|
1010
|
-
async verifyIntermediateCaCertificate(rootCert: RootCertificate, icaCert: IntermediateCertificate) {
|
|
1011
|
-
this.validateGeneralCertificateFields(icaCert);
|
|
1012
|
-
|
|
1013
|
-
// The subject DN SHALL NOT encode any matter-node-id attribute.
|
|
1014
|
-
if ("nodeId" in icaCert.subject) {
|
|
1015
|
-
throw new CertificateError(`Ica certificate must not contain a nodeId.`);
|
|
1016
|
-
}
|
|
1017
|
-
|
|
1018
|
-
// The subject DN MAY encode at most one matter-fabric-id attribute.
|
|
1019
|
-
if (icaCert.subject.fabricId !== undefined) {
|
|
1020
|
-
if (Array.isArray(icaCert.subject.fabricId)) {
|
|
1021
|
-
throw new CertificateError(
|
|
1022
|
-
`Invalid fabricId in NoC certificate: ${Diagnostic.json(icaCert.subject.fabricId)}`,
|
|
1023
|
-
);
|
|
1024
|
-
}
|
|
1025
|
-
// If present, the matter-fabric-id attribute’s value SHALL NOT be 0
|
|
1026
|
-
if (icaCert.subject.fabricId === FabricId(0)) {
|
|
1027
|
-
throw new CertificateError(
|
|
1028
|
-
`Invalid fabricId in NoC certificate: ${Diagnostic.json(icaCert.subject.fabricId)}`,
|
|
1029
|
-
);
|
|
1030
|
-
}
|
|
1031
|
-
}
|
|
1032
|
-
|
|
1033
|
-
// The subject DN SHALL encode exactly one matter-icac-id attribute.
|
|
1034
|
-
if (icaCert.subject.icacId === undefined || Array.isArray(icaCert.subject.icacId)) {
|
|
1035
|
-
throw new CertificateError(`Invalid icacId in Ica certificate: ${Diagnostic.json(icaCert.subject.icacId)}`);
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
// The subject DN SHALL NOT encode any matter-rcac-id attribute.
|
|
1039
|
-
if ("rcacId" in icaCert.subject) {
|
|
1040
|
-
throw new CertificateError(`Ica certificate must not contain an rcacId.`);
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
|
-
// The subject DN SHALL NOT encode any matter-noc-cat attribute.
|
|
1044
|
-
if ("caseAuthenticatedTags" in icaCert.subject) {
|
|
1045
|
-
throw new CertificateError(`Ica certificate must not contain a caseAuthenticatedTags.`);
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
|
-
// When any matter-fabric-id attributes are present in either the Matter Root CA Certificate or the Matter ICA
|
|
1049
|
-
// Certificate, the value SHALL match the one present in the Matter Node Operational Certificate (NOC) within
|
|
1050
|
-
// the same certificate chain.
|
|
1051
|
-
// Here means: When both are set, they must match
|
|
1052
|
-
if (
|
|
1053
|
-
rootCert.subject.fabricId !== undefined &&
|
|
1054
|
-
icaCert.subject.fabricId !== undefined &&
|
|
1055
|
-
rootCert.subject.fabricId !== icaCert.subject.fabricId
|
|
1056
|
-
) {
|
|
1057
|
-
throw new CertificateError(
|
|
1058
|
-
`FabricId in Ica certificate does not match the fabricId in the parent certificate. ${Diagnostic.json(
|
|
1059
|
-
rootCert.subject.fabricId,
|
|
1060
|
-
)} !== ${Diagnostic.json(icaCert.subject.fabricId)}`,
|
|
1061
|
-
);
|
|
1062
|
-
}
|
|
1063
|
-
|
|
1064
|
-
// Verify the certificate chain by checking rcac ids in subject and issuer
|
|
1065
|
-
if (rootCert.subject.rcacId !== icaCert.issuer.rcacId) {
|
|
1066
|
-
throw new CertificateError(
|
|
1067
|
-
`RcacId in Ica certificate does not match the rcacId in the parent certificate. ${Diagnostic.json(
|
|
1068
|
-
rootCert.subject.rcacId,
|
|
1069
|
-
)} !== ${Diagnostic.json(icaCert.issuer.rcacId)}`,
|
|
1070
|
-
);
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
// The basic constraints extension SHALL be encoded with is-ca set to true.
|
|
1074
|
-
if (!icaCert.extensions.basicConstraints.isCa) {
|
|
1075
|
-
throw new CertificateError(`Ica certificate must have isCa set to true.`);
|
|
1076
|
-
}
|
|
1077
|
-
|
|
1078
|
-
// The key usage extension SHALL be encoded with at least two flags: keyCertSign (0x0020) and CRLSign (0x0040)
|
|
1079
|
-
// and optionally with digitalSignature (0x0001).
|
|
1080
|
-
if (
|
|
1081
|
-
ExtensionKeyUsageSchema.encode(rootCert.extensions.keyUsage) !== 0x0060 &&
|
|
1082
|
-
ExtensionKeyUsageSchema.encode(rootCert.extensions.keyUsage) !== 0x0061
|
|
1083
|
-
) {
|
|
1084
|
-
throw new CertificateError(
|
|
1085
|
-
`Ica certificate keyUsage must have keyCertSign and CRLSign and optionally digitalSignature set.`,
|
|
1086
|
-
);
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
// The extended key usage extension SHALL NOT be present.
|
|
1090
|
-
if (icaCert.extensions.extendedKeyUsage !== undefined) {
|
|
1091
|
-
throw new CertificateError(`Ica certificate must not have extendedKeyUsage set.`);
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
// The subject key identifier extension SHALL be present and 160 bit long.
|
|
1095
|
-
if (icaCert.extensions.subjectKeyIdentifier === undefined) {
|
|
1096
|
-
throw new CertificateError(`Ica certificate must have subjectKeyIdentifier set.`);
|
|
1097
|
-
}
|
|
1098
|
-
if (icaCert.extensions.subjectKeyIdentifier.length !== 20) {
|
|
1099
|
-
throw new CertificateError(`Ica certificate subjectKeyIdentifier must be 160 bit.`);
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
// The authority key identifier extension SHALL be present and 160 bit long.
|
|
1103
|
-
if (icaCert.extensions.authorityKeyIdentifier === undefined) {
|
|
1104
|
-
throw new CertificateError(`Ica certificate must have authorityKeyIdentifier set.`);
|
|
1105
|
-
}
|
|
1106
|
-
if (icaCert.extensions.authorityKeyIdentifier.length !== 20) {
|
|
1107
|
-
throw new CertificateError(`Ica certificate authorityKeyIdentifier must be 160 bit.`);
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
|
-
// Validate authority key identifier against subject key identifier
|
|
1111
|
-
if (!Bytes.areEqual(icaCert.extensions.authorityKeyIdentifier, rootCert.extensions.subjectKeyIdentifier)) {
|
|
1112
|
-
throw new CertificateError(
|
|
1113
|
-
`Ica certificate authorityKeyIdentifier must be equal to root cert subjectKeyIdentifier.`,
|
|
1114
|
-
);
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
|
-
await this.#crypto.verifyEcdsa(
|
|
1118
|
-
PublicKey(rootCert.ellipticCurvePublicKey),
|
|
1119
|
-
this.intermediateCaCertToAsn1(icaCert),
|
|
1120
|
-
icaCert.signature,
|
|
1121
|
-
);
|
|
1122
|
-
}
|
|
1123
|
-
|
|
1124
|
-
async createCertificateSigningRequest(key: Key) {
|
|
1125
|
-
const request = {
|
|
1126
|
-
version: 0,
|
|
1127
|
-
subject: { organization: X520.OrganisationName("CSR") },
|
|
1128
|
-
publicKey: X962.PublicKeyEcPrime256v1(key.publicKey),
|
|
1129
|
-
endSignedBytes: ContextTagged(0),
|
|
1130
|
-
};
|
|
1131
|
-
|
|
1132
|
-
return DerCodec.encode({
|
|
1133
|
-
request,
|
|
1134
|
-
signAlgorithm: X962.EcdsaWithSHA256,
|
|
1135
|
-
signature: DerBitString(await this.#crypto.signEcdsa(key, DerCodec.encode(request), "der")),
|
|
1136
|
-
});
|
|
1137
|
-
}
|
|
1138
|
-
|
|
1139
|
-
async getPublicKeyFromCsr(csr: Uint8Array) {
|
|
1140
|
-
const { [DerKey.Elements]: rootElements } = DerCodec.decode(csr);
|
|
1141
|
-
if (rootElements?.length !== 3) throw new CertificateError("Invalid CSR data");
|
|
1142
|
-
const [requestNode, signAlgorithmNode, signatureNode] = rootElements;
|
|
1143
|
-
|
|
1144
|
-
// Extract the public key
|
|
1145
|
-
const { [DerKey.Elements]: requestElements } = requestNode;
|
|
1146
|
-
if (requestElements?.length !== 4) throw new CertificateError("Invalid CSR data");
|
|
1147
|
-
const [versionNode, _subjectNode, publicKeyNode] = requestElements;
|
|
1148
|
-
const requestVersion = versionNode[DerKey.Bytes][0];
|
|
1149
|
-
if (requestVersion !== 0) throw new CertificateError(`Unsupported request version${requestVersion}`);
|
|
1150
|
-
// TODO: verify subject = { OrganisationName: "CSR" }
|
|
1151
|
-
|
|
1152
|
-
const { [DerKey.Elements]: publicKeyElements } = publicKeyNode;
|
|
1153
|
-
if (publicKeyElements?.length !== 2) throw new CertificateError("Invalid CSR data");
|
|
1154
|
-
const [_publicKeyTypeNode, publicKeyBytesNode] = publicKeyElements;
|
|
1155
|
-
// TODO: verify publicKey algorithm
|
|
1156
|
-
const publicKey = publicKeyBytesNode[DerKey.Bytes];
|
|
1157
|
-
|
|
1158
|
-
// Verify the CSR signature
|
|
1159
|
-
if (
|
|
1160
|
-
signAlgorithmNode[DerKey.Elements]?.[0]?.[DerKey.Bytes] === undefined ||
|
|
1161
|
-
!Bytes.areEqual(
|
|
1162
|
-
X962.EcdsaWithSHA256[DerKey.ObjectId][DerKey.Bytes],
|
|
1163
|
-
signAlgorithmNode[DerKey.Elements]?.[0]?.[DerKey.Bytes],
|
|
1164
|
-
)
|
|
1165
|
-
)
|
|
1166
|
-
throw new CertificateError("Unsupported signature type");
|
|
1167
|
-
await this.#crypto.verifyEcdsa(
|
|
1168
|
-
PublicKey(publicKey),
|
|
1169
|
-
DerCodec.encode(requestNode),
|
|
1170
|
-
signatureNode[DerKey.Bytes],
|
|
1171
|
-
"der",
|
|
1172
|
-
);
|
|
1173
|
-
|
|
1174
|
-
return publicKey;
|
|
1175
|
-
}
|
|
1176
|
-
}
|