@opentdf/sdk 0.2.0-beta.1758 → 0.2.0-beta.1941

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 (265) hide show
  1. package/README.md +45 -38
  2. package/dist/cjs/src/access.js +47 -11
  3. package/dist/cjs/src/auth/auth.js +5 -5
  4. package/dist/cjs/src/auth/oidc-clientcredentials-provider.js +1 -1
  5. package/dist/cjs/src/auth/oidc-externaljwt-provider.js +1 -1
  6. package/dist/cjs/src/auth/oidc-refreshtoken-provider.js +1 -1
  7. package/dist/cjs/src/auth/oidc.js +1 -1
  8. package/dist/cjs/src/auth/providers.js +1 -1
  9. package/dist/cjs/src/concurrency.js +3 -4
  10. package/dist/cjs/src/encodings/base64.js +4 -4
  11. package/dist/cjs/src/encodings/hex.js +5 -6
  12. package/dist/cjs/src/encodings/index.js +18 -8
  13. package/dist/cjs/src/errors.js +1 -1
  14. package/dist/cjs/src/index.js +28 -318
  15. package/dist/cjs/src/nanoclients.js +285 -0
  16. package/dist/cjs/src/nanoindex.js +47 -0
  17. package/dist/cjs/src/nanotdf/Client.js +18 -8
  18. package/dist/cjs/src/nanotdf/NanoTDF.js +1 -1
  19. package/dist/cjs/src/nanotdf/decrypt.js +2 -2
  20. package/dist/cjs/src/nanotdf/encrypt-dataset.js +2 -2
  21. package/dist/cjs/src/nanotdf/encrypt.js +2 -2
  22. package/dist/cjs/src/nanotdf/helpers/calculateByCurve.js +3 -4
  23. package/dist/cjs/src/nanotdf/helpers/getHkdfSalt.js +2 -2
  24. package/dist/cjs/src/nanotdf/models/Ciphers.js +3 -3
  25. package/dist/cjs/src/nanotdf/models/EcCurves.js +3 -3
  26. package/dist/cjs/src/nanotdf/models/Header.js +1 -1
  27. package/dist/cjs/src/nanotdf/models/Payload.js +1 -1
  28. package/dist/cjs/src/nanotdf/models/Policy/AbstractPolicy.js +1 -1
  29. package/dist/cjs/src/nanotdf/models/Policy/EmbeddedPolicy.js +1 -1
  30. package/dist/cjs/src/nanotdf/models/Policy/PolicyFactory.js +1 -1
  31. package/dist/cjs/src/nanotdf/models/ResourceLocator.js +1 -1
  32. package/dist/cjs/src/nanotdf/models/Signature.js +1 -1
  33. package/dist/cjs/src/nanotdf-crypto/ciphers.js +1 -1
  34. package/dist/cjs/src/nanotdf-crypto/decrypt.js +2 -2
  35. package/dist/cjs/src/nanotdf-crypto/digest.js +2 -2
  36. package/dist/cjs/src/nanotdf-crypto/ecdsaSignature.js +4 -5
  37. package/dist/cjs/src/nanotdf-crypto/encrypt.js +2 -2
  38. package/dist/cjs/src/nanotdf-crypto/exportCryptoKey.js +2 -2
  39. package/dist/cjs/src/nanotdf-crypto/generateKeyPair.js +2 -2
  40. package/dist/cjs/src/nanotdf-crypto/generateRandomNumber.js +2 -2
  41. package/dist/cjs/src/nanotdf-crypto/index.js +21 -13
  42. package/dist/cjs/src/nanotdf-crypto/keyAgreement.js +10 -8
  43. package/dist/cjs/src/nanotdf-crypto/pemPublicToCrypto.js +20 -11
  44. package/dist/cjs/src/opentdf.js +243 -0
  45. package/dist/cjs/src/policy/api.js +2 -3
  46. package/dist/cjs/src/policy/granter.js +3 -4
  47. package/dist/cjs/src/seekable.js +157 -0
  48. package/dist/cjs/src/tdf/AttributeObject.js +2 -4
  49. package/dist/cjs/src/tdf/Policy.js +1 -2
  50. package/dist/cjs/src/utils.js +12 -14
  51. package/dist/cjs/src/version.js +6 -2
  52. package/dist/cjs/tdf3/index.js +27 -15
  53. package/dist/cjs/tdf3/src/assertions.js +25 -11
  54. package/dist/cjs/tdf3/src/binary.js +1 -1
  55. package/dist/cjs/tdf3/src/ciphers/aes-gcm-cipher.js +1 -1
  56. package/dist/cjs/tdf3/src/ciphers/symmetric-cipher-base.js +1 -1
  57. package/dist/cjs/tdf3/src/client/DecoratedReadableStream.js +7 -74
  58. package/dist/cjs/tdf3/src/client/builders.js +26 -22
  59. package/dist/cjs/tdf3/src/client/index.js +88 -61
  60. package/dist/cjs/tdf3/src/client/validation.js +3 -3
  61. package/dist/cjs/tdf3/src/crypto/crypto-utils.js +1 -1
  62. package/dist/cjs/tdf3/src/crypto/index.js +18 -18
  63. package/dist/cjs/tdf3/src/index.js +22 -11
  64. package/dist/cjs/tdf3/src/models/attribute-set.js +1 -1
  65. package/dist/cjs/tdf3/src/models/encryption-information.js +3 -3
  66. package/dist/cjs/tdf3/src/models/key-access.js +67 -35
  67. package/dist/cjs/tdf3/src/models/policy.js +3 -3
  68. package/dist/cjs/tdf3/src/tdf.js +177 -151
  69. package/dist/cjs/tdf3/src/utils/buffer-crc32.js +2 -3
  70. package/dist/cjs/tdf3/src/utils/index.js +30 -28
  71. package/dist/cjs/tdf3/src/utils/keysplit.js +4 -5
  72. package/dist/cjs/tdf3/src/utils/unwrap.js +21 -0
  73. package/dist/cjs/tdf3/src/utils/zip-reader.js +4 -4
  74. package/dist/cjs/tdf3/src/utils/zip-writer.js +4 -4
  75. package/dist/types/src/access.d.ts +3 -0
  76. package/dist/types/src/access.d.ts.map +1 -1
  77. package/dist/types/src/auth/providers.d.ts.map +1 -1
  78. package/dist/types/src/index.d.ts +5 -136
  79. package/dist/types/src/index.d.ts.map +1 -1
  80. package/dist/types/src/nanoclients.d.ts +107 -0
  81. package/dist/types/src/nanoclients.d.ts.map +1 -0
  82. package/dist/types/src/nanoindex.d.ts +5 -0
  83. package/dist/types/src/nanoindex.d.ts.map +1 -0
  84. package/dist/types/src/nanotdf/enum/CipherEnum.d.ts +1 -1
  85. package/dist/types/src/nanotdf/enum/CipherEnum.d.ts.map +1 -1
  86. package/dist/types/src/nanotdf/enum/PolicyTypeEnum.d.ts +1 -1
  87. package/dist/types/src/nanotdf/enum/PolicyTypeEnum.d.ts.map +1 -1
  88. package/dist/types/src/nanotdf/models/DefaultParams.d.ts +1 -1
  89. package/dist/types/src/nanotdf/models/ResourceLocator.d.ts.map +1 -1
  90. package/dist/types/src/nanotdf-crypto/generateKeyPair.d.ts +1 -1
  91. package/dist/types/src/nanotdf-crypto/generateKeyPair.d.ts.map +1 -1
  92. package/dist/types/src/nanotdf-crypto/generateRandomNumber.d.ts +1 -1
  93. package/dist/types/src/nanotdf-crypto/generateRandomNumber.d.ts.map +1 -1
  94. package/dist/types/src/nanotdf-crypto/index.d.ts +2 -3
  95. package/dist/types/src/nanotdf-crypto/index.d.ts.map +1 -1
  96. package/dist/types/src/nanotdf-crypto/keyAgreement.d.ts.map +1 -1
  97. package/dist/types/src/opentdf.d.ts +106 -0
  98. package/dist/types/src/opentdf.d.ts.map +1 -0
  99. package/dist/types/src/seekable.d.ts +39 -0
  100. package/dist/types/src/seekable.d.ts.map +1 -0
  101. package/dist/types/src/tdf/AttributeObject.d.ts +0 -2
  102. package/dist/types/src/tdf/AttributeObject.d.ts.map +1 -1
  103. package/dist/types/src/tdf/NanoTDF/NanoTDF.d.ts +2 -2
  104. package/dist/types/src/tdf/NanoTDF/NanoTDF.d.ts.map +1 -1
  105. package/dist/types/src/tdf/Policy.d.ts.map +1 -1
  106. package/dist/types/src/tdf/PolicyObject.d.ts +0 -1
  107. package/dist/types/src/tdf/PolicyObject.d.ts.map +1 -1
  108. package/dist/types/src/utils.d.ts +0 -1
  109. package/dist/types/src/utils.d.ts.map +1 -1
  110. package/dist/types/src/version.d.ts +4 -0
  111. package/dist/types/src/version.d.ts.map +1 -1
  112. package/dist/types/tdf3/index.d.ts +3 -2
  113. package/dist/types/tdf3/index.d.ts.map +1 -1
  114. package/dist/types/tdf3/src/assertions.d.ts +3 -3
  115. package/dist/types/tdf3/src/assertions.d.ts.map +1 -1
  116. package/dist/types/tdf3/src/client/DecoratedReadableStream.d.ts +1 -13
  117. package/dist/types/tdf3/src/client/DecoratedReadableStream.d.ts.map +1 -1
  118. package/dist/types/tdf3/src/client/builders.d.ts +43 -37
  119. package/dist/types/tdf3/src/client/builders.d.ts.map +1 -1
  120. package/dist/types/tdf3/src/client/index.d.ts +8 -9
  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/crypto/crypto-utils.d.ts.map +1 -1
  125. package/dist/types/tdf3/src/index.d.ts +1 -1
  126. package/dist/types/tdf3/src/index.d.ts.map +1 -1
  127. package/dist/types/tdf3/src/models/key-access.d.ts +63 -15
  128. package/dist/types/tdf3/src/models/key-access.d.ts.map +1 -1
  129. package/dist/types/tdf3/src/models/manifest.d.ts +2 -0
  130. package/dist/types/tdf3/src/models/manifest.d.ts.map +1 -1
  131. package/dist/types/tdf3/src/models/policy.d.ts +0 -1
  132. package/dist/types/tdf3/src/models/policy.d.ts.map +1 -1
  133. package/dist/types/tdf3/src/tdf.d.ts +20 -24
  134. package/dist/types/tdf3/src/tdf.d.ts.map +1 -1
  135. package/dist/types/tdf3/src/utils/index.d.ts +0 -2
  136. package/dist/types/tdf3/src/utils/index.d.ts.map +1 -1
  137. package/dist/types/tdf3/src/utils/unwrap.d.ts +2 -0
  138. package/dist/types/tdf3/src/utils/unwrap.d.ts.map +1 -0
  139. package/dist/types/tdf3/src/utils/zip-reader.d.ts +1 -1
  140. package/dist/types/tdf3/src/utils/zip-reader.d.ts.map +1 -1
  141. package/dist/types/tdf3/src/utils/zip-writer.d.ts +2 -2
  142. package/dist/web/src/access.js +40 -7
  143. package/dist/web/src/auth/auth.js +1 -1
  144. package/dist/web/src/auth/oidc-clientcredentials-provider.js +1 -1
  145. package/dist/web/src/auth/oidc-externaljwt-provider.js +1 -1
  146. package/dist/web/src/auth/oidc-refreshtoken-provider.js +1 -1
  147. package/dist/web/src/auth/oidc.js +1 -1
  148. package/dist/web/src/auth/providers.js +1 -1
  149. package/dist/web/src/concurrency.js +1 -1
  150. package/dist/web/src/encodings/base64.js +1 -1
  151. package/dist/web/src/encodings/hex.js +1 -1
  152. package/dist/web/src/errors.js +1 -1
  153. package/dist/web/src/index.js +6 -310
  154. package/dist/web/src/nanoclients.js +280 -0
  155. package/dist/web/src/nanoindex.js +5 -0
  156. package/dist/web/src/nanotdf/Client.js +1 -1
  157. package/dist/web/src/nanotdf/NanoTDF.js +1 -1
  158. package/dist/web/src/nanotdf/encrypt-dataset.js +1 -1
  159. package/dist/web/src/nanotdf/encrypt.js +1 -1
  160. package/dist/web/src/nanotdf/models/Ciphers.js +1 -1
  161. package/dist/web/src/nanotdf/models/EcCurves.js +1 -1
  162. package/dist/web/src/nanotdf/models/Header.js +1 -1
  163. package/dist/web/src/nanotdf/models/Payload.js +1 -1
  164. package/dist/web/src/nanotdf/models/Policy/AbstractPolicy.js +1 -1
  165. package/dist/web/src/nanotdf/models/Policy/EmbeddedPolicy.js +1 -1
  166. package/dist/web/src/nanotdf/models/Policy/PolicyFactory.js +1 -1
  167. package/dist/web/src/nanotdf/models/ResourceLocator.js +1 -1
  168. package/dist/web/src/nanotdf/models/Signature.js +1 -1
  169. package/dist/web/src/nanotdf-crypto/ciphers.js +1 -1
  170. package/dist/web/src/nanotdf-crypto/ecdsaSignature.js +1 -1
  171. package/dist/web/src/nanotdf-crypto/generateKeyPair.js +2 -2
  172. package/dist/web/src/nanotdf-crypto/generateRandomNumber.js +2 -2
  173. package/dist/web/src/nanotdf-crypto/index.js +3 -4
  174. package/dist/web/src/nanotdf-crypto/keyAgreement.js +9 -6
  175. package/dist/web/src/nanotdf-crypto/pemPublicToCrypto.js +1 -1
  176. package/dist/web/src/opentdf.js +234 -0
  177. package/dist/web/src/policy/api.js +1 -1
  178. package/dist/web/src/policy/granter.js +1 -1
  179. package/dist/web/src/seekable.js +148 -0
  180. package/dist/web/src/tdf/AttributeObject.js +1 -2
  181. package/dist/web/src/tdf/Policy.js +1 -2
  182. package/dist/web/src/utils.js +2 -3
  183. package/dist/web/src/version.js +5 -1
  184. package/dist/web/tdf3/index.js +3 -2
  185. package/dist/web/tdf3/src/assertions.js +21 -6
  186. package/dist/web/tdf3/src/binary.js +1 -1
  187. package/dist/web/tdf3/src/ciphers/aes-gcm-cipher.js +1 -1
  188. package/dist/web/tdf3/src/ciphers/symmetric-cipher-base.js +1 -1
  189. package/dist/web/tdf3/src/client/DecoratedReadableStream.js +4 -68
  190. package/dist/web/tdf3/src/client/builders.js +26 -22
  191. package/dist/web/tdf3/src/client/index.js +69 -52
  192. package/dist/web/tdf3/src/client/validation.js +1 -1
  193. package/dist/web/tdf3/src/crypto/crypto-utils.js +1 -1
  194. package/dist/web/tdf3/src/crypto/index.js +1 -1
  195. package/dist/web/tdf3/src/index.js +2 -2
  196. package/dist/web/tdf3/src/models/attribute-set.js +1 -1
  197. package/dist/web/tdf3/src/models/encryption-information.js +3 -3
  198. package/dist/web/tdf3/src/models/key-access.js +47 -24
  199. package/dist/web/tdf3/src/models/policy.js +1 -1
  200. package/dist/web/tdf3/src/tdf.js +149 -130
  201. package/dist/web/tdf3/src/utils/buffer-crc32.js +1 -1
  202. package/dist/web/tdf3/src/utils/index.js +1 -5
  203. package/dist/web/tdf3/src/utils/keysplit.js +1 -1
  204. package/dist/web/tdf3/src/utils/unwrap.js +18 -0
  205. package/dist/web/tdf3/src/utils/zip-reader.js +1 -1
  206. package/dist/web/tdf3/src/utils/zip-writer.js +1 -1
  207. package/package.json +45 -42
  208. package/src/access.ts +37 -1
  209. package/src/index.ts +5 -435
  210. package/src/nanoclients.ts +405 -0
  211. package/src/nanoindex.ts +4 -0
  212. package/src/nanotdf-crypto/generateKeyPair.ts +1 -1
  213. package/src/nanotdf-crypto/generateRandomNumber.ts +1 -1
  214. package/src/nanotdf-crypto/index.ts +2 -3
  215. package/src/nanotdf-crypto/keyAgreement.ts +14 -7
  216. package/src/opentdf.ts +441 -0
  217. package/{tdf3/src/utils/chunkers.ts → src/seekable.ts} +69 -20
  218. package/src/tdf/AttributeObject.ts +0 -3
  219. package/src/tdf/Policy.ts +0 -1
  220. package/src/tdf/PolicyObject.ts +0 -1
  221. package/src/utils.ts +1 -3
  222. package/src/version.ts +5 -0
  223. package/tdf3/index.ts +14 -2
  224. package/tdf3/src/assertions.ts +33 -8
  225. package/tdf3/src/client/DecoratedReadableStream.ts +2 -78
  226. package/tdf3/src/client/builders.ts +44 -26
  227. package/tdf3/src/client/index.ts +101 -86
  228. package/tdf3/src/index.ts +1 -1
  229. package/tdf3/src/models/encryption-information.ts +2 -2
  230. package/tdf3/src/models/key-access.ts +120 -38
  231. package/tdf3/src/models/manifest.ts +3 -0
  232. package/tdf3/src/models/policy.ts +0 -1
  233. package/tdf3/src/tdf.ts +251 -207
  234. package/tdf3/src/utils/index.ts +0 -5
  235. package/tdf3/src/utils/unwrap.ts +17 -0
  236. package/tdf3/src/utils/zip-reader.ts +1 -1
  237. package/dist/cjs/src/nanotdf-crypto/importRawKey.js +0 -18
  238. package/dist/cjs/tdf3/src/templates/default.html.js +0 -98
  239. package/dist/cjs/tdf3/src/templates/escaper.js +0 -15
  240. package/dist/cjs/tdf3/src/templates/index.js +0 -12
  241. package/dist/cjs/tdf3/src/utils/chunkers.js +0 -114
  242. package/dist/cjs/tdf3/src/version.js +0 -6
  243. package/dist/types/src/nanotdf-crypto/importRawKey.d.ts +0 -13
  244. package/dist/types/src/nanotdf-crypto/importRawKey.d.ts.map +0 -1
  245. package/dist/types/tdf3/src/templates/default.html.d.ts +0 -8
  246. package/dist/types/tdf3/src/templates/default.html.d.ts.map +0 -1
  247. package/dist/types/tdf3/src/templates/escaper.d.ts +0 -6
  248. package/dist/types/tdf3/src/templates/escaper.d.ts.map +0 -1
  249. package/dist/types/tdf3/src/templates/index.d.ts +0 -3
  250. package/dist/types/tdf3/src/templates/index.d.ts.map +0 -1
  251. package/dist/types/tdf3/src/utils/chunkers.d.ts +0 -29
  252. package/dist/types/tdf3/src/utils/chunkers.d.ts.map +0 -1
  253. package/dist/types/tdf3/src/version.d.ts +0 -3
  254. package/dist/types/tdf3/src/version.d.ts.map +0 -1
  255. package/dist/web/src/nanotdf-crypto/importRawKey.js +0 -15
  256. package/dist/web/tdf3/src/templates/default.html.js +0 -96
  257. package/dist/web/tdf3/src/templates/escaper.js +0 -10
  258. package/dist/web/tdf3/src/templates/index.js +0 -3
  259. package/dist/web/tdf3/src/utils/chunkers.js +0 -107
  260. package/dist/web/tdf3/src/version.js +0 -3
  261. package/src/nanotdf-crypto/importRawKey.ts +0 -19
  262. package/tdf3/src/templates/default.html.ts +0 -105
  263. package/tdf3/src/templates/escaper.ts +0 -10
  264. package/tdf3/src/templates/index.ts +0 -2
  265. package/tdf3/src/version.ts +0 -2
