@opentdf/sdk 0.14.0 → 0.16.0-beta.149
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/src/index.js +23 -6
- package/dist/cjs/src/opentdf.js +4 -2
- package/dist/cjs/src/platform/authorization/entity-identifiers.js +3 -2
- package/dist/cjs/src/platform/authorization/resources.js +57 -0
- package/dist/cjs/src/version.js +1 -1
- package/dist/cjs/tdf3/src/ciphers/aes-gcm-cipher.js +11 -7
- package/dist/cjs/tdf3/src/ciphers/symmetric-cipher-base.js +1 -1
- package/dist/cjs/tdf3/src/client/builders.js +17 -1
- package/dist/cjs/tdf3/src/client/index.js +4 -2
- package/dist/cjs/tdf3/src/crypto/core/symmetric.js +29 -12
- package/dist/cjs/tdf3/src/models/encryption-information.js +1 -1
- package/dist/cjs/tdf3/src/tdf.js +199 -53
- package/dist/cjs/tdf3/src/utils/zip-reader.js +2 -2
- package/dist/types/src/index.d.ts +14 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/opentdf.d.ts +10 -0
- package/dist/types/src/opentdf.d.ts.map +1 -1
- package/dist/types/src/platform/authorization/entity-identifiers.d.ts +2 -1
- package/dist/types/src/platform/authorization/entity-identifiers.d.ts.map +1 -1
- package/dist/types/src/platform/authorization/resources.d.ts +37 -0
- package/dist/types/src/platform/authorization/resources.d.ts.map +1 -0
- package/dist/types/src/version.d.ts +1 -1
- package/dist/types/tdf3/src/ciphers/aes-gcm-cipher.d.ts +1 -1
- package/dist/types/tdf3/src/ciphers/aes-gcm-cipher.d.ts.map +1 -1
- package/dist/types/tdf3/src/ciphers/symmetric-cipher-base.d.ts +1 -1
- package/dist/types/tdf3/src/ciphers/symmetric-cipher-base.d.ts.map +1 -1
- package/dist/types/tdf3/src/client/builders.d.ts +14 -0
- package/dist/types/tdf3/src/client/builders.d.ts.map +1 -1
- package/dist/types/tdf3/src/client/index.d.ts +1 -1
- package/dist/types/tdf3/src/client/index.d.ts.map +1 -1
- package/dist/types/tdf3/src/crypto/core/symmetric.d.ts +1 -0
- package/dist/types/tdf3/src/crypto/core/symmetric.d.ts.map +1 -1
- package/dist/types/tdf3/src/models/encryption-information.d.ts +1 -1
- package/dist/types/tdf3/src/models/encryption-information.d.ts.map +1 -1
- package/dist/types/tdf3/src/tdf.d.ts +20 -0
- package/dist/types/tdf3/src/tdf.d.ts.map +1 -1
- package/dist/types/tdf3/src/utils/zip-reader.d.ts.map +1 -1
- package/dist/web/src/index.js +15 -2
- package/dist/web/src/opentdf.js +4 -2
- package/dist/web/src/platform/authorization/entity-identifiers.js +3 -2
- package/dist/web/src/platform/authorization/resources.js +53 -0
- package/dist/web/src/version.js +1 -1
- package/dist/web/tdf3/src/ciphers/aes-gcm-cipher.js +11 -7
- package/dist/web/tdf3/src/ciphers/symmetric-cipher-base.js +1 -1
- package/dist/web/tdf3/src/client/builders.js +17 -1
- package/dist/web/tdf3/src/client/index.js +4 -2
- package/dist/web/tdf3/src/crypto/core/symmetric.js +28 -12
- package/dist/web/tdf3/src/models/encryption-information.js +1 -1
- package/dist/web/tdf3/src/tdf.js +198 -53
- package/dist/web/tdf3/src/utils/zip-reader.js +2 -2
- package/package.json +2 -2
- package/src/index.ts +17 -6
- package/src/opentdf.ts +16 -0
- package/src/platform/authorization/entity-identifiers.ts +2 -1
- package/src/platform/authorization/resources.ts +59 -0
- package/src/version.ts +1 -1
- package/tdf3/src/ciphers/aes-gcm-cipher.ts +19 -14
- package/tdf3/src/ciphers/symmetric-cipher-base.ts +5 -1
- package/tdf3/src/client/builders.ts +20 -0
- package/tdf3/src/client/index.ts +4 -0
- package/tdf3/src/crypto/core/symmetric.ts +48 -14
- package/tdf3/src/models/encryption-information.ts +1 -1
- package/tdf3/src/tdf.ts +296 -79
- package/tdf3/src/utils/zip-reader.ts +1 -2
package/src/opentdf.ts
CHANGED
|
@@ -153,6 +153,18 @@ export type ReadOptions = {
|
|
|
153
153
|
/** If set, prevents more than this number of concurrent requests to the KAS. */
|
|
154
154
|
concurrencyLimit?: number;
|
|
155
155
|
|
|
156
|
+
/**
|
|
157
|
+
* Maximum number of payload segments to fetch and decrypt per batch.
|
|
158
|
+
* Adjust together with `maxConcurrentSegmentBatches` for expected throughput.
|
|
159
|
+
*/
|
|
160
|
+
segmentBatchSize?: number;
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Maximum number of segment batches that may be fetched concurrently.
|
|
164
|
+
* Adjust together with `segmentBatchSize` for expected throughput.
|
|
165
|
+
*/
|
|
166
|
+
maxConcurrentSegmentBatches?: number;
|
|
167
|
+
|
|
156
168
|
/** Type of key to use for wrapping responses. */
|
|
157
169
|
wrappingKeyAlgorithm?: KasPublicKeyAlgorithm;
|
|
158
170
|
};
|
|
@@ -495,7 +507,9 @@ class ZTDFReader {
|
|
|
495
507
|
async decrypt(): Promise<DecoratedStream> {
|
|
496
508
|
const {
|
|
497
509
|
assertionVerificationKeys,
|
|
510
|
+
maxConcurrentSegmentBatches,
|
|
498
511
|
noVerify: noVerifyAssertions,
|
|
512
|
+
segmentBatchSize,
|
|
499
513
|
wrappingKeyAlgorithm,
|
|
500
514
|
} = this.opts;
|
|
501
515
|
|
|
@@ -534,7 +548,9 @@ class ZTDFReader {
|
|
|
534
548
|
keyMiddleware: async (k) => k,
|
|
535
549
|
progressHandler: this.client.clientConfig.progressHandler,
|
|
536
550
|
assertionVerificationKeys,
|
|
551
|
+
maxConcurrentSegmentBatches,
|
|
537
552
|
noVerifyAssertions,
|
|
553
|
+
segmentBatchSize,
|
|
538
554
|
wrappingKeyAlgorithm,
|
|
539
555
|
fulfillableObligations: this.opts.fulfillableObligationFQNs || [],
|
|
540
556
|
},
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { create } from '@bufbuild/protobuf';
|
|
2
|
+
import {
|
|
3
|
+
type Resource,
|
|
4
|
+
ResourceSchema,
|
|
5
|
+
Resource_AttributeValuesSchema,
|
|
6
|
+
} from './v2/authorization_pb.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Convenience constructors for {@link Resource}, analogous to the
|
|
10
|
+
* {@link EntityIdentifiers} helpers for {@link EntityIdentifier}.
|
|
11
|
+
*
|
|
12
|
+
* Each function builds a complete `Resource` so callers avoid deeply nested
|
|
13
|
+
* object literals.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* // Before
|
|
18
|
+
* const resource = create(ResourceSchema, {
|
|
19
|
+
* resource: {
|
|
20
|
+
* case: 'attributeValues',
|
|
21
|
+
* value: create(Resource_AttributeValuesSchema, {
|
|
22
|
+
* fqns: ['https://example.com/attr/department/value/finance'],
|
|
23
|
+
* }),
|
|
24
|
+
* },
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* // After
|
|
28
|
+
* import { Resources } from '@opentdf/sdk';
|
|
29
|
+
* const resource = Resources.forAttributeValues('https://example.com/attr/department/value/finance');
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Returns a Resource containing the given attribute value FQNs.
|
|
35
|
+
* This is the most common Resource variant, used when authorizing against
|
|
36
|
+
* attribute values attached to data (e.g. those on a TDF).
|
|
37
|
+
*/
|
|
38
|
+
export function forAttributeValues(fqn: string, ...moreFqns: string[]): Resource {
|
|
39
|
+
const fqns = [fqn, ...moreFqns];
|
|
40
|
+
return create(ResourceSchema, {
|
|
41
|
+
resource: {
|
|
42
|
+
case: 'attributeValues',
|
|
43
|
+
value: create(Resource_AttributeValuesSchema, { fqns }),
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Returns a Resource that references a single registered resource value
|
|
50
|
+
* by its fully qualified name, as stored in platform policy.
|
|
51
|
+
*/
|
|
52
|
+
export function forRegisteredResourceValueFqn(fqn: string): Resource {
|
|
53
|
+
return create(ResourceSchema, {
|
|
54
|
+
resource: {
|
|
55
|
+
case: 'registeredResourceValueFqn',
|
|
56
|
+
value: fqn,
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
}
|
package/src/version.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Exposes the released version number of the `@opentdf/sdk` package
|
|
3
3
|
*/
|
|
4
|
-
export const version = '0.
|
|
4
|
+
export const version = '0.16.0'; // x-release-please-version
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* A string name used to label requests as coming from this library client.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Binary } from '../binary.js';
|
|
2
2
|
import { Algorithms } from './algorithms.js';
|
|
3
3
|
import { SymmetricCipher } from './symmetric-cipher-base.js';
|
|
4
|
+
import { decryptBufferSource } from '../crypto/core/symmetric.js';
|
|
4
5
|
import { concatUint8 } from '../utils/index.js';
|
|
5
6
|
|
|
6
7
|
import {
|
|
@@ -16,20 +17,17 @@ const IV_LENGTH = 12;
|
|
|
16
17
|
type ProcessGcmPayload = {
|
|
17
18
|
payload: Binary;
|
|
18
19
|
payloadIv: Binary;
|
|
19
|
-
payloadAuthTag: Binary;
|
|
20
20
|
};
|
|
21
21
|
// Should this be a Binary, Buffer, or... both?
|
|
22
22
|
function processGcmPayload(source: ArrayBuffer): ProcessGcmPayload {
|
|
23
23
|
// Read the 12 byte IV from the beginning of the stream
|
|
24
24
|
const payloadIv = Binary.fromArrayBuffer(source.slice(0, 12));
|
|
25
25
|
|
|
26
|
-
// Slice the final 16 bytes of the buffer for the authentication tag
|
|
27
|
-
const payloadAuthTag = Binary.fromArrayBuffer(source.slice(-16));
|
|
28
|
-
|
|
29
26
|
return {
|
|
30
|
-
|
|
27
|
+
// WebCrypto AES-GCM expects ciphertext with the auth tag appended, so keep
|
|
28
|
+
// the tag attached instead of splitting and re-concatenating it later.
|
|
29
|
+
payload: Binary.fromArrayBuffer(source.slice(12)),
|
|
31
30
|
payloadIv,
|
|
32
|
-
payloadAuthTag,
|
|
33
31
|
};
|
|
34
32
|
}
|
|
35
33
|
|
|
@@ -64,18 +62,25 @@ export class AesGcmCipher extends SymmetricCipher {
|
|
|
64
62
|
*/
|
|
65
63
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
66
64
|
override async decrypt(
|
|
67
|
-
buffer: ArrayBuffer,
|
|
65
|
+
buffer: ArrayBuffer | Uint8Array,
|
|
68
66
|
key: SymmetricKey,
|
|
69
67
|
iv?: Binary
|
|
70
68
|
): Promise<DecryptResult> {
|
|
71
|
-
const
|
|
69
|
+
const input = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
|
|
70
|
+
|
|
71
|
+
if (this.cryptoService.name === 'BrowserNativeCryptoService') {
|
|
72
|
+
return decryptBufferSource(
|
|
73
|
+
input.subarray(12),
|
|
74
|
+
key,
|
|
75
|
+
input.subarray(0, 12),
|
|
76
|
+
Algorithms.AES_256_GCM
|
|
77
|
+
);
|
|
78
|
+
}
|
|
72
79
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
key,
|
|
76
|
-
payloadIv,
|
|
77
|
-
Algorithms.AES_256_GCM,
|
|
78
|
-
payloadAuthTag
|
|
80
|
+
const { payload, payloadIv } = processGcmPayload(
|
|
81
|
+
input.buffer.slice(input.byteOffset, input.byteOffset + input.byteLength)
|
|
79
82
|
);
|
|
83
|
+
|
|
84
|
+
return this.cryptoService.decrypt(payload, key, payloadIv, Algorithms.AES_256_GCM);
|
|
80
85
|
}
|
|
81
86
|
}
|
|
@@ -37,5 +37,9 @@ export abstract class SymmetricCipher {
|
|
|
37
37
|
|
|
38
38
|
abstract encrypt(payload: Binary, key: SymmetricKey, iv: Binary): Promise<EncryptResult>;
|
|
39
39
|
|
|
40
|
-
abstract decrypt(
|
|
40
|
+
abstract decrypt(
|
|
41
|
+
payload: ArrayBuffer | Uint8Array,
|
|
42
|
+
key: SymmetricKey,
|
|
43
|
+
iv?: Binary
|
|
44
|
+
): Promise<DecryptResult>;
|
|
41
45
|
}
|
|
@@ -536,6 +536,8 @@ export type DecryptParams = {
|
|
|
536
536
|
streamMiddleware?: DecryptStreamMiddleware;
|
|
537
537
|
assertionVerificationKeys?: AssertionVerificationKeys;
|
|
538
538
|
concurrencyLimit?: number;
|
|
539
|
+
segmentBatchSize?: number;
|
|
540
|
+
maxConcurrentSegmentBatches?: number;
|
|
539
541
|
noVerifyAssertions?: boolean;
|
|
540
542
|
wrappingKeyAlgorithm?: KasPublicKeyAlgorithm;
|
|
541
543
|
fulfillableObligationFQNs?: string[];
|
|
@@ -725,6 +727,24 @@ class DecryptParamsBuilder {
|
|
|
725
727
|
return this;
|
|
726
728
|
}
|
|
727
729
|
|
|
730
|
+
/**
|
|
731
|
+
* Set the number of payload segments to fetch and decrypt per batch.
|
|
732
|
+
* Adjust together with `withMaxConcurrentSegmentBatches()` for expected throughput.
|
|
733
|
+
*/
|
|
734
|
+
withSegmentBatchSize(size: number): DecryptParamsBuilder {
|
|
735
|
+
this._params.segmentBatchSize = size;
|
|
736
|
+
return this;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* Set the maximum number of payload segment batches to fetch concurrently.
|
|
741
|
+
* Adjust together with `withSegmentBatchSize()` for expected throughput.
|
|
742
|
+
*/
|
|
743
|
+
withMaxConcurrentSegmentBatches(limit: number): DecryptParamsBuilder {
|
|
744
|
+
this._params.maxConcurrentSegmentBatches = limit;
|
|
745
|
+
return this;
|
|
746
|
+
}
|
|
747
|
+
|
|
728
748
|
/**
|
|
729
749
|
* Generate a parameters object in the form expected by <code>{@link Client#decrypt|decrypt}</code>.
|
|
730
750
|
* <br/><br/>
|
package/tdf3/src/client/index.ts
CHANGED
|
@@ -800,6 +800,8 @@ export class Client {
|
|
|
800
800
|
assertionVerificationKeys,
|
|
801
801
|
noVerifyAssertions,
|
|
802
802
|
concurrencyLimit = 1,
|
|
803
|
+
maxConcurrentSegmentBatches,
|
|
804
|
+
segmentBatchSize,
|
|
803
805
|
wrappingKeyAlgorithm,
|
|
804
806
|
fulfillableObligationFQNs = [],
|
|
805
807
|
}: DecryptParams): Promise<DecoratedReadableStream> {
|
|
@@ -836,7 +838,9 @@ export class Client {
|
|
|
836
838
|
keyMiddleware,
|
|
837
839
|
progressHandler: this.clientConfig.progressHandler,
|
|
838
840
|
assertionVerificationKeys,
|
|
841
|
+
maxConcurrentSegmentBatches,
|
|
839
842
|
noVerifyAssertions,
|
|
843
|
+
segmentBatchSize,
|
|
840
844
|
wrappingKeyAlgorithm,
|
|
841
845
|
fulfillableObligations: fulfillableObligationFQNs,
|
|
842
846
|
})
|
|
@@ -13,6 +13,16 @@ import { unwrapSymmetricKey, wrapSymmetricKey } from './keys.js';
|
|
|
13
13
|
|
|
14
14
|
const ENC_DEC_METHODS: KeyUsage[] = ['encrypt', 'decrypt'];
|
|
15
15
|
|
|
16
|
+
function asUint8ArrayView(buffer: BufferSource): Uint8Array {
|
|
17
|
+
if (buffer instanceof Uint8Array) {
|
|
18
|
+
return buffer;
|
|
19
|
+
}
|
|
20
|
+
if (ArrayBuffer.isView(buffer)) {
|
|
21
|
+
return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
22
|
+
}
|
|
23
|
+
return new Uint8Array(buffer);
|
|
24
|
+
}
|
|
25
|
+
|
|
16
26
|
/**
|
|
17
27
|
* Generate a random symmetric key (opaque).
|
|
18
28
|
* @param length - Key length in bytes (default 32 for AES-256)
|
|
@@ -61,7 +71,23 @@ export function decrypt(
|
|
|
61
71
|
algorithm?: AlgorithmUrn,
|
|
62
72
|
authTag?: Binary
|
|
63
73
|
): Promise<DecryptResult> {
|
|
64
|
-
return
|
|
74
|
+
return _doDecryptBufferSource(
|
|
75
|
+
payload.asArrayBuffer(),
|
|
76
|
+
key,
|
|
77
|
+
iv.asArrayBuffer(),
|
|
78
|
+
algorithm,
|
|
79
|
+
authTag?.asArrayBuffer()
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function decryptBufferSource(
|
|
84
|
+
payload: BufferSource,
|
|
85
|
+
key: SymmetricKey,
|
|
86
|
+
iv: BufferSource,
|
|
87
|
+
algorithm?: AlgorithmUrn,
|
|
88
|
+
authTag?: BufferSource
|
|
89
|
+
): Promise<DecryptResult> {
|
|
90
|
+
return _doDecryptBufferSource(payload, key, iv, algorithm, authTag);
|
|
65
91
|
}
|
|
66
92
|
|
|
67
93
|
/**
|
|
@@ -115,32 +141,33 @@ async function _doEncrypt(
|
|
|
115
141
|
};
|
|
116
142
|
}
|
|
117
143
|
|
|
118
|
-
async function
|
|
119
|
-
payload:
|
|
144
|
+
async function _doDecryptBufferSource(
|
|
145
|
+
payload: BufferSource,
|
|
120
146
|
key: SymmetricKey,
|
|
121
|
-
iv:
|
|
147
|
+
iv: BufferSource,
|
|
122
148
|
algorithm?: AlgorithmUrn,
|
|
123
|
-
authTag?:
|
|
149
|
+
authTag?: BufferSource
|
|
124
150
|
): Promise<DecryptResult> {
|
|
125
151
|
console.assert(payload != null);
|
|
126
152
|
console.assert(key != null);
|
|
127
153
|
console.assert(iv != null);
|
|
128
154
|
|
|
129
|
-
let payloadBuffer = payload
|
|
155
|
+
let payloadBuffer: BufferSource = payload;
|
|
130
156
|
|
|
131
157
|
// Concat the the auth tag to the payload for decryption
|
|
132
158
|
if (authTag) {
|
|
133
|
-
const
|
|
134
|
-
const
|
|
135
|
-
gcmPayload
|
|
136
|
-
gcmPayload.set(
|
|
137
|
-
|
|
159
|
+
const payloadBytes = asUint8ArrayView(payloadBuffer);
|
|
160
|
+
const authTagBytes = asUint8ArrayView(authTag);
|
|
161
|
+
const gcmPayload = new Uint8Array(payloadBytes.byteLength + authTagBytes.byteLength);
|
|
162
|
+
gcmPayload.set(payloadBytes, 0);
|
|
163
|
+
gcmPayload.set(authTagBytes, payloadBytes.byteLength);
|
|
164
|
+
payloadBuffer = gcmPayload;
|
|
138
165
|
}
|
|
139
166
|
|
|
140
|
-
const
|
|
167
|
+
const ivBytes = asUint8ArrayView(iv);
|
|
168
|
+
const algoDomString = getSymmetricAlgoDomStringFromIv(ivBytes, algorithm);
|
|
141
169
|
const keyBytes = unwrapSymmetricKey(key);
|
|
142
170
|
const importedKey = await _importKey(keyBytes, algoDomString);
|
|
143
|
-
algoDomString.iv = iv.asArrayBuffer();
|
|
144
171
|
|
|
145
172
|
const decrypted = await crypto.subtle
|
|
146
173
|
.decrypt(algoDomString, importedKey, payloadBuffer)
|
|
@@ -168,6 +195,13 @@ function _importKey(keyBytes: Uint8Array, algorithm: AesCbcParams | AesGcmParams
|
|
|
168
195
|
function getSymmetricAlgoDomString(
|
|
169
196
|
iv: Binary,
|
|
170
197
|
algorithm?: AlgorithmUrn
|
|
198
|
+
): AesCbcParams | AesGcmParams {
|
|
199
|
+
return getSymmetricAlgoDomStringFromIv(asUint8ArrayView(iv.asArrayBuffer()), algorithm);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function getSymmetricAlgoDomStringFromIv(
|
|
203
|
+
iv: Uint8Array,
|
|
204
|
+
algorithm?: AlgorithmUrn
|
|
171
205
|
): AesCbcParams | AesGcmParams {
|
|
172
206
|
let nativeAlgorithm = 'AES-CBC';
|
|
173
207
|
if (algorithm === Algorithms.AES_256_GCM) {
|
|
@@ -176,7 +210,7 @@ function getSymmetricAlgoDomString(
|
|
|
176
210
|
|
|
177
211
|
return {
|
|
178
212
|
name: nativeAlgorithm,
|
|
179
|
-
iv
|
|
213
|
+
iv,
|
|
180
214
|
};
|
|
181
215
|
}
|
|
182
216
|
|
|
@@ -72,7 +72,7 @@ export class SplitKey {
|
|
|
72
72
|
return this.cipher.encrypt(contentBinary, key, ivBinary);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
async decrypt(content: Uint8Array, key: SymmetricKey): Promise<DecryptResult> {
|
|
75
|
+
async decrypt(content: ArrayBuffer | Uint8Array, key: SymmetricKey): Promise<DecryptResult> {
|
|
76
76
|
return this.cipher.decrypt(content, key);
|
|
77
77
|
}
|
|
78
78
|
|