sello 0.1.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/dist/cbor.js +337 -0
- package/dist/cli/bench.js +389 -0
- package/dist/cli/demo.js +113 -0
- package/dist/cli/sello.js +515 -0
- package/dist/cose/protected-header.js +210 -0
- package/dist/cose/sign1.js +124 -0
- package/dist/crypto/ed25519.js +117 -0
- package/dist/crypto/identifiers.js +64 -0
- package/dist/hpke/base.js +349 -0
- package/dist/hpke/receipt.js +79 -0
- package/dist/index.js +15 -0
- package/dist/log/canonical-url.js +168 -0
- package/dist/log/mock-log.js +147 -0
- package/dist/log/rekor.js +120 -0
- package/dist/log/types.js +0 -0
- package/dist/mcp/middleware.js +162 -0
- package/dist/owner/verify.js +271 -0
- package/dist/receipt/body.js +210 -0
- package/dist/registry/json-registry.js +233 -0
- package/dist/sdk/index.js +22 -0
- package/dist/sdk/keys.js +191 -0
- package/dist/sdk/logs.js +196 -0
- package/dist/sdk/publisher.js +106 -0
- package/dist/sdk/service.js +561 -0
- package/dist/service/create-receipt.js +174 -0
- package/dist/token/jws-profile.js +174 -0
- package/docs/release-checklist.md +1 -0
- package/package.json +9 -5
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { decodeCbor, encodeCbor } from "../cbor.js";
|
|
2
|
+
import { assertTokenRef } from "../crypto/identifiers.js";
|
|
3
|
+
import {
|
|
4
|
+
|
|
5
|
+
assertCanonicalLogUrl,
|
|
6
|
+
} from "../log/canonical-url.js";
|
|
7
|
+
|
|
8
|
+
export const COSE_ALG_LABEL = 1;
|
|
9
|
+
export const COSE_CRIT_LABEL = 2;
|
|
10
|
+
export const COSE_KID_LABEL = 4;
|
|
11
|
+
export const SELLO_VERSION_LABEL = -65537;
|
|
12
|
+
export const SELLO_TOKEN_REF_LABEL = -65538;
|
|
13
|
+
export const SELLO_LOG_URL_LABEL = -65539;
|
|
14
|
+
|
|
15
|
+
export const COSE_ALG_EDDSA = -8;
|
|
16
|
+
export const SELLO_VERSION = "0.1.0";
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
const UNDERSTOOD_LABELS = new Set([
|
|
39
|
+
COSE_ALG_LABEL,
|
|
40
|
+
COSE_CRIT_LABEL,
|
|
41
|
+
COSE_KID_LABEL,
|
|
42
|
+
SELLO_VERSION_LABEL,
|
|
43
|
+
SELLO_TOKEN_REF_LABEL,
|
|
44
|
+
SELLO_LOG_URL_LABEL,
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
export function encodeProtectedHeader(header ) {
|
|
48
|
+
validateProtectedHeaderInput(header);
|
|
49
|
+
|
|
50
|
+
const map = new Map([
|
|
51
|
+
[COSE_ALG_LABEL, header.alg ?? COSE_ALG_EDDSA],
|
|
52
|
+
[COSE_KID_LABEL, header.kid],
|
|
53
|
+
[SELLO_VERSION_LABEL, header.sello_version ?? SELLO_VERSION],
|
|
54
|
+
[SELLO_TOKEN_REF_LABEL, header.sello_token_ref],
|
|
55
|
+
[SELLO_LOG_URL_LABEL, header.sello_log_url],
|
|
56
|
+
]);
|
|
57
|
+
|
|
58
|
+
if (header.crit !== undefined) {
|
|
59
|
+
map.set(COSE_CRIT_LABEL, [...header.crit]);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return encodeCbor(map);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function decodeProtectedHeader(bytes ) {
|
|
66
|
+
if (!(bytes instanceof Uint8Array)) {
|
|
67
|
+
throw new TypeError("protected header bytes must be a Uint8Array");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const decoded = decodeCbor(bytes);
|
|
71
|
+
if (!(decoded instanceof Map)) {
|
|
72
|
+
throw new TypeError("protected header must be a CBOR map");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const alg = readRequired(decoded, COSE_ALG_LABEL, "alg");
|
|
76
|
+
if (alg !== COSE_ALG_EDDSA) {
|
|
77
|
+
throw new TypeError("protected header alg must be -8");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const kid = readRequired(decoded, COSE_KID_LABEL, "kid");
|
|
81
|
+
assertKid(kid);
|
|
82
|
+
|
|
83
|
+
const selloVersion = readRequired(
|
|
84
|
+
decoded,
|
|
85
|
+
SELLO_VERSION_LABEL,
|
|
86
|
+
"sello_version",
|
|
87
|
+
);
|
|
88
|
+
if (selloVersion !== SELLO_VERSION) {
|
|
89
|
+
throw new TypeError(`protected header sello_version must be ${SELLO_VERSION}`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const selloTokenRef = readRequired(
|
|
93
|
+
decoded,
|
|
94
|
+
SELLO_TOKEN_REF_LABEL,
|
|
95
|
+
"sello_token_ref",
|
|
96
|
+
);
|
|
97
|
+
assertTokenRef(selloTokenRef, "protected header sello_token_ref");
|
|
98
|
+
|
|
99
|
+
const selloLogUrl = readRequired(decoded, SELLO_LOG_URL_LABEL, "sello_log_url");
|
|
100
|
+
assertCanonicalLogUrl(selloLogUrl, "protected header sello_log_url");
|
|
101
|
+
|
|
102
|
+
const crit = readCrit(decoded.get(COSE_CRIT_LABEL));
|
|
103
|
+
const unknownHeaders = collectUnknownHeaders(decoded);
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
alg,
|
|
107
|
+
kid: copyBytes(kid),
|
|
108
|
+
...(crit === undefined ? {} : { crit }),
|
|
109
|
+
sello_version: selloVersion,
|
|
110
|
+
sello_token_ref: copyBytes(selloTokenRef),
|
|
111
|
+
sello_log_url: selloLogUrl,
|
|
112
|
+
protectedBytes: copyBytes(bytes),
|
|
113
|
+
unknownHeaders,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function validateProtectedHeaderInput(header ) {
|
|
118
|
+
if (typeof header !== "object" || header === null) {
|
|
119
|
+
throw new TypeError("protected header must be an object");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (header.alg !== undefined && header.alg !== COSE_ALG_EDDSA) {
|
|
123
|
+
throw new TypeError("protected header alg must be -8");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
assertKid(header.kid);
|
|
127
|
+
|
|
128
|
+
if (
|
|
129
|
+
header.sello_version !== undefined &&
|
|
130
|
+
header.sello_version !== SELLO_VERSION
|
|
131
|
+
) {
|
|
132
|
+
throw new TypeError(`protected header sello_version must be ${SELLO_VERSION}`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
assertTokenRef(header.sello_token_ref, "protected header sello_token_ref");
|
|
136
|
+
assertCanonicalLogUrl(header.sello_log_url, "protected header sello_log_url");
|
|
137
|
+
readCrit(header.crit);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function readRequired(
|
|
141
|
+
map ,
|
|
142
|
+
label ,
|
|
143
|
+
name ,
|
|
144
|
+
) {
|
|
145
|
+
if (!map.has(label)) {
|
|
146
|
+
throw new TypeError(`protected header is missing ${name}`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return map.get(label) ;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function readCrit(value ) {
|
|
153
|
+
if (value === undefined) {
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (!Array.isArray(value)) {
|
|
158
|
+
throw new TypeError("protected header crit must be an array");
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (value.length === 0) {
|
|
162
|
+
throw new TypeError("protected header crit must not be empty");
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const seen = new Set ();
|
|
166
|
+
const out = [];
|
|
167
|
+
|
|
168
|
+
for (const label of value) {
|
|
169
|
+
if (!Number.isSafeInteger(label)) {
|
|
170
|
+
throw new TypeError("protected header crit labels must be integers");
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (seen.has(label)) {
|
|
174
|
+
throw new TypeError("protected header crit must not contain duplicates");
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (!UNDERSTOOD_LABELS.has(label)) {
|
|
178
|
+
throw new TypeError(`unknown critical protected header label ${label}`);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
seen.add(label);
|
|
182
|
+
out.push(label);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return out;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function assertKid(value ) {
|
|
189
|
+
if (!(value instanceof Uint8Array) || value.byteLength === 0) {
|
|
190
|
+
throw new TypeError("protected header kid must be non-empty bytes");
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function collectUnknownHeaders(map ) {
|
|
195
|
+
const unknownHeaders = new Map();
|
|
196
|
+
|
|
197
|
+
for (const [label, value] of map) {
|
|
198
|
+
if (typeof label === "number" && UNDERSTOOD_LABELS.has(label)) {
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
unknownHeaders.set(label, value);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return unknownHeaders;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function copyBytes(value ) {
|
|
209
|
+
return new Uint8Array(value);
|
|
210
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { decodeCbor, encodeCbor } from "../cbor.js";
|
|
2
|
+
import {
|
|
3
|
+
assertEd25519PrivateKey,
|
|
4
|
+
assertEd25519PublicKey,
|
|
5
|
+
assertEd25519Signature,
|
|
6
|
+
generateEd25519KeyPair,
|
|
7
|
+
signEd25519,
|
|
8
|
+
verifyEd25519Signature,
|
|
9
|
+
|
|
10
|
+
} from "../crypto/ed25519.js";
|
|
11
|
+
import { decodeProtectedHeader } from "./protected-header.js";
|
|
12
|
+
|
|
13
|
+
export { generateEd25519KeyPair, };
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
const EMPTY = new Uint8Array();
|
|
33
|
+
|
|
34
|
+
export function signReceiptEnvelope(input ) {
|
|
35
|
+
assertBytes(input.protectedHeaderBytes, "protectedHeaderBytes");
|
|
36
|
+
assertBytes(input.payload, "payload");
|
|
37
|
+
assertEd25519PrivateKey(input.servicePrivateKey, "servicePrivateKey");
|
|
38
|
+
decodeProtectedHeader(input.protectedHeaderBytes);
|
|
39
|
+
|
|
40
|
+
const signature = signEd25519(
|
|
41
|
+
buildSigStructure(input.protectedHeaderBytes, input.payload),
|
|
42
|
+
input.servicePrivateKey,
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
return encodeCbor([
|
|
46
|
+
input.protectedHeaderBytes,
|
|
47
|
+
new Map(),
|
|
48
|
+
input.payload,
|
|
49
|
+
signature,
|
|
50
|
+
]);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function verifyReceiptEnvelope(
|
|
54
|
+
input ,
|
|
55
|
+
) {
|
|
56
|
+
assertBytes(input.envelope, "envelope");
|
|
57
|
+
assertEd25519PublicKey(input.servicePublicKey, "servicePublicKey");
|
|
58
|
+
|
|
59
|
+
const decoded = decodeReceiptEnvelope(input.envelope);
|
|
60
|
+
decodeProtectedHeader(decoded.protectedBytes);
|
|
61
|
+
|
|
62
|
+
const ok = verifyEd25519Signature(
|
|
63
|
+
buildSigStructure(decoded.protectedBytes, decoded.payload),
|
|
64
|
+
decoded.signature,
|
|
65
|
+
input.servicePublicKey,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
if (!ok) {
|
|
69
|
+
throw new TypeError("COSE_Sign1 signature verification failed");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return decoded;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function decodeReceiptEnvelope(envelope ) {
|
|
76
|
+
const decoded = decodeCbor(envelope);
|
|
77
|
+
|
|
78
|
+
if (!Array.isArray(decoded) || decoded.length !== 4) {
|
|
79
|
+
throw new TypeError("COSE_Sign1 envelope must be a 4-element array");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const [protectedBytes, unprotected, payload, signature] = decoded;
|
|
83
|
+
|
|
84
|
+
assertBytes(protectedBytes, "COSE_Sign1 protected header");
|
|
85
|
+
assertEmptyUnprotected(unprotected);
|
|
86
|
+
assertBytes(payload, "COSE_Sign1 payload");
|
|
87
|
+
assertEd25519Signature(signature, "COSE_Sign1 signature");
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
protectedBytes: copyBytes(protectedBytes),
|
|
91
|
+
payload: copyBytes(payload),
|
|
92
|
+
signature: copyBytes(signature),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function buildSigStructure(
|
|
97
|
+
protectedHeaderBytes ,
|
|
98
|
+
payload ,
|
|
99
|
+
) {
|
|
100
|
+
assertBytes(protectedHeaderBytes, "protectedHeaderBytes");
|
|
101
|
+
assertBytes(payload, "payload");
|
|
102
|
+
|
|
103
|
+
return encodeCbor(["Signature1", protectedHeaderBytes, EMPTY, payload]);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function assertEmptyUnprotected(value ) {
|
|
107
|
+
if (!(value instanceof Map)) {
|
|
108
|
+
throw new TypeError("COSE_Sign1 unprotected header must be a map");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (value.size !== 0) {
|
|
112
|
+
throw new TypeError("COSE_Sign1 unprotected header must be empty");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function assertBytes(value , name ) {
|
|
117
|
+
if (!(value instanceof Uint8Array)) {
|
|
118
|
+
throw new TypeError(`${name} must be a Uint8Array`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function copyBytes(value ) {
|
|
123
|
+
return new Uint8Array(value);
|
|
124
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createPrivateKey,
|
|
3
|
+
createPublicKey,
|
|
4
|
+
generateKeyPairSync,
|
|
5
|
+
sign,
|
|
6
|
+
verify,
|
|
7
|
+
} from "node:crypto";
|
|
8
|
+
|
|
9
|
+
import { concat } from "../cbor.js";
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
export const ED25519_KEY_LENGTH = 32;
|
|
17
|
+
export const ED25519_SIGNATURE_LENGTH = 64;
|
|
18
|
+
|
|
19
|
+
const ED25519_PUBLIC_KEY_SPKI_PREFIX = hex("302a300506032b6570032100");
|
|
20
|
+
const ED25519_PRIVATE_KEY_PKCS8_PREFIX = hex("302e020100300506032b657004220420");
|
|
21
|
+
|
|
22
|
+
export function generateEd25519KeyPair() {
|
|
23
|
+
const { publicKey, privateKey } = generateKeyPairSync("ed25519");
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
publicKey: exportRawEd25519PublicKey(publicKey),
|
|
27
|
+
privateKey: exportRawEd25519PrivateKey(privateKey),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function signEd25519(message , privateKey ) {
|
|
32
|
+
assertBytes(message, "message");
|
|
33
|
+
assertEd25519PrivateKey(privateKey, "privateKey");
|
|
34
|
+
|
|
35
|
+
return new Uint8Array(sign(null, message, createEd25519PrivateKey(privateKey)));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function verifyEd25519Signature(
|
|
39
|
+
message ,
|
|
40
|
+
signature ,
|
|
41
|
+
publicKey ,
|
|
42
|
+
) {
|
|
43
|
+
assertBytes(message, "message");
|
|
44
|
+
assertEd25519Signature(signature, "signature");
|
|
45
|
+
assertEd25519PublicKey(publicKey, "publicKey");
|
|
46
|
+
|
|
47
|
+
return verify(null, message, createEd25519PublicKey(publicKey), signature);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function assertEd25519PublicKey(
|
|
51
|
+
value ,
|
|
52
|
+
name = "publicKey",
|
|
53
|
+
) {
|
|
54
|
+
assertByteLength(value, ED25519_KEY_LENGTH, name);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function assertEd25519PrivateKey(
|
|
58
|
+
value ,
|
|
59
|
+
name = "privateKey",
|
|
60
|
+
) {
|
|
61
|
+
assertByteLength(value, ED25519_KEY_LENGTH, name);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function assertEd25519Signature(
|
|
65
|
+
value ,
|
|
66
|
+
name = "signature",
|
|
67
|
+
) {
|
|
68
|
+
assertByteLength(value, ED25519_SIGNATURE_LENGTH, name);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function createEd25519PublicKey(rawPublicKey ) {
|
|
72
|
+
assertEd25519PublicKey(rawPublicKey, "rawPublicKey");
|
|
73
|
+
return createPublicKey({
|
|
74
|
+
key: Buffer.from(concat([ED25519_PUBLIC_KEY_SPKI_PREFIX, rawPublicKey])),
|
|
75
|
+
format: "der",
|
|
76
|
+
type: "spki",
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function createEd25519PrivateKey(rawPrivateKey ) {
|
|
81
|
+
assertEd25519PrivateKey(rawPrivateKey, "rawPrivateKey");
|
|
82
|
+
return createPrivateKey({
|
|
83
|
+
key: Buffer.from(concat([ED25519_PRIVATE_KEY_PKCS8_PREFIX, rawPrivateKey])),
|
|
84
|
+
format: "der",
|
|
85
|
+
type: "pkcs8",
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function exportRawEd25519PublicKey(key ) {
|
|
90
|
+
const der = new Uint8Array(key.export({ format: "der", type: "spki" }));
|
|
91
|
+
return new Uint8Array(der.subarray(der.byteLength - ED25519_KEY_LENGTH));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function exportRawEd25519PrivateKey(key ) {
|
|
95
|
+
const der = new Uint8Array(key.export({ format: "der", type: "pkcs8" }));
|
|
96
|
+
return new Uint8Array(der.subarray(der.byteLength - ED25519_KEY_LENGTH));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function assertByteLength(
|
|
100
|
+
value ,
|
|
101
|
+
length ,
|
|
102
|
+
name ,
|
|
103
|
+
) {
|
|
104
|
+
if (!(value instanceof Uint8Array) || value.byteLength !== length) {
|
|
105
|
+
throw new TypeError(`${name} must be a ${length}-byte Uint8Array`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function assertBytes(value , name ) {
|
|
110
|
+
if (!(value instanceof Uint8Array)) {
|
|
111
|
+
throw new TypeError(`${name} must be a Uint8Array`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function hex(value ) {
|
|
116
|
+
return Uint8Array.from(Buffer.from(value, "hex"));
|
|
117
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
export const SHA256_DIGEST_LENGTH = 32;
|
|
4
|
+
export const AGENT_IDENTIFIER_HEX_LENGTH = 32;
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
export function sha256(bytes ) {
|
|
12
|
+
assertUint8Array(bytes, "bytes");
|
|
13
|
+
return new Uint8Array(createHash("sha256").update(bytes).digest());
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function deriveTokenIdentifiers(
|
|
17
|
+
authorizationTokenBytes ,
|
|
18
|
+
) {
|
|
19
|
+
assertUint8Array(authorizationTokenBytes, "authorizationTokenBytes");
|
|
20
|
+
|
|
21
|
+
const digest = sha256(authorizationTokenBytes);
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
sello_token_ref: digest,
|
|
25
|
+
agent_identifier: toHex(digest.subarray(0, 16)),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function isTokenRef(value ) {
|
|
30
|
+
return value instanceof Uint8Array && value.byteLength === SHA256_DIGEST_LENGTH;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function assertTokenRef(value , name = "sello_token_ref") {
|
|
34
|
+
if (!isTokenRef(value)) {
|
|
35
|
+
throw new TypeError(`${name} must be a 32-byte Uint8Array`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function isAgentIdentifier(value ) {
|
|
40
|
+
return (
|
|
41
|
+
typeof value === "string" &&
|
|
42
|
+
/^[0-9a-f]{32}$/.test(value)
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function assertAgentIdentifier(
|
|
47
|
+
value ,
|
|
48
|
+
name = "agent_identifier",
|
|
49
|
+
) {
|
|
50
|
+
if (!isAgentIdentifier(value)) {
|
|
51
|
+
throw new TypeError(`${name} must be a 32-character lowercase hex string`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function toHex(bytes ) {
|
|
56
|
+
assertUint8Array(bytes, "bytes");
|
|
57
|
+
return Buffer.from(bytes).toString("hex");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function assertUint8Array(value , name ) {
|
|
61
|
+
if (!(value instanceof Uint8Array)) {
|
|
62
|
+
throw new TypeError(`${name} must be a Uint8Array`);
|
|
63
|
+
}
|
|
64
|
+
}
|