@reclaimprotocol/attestor-core 5.0.1-beta.21 → 5.0.1-beta.22

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.
Files changed (132) hide show
  1. package/browser/resources/attestor-browser.min.mjs +9 -9
  2. package/lib/avs/abis/avsDirectoryABI.js +340 -0
  3. package/lib/avs/abis/delegationABI.js +1 -0
  4. package/lib/avs/abis/registryABI.js +725 -0
  5. package/lib/avs/client/create-claim-on-avs.js +140 -0
  6. package/lib/avs/config.js +20 -0
  7. package/lib/avs/contracts/factories/ReclaimServiceManager__factory.js +1166 -0
  8. package/lib/avs/contracts/factories/index.js +4 -0
  9. package/lib/avs/contracts/index.js +2 -0
  10. package/lib/avs/utils/contracts.js +33 -0
  11. package/lib/avs/utils/register.js +79 -0
  12. package/lib/avs/utils/tasks.js +41 -0
  13. package/lib/client/create-claim.js +432 -0
  14. package/lib/client/index.js +3 -0
  15. package/lib/client/tunnels/make-rpc-tcp-tunnel.js +51 -0
  16. package/lib/client/tunnels/make-rpc-tls-tunnel.js +131 -0
  17. package/lib/client/utils/attestor-pool.js +25 -0
  18. package/lib/client/utils/client-socket.js +97 -0
  19. package/lib/client/utils/message-handler.js +87 -0
  20. package/lib/config/index.js +44 -0
  21. package/lib/external-rpc/benchmark.js +69 -0
  22. package/lib/external-rpc/event-bus.js +14 -0
  23. package/lib/external-rpc/handle-incoming-msg.js +232 -0
  24. package/lib/external-rpc/index.js +3 -10399
  25. package/lib/external-rpc/jsc-polyfills/1.js +82 -0
  26. package/lib/external-rpc/jsc-polyfills/2.js +20 -0
  27. package/lib/external-rpc/jsc-polyfills/event.js +14 -0
  28. package/lib/external-rpc/jsc-polyfills/index.js +2 -0
  29. package/lib/external-rpc/jsc-polyfills/ws.js +81 -0
  30. package/lib/external-rpc/setup-browser.js +33 -0
  31. package/lib/external-rpc/setup-jsc.js +22 -0
  32. package/lib/external-rpc/types.js +1 -0
  33. package/lib/external-rpc/utils.js +100 -0
  34. package/lib/external-rpc/zk.js +63 -0
  35. package/lib/index.js +9 -8326
  36. package/lib/mechain/abis/governanceABI.js +458 -0
  37. package/lib/mechain/abis/taskABI.js +509 -0
  38. package/lib/mechain/client/create-claim-on-mechain.js +28 -0
  39. package/lib/mechain/client/index.js +1 -0
  40. package/lib/mechain/constants/index.js +3 -0
  41. package/lib/mechain/index.js +2 -0
  42. package/lib/proto/api.js +4363 -0
  43. package/lib/proto/tee-bundle.js +1316 -0
  44. package/lib/providers/http/index.js +653 -0
  45. package/lib/providers/http/patch-parse5-tree.js +32 -0
  46. package/lib/providers/http/utils.js +324 -0
  47. package/lib/providers/index.js +4 -0
  48. package/lib/server/create-server.js +103 -0
  49. package/lib/server/handlers/claimTeeBundle.js +252 -0
  50. package/lib/server/handlers/claimTunnel.js +73 -0
  51. package/lib/server/handlers/completeClaimOnChain.js +24 -0
  52. package/lib/server/handlers/createClaimOnChain.js +26 -0
  53. package/lib/server/handlers/createTaskOnMechain.js +47 -0
  54. package/lib/server/handlers/createTunnel.js +93 -0
  55. package/lib/server/handlers/disconnectTunnel.js +5 -0
  56. package/lib/server/handlers/fetchCertificateBytes.js +41 -0
  57. package/lib/server/handlers/index.js +22 -0
  58. package/lib/server/handlers/init.js +32 -0
  59. package/lib/server/handlers/toprf.js +16 -0
  60. package/lib/server/index.js +4 -0
  61. package/lib/server/socket.js +109 -0
  62. package/lib/server/tunnels/make-tcp-tunnel.js +177 -0
  63. package/lib/server/utils/apm.js +36 -0
  64. package/lib/server/utils/assert-valid-claim-request.js +325 -0
  65. package/lib/server/utils/config-env.js +4 -0
  66. package/lib/server/utils/dns.js +18 -0
  67. package/lib/server/utils/gcp-attestation.js +289 -0
  68. package/lib/server/utils/generics.d.ts +1 -1
  69. package/lib/server/utils/generics.js +51 -0
  70. package/lib/server/utils/iso.js +256 -0
  71. package/lib/server/utils/keep-alive.js +38 -0
  72. package/lib/server/utils/nitro-attestation.js +324 -0
  73. package/lib/server/utils/oprf-raw.js +54 -0
  74. package/lib/server/utils/process-handshake.js +215 -0
  75. package/lib/server/utils/proxy-session.js +6 -0
  76. package/lib/server/utils/tee-oprf-mpc-verification.js +90 -0
  77. package/lib/server/utils/tee-oprf-verification.js +174 -0
  78. package/lib/server/utils/tee-transcript-reconstruction.js +187 -0
  79. package/lib/server/utils/tee-verification.js +421 -0
  80. package/lib/server/utils/validation.js +38 -0
  81. package/lib/types/bgp.js +1 -0
  82. package/lib/types/claims.js +1 -0
  83. package/lib/types/client.js +1 -0
  84. package/lib/types/general.js +1 -0
  85. package/lib/types/handlers.js +1 -0
  86. package/lib/types/index.js +10 -0
  87. package/lib/types/providers.d.ts +3 -2
  88. package/lib/types/providers.gen.js +10 -0
  89. package/lib/types/providers.js +1 -0
  90. package/lib/types/rpc.js +1 -0
  91. package/lib/types/signatures.d.ts +1 -2
  92. package/lib/types/signatures.js +1 -0
  93. package/lib/types/tunnel.js +1 -0
  94. package/lib/types/zk.js +1 -0
  95. package/lib/utils/auth.js +59 -0
  96. package/lib/utils/b64-json.js +17 -0
  97. package/lib/utils/bgp-listener.js +119 -0
  98. package/lib/utils/claims.js +98 -0
  99. package/lib/utils/env.js +15 -0
  100. package/lib/utils/error.js +50 -0
  101. package/lib/utils/generics.js +317 -0
  102. package/lib/utils/http-parser.js +246 -0
  103. package/lib/utils/index.js +13 -0
  104. package/lib/utils/logger.js +91 -0
  105. package/lib/utils/prepare-packets.js +71 -0
  106. package/lib/utils/redactions.js +177 -0
  107. package/lib/utils/retries.js +24 -0
  108. package/lib/utils/signatures/eth.js +32 -0
  109. package/lib/utils/signatures/index.js +7 -0
  110. package/lib/utils/socket-base.js +92 -0
  111. package/lib/utils/tls.js +58 -0
  112. package/lib/utils/ws.js +22 -0
  113. package/lib/utils/zk.js +585 -0
  114. package/package.json +5 -3
  115. package/lib/scripts/check-avs-registration.d.ts +0 -1
  116. package/lib/scripts/fallbacks/crypto.d.ts +0 -1
  117. package/lib/scripts/fallbacks/empty.d.ts +0 -3
  118. package/lib/scripts/fallbacks/re2.d.ts +0 -1
  119. package/lib/scripts/fallbacks/snarkjs.d.ts +0 -1
  120. package/lib/scripts/fallbacks/stwo.d.ts +0 -6
  121. package/lib/scripts/generate-provider-types.d.ts +0 -5
  122. package/lib/scripts/generate-receipt.d.ts +0 -9
  123. package/lib/scripts/jsc-cli-rpc.d.ts +0 -1
  124. package/lib/scripts/register-avs-operator.d.ts +0 -1
  125. package/lib/scripts/start-server.d.ts +0 -1
  126. package/lib/scripts/update-avs-metadata.d.ts +0 -1
  127. package/lib/scripts/utils.d.ts +0 -1
  128. package/lib/scripts/whitelist-operator.d.ts +0 -1
  129. /package/lib/{scripts/build-browser.d.ts → avs/contracts/ReclaimServiceManager.js} +0 -0
  130. /package/lib/{scripts/build-jsc.d.ts → avs/contracts/common.js} +0 -0
  131. /package/lib/{scripts/build-lib.d.ts → avs/types/index.js} +0 -0
  132. /package/lib/{scripts/generate-toprf-keys.d.ts → mechain/types/index.js} +0 -0
