@opentdf/sdk 0.4.0-beta.4 → 0.4.0-beta.41

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 (217) hide show
  1. package/dist/cjs/src/access/access-fetch.js +2 -1
  2. package/dist/cjs/src/access/access-rpc.js +11 -5
  3. package/dist/cjs/src/access/constants.js +6 -0
  4. package/dist/cjs/src/access.js +39 -4
  5. package/dist/cjs/src/auth/oidc-clientcredentials-provider.js +4 -2
  6. package/dist/cjs/src/auth/oidc-externaljwt-provider.js +5 -3
  7. package/dist/cjs/src/auth/oidc-refreshtoken-provider.js +19 -3
  8. package/dist/cjs/src/auth/oidc.js +9 -8
  9. package/dist/cjs/src/auth/providers.js +7 -1
  10. package/dist/cjs/src/index.js +4 -2
  11. package/dist/cjs/src/nanoclients.js +4 -4
  12. package/dist/cjs/src/nanotdf/Client.js +10 -6
  13. package/dist/cjs/src/opentdf.js +103 -13
  14. package/dist/cjs/src/platform/authorization/v2/authorization_pb.js +112 -0
  15. package/dist/cjs/src/platform/buf/validate/validate_pb.js +114 -170
  16. package/dist/cjs/src/platform/common/common_pb.js +16 -5
  17. package/dist/cjs/src/platform/entity/entity_pb.js +51 -0
  18. package/dist/cjs/src/platform/entityresolution/entity_resolution_pb.js +1 -1
  19. package/dist/cjs/src/platform/entityresolution/v2/entity_resolution_pb.js +49 -0
  20. package/dist/cjs/src/platform/google/api/annotations_pb.js +1 -1
  21. package/dist/cjs/src/platform/google/api/http_pb.js +3 -3
  22. package/dist/cjs/src/platform/kas/kas_pb.js +2 -2
  23. package/dist/cjs/src/platform/policy/attributes/attributes_pb.js +12 -2
  24. package/dist/cjs/src/platform/policy/kasregistry/key_access_server_registry_pb.js +57 -4
  25. package/dist/cjs/src/platform/policy/keymanagement/key_management_pb.js +2 -2
  26. package/dist/cjs/src/platform/policy/namespaces/namespaces_pb.js +31 -4
  27. package/dist/cjs/src/platform/policy/objects_pb.js +116 -42
  28. package/dist/cjs/src/platform/policy/obligations/obligations_pb.js +159 -0
  29. package/dist/cjs/src/platform/policy/registeredresources/registered_resources_pb.js +20 -15
  30. package/dist/cjs/src/platform/policy/resourcemapping/resource_mapping_pb.js +2 -3
  31. package/dist/cjs/src/platform/policy/selectors_pb.js +1 -1
  32. package/dist/cjs/src/platform/policy/subjectmapping/subject_mapping_pb.js +2 -3
  33. package/dist/cjs/src/platform/policy/unsafe/unsafe_pb.js +2 -4
  34. package/dist/cjs/src/platform.js +20 -3
  35. package/dist/cjs/src/policy/api.js +27 -7
  36. package/dist/cjs/src/policy/granter.js +75 -48
  37. package/dist/cjs/src/seekable.js +32 -1
  38. package/dist/cjs/src/utils.js +85 -3
  39. package/dist/cjs/tdf3/src/assertions.js +39 -2
  40. package/dist/cjs/tdf3/src/client/DecoratedReadableStream.js +8 -1
  41. package/dist/cjs/tdf3/src/client/builders.js +13 -1
  42. package/dist/cjs/tdf3/src/client/index.js +213 -54
  43. package/dist/cjs/tdf3/src/client/validation.js +3 -3
  44. package/dist/cjs/tdf3/src/tdf.js +42 -9
  45. package/dist/cjs/tdf3/src/utils/unwrap.js +2 -2
  46. package/dist/types/src/access/access-fetch.d.ts +1 -0
  47. package/dist/types/src/access/access-fetch.d.ts.map +1 -1
  48. package/dist/types/src/access/access-rpc.d.ts +2 -1
  49. package/dist/types/src/access/access-rpc.d.ts.map +1 -1
  50. package/dist/types/src/access/constants.d.ts +3 -0
  51. package/dist/types/src/access/constants.d.ts.map +1 -0
  52. package/dist/types/src/access.d.ts +30 -1
  53. package/dist/types/src/access.d.ts.map +1 -1
  54. package/dist/types/src/auth/oidc-clientcredentials-provider.d.ts +1 -1
  55. package/dist/types/src/auth/oidc-clientcredentials-provider.d.ts.map +1 -1
  56. package/dist/types/src/auth/oidc-externaljwt-provider.d.ts +1 -1
  57. package/dist/types/src/auth/oidc-externaljwt-provider.d.ts.map +1 -1
  58. package/dist/types/src/auth/oidc-refreshtoken-provider.d.ts +15 -1
  59. package/dist/types/src/auth/oidc-refreshtoken-provider.d.ts.map +1 -1
  60. package/dist/types/src/auth/oidc.d.ts +4 -0
  61. package/dist/types/src/auth/oidc.d.ts.map +1 -1
  62. package/dist/types/src/auth/providers.d.ts.map +1 -1
  63. package/dist/types/src/index.d.ts +1 -0
  64. package/dist/types/src/index.d.ts.map +1 -1
  65. package/dist/types/src/nanotdf/Client.d.ts +8 -1
  66. package/dist/types/src/nanotdf/Client.d.ts.map +1 -1
  67. package/dist/types/src/opentdf.d.ts +137 -6
  68. package/dist/types/src/opentdf.d.ts.map +1 -1
  69. package/dist/types/src/platform/authorization/v2/authorization_pb.d.ts +439 -0
  70. package/dist/types/src/platform/authorization/v2/authorization_pb.d.ts.map +1 -0
  71. package/dist/types/src/platform/buf/validate/validate_pb.d.ts +495 -370
  72. package/dist/types/src/platform/buf/validate/validate_pb.d.ts.map +1 -1
  73. package/dist/types/src/platform/common/common_pb.d.ts +36 -0
  74. package/dist/types/src/platform/common/common_pb.d.ts.map +1 -1
  75. package/dist/types/src/platform/entity/entity_pb.d.ts +130 -0
  76. package/dist/types/src/platform/entity/entity_pb.d.ts.map +1 -0
  77. package/dist/types/src/platform/entityresolution/entity_resolution_pb.d.ts +4 -0
  78. package/dist/types/src/platform/entityresolution/entity_resolution_pb.d.ts.map +1 -1
  79. package/dist/types/src/platform/entityresolution/v2/entity_resolution_pb.d.ts +136 -0
  80. package/dist/types/src/platform/entityresolution/v2/entity_resolution_pb.d.ts.map +1 -0
  81. package/dist/types/src/platform/google/api/http_pb.d.ts.map +1 -1
  82. package/dist/types/src/platform/kas/kas_pb.d.ts +5 -0
  83. package/dist/types/src/platform/kas/kas_pb.d.ts.map +1 -1
  84. package/dist/types/src/platform/policy/attributes/attributes_pb.d.ts +44 -13
  85. package/dist/types/src/platform/policy/attributes/attributes_pb.d.ts.map +1 -1
  86. package/dist/types/src/platform/policy/kasregistry/key_access_server_registry_pb.d.ts +329 -24
  87. package/dist/types/src/platform/policy/kasregistry/key_access_server_registry_pb.d.ts.map +1 -1
  88. package/dist/types/src/platform/policy/keymanagement/key_management_pb.d.ts +20 -1
  89. package/dist/types/src/platform/policy/keymanagement/key_management_pb.d.ts.map +1 -1
  90. package/dist/types/src/platform/policy/namespaces/namespaces_pb.d.ts +143 -5
  91. package/dist/types/src/platform/policy/namespaces/namespaces_pb.d.ts.map +1 -1
  92. package/dist/types/src/platform/policy/objects_pb.d.ts +382 -33
  93. package/dist/types/src/platform/policy/objects_pb.d.ts.map +1 -1
  94. package/dist/types/src/platform/policy/obligations/obligations_pb.d.ts +670 -0
  95. package/dist/types/src/platform/policy/obligations/obligations_pb.d.ts.map +1 -0
  96. package/dist/types/src/platform/policy/registeredresources/registered_resources_pb.d.ts +67 -0
  97. package/dist/types/src/platform/policy/registeredresources/registered_resources_pb.d.ts.map +1 -1
  98. package/dist/types/src/platform/policy/resourcemapping/resource_mapping_pb.d.ts.map +1 -1
  99. package/dist/types/src/platform/policy/selectors_pb.d.ts +18 -0
  100. package/dist/types/src/platform/policy/selectors_pb.d.ts.map +1 -1
  101. package/dist/types/src/platform/policy/subjectmapping/subject_mapping_pb.d.ts.map +1 -1
  102. package/dist/types/src/platform/policy/unsafe/unsafe_pb.d.ts +18 -4
  103. package/dist/types/src/platform/policy/unsafe/unsafe_pb.d.ts.map +1 -1
  104. package/dist/types/src/platform.d.ts +21 -0
  105. package/dist/types/src/platform.d.ts.map +1 -1
  106. package/dist/types/src/policy/api.d.ts +2 -0
  107. package/dist/types/src/policy/api.d.ts.map +1 -1
  108. package/dist/types/src/policy/granter.d.ts +11 -6
  109. package/dist/types/src/policy/granter.d.ts.map +1 -1
  110. package/dist/types/src/seekable.d.ts +31 -0
  111. package/dist/types/src/seekable.d.ts.map +1 -1
  112. package/dist/types/src/utils.d.ts +61 -2
  113. package/dist/types/src/utils.d.ts.map +1 -1
  114. package/dist/types/tdf3/src/assertions.d.ts +4 -0
  115. package/dist/types/tdf3/src/assertions.d.ts.map +1 -1
  116. package/dist/types/tdf3/src/client/DecoratedReadableStream.d.ts +6 -0
  117. package/dist/types/tdf3/src/client/DecoratedReadableStream.d.ts.map +1 -1
  118. package/dist/types/tdf3/src/client/builders.d.ts +14 -0
  119. package/dist/types/tdf3/src/client/builders.d.ts.map +1 -1
  120. package/dist/types/tdf3/src/client/index.d.ts +25 -4
  121. package/dist/types/tdf3/src/client/index.d.ts.map +1 -1
  122. package/dist/types/tdf3/src/client/validation.d.ts +3 -3
  123. package/dist/types/tdf3/src/client/validation.d.ts.map +1 -1
  124. package/dist/types/tdf3/src/tdf.d.ts +3 -1
  125. package/dist/types/tdf3/src/tdf.d.ts.map +1 -1
  126. package/dist/types/tdf3/src/utils/unwrap.d.ts.map +1 -1
  127. package/dist/web/src/access/access-fetch.js +2 -1
  128. package/dist/web/src/access/access-rpc.js +11 -5
  129. package/dist/web/src/access/constants.js +3 -0
  130. package/dist/web/src/access.js +37 -3
  131. package/dist/web/src/auth/oidc-clientcredentials-provider.js +4 -2
  132. package/dist/web/src/auth/oidc-externaljwt-provider.js +5 -3
  133. package/dist/web/src/auth/oidc-refreshtoken-provider.js +19 -3
  134. package/dist/web/src/auth/oidc.js +9 -8
  135. package/dist/web/src/auth/providers.js +7 -1
  136. package/dist/web/src/index.js +2 -1
  137. package/dist/web/src/nanoclients.js +4 -4
  138. package/dist/web/src/nanotdf/Client.js +11 -7
  139. package/dist/web/src/opentdf.js +103 -13
  140. package/dist/web/src/platform/authorization/v2/authorization_pb.js +109 -0
  141. package/dist/web/src/platform/buf/validate/validate_pb.js +113 -169
  142. package/dist/web/src/platform/common/common_pb.js +15 -4
  143. package/dist/web/src/platform/entity/entity_pb.js +48 -0
  144. package/dist/web/src/platform/entityresolution/entity_resolution_pb.js +1 -1
  145. package/dist/web/src/platform/entityresolution/v2/entity_resolution_pb.js +46 -0
  146. package/dist/web/src/platform/google/api/annotations_pb.js +1 -1
  147. package/dist/web/src/platform/google/api/http_pb.js +3 -3
  148. package/dist/web/src/platform/kas/kas_pb.js +2 -2
  149. package/dist/web/src/platform/policy/attributes/attributes_pb.js +12 -2
  150. package/dist/web/src/platform/policy/kasregistry/key_access_server_registry_pb.js +55 -3
  151. package/dist/web/src/platform/policy/keymanagement/key_management_pb.js +2 -2
  152. package/dist/web/src/platform/policy/namespaces/namespaces_pb.js +30 -3
  153. package/dist/web/src/platform/policy/objects_pb.js +114 -41
  154. package/dist/web/src/platform/policy/obligations/obligations_pb.js +156 -0
  155. package/dist/web/src/platform/policy/registeredresources/registered_resources_pb.js +19 -14
  156. package/dist/web/src/platform/policy/resourcemapping/resource_mapping_pb.js +2 -3
  157. package/dist/web/src/platform/policy/selectors_pb.js +1 -1
  158. package/dist/web/src/platform/policy/subjectmapping/subject_mapping_pb.js +2 -3
  159. package/dist/web/src/platform/policy/unsafe/unsafe_pb.js +2 -4
  160. package/dist/web/src/platform.js +20 -3
  161. package/dist/web/src/policy/api.js +26 -7
  162. package/dist/web/src/policy/granter.js +75 -48
  163. package/dist/web/src/seekable.js +32 -1
  164. package/dist/web/src/utils.js +84 -3
  165. package/dist/web/tdf3/src/assertions.js +38 -2
  166. package/dist/web/tdf3/src/client/DecoratedReadableStream.js +8 -1
  167. package/dist/web/tdf3/src/client/builders.js +13 -1
  168. package/dist/web/tdf3/src/client/index.js +215 -57
  169. package/dist/web/tdf3/src/client/validation.js +3 -3
  170. package/dist/web/tdf3/src/tdf.js +42 -9
  171. package/dist/web/tdf3/src/utils/unwrap.js +2 -2
  172. package/package.json +7 -5
  173. package/src/access/access-fetch.ts +1 -0
  174. package/src/access/access-rpc.ts +13 -4
  175. package/src/access/constants.ts +2 -0
  176. package/src/access.ts +54 -2
  177. package/src/auth/oidc-clientcredentials-provider.ts +4 -0
  178. package/src/auth/oidc-externaljwt-provider.ts +5 -1
  179. package/src/auth/oidc-refreshtoken-provider.ts +19 -1
  180. package/src/auth/oidc.ts +12 -7
  181. package/src/auth/providers.ts +6 -0
  182. package/src/index.ts +1 -0
  183. package/src/nanoclients.ts +3 -3
  184. package/src/nanotdf/Client.ts +28 -6
  185. package/src/opentdf.ts +206 -73
  186. package/src/platform/authorization/v2/authorization_pb.ts +503 -0
  187. package/src/platform/buf/validate/validate_pb.ts +529 -401
  188. package/src/platform/common/common_pb.ts +48 -3
  189. package/src/platform/entity/entity_pb.ts +154 -0
  190. package/src/platform/entityresolution/entity_resolution_pb.ts +4 -0
  191. package/src/platform/entityresolution/v2/entity_resolution_pb.ts +170 -0
  192. package/src/platform/google/api/annotations_pb.ts +1 -1
  193. package/src/platform/google/api/http_pb.ts +2 -2
  194. package/src/platform/kas/kas_pb.ts +6 -1
  195. package/src/platform/policy/attributes/attributes_pb.ts +46 -16
  196. package/src/platform/policy/kasregistry/key_access_server_registry_pb.ts +371 -27
  197. package/src/platform/policy/keymanagement/key_management_pb.ts +24 -2
  198. package/src/platform/policy/namespaces/namespaces_pb.ts +163 -7
  199. package/src/platform/policy/objects_pb.ts +474 -59
  200. package/src/platform/policy/obligations/obligations_pb.ts +788 -0
  201. package/src/platform/policy/registeredresources/registered_resources_pb.ts +80 -13
  202. package/src/platform/policy/resourcemapping/resource_mapping_pb.ts +1 -2
  203. package/src/platform/policy/selectors_pb.ts +18 -0
  204. package/src/platform/policy/subjectmapping/subject_mapping_pb.ts +1 -2
  205. package/src/platform/policy/unsafe/unsafe_pb.ts +21 -6
  206. package/src/platform.ts +29 -5
  207. package/src/policy/api.ts +37 -6
  208. package/src/policy/granter.ts +82 -56
  209. package/src/seekable.ts +31 -0
  210. package/src/utils.ts +88 -2
  211. package/tdf3/src/assertions.ts +52 -1
  212. package/tdf3/src/client/DecoratedReadableStream.ts +9 -0
  213. package/tdf3/src/client/builders.ts +16 -0
  214. package/tdf3/src/client/index.ts +309 -73
  215. package/tdf3/src/client/validation.ts +2 -2
  216. package/tdf3/src/tdf.ts +53 -9
  217. package/tdf3/src/utils/unwrap.ts +2 -1
