edockit 0.1.1-beta.1 → 0.1.2
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/README.md +1 -1
- package/dist/core/parser/types.d.ts +1 -0
- package/dist/core/rsa-modulus-padding-fix.d.ts +15 -0
- package/dist/core/verification.d.ts +8 -0
- package/dist/index.cjs.js +343 -12
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +343 -12
- package/dist/index.esm.js.map +1 -1
- package/dist/index.umd.js +12 -9
- package/dist/index.umd.js.map +1 -1
- package/package.json +11 -2
package/dist/index.esm.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* MIT License
|
|
3
|
+
* Copyright (c) 2025 Edgars Jēkabsons, ZenomyTech SIA
|
|
4
|
+
*/
|
|
1
5
|
import { unzipSync } from 'fflate';
|
|
2
6
|
import { X509Certificate } from '@peculiar/x509';
|
|
3
7
|
|
|
@@ -1242,6 +1246,7 @@ function parseSignatureElement(signatureElement, xmlDoc) {
|
|
|
1242
1246
|
// Get references and checksums
|
|
1243
1247
|
const references = [];
|
|
1244
1248
|
const signedChecksums = {};
|
|
1249
|
+
const digestAlgorithms = {};
|
|
1245
1250
|
const referenceElements = querySelectorAll(signedInfo, "ds\\:Reference, Reference");
|
|
1246
1251
|
for (const reference of referenceElements) {
|
|
1247
1252
|
const uri = reference.getAttribute("URI") || "";
|
|
@@ -1261,6 +1266,14 @@ function parseSignatureElement(signatureElement, xmlDoc) {
|
|
|
1261
1266
|
// Clean up URI
|
|
1262
1267
|
const cleanUri = decodedUri.startsWith("./") ? decodedUri.substring(2) : decodedUri;
|
|
1263
1268
|
references.push(cleanUri);
|
|
1269
|
+
// Find DigestMethod algorithm
|
|
1270
|
+
const digestMethodEl = querySelector(reference, "ds\\:DigestMethod, DigestMethod");
|
|
1271
|
+
if (digestMethodEl) {
|
|
1272
|
+
const algo = digestMethodEl.getAttribute("Algorithm");
|
|
1273
|
+
if (algo) {
|
|
1274
|
+
digestAlgorithms[cleanUri] = algo;
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1264
1277
|
// Find DigestValue
|
|
1265
1278
|
const digestValueEl = querySelector(reference, "ds\\:DigestValue, DigestValue");
|
|
1266
1279
|
if (digestValueEl && digestValueEl.textContent) {
|
|
@@ -1275,6 +1288,7 @@ function parseSignatureElement(signatureElement, xmlDoc) {
|
|
|
1275
1288
|
publicKey,
|
|
1276
1289
|
signerInfo,
|
|
1277
1290
|
signedChecksums,
|
|
1291
|
+
digestAlgorithms,
|
|
1278
1292
|
references,
|
|
1279
1293
|
algorithm: signatureAlgorithm,
|
|
1280
1294
|
signatureValue,
|
|
@@ -1403,6 +1417,206 @@ function parseEdoc(edocBuffer) {
|
|
|
1403
1417
|
}
|
|
1404
1418
|
}
|
|
1405
1419
|
|
|
1420
|
+
/**
|
|
1421
|
+
* Fixes ASN.1 DER encoding of RSA keys to be compatible with browser Web Crypto
|
|
1422
|
+
*
|
|
1423
|
+
* This specifically targets the issue with modulus padding in SPKI format RSA keys.
|
|
1424
|
+
* In DER encoding, when the high bit of an INTEGER is set, a 0x00 byte must be
|
|
1425
|
+
* prepended to distinguish it from a negative number. Some libraries omit this padding,
|
|
1426
|
+
* which works in Node.js but causes browsers to reject the key.
|
|
1427
|
+
*/
|
|
1428
|
+
/**
|
|
1429
|
+
* Fixes ASN.1 DER encoding of RSA modulus to ensure proper padding
|
|
1430
|
+
*
|
|
1431
|
+
* @param publicKeyData The original public key data as ArrayBuffer
|
|
1432
|
+
* @returns Fixed key with proper modulus padding
|
|
1433
|
+
*/
|
|
1434
|
+
function fixRSAModulusPadding(publicKeyData) {
|
|
1435
|
+
const log = () => { };
|
|
1436
|
+
const keyBytes = new Uint8Array(publicKeyData);
|
|
1437
|
+
// Check if we have a valid SPKI format RSA key
|
|
1438
|
+
// It should start with SEQUENCE tag (0x30)
|
|
1439
|
+
if (keyBytes[0] !== 0x30) {
|
|
1440
|
+
return publicKeyData;
|
|
1441
|
+
}
|
|
1442
|
+
// Look for RSA OID to confirm it's an RSA key
|
|
1443
|
+
const RSA_OID = [0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01];
|
|
1444
|
+
let oidPosition = -1;
|
|
1445
|
+
for (let i = 0; i <= keyBytes.length - RSA_OID.length; i++) {
|
|
1446
|
+
let match = true;
|
|
1447
|
+
for (let j = 0; j < RSA_OID.length; j++) {
|
|
1448
|
+
if (keyBytes[i + j] !== RSA_OID[j]) {
|
|
1449
|
+
match = false;
|
|
1450
|
+
break;
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
if (match) {
|
|
1454
|
+
oidPosition = i;
|
|
1455
|
+
break;
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
if (oidPosition === -1) {
|
|
1459
|
+
return publicKeyData;
|
|
1460
|
+
}
|
|
1461
|
+
// Find the BitString that contains the key
|
|
1462
|
+
let bitStringPosition = -1;
|
|
1463
|
+
for (let i = oidPosition + RSA_OID.length; i < keyBytes.length; i++) {
|
|
1464
|
+
if (keyBytes[i] === 0x03) {
|
|
1465
|
+
// BIT STRING tag
|
|
1466
|
+
bitStringPosition = i;
|
|
1467
|
+
break;
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
if (bitStringPosition === -1) {
|
|
1471
|
+
return publicKeyData;
|
|
1472
|
+
}
|
|
1473
|
+
// Skip BIT STRING tag and length bytes to find unused bits byte
|
|
1474
|
+
let bitStringLengthBytes = 0;
|
|
1475
|
+
if ((keyBytes[bitStringPosition + 1] & 0x80) === 0) {
|
|
1476
|
+
// Short form length
|
|
1477
|
+
bitStringLengthBytes = 1;
|
|
1478
|
+
}
|
|
1479
|
+
else {
|
|
1480
|
+
// Long form length
|
|
1481
|
+
bitStringLengthBytes = 1 + (keyBytes[bitStringPosition + 1] & 0x7f);
|
|
1482
|
+
}
|
|
1483
|
+
// The unused bits byte follows the length bytes
|
|
1484
|
+
const unusedBitsPosition = bitStringPosition + 1 + bitStringLengthBytes;
|
|
1485
|
+
if (unusedBitsPosition >= keyBytes.length) {
|
|
1486
|
+
return publicKeyData;
|
|
1487
|
+
}
|
|
1488
|
+
// The inner SEQUENCE (RSA key) should follow the unused bits byte
|
|
1489
|
+
const innerSequencePosition = unusedBitsPosition + 1;
|
|
1490
|
+
if (innerSequencePosition >= keyBytes.length || keyBytes[innerSequencePosition] !== 0x30) {
|
|
1491
|
+
return publicKeyData;
|
|
1492
|
+
}
|
|
1493
|
+
// Skip the inner SEQUENCE tag and length bytes to find the modulus
|
|
1494
|
+
let innerSequenceLengthBytes = 0;
|
|
1495
|
+
if ((keyBytes[innerSequencePosition + 1] & 0x80) === 0) {
|
|
1496
|
+
// Short form length
|
|
1497
|
+
innerSequenceLengthBytes = 1;
|
|
1498
|
+
}
|
|
1499
|
+
else {
|
|
1500
|
+
// Long form length
|
|
1501
|
+
innerSequenceLengthBytes = 1 + (keyBytes[innerSequencePosition + 1] & 0x7f);
|
|
1502
|
+
}
|
|
1503
|
+
// The modulus should be an INTEGER (tag 0x02) after the inner SEQUENCE
|
|
1504
|
+
const modulusPosition = innerSequencePosition + 1 + innerSequenceLengthBytes;
|
|
1505
|
+
if (modulusPosition >= keyBytes.length || keyBytes[modulusPosition] !== 0x02) {
|
|
1506
|
+
return publicKeyData;
|
|
1507
|
+
}
|
|
1508
|
+
// Skip the INTEGER tag and parse its length to find the modulus value
|
|
1509
|
+
let modulusLengthBytes = 0;
|
|
1510
|
+
let modulusLength = 0;
|
|
1511
|
+
if ((keyBytes[modulusPosition + 1] & 0x80) === 0) {
|
|
1512
|
+
// Short form length
|
|
1513
|
+
modulusLength = keyBytes[modulusPosition + 1];
|
|
1514
|
+
modulusLengthBytes = 1;
|
|
1515
|
+
}
|
|
1516
|
+
else {
|
|
1517
|
+
// Long form length
|
|
1518
|
+
const numLengthBytes = keyBytes[modulusPosition + 1] & 0x7f;
|
|
1519
|
+
modulusLengthBytes = 1 + numLengthBytes;
|
|
1520
|
+
// Calculate multi-byte length
|
|
1521
|
+
modulusLength = 0;
|
|
1522
|
+
for (let i = 0; i < numLengthBytes; i++) {
|
|
1523
|
+
modulusLength = (modulusLength << 8) | keyBytes[modulusPosition + 2 + i];
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
// The first byte of the modulus value
|
|
1527
|
+
const modulusValuePosition = modulusPosition + 1 + modulusLengthBytes;
|
|
1528
|
+
if (modulusValuePosition >= keyBytes.length) {
|
|
1529
|
+
return publicKeyData;
|
|
1530
|
+
}
|
|
1531
|
+
// Check if the high bit is set and padding is needed
|
|
1532
|
+
if ((keyBytes[modulusValuePosition] & 0x80) !== 0) {
|
|
1533
|
+
// Create a new key buffer with room for the padding byte
|
|
1534
|
+
const fixedKey = new Uint8Array(keyBytes.length + 1);
|
|
1535
|
+
// Copy bytes up to the modulus value
|
|
1536
|
+
fixedKey.set(keyBytes.slice(0, modulusValuePosition));
|
|
1537
|
+
// Add the padding byte
|
|
1538
|
+
fixedKey[modulusValuePosition] = 0x00;
|
|
1539
|
+
// Copy the rest of the original key after the padding
|
|
1540
|
+
fixedKey.set(keyBytes.slice(modulusValuePosition), modulusValuePosition + 1);
|
|
1541
|
+
// Now fix all the length fields that need to be incremented
|
|
1542
|
+
// 1. Fix modulus length field
|
|
1543
|
+
if ((keyBytes[modulusPosition + 1] & 0x80) === 0) {
|
|
1544
|
+
// Short form
|
|
1545
|
+
fixedKey[modulusPosition + 1] = keyBytes[modulusPosition + 1] + 1;
|
|
1546
|
+
}
|
|
1547
|
+
else {
|
|
1548
|
+
// Long form
|
|
1549
|
+
const numLengthBytes = keyBytes[modulusPosition + 1] & 0x7f;
|
|
1550
|
+
let lengthValue = 0;
|
|
1551
|
+
for (let i = 0; i < numLengthBytes; i++) {
|
|
1552
|
+
lengthValue = (lengthValue << 8) | keyBytes[modulusPosition + 2 + i];
|
|
1553
|
+
}
|
|
1554
|
+
lengthValue += 1;
|
|
1555
|
+
for (let i = numLengthBytes - 1; i >= 0; i--) {
|
|
1556
|
+
fixedKey[modulusPosition + 2 + i] = lengthValue & 0xff;
|
|
1557
|
+
lengthValue >>= 8;
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
// 2. Fix inner SEQUENCE length field
|
|
1561
|
+
if ((keyBytes[innerSequencePosition + 1] & 0x80) === 0) {
|
|
1562
|
+
// Short form
|
|
1563
|
+
fixedKey[innerSequencePosition + 1] = keyBytes[innerSequencePosition + 1] + 1;
|
|
1564
|
+
}
|
|
1565
|
+
else {
|
|
1566
|
+
// Long form
|
|
1567
|
+
const numLengthBytes = keyBytes[innerSequencePosition + 1] & 0x7f;
|
|
1568
|
+
let lengthValue = 0;
|
|
1569
|
+
for (let i = 0; i < numLengthBytes; i++) {
|
|
1570
|
+
lengthValue = (lengthValue << 8) | keyBytes[innerSequencePosition + 2 + i];
|
|
1571
|
+
}
|
|
1572
|
+
lengthValue += 1;
|
|
1573
|
+
for (let i = numLengthBytes - 1; i >= 0; i--) {
|
|
1574
|
+
fixedKey[innerSequencePosition + 2 + i] = lengthValue & 0xff;
|
|
1575
|
+
lengthValue >>= 8;
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
// 3. Fix BIT STRING length field
|
|
1579
|
+
if ((keyBytes[bitStringPosition + 1] & 0x80) === 0) {
|
|
1580
|
+
// Short form
|
|
1581
|
+
fixedKey[bitStringPosition + 1] = keyBytes[bitStringPosition + 1] + 1;
|
|
1582
|
+
}
|
|
1583
|
+
else {
|
|
1584
|
+
// Long form
|
|
1585
|
+
const numLengthBytes = keyBytes[bitStringPosition + 1] & 0x7f;
|
|
1586
|
+
let lengthValue = 0;
|
|
1587
|
+
for (let i = 0; i < numLengthBytes; i++) {
|
|
1588
|
+
lengthValue = (lengthValue << 8) | keyBytes[bitStringPosition + 2 + i];
|
|
1589
|
+
}
|
|
1590
|
+
lengthValue += 1;
|
|
1591
|
+
for (let i = numLengthBytes - 1; i >= 0; i--) {
|
|
1592
|
+
fixedKey[bitStringPosition + 2 + i] = lengthValue & 0xff;
|
|
1593
|
+
lengthValue >>= 8;
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
// 4. Fix outer SEQUENCE length field
|
|
1597
|
+
if ((keyBytes[1] & 0x80) === 0) {
|
|
1598
|
+
// Short form
|
|
1599
|
+
fixedKey[1] = keyBytes[1] + 1;
|
|
1600
|
+
}
|
|
1601
|
+
else {
|
|
1602
|
+
// Long form
|
|
1603
|
+
const numLengthBytes = keyBytes[1] & 0x7f;
|
|
1604
|
+
let lengthValue = 0;
|
|
1605
|
+
for (let i = 0; i < numLengthBytes; i++) {
|
|
1606
|
+
lengthValue = (lengthValue << 8) | keyBytes[1 + 1 + i];
|
|
1607
|
+
}
|
|
1608
|
+
lengthValue += 1;
|
|
1609
|
+
for (let i = numLengthBytes - 1; i >= 0; i--) {
|
|
1610
|
+
fixedKey[1 + 1 + i] = lengthValue & 0xff;
|
|
1611
|
+
lengthValue >>= 8;
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
log("Fixed key length: " + fixedKey.length);
|
|
1615
|
+
return fixedKey.buffer;
|
|
1616
|
+
}
|
|
1617
|
+
return publicKeyData;
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1406
1620
|
/**
|
|
1407
1621
|
* Detects if code is running in a browser environment
|
|
1408
1622
|
* @returns true if in browser, false otherwise
|
|
@@ -1489,6 +1703,23 @@ function nodeDigest(fileContent, hashAlgo) {
|
|
|
1489
1703
|
}
|
|
1490
1704
|
});
|
|
1491
1705
|
}
|
|
1706
|
+
/**
|
|
1707
|
+
* Parse digest algorithm URI to normalized algorithm name
|
|
1708
|
+
* @param algorithmUri The algorithm URI (e.g., http://www.w3.org/2001/04/xmlenc#sha256)
|
|
1709
|
+
* @returns Normalized algorithm name (e.g., SHA-256)
|
|
1710
|
+
*/
|
|
1711
|
+
function parseDigestAlgorithmUri(algorithmUri) {
|
|
1712
|
+
const uri = algorithmUri.toLowerCase();
|
|
1713
|
+
if (uri.includes("sha512"))
|
|
1714
|
+
return "SHA-512";
|
|
1715
|
+
if (uri.includes("sha384"))
|
|
1716
|
+
return "SHA-384";
|
|
1717
|
+
if (uri.includes("sha256"))
|
|
1718
|
+
return "SHA-256";
|
|
1719
|
+
if (uri.includes("sha1"))
|
|
1720
|
+
return "SHA-1";
|
|
1721
|
+
return "SHA-256"; // Default fallback
|
|
1722
|
+
}
|
|
1492
1723
|
/**
|
|
1493
1724
|
* Verify checksums of files against signature
|
|
1494
1725
|
* @param signature The signature information
|
|
@@ -1498,20 +1729,24 @@ function nodeDigest(fileContent, hashAlgo) {
|
|
|
1498
1729
|
async function verifyChecksums(signature, files) {
|
|
1499
1730
|
const results = {};
|
|
1500
1731
|
let allValid = true;
|
|
1501
|
-
//
|
|
1502
|
-
let
|
|
1732
|
+
// Default digest algorithm from signature algorithm (fallback if per-file not available)
|
|
1733
|
+
let defaultDigestAlgorithm = "SHA-256";
|
|
1503
1734
|
if (signature.algorithm) {
|
|
1504
1735
|
if (signature.algorithm.includes("sha1")) {
|
|
1505
|
-
|
|
1736
|
+
defaultDigestAlgorithm = "SHA-1";
|
|
1506
1737
|
}
|
|
1507
1738
|
else if (signature.algorithm.includes("sha384")) {
|
|
1508
|
-
|
|
1739
|
+
defaultDigestAlgorithm = "SHA-384";
|
|
1509
1740
|
}
|
|
1510
1741
|
else if (signature.algorithm.includes("sha512")) {
|
|
1511
|
-
|
|
1742
|
+
defaultDigestAlgorithm = "SHA-512";
|
|
1512
1743
|
}
|
|
1513
1744
|
}
|
|
1514
1745
|
const checksumPromises = Object.entries(signature.signedChecksums).map(async ([filename, expectedChecksum]) => {
|
|
1746
|
+
// Get the per-file digest algorithm, or fall back to default
|
|
1747
|
+
const digestAlgorithm = signature.digestAlgorithms?.[filename]
|
|
1748
|
+
? parseDigestAlgorithmUri(signature.digestAlgorithms[filename])
|
|
1749
|
+
: defaultDigestAlgorithm;
|
|
1515
1750
|
// Check if file exists in the container
|
|
1516
1751
|
const fileContent = files.get(filename);
|
|
1517
1752
|
if (!fileContent) {
|
|
@@ -1671,12 +1906,50 @@ async function verifySignedInfo(signatureXml, signatureValue, publicKeyData, alg
|
|
|
1671
1906
|
let publicKey;
|
|
1672
1907
|
try {
|
|
1673
1908
|
const subtle = getCryptoSubtle();
|
|
1909
|
+
if (isBrowser() && algorithm.name === "RSASSA-PKCS1-v1_5") {
|
|
1910
|
+
// console.log("Browser environment detected, applying RSA key fix");
|
|
1911
|
+
publicKeyData = fixRSAModulusPadding(publicKeyData);
|
|
1912
|
+
}
|
|
1674
1913
|
publicKey = await subtle.importKey("spki", publicKeyData, algorithm, false, ["verify"]);
|
|
1675
1914
|
}
|
|
1676
|
-
catch (
|
|
1915
|
+
catch (unknownError) {
|
|
1916
|
+
// First cast to Error type if applicable
|
|
1917
|
+
const error = unknownError instanceof Error ? unknownError : new Error(String(unknownError));
|
|
1918
|
+
// Determine detailed error reason
|
|
1919
|
+
let detailedReason = "Unknown reason";
|
|
1920
|
+
let errorCategory = "KEY_IMPORT_ERROR";
|
|
1921
|
+
// Categorize the error
|
|
1922
|
+
if (error.name === "DataError") {
|
|
1923
|
+
detailedReason = "Key data format is invalid or incompatible";
|
|
1924
|
+
errorCategory = "INVALID_KEY_FORMAT";
|
|
1925
|
+
}
|
|
1926
|
+
else if (error.name === "NotSupportedError") {
|
|
1927
|
+
detailedReason = "Algorithm or parameters not supported";
|
|
1928
|
+
errorCategory = "UNSUPPORTED_ALGORITHM";
|
|
1929
|
+
}
|
|
1930
|
+
else if (error.message.includes("namedCurve")) {
|
|
1931
|
+
detailedReason = "Missing or invalid namedCurve parameter";
|
|
1932
|
+
errorCategory = "INVALID_CURVE";
|
|
1933
|
+
}
|
|
1934
|
+
else if (error.message.includes("hash")) {
|
|
1935
|
+
detailedReason = "Incompatible or unsupported hash algorithm";
|
|
1936
|
+
errorCategory = "INVALID_HASH";
|
|
1937
|
+
}
|
|
1938
|
+
// Add ECDSA-specific diagnostics
|
|
1939
|
+
if (algorithm.name === "ECDSA") {
|
|
1940
|
+
const keyLength = publicKeyData.byteLength;
|
|
1941
|
+
detailedReason += ` (Key length: ${keyLength})`;
|
|
1942
|
+
}
|
|
1677
1943
|
return {
|
|
1678
1944
|
isValid: false,
|
|
1679
|
-
reason: `Failed to import public key: ${
|
|
1945
|
+
reason: `Failed to import public key: ${detailedReason}`,
|
|
1946
|
+
errorDetails: {
|
|
1947
|
+
category: errorCategory,
|
|
1948
|
+
originalMessage: error.message,
|
|
1949
|
+
algorithm: { ...algorithm },
|
|
1950
|
+
environment: isBrowser() ? "browser" : "node",
|
|
1951
|
+
keyLength: publicKeyData.byteLength,
|
|
1952
|
+
},
|
|
1680
1953
|
};
|
|
1681
1954
|
}
|
|
1682
1955
|
// Verify the signature
|
|
@@ -1714,10 +1987,23 @@ async function verifySignature(signatureInfo, files, options = {}) {
|
|
|
1714
1987
|
const errors = [];
|
|
1715
1988
|
// Verify certificate
|
|
1716
1989
|
const certResult = await verifyCertificate(signatureInfo.certificatePEM, options.verifyTime || signatureInfo.signingTime);
|
|
1990
|
+
// If certificate validation failed, add detailed error
|
|
1991
|
+
if (!certResult.isValid) {
|
|
1992
|
+
const certErrorMsg = `Certificate validation error: ${certResult.reason || "Unknown reason"}`;
|
|
1993
|
+
errors.push(certErrorMsg);
|
|
1994
|
+
}
|
|
1717
1995
|
// Verify checksums
|
|
1718
1996
|
const checksumResult = options.verifyChecksums !== false
|
|
1719
1997
|
? await verifyChecksums(signatureInfo, files)
|
|
1720
1998
|
: { isValid: true, details: {} };
|
|
1999
|
+
// If checksum validation failed, add detailed error
|
|
2000
|
+
if (!checksumResult.isValid) {
|
|
2001
|
+
const failedChecksums = Object.entries(checksumResult.details)
|
|
2002
|
+
.filter(([_, details]) => !details.matches)
|
|
2003
|
+
.map(([filename]) => filename)
|
|
2004
|
+
.join(", ");
|
|
2005
|
+
errors.push(`Checksum validation failed for files: ${failedChecksums}`);
|
|
2006
|
+
}
|
|
1721
2007
|
// Verify XML signature if we have the necessary components
|
|
1722
2008
|
let signatureResult = { isValid: true };
|
|
1723
2009
|
if (options.verifySignatures !== false &&
|
|
@@ -1730,19 +2016,64 @@ async function verifySignature(signatureInfo, files, options = {}) {
|
|
|
1730
2016
|
name: "RSASSA-PKCS1-v1_5",
|
|
1731
2017
|
hash: "SHA-256",
|
|
1732
2018
|
};
|
|
1733
|
-
if (algorithm.includes("ecdsa
|
|
2019
|
+
if (algorithm.includes("ecdsa") && signatureInfo.publicKey.namedCurve) {
|
|
2020
|
+
keyAlgorithm.namedCurve = signatureInfo.publicKey.namedCurve;
|
|
1734
2021
|
keyAlgorithm.name = "ECDSA";
|
|
2022
|
+
}
|
|
2023
|
+
if (algorithm.includes("ecdsa-sha256")) {
|
|
1735
2024
|
keyAlgorithm.hash = "SHA-256";
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
2025
|
+
}
|
|
2026
|
+
else if (algorithm.includes("ecdsa-sha384")) {
|
|
2027
|
+
keyAlgorithm.hash = "SHA-384";
|
|
2028
|
+
}
|
|
2029
|
+
else if (algorithm.includes("ecdsa-sha512")) {
|
|
2030
|
+
keyAlgorithm.hash = "SHA-512";
|
|
1739
2031
|
}
|
|
1740
2032
|
else if (algorithm.includes("rsa-sha1")) {
|
|
1741
2033
|
keyAlgorithm.hash = "SHA-1";
|
|
1742
2034
|
}
|
|
2035
|
+
else if (algorithm.includes("rsa-pss")) {
|
|
2036
|
+
keyAlgorithm.name = "RSA-PSS";
|
|
2037
|
+
keyAlgorithm.saltLength = 32; // Default salt length (adjust based on hash size)
|
|
2038
|
+
if (algorithm.includes("sha384")) {
|
|
2039
|
+
keyAlgorithm.hash = "SHA-384";
|
|
2040
|
+
keyAlgorithm.saltLength = 48;
|
|
2041
|
+
}
|
|
2042
|
+
else if (algorithm.includes("sha512")) {
|
|
2043
|
+
keyAlgorithm.hash = "SHA-512";
|
|
2044
|
+
keyAlgorithm.saltLength = 64;
|
|
2045
|
+
}
|
|
2046
|
+
else {
|
|
2047
|
+
keyAlgorithm.hash = "SHA-256"; // Default
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
else if (algorithm.includes("rsa-sha384")) {
|
|
2051
|
+
keyAlgorithm.hash = "SHA-384";
|
|
2052
|
+
}
|
|
2053
|
+
else if (algorithm.includes("rsa-sha512")) {
|
|
2054
|
+
keyAlgorithm.hash = "SHA-512";
|
|
2055
|
+
}
|
|
1743
2056
|
signatureResult = await verifySignedInfo(signatureInfo.rawXml, signatureInfo.signatureValue, signatureInfo.publicKey.rawData, keyAlgorithm, signatureInfo.canonicalizationMethod);
|
|
2057
|
+
// If signature validation failed, add detailed error
|
|
1744
2058
|
if (!signatureResult.isValid) {
|
|
1745
|
-
|
|
2059
|
+
// Format a detailed error message with the error details
|
|
2060
|
+
let detailedErrorMessage = signatureResult.reason || "XML signature verification failed";
|
|
2061
|
+
// Add error details if available
|
|
2062
|
+
if (signatureResult.errorDetails) {
|
|
2063
|
+
const details = signatureResult.errorDetails;
|
|
2064
|
+
detailedErrorMessage += ` [Category: ${details.category}, Environment: ${details.environment}`;
|
|
2065
|
+
if (details.algorithm) {
|
|
2066
|
+
detailedErrorMessage += `, Algorithm: ${details.algorithm.name}`;
|
|
2067
|
+
if (details.algorithm.namedCurve) {
|
|
2068
|
+
detailedErrorMessage += `, Curve: ${details.algorithm.namedCurve}`;
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
if (details.keyLength) {
|
|
2072
|
+
detailedErrorMessage += `, Key length: ${details.keyLength} bytes`;
|
|
2073
|
+
}
|
|
2074
|
+
detailedErrorMessage += `]`;
|
|
2075
|
+
}
|
|
2076
|
+
errors.push(detailedErrorMessage);
|
|
1746
2077
|
}
|
|
1747
2078
|
}
|
|
1748
2079
|
else if (options.verifySignatures !== false) {
|