package/src/opentdf.ts ADDED
@@ -0,0 +1,441 @@
1
+ import { type AuthProvider } from './auth/providers.js';
2
+ import { ConfigurationError, InvalidFileError } from './errors.js';
3
+ import { NanoTDFDatasetClient } from './nanoclients.js';
4
+ export { Client as TDF3Client } from '../tdf3/src/client/index.js';
5
+ import NanoTDF from './nanotdf/NanoTDF.js';
6
+ import decryptNanoTDF from './nanotdf/decrypt.js';
7
+ import Client from './nanotdf/Client.js';
8
+ import Header from './nanotdf/models/Header.js';
9
+ import { fromSource, sourceToStream, type Source } from './seekable.js';
10
+ import { Client as TDF3Client } from '../tdf3/src/client/index.js';
11
+ import { AssertionConfig, AssertionVerificationKeys } from '../tdf3/src/assertions.js';
12
+ import { type KasPublicKeyAlgorithm, OriginAllowList, isPublicKeyAlgorithm } from './access.js';
13
+ import { type Manifest } from '../tdf3/src/models/manifest.js';
14
+
15
+ export { type KasPublicKeyAlgorithm, isPublicKeyAlgorithm };
16
+
17
+ export type Keys = {
18
+ [keyID: string]: CryptoKey | CryptoKeyPair;
19
+ };
20
+
21
+ // Options when creating a new TDF object
22
+ // that are shared between all container types.
23
+ export type CreateOptions = {
24
+ // If the policy service should be used to control creation options
25
+ autoconfigure?: boolean;
26
+
27
+ // List of attributes that will be assigned to the object's policy
28
+ attributes?: string[];
29
+
30
+ // If set and positive, this represents the maxiumum number of bytes to read from a stream to encrypt.
31
+ // This is helpful for enforcing size limits and preventing DoS attacks.
32
+ byteLimit?: number;
33
+
34
+ // The KAS to use for creation, if none is specified by the attribute service.
35
+ defaultKASEndpoint?: string;
36
+
37
+ // Private (or shared) keys for signing assertions and bindings
38
+ signers?: Keys;
39
+
40
+ // Source of plaintext data
41
+ source: Source;
42
+ };
43
+
44
+ export type CreateNanoTDFOptions = CreateOptions & {
45
+ bindingType?: 'ecdsa' | 'gmac';
46
+
47
+ // When creating a new collection, use ECDSA binding with this key id from the signers,
48
+ // instead of the DEK.
49
+ ecdsaBindingKeyID?: string;
50
+
51
+ // When creating a new collection,
52
+ // use the key in the `signers` list with this id
53
+ // to generate a signature for each element.
54
+ // When absent, the nanotdf is unsigned.
55
+ signingKeyID?: string;
56
+ };
57
+
58
+ export type CreateNanoTDFCollectionOptions = CreateNanoTDFOptions & {
59
+ // The maximum number of key iterations to use for a single DEK.
60
+ maxKeyIterations?: number;
61
+ };
62
+
63
+ // Metadata for a TDF object.
64
+ export type Metadata = object;
65
+
66
+ // MIME type of the decrypted content.
67
+ export type MimeType = `${string}/${string}`;
68
+
69
+ // Template for a Key Access Object (KAO) to be filled in during encrypt.
70
+ export type SplitStep = {
71
+ // Which KAS to use to rewrap this segment of the key
72
+ kas: string;
73
+
74
+ // An identifier for a key segment.
75
+ // Leave empty to share the key.
76
+ sid?: string;
77
+ };
78
+
79
+ /// Options specific to the ZTDF container format.
80
+ export type CreateZTDFOptions = CreateOptions & {
81
+ // Configuration for bound metadata.
82
+ assertionConfigs?: AssertionConfig[];
83
+
84
+ // Unbound metadata (deprecated)
85
+ metadata?: Metadata;
86
+
87
+ // MIME type of the decrypted content. Used for display.
88
+ mimeType?: MimeType;
89
+
90
+ // How to split or share the data encryption key across multiple KASes.
91
+ splitPlan?: SplitStep[];
92
+
93
+ // The segment size for the content; smaller is slower, but allows faster random access.
94
+ // The current default is 1 MiB (2^20 bytes).
95
+ windowSize?: number;
96
+
97
+ // Preferred algorithm to use for Key Access Objects.
98
+ wrappingKeyAlgorithm?: KasPublicKeyAlgorithm;
99
+ };
100
+
101
+ // Settings for decrypting any variety of TDF file.
102
+ export type ReadOptions = {
103
+ // ciphertext
104
+ source: Source;
105
+ // list of KASes that may be contacted for a rewrap
106
+ allowedKASEndpoints?: string[];
107
+ // Optionally disable checking the allowlist
108
+ ignoreAllowlist?: boolean;
109
+ // Public (or shared) keys for verifying assertions
110
+ assertionVerificationKeys?: AssertionVerificationKeys;
111
+ // Optionally disable assertion verification
112
+ noVerify?: boolean;
113
+
114
+ // If set, prevents more than this number of concurrent requests to the KAS.
115
+ concurrencyLimit?: number;
116
+
117
+ // Type of key to use for wrapping responses.
118
+ wrappingKeyAlgorithm?: KasPublicKeyAlgorithm;
119
+ };
120
+
121
+ // Defaults and shared settings that are relevant to creating TDF objects.
122
+ export type OpenTDFOptions = {
123
+ // Policy service endpoint
124
+ policyEndpoint?: string;
125
+
126
+ // Auth provider for connections to the policy service and KASes.
127
+ authProvider: AuthProvider;
128
+
129
+ // Default settings for 'encrypt' type requests.
130
+ defaultCreateOptions?: Omit<CreateOptions, 'source'>;
131
+
132
+ // Default settings for 'decrypt' type requests.
133
+ defaultReadOptions?: Omit<ReadOptions, 'source'>;
134
+
135
+ // If we want to *not* send a DPoP token
136
+ disableDPoP?: boolean;
137
+
138
+ // Optional keys for DPoP requests to a server.
139
+ // These often must be registered via a DPoP flow with the IdP
140
+ // which is out of the scope of this library.
141
+ dpopKeys?: Promise<CryptoKeyPair>;
142
+
143
+ // Configuration options for the collection header cache.
144
+ rewrapCacheOptions?: RewrapCacheOptions;
145
+ };
146
+
147
+ export type DecoratedStream = ReadableStream<Uint8Array> & {
148
+ // If the source is a TDF3/ZTDF, and includes metadata, and it has been read.
149
+ metadata?: Promise<unknown>;
150
+ manifest?: Promise<Manifest>;
151
+ // If the source is a NanoTDF, this will be set.
152
+ header?: Header;
153
+ };
154
+
155
+ // Configuration options for the collection header cache.
156
+ export type RewrapCacheOptions = {
157
+ // If we should disable (bypass) the cache.
158
+ bypass?: boolean;
159
+
160
+ // Evict keys after this many milliseconds.
161
+ maxAge?: number;
162
+
163
+ // Check for expired keys once every this many milliseconds.
164
+ pollInterval?: number;
165
+ };
166
+
167
+ const defaultRewrapCacheOptions: Required<RewrapCacheOptions> = {
168
+ bypass: false,
169
+ maxAge: 300000,
170
+ pollInterval: 500,
171
+ };
172
+
173
+ // Cache for headers of nanotdf collections.
174
+ // This allows the SDK to quickly open multiple entries of the same collection.
175
+ // It has a demon that removes all keys that have not been accessed in the last 5 minutes.
176
+ // To cancel the demon, and clear the cache, call `close()`.
177
+ export class RewrapCache {
178
+ private cache?: Map<Uint8Array, { lastAccessTime: number; value: CryptoKey }>;
179
+ private closer?: ReturnType<typeof setInterval>;
180
+ constructor(opts?: RewrapCacheOptions) {
181
+ const { bypass, maxAge, pollInterval } = { ...defaultRewrapCacheOptions, ...opts };
182
+ if (bypass) {
183
+ return;
184
+ }
185
+ this.cache = new Map();
186
+ this.closer = setInterval(() => {
187
+ const now = Date.now();
188
+ const c = this.cache;
189
+ if (!c) {
190
+ return;
191
+ }
192
+ for (const [key, value] of c.entries()) {
193
+ if (now - value.lastAccessTime > maxAge) {
194
+ c.delete(key);
195
+ }
196
+ }
197
+ }, pollInterval);
198
+ }
199
+
200
+ get(key: Uint8Array): CryptoKey | undefined {
201
+ if (!this.cache) {
202
+ return undefined;
203
+ }
204
+ const entry = this.cache.get(key);
205
+ if (entry) {
206
+ entry.lastAccessTime = Date.now();
207
+ return entry.value;
208
+ }
209
+ return undefined;
210
+ }
211
+
212
+ set(key: Uint8Array, value: CryptoKey) {
213
+ if (!this.cache) {
214
+ return;
215
+ }
216
+ this.cache.set(key, { lastAccessTime: Date.now(), value });
217
+ }
218
+
219
+ close() {
220
+ if (this.closer !== undefined) {
221
+ clearInterval(this.closer);
222
+ delete this.closer;
223
+ delete this.cache;
224
+ }
225
+ }
226
+ }
227
+
228
+ // SDK for dealing with OpenTDF data and policy services.
229
+ export class OpenTDF {
230
+ // Configuration service and more is at this URL/connectRPC endpoint
231
+ readonly policyEndpoint: string;
232
+ readonly authProvider: AuthProvider;
233
+ readonly dpopEnabled: boolean;
234
+ defaultCreateOptions: Omit<CreateOptions, 'source'>;
235
+ defaultReadOptions: Omit<ReadOptions, 'source'>;
236
+ readonly dpopKeys: Promise<CryptoKeyPair>;
237
+
238
+ // Header cache for reading nanotdf collections
239
+ private readonly rewrapCache: RewrapCache;
240
+ private tdf3Client: TDF3Client;
241
+
242
+ constructor({
243
+ authProvider,
244
+ dpopKeys,
245
+ defaultCreateOptions,
246
+ defaultReadOptions,
247
+ disableDPoP,
248
+ policyEndpoint,
249
+ rewrapCacheOptions,
250
+ }: OpenTDFOptions) {
251
+ this.authProvider = authProvider;
252
+ this.defaultCreateOptions = defaultCreateOptions || {};
253
+ this.defaultReadOptions = defaultReadOptions || {};
254
+ this.dpopEnabled = !!disableDPoP;
255
+ this.policyEndpoint = policyEndpoint || '';
256
+ this.rewrapCache = new RewrapCache(rewrapCacheOptions);
257
+ this.tdf3Client = new TDF3Client({
258
+ authProvider,
259
+ dpopKeys,
260
+ kasEndpoint: 'https://disallow.all.invalid',
261
+ policyEndpoint,
262
+ });
263
+ this.dpopKeys =
264
+ dpopKeys ??
265
+ crypto.subtle.generateKey(
266
+ {
267
+ name: 'RSASSA-PKCS1-v1_5',
268
+ hash: 'SHA-256',
269
+ modulusLength: 2048,
270
+ publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
271
+ },
272
+ true,
273
+ ['sign', 'verify']
274
+ );
275
+ }
276
+
277
+ async createNanoTDF(opts: CreateNanoTDFOptions): Promise<DecoratedStream> {
278
+ opts = { ...this.defaultCreateOptions, ...opts };
279
+ const collection = await this.createNanoTDFCollection(opts);
280
+ try {
281
+ return await collection.encrypt(opts.source);
282
+ } finally {
283
+ await collection.close();
284
+ }
285
+ }
286
+
287
+ /**
288
+ * Creates a new collection object, which can be used to encrypt a series of data with the same policy.
289
+ * @returns
290
+ */
291
+ async createNanoTDFCollection(opts: CreateNanoTDFCollectionOptions): Promise<NanoTDFCollection> {
292
+ opts = { ...this.defaultCreateOptions, ...opts };
293
+ return new Collection(this.authProvider, opts);
294
+ }
295
+
296
+ async createZTDF(opts: CreateZTDFOptions): Promise<DecoratedStream> {
297
+ opts = { ...this.defaultCreateOptions, ...opts };
298
+ const oldStream = await this.tdf3Client.encrypt({
299
+ source: await sourceToStream(opts.source),
300
+
301
+ assertionConfigs: opts.assertionConfigs,
302
+ autoconfigure: !!opts.autoconfigure,
303
+ defaultKASEndpoint: opts.defaultKASEndpoint,
304
+ byteLimit: opts.byteLimit,
305
+ mimeType: opts.mimeType,
306
+ scope: {
307
+ attributes: opts.attributes,
308
+ },
309
+ splitPlan: opts.splitPlan,
310
+ windowSize: opts.windowSize,
311
+ wrappingKeyAlgorithm: opts.wrappingKeyAlgorithm,
312
+ });
313
+ const stream: DecoratedStream = oldStream.stream;
314
+ stream.manifest = Promise.resolve(oldStream.manifest);
315
+ stream.metadata = Promise.resolve(oldStream.metadata);
316
+ return stream;
317
+ }
318
+
319
+ /**
320
+ * Decrypts a nanotdf object. Optionally, stores the collection header and its DEK.
321
+ * @param ciphertext
322
+ */
323
+ async read(opts: ReadOptions): Promise<DecoratedStream> {
324
+ opts = { ...this.defaultReadOptions, ...opts };
325
+ const chunker = await fromSource(opts.source);
326
+ const prefix = await chunker(0, 3);
327
+ // switch for prefix, if starts with `PK` in ascii, or `L1L` in ascii:
328
+ if (prefix[0] === 0x50 && prefix[1] === 0x4b) {
329
+ const allowList = new OriginAllowList(opts.allowedKASEndpoints ?? [], opts.ignoreAllowlist);
330
+ const oldStream = await this.tdf3Client.decrypt({
331
+ source: opts.source,
332
+ allowList,
333
+ assertionVerificationKeys: opts.assertionVerificationKeys,
334
+ noVerifyAssertions: opts.noVerify,
335
+ wrappingKeyAlgorithm: opts.wrappingKeyAlgorithm,
336
+ });
337
+ const stream: DecoratedStream = oldStream.stream;
338
+ stream.metadata = Promise.resolve(oldStream.metadata);
339
+ return stream;
340
+ } else if (prefix[0] === 0x4c && prefix[1] === 0x31 && prefix[2] === 0x4c) {
341
+ const ciphertext = await chunker();
342
+ const nanotdf = NanoTDF.from(ciphertext);
343
+ const cachedDEK = this.rewrapCache.get(nanotdf.header.ephemeralPublicKey);
344
+ if (cachedDEK) {
345
+ const r: DecoratedStream = await streamify(decryptNanoTDF(cachedDEK, nanotdf));
346
+ r.header = nanotdf.header;
347
+ return r;
348
+ }
349
+ const nc = new Client({
350
+ allowedKases: opts.allowedKASEndpoints,
351
+ authProvider: this.authProvider,
352
+ ignoreAllowList: opts.ignoreAllowlist,
353
+ dpopEnabled: this.dpopEnabled,
354
+ dpopKeys: this.dpopKeys,
355
+ kasEndpoint: opts.allowedKASEndpoints?.[0] || 'https://disallow.all.invalid',
356
+ });
357
+ // TODO: The version number should be fetched from the API
358
+ const version = '0.0.1';
359
+ // Rewrap key on every request
360
+ const dek = await nc.rewrapKey(
361
+ nanotdf.header.toBuffer(),
362
+ nanotdf.header.getKasRewrapUrl(),
363
+ nanotdf.header.magicNumberVersion,
364
+ version
365
+ );
366
+ if (!dek) {
367
+ // These should have thrown already.
368
+ throw new Error('internal: key rewrap failure');
369
+ }
370
+ this.rewrapCache.set(nanotdf.header.ephemeralPublicKey, dek);
371
+ const r: DecoratedStream = await streamify(decryptNanoTDF(dek, nanotdf));
372
+ r.header = nanotdf.header;
373
+ return r;
374
+ }
375
+ throw new InvalidFileError(`unsupported format; prefix not recognized ${prefix}`);
376
+ }
377
+
378
+ close() {
379
+ this.rewrapCache.close();
380
+ }
381
+ }
382
+
383
+ async function streamify(ab: Promise<ArrayBuffer>): Promise<ReadableStream<Uint8Array>> {
384
+ const stream = new ReadableStream<Uint8Array>({
385
+ start(controller) {
386
+ ab.then((arrayBuffer) => {
387
+ controller.enqueue(new Uint8Array(arrayBuffer));
388
+ controller.close();
389
+ });
390
+ },
391
+ });
392
+ return stream;
393
+ }
394
+
395
+ export type NanoTDFCollection = {
396
+ encrypt: (source: Source) => Promise<ReadableStream<Uint8Array>>;
397
+ close: () => Promise<void>;
398
+ };
399
+
400
+ class Collection {
401
+ client?: NanoTDFDatasetClient;
402
+
403
+ constructor(authProvider: AuthProvider, opts: CreateNanoTDFCollectionOptions) {
404
+ if (opts.signers || opts.signingKeyID) {
405
+ throw new ConfigurationError('ntdf signing not implemented');
406
+ }
407
+ if (opts.autoconfigure) {
408
+ throw new ConfigurationError('autoconfigure not implemented');
409
+ }
410
+ if (opts.ecdsaBindingKeyID) {
411
+ throw new ConfigurationError('custom binding key not implemented');
412
+ }
413
+
414
+ this.client = new NanoTDFDatasetClient({
415
+ authProvider,
416
+ kasEndpoint: opts.defaultKASEndpoint ?? 'https://disallow.all.invalid',
417
+ maxKeyIterations: opts.maxKeyIterations,
418
+ });
419
+ }
420
+
421
+ async encrypt(source: Source): Promise<DecoratedStream> {
422
+ if (!this.client) {
423
+ throw new ConfigurationError('Collection is closed');
424
+ }
425
+ const chunker = await fromSource(source);
426
+ const cipherChunk = await this.client.encrypt(await chunker());
427
+ const stream: DecoratedStream = new ReadableStream<Uint8Array>({
428
+ start(controller) {
429
+ controller.enqueue(new Uint8Array(cipherChunk));
430
+ controller.close();
431
+ },
432
+ });
433
+ // TODO: client's header object is private
434
+ // stream.header = this.client.header;
435
+ return stream;
436
+ }
437
+
438
+ async close() {
439
+ delete this.client;
440
+ }
441
+ }
@@ -1,30 +1,52 @@
1
- import {
2
- type DecoratedReadableStream,
3
- isDecoratedReadableStream,
4
- } from '../client/DecoratedReadableStream.js';
5
- import { ConfigurationError, InvalidFileError, NetworkError } from '../../../src/errors.js';
1
+ import { ConfigurationError, InvalidFileError, NetworkError } from './errors.js';
6
2
 