@@ -1,9 +1,13 @@
1
1
  import { ConfigurationError } from '../errors.js';
2
2
  import { Attribute, AttributeRuleType, KeyAccessServer, Value } from './attributes.js';
3
+ import { SimpleKasPublicKey } from '../platform/policy/objects_pb.js';
3
4
 
4
- export type KeySplitStep = {
5
- kas: KeyAccessServer;
6
- sid?: string;
5
+ type KeyHolder = KeyAccessServer | (SimpleKasPublicKey & { kasUri: string });
6
+
7
+ type KeySplitStep = {
8
+ kas: KeyHolder;
9
+ kid?: string;
10
+ sid: string;
7
11
  };
8
12
 
9
13
  type AttributeClause = {
@@ -13,17 +17,17 @@ type AttributeClause = {
13
17
 
14
18
  type AndClause = {
15
19
  op: 'allOf';
16
- kases: string[];
20
+ granters: KeyHolder[];
17
21
  };
18
22
 
19
23
  type HeirarchyClause = {
20
24
  op: 'hierarchy';
21
- kases: string[];
25
+ granters: KeyHolder[];
22
26
  };
23
27
 
24
28
  type OrClause = {
25
29
  op: 'anyOf';
26
- kases: string[];
30
+ granters: KeyHolder[];
27
31
  };
28
32
 
29
33
  type BooleanClause = AndClause | OrClause | HeirarchyClause;
@@ -49,53 +53,51 @@ export function booleanOperatorFor(rule?: AttributeRuleType): BooleanOperator {
49
53
  }
50
54
  }
51
55
 
56
+ type ValueFQN = string;
52
57
  export function plan(dataAttrs: Value[]): KeySplitStep[] {
53
58
  // KASes by value
54
- const grants: Record<string, Set<string>> = {};
55
- // KAS detail by KAS url
56
- const kasInfo: Record<string, KeyAccessServer> = {};
57
- // Attribute definitions in use
58
- const prefixes: Set<string> = new Set();
59
+ const granters: Record<ValueFQN, Set<KeyHolder>> = Object.create(null);
59
60
  // Values grouped by normalized attribute prefix
60
- const allClauses: Record<string, AttributeClause> = {};
61
- // Values by normalized FQN
62
- const allValues: Record<string, Value> = {};
61
+ const allClauses: Record<string, AttributeClause> = Object.create(null);
63
62
 
64
- const addGrants = (val: string, gs?: KeyAccessServer[]): boolean => {
63
+ const addGrants = (valueFQN: string, gs?: KeyHolder[]): boolean => {
64
+ if (!(valueFQN in granters)) {
65
+ granters[valueFQN] = new Set();
66
+ }
65
67
  if (!gs?.length) {
66
- if (!(val in grants)) {
67
- grants[val] = new Set();
68
- }
69
68
  return false;
70
69
  }
71
70
  for (const g of gs) {
72
- if (val in grants) {
73
- grants[val].add(g.uri);
74
- } else {
75
- grants[val] = new Set([g.uri]);
76
- }
77
- kasInfo[g.uri] = g;
71
+ granters[valueFQN].add(g);
78
72
  }
79
73
  return true;
80
74
  };
81
75
 
82
76
  for (const v of dataAttrs) {
83
- const { attribute, fqn } = v;
77
+ const { attribute, fqn, kasKeys } = v;
84
78
  if (!attribute) {
85
79
  throw new ConfigurationError(`attribute not defined for [${fqn}]`);
86
80
  }
87
81
  const valFqn = fqn.toLowerCase();
88
82
  const attrFqn = attribute.fqn.toLowerCase();
89
- if (!prefixes.has(attrFqn)) {
90
- prefixes.add(attrFqn);
83
+ if (!(attrFqn in allClauses)) {
91
84
  allClauses[attrFqn] = {
92
85
  def: attribute,
93
86
  values: [],
94
87
  };
95
88
  }
96
89
  allClauses[attrFqn].values.push(valFqn);
97
- allValues[valFqn] = v;
98
- if (!addGrants(valFqn, v.grants)) {
90
+ const validKasKeys = kasKeys
91
+ .map((kasKey) => {
92
+ if (!kasKey.publicKey) {
93
+ return null;
94
+ }
95
+ return Object.assign({ kasUri: kasKey.kasUri }, kasKey.publicKey);
96
+ })
97
+ .filter((kasKey) => kasKey !== null);
98
+ if (validKasKeys.length) {
99
+ addGrants(valFqn, validKasKeys);
100
+ } else if (!addGrants(valFqn, v.grants)) {
99
101
  if (!addGrants(valFqn, attribute.grants)) {
100
102
  addGrants(valFqn, attribute.namespace?.grants);
101
103
  }
@@ -103,64 +105,80 @@ export function plan(dataAttrs: Value[]): KeySplitStep[] {
103
105
  }
104
106
  const kcs: ComplexBooleanClause[] = [];
105
107
  for (const attrClause of Object.values(allClauses)) {
106
- const ccv: BooleanClause[] = [];
107
- for (const term of attrClause.values) {
108
- const grantsForTerm = Array.from(grants[term] || []);
109
- if (grantsForTerm?.length) {
110
- ccv.push({
108
+ // Create wrapper clauses for each value, [(ANY_OF Value-1), (ANY_OF Value-2)]
109
+ const individualValueClauses: BooleanClause[] = [];
110
+ for (const attrValue of attrClause.values) {
111
+ const grantersForAttr = granters[attrValue] || new Set();
112
+ if (grantersForAttr.size) {
113
+ individualValueClauses.push({
111
114
  op: 'anyOf',
112
- kases: grantsForTerm,
115
+ granters: Array.from(grantersForAttr.values()),
113
116
  });
114
117
  }
115
118
  }
119
+ // Use proper boolean operation with wrapped values.
116
120
  const op = booleanOperatorFor(attrClause.def?.rule);
117
121
  kcs.push({
118
122
  op,
119
- children: ccv,
123
+ children: individualValueClauses,
120
124
  });
121
125
  }
122
- return simplify(kcs, kasInfo);
126
+ return simplify(kcs);
123
127
  }
124
128
 
125
- function simplify(
126
- clauses: ComplexBooleanClause[],
127
- kasInfo: Record<string, KeyAccessServer>
128
- ): KeySplitStep[] {
129
- const conjunction: Record<string, string[]> = {};
130
- function keyFor(kases: string[]): string {
131
- const k = Array.from(new Set([kases])).sort();
132
- return k.join('|');
129
+ function simplify(clauses: ComplexBooleanClause[]): KeySplitStep[] {
130
+ const conjunction: Record<string, KeyHolder[]> = {};
131
+ function keyFor(granters: KeyHolder[]): string {
132
+ const keyParts = granters
133
+ .map((keyHolder) => {
134
+ const keyParts: string[] = [];
135
+ if ('kid' in keyHolder) {
136
+ keyParts.push(keyHolder.kasUri);
137
+ keyParts.push(keyHolder.kid);
138
+ } else {
139
+ keyParts.push(keyHolder.uri);
140
+ keyParts.push('');
141
+ }
142
+ return [keyHolder, keyParts.join('/')] as [KeyHolder, string];
143
+ })
144
+ .sort(([, sortKeyA], [, sortKeyB]) => {
145
+ return sortKeyA.localeCompare(sortKeyB);
146
+ })
147
+ .map(([, sortKey]) => {
148
+ return sortKey;
149
+ });
150
+ return keyParts.join(':');
133
151
  }
134
152
  for (const { op, children } of clauses) {
135
153
  if (!children) {
136
154
  continue;
137
155
  }
138
156
  if (op === 'anyOf') {
139
- const anyKids = [];
157
+ const granters: KeyHolder[] = [];
140
158
  for (const bc of children) {
141
159
  if (bc.op != 'anyOf') {
142
160
  throw new Error('internal: autoconfigure inversion in disjunction');
143
161
  }
144
- if (!bc.kases?.length) {
162
+ if (!bc.granters.length) {
145
163
  continue;
146
164
  }
147
- anyKids.push(...bc.kases);
165
+ granters.push(...bc.granters);
148
166
  }
149
- if (!anyKids?.length) {
167
+ if (!granters.length) {
150
168
  continue;
151
169
  }
152
- const k = keyFor(anyKids);
153
- conjunction[k] = anyKids;
170
+ const k = keyFor(granters);
171
+ conjunction[k] = granters;
154
172
  } else {
155
173
  for (const bc of children) {
156
174
  if (bc.op != 'anyOf') {
157
- throw new Error('insternal: autoconfigure inversion in conjunction');
175
+ throw new Error('internal: autoconfigure inversion in conjunction');
158
176
  }
159
- if (!bc.kases?.length) {
177
+ if (!bc.granters.length) {
160
178
  continue;
161
179
  }
162
- const k = keyFor(bc.kases);
163
- conjunction[k] = bc.kases;
180
+ const k = keyFor(bc.granters);
181
+ conjunction[k] = bc.granters;
164
182
  }
165
183
  }
166
184
  }
@@ -173,7 +191,15 @@ function simplify(
173
191
  i += 1;
174
192
  const sid = '' + i;
175
193
  for (const kas of conjunction[k]) {
176
- t.push({ sid, kas: kasInfo[kas] });
194
+ if ('kid' in kas) {
195
+ t.push({ sid, kas: kas, kid: kas.kid });
196
+ } else if (kas.publicKey && kas.publicKey.publicKey.case === 'cached') {
197
+ kas.publicKey.publicKey.value.keys.forEach((key) => {
198
+ t.push({ sid, kas: kas, kid: key.kid });
199
+ });
200
+ } else {
201
+ t.push({ sid, kas: kas });
202
+ }
177
203
  }
178
204
  }
179
205
  return t;
package/src/seekable.ts CHANGED
@@ -33,12 +33,30 @@ export const fromBrowserFile = (fileRef: Blob): Chunker => {
33
33
  };
34
34
  };
35
35
 
36
+ /**
37
+ * Creates a seekable object from a buffer.
38
+ * @param source A Uint8Array to read from.
39
+ * If byteStart and byteEnd are not provided, reads the entire array.
40
+ * If byteStart is provided, reads from that index to the end of the array.
41
+ * If byteEnd is provided, reads from byteStart to byteEnd (exclusive).
42
+ * If both byteStart and byteEnd are provided, reads from byteStart to byteEnd (exclusive).
43
+ * @returns A promise that resolves to a Uint8Array containing the requested data.
44
+ */
36
45
  export const fromBuffer = (source: Uint8Array): Chunker => {
37
46
  return (byteStart?: number, byteEnd?: number) => {
38
47
  return Promise.resolve(source.slice(byteStart, byteEnd));
39
48
  };
40
49
  };
41
50
 
51
+ /**
52
+ * Creates a seekable object from a string.
53
+ * @param source A string to read from.
54
+ * If byteStart and byteEnd are not provided, reads the entire string.
55
+ * If byteStart is provided, reads from that index to the end of the string.
56
+ * If byteEnd is provided, reads from byteStart to byteEnd (exclusive).
57
+ * If both byteStart and byteEnd are provided, reads from byteStart to byteEnd (exclusive).
58
+ * @returns A promise that resolves to a Uint8Array containing the requested data.
59
+ */
42
60
  export const fromString = (source: string): Chunker => {
43
61
  return fromBuffer(new TextEncoder().encode(source));
44
62
  };
@@ -110,6 +128,12 @@ export const fromUrl = async (location: string): Promise<Chunker> => {
110
128
  };
111
129
  };
112
130
 
131
+ /**
132
+ * Creates a seekable object from a source.
133
+ * @param source A Source object containing the type and location of the data.
134
+ * @returns A promise that resolves to a Chunker function.
135
+ * @throws ConfigurationError if the source type is not supported or the location is invalid.
136
+ */
113
137
  export const fromSource = async ({ type, location }: Source): Promise<Chunker> => {
114
138
  switch (type) {
115
139
  case 'buffer':
@@ -139,6 +163,13 @@ export const fromSource = async ({ type, location }: Source): Promise<Chunker> =
139
163
  }
140
164
  };
141
165
 
166
+ /**
167
+ * Converts a Source object to a ReadableStream.
168
+ * @param source A Source object containing the type and location of the data.
169
+ * Converts the source to a ReadableStream of Uint8Array.
170
+ * This is useful for streaming data from various sources like files, remote URLs, or chunkers.
171
+ * @returns A ReadableStream of Uint8Array.
172
+ */
142
173
  export async function sourceToStream(source: Source): Promise<ReadableStream<Uint8Array>> {
143
174
  switch (source.type) {
144
175
  case 'stream':
package/src/utils.ts CHANGED
@@ -3,8 +3,11 @@ import { exportSPKI, importX509 } from 'jose';
3
3
  import { base64 } from './encodings/index.js';
4
4
  import { pemCertToCrypto, pemPublicToCrypto } from './nanotdf-crypto/pemPublicToCrypto.js';
5
5
  import { ConfigurationError } from './errors.js';
6
+ import { RewrapResponse } from './platform/kas/kas_pb.js';
6
7
  import { ConnectError } from '@connectrpc/connect';
7
8
 
9
+ const REQUIRED_OBLIGATIONS_METADATA_KEY = 'X-Required-Obligations';
10
+
8
11
  /**
9
12
  * Check to see if the given URL is 'secure'. This assumes:
10
13
  *
@@ -35,6 +38,12 @@ export function validateSecureUrl(url: string): boolean {
35
38
  return true;
36
39
  }
37
40
 
41
+ /**
42
+ * Pads a URL with a trailing slash if it does not already have one.
43
+ * This is useful for ensuring that URLs are in a consistent format.
44
+ * @param u The URL to pad.
45
+ * @returns The padded URL.
46
+ */
38
47
  export function padSlashToUrl(u: string): string {
39
48
  if (u.endsWith('/')) {
40
49
  return u;
@@ -42,10 +51,21 @@ export function padSlashToUrl(u: string): string {
42
51
  return `${u}/`;
43
52
  }
44
53
 
54
+ /**
55
+ * Checks if the current environment is a browser.
56
+ * This is useful for determining if certain APIs or features are available.
57
+ * @returns true if running in a browser, false otherwise.
58
+ */
45
59
  export function isBrowser() {
46
60
  return typeof window !== 'undefined'; // eslint-disable-line
47
61
  }
48
62
 
63
+ /**
64
+ * Removes trailing characters from a string.
65
+ * @param str The string to trim.
66
+ * @param suffix The suffix to remove (default is a single space).
67
+ * @returns The trimmed string.
68
+ */
49
69
  export const rstrip = (str: string, suffix = ' '): string => {
50
70
  while (str && suffix && str.endsWith(suffix)) {
51
71
  str = str.slice(0, -suffix.length);
@@ -92,6 +112,15 @@ export const estimateSkewFromHeaders = (headers: AnyHeaders, dateNowBefore?: num
92
112
  return Math.round((deltaBefore + deltaAfter) / 2);
93
113
  };
94
114
 
115
+ /**
116
+ * Adds new lines to a string every 64 characters.
117
+ * @param str A string to add new lines to.
118
+ * This function takes a string and adds new lines every 64 characters.
119
+ * If the string is empty or undefined, it returns the original string.
120
+ * This is useful for formatting long strings, such as public keys or certificates,
121
+ * to ensure they are properly formatted for PEM encoding.
122
+ * @returns The formatted string with new lines added.
123
+ */
95
124
  export function addNewLines(str: string): string {
96
125
  if (!str) {
97
126
  return str;
@@ -105,6 +134,11 @@ export function addNewLines(str: string): string {
105
134
  return finalString;
106
135
  }
107
136
 
137
+ /**
138
+ * Creates a PEM-encoded string from a public key.
139
+ * @param publicKey The public key to convert.
140
+ * @returns A promise that resolves to a PEM-encoded string.
141
+ */
108
142
  export async function cryptoPublicToPem(publicKey: CryptoKey): Promise<string> {
109
143
  if (publicKey.type !== 'public') {
110
144
  throw new ConfigurationError('incorrect key type');
@@ -116,6 +150,11 @@ export async function cryptoPublicToPem(publicKey: CryptoKey): Promise<string> {
116
150
  return `-----BEGIN PUBLIC KEY-----\r\n${pem}-----END PUBLIC KEY-----`;
117
151
  }
118
152
 
153
+ /**
154
+ * Converts a PEM-encoded public key to a CryptoKey.
155
+ * @param pem The PEM-encoded public key.
156
+ * @returns A promise that resolves to a CryptoKey.
157
+ */
119
158
  export async function pemToCryptoPublicKey(pem: string): Promise<CryptoKey> {
120
159
  if (/-----BEGIN PUBLIC KEY-----/.test(pem)) {
121
160
  return pemPublicToCrypto(pem);
@@ -128,6 +167,15 @@ export async function pemToCryptoPublicKey(pem: string): Promise<CryptoKey> {
128
167
  throw new TypeError(`unsupported pem type [${pem}]`);
129
168
  }
130
169
 
170
+ /**
171
+ * Extracts the PEM-encoded public key from a key string.
172
+ * @param keyString A string containing a public key or certificate.
173
+ * This function extracts the PEM-encoded public key from a given key string.
174
+ * If the key string contains a certificate, it imports the certificate and exports
175
+ * the public key in PEM format. If the key string is already in PEM format, it returns
176
+ * the key string as is.
177
+ * @returns A promise that resolves to a PEM-encoded public key.
178
+ */
131
179
  export async function extractPemFromKeyString(keyString: string): Promise<string> {
132
180
  let pem: string = keyString;
133
181
 
@@ -143,6 +191,12 @@ export async function extractPemFromKeyString(keyString: string): Promise<string
143
191
 
144
192
  /**
145
193
  * Extracts the error message from an RPC catch error.
194
+ * @param error An error object, typically from a network request.
195
+ * This function extracts the error message from a ConnectError or a generic Error.
196
+ * If the error is a ConnectError or a standard Error, it returns the message.
197
+ * If the error is of an unknown type, it returns a default message indicating
198
+ * that an unknown network error occurred.
199
+ * @returns The extracted error message.
146
200
  */
147
201
  export function extractRpcErrorMessage(error: unknown): string {
148
202
  if (error instanceof ConnectError || error instanceof Error) {
@@ -153,8 +207,11 @@ export function extractRpcErrorMessage(error: unknown): string {
153
207
 
154
208
  /**
155
209
  * Converts a KAS endpoint URL to a platform URL.
156
- * If the KAS endpoint ends with '/kas', it returns the host url
157
- * Otherwise, it returns the original KAS endpoint.
210
+ * @param endpoint The KAS endpoint URL to extract the platform URL from.
211
+ * This function extracts the base URL from a KAS endpoint URL.
212
+ * It removes any trailing slashes and specific path segments related to rewrap or kas.
213
+ * This is useful for obtaining the base URL for further API requests.
214
+ * @returns The base URL of the platform.
158
215
  */
159
216
  export function getPlatformUrlFromKasEndpoint(endpoint: string): string {
160
217
  let result = endpoint || '';
@@ -169,3 +226,32 @@ export function getPlatformUrlFromKasEndpoint(endpoint: string): string {
169
226
  }
170
227
  return result;
171
228
  }
229
+
230
+ /**
231
+ * Retrieves the fully qualified Obligations (values) that must be fulfilled from a rewrap response.
232
+ */
233
+ export function getRequiredObligationFQNs(response: RewrapResponse) {
234
+ const requiredObligations = new Set<string>();
235
+
236
+ // Loop through response key access object results, checking proto values/types for a metadata key
237
+ // that matches the expected KAS-provided fulfillable obligations list.
238
+ for (const resp of response.responses) {
239
+ for (const result of resp.results) {
240
+ if (!result.metadata.hasOwnProperty(REQUIRED_OBLIGATIONS_METADATA_KEY)) {
241
+ continue;
242
+ }
243
+ const value = result.metadata[REQUIRED_OBLIGATIONS_METADATA_KEY];
244
+ if (value?.kind.case !== 'listValue') {
245
+ continue;
246
+ }
247
+ const obligations = value.kind.value.values;
248
+ for (const obligation of obligations) {
249
+ if (obligation.kind.case === 'stringValue') {
250
+ requiredObligations.add(obligation.kind.value.toLowerCase());
251
+ }
252
+ }
253
+ }
254
+ }
255
+
256
+ return [...requiredObligations.values()];
257
+ }
@@ -2,6 +2,7 @@ import { canonicalizeEx } from 'json-canonicalize';
2
2
  import { SignJWT, jwtVerify } from 'jose';
3
3
  import { base64, hex } from '../../src/encodings/index.js';
4
4
  import { ConfigurationError, IntegrityError, InvalidFileError } from '../../src/errors.js';
5
+ import { tdfSpecVersion, version as sdkVersion } from '../../src/version.js';
5
6
 
6
7
  export type AssertionKeyAlg = 'ES256' | 'RS256' | 'HS256';
7
8
  export type AssertionType = 'handling' | 'other';
@@ -43,7 +44,9 @@ export type AssertionPayload = {
43
44
  * @returns the hexadecimal string representation of the hash
44
45
  */
45
46
  export async function hash(a: Assertion): Promise<string> {
46
- const result = canonicalizeEx(a, { exclude: ['binding', 'hash', 'sign', 'verify'] });
47
+ const result = canonicalizeEx(a, {
48
+ exclude: ['binding', 'hash', 'sign', 'verify', 'signingKey'],
49
+ });
47
50
 
48
51
  const hash = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(result));
49
52
  return hex.encodeArrayBuffer(hash);
@@ -227,6 +230,54 @@ export type AssertionVerificationKeys = {
227
230
  Keys: Record<string, AssertionKey>;
228
231
  };
229
232
 
233
+ /**
234
+ * Metadata structure for system information.
235
+ */
236
+ type SystemMetadata = {
237
+ tdf_spec_version: string;
238
+ creation_date: string;
239
+ sdk_version: string;
240
+ browser_user_agent?: string;
241
+ // platform is often the same as os in browser, but kept for consistency with original Go struct
242
+ platform?: string;
243
+ };
244
+
245
+ /**
246
+ * Returns a default assertion configuration populated with system metadata.
247
+ */
248
+ export function getSystemMetadataAssertionConfig(): AssertionConfig {
249
+ let platformIdentifier = 'unknown';
250
+ if (typeof navigator !== 'undefined') {
251
+ if (typeof navigator.userAgent === 'string') {
252
+ platformIdentifier = navigator.userAgent;
253
+ } else if (typeof navigator.platform === 'string') {
254
+ platformIdentifier = navigator.platform; // Deprecated, but used as a fallback
255
+ }
256
+ }
257
+
258
+ const metadata: SystemMetadata = {
259
+ tdf_spec_version: tdfSpecVersion,
260
+ creation_date: new Date().toISOString(),
261
+ sdk_version: `JS-${sdkVersion}`, // Prefixed to distinguish from Go SDK version
262
+ browser_user_agent: typeof navigator !== 'undefined' ? navigator.userAgent : 'unknown',
263
+ platform: platformIdentifier,
264
+ };
265
+
266
+ const metadataJSON = JSON.stringify(metadata);
267
+
268
+ return {
269
+ id: 'system-metadata', // Consistent ID for this type of assertion
270
+ type: 'other', // General type for metadata assertions
271
+ scope: 'tdo', // Metadata typically applies to the TDF Data Object as a whole
272
+ appliesToState: 'unencrypted', // Metadata itself is not encrypted by this assertion's scope
273
+ statement: {
274
+ format: 'json',
275
+ schema: 'system-metadata-v1', // A schema name for this metadata
276
+ value: metadataJSON,
277
+ },
278
+ };
279
+ }
280
+
230
281
  function concatenateUint8Arrays(array1: Uint8Array, array2: Uint8Array): Uint8Array {
231
282
  const combinedLength = array1.length + array2.length;
232
283
  const combinedArray = new Uint8Array(combinedLength);
@@ -21,6 +21,7 @@ export class DecoratedReadableStream {
21
21
  metadata?: Metadata;
22
22
  manifest: Manifest;
23
23
  fileStreamServiceWorker?: string;
24
+ requiredObligations?: string[];
24
25
 
25
26
  constructor(
26
27
  underlyingSource: UnderlyingSource & {
@@ -60,6 +61,14 @@ export class DecoratedReadableStream {
60
61
  async toString(): Promise<string> {
61
62
  return new Response(this.stream).text();
62
63
  }
64
+
65
+ /**
66
+ * The fully qualified obligations required to be fulfilled on stream contents
67
+ * are set as decoration during the decrypt flow.
68
+ */
69
+ obligations(): string[] {
70
+ return this.requiredObligations ?? [];
71
+ }
63
72
  }
64
73
 
65
74
  export function isDecoratedReadableStream(s: unknown): s is DecoratedReadableStream {
@@ -31,6 +31,7 @@ export type EncryptStreamMiddleware = (
31
31
 
32
32
  export type SplitStep = {
33
33
  kas: string;
34
+ kid?: string;
34
35
  sid?: string;
35
36
  };
36
37
 
@@ -50,6 +51,7 @@ export type EncryptParams = {
50
51
  splitPlan?: SplitStep[];
51
52
  streamMiddleware?: EncryptStreamMiddleware;
52
53
  assertionConfigs?: AssertionConfig[];
54
+ systemMetadataAssertion?: boolean;
53
55
  defaultKASEndpoint?: string;
54
56
 
55
57
  // Preferred wrapping key algorithm. Used when KID resolution is not available.
@@ -499,6 +501,19 @@ class EncryptParamsBuilder {
499
501
  this._params.assertionConfigs = assertionConfigs;
500
502
  return this;
501
503
  }
504
+
505
+ /**
506
+ * Specifies whether a default system metadata assertion should be automatically
507
+ * included during the encryption process.
508
+ *
509
+ * @param {boolean} systemMetadataAssertion - True to include the system metadata assertion, false otherwise.
510
+ * @returns {EncryptParamsBuilder} The current instance of the EncryptParamsBuilder for method chaining.
511
+ * @see {@link getSystemMetadataAssertionConfig}
512
+ */
513
+ withSystemMetadataAssertion(systemMetadataAssertion: boolean): EncryptParamsBuilder {
514
+ this._params.systemMetadataAssertion = systemMetadataAssertion;
515
+ return this;
516
+ }
502
517
  }
503
518
 
504
519
  export type DecryptKeyMiddleware = (key: Binary) => Promise<Binary>;
@@ -523,6 +538,7 @@ export type DecryptParams = {
523
538
  concurrencyLimit?: number;
524
539
  noVerifyAssertions?: boolean;
525
540
  wrappingKeyAlgorithm?: KasPublicKeyAlgorithm;
541
+ fulfillableObligationFQNs?: string[];
526
542
  };
527
543
 
528
544
  /**