@open-charging-cloud/chargy-core 0.13.0 → 0.14.0
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/CHANGELOG.md +44 -0
- package/README.md +1 -1
- package/dist/DocumentSignatures.d.ts +70 -0
- package/dist/DocumentSignatures.d.ts.map +1 -0
- package/dist/OCMF.d.ts +1 -1
- package/dist/OCMF.d.ts.map +1 -1
- package/dist/browser/index.js +333 -29
- package/dist/browser/index.js.map +1 -1
- package/dist/chargy.d.ts +1 -0
- package/dist/chargy.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/interfaces/IChargeTransparencyLiveLink.d.ts +19 -1
- package/dist/interfaces/IChargeTransparencyLiveLink.d.ts.map +1 -1
- package/dist/node/index.js +333 -29
- package/dist/node/index.js.map +1 -1
- package/i18n.json +25 -0
- package/package.json +1 -1
package/dist/browser/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { ed448ph, ed448 } from '@noble/curves/ed448.js';
|
|
|
5
5
|
import { p521, p384, p256 } from '@noble/curves/nist.js';
|
|
6
6
|
import { secp256k1 } from '@noble/curves/secp256k1.js';
|
|
7
7
|
import { ml_dsa87, ml_dsa65, ml_dsa44 } from '@noble/post-quantum/ml-dsa.js';
|
|
8
|
-
import { sha256 as sha256$1 } from '@noble/hashes/sha2.js';
|
|
8
|
+
import { sha256 as sha256$1, sha512 as sha512$1, sha384 as sha384$1 } from '@noble/hashes/sha2.js';
|
|
9
9
|
import { fileTypeFromBuffer } from 'file-type';
|
|
10
10
|
import isURL from 'is-url-superb';
|
|
11
11
|
import jsQR from 'jsqr';
|
|
@@ -3073,10 +3073,10 @@ var NobleCompatibleCurve = class {
|
|
|
3073
3073
|
keyFromPublic(publicKey, encoding) {
|
|
3074
3074
|
if (encoding.toLowerCase() !== "hex")
|
|
3075
3075
|
throw new TypeError("Only hexadecimal public keys are supported by the compatibility API.");
|
|
3076
|
-
const
|
|
3077
|
-
if (!this.suite.isValidPublicKey(
|
|
3076
|
+
const publicKeyBytes2 = normalizeSEC1PublicKey(publicKey, this.coordinateLength);
|
|
3077
|
+
if (!this.suite.isValidPublicKey(publicKeyBytes2))
|
|
3078
3078
|
throw new TypeError("Public key is not a valid point on the selected curve.");
|
|
3079
|
-
return new NobleCompatiblePublicKey(this.suite,
|
|
3079
|
+
return new NobleCompatiblePublicKey(this.suite, publicKeyBytes2, this.coordinateLength);
|
|
3080
3080
|
}
|
|
3081
3081
|
};
|
|
3082
3082
|
var suites = {
|
|
@@ -5910,7 +5910,7 @@ async function signJSONMessage(JSONMessage, KeyPairs, options) {
|
|
|
5910
5910
|
continue;
|
|
5911
5911
|
const publicKeyEncoding = isRawSignatureAlgorithm(algorithm) ? "raw" : "sec1";
|
|
5912
5912
|
const signatureEncoding = options?.signatureEncoding ?? suite.signatureEncoding;
|
|
5913
|
-
const
|
|
5913
|
+
const publicKeyBytes2 = KeyPair.publicKey ?? suite.getPublicKey(KeyPair.privateKey);
|
|
5914
5914
|
const signatureBytes = suite.sign(
|
|
5915
5915
|
plainText,
|
|
5916
5916
|
KeyPair.privateKey,
|
|
@@ -5920,8 +5920,8 @@ async function signJSONMessage(JSONMessage, KeyPairs, options) {
|
|
|
5920
5920
|
algorithm,
|
|
5921
5921
|
publicKeyEncoding,
|
|
5922
5922
|
signatureEncoding,
|
|
5923
|
-
publicKey: bytesToBase64(
|
|
5924
|
-
publicKeyHEX: bytesToHex(
|
|
5923
|
+
publicKey: bytesToBase64(publicKeyBytes2),
|
|
5924
|
+
publicKeyHEX: bytesToHex(publicKeyBytes2),
|
|
5925
5925
|
signature: bytesToBase64(signatureBytes),
|
|
5926
5926
|
signatureHEX: bytesToHex(signatureBytes)
|
|
5927
5927
|
};
|
|
@@ -5932,7 +5932,7 @@ async function signJSONMessage(JSONMessage, KeyPairs, options) {
|
|
|
5932
5932
|
const signatureIsValid = suite.verify(
|
|
5933
5933
|
plainText,
|
|
5934
5934
|
signatureBytes,
|
|
5935
|
-
|
|
5935
|
+
publicKeyBytes2,
|
|
5936
5936
|
signatureOptions(options, signatureEncoding)
|
|
5937
5937
|
);
|
|
5938
5938
|
if (!signatureIsValid)
|
|
@@ -5975,11 +5975,11 @@ async function verifyJSONSignatureResult(JSONMessage, signature, options) {
|
|
|
5975
5975
|
const resolvedOptions = resolveSignOptions(options);
|
|
5976
5976
|
const algorithm = signature.algorithm ?? resolvedOptions.algorithm;
|
|
5977
5977
|
const suite = getSignatureSuite(algorithm);
|
|
5978
|
-
const
|
|
5978
|
+
const publicKeyBytes2 = hexToBytes(signature.publicKeyHEX);
|
|
5979
5979
|
const signatureBytes = hexToBytes(signature.signatureHEX);
|
|
5980
5980
|
const signatureEncoding = signature.signatureEncoding ?? (isRawSignatureAlgorithm(algorithm) ? "raw" : "der");
|
|
5981
5981
|
try {
|
|
5982
|
-
const publicKeyIsValid = suite.isValidPublicKey(
|
|
5982
|
+
const publicKeyIsValid = suite.isValidPublicKey(publicKeyBytes2);
|
|
5983
5983
|
if (!publicKeyIsValid)
|
|
5984
5984
|
return verificationResult(
|
|
5985
5985
|
"InvalidPublicKey" /* InvalidPublicKey */,
|
|
@@ -5995,7 +5995,7 @@ async function verifyJSONSignatureResult(JSONMessage, signature, options) {
|
|
|
5995
5995
|
const signatureIsValid = suite.verify(
|
|
5996
5996
|
plainText,
|
|
5997
5997
|
signatureBytes,
|
|
5998
|
-
|
|
5998
|
+
publicKeyBytes2,
|
|
5999
5999
|
signatureVerificationOptions(signature, options, signatureEncoding)
|
|
6000
6000
|
);
|
|
6001
6001
|
return signatureIsValid ? verificationResult("True" /* True */) : verificationResult(
|
|
@@ -8324,10 +8324,10 @@ var OCMF = class {
|
|
|
8324
8324
|
try {
|
|
8325
8325
|
if (typeof PublicKey !== "string")
|
|
8326
8326
|
throw new TypeError("EdDSA and ML-DSA public keys must use a raw string encoding.");
|
|
8327
|
-
const
|
|
8327
|
+
const publicKeyBytes2 = decodeRawOCMFBytes(PublicKey, PublicKeyEncoding);
|
|
8328
8328
|
const signatureBytes = OCMFJSONDocument.signatureBytes;
|
|
8329
8329
|
const rawPayload = OCMFJSONDocument.rawPayload;
|
|
8330
|
-
if (!signatureSuite.isValidPublicKey(
|
|
8330
|
+
if (!signatureSuite.isValidPublicKey(publicKeyBytes2))
|
|
8331
8331
|
throw new TypeError("Public key is invalid for the selected signature algorithm.");
|
|
8332
8332
|
if (signatureBytes == null)
|
|
8333
8333
|
throw new TypeError("Missing raw signature bytes.");
|
|
@@ -8336,7 +8336,7 @@ var OCMF = class {
|
|
|
8336
8336
|
const signatureValid = signatureSuite.verify(
|
|
8337
8337
|
new TextEncoder().encode(rawPayload),
|
|
8338
8338
|
signatureBytes,
|
|
8339
|
-
|
|
8339
|
+
publicKeyBytes2
|
|
8340
8340
|
);
|
|
8341
8341
|
if (!signatureValid)
|
|
8342
8342
|
this.AddValidationError(OCMFJSONDocument, "Verification_SignatureMismatch");
|
|
@@ -8353,7 +8353,7 @@ var OCMF = class {
|
|
|
8353
8353
|
if (curve === null)
|
|
8354
8354
|
throw new Error(`Unsupported ECC curve '${OCMFJSONDocument.signature.SA ?? ""}'!`);
|
|
8355
8355
|
OCMFJSONDocument.publicKey ??= PublicKey;
|
|
8356
|
-
let
|
|
8356
|
+
let publicKeyBytes2 = null;
|
|
8357
8357
|
if (typeof OCMFJSONDocument.publicKey === "string") {
|
|
8358
8358
|
const ECPoint = this.chargy.asn1.define("ECPoint", function() {
|
|
8359
8359
|
this.seq().obj(
|
|
@@ -8367,36 +8367,36 @@ var OCMF = class {
|
|
|
8367
8367
|
if (PublicKeyEncoding != null && PublicKeyEncoding.length > 0) {
|
|
8368
8368
|
switch (PublicKeyEncoding.toLowerCase()) {
|
|
8369
8369
|
case "hex":
|
|
8370
|
-
|
|
8370
|
+
publicKeyBytes2 = Buffer.from(OCMFJSONDocument.publicKey, "hex");
|
|
8371
8371
|
break;
|
|
8372
8372
|
case "base32":
|
|
8373
|
-
|
|
8373
|
+
publicKeyBytes2 = Buffer.from(this.chargy.base32Decode(OCMFJSONDocument.publicKey, "RFC4648"));
|
|
8374
8374
|
break;
|
|
8375
8375
|
case "base64":
|
|
8376
|
-
|
|
8376
|
+
publicKeyBytes2 = Buffer.from(OCMFJSONDocument.publicKey, "base64");
|
|
8377
8377
|
break;
|
|
8378
8378
|
}
|
|
8379
8379
|
}
|
|
8380
|
-
if (
|
|
8380
|
+
if (publicKeyBytes2 == null) {
|
|
8381
8381
|
const hexRegex = /^[0-9A-Fa-f]+$/;
|
|
8382
8382
|
const base32Regex = /^(?:[A-Z2-7]{8})*(?:[A-Z2-7]{2}={6}|[A-Z2-7]{4}={4}|[A-Z2-7]{5}={3}|[A-Z2-7]{7}=)?$/;
|
|
8383
8383
|
const base64Regex = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
|
|
8384
8384
|
if (hexRegex.test(OCMFJSONDocument.publicKey)) {
|
|
8385
8385
|
PublicKeyEncoding = "hex";
|
|
8386
|
-
|
|
8386
|
+
publicKeyBytes2 = Buffer.from(OCMFJSONDocument.publicKey, "hex");
|
|
8387
8387
|
} else if (base32Regex.test(OCMFJSONDocument.publicKey)) {
|
|
8388
8388
|
PublicKeyEncoding = "base32";
|
|
8389
|
-
|
|
8389
|
+
publicKeyBytes2 = Buffer.from(this.chargy.base32Decode(OCMFJSONDocument.publicKey, "RFC4648"));
|
|
8390
8390
|
} else if (base64Regex.test(OCMFJSONDocument.publicKey)) {
|
|
8391
8391
|
PublicKeyEncoding = "base64";
|
|
8392
|
-
|
|
8392
|
+
publicKeyBytes2 = Buffer.from(OCMFJSONDocument.publicKey, "base64");
|
|
8393
8393
|
}
|
|
8394
8394
|
}
|
|
8395
|
-
if (
|
|
8395
|
+
if (publicKeyBytes2 == null) {
|
|
8396
8396
|
OCMFJSONDocument.validationStatus = "UnknownPublicKeyFormat" /* UnknownPublicKeyFormat */;
|
|
8397
8397
|
return OCMFJSONDocument.validationStatus;
|
|
8398
8398
|
}
|
|
8399
|
-
const decodedPublicKeyASN1 = ECPoint.decode(
|
|
8399
|
+
const decodedPublicKeyASN1 = ECPoint.decode(publicKeyBytes2, "der");
|
|
8400
8400
|
const publicKeyASN1 = decodedPublicKeyASN1;
|
|
8401
8401
|
const coordinates = publicKeyASN1.pubKey.data.subarray(1);
|
|
8402
8402
|
const halfLength = coordinates.length / 2;
|
|
@@ -12057,7 +12057,9 @@ function toSessionVerificationResults(CTR, noRecordsMessage = { "en": "No charge
|
|
|
12057
12057
|
var IChargeTransparencyLiveLink_exports = {};
|
|
12058
12058
|
__export(IChargeTransparencyLiveLink_exports, {
|
|
12059
12059
|
ChargeTransparencyLiveLinkContext: () => ChargeTransparencyLiveLinkContext,
|
|
12060
|
-
IsAChargeTransparencyLiveLink: () => IsAChargeTransparencyLiveLink
|
|
12060
|
+
IsAChargeTransparencyLiveLink: () => IsAChargeTransparencyLiveLink,
|
|
12061
|
+
isConnector: () => isConnector,
|
|
12062
|
+
isTransport: () => isTransport
|
|
12061
12063
|
});
|
|
12062
12064
|
var ChargeTransparencyLiveLinkContext = "https://open.charging.cloud/contexts/chargeTransparency/live/link/1.0";
|
|
12063
12065
|
function isConnector(data) {
|
|
@@ -12083,9 +12085,7 @@ function isTransport(data) {
|
|
|
12083
12085
|
return (data["url"] === void 0 || typeof data["url"] === "string") && (data["urls"] === void 0 || Array.isArray(data["urls"]) && data["urls"].every(isTransportURL)) && (data["totp"] === void 0 || isTOTPConfig(data["totp"]));
|
|
12084
12086
|
}
|
|
12085
12087
|
function IsAChargeTransparencyLiveLink(data) {
|
|
12086
|
-
|
|
12087
|
-
return false;
|
|
12088
|
-
return data["@context"] === ChargeTransparencyLiveLinkContext && (data["created"] === void 0 || data["created"] === null || typeof data["created"] === "string") && (data["description"] === void 0 || isI18NString(data["description"])) && (data["imageURLs"] === void 0 || Array.isArray(data["imageURLs"]) && data["imageURLs"].every((value) => typeof value === "string")) && (data["geoLocation"] === void 0 || isGeoLocation(data["geoLocation"])) && (data["connector"] === void 0 || isConnector(data["connector"])) && (data["liveTransports"] === void 0 || Array.isArray(data["liveTransports"]) && data["liveTransports"].every(isTransport)) && (data["signatures"] === void 0 || Array.isArray(data["signatures"]));
|
|
12088
|
+
return isMandatoryJSONObject(data) && data["@context"] === ChargeTransparencyLiveLinkContext;
|
|
12089
12089
|
}
|
|
12090
12090
|
function isTOTPConfig(data) {
|
|
12091
12091
|
return isObject(data) && typeof data["initialSharedSecret"] === "string" && typeof data["timeStep"] === "number";
|
|
@@ -12329,6 +12329,259 @@ var validationRules_default = {
|
|
|
12329
12329
|
}
|
|
12330
12330
|
}
|
|
12331
12331
|
};
|
|
12332
|
+
var defaultKeyIdGeneration = ["SubjectPublicKeyInfo", "DER", "SHA-256", "hex"];
|
|
12333
|
+
var defaultExcludedProperties = ["signatures"];
|
|
12334
|
+
var canonicalJSONEncodings = ["JSON", "JCS", "UTF-8"];
|
|
12335
|
+
var subjectPublicKeyInfoHeaders = {
|
|
12336
|
+
"EdDSA-Ed25519": "302A300506032B6570032100",
|
|
12337
|
+
"EdDSA-Ed448": "3043300506032B6571033A00",
|
|
12338
|
+
"ECDSA-secp256r1": "3059301306072A8648CE3D020106082A8648CE3D030107034200",
|
|
12339
|
+
"ECDSA-secp384r1": "3076301006072A8648CE3D020106052B81040022036200",
|
|
12340
|
+
"ECDSA-secp521r1": "30819B301006072A8648CE3D020106052B8104002303818600"
|
|
12341
|
+
};
|
|
12342
|
+
function readDERLength2(bytes, offset) {
|
|
12343
|
+
const first = bytes[offset];
|
|
12344
|
+
if (first === void 0)
|
|
12345
|
+
throw new Error("Truncated ASN.1 length!");
|
|
12346
|
+
if (first < 128)
|
|
12347
|
+
return { length: first, size: 1 };
|
|
12348
|
+
const count = first & 127;
|
|
12349
|
+
if (count === 0 || count > 4)
|
|
12350
|
+
throw new Error("Unsupported ASN.1 length!");
|
|
12351
|
+
let length = 0;
|
|
12352
|
+
for (let index = 1; index <= count; index++) {
|
|
12353
|
+
const byte = bytes[offset + index];
|
|
12354
|
+
if (byte === void 0)
|
|
12355
|
+
throw new Error("Truncated ASN.1 length!");
|
|
12356
|
+
length = length * 256 + byte;
|
|
12357
|
+
}
|
|
12358
|
+
return { length, size: count + 1 };
|
|
12359
|
+
}
|
|
12360
|
+
function subjectPublicKeyInfoKeyMaterial(spki) {
|
|
12361
|
+
if (spki[0] !== 48)
|
|
12362
|
+
throw new Error("A SubjectPublicKeyInfo must start with a SEQUENCE!");
|
|
12363
|
+
let index = 1 + readDERLength2(spki, 1).size;
|
|
12364
|
+
if (spki[index] !== 48)
|
|
12365
|
+
throw new Error("The SubjectPublicKeyInfo does not start with an AlgorithmIdentifier!");
|
|
12366
|
+
const algorithmLength = readDERLength2(spki, index + 1);
|
|
12367
|
+
index += 1 + algorithmLength.size + algorithmLength.length;
|
|
12368
|
+
if (spki[index] !== 3)
|
|
12369
|
+
throw new Error("The SubjectPublicKeyInfo does not contain a BIT STRING!");
|
|
12370
|
+
const keyLength = readDERLength2(spki, index + 1);
|
|
12371
|
+
const start = index + 1 + keyLength.size;
|
|
12372
|
+
if (spki[start] !== 0)
|
|
12373
|
+
throw new Error("The SubjectPublicKeyInfo BIT STRING has unused bits!");
|
|
12374
|
+
const keyMaterial = spki.subarray(start + 1, start + keyLength.length);
|
|
12375
|
+
if (keyMaterial.length === 0)
|
|
12376
|
+
throw new Error("The SubjectPublicKeyInfo contains no key material!");
|
|
12377
|
+
return keyMaterial;
|
|
12378
|
+
}
|
|
12379
|
+
function wrapInSubjectPublicKeyInfo(keyMaterial, algorithm) {
|
|
12380
|
+
const header = subjectPublicKeyInfoHeaders[algorithm];
|
|
12381
|
+
if (header === void 0)
|
|
12382
|
+
throw new Error("Cannot wrap a raw " + algorithm + " key into a SubjectPublicKeyInfo!");
|
|
12383
|
+
const headerBytes = hexToBytes(header);
|
|
12384
|
+
const spki = new Uint8Array(headerBytes.length + keyMaterial.length);
|
|
12385
|
+
spki.set(headerBytes, 0);
|
|
12386
|
+
spki.set(keyMaterial, headerBytes.length);
|
|
12387
|
+
return spki;
|
|
12388
|
+
}
|
|
12389
|
+
function decodeText(value, encoding) {
|
|
12390
|
+
switch (encoding) {
|
|
12391
|
+
case "hex":
|
|
12392
|
+
return hexToBytes(value);
|
|
12393
|
+
case "base64":
|
|
12394
|
+
return base64ToBytes(value);
|
|
12395
|
+
default:
|
|
12396
|
+
throw new Error("Unsupported text encoding: " + String(encoding));
|
|
12397
|
+
}
|
|
12398
|
+
}
|
|
12399
|
+
function encodeText(bytes, encoding) {
|
|
12400
|
+
switch (encoding) {
|
|
12401
|
+
case "hex":
|
|
12402
|
+
return bytesToHex(bytes).toUpperCase();
|
|
12403
|
+
case "base64":
|
|
12404
|
+
return bytesToBase64(bytes);
|
|
12405
|
+
default:
|
|
12406
|
+
return null;
|
|
12407
|
+
}
|
|
12408
|
+
}
|
|
12409
|
+
function publicKeyBytes(publicKey, structure) {
|
|
12410
|
+
const storedStructure = publicKey.encodings[0];
|
|
12411
|
+
const storedBytes = decodeText(publicKey.value, publicKey.encodings[publicKey.encodings.length - 1]);
|
|
12412
|
+
if (storedStructure === structure)
|
|
12413
|
+
return storedBytes;
|
|
12414
|
+
if (storedStructure === "SubjectPublicKeyInfo" && structure === "raw")
|
|
12415
|
+
return subjectPublicKeyInfoKeyMaterial(storedBytes);
|
|
12416
|
+
if (storedStructure === "raw" && structure === "SubjectPublicKeyInfo")
|
|
12417
|
+
return wrapInSubjectPublicKeyInfo(storedBytes, publicKey.algorithm);
|
|
12418
|
+
throw new Error("Cannot represent a " + String(storedStructure) + " key as " + structure + "!");
|
|
12419
|
+
}
|
|
12420
|
+
function computeKeyId(publicKey, keyIdGeneration) {
|
|
12421
|
+
let index = 0;
|
|
12422
|
+
const structure = keyIdGeneration[index++];
|
|
12423
|
+
if (structure === void 0)
|
|
12424
|
+
return null;
|
|
12425
|
+
if (structure === "SubjectPublicKeyInfo" && keyIdGeneration[index] === "DER")
|
|
12426
|
+
index++;
|
|
12427
|
+
let bytes;
|
|
12428
|
+
try {
|
|
12429
|
+
bytes = publicKeyBytes(publicKey, structure);
|
|
12430
|
+
} catch {
|
|
12431
|
+
return null;
|
|
12432
|
+
}
|
|
12433
|
+
for (; index < keyIdGeneration.length; index++) {
|
|
12434
|
+
const step = keyIdGeneration[index];
|
|
12435
|
+
if (step === void 0)
|
|
12436
|
+
return null;
|
|
12437
|
+
switch (step) {
|
|
12438
|
+
case "SHA-256":
|
|
12439
|
+
bytes = sha256$1(bytes);
|
|
12440
|
+
break;
|
|
12441
|
+
case "SHA-384":
|
|
12442
|
+
bytes = sha384$1(bytes);
|
|
12443
|
+
break;
|
|
12444
|
+
case "SHA-512":
|
|
12445
|
+
bytes = sha512$1(bytes);
|
|
12446
|
+
break;
|
|
12447
|
+
// A pipeline ends with the text encoding of the id.
|
|
12448
|
+
default:
|
|
12449
|
+
return encodeText(bytes, step);
|
|
12450
|
+
}
|
|
12451
|
+
}
|
|
12452
|
+
return null;
|
|
12453
|
+
}
|
|
12454
|
+
function nobleSignatureAlgorithm(algorithm) {
|
|
12455
|
+
switch (algorithm) {
|
|
12456
|
+
case "ECDSA-secp256r1-SHA256":
|
|
12457
|
+
return "ECDSA-P256";
|
|
12458
|
+
case "ECDSA-secp384r1-SHA384":
|
|
12459
|
+
return "ECDSA-P384";
|
|
12460
|
+
case "ECDSA-secp521r1-SHA512":
|
|
12461
|
+
return "ECDSA-P521";
|
|
12462
|
+
case "EdDSA-Ed25519":
|
|
12463
|
+
return "Ed25519";
|
|
12464
|
+
case "EdDSA-Ed448":
|
|
12465
|
+
return "Ed448";
|
|
12466
|
+
case "ML-DSA-44":
|
|
12467
|
+
return "ML-DSA-44";
|
|
12468
|
+
case "ML-DSA-65":
|
|
12469
|
+
return "ML-DSA-65";
|
|
12470
|
+
case "ML-DSA-87":
|
|
12471
|
+
return "ML-DSA-87";
|
|
12472
|
+
default:
|
|
12473
|
+
return null;
|
|
12474
|
+
}
|
|
12475
|
+
}
|
|
12476
|
+
function asStringArray(value) {
|
|
12477
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string") ? value : void 0;
|
|
12478
|
+
}
|
|
12479
|
+
function collectDocumentPublicKeys(Document) {
|
|
12480
|
+
const publicKeys = new Array();
|
|
12481
|
+
const collectFrom = (candidates) => {
|
|
12482
|
+
if (!Array.isArray(candidates))
|
|
12483
|
+
return;
|
|
12484
|
+
for (const candidate of candidates) {
|
|
12485
|
+
const entry = asJSONObject(candidate);
|
|
12486
|
+
if (entry === void 0)
|
|
12487
|
+
continue;
|
|
12488
|
+
const algorithm = asString(entry["algorithm"]);
|
|
12489
|
+
const value = asString(entry["value"]);
|
|
12490
|
+
const encodings = asStringArray(entry["encodings"]);
|
|
12491
|
+
const keyUsage = asStringArray(entry["keyUsage"]) ?? [];
|
|
12492
|
+
if (algorithm === void 0 || algorithm === "" || value === void 0 || value === "" || encodings === void 0 || encodings.length === 0) {
|
|
12493
|
+
continue;
|
|
12494
|
+
}
|
|
12495
|
+
publicKeys.push({ algorithm, encodings, value, keyUsage });
|
|
12496
|
+
}
|
|
12497
|
+
};
|
|
12498
|
+
const chargingStation = asJSONObject(Document["chargingStation"]);
|
|
12499
|
+
const evse = asJSONObject(chargingStation?.["EVSE"]);
|
|
12500
|
+
collectFrom(asJSONObject(Document["chargingStationOperator"])?.["publicKeys"]);
|
|
12501
|
+
collectFrom(asJSONObject(evse?.["energyMeter"])?.["publicKeys"]);
|
|
12502
|
+
return publicKeys;
|
|
12503
|
+
}
|
|
12504
|
+
function verifyDocumentSignature(Document, Signature, Index, PublicKeys, KeyIdGeneration) {
|
|
12505
|
+
const entry = asJSONObject(Signature);
|
|
12506
|
+
if (entry === void 0)
|
|
12507
|
+
return { index: Index, status: "malformed", details: "The signature is not a JSON object." };
|
|
12508
|
+
const keyId = asString(entry["keyId"]);
|
|
12509
|
+
const algorithm = asString(entry["algorithm"]);
|
|
12510
|
+
const value = asString(entry["value"]);
|
|
12511
|
+
const encodings = asStringArray(entry["encodings"]);
|
|
12512
|
+
const signedData = asJSONObject(entry["signedData"]);
|
|
12513
|
+
const described = (status, details) => ({
|
|
12514
|
+
index: Index,
|
|
12515
|
+
...keyId !== void 0 ? { keyId } : {},
|
|
12516
|
+
...algorithm !== void 0 ? { algorithm } : {},
|
|
12517
|
+
status,
|
|
12518
|
+
...details !== void 0 ? { details } : {}
|
|
12519
|
+
});
|
|
12520
|
+
if (keyId === void 0 || keyId === "" || algorithm === void 0 || algorithm === "" || value === void 0 || value === "" || encodings === void 0 || encodings.length === 0) {
|
|
12521
|
+
return described("malformed", "A signature needs a keyId, an algorithm, encodings and a value.");
|
|
12522
|
+
}
|
|
12523
|
+
const signedEncodings = asStringArray(signedData?.["encodings"]) ?? canonicalJSONEncodings;
|
|
12524
|
+
if (signedEncodings.length !== canonicalJSONEncodings.length || !signedEncodings.every((step, position) => step === canonicalJSONEncodings[position])) {
|
|
12525
|
+
return described(
|
|
12526
|
+
"unsupportedAlgorithm",
|
|
12527
|
+
"Only [ " + canonicalJSONEncodings.join(", ") + " ] signed data is understood here."
|
|
12528
|
+
);
|
|
12529
|
+
}
|
|
12530
|
+
const excludedProperties = asStringArray(signedData?.["excludedProperties"]) ?? defaultExcludedProperties;
|
|
12531
|
+
if (!excludedProperties.includes("signatures"))
|
|
12532
|
+
excludedProperties.push("signatures");
|
|
12533
|
+
let signedBytes;
|
|
12534
|
+
try {
|
|
12535
|
+
signedBytes = canonicalJSONBytes(
|
|
12536
|
+
Object.fromEntries(
|
|
12537
|
+
Object.entries(Document).filter(([property]) => !excludedProperties.includes(property))
|
|
12538
|
+
)
|
|
12539
|
+
);
|
|
12540
|
+
} catch (exception) {
|
|
12541
|
+
return described(
|
|
12542
|
+
"malformed",
|
|
12543
|
+
"The signed properties could not be canonicalized: " + (exception instanceof Error ? exception.message : String(exception))
|
|
12544
|
+
);
|
|
12545
|
+
}
|
|
12546
|
+
const suiteAlgorithm = nobleSignatureAlgorithm(algorithm);
|
|
12547
|
+
if (suiteAlgorithm === null)
|
|
12548
|
+
return described("unsupportedAlgorithm", "Unsupported signature algorithm: " + algorithm);
|
|
12549
|
+
const wantedKeyId = keyId.toUpperCase();
|
|
12550
|
+
const publicKey = PublicKeys.find((candidate) => computeKeyId(candidate, KeyIdGeneration)?.toUpperCase() === wantedKeyId);
|
|
12551
|
+
if (publicKey === void 0)
|
|
12552
|
+
return described("unknownPublicKey", "No public key of this document has the key id " + keyId + ".");
|
|
12553
|
+
try {
|
|
12554
|
+
const suite = getSignatureSuite(suiteAlgorithm);
|
|
12555
|
+
const publicKeyBytes_ = publicKeyBytes(publicKey, "raw");
|
|
12556
|
+
const signatureBytes = decodeText(value, encodings[encodings.length - 1]);
|
|
12557
|
+
if (!suite.isValidPublicKey(publicKeyBytes_))
|
|
12558
|
+
return described("malformed", "The public key is not valid for " + algorithm + ".");
|
|
12559
|
+
return described(suite.verify(signedBytes, signatureBytes, publicKeyBytes_) ? "validSignature" : "invalidSignature");
|
|
12560
|
+
} catch (exception) {
|
|
12561
|
+
return described(
|
|
12562
|
+
"malformed",
|
|
12563
|
+
exception instanceof Error ? exception.message : String(exception)
|
|
12564
|
+
);
|
|
12565
|
+
}
|
|
12566
|
+
}
|
|
12567
|
+
function verifyDocumentSignatures(Document) {
|
|
12568
|
+
const signatures = Document["signatures"];
|
|
12569
|
+
if (!Array.isArray(signatures) || signatures.length === 0)
|
|
12570
|
+
return { status: "unsigned", signatures: [], validCount: 0 };
|
|
12571
|
+
const keyIdGeneration = asStringArray(Document["keyIdGeneration"]) ?? defaultKeyIdGeneration;
|
|
12572
|
+
const publicKeys = collectDocumentPublicKeys(Document);
|
|
12573
|
+
const results = signatures.map(
|
|
12574
|
+
(signature, index) => verifyDocumentSignature(Document, signature, index, publicKeys, keyIdGeneration)
|
|
12575
|
+
);
|
|
12576
|
+
const validCount = results.filter((result) => result.status === "validSignature").length;
|
|
12577
|
+
return {
|
|
12578
|
+
status: validCount === 0 ? "noneValid" : validCount === results.length ? "allValid" : "someValid",
|
|
12579
|
+
signatures: results,
|
|
12580
|
+
validCount
|
|
12581
|
+
};
|
|
12582
|
+
}
|
|
12583
|
+
|
|
12584
|
+
// src/chargy.ts
|
|
12332
12585
|
function isPdfAttachment(value) {
|
|
12333
12586
|
if (!isMandatoryJSONObject(value))
|
|
12334
12587
|
return false;
|
|
@@ -13265,6 +13518,7 @@ var Chargy = class {
|
|
|
13265
13518
|
throw new Error("Parsed JSON content is not a JSON object!");
|
|
13266
13519
|
const JSONContext = JSONContent["@context"];
|
|
13267
13520
|
if (IsAChargeTransparencyLiveLink(JSONContent)) {
|
|
13521
|
+
this.verifyLiveLinkSignatures(JSONContent);
|
|
13268
13522
|
JSONContent.created ??= (/* @__PURE__ */ new Date()).toISOString();
|
|
13269
13523
|
processedFile.result = JSONContent;
|
|
13270
13524
|
} else if (JSONContent["format"] === "ptb")
|
|
@@ -13434,6 +13688,56 @@ var Chargy = class {
|
|
|
13434
13688
|
return IsAChargeTransparencyRecord(verifiedCTR) ? verifiedCTR : void 0;
|
|
13435
13689
|
}
|
|
13436
13690
|
//#endregion
|
|
13691
|
+
//#region (private) verifyLiveLinkSignatures(LiveLink)
|
|
13692
|
+
// A live link may be signed as a whole by the operator, which is what ties
|
|
13693
|
+
// the transport URLs and the listed public keys to whoever signed them.
|
|
13694
|
+
// Those signatures are verified whenever the document carries any.
|
|
13695
|
+
//
|
|
13696
|
+
// Nothing here rejects a document. An unsigned live link, an unknown key or
|
|
13697
|
+
// even a signature that does not match is reported as a warning and the
|
|
13698
|
+
// document stays usable: its transports still work, and its signed meter
|
|
13699
|
+
// values carry their own signatures, which are verified separately. What a
|
|
13700
|
+
// reader makes of the warning is the reader's decision.
|
|
13701
|
+
//
|
|
13702
|
+
// Must run before anything is added to the document - see the note at the
|
|
13703
|
+
// call site.
|
|
13704
|
+
verifyLiveLinkSignatures(LiveLink) {
|
|
13705
|
+
const result = verifyDocumentSignatures(LiveLink);
|
|
13706
|
+
const warnings = new Array();
|
|
13707
|
+
const warn = (messageKey, level) => {
|
|
13708
|
+
const message = this.GetMultilanguageText(messageKey);
|
|
13709
|
+
if (!warnings.some((warning) => warning.message === message))
|
|
13710
|
+
warnings.push({ level, message });
|
|
13711
|
+
};
|
|
13712
|
+
if (result.status === "unsigned")
|
|
13713
|
+
warn("DocumentSignature_Missing", "low" /* low */);
|
|
13714
|
+
for (const signature of result.signatures) {
|
|
13715
|
+
switch (signature.status) {
|
|
13716
|
+
case "validSignature":
|
|
13717
|
+
break;
|
|
13718
|
+
// A signature that demonstrably does not match its document is
|
|
13719
|
+
// the one case that says something is actually wrong.
|
|
13720
|
+
case "invalidSignature":
|
|
13721
|
+
warn("DocumentSignature_Mismatch", "high" /* high */);
|
|
13722
|
+
break;
|
|
13723
|
+
// The rest mean "cannot be judged here", which is a weaker
|
|
13724
|
+
// statement than "is wrong".
|
|
13725
|
+
case "unknownPublicKey":
|
|
13726
|
+
warn("DocumentSignature_UnknownPublicKey", "medium" /* medium */);
|
|
13727
|
+
break;
|
|
13728
|
+
case "unsupportedAlgorithm":
|
|
13729
|
+
warn("DocumentSignature_UnsupportedAlgorithm", "medium" /* medium */);
|
|
13730
|
+
break;
|
|
13731
|
+
case "malformed":
|
|
13732
|
+
warn("DocumentSignature_Malformed", "medium" /* medium */);
|
|
13733
|
+
break;
|
|
13734
|
+
}
|
|
13735
|
+
}
|
|
13736
|
+
LiveLink.signatureVerification = result;
|
|
13737
|
+
if (warnings.length > 0)
|
|
13738
|
+
LiveLink.warnings = [...LiveLink.warnings ?? [], ...warnings];
|
|
13739
|
+
}
|
|
13740
|
+
//#endregion
|
|
13437
13741
|
//#region (private) collectLiveLinkMeterValueKeys(LiveLink)
|
|
13438
13742
|
// Every public key of the document that is allowed to sign meter values.
|
|
13439
13743
|
// A charging session is regularly signed by more than one of them: the
|
|
@@ -13813,6 +14117,6 @@ buffer/index.js:
|
|
|
13813
14117
|
*)
|
|
13814
14118
|
*/
|
|
13815
14119
|
|
|
13816
|
-
export { ACrypt, Alfen, AlfenCrypt01, BSMCrypt01, CanonicalJSONError, ChargeIT, ChargePoint, ChargePointCrypt01, IChargeTransparencyLiveLink_exports as ChargeTransparencyLiveLink, ChargeTransparencyLiveLinkContext, IChargeTransparencyRecord_exports as ChargeTransparencyRecord, Chargy, chargyInterfaces_exports as ChargyInterfaces, Clone, CloneCTR, ConcatenateBuffers, CreateDiv, CreateDiv2, CreateError, CreateWarning, CryptoAlgorithms, CryptoHashAlgorithms, DayOfWeek, DisplayPrefixes, EDL40, EDL40Crypt01, EDL40ValidationError, EDL40_OBIS, EDL40_SESSION_CONTEXT, EDL40_SIGNATURE_CONTEXT, EMHCrypt01, ErrorLevel, GDFCrypt01, IECCurves, IEncoding, InformationRelevance, InformationRelevanceToString, IsAChargeTransparencyLiveLink, IsAChargeTransparencyRecord, IsAPublicKey, IsAPublicKeyLookup, IsAPublicKeySignature, IsAPublicKeyXY, IsASessionCryptoResult, IsAURL, IsNullOrEmpty, IsValidURL, JSONSignatureVerificationStatus, MENNEKES_EDL40_OBIS, MENNEKES_EDL40_XMLNS, Mennekes, MennekesCrypt01, OBIS2Hex, OBIS2MeasurementName, OBIS_RegExpr, OCMF, OCMFBonnTariffParseError, OCMFTransactionTypes, OCMF_SIGNATURE_ALGORITHMS, OCMFv1_x, OCPI, OIDInfo, PCDF, PCDFCrypt01, PCDFParseError, PCDFValidationError, PCDF_FIELD_ORDER, PCDF_PREFIX, PTB, ParseJSON_LD, PublicKeyFormats, IPublicKeyInfo_exports as PublicKeyInfo, SAFEXML, SessionVerificationResult, SetHex, SetInt8, SetText, SetText_withLength, SetTimestamp, SetTimestamp32, SetUInt32, SetUInt32_withCode, SetUInt64, SetUInt64D, SignMessage, SignatureFormats, IURL_exports as SimpleURL, TimeStatusTypes, URLContext, UTC2human, VerificationResult, VerifyJSONMessageSignatures, WarningLevel, WhenNullOrEmpty, XMLContainer, asJSONArray, asJSONObject, asNumber, asString, base64ToBytes, buf2hex, buildEDL40Signature, buildIsaSignature, buildMennekesSignatureData, bytesToBase64, bytesToHex, canParseEDL40, canonicalJSONBytes, canonicalJSONStringify, cleanHex, closeFullscreen, createCompatibleCurve, createHexString, createLegacyP192Curve, dateToMennekesLocalEpochSeconds, decodeSmlMessages, extractMennekesChargingProcesses, findEntryByObis, findGetListRes, firstKey, firstValue, generateSignatureKeyPair, getArrayElement, getArrayLikeElement, getDirectChildByLocalName, getDirectChildrenByLocalName, getElementsByLocalName, getFirstArrayElement, getInt16Bytes, getInt32Bytes, getInt64Bytes, getInt8Bytes, getLastArrayElement, getOCMFSignatureDisplay, getSignatureSuite, getTrimmedTextContent, hashFile, hex2bin, hex32, hexToArrayBuffer, hexToBytes, intFromBytes, isEncodedValue, isGeoLocation, isI18NString, isICryptoResult, isIFileInfo, isISessionCryptoResult1, isISessionCryptoResult2, isJSONLDObject, isMandatoryArrayOfStrings, isMandatoryBoolean, isMandatoryDecimal, isMandatoryJSONArray, isMandatoryJSONObject, isMandatoryNumber, isMandatoryString, isMandatoryURL, isOIDInfo, isObject, isOptionalArrayOfStrings, isOptionalDecimal, isOptionalJSONArray, isOptionalJSONArrayError, isOptionalJSONArrayOk, isOptionalJSONObject, isOptionalNumber, isOptionalString, isOptionalStringArray, isOptionalStringOrOIDInfo, isOptionalURL, isPCDFText, isPublicKeySubject, isString, isStringArray, isStringOrOIDInfo, isStringOrStringArray, isaListNameContext, jsonPrettyPrinter, measurementName2human, meterTimeZone, normalizePCDFPublicKeyHex, normalizeXMLText, ocmfBonnTariffToChargingTariff, openFullscreen, pad, parseAndVerifyJSONSignatures, parseDescription, parseEDL40, parseHexString, parseMennekesXMLDocument, parseNumber, parseOBIS, parseOCMFBonnTariffText, parsePCDFDocument, parsePCDFPublicKey, parsePCDFSignature, parseSmlTime, parseUTC, readQRCodeTextFromImage, readQRCodeTextFromImageData, readTLV, secp224k1, setUILocale, sha256, sha256____, sha384, sha384____, sha512, sha512____, signJSONMessage, signMessage, stripPCDFControlCharacters, stripTransport, time2human, timeZoneOffsetMinutes, toArrayBuffer, toSessionVerificationResults, toUint8Array, transformEDL40Status, tryParseOCMFBonnTariffText, unquotePCDFText, validatePCDFFields, verifyEDL40Document, verifyJSONMessageSignatureResults, verifyJSONMessageSignatures, verifyJSONSignature, verifyJSONSignatureResult, verifyPCDFDocument };
|
|
14120
|
+
export { ACrypt, Alfen, AlfenCrypt01, BSMCrypt01, CanonicalJSONError, ChargeIT, ChargePoint, ChargePointCrypt01, IChargeTransparencyLiveLink_exports as ChargeTransparencyLiveLink, ChargeTransparencyLiveLinkContext, IChargeTransparencyRecord_exports as ChargeTransparencyRecord, Chargy, chargyInterfaces_exports as ChargyInterfaces, Clone, CloneCTR, ConcatenateBuffers, CreateDiv, CreateDiv2, CreateError, CreateWarning, CryptoAlgorithms, CryptoHashAlgorithms, DayOfWeek, DisplayPrefixes, EDL40, EDL40Crypt01, EDL40ValidationError, EDL40_OBIS, EDL40_SESSION_CONTEXT, EDL40_SIGNATURE_CONTEXT, EMHCrypt01, ErrorLevel, GDFCrypt01, IECCurves, IEncoding, InformationRelevance, InformationRelevanceToString, IsAChargeTransparencyLiveLink, IsAChargeTransparencyRecord, IsAPublicKey, IsAPublicKeyLookup, IsAPublicKeySignature, IsAPublicKeyXY, IsASessionCryptoResult, IsAURL, IsNullOrEmpty, IsValidURL, JSONSignatureVerificationStatus, MENNEKES_EDL40_OBIS, MENNEKES_EDL40_XMLNS, Mennekes, MennekesCrypt01, OBIS2Hex, OBIS2MeasurementName, OBIS_RegExpr, OCMF, OCMFBonnTariffParseError, OCMFTransactionTypes, OCMF_SIGNATURE_ALGORITHMS, OCMFv1_x, OCPI, OIDInfo, PCDF, PCDFCrypt01, PCDFParseError, PCDFValidationError, PCDF_FIELD_ORDER, PCDF_PREFIX, PTB, ParseJSON_LD, PublicKeyFormats, IPublicKeyInfo_exports as PublicKeyInfo, SAFEXML, SessionVerificationResult, SetHex, SetInt8, SetText, SetText_withLength, SetTimestamp, SetTimestamp32, SetUInt32, SetUInt32_withCode, SetUInt64, SetUInt64D, SignMessage, SignatureFormats, IURL_exports as SimpleURL, TimeStatusTypes, URLContext, UTC2human, VerificationResult, VerifyJSONMessageSignatures, WarningLevel, WhenNullOrEmpty, XMLContainer, asJSONArray, asJSONObject, asNumber, asString, base64ToBytes, buf2hex, buildEDL40Signature, buildIsaSignature, buildMennekesSignatureData, bytesToBase64, bytesToHex, canParseEDL40, canonicalJSONBytes, canonicalJSONStringify, cleanHex, closeFullscreen, collectDocumentPublicKeys, createCompatibleCurve, createHexString, createLegacyP192Curve, dateToMennekesLocalEpochSeconds, decodeSmlMessages, extractMennekesChargingProcesses, findEntryByObis, findGetListRes, firstKey, firstValue, generateSignatureKeyPair, getArrayElement, getArrayLikeElement, getDirectChildByLocalName, getDirectChildrenByLocalName, getElementsByLocalName, getFirstArrayElement, getInt16Bytes, getInt32Bytes, getInt64Bytes, getInt8Bytes, getLastArrayElement, getOCMFSignatureDisplay, getSignatureSuite, getTrimmedTextContent, hashFile, hex2bin, hex32, hexToArrayBuffer, hexToBytes, intFromBytes, isEncodedValue, isGeoLocation, isI18NString, isICryptoResult, isIFileInfo, isISessionCryptoResult1, isISessionCryptoResult2, isJSONLDObject, isMandatoryArrayOfStrings, isMandatoryBoolean, isMandatoryDecimal, isMandatoryJSONArray, isMandatoryJSONObject, isMandatoryNumber, isMandatoryString, isMandatoryURL, isOIDInfo, isObject, isOptionalArrayOfStrings, isOptionalDecimal, isOptionalJSONArray, isOptionalJSONArrayError, isOptionalJSONArrayOk, isOptionalJSONObject, isOptionalNumber, isOptionalString, isOptionalStringArray, isOptionalStringOrOIDInfo, isOptionalURL, isPCDFText, isPublicKeySubject, isString, isStringArray, isStringOrOIDInfo, isStringOrStringArray, isaListNameContext, jsonPrettyPrinter, measurementName2human, meterTimeZone, normalizePCDFPublicKeyHex, normalizeXMLText, ocmfBonnTariffToChargingTariff, openFullscreen, pad, parseAndVerifyJSONSignatures, parseDescription, parseEDL40, parseHexString, parseMennekesXMLDocument, parseNumber, parseOBIS, parseOCMFBonnTariffText, parsePCDFDocument, parsePCDFPublicKey, parsePCDFSignature, parseSmlTime, parseUTC, readQRCodeTextFromImage, readQRCodeTextFromImageData, readTLV, secp224k1, setUILocale, sha256, sha256____, sha384, sha384____, sha512, sha512____, signJSONMessage, signMessage, stripPCDFControlCharacters, stripTransport, time2human, timeZoneOffsetMinutes, toArrayBuffer, toSessionVerificationResults, toUint8Array, transformEDL40Status, tryParseOCMFBonnTariffText, unquotePCDFText, validatePCDFFields, verifyDocumentSignatures, verifyEDL40Document, verifyJSONMessageSignatureResults, verifyJSONMessageSignatures, verifyJSONSignature, verifyJSONSignatureResult, verifyPCDFDocument };
|
|
13817
14121
|
//# sourceMappingURL=index.js.map
|
|
13818
14122
|
//# sourceMappingURL=index.js.map
|