7
3
  /**
8
4
  * Read data from a seekable stream.
5
+ * This is an abstraction for URLs with range queries and local file objects.
9
6
  * @param byteStart First byte to read. If negative, reads from the end. If absent, reads everything
10
7
  * @param byteEnd Index after last byte to read (exclusive)
11
8
  */
12
9
  export type Chunker = (byteStart?: number, byteEnd?: number) => Promise<Uint8Array>;
13
10
 
11
+ /**
12
+ * Type union for a variety of inputs.
13
+ */
14
+ export type Source =
15
+ | { type: 'buffer'; location: Uint8Array }
16
+ | { type: 'chunker'; location: Chunker }
17
+ | { type: 'file-browser'; location: Blob }
18
+ | { type: 'remote'; location: string }
19
+ | { type: 'stream'; location: ReadableStream<Uint8Array> };
20
+
21
+ /**
22
+ * Creates a seekable object from a browser file object.
23
+ * @param fileRef the browser file data
24
+ */
14
25
  export const fromBrowserFile = (fileRef: Blob): Chunker => {
15
26
  return async (byteStart?: number, byteEnd?: number): Promise<Uint8Array> => {
27
+ if (byteStart === undefined) {
28
+ return new Uint8Array(await fileRef.arrayBuffer());
29
+ }
16
30
  const chunkBlob = fileRef.slice(byteStart, byteEnd);
17
31
  const arrayBuffer = await new Response(chunkBlob).arrayBuffer();
18
32
  return new Uint8Array(arrayBuffer);
19
33
  };
20
34
  };
