@enbox/agent 0.3.1 → 0.5.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/dist/browser.mjs +12 -30
- package/dist/browser.mjs.map +4 -4
- package/dist/esm/dwn-api.js +149 -22
- package/dist/esm/dwn-api.js.map +1 -1
- package/dist/esm/dwn-discovery-file.js +1 -1
- package/dist/esm/dwn-discovery-payload.js +20 -21
- package/dist/esm/dwn-discovery-payload.js.map +1 -1
- package/dist/esm/dwn-key-delivery.js.map +1 -1
- package/dist/esm/{oidc.js → enbox-connect-protocol.js} +219 -251
- package/dist/esm/enbox-connect-protocol.js.map +1 -0
- package/dist/esm/enbox-user-agent.js +19 -12
- package/dist/esm/enbox-user-agent.js.map +1 -1
- package/dist/esm/hd-identity-vault.js +11 -0
- package/dist/esm/hd-identity-vault.js.map +1 -1
- package/dist/esm/index.js +4 -4
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/local-dwn.js +21 -51
- package/dist/esm/local-dwn.js.map +1 -1
- package/dist/esm/permissions-api.js.map +1 -1
- package/dist/esm/store-data.js.map +1 -1
- package/dist/esm/sync-engine-level.js +1 -1
- package/dist/esm/sync-engine-level.js.map +1 -1
- package/dist/esm/sync-messages.js +1 -1
- package/dist/esm/sync-messages.js.map +1 -1
- package/dist/esm/test-harness.js +2 -3
- package/dist/esm/test-harness.js.map +1 -1
- package/dist/esm/types/dwn.js.map +1 -1
- package/dist/types/dwn-api.d.ts +46 -6
- package/dist/types/dwn-api.d.ts.map +1 -1
- package/dist/types/dwn-discovery-file.d.ts +1 -1
- package/dist/types/dwn-discovery-payload.d.ts +18 -19
- package/dist/types/dwn-discovery-payload.d.ts.map +1 -1
- package/dist/types/enbox-connect-protocol.d.ts +206 -0
- package/dist/types/enbox-connect-protocol.d.ts.map +1 -0
- package/dist/types/enbox-user-agent.d.ts +13 -8
- package/dist/types/enbox-user-agent.d.ts.map +1 -1
- package/dist/types/hd-identity-vault.d.ts +7 -0
- package/dist/types/hd-identity-vault.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -4
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/local-dwn.d.ts +16 -32
- package/dist/types/local-dwn.d.ts.map +1 -1
- package/dist/types/test-harness.d.ts.map +1 -1
- package/dist/types/types/agent.d.ts +2 -7
- package/dist/types/types/agent.d.ts.map +1 -1
- package/dist/types/types/dwn.d.ts +0 -10
- package/dist/types/types/dwn.d.ts.map +1 -1
- package/dist/types/types/sync.d.ts +6 -0
- package/dist/types/types/sync.d.ts.map +1 -1
- package/package.json +14 -16
- package/src/dwn-api.ts +175 -29
- package/src/dwn-discovery-file.ts +1 -1
- package/src/dwn-discovery-payload.ts +23 -24
- package/src/dwn-key-delivery.ts +1 -1
- package/src/enbox-connect-protocol.ts +753 -0
- package/src/enbox-user-agent.ts +31 -18
- package/src/hd-identity-vault.ts +21 -0
- package/src/index.ts +4 -4
- package/src/local-dwn.ts +22 -53
- package/src/permissions-api.ts +3 -3
- package/src/store-data.ts +1 -1
- package/src/sync-engine-level.ts +1 -1
- package/src/sync-messages.ts +1 -1
- package/src/test-harness.ts +2 -3
- package/src/types/agent.ts +3 -14
- package/src/types/dwn.ts +1 -13
- package/src/types/sync.ts +7 -0
- package/dist/esm/connect.js +0 -180
- package/dist/esm/connect.js.map +0 -1
- package/dist/esm/oidc.js.map +0 -1
- package/dist/esm/sync-api.js +0 -64
- package/dist/esm/sync-api.js.map +0 -1
- package/dist/types/connect.d.ts +0 -88
- package/dist/types/connect.d.ts.map +0 -1
- package/dist/types/oidc.d.ts +0 -250
- package/dist/types/oidc.d.ts.map +0 -1
- package/dist/types/sync-api.d.ts +0 -40
- package/dist/types/sync-api.d.ts.map +0 -1
- package/src/connect.ts +0 -285
- package/src/oidc.ts +0 -864
- package/src/sync-api.ts +0 -75
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enbox Connect Protocol
|
|
3
|
+
*
|
|
4
|
+
* A capability delegation protocol for DWN access. Enables apps to request
|
|
5
|
+
* scoped permission grants from a wallet (provider), receiving a delegate DID
|
|
6
|
+
* with the granted permissions.
|
|
7
|
+
*
|
|
8
|
+
* Two transport modes:
|
|
9
|
+
* - Local (`dwn://connect`): same-device, direct HTTP against the local DWN
|
|
10
|
+
* - Remote (`enbox://connect`): cross-device, relay-mediated with QR/deep link
|
|
11
|
+
*
|
|
12
|
+
* The protocol uses JWTs for signing, JWE (XChaCha20-Poly1305) for encryption,
|
|
13
|
+
* and ECDH (Ed25519 → X25519 + HKDF) for key agreement.
|
|
14
|
+
*/
|
|
1
15
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
16
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
17
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -20,110 +34,47 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
20
34
|
};
|
|
21
35
|
import { DidJwk } from '@enbox/dids';
|
|
22
36
|
import { Convert, logger } from '@enbox/common';
|
|
23
|
-
import { CryptoUtils, Ed25519, EdDsaAlgorithm, Hkdf,
|
|
37
|
+
import { CryptoUtils, Ed25519, EdDsaAlgorithm, Hkdf, X25519, XChaCha20Poly1305, } from '@enbox/crypto';
|
|
24
38
|
import { DwnInterfaceName, DwnMethodName } from '@enbox/dwn-sdk-js';
|
|
25
39
|
import { AgentPermissionsApi } from './permissions-api.js';
|
|
26
40
|
import { concatenateUrl } from './utils.js';
|
|
27
41
|
import { DwnInterface } from './types/dwn.js';
|
|
28
42
|
import { isRecordPermissionScope } from './dwn-api.js';
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// URL building
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
29
46
|
/**
|
|
30
|
-
*
|
|
31
|
-
* Handles a trailing slash on baseURL
|
|
47
|
+
* Builds the URL for a connect server endpoint.
|
|
32
48
|
*
|
|
33
|
-
* @param
|
|
34
|
-
* @param
|
|
35
|
-
* @param
|
|
36
|
-
* @param
|
|
37
|
-
* @param {string} options.tokenParam this is the random state as b64url which must be provided with the `token` endpoint
|
|
49
|
+
* @param options.baseURL - The connect server base URL (e.g. `http://localhost:3000/connect/`)
|
|
50
|
+
* @param options.endpoint - The endpoint type
|
|
51
|
+
* @param options.authParam - Required for `authorize` endpoint (the request ID)
|
|
52
|
+
* @param options.tokenParam - Required for `token` endpoint (the state value)
|
|
38
53
|
*/
|
|
39
|
-
function
|
|
54
|
+
function buildConnectUrl({ baseURL, endpoint, authParam, tokenParam, }) {
|
|
40
55
|
switch (endpoint) {
|
|
41
|
-
/** 1. client sends {@link PushedAuthRequest} & client receives {@link PushedAuthResponse} */
|
|
42
56
|
case 'pushedAuthorizationRequest':
|
|
43
57
|
return concatenateUrl(baseURL, 'par');
|
|
44
|
-
/** 2. provider gets {@link EnboxConnectAuthRequest} */
|
|
45
58
|
case 'authorize':
|
|
46
59
|
if (!authParam) {
|
|
47
|
-
throw new Error(
|
|
60
|
+
throw new Error('authParam must be provided when building an authorize URL');
|
|
48
61
|
}
|
|
49
62
|
return concatenateUrl(baseURL, `authorize/${authParam}.jwt`);
|
|
50
|
-
/** 3. provider sends {@link EnboxConnectAuthResponse} */
|
|
51
63
|
case 'callback':
|
|
52
|
-
return concatenateUrl(baseURL,
|
|
53
|
-
/** 4. client gets {@link EnboxConnectAuthResponse */
|
|
64
|
+
return concatenateUrl(baseURL, 'callback');
|
|
54
65
|
case 'token':
|
|
55
66
|
if (!tokenParam) {
|
|
56
|
-
throw new Error(
|
|
67
|
+
throw new Error('tokenParam must be provided when building a token URL');
|
|
57
68
|
}
|
|
58
69
|
return concatenateUrl(baseURL, `token/${tokenParam}.jwt`);
|
|
59
|
-
// TODO: metadata endpoints?
|
|
60
70
|
default:
|
|
61
|
-
throw new Error(`
|
|
71
|
+
throw new Error(`Unknown connect endpoint: ${endpoint}`);
|
|
62
72
|
}
|
|
63
73
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
* @see {@link https://datatracker.ietf.org/doc/html/rfc7636#section-4.2 | RFC 7636 }
|
|
69
|
-
*/
|
|
70
|
-
function generateCodeChallenge() {
|
|
71
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
72
|
-
const codeVerifierBytes = CryptoUtils.randomBytes(32);
|
|
73
|
-
const codeChallengeBytes = yield Sha256.digest({ data: codeVerifierBytes });
|
|
74
|
-
const codeChallengeBase64Url = Convert.uint8Array(codeChallengeBytes).toBase64Url();
|
|
75
|
-
return { codeChallengeBytes, codeChallengeBase64Url };
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
/** Client creates the {@link EnboxConnectAuthRequest} */
|
|
79
|
-
function createAuthRequest(options) {
|
|
80
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
81
|
-
// Generate a random state value to associate the authorization request with the response.
|
|
82
|
-
const stateBytes = CryptoUtils.randomBytes(16);
|
|
83
|
-
// Generate a random nonce value to associate the ID Token with the authorization request.
|
|
84
|
-
const nonceBytes = CryptoUtils.randomBytes(16);
|
|
85
|
-
const requestObject = Object.assign(Object.assign({}, options), { nonce: Convert.uint8Array(nonceBytes).toBase64Url(), response_type: 'id_token', response_mode: 'direct_post', state: Convert.uint8Array(stateBytes).toBase64Url(), client_metadata: {
|
|
86
|
-
subject_syntax_types_supported: ['did:dht', 'did:jwk'],
|
|
87
|
-
} });
|
|
88
|
-
return requestObject;
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
/** Encrypts the auth request with the key which will be passed through QR code */
|
|
92
|
-
function encryptAuthRequest(_a) {
|
|
93
|
-
return __awaiter(this, arguments, void 0, function* ({ jwt, encryptionKey, }) {
|
|
94
|
-
const protectedHeader = {
|
|
95
|
-
alg: 'dir',
|
|
96
|
-
cty: 'JWT',
|
|
97
|
-
enc: 'XC20P',
|
|
98
|
-
typ: 'JWT',
|
|
99
|
-
};
|
|
100
|
-
const nonce = CryptoUtils.randomBytes(24);
|
|
101
|
-
const additionalData = Convert.object(protectedHeader).toUint8Array();
|
|
102
|
-
const jwtBytes = Convert.string(jwt).toUint8Array();
|
|
103
|
-
const ciphertextAndTag = yield XChaCha20Poly1305.encryptRaw({ data: jwtBytes, keyBytes: encryptionKey, nonce, additionalData });
|
|
104
|
-
/** The cipher output concatenates the encrypted data and tag
|
|
105
|
-
* so we need to extract the values for use in the JWE. */
|
|
106
|
-
const ciphertext = ciphertextAndTag.subarray(0, -16);
|
|
107
|
-
const authenticationTag = ciphertextAndTag.subarray(-16);
|
|
108
|
-
const compactJwe = [
|
|
109
|
-
Convert.object(protectedHeader).toBase64Url(),
|
|
110
|
-
'', // Empty string since there is no wrapped key.
|
|
111
|
-
Convert.uint8Array(nonce).toBase64Url(),
|
|
112
|
-
Convert.uint8Array(ciphertext).toBase64Url(),
|
|
113
|
-
Convert.uint8Array(authenticationTag).toBase64Url(),
|
|
114
|
-
].join('.');
|
|
115
|
-
return compactJwe;
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
/** Create a response object compatible with Web5 Connect and OIDC SIOPv2 */
|
|
119
|
-
function createResponseObject(options) {
|
|
120
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
121
|
-
const currentTimeInSeconds = Math.floor(Date.now() / 1000);
|
|
122
|
-
const responseObject = Object.assign(Object.assign({}, options), { iat: currentTimeInSeconds, exp: currentTimeInSeconds + 600 });
|
|
123
|
-
return responseObject;
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
/** sign an object and transform it into a jwt using a did */
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
// JWT signing and verification
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
/** Signs an object as a JWT using an Ed25519 DID key. */
|
|
127
78
|
function signJwt(_a) {
|
|
128
79
|
return __awaiter(this, arguments, void 0, function* ({ did, data, }) {
|
|
129
80
|
const header = Convert.object({
|
|
@@ -132,37 +83,32 @@ function signJwt(_a) {
|
|
|
132
83
|
typ: 'JWT',
|
|
133
84
|
}).toBase64Url();
|
|
134
85
|
const payload = Convert.object(data).toBase64Url();
|
|
135
|
-
// signs using ed25519 EdDSA
|
|
136
86
|
const signer = yield did.getSigner();
|
|
137
87
|
const signature = yield signer.sign({
|
|
138
88
|
data: Convert.string(`${header}.${payload}`).toUint8Array(),
|
|
139
89
|
});
|
|
140
90
|
const signatureBase64Url = Convert.uint8Array(signature).toBase64Url();
|
|
141
|
-
|
|
142
|
-
return jwt;
|
|
91
|
+
return `${header}.${payload}.${signatureBase64Url}`;
|
|
143
92
|
});
|
|
144
93
|
}
|
|
145
|
-
/**
|
|
94
|
+
/** Verifies a JWT signature using the DID in the `kid` header. Returns the parsed payload. */
|
|
146
95
|
function verifyJwt(_a) {
|
|
147
96
|
return __awaiter(this, arguments, void 0, function* ({ jwt }) {
|
|
148
97
|
var _b, _c;
|
|
149
98
|
const [headerB64U, payloadB64U, signatureB64U] = jwt.split('.');
|
|
150
|
-
// Convert the header back to a JOSE object and verify that the 'kid' header value is present.
|
|
151
99
|
const header = Convert.base64Url(headerB64U).toObject();
|
|
152
100
|
if (!header.kid) {
|
|
153
|
-
throw new Error(
|
|
101
|
+
throw new Error('Connect: JWT missing required "kid" header value.');
|
|
154
102
|
}
|
|
155
|
-
// Resolve the Client DID document.
|
|
156
103
|
const { didDocument } = yield DidJwk.resolve(header.kid.split('#')[0]);
|
|
157
104
|
if (!didDocument) {
|
|
158
|
-
throw new Error('
|
|
105
|
+
throw new Error('Connect: JWT verification failed — could not resolve DID.');
|
|
159
106
|
}
|
|
160
|
-
// Get the public key used to sign the Object from the DID document.
|
|
161
107
|
const { publicKeyJwk } = (_c = (_b = didDocument.verificationMethod) === null || _b === void 0 ? void 0 : _b.find((method) => {
|
|
162
108
|
return method.id === header.kid;
|
|
163
109
|
})) !== null && _c !== void 0 ? _c : {};
|
|
164
110
|
if (!publicKeyJwk) {
|
|
165
|
-
throw new Error('
|
|
111
|
+
throw new Error('Connect: JWT verification failed — public key not found in DID document.');
|
|
166
112
|
}
|
|
167
113
|
const EdDsa = new EdDsaAlgorithm();
|
|
168
114
|
const isValid = yield EdDsa.verify({
|
|
@@ -171,86 +117,56 @@ function verifyJwt(_a) {
|
|
|
171
117
|
data: Convert.string(`${headerB64U}.${payloadB64U}`).toUint8Array(),
|
|
172
118
|
});
|
|
173
119
|
if (!isValid) {
|
|
174
|
-
throw new Error('
|
|
120
|
+
throw new Error('Connect: JWT verification failed — invalid signature.');
|
|
175
121
|
}
|
|
176
|
-
|
|
177
|
-
return object;
|
|
122
|
+
return Convert.base64Url(payloadB64U).toObject();
|
|
178
123
|
});
|
|
179
124
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
*/
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
const authenticationTag = Convert.base64Url(authenticationTagB64U).toUint8Array();
|
|
206
|
-
// The cipher expects the encrypted data and tag to be concatenated.
|
|
207
|
-
const ciphertextAndTag = new Uint8Array([
|
|
208
|
-
...ciphertext,
|
|
209
|
-
...authenticationTag,
|
|
210
|
-
]);
|
|
211
|
-
const decryptedJwtBytes = yield XChaCha20Poly1305.decryptRaw({ data: ciphertextAndTag, keyBytes: encryptionKeyBytes, nonce, additionalData });
|
|
212
|
-
const jwt = Convert.uint8Array(decryptedJwtBytes).toString();
|
|
213
|
-
return jwt;
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
// Encryption: request (symmetric key via QR/deep link)
|
|
127
|
+
// ---------------------------------------------------------------------------
|
|
128
|
+
/** Encrypts the connect request JWT with a symmetric key (shared via QR code or deep link). */
|
|
129
|
+
function encryptRequest(_a) {
|
|
130
|
+
return __awaiter(this, arguments, void 0, function* ({ jwt, encryptionKey, }) {
|
|
131
|
+
const protectedHeader = {
|
|
132
|
+
alg: 'dir',
|
|
133
|
+
cty: 'JWT',
|
|
134
|
+
enc: 'XC20P',
|
|
135
|
+
typ: 'JWT',
|
|
136
|
+
};
|
|
137
|
+
const nonce = CryptoUtils.randomBytes(24);
|
|
138
|
+
const additionalData = Convert.object(protectedHeader).toUint8Array();
|
|
139
|
+
const jwtBytes = Convert.string(jwt).toUint8Array();
|
|
140
|
+
const ciphertextAndTag = yield XChaCha20Poly1305.encryptRaw({ data: jwtBytes, keyBytes: encryptionKey, nonce, additionalData });
|
|
141
|
+
const ciphertext = ciphertextAndTag.subarray(0, -16);
|
|
142
|
+
const authenticationTag = ciphertextAndTag.subarray(-16);
|
|
143
|
+
return [
|
|
144
|
+
Convert.object(protectedHeader).toBase64Url(),
|
|
145
|
+
'', // No wrapped key (direct encryption).
|
|
146
|
+
Convert.uint8Array(nonce).toBase64Url(),
|
|
147
|
+
Convert.uint8Array(ciphertext).toBase64Url(),
|
|
148
|
+
Convert.uint8Array(authenticationTag).toBase64Url(),
|
|
149
|
+
].join('.');
|
|
214
150
|
});
|
|
215
151
|
}
|
|
216
|
-
/**
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
*
|
|
220
|
-
* @async
|
|
221
|
-
* @param {BearerDid} clientDid - The did that was initially used by the client for ECDH at connect init.
|
|
222
|
-
* @param {string} jwe - The encrypted data as a jwe.
|
|
223
|
-
* @param {string} pin - The pin that was obtained from the user.
|
|
224
|
-
*/
|
|
225
|
-
function decryptAuthResponse(clientDid, jwe, pin) {
|
|
226
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
152
|
+
/** Decrypts an encrypted connect request JWE using the symmetric key from the QR/deep link. */
|
|
153
|
+
function decryptRequest(_a) {
|
|
154
|
+
return __awaiter(this, arguments, void 0, function* ({ jwe, encryptionKey, }) {
|
|
227
155
|
const [protectedHeaderB64U, , nonceB64U, ciphertextB64U, authenticationTagB64U,] = jwe.split('.');
|
|
228
|
-
|
|
229
|
-
const
|
|
230
|
-
if (!header.kid) {
|
|
231
|
-
throw new Error('JWE protected header is missing required "kid" property');
|
|
232
|
-
}
|
|
233
|
-
const delegateResolvedDid = yield DidJwk.resolve(header.kid.split('#')[0]);
|
|
234
|
-
// derive ECDH shared key using the provider's public key and our clientDid private key
|
|
235
|
-
const sharedKey = yield Oidc.deriveSharedKey(clientDid, delegateResolvedDid.didDocument);
|
|
236
|
-
// add the pin to the AAD
|
|
237
|
-
const additionalData = Object.assign(Object.assign({}, header), { pin: pin });
|
|
238
|
-
const AAD = Convert.object(additionalData).toUint8Array();
|
|
156
|
+
const encryptionKeyBytes = Convert.base64Url(encryptionKey).toUint8Array();
|
|
157
|
+
const additionalData = Convert.base64Url(protectedHeaderB64U).toUint8Array();
|
|
239
158
|
const nonce = Convert.base64Url(nonceB64U).toUint8Array();
|
|
240
159
|
const ciphertext = Convert.base64Url(ciphertextB64U).toUint8Array();
|
|
241
160
|
const authenticationTag = Convert.base64Url(authenticationTagB64U).toUint8Array();
|
|
242
|
-
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
...authenticationTag,
|
|
246
|
-
]);
|
|
247
|
-
// decrypt using the sharedKey
|
|
248
|
-
const decryptedJwtBytes = yield XChaCha20Poly1305.decryptRaw({ data: ciphertextAndTag, keyBytes: sharedKey, nonce, additionalData: AAD });
|
|
249
|
-
const jwt = Convert.uint8Array(decryptedJwtBytes).toString();
|
|
250
|
-
return jwt;
|
|
161
|
+
const ciphertextAndTag = new Uint8Array([...ciphertext, ...authenticationTag]);
|
|
162
|
+
const decryptedJwtBytes = yield XChaCha20Poly1305.decryptRaw({ data: ciphertextAndTag, keyBytes: encryptionKeyBytes, nonce, additionalData });
|
|
163
|
+
return Convert.uint8Array(decryptedJwtBytes).toString();
|
|
251
164
|
});
|
|
252
165
|
}
|
|
253
|
-
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
// Encryption: response (ECDH shared key + optional PIN)
|
|
168
|
+
// ---------------------------------------------------------------------------
|
|
169
|
+
/** Derives a shared ECDH key for encrypting/decrypting the connect response. */
|
|
254
170
|
function deriveSharedKey(privateKeyDid, publicKeyDid) {
|
|
255
171
|
return __awaiter(this, void 0, void 0, function* () {
|
|
256
172
|
var _a, _b;
|
|
@@ -258,34 +174,32 @@ function deriveSharedKey(privateKeyDid, publicKeyDid) {
|
|
|
258
174
|
const publicJwk = (_a = publicKeyDid.verificationMethod) === null || _a === void 0 ? void 0 : _a[0].publicKeyJwk;
|
|
259
175
|
const privateJwk = (_b = privatePortableDid.privateKeys) === null || _b === void 0 ? void 0 : _b[0];
|
|
260
176
|
publicJwk.alg = 'EdDSA';
|
|
261
|
-
const publicX25519 = yield Ed25519.convertPublicKeyToX25519({
|
|
262
|
-
|
|
263
|
-
});
|
|
264
|
-
const privateX25519 = yield Ed25519.convertPrivateKeyToX25519({
|
|
265
|
-
privateKey: privateJwk,
|
|
266
|
-
});
|
|
177
|
+
const publicX25519 = yield Ed25519.convertPublicKeyToX25519({ publicKey: publicJwk });
|
|
178
|
+
const privateX25519 = yield Ed25519.convertPrivateKeyToX25519({ privateKey: privateJwk });
|
|
267
179
|
const sharedKey = yield X25519.sharedSecret({
|
|
268
180
|
privateKeyA: privateX25519,
|
|
269
181
|
publicKeyB: publicX25519,
|
|
270
182
|
});
|
|
271
|
-
|
|
183
|
+
return Hkdf.deriveKeyBytes({
|
|
272
184
|
baseKeyBytes: new Uint8Array(sharedKey),
|
|
273
185
|
hash: 'SHA-256',
|
|
274
186
|
salt: new Uint8Array(),
|
|
275
187
|
info: new Uint8Array(),
|
|
276
188
|
length: 256,
|
|
277
189
|
});
|
|
278
|
-
return sharedEncryptionKey;
|
|
279
190
|
});
|
|
280
191
|
}
|
|
281
192
|
/**
|
|
282
|
-
* Encrypts the
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
*
|
|
193
|
+
* Encrypts the connect response JWT.
|
|
194
|
+
*
|
|
195
|
+
* For remote (relay-mediated) flows, `pin` is required — it is added to the
|
|
196
|
+
* AAD to prevent MITM attacks via the untrusted relay.
|
|
197
|
+
*
|
|
198
|
+
* For local (same-device) flows, `pin` may be omitted — the ECDH encryption
|
|
199
|
+
* alone is sufficient when there is no untrusted intermediary.
|
|
286
200
|
*/
|
|
287
|
-
function
|
|
288
|
-
return __awaiter(this, arguments, void 0, function* ({ jwt, encryptionKey, delegateDidKeyId,
|
|
201
|
+
function encryptResponse(_a) {
|
|
202
|
+
return __awaiter(this, arguments, void 0, function* ({ jwt, encryptionKey, delegateDidKeyId, pin, }) {
|
|
289
203
|
const protectedHeader = {
|
|
290
204
|
alg: 'dir',
|
|
291
205
|
cty: 'JWT',
|
|
@@ -294,60 +208,117 @@ function encryptAuthResponse(_a) {
|
|
|
294
208
|
kid: delegateDidKeyId,
|
|
295
209
|
};
|
|
296
210
|
const nonce = CryptoUtils.randomBytes(24);
|
|
297
|
-
|
|
211
|
+
// Build AAD — include PIN if provided (remote flows).
|
|
212
|
+
const aadObject = pin
|
|
213
|
+
? Object.assign(Object.assign({}, protectedHeader), { pin }) : Object.assign({}, protectedHeader);
|
|
214
|
+
const additionalData = Convert.object(aadObject).toUint8Array();
|
|
298
215
|
const jwtBytes = Convert.string(jwt).toUint8Array();
|
|
299
216
|
const ciphertextAndTag = yield XChaCha20Poly1305.encryptRaw({ data: jwtBytes, keyBytes: encryptionKey, nonce, additionalData });
|
|
300
|
-
/** The cipher output concatenates the encrypted data and tag
|
|
301
|
-
* so we need to extract the values for use in the JWE. */
|
|
302
217
|
const ciphertext = ciphertextAndTag.subarray(0, -16);
|
|
303
218
|
const authenticationTag = ciphertextAndTag.subarray(-16);
|
|
304
|
-
|
|
219
|
+
return [
|
|
305
220
|
Convert.object(protectedHeader).toBase64Url(),
|
|
306
|
-
'', //
|
|
221
|
+
'', // No wrapped key (direct encryption).
|
|
307
222
|
Convert.uint8Array(nonce).toBase64Url(),
|
|
308
223
|
Convert.uint8Array(ciphertext).toBase64Url(),
|
|
309
224
|
Convert.uint8Array(authenticationTag).toBase64Url(),
|
|
310
225
|
].join('.');
|
|
311
|
-
return compactJwe;
|
|
312
226
|
});
|
|
313
227
|
}
|
|
228
|
+
/**
|
|
229
|
+
* Decrypts the connect response JWE using ECDH + optional PIN.
|
|
230
|
+
*
|
|
231
|
+
* @param clientDid - The ephemeral DID used at connect initiation (for ECDH).
|
|
232
|
+
* @param jwe - The encrypted response JWE.
|
|
233
|
+
* @param pin - The PIN entered by the user (required for remote flows, omit for local).
|
|
234
|
+
*/
|
|
235
|
+
function decryptResponse(clientDid, jwe, pin) {
|
|
236
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
237
|
+
const [protectedHeaderB64U, , nonceB64U, ciphertextB64U, authenticationTagB64U,] = jwe.split('.');
|
|
238
|
+
const header = Convert.base64Url(protectedHeaderB64U).toObject();
|
|
239
|
+
if (!header.kid) {
|
|
240
|
+
throw new Error('Connect: JWE protected header is missing required "kid" property.');
|
|
241
|
+
}
|
|
242
|
+
const delegateResolvedDid = yield DidJwk.resolve(header.kid.split('#')[0]);
|
|
243
|
+
const sharedKey = yield EnboxConnectProtocol.deriveSharedKey(clientDid, delegateResolvedDid.didDocument);
|
|
244
|
+
// Build AAD — include PIN if provided (must match what was used during encryption).
|
|
245
|
+
const aadObject = pin
|
|
246
|
+
? Object.assign(Object.assign({}, header), { pin }) : Object.assign({}, header);
|
|
247
|
+
const AAD = Convert.object(aadObject).toUint8Array();
|
|
248
|
+
const nonce = Convert.base64Url(nonceB64U).toUint8Array();
|
|
249
|
+
const ciphertext = Convert.base64Url(ciphertextB64U).toUint8Array();
|
|
250
|
+
const authenticationTag = Convert.base64Url(authenticationTagB64U).toUint8Array();
|
|
251
|
+
const ciphertextAndTag = new Uint8Array([...ciphertext, ...authenticationTag]);
|
|
252
|
+
const decryptedJwtBytes = yield XChaCha20Poly1305.decryptRaw({ data: ciphertextAndTag, keyBytes: sharedKey, nonce, additionalData: AAD });
|
|
253
|
+
return Convert.uint8Array(decryptedJwtBytes).toString();
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
// ---------------------------------------------------------------------------
|
|
257
|
+
// Request creation and retrieval
|
|
258
|
+
// ---------------------------------------------------------------------------
|
|
259
|
+
/** Creates an {@link EnboxConnectRequest}. */
|
|
260
|
+
function createConnectRequest(options) {
|
|
261
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
262
|
+
var _a;
|
|
263
|
+
const stateBytes = CryptoUtils.randomBytes(16);
|
|
264
|
+
const nonceBytes = CryptoUtils.randomBytes(16);
|
|
265
|
+
return Object.assign(Object.assign({}, options), { nonce: Convert.uint8Array(nonceBytes).toBase64Url(), responseMode: 'direct_post', state: Convert.uint8Array(stateBytes).toBase64Url(), supportedDidMethods: (_a = options.supportedDidMethods) !== null && _a !== void 0 ? _a : ['did:dht', 'did:jwk'] });
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Fetches an encrypted connect request from the authorize endpoint
|
|
270
|
+
* and decrypts it using the encryption key from the QR/deep link.
|
|
271
|
+
*/
|
|
272
|
+
function getConnectRequest(requestUri, encryptionKey) {
|
|
273
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
274
|
+
const response = yield fetch(requestUri, { signal: AbortSignal.timeout(30000) });
|
|
275
|
+
const jwe = yield response.text();
|
|
276
|
+
const jwt = yield decryptRequest({ jwe, encryptionKey });
|
|
277
|
+
return (yield verifyJwt({ jwt }));
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
// ---------------------------------------------------------------------------
|
|
281
|
+
// Response creation
|
|
282
|
+
// ---------------------------------------------------------------------------
|
|
283
|
+
/** Creates an {@link EnboxConnectResponse} with timestamps. */
|
|
284
|
+
function createConnectResponse(options) {
|
|
285
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
286
|
+
const currentTimeInSeconds = Math.floor(Date.now() / 1000);
|
|
287
|
+
return Object.assign(Object.assign({}, options), { iat: currentTimeInSeconds, exp: currentTimeInSeconds + 600 });
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
// ---------------------------------------------------------------------------
|
|
291
|
+
// Permission grants
|
|
292
|
+
// ---------------------------------------------------------------------------
|
|
314
293
|
function shouldUseDelegatePermission(scope) {
|
|
315
|
-
// Currently all record permissions are treated as delegated permissions
|
|
316
|
-
// In the future only methods that modify state will be delegated and the rest will be normal permissions
|
|
317
294
|
if (isRecordPermissionScope(scope)) {
|
|
318
295
|
return true;
|
|
319
296
|
}
|
|
320
297
|
else if (scope.interface === DwnInterfaceName.Protocols && scope.method === DwnMethodName.Configure) {
|
|
321
|
-
// ProtocolConfigure messages are also delegated, as they modify state
|
|
322
298
|
return true;
|
|
323
299
|
}
|
|
324
|
-
// All other permissions are not treated as delegated
|
|
325
300
|
return false;
|
|
326
301
|
}
|
|
327
302
|
/**
|
|
328
|
-
* Creates
|
|
329
|
-
* permissions that the web app requested in the {@link EnboxConnectAuthRequest}
|
|
303
|
+
* Creates permission grants that assign the requested scopes to a delegate DID.
|
|
330
304
|
*/
|
|
331
305
|
function createPermissionGrants(selectedDid, delegateBearerDid, agent, scopes) {
|
|
332
306
|
return __awaiter(this, void 0, void 0, function* () {
|
|
333
307
|
const permissionsApi = new AgentPermissionsApi({ agent });
|
|
334
|
-
|
|
335
|
-
logger.log(`Creating permission grants for ${scopes.length} scopes given...`);
|
|
308
|
+
logger.log(`Creating permission grants for ${scopes.length} scopes...`);
|
|
336
309
|
const permissionGrants = yield Promise.all(scopes.map((scope) => {
|
|
337
|
-
// check if the scope is a records permission scope, or a protocol configure scope, if so it should use a delegated permission.
|
|
338
310
|
const delegated = shouldUseDelegatePermission(scope);
|
|
339
311
|
return permissionsApi.createGrant({
|
|
340
312
|
delegated,
|
|
341
313
|
store: true,
|
|
342
314
|
grantedTo: delegateBearerDid.uri,
|
|
343
315
|
scope,
|
|
344
|
-
dateExpires: '2040-06-25T16:09:16.693356Z', // TODO: make dateExpires
|
|
316
|
+
dateExpires: '2040-06-25T16:09:16.693356Z', // TODO: make dateExpires configurable
|
|
345
317
|
author: selectedDid,
|
|
346
318
|
});
|
|
347
319
|
}));
|
|
348
320
|
logger.log(`Sending ${permissionGrants.length} permission grants to remote DWN...`);
|
|
349
321
|
const messagePromises = permissionGrants.map((grant) => __awaiter(this, void 0, void 0, function* () {
|
|
350
|
-
// Quirk: we have to pull out encodedData out of the message the schema validator doesn't want it there
|
|
351
322
|
const _a = grant.message, { encodedData } = _a, rawMessage = __rest(_a, ["encodedData"]);
|
|
352
323
|
const data = Convert.base64Url(encodedData).toUint8Array();
|
|
353
324
|
const { reply } = yield agent.sendDwnRequest({
|
|
@@ -357,17 +328,14 @@ function createPermissionGrants(selectedDid, delegateBearerDid, agent, scopes) {
|
|
|
357
328
|
dataStream: new Blob([data]),
|
|
358
329
|
rawMessage,
|
|
359
330
|
});
|
|
360
|
-
// check if the message was sent successfully, if the remote returns 409 the message may have come through already via sync
|
|
361
331
|
if (reply.status.code !== 202 && reply.status.code !== 409) {
|
|
362
332
|
logger.error(`Error sending RecordsWrite: ${reply.status.detail}`);
|
|
363
|
-
|
|
364
|
-
throw new Error(`Could not send the message. Error details: ${reply.status.detail}`);
|
|
333
|
+
throw new Error(`Could not send permission grant. Error: ${reply.status.detail}`);
|
|
365
334
|
}
|
|
366
335
|
return grant.message;
|
|
367
336
|
}));
|
|
368
337
|
try {
|
|
369
|
-
|
|
370
|
-
return messages;
|
|
338
|
+
return yield Promise.all(messagePromises);
|
|
371
339
|
}
|
|
372
340
|
catch (error) {
|
|
373
341
|
logger.error(`Error during batch-send of permission grants: ${error}`);
|
|
@@ -375,8 +343,12 @@ function createPermissionGrants(selectedDid, delegateBearerDid, agent, scopes) {
|
|
|
375
343
|
}
|
|
376
344
|
});
|
|
377
345
|
}
|
|
346
|
+
// ---------------------------------------------------------------------------
|
|
347
|
+
// Protocol installation
|
|
348
|
+
// ---------------------------------------------------------------------------
|
|
378
349
|
/**
|
|
379
|
-
* Installs
|
|
350
|
+
* Installs a DWN protocol on the provider's DWN if it doesn't already exist.
|
|
351
|
+
* Ensures the protocol is available on both the local and remote DWN.
|
|
380
352
|
*/
|
|
381
353
|
function prepareProtocol(selectedDid, agent, protocolDefinition) {
|
|
382
354
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -387,23 +359,19 @@ function prepareProtocol(selectedDid, agent, protocolDefinition) {
|
|
|
387
359
|
messageParams: { filter: { protocol: protocolDefinition.protocol } },
|
|
388
360
|
});
|
|
389
361
|
if (queryMessage.reply.status.code !== 200) {
|
|
390
|
-
// if the query failed, throw an error
|
|
391
362
|
throw new Error(`Could not fetch protocol: ${queryMessage.reply.status.detail}`);
|
|
392
363
|
}
|
|
393
364
|
else if (queryMessage.reply.entries === undefined || queryMessage.reply.entries.length === 0) {
|
|
394
365
|
logger.log(`Protocol does not exist, creating: ${protocolDefinition.protocol}`);
|
|
395
|
-
// send the protocol definition to the remote DWN first, if it passes we can process it locally
|
|
396
366
|
const { reply: sendReply, message: configureMessage } = yield agent.sendDwnRequest({
|
|
397
367
|
author: selectedDid,
|
|
398
368
|
target: selectedDid,
|
|
399
369
|
messageType: DwnInterface.ProtocolsConfigure,
|
|
400
370
|
messageParams: { definition: protocolDefinition },
|
|
401
371
|
});
|
|
402
|
-
// check if the message was sent successfully, if the remote returns 409 the message may have come through already via sync
|
|
403
372
|
if (sendReply.status.code !== 202 && sendReply.status.code !== 409) {
|
|
404
373
|
throw new Error(`Could not send protocol: ${sendReply.status.detail}`);
|
|
405
374
|
}
|
|
406
|
-
// process the protocol locally, we don't have to check if it exists as this is just a convenience over waiting for sync.
|
|
407
375
|
yield agent.processDwnRequest({
|
|
408
376
|
author: selectedDid,
|
|
409
377
|
target: selectedDid,
|
|
@@ -413,7 +381,6 @@ function prepareProtocol(selectedDid, agent, protocolDefinition) {
|
|
|
413
381
|
}
|
|
414
382
|
else {
|
|
415
383
|
logger.log(`Protocol already exists: ${protocolDefinition.protocol}`);
|
|
416
|
-
// the protocol already exists, let's make sure it exists on the remote DWN as the requesting app will need it
|
|
417
384
|
const configureMessage = queryMessage.reply.entries[0];
|
|
418
385
|
const { reply: sendReply } = yield agent.sendDwnRequest({
|
|
419
386
|
author: selectedDid,
|
|
@@ -427,66 +394,65 @@ function prepareProtocol(selectedDid, agent, protocolDefinition) {
|
|
|
427
394
|
}
|
|
428
395
|
});
|
|
429
396
|
}
|
|
397
|
+
// ---------------------------------------------------------------------------
|
|
398
|
+
// Full wallet-side flow (provider submits response)
|
|
399
|
+
// ---------------------------------------------------------------------------
|
|
430
400
|
/**
|
|
431
|
-
*
|
|
432
|
-
*
|
|
433
|
-
*
|
|
434
|
-
*
|
|
435
|
-
*
|
|
436
|
-
*
|
|
437
|
-
*
|
|
401
|
+
* Executes the full wallet-side (provider) flow:
|
|
402
|
+
* 1. Creates a delegate DID
|
|
403
|
+
* 2. Installs requested protocols
|
|
404
|
+
* 3. Creates permission grants
|
|
405
|
+
* 4. Builds, signs, and encrypts the response
|
|
406
|
+
* 5. POSTs the encrypted response to the callback URL
|
|
407
|
+
*
|
|
408
|
+
* @param selectedDid - The provider's DID that is granting access.
|
|
409
|
+
* @param connectRequest - The decoded connect request from the app.
|
|
410
|
+
* @param pin - The PIN for response encryption AAD (required for remote flows).
|
|
411
|
+
* @param agent - The agent instance for DWN operations.
|
|
438
412
|
*/
|
|
439
|
-
function
|
|
413
|
+
function submitConnectResponse(selectedDid, connectRequest, pin, agent) {
|
|
440
414
|
return __awaiter(this, void 0, void 0, function* () {
|
|
441
415
|
const delegateBearerDid = yield DidJwk.create();
|
|
442
416
|
const delegatePortableDid = yield delegateBearerDid.export();
|
|
443
|
-
|
|
444
|
-
const delegateGrantPromises = authRequest.permissionRequests.map((permissionRequest) => __awaiter(this, void 0, void 0, function* () {
|
|
417
|
+
const delegateGrantPromises = connectRequest.permissionRequests.map((permissionRequest) => __awaiter(this, void 0, void 0, function* () {
|
|
445
418
|
const { protocolDefinition, permissionScopes } = permissionRequest;
|
|
446
|
-
// We validate that all permission scopes match the protocol uri of the protocol definition they are provided with.
|
|
447
419
|
const grantsMatchProtocolUri = permissionScopes.every(scope => 'protocol' in scope && scope.protocol === protocolDefinition.protocol);
|
|
448
420
|
if (!grantsMatchProtocolUri) {
|
|
449
|
-
throw new Error('All permission scopes must match the protocol
|
|
421
|
+
throw new Error('All permission scopes must match the protocol URI they are provided with.');
|
|
450
422
|
}
|
|
451
423
|
yield prepareProtocol(selectedDid, agent, protocolDefinition);
|
|
452
|
-
|
|
453
|
-
return permissionGrants;
|
|
424
|
+
return EnboxConnectProtocol.createPermissionGrants(selectedDid, delegateBearerDid, agent, permissionScopes);
|
|
454
425
|
}));
|
|
455
426
|
const delegateGrants = (yield Promise.all(delegateGrantPromises)).flat();
|
|
456
|
-
logger.log('
|
|
457
|
-
const responseObject = yield
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
//* the client's temporary ephemeral did used for connect
|
|
463
|
-
aud: authRequest.client_id,
|
|
464
|
-
//* the nonce of the original auth request
|
|
465
|
-
nonce: authRequest.nonce,
|
|
427
|
+
logger.log('Building connect response...');
|
|
428
|
+
const responseObject = yield EnboxConnectProtocol.createConnectResponse({
|
|
429
|
+
providerDid: selectedDid,
|
|
430
|
+
delegateDid: delegateBearerDid.uri,
|
|
431
|
+
aud: connectRequest.clientDid,
|
|
432
|
+
nonce: connectRequest.nonce,
|
|
466
433
|
delegateGrants,
|
|
467
434
|
delegatePortableDid,
|
|
468
435
|
});
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
const responseObjectJwt = yield Oidc.signJwt({
|
|
436
|
+
logger.log('Signing connect response...');
|
|
437
|
+
const responseObjectJwt = yield EnboxConnectProtocol.signJwt({
|
|
472
438
|
did: delegateBearerDid,
|
|
473
439
|
data: responseObject,
|
|
474
440
|
});
|
|
475
|
-
const clientDid = yield DidJwk.resolve(
|
|
476
|
-
const sharedKey = yield
|
|
477
|
-
logger.log('Encrypting
|
|
478
|
-
const encryptedResponse = yield
|
|
441
|
+
const clientDid = yield DidJwk.resolve(connectRequest.clientDid);
|
|
442
|
+
const sharedKey = yield EnboxConnectProtocol.deriveSharedKey(delegateBearerDid, clientDid === null || clientDid === void 0 ? void 0 : clientDid.didDocument);
|
|
443
|
+
logger.log('Encrypting connect response...');
|
|
444
|
+
const encryptedResponse = yield EnboxConnectProtocol.encryptResponse({
|
|
479
445
|
jwt: responseObjectJwt,
|
|
480
446
|
encryptionKey: sharedKey,
|
|
481
447
|
delegateDidKeyId: delegateBearerDid.document.verificationMethod[0].id,
|
|
482
|
-
|
|
448
|
+
pin,
|
|
483
449
|
});
|
|
484
450
|
const formEncodedRequest = new URLSearchParams({
|
|
485
451
|
id_token: encryptedResponse,
|
|
486
|
-
state:
|
|
452
|
+
state: connectRequest.state,
|
|
487
453
|
}).toString();
|
|
488
|
-
logger.log(`Sending
|
|
489
|
-
yield fetch(
|
|
454
|
+
logger.log(`Sending connect response to: ${connectRequest.callbackUrl}`);
|
|
455
|
+
yield fetch(connectRequest.callbackUrl, {
|
|
490
456
|
body: formEncodedRequest,
|
|
491
457
|
method: 'POST',
|
|
492
458
|
headers: {
|
|
@@ -496,20 +462,22 @@ function submitAuthResponse(selectedDid, authRequest, randomPin, agent) {
|
|
|
496
462
|
});
|
|
497
463
|
});
|
|
498
464
|
}
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
createPermissionGrants,
|
|
505
|
-
createResponseObject,
|
|
506
|
-
encryptAuthResponse,
|
|
507
|
-
decryptAuthResponse,
|
|
508
|
-
deriveSharedKey,
|
|
465
|
+
// ---------------------------------------------------------------------------
|
|
466
|
+
// Namespace export
|
|
467
|
+
// ---------------------------------------------------------------------------
|
|
468
|
+
export const EnboxConnectProtocol = {
|
|
469
|
+
buildConnectUrl,
|
|
509
470
|
signJwt,
|
|
510
471
|
verifyJwt,
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
472
|
+
encryptRequest,
|
|
473
|
+
decryptRequest,
|
|
474
|
+
encryptResponse,
|
|
475
|
+
decryptResponse,
|
|
476
|
+
deriveSharedKey,
|
|
477
|
+
createConnectRequest,
|
|
478
|
+
getConnectRequest,
|
|
479
|
+
createConnectResponse,
|
|
480
|
+
createPermissionGrants,
|
|
481
|
+
submitConnectResponse,
|
|
514
482
|
};
|
|
515
|
-
//# sourceMappingURL=
|
|
483
|
+
//# sourceMappingURL=enbox-connect-protocol.js.map
|