@distrohelena/canton-typescript-sdk 0.1.2 → 0.1.3
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.
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { ExternalPartyCryptoKeyFormat } from "../core/types/external-party/external-party-crypto-key-format.js";
|
|
1
2
|
export declare class CantonHashingClient {
|
|
2
3
|
/** Computes a Canton multihash for the provided content and hash purpose. */
|
|
3
4
|
computeHash(content: Uint8Array, purpose: number): Uint8Array;
|
|
4
5
|
/** Computes a Canton multihash as lowercase hexadecimal for the provided content and hash purpose. */
|
|
5
6
|
computeHashHex(content: Uint8Array, purpose: number): string;
|
|
6
7
|
/** Computes the canonical Canton public-key fingerprint from serialized public key bytes. */
|
|
7
|
-
computePublicKeyFingerprint(publicKey: Uint8Array): string;
|
|
8
|
+
computePublicKeyFingerprint(publicKey: Uint8Array, format?: ExternalPartyCryptoKeyFormat): string;
|
|
8
9
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { computeCantonHash, computeCantonHashHex, } from "../core/hashing/canton-hash.js";
|
|
2
|
-
import { CantonHashPurpose } from "../core/types/canton-hash-purpose.js";
|
|
1
|
+
import { computeCantonHash, computeCantonHashHex, computeCantonPublicKeyFingerprint, } from "../core/hashing/canton-hash.js";
|
|
3
2
|
export class CantonHashingClient {
|
|
4
3
|
/** Computes a Canton multihash for the provided content and hash purpose. */
|
|
5
4
|
computeHash(content, purpose) {
|
|
@@ -10,7 +9,7 @@ export class CantonHashingClient {
|
|
|
10
9
|
return computeCantonHashHex(content, purpose);
|
|
11
10
|
}
|
|
12
11
|
/** Computes the canonical Canton public-key fingerprint from serialized public key bytes. */
|
|
13
|
-
computePublicKeyFingerprint(publicKey) {
|
|
14
|
-
return
|
|
12
|
+
computePublicKeyFingerprint(publicKey, format) {
|
|
13
|
+
return computeCantonPublicKeyFingerprint(publicKey, format) ?? "";
|
|
15
14
|
}
|
|
16
15
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export declare function computeCantonHash(content: Uint8Array, purpose: number): Uint8Array;
|
|
2
2
|
export declare function computeCantonHashHex(content: Uint8Array, purpose: number): string;
|
|
3
|
-
export declare function computeCantonPublicKeyFingerprint(publicKey: Uint8Array | undefined): string | undefined;
|
|
3
|
+
export declare function computeCantonPublicKeyFingerprint(publicKey: Uint8Array | undefined, format?: string): string | undefined;
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { CantonHashPurpose } from "../types/canton-hash-purpose.js";
|
|
3
3
|
const sha256MultihashPrefix = new Uint8Array([0x12, 0x20]);
|
|
4
|
+
const asn1SequenceTag = 0x30;
|
|
5
|
+
const asn1ObjectIdentifierTag = 0x06;
|
|
6
|
+
const asn1BitStringTag = 0x03;
|
|
7
|
+
const ed25519ObjectIdentifier = new Uint8Array([0x2b, 0x65, 0x70]);
|
|
8
|
+
const derX509SubjectPublicKeyInfoFormat = "derX509SubjectPublicKeyInfo";
|
|
4
9
|
export function computeCantonHash(content, purpose) {
|
|
5
10
|
const purposePrefix = Buffer.alloc(4);
|
|
6
11
|
purposePrefix.writeUInt32BE(purpose, 0);
|
|
@@ -16,9 +21,99 @@ export function computeCantonHash(content, purpose) {
|
|
|
16
21
|
export function computeCantonHashHex(content, purpose) {
|
|
17
22
|
return Buffer.from(computeCantonHash(content, purpose)).toString("hex");
|
|
18
23
|
}
|
|
19
|
-
export function computeCantonPublicKeyFingerprint(publicKey) {
|
|
24
|
+
export function computeCantonPublicKeyFingerprint(publicKey, format) {
|
|
20
25
|
if (publicKey === undefined || publicKey.length === 0) {
|
|
21
26
|
return undefined;
|
|
22
27
|
}
|
|
23
|
-
return computeCantonHashHex(publicKey, CantonHashPurpose.publicKeyFingerprint);
|
|
28
|
+
return computeCantonHashHex(normalizePublicKeyForFingerprint(publicKey, format), CantonHashPurpose.publicKeyFingerprint);
|
|
29
|
+
}
|
|
30
|
+
function normalizePublicKeyForFingerprint(publicKey, format) {
|
|
31
|
+
if (format !== derX509SubjectPublicKeyInfoFormat) {
|
|
32
|
+
return publicKey;
|
|
33
|
+
}
|
|
34
|
+
return tryExtractEd25519PublicKeyFromSpki(publicKey) ?? publicKey;
|
|
35
|
+
}
|
|
36
|
+
function tryExtractEd25519PublicKeyFromSpki(spki) {
|
|
37
|
+
try {
|
|
38
|
+
const outer = readAsn1Element(spki, 0);
|
|
39
|
+
if (outer.tag !== asn1SequenceTag) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
const algorithm = readAsn1Element(spki, outer.contentOffset);
|
|
43
|
+
if (algorithm.tag !== asn1SequenceTag) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
const algorithmIdentifier = readAsn1Element(spki, algorithm.contentOffset);
|
|
47
|
+
if (algorithmIdentifier.tag !== asn1ObjectIdentifierTag
|
|
48
|
+
|| !bytesEqual(spki.subarray(algorithmIdentifier.contentOffset, algorithmIdentifier.endOffset), ed25519ObjectIdentifier)) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
const subjectPublicKey = readAsn1Element(spki, algorithm.endOffset);
|
|
52
|
+
if (subjectPublicKey.tag !== asn1BitStringTag) {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
const bitString = spki.subarray(subjectPublicKey.contentOffset, subjectPublicKey.endOffset);
|
|
56
|
+
if (bitString.length !== 33 || bitString[0] !== 0) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
return bitString.subarray(1);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function readAsn1Element(bytes, offset) {
|
|
66
|
+
if (offset + 2 > bytes.length) {
|
|
67
|
+
throw new Error("ASN.1 element header is truncated.");
|
|
68
|
+
}
|
|
69
|
+
const tag = bytes[offset];
|
|
70
|
+
const lengthResult = readAsn1Length(bytes, offset + 1);
|
|
71
|
+
const contentOffset = lengthResult.nextOffset;
|
|
72
|
+
const endOffset = contentOffset + lengthResult.length;
|
|
73
|
+
if (endOffset > bytes.length) {
|
|
74
|
+
throw new Error("ASN.1 element content is truncated.");
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
tag,
|
|
78
|
+
contentOffset,
|
|
79
|
+
endOffset,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function readAsn1Length(bytes, offset) {
|
|
83
|
+
if (offset >= bytes.length) {
|
|
84
|
+
throw new Error("ASN.1 length is truncated.");
|
|
85
|
+
}
|
|
86
|
+
const firstByte = bytes[offset];
|
|
87
|
+
if ((firstByte & 0x80) === 0) {
|
|
88
|
+
return {
|
|
89
|
+
length: firstByte,
|
|
90
|
+
nextOffset: offset + 1,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
const lengthByteCount = firstByte & 0x7f;
|
|
94
|
+
if (lengthByteCount === 0 || lengthByteCount > 4) {
|
|
95
|
+
throw new Error("ASN.1 length encoding is unsupported.");
|
|
96
|
+
}
|
|
97
|
+
if (offset + 1 + lengthByteCount > bytes.length) {
|
|
98
|
+
throw new Error("ASN.1 long-form length is truncated.");
|
|
99
|
+
}
|
|
100
|
+
let length = 0;
|
|
101
|
+
for (let index = 0; index < lengthByteCount; index += 1) {
|
|
102
|
+
length = (length << 8) | bytes[offset + 1 + index];
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
length,
|
|
106
|
+
nextOffset: offset + 1 + lengthByteCount,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function bytesEqual(left, right) {
|
|
110
|
+
if (left.length !== right.length) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
114
|
+
if (left[index] !== right[index]) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return true;
|
|
24
119
|
}
|
|
@@ -342,9 +342,10 @@ export function mapSdkDuration(value) {
|
|
|
342
342
|
});
|
|
343
343
|
}
|
|
344
344
|
export function mapSdkSigningPublicKey(value) {
|
|
345
|
+
const format = mapSdkCryptoKeyFormat(value?.format);
|
|
345
346
|
return new TopologySigningPublicKey({
|
|
346
|
-
fingerprint: computeCantonPublicKeyFingerprint(value?.publicKey),
|
|
347
|
-
format
|
|
347
|
+
fingerprint: computeCantonPublicKeyFingerprint(value?.publicKey, format),
|
|
348
|
+
format,
|
|
348
349
|
publicKey: value?.publicKey,
|
|
349
350
|
scheme: undefined,
|
|
350
351
|
usage: (value?.usage ?? []).map(mapSdkSigningKeyUsage),
|
|
@@ -352,9 +353,10 @@ export function mapSdkSigningPublicKey(value) {
|
|
|
352
353
|
});
|
|
353
354
|
}
|
|
354
355
|
export function mapSdkEncryptionPublicKey(value) {
|
|
356
|
+
const format = mapSdkCryptoKeyFormat(value?.format);
|
|
355
357
|
return new TopologyEncryptionPublicKey({
|
|
356
|
-
fingerprint: computeCantonPublicKeyFingerprint(value?.publicKey),
|
|
357
|
-
format
|
|
358
|
+
fingerprint: computeCantonPublicKeyFingerprint(value?.publicKey, format),
|
|
359
|
+
format,
|
|
358
360
|
publicKey: value?.publicKey,
|
|
359
361
|
scheme: undefined,
|
|
360
362
|
keySpec: mapSdkEncryptionKeySpec(value?.keySpec),
|