@tinycloud/sdk-services 3.8.0-beta.1 → 3.8.0-beta.16

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.
@@ -56,6 +56,7 @@ __export(encryption_exports, {
56
56
  isNetworkId: () => isNetworkId,
57
57
  networkDiscoveryKey: () => networkDiscoveryKey,
58
58
  openWrappedKey: () => openWrappedKey,
59
+ parseEncryptionResource: () => parseEncryptionResource,
59
60
  parseNetworkId: () => parseNetworkId,
60
61
  utf8Decode: () => utf8Decode,
61
62
  utf8Encode: () => utf8Encode,
@@ -201,6 +202,26 @@ function parseNetworkId(networkId) {
201
202
  }
202
203
  return { networkId, ownerDid, name };
203
204
  }
205
+ function parseEncryptionResource(resource) {
206
+ const networkSeparator = resource.lastIndexOf(":");
207
+ const nameAndPath = resource.slice(networkSeparator + 1);
208
+ const pathSeparator = nameAndPath.indexOf("/");
209
+ const name = pathSeparator === -1 ? nameAndPath : nameAndPath.slice(0, pathSeparator);
210
+ const networkId = pathSeparator === -1 ? resource : `${resource.slice(0, networkSeparator + 1)}${name}`;
211
+ const parsed = parseNetworkId(networkId);
212
+ if (pathSeparator === -1) return parsed;
213
+ const keyPath = nameAndPath.slice(pathSeparator + 1);
214
+ const segments = keyPath.split("/");
215
+ const prefix = keyPath.endsWith("/");
216
+ if (keyPath.length === 0 || /[\u0000-\u001f\u007f\\]/.test(keyPath) || segments.some(
217
+ (segment, index) => segment === "." || segment === ".." || segment.length === 0 && !(prefix && index === segments.length - 1)
218
+ )) {
219
+ throw new NetworkIdError(
220
+ `encryption resource has an invalid keyPath (got ${JSON.stringify(resource)})`
221
+ );
222
+ }
223
+ return { ...parsed, keyPath };
224
+ }
204
225
  function buildNetworkId(ownerDid, name) {
205
226
  if (typeof ownerDid !== "string" || !ownerDid.startsWith("did:")) {
206
227
  throw new NetworkIdError("ownerDid must be a DID");
@@ -913,7 +934,12 @@ function ensureNetworkUsableForDecrypt(descriptor) {
913
934
 
914
935
  // src/encryption/envelope.ts
915
936
  function encryptToNetwork(crypto, input) {
916
- parseNetworkId(input.networkId);
937
+ const parsedResource = parseEncryptionResource(
938
+ input.keyPath === void 0 ? input.networkId : `${input.networkId}/${input.keyPath}`
939
+ );
940
+ if (parsedResource.keyPath !== input.keyPath || input.keyPath?.endsWith("/")) {
941
+ throw new NetworkIdError("keyPath must name an exact encryption key");
942
+ }
917
943
  const alg = input.alg ?? DEFAULT_ENCRYPTION_ALG;
918
944
  const keyVersion = input.keyVersion ?? DEFAULT_KEY_VERSION;
919
945
  const symmetricKey = crypto.randomBytes(32);
@@ -927,6 +953,7 @@ function encryptToNetwork(crypto, input) {
927
953
  const envelope = {
928
954
  v: ENVELOPE_VERSION,
929
955
  networkId: input.networkId,
956
+ ...input.keyPath !== void 0 ? { keyPath: input.keyPath } : {},
930
957
  alg,
931
958
  keyVersion,
932
959
  encryptedSymmetricKey,
@@ -958,13 +985,24 @@ function validateEnvelope(crypto, envelope) {
958
985
  };
959
986
  }
960
987
  try {
961
- parseNetworkId(e.networkId);
988
+ if (parseEncryptionResource(e.networkId).keyPath !== void 0) {
989
+ throw new Error("networkId must identify the network root");
990
+ }
991
+ if (e.keyPath !== void 0) {
992
+ if (typeof e.keyPath !== "string") {
993
+ throw new Error("keyPath must be a string");
994
+ }
995
+ const parsed = parseEncryptionResource(`${e.networkId}/${e.keyPath}`);
996
+ if (parsed.keyPath !== e.keyPath || e.keyPath.endsWith("/")) {
997
+ throw new Error("keyPath must name an exact encryption key");
998
+ }
999
+ }
962
1000
  } catch (err2) {
963
1001
  return {
964
1002
  ok: false,
965
1003
  error: encryptionError({
966
1004
  code: "INVALID_ENVELOPE",
967
- message: `envelope.networkId is malformed: ${err2 instanceof Error ? err2.message : String(err2)}`
1005
+ message: `envelope encryption resource is malformed: ${err2 instanceof Error ? err2.message : String(err2)}`
968
1006
  })
969
1007
  };
970
1008
  }
@@ -1037,6 +1075,7 @@ function buildDecryptFacts(input) {
1037
1075
  type: DECRYPT_FACT_TYPE,
1038
1076
  targetNode: input.body.targetNode,
1039
1077
  networkId: input.body.networkId,
1078
+ ...input.body.keyPath !== void 0 ? { keyPath: input.body.keyPath } : {},
1040
1079
  bodyHash,
1041
1080
  encryptedSymmetricKeyHash: input.encryptedSymmetricKeyHash,
1042
1081
  receiverPublicKeyHash,
@@ -1044,10 +1083,14 @@ function buildDecryptFacts(input) {
1044
1083
  keyVersion: input.body.keyVersion
1045
1084
  };
1046
1085
  }
1047
- function buildDecryptAttenuation(networkId) {
1048
- parseNetworkId(networkId);
1086
+ function buildDecryptAttenuation(networkId, keyPath) {
1087
+ const resource = keyPath === void 0 ? networkId : `${networkId}/${keyPath}`;
1088
+ const parsed = parseEncryptionResource(resource);
1089
+ if (parsed.networkId !== networkId || parsed.keyPath !== keyPath || keyPath?.endsWith("/")) {
1090
+ throw new NetworkIdError("keyPath must name an exact encryption key");
1091
+ }
1049
1092
  return {
1050
- [networkId]: {
1093
+ [resource]: {
1051
1094
  [DECRYPT_ACTION]: {}
1052
1095
  }
1053
1096
  };
@@ -1107,6 +1150,15 @@ function checkDecryptInvocationInput(crypto, input) {
1107
1150
  })
1108
1151
  };
1109
1152
  }
1153
+ if (input.facts.keyPath !== input.body.keyPath) {
1154
+ return {
1155
+ ok: false,
1156
+ error: encryptionError({
1157
+ code: "INVALID_INPUT",
1158
+ message: "facts.keyPath must equal body.keyPath"
1159
+ })
1160
+ };
1161
+ }
1110
1162
  if (input.facts.alg !== input.body.alg) {
1111
1163
  return {
1112
1164
  ok: false,
@@ -1144,7 +1196,9 @@ function checkDecryptInvocationInput(crypto, input) {
1144
1196
  };
1145
1197
  }
1146
1198
  try {
1147
- parseNetworkId(input.networkId);
1199
+ if (parseEncryptionResource(input.networkId).keyPath !== void 0) {
1200
+ throw new NetworkIdError("networkId must identify the network root");
1201
+ }
1148
1202
  } catch (err2) {
1149
1203
  return {
1150
1204
  ok: false,
@@ -1154,6 +1208,22 @@ function checkDecryptInvocationInput(crypto, input) {
1154
1208
  })
1155
1209
  };
1156
1210
  }
1211
+ try {
1212
+ const resource = parseEncryptionResource(
1213
+ input.body.keyPath === void 0 ? input.body.networkId : `${input.body.networkId}/${input.body.keyPath}`
1214
+ );
1215
+ if (resource.keyPath !== input.body.keyPath || input.body.keyPath?.endsWith("/")) {
1216
+ throw new NetworkIdError("keyPath must name an exact encryption key");
1217
+ }
1218
+ } catch (err2) {
1219
+ return {
1220
+ ok: false,
1221
+ error: encryptionError({
1222
+ code: "INVALID_INPUT",
1223
+ message: err2 instanceof Error ? err2.message : String(err2)
1224
+ })
1225
+ };
1226
+ }
1157
1227
  const canonicalBody = canonicalize(
1158
1228
  input.body
1159
1229
  );
@@ -1394,6 +1464,7 @@ var EncryptionService = class extends BaseService {
1394
1464
  // that spelling in the envelope so a later response can be compared
1395
1465
  // byte-for-byte (notably for checksummed did:pkh principals).
1396
1466
  networkId: descriptor.networkId,
1467
+ ...options?.keyPath !== void 0 ? { keyPath: options.keyPath } : {},
1397
1468
  networkPublicKey,
1398
1469
  plaintext,
1399
1470
  ...options?.aad !== void 0 ? { aad: options.aad } : {},
@@ -1453,6 +1524,7 @@ var EncryptionService = class extends BaseService {
1453
1524
  type: DECRYPT_FACT_TYPE,
1454
1525
  targetNode,
1455
1526
  networkId: envelope.networkId,
1527
+ ...envelope.keyPath !== void 0 ? { keyPath: envelope.keyPath } : {},
1456
1528
  alg: envelope.alg,
1457
1529
  keyVersion: envelope.keyVersion,
1458
1530
  encryptedSymmetricKey: envelope.encryptedSymmetricKey,
@@ -1597,6 +1669,7 @@ var import_decrypt_transport_response_error3 = require("@tinycloud/sdk-services/
1597
1669
  isNetworkId,
1598
1670
  networkDiscoveryKey,
1599
1671
  openWrappedKey,
1672
+ parseEncryptionResource,
1600
1673
  parseNetworkId,
1601
1674
  utf8Decode,
1602
1675
  utf8Encode,