21
35
 
22
- export const fromBuffer = (source: Uint8Array | Buffer): Chunker => {
36
+ export const fromBuffer = (source: Uint8Array): Chunker => {
23
37
  return (byteStart?: number, byteEnd?: number) => {
24
38
  return Promise.resolve(source.slice(byteStart, byteEnd));
25
39
  };
26
40
  };
27
41
 
42
+ export const fromString = (source: string): Chunker => {
43
+ return fromBuffer(new TextEncoder().encode(source));
44
+ };
45
+
46
+ async function sleep(ms: number) {
47
+ return new Promise((resolve) => setTimeout(resolve, ms));
48
+ }
49
+
28
50
  async function getRemoteChunk(url: string, range?: string): Promise<Uint8Array> {
29
51
  // loop with fetch for three times, with an exponential backoff
30
52
  // if the fetch fails with a network error
@@ -88,14 +110,7 @@ export const fromUrl = async (location: string): Promise<Chunker> => {
88
110
  };
89
111
  };
90
112
 
91
- export type DataSource =
92
- | { type: 'buffer'; location: Uint8Array }
93
- | { type: 'chunker'; location: Chunker }
94
- | { type: 'file-browser'; location: Blob }
95
- | { type: 'remote'; location: string }
96
- | { type: 'stream'; location: DecoratedReadableStream };
97
-
98
- export const fromDataSource = async ({ type, location }: DataSource) => {
113
+ export const fromSource = async ({ type, location }: Source): Promise<Chunker> => {
99
114
  switch (type) {
100
115
  case 'buffer':
101
116
  if (!(location instanceof Uint8Array)) {
@@ -118,14 +133,48 @@ export const fromDataSource = async ({ type, location }: DataSource) => {
118
133
  }
119
134
  return fromUrl(location);
120
135
  case 'stream':
121
- if (!isDecoratedReadableStream(location)) {
122
- throw new ConfigurationError('Invalid data source; must be DecoratedTdfStream');
123
- }
124
- return fromBuffer(await location.toBuffer());
136
+ return fromBuffer(new Uint8Array(await new Response(location).arrayBuffer()));
125
137
  default:
126
138
  throw new ConfigurationError(`Data source type not defined, or not supported: ${type}}`);
127
139
  }
128
140
  };
129
- async function sleep(ms: number) {
130
- return new Promise((resolve) => setTimeout(resolve, ms));
141
+
142
+ export async function sourceToStream(source: Source): Promise<ReadableStream<Uint8Array>> {
143
+ switch (source.type) {
144
+ case 'stream':
145
+ return source.location;
146
+ case 'file-browser':
147
+ return source.location.stream();
148
+ case 'chunker': {
149
+ const chunkSize = 8 * 1024 * 1024; // 8 megabytes
150
+ let offset = 0;
151
+ return new ReadableStream({
152
+ async pull(controller) {
153
+ const chunk = await source.location(offset, offset + chunkSize);
154
+ if (chunk.length === 0) {
155
+ controller.close();
156
+ return;
157
+ }
158
+ controller.enqueue(chunk);
159
+ offset += chunk.length;
160
+ },
161
+ });
162
+ }
163
+ default: {
164
+ const chunker = await fromSource(source);
165
+ return new ReadableStream({
166
+ async start(controller) {
167
+ const chunk = await chunker();
168
+ controller.enqueue(chunk);
169
+ controller.close();
170
+ },
171
+ });
172
+ }
173
+ }
131
174
  }
175
+
176
+ // Deprected name, prefer `fromSource`
177
+ export const fromDataSource = fromSource;
178
+
179
+ // Deprecated Name; prefer just `Source`
180
+ export type DataSource = Source;
@@ -7,8 +7,6 @@ export interface AttributeObject {
7
7
  /** PEM encoded public key */
8
8
  readonly pubKey: string;
9
9
  readonly kasUrl: string;
10
- /** The most recent version 1.1.0. */
11
- readonly schemaVersion?: string;
12
10
  }
13
11
 
14
12
  export async function createAttribute(
@@ -22,6 +20,5 @@ export async function createAttribute(
22
20
  displayName: '',
23
21
  pubKey: pubKey.publicKey,
24
22
  kasUrl,
25
- schemaVersion: '1.1.0',
26
23
  };
27
24
  }
package/src/tdf/Policy.ts CHANGED
@@ -7,7 +7,6 @@ export class Policy {
7
7
  private uuidStr = uuid();
8
8
  private dataAttributesList: AttributeObject[] = [];
9
9
  private dissemList: string[] = [];
10
- // private schemaVersionStr = Policy.CURRENT_VERSION;
11
10
 
12
11
  /**
13
12
  * Adds a group of entities, to the Policy's dissem list
@@ -8,5 +8,4 @@ export interface PolicyObjectBody {
8
8
  export interface PolicyObject {
9
9
  readonly uuid: string;
10
10
  readonly body: PolicyObjectBody;
11
- readonly schemaVersion?: string;
12
11
  }
package/src/utils.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { exportSPKI, importX509 } from 'jose';
2
2
 
3
3
  import { base64 } from './encodings/index.js';
4
- import { pemCertToCrypto, pemPublicToCrypto } from './nanotdf-crypto/index.js';
4
+ import { pemCertToCrypto, pemPublicToCrypto } from './nanotdf-crypto/pemPublicToCrypto.js';
5
5
  import { ConfigurationError } from './errors.js';
6
6
 
7
7
  /**
@@ -45,8 +45,6 @@ export function isBrowser() {
45
45
  return typeof window !== 'undefined'; // eslint-disable-line
46
46
  }
47
47
 
48
- export const isFirefox = (): boolean => isBrowser() && 'InstallTrigger' in window;
49
-
50
48
  export const rstrip = (str: string, suffix = ' '): string => {
51
49
  while (str && suffix && str.endsWith(suffix)) {
52
50
  str = str.slice(0, -suffix.length);
package/src/version.ts CHANGED
@@ -7,3 +7,8 @@ export const version = '0.2.0';
7
7
  * A string name used to label requests as coming from this library client.
8
8
  */
9
9
  export const clientType = 'web-sdk';
10
+
11
+ /**
12
+ * Version of the opentdf/spec this library is targeting
13
+ */
14
+ export const tdfSpecVersion = '4.3.0';
package/tdf3/index.ts CHANGED
@@ -33,9 +33,9 @@ import {
33
33
  AuthProviders,
34
34
  version,
35
35
  clientType,
36
- } from '../src/index.js';
36
+ } from '../src/nanoindex.js';
37
37
  import { Algorithms, type AlgorithmName, type AlgorithmUrn } from './src/ciphers/algorithms.js';
38
- import { type Chunker } from './src/utils/chunkers.js';
38
+ import { type Chunker } from '../src/seekable.js';
39
39
 
40
40
  export type {
41
41
  AlgorithmName,
@@ -82,3 +82,15 @@ export {
82
82
  };
83
83
 
84
84
  export * as WebCryptoService from './src/crypto/index.js';
85
+ export {
86
+ type CreateNanoTDFCollectionOptions,
87
+ type CreateNanoTDFOptions,
88
+ type CreateOptions,
89
+ type CreateZTDFOptions,
90
+ type DecoratedStream,
91
+ type Keys,
92
+ type OpenTDFOptions,
93
+ type NanoTDFCollection,
94
+ type ReadOptions,
95
+ OpenTDF,
96
+ } from '../src/opentdf.js';