@@ -0,0 +1,119 @@
1
+ import CIDR from 'ip-cidr';
2
+ import { BGP_WS_URL } from "../config/index.js";
3
+ import { makeWebSocket } from "./ws.js";
4
+ const ANNOUNCEMENT_OVERLAP = 'announcement-overlap';
5
+ class BGPAnnouncementOverlapEvent extends Event {
6
+ data;
7
+ constructor(data) {
8
+ super(ANNOUNCEMENT_OVERLAP);
9
+ this.data = data;
10
+ }
11
+ }
12
+ /**
13
+ * Listens for BGP announcements and emits events whenever
14
+ * an announcement overlaps with a target IP.
15
+ */
16
+ export function createBgpListener(logger) {
17
+ let ws;
18
+ let closed = false;
19
+ const targetIps = new Set();
20
+ const eventTarget = new EventTarget();
21
+ openWs();
22
+ return {
23
+ onOverlap(ips, callback) {
24
+ for (const ip of ips) {
25
+ targetIps.add(ip);
26
+ }
27
+ eventTarget.addEventListener(ANNOUNCEMENT_OVERLAP, _callback);
28
+ return () => {
29
+ for (const ip of ips) {
30
+ targetIps.delete(ip);
31
+ }
32
+ eventTarget.removeEventListener(ANNOUNCEMENT_OVERLAP, _callback);
33
+ };
34
+ function _callback(event) {
35
+ callback(event.data);
36
+ }
37
+ },
38
+ close() {
39
+ ws.onclose = null;
40
+ ws.onerror = null;
41
+ ws.close();
42
+ closed = true;
43
+ }
44
+ };
45
+ function openWs() {
46
+ logger.debug('connecting to BGP websocket');
47
+ ws = makeWebSocket(BGP_WS_URL);
48
+ ws.onopen = onOpen;
49
+ ws.onerror = (ev) => onClose(ev);
50
+ ws.onclose = () => onClose(new Error('Unexpected close'));
51
+ ws.onmessage = ({ data }) => {
52
+ const str = typeof data === 'string' ? data : data.toString();
53
+ try {
54
+ onMessage(str);
55
+ }
56
+ catch (err) {
57
+ logger.error({ data, err }, 'error processing BGP message');
58
+ }
59
+ };
60
+ }
61
+ function onOpen() {
62
+ const subscriptionMessage = {
63
+ type: 'ris_subscribe',
64
+ data: {
65
+ type: 'UPDATE',
66
+ },
67
+ };
68
+ ws.send(JSON.stringify(subscriptionMessage));
69
+ logger.info('connected to BGP websocket');
70
+ }
71
+ function onClose(err) {
72
+ if (closed) {
73
+ return;
74
+ }
75
+ logger.info({ err }, 'BGP websocket closed');
76
+ if (!err) {
77
+ return;
78
+ }
79
+ logger.info('reconnecting to BGP websocket');
80
+ openWs();
81
+ }
82
+ function onMessage(message) {
83
+ const data = JSON.parse(message);
84
+ const announcements = data?.data?.announcements;
85
+ logger.trace({ data }, 'got BGP update');
86
+ if (!Array.isArray(announcements)) {
87
+ return;
88
+ }
89
+ const asPath = data?.data?.path;
90
+ for (const announcement of announcements) {
91
+ const prefixes = announcement?.prefixes;
92
+ const nextHop = announcement?.['next_hop'];
93
+ const hasPrefixes = prefixes?.length && (nextHop || asPath);
94
+ if (!hasPrefixes) {
95
+ return;
96
+ }
97
+ for (const prefix of prefixes) {
98
+ if (!overlapsTargetIps(prefix)) {
99
+ continue;
100
+ }
101
+ // emit event
102
+ eventTarget.dispatchEvent(new BGPAnnouncementOverlapEvent({ prefix }));
103
+ }
104
+ }
105
+ }
106
+ function overlapsTargetIps(prefix) {
107
+ // ignore all prefixes that end with /0
108
+ if (prefix.endsWith('/0')) {
109
+ return false;
110
+ }
111
+ const cidr = new CIDR(prefix);
112
+ for (const ip of targetIps) {
113
+ if (cidr.contains(ip)) {
114
+ return true;
115
+ }
116
+ }
117
+ return false;
118
+ }
119
+ }
@@ -0,0 +1,98 @@
1
+ import canonicalize from 'canonicalize';
2
+ import { keccak256 } from 'ethers';
3
+ import { DEFAULT_METADATA } from "../config/index.js";
4
+ import { ClaimTunnelResponse } from "../proto/api.js";
5
+ import { SIGNATURES, strToUint8Array } from "../..";
6
+ /**
7
+ * Creates the standard string to sign for a claim.
8
+ * This data is what the attestor will sign when it successfully
9
+ * verifies a claim.
10
+ */
11
+ export function createSignDataForClaim(data) {
12
+ const lines = [
13
+ getIdentifierFromClaimInfo(data),
14
+ // we lowercase the owner to ensure that the
15
+ // ETH addresses always serialize the same way
16
+ data.owner.toLowerCase(),
17
+ data.timestampS.toString(),
18
+ data.epoch.toString(),
19
+ ];
20
+ return lines.join('\n');
21
+ }
22
+ /**
23
+ * Verify the claim tunnel response from a attestor.
24
+ *
25
+ * If you'd only like to verify the claim signature, you can
26
+ * optionally only pass "claim" & "signatures.claimSignature"
27
+ * to this function.
28
+ *
29
+ * The successful run of this function means that the claim
30
+ * is valid, and the attestor that signed the claim is valid.
31
+ */
32
+ export async function assertValidClaimSignatures({ signatures, ...res }, metadata = DEFAULT_METADATA) {
33
+ if (!signatures) {
34
+ throw new Error('No signatures provided');
35
+ }
36
+ const { resultSignature, claimSignature, attestorAddress } = signatures;
37
+ const { verify } = SIGNATURES[metadata.signatureType];
38
+ if (signatures?.resultSignature) {
39
+ const resBytes = ClaimTunnelResponse
40
+ .encode(ClaimTunnelResponse.create(res)).finish();
41
+ const verified = await verify(resBytes, resultSignature, attestorAddress);
42
+ if (!verified) {
43
+ throw new Error('Invalid result signature');
44
+ }
45
+ }
46
+ // claim wasn't generated -- i.e. the transcript
47
+ // did not contain the necessary data
48
+ if (!res.claim) {
49
+ return;
50
+ }
51
+ const signData = createSignDataForClaim(res.claim);
52
+ const verifiedClaim = await verify(strToUint8Array(signData), claimSignature, attestorAddress);
53
+ if (!verifiedClaim) {
54
+ throw new Error('Invalid claim signature');
55
+ }
56
+ }
57
+ /**
58
+ * Generates a unique identifier for given claim info
59
+ * @param info
60
+ * @returns
61
+ */
62
+ export function getIdentifierFromClaimInfo(info) {
63
+ //re-canonicalize context if it's not empty
64
+ if (info.context?.length > 0) {
65
+ try {
66
+ const ctx = JSON.parse(info.context);
67
+ info.context = canonicalStringify(ctx);
68
+ }
69
+ catch {
70
+ throw new Error('unable to parse non-empty context. Must be JSON');
71
+ }
72
+ }
73
+ const str = `${info.provider}\n${info.parameters}\n${info.context || ''}`;
74
+ //console.log('Identifier: ' + btoa(str))
75
+ return keccak256(strToUint8Array(str)).toLowerCase();
76
+ }
77
+ /**
78
+ * Canonically stringifies an object, so that the same object will always
79
+ * produce the same string despite the order of keys
80
+ */
81
+ export function canonicalStringify(params) {
82
+ if (!params) {
83
+ return '';
84
+ }
85
+ // have to cast as ESM isn't correctly typing this
86
+ return canonicalize(params) || '';
87
+ }
88
+ export function hashProviderParams(params) {
89
+ const filteredParams = {
90
+ url: params.url,
91
+ method: params.method,
92
+ body: params.body,
93
+ responseMatches: params.responseMatches,
94
+ responseRedactions: params.responseRedactions
95
+ };
96
+ const serializedParams = canonicalStringify(filteredParams);
97
+ return keccak256(strToUint8Array(serializedParams)).toLowerCase();
98
+ }
@@ -0,0 +1,15 @@
1
+ export function detectEnvironment() {
2
+ if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') {
3
+ return 'react-native';
4
+ }
5
+ if (typeof window !== 'undefined') {
6
+ return 'browser';
7
+ }
8
+ return 'node';
9
+ }
10
+ export function getEnvVariable(name) {
11
+ if (typeof process === 'undefined') {
12
+ return undefined;
13
+ }
14
+ return process?.env[name];
15
+ }
@@ -0,0 +1,50 @@
1
+ import { ErrorCode, ErrorData } from "../proto/api.js";
2
+ const PROTO_ERROR = ErrorData.fromJSON({});
3
+ /**
4
+ * Represents an error that can be thrown by the Attestor Core
5
+ * or server. Provides a code, and optional data
6
+ * to pass along with the error.
7
+ */
8
+ export class AttestorError extends Error {
9
+ name = 'AttestorError';
10
+ code;
11
+ data;
12
+ constructor(code, message, data) {
13
+ super(message);
14
+ this.code = code;
15
+ this.data = data;
16
+ }
17
+ /**
18
+ * Encodes the error as a ErrorData
19
+ * protobuf message
20
+ */
21
+ toProto() {
22
+ return ErrorData.create({
23
+ code: ErrorCode[this.code],
24
+ message: this.message,
25
+ data: JSON.stringify(this.data)
26
+ });
27
+ }
28
+ static fromProto(data = PROTO_ERROR) {
29
+ return new AttestorError(typeof data.code === 'number'
30
+ ? getKeyForValue(ErrorCode, data.code) || 'UNRECOGNIZED'
31
+ : data.code, data.message, data.data ? JSON.parse(data.data) : undefined);
32
+ }
33
+ static fromError(err, code = 'ERROR_INTERNAL') {
34
+ if (err instanceof AttestorError) {
35
+ return err;
36
+ }
37
+ return new AttestorError(code, err.message);
38
+ }
39
+ static badRequest(message, data) {
40
+ return new AttestorError('ERROR_BAD_REQUEST', message, data);
41
+ }
42
+ }
43
+ function getKeyForValue(obj, value) {
44
+ for (const key in obj) {
45
+ if (obj[key] === value) {
46
+ return key;
47
+ }
48
+ }
49
+ return undefined;
50
+ }
@@ -0,0 +1,317 @@
1
+ import { areUint8ArraysEqual, CONTENT_TYPE_MAP, crypto, decryptWrappedRecord, PACKET_TYPE, SUPPORTED_CIPHER_SUITE_MAP, uint8ArrayToBinaryStr, uint8ArrayToDataView } from '@reclaimprotocol/tls';
2
+ import { REDACTION_CHAR_CODE } from '@reclaimprotocol/zk-symmetric-crypto';
3
+ import { RPCMessage, RPCMessages } from "../proto/api.js";
4
+ const DEFAULT_REDACTION_DATA = new Uint8Array(4)
5
+ .fill(REDACTION_CHAR_CODE);
6
+ export { uint8ArrayToBinaryStr };
7
+ /**
8
+ * Decodes a Uint8Array to a UTF-8 string.
9
+ */
10
+ export function uint8ArrayToStr(arr) {
11
+ return new TextDecoder().decode(arr);
12
+ }
13
+ /**
14
+ * Encodes a UTF-8 string to a Uint8Array.
15
+ */
16
+ export function strToUint8Array(str) {
17
+ return new TextEncoder().encode(str);
18
+ }
19
+ export function getTranscriptString(receipt) {
20
+ const applMsgs = extractApplicationDataFromTranscript(receipt);
21
+ const strList = [];
22
+ for (const { message, sender } of applMsgs) {
23
+ const content = uint8ArrayToStr(message);
24
+ if (strList[strList.length - 1]?.startsWith(sender)) {
25
+ strList[strList.length - 1] += content;
26
+ }
27
+ else {
28
+ strList.push(`${sender}: ${content}`);
29
+ }
30
+ }
31
+ return strList.join('\n');
32
+ }
33
+ export const unixTimestampSeconds = () => Math.floor(Date.now() / 1000);
34
+ /**
35
+ * Find index of needle in haystack
36
+ */
37
+ export function findIndexInUint8Array(haystack, needle) {
38
+ for (let i = 0; i < haystack.length; i++) {
39
+ if (areUint8ArraysEqual(haystack.slice(i, i + needle.length), needle)) {
40
+ return i;
41
+ }
42
+ }
43
+ return -1;
44
+ }
45
+ /**
46
+ * Fetch the ZK algorithm for the specified cipher suite
47
+ */
48
+ export function getZkAlgorithmForCipherSuite(cipherSuite) {
49
+ if (cipherSuite.includes('CHACHA20')) {
50
+ return 'chacha20';
51
+ }
52
+ if (cipherSuite.includes('AES_256_GCM')) {
53
+ return 'aes-256-ctr';
54
+ }
55
+ if (cipherSuite.includes('AES_128_GCM')) {
56
+ return 'aes-128-ctr';
57
+ }
58
+ throw new Error(`${cipherSuite} not supported for ZK ops`);
59
+ }
60
+ /**
61
+ * Get the pure ciphertext without any MAC,
62
+ * or authentication tag,
63
+ * @param content content w/o header
64
+ * @param cipherSuite
65
+ */
66
+ export function getPureCiphertext(content, cipherSuite) {
67
+ // assert that the cipher suite is supported
68
+ getZkAlgorithmForCipherSuite(cipherSuite);
69
+ // 16 => auth tag length
70
+ content = content.slice(0, -16);
71
+ const { ivLength: fixedIvLength, } = SUPPORTED_CIPHER_SUITE_MAP[cipherSuite];
72
+ // 12 => total IV length
73
+ const recordIvLength = 12 - fixedIvLength;
74
+ // record IV is prefixed to the ciphertext
75
+ content = content.slice(recordIvLength);
76
+ return content;
77
+ }
78
+ /**
79
+ * Get the 8 byte IV part that's stored in the record for some cipher suites
80
+ * @param content content w/o header
81
+ * @param cipherSuite
82
+ */
83
+ export function getRecordIV(content, cipherSuite) {
84
+ // assert that the cipher suite is supported
85
+ getZkAlgorithmForCipherSuite(cipherSuite);
86
+ const { ivLength: fixedIvLength, } = SUPPORTED_CIPHER_SUITE_MAP[cipherSuite];
87
+ // 12 => total IV length
88
+ const recordIvLength = 12 - fixedIvLength;
89
+ return content.slice(0, recordIvLength);
90
+ }
91
+ export function getProviderValue(params, fn, secretParams) {
92
+ return typeof fn === 'function'
93
+ // @ts-ignore
94
+ ? fn(params, secretParams)
95
+ : fn;
96
+ }
97
+ export function generateRpcMessageId() {
98
+ return uint8ArrayToDataView(crypto.randomBytes(4)).getUint32(0);
99
+ }
100
+ /**
101
+ * Random session ID for a WebSocket client.
102
+ */
103
+ export function generateSessionId() {
104
+ return generateRpcMessageId();
105
+ }
106
+ /**
107
+ * Random ID for a tunnel.
108
+ */
109
+ export function generateTunnelId() {
110
+ return generateRpcMessageId();
111
+ }
112
+ export function makeRpcEvent(type, data) {
113
+ const ev = new Event(type);
114
+ ev.data = data;
115
+ return ev;
116
+ }
117
+ /**
118
+ * Get the RPC type from the key.
119
+ * For eg. "claimTunnelRequest" ->
120
+ * { type: 'claimTunnel', direction: 'request' }
121
+ */
122
+ export function getRpcTypeFromKey(key) {
123
+ if (key.endsWith('Request')) {
124
+ return {
125
+ type: key.slice(0, -7),
126
+ direction: 'request'
127
+ };
128
+ }
129
+ if (key.endsWith('Response')) {
130
+ return {
131
+ type: key.slice(0, -8),
132
+ direction: 'response'
133
+ };
134
+ }
135
+ }
136
+ /**
137
+ * Get the RPC response type from the RPC type.
138
+ * For eg. "claimTunnel" -> "claimTunnelResponse"
139
+ */
140
+ export function getRpcResponseType(type) {
141
+ return `${type}Response`;
142
+ }
143
+ /**
144
+ * Get the RPC request type from the RPC type.
145
+ * For eg. "claimTunnel" -> "claimTunnelRequest"
146
+ */
147
+ export function getRpcRequestType(type) {
148
+ return `${type}Request`;
149
+ }
150
+ export function isApplicationData(packet, tlsVersion) {
151
+ return packet.type === 'ciphertext'
152
+ && (packet.contentType === 'APPLICATION_DATA'
153
+ || (packet.data[0] === PACKET_TYPE.WRAPPED_RECORD
154
+ && tlsVersion === 'TLS1_2'));
155
+ }
156
+ /**
157
+ * Convert the received data from a WS to a Uint8Array
158
+ */
159
+ export async function extractArrayBufferFromWsData(data) {
160
+ if (data instanceof ArrayBuffer) {
161
+ return new Uint8Array(data);
162
+ }
163
+ // uint8array/Buffer
164
+ if (data instanceof Uint8Array
165
+ || (typeof data === 'object' && data && 'buffer' in data)) {
166
+ return data;
167
+ }
168
+ if (typeof data === 'string') {
169
+ return strToUint8Array(data);
170
+ }
171
+ if (typeof Blob !== 'undefined' && data instanceof Blob) {
172
+ return new Uint8Array(await data.arrayBuffer());
173
+ }
174
+ throw new Error('unsupported data: ' + String(data));
175
+ }
176
+ /**
177
+ * Check if the RPC message is a request or a response.
178
+ */
179
+ export function getRpcRequest(msg) {
180
+ if (msg.requestError) {
181
+ return {
182
+ direction: 'response',
183
+ type: 'error'
184
+ };
185
+ }
186
+ for (const key in msg) {
187
+ if (!msg[key]) {
188
+ continue;
189
+ }
190
+ const rpcType = getRpcTypeFromKey(key);
191
+ if (!rpcType) {
192
+ continue;
193
+ }
194
+ return rpcType;
195
+ }
196
+ }
197
+ /**
198
+ * Finds all application data messages in a transcript
199
+ * and returns them. Removes the "contentType" suffix from the message.
200
+ * in TLS 1.3
201
+ */
202
+ export function extractApplicationDataFromTranscript({ transcript, tlsVersion }) {
203
+ const msgs = [];
204
+ for (const m of transcript) {
205
+ let message;
206
+ // redacted msgs but with a valid packet header
207
+ // can be considered application data messages
208
+ if (m.redacted) {
209
+ if (!m.plaintextLength) {
210
+ message = DEFAULT_REDACTION_DATA;
211
+ }
212
+ else {
213
+ const len = tlsVersion === 'TLS1_3'
214
+ // remove content type suffix
215
+ ? m.plaintextLength - 1
216
+ : m.plaintextLength;
217
+ message = new Uint8Array(len)
218
+ .fill(REDACTION_CHAR_CODE);
219
+ }
220
+ // otherwise, we need to check the content type
221
+ }
222
+ else if (tlsVersion === 'TLS1_3') {
223
+ const contentType = m.message[m.message.length - 1];
224
+ if (contentType !== CONTENT_TYPE_MAP['APPLICATION_DATA']) {
225
+ continue;
226
+ }
227
+ message = m.message.slice(0, -1);
228
+ }
229
+ else if (m.recordHeader[0] === PACKET_TYPE.WRAPPED_RECORD) {
230
+ message = m.message;
231
+ }
232
+ else {
233
+ continue;
234
+ }
235
+ msgs.push({ message, sender: m.sender });
236
+ }
237
+ return msgs;
238
+ }
239
+ export function extractHandshakeFromTranscript({ transcript, tlsVersion }) {
240
+ const msgs = [];
241
+ for (const [i, m] of transcript.entries()) {
242
+ if (m.redacted) {
243
+ break; // stop at first encrypted message
244
+ }
245
+ let message;
246
+ if (m.recordHeader[0] === PACKET_TYPE.HELLO) {
247
+ message = m.message;
248
+ }
249
+ else if (m.recordHeader[0] === PACKET_TYPE.WRAPPED_RECORD) {
250
+ if (tlsVersion === 'TLS1_3') {
251
+ const contentType = m.message[m.message.length - 1];
252
+ if (contentType !== CONTENT_TYPE_MAP['HANDSHAKE']) {
253
+ break;
254
+ }
255
+ message = m.message.slice(0, -1);
256
+ }
257
+ else {
258
+ break;
259
+ }
260
+ }
261
+ else {
262
+ continue;
263
+ }
264
+ if (!message.length) {
265
+ throw new Error('unsupported handshake message');
266
+ }
267
+ msgs.push({ message, sender: m.sender, index: i });
268
+ }
269
+ return msgs;
270
+ }
271
+ export async function decryptDirect(directReveal, cipherSuite, recordHeader, serverTlsVersion, content) {
272
+ const { key, iv, recordNumber } = directReveal;
273
+ const { cipher } = SUPPORTED_CIPHER_SUITE_MAP[cipherSuite];
274
+ const importedKey = await crypto.importKey(cipher, key);
275
+ return await decryptWrappedRecord(content, {
276
+ iv,
277
+ key: importedKey,
278
+ recordHeader,
279
+ recordNumber,
280
+ version: serverTlsVersion,
281
+ cipherSuite,
282
+ });
283
+ }
284
+ export function packRpcMessages(...msgs) {
285
+ return RPCMessages.create({
286
+ messages: msgs.map(msg => (RPCMessage.create({
287
+ ...msg,
288
+ id: msg.id || generateRpcMessageId()
289
+ })))
290
+ });
291
+ }
292
+ /**
293
+ * Converts an Ethers struct (an array w named keys) to
294
+ * a plain object. Recursively converts all structs inside.
295
+ * Required to correctly JSON.stringify the struct.
296
+ */
297
+ export function ethersStructToPlainObject(struct) {
298
+ if (!Array.isArray(struct)) {
299
+ return struct;
300
+ }
301
+ const namedKeys = Object.keys(struct)
302
+ .filter(key => isNaN(Number(key)));
303
+ // seems to be an actual array
304
+ if (!namedKeys.length) {
305
+ return struct.map(ethersStructToPlainObject);
306
+ }
307
+ const obj = {};
308
+ for (const key of namedKeys) {
309
+ obj[key] = ethersStructToPlainObject(struct[key]);
310
+ }
311
+ return obj;
312
+ }
313
+ export function isTls13Suite(suite) {
314
+ return suite === 'TLS_AES_128_GCM_SHA256'
315
+ || suite === 'TLS_AES_256_GCM_SHA384'
316
+ || suite === 'TLS_CHACHA20_POLY1305_SHA256';
317
+ }