@opentdf/sdk 0.2.0-beta.1758 → 0.2.0
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/README.md +45 -38
- package/dist/cjs/src/access.js +47 -11
- package/dist/cjs/src/auth/auth.js +5 -5
- package/dist/cjs/src/auth/oidc-clientcredentials-provider.js +1 -1
- package/dist/cjs/src/auth/oidc-externaljwt-provider.js +1 -1
- package/dist/cjs/src/auth/oidc-refreshtoken-provider.js +1 -1
- package/dist/cjs/src/auth/oidc.js +1 -1
- package/dist/cjs/src/auth/providers.js +1 -1
- package/dist/cjs/src/concurrency.js +3 -4
- package/dist/cjs/src/encodings/base64.js +4 -4
- package/dist/cjs/src/encodings/hex.js +5 -6
- package/dist/cjs/src/encodings/index.js +18 -8
- package/dist/cjs/src/errors.js +1 -1
- package/dist/cjs/src/index.js +28 -318
- package/dist/cjs/src/nanoclients.js +285 -0
- package/dist/cjs/src/nanoindex.js +47 -0
- package/dist/cjs/src/nanotdf/Client.js +18 -8
- package/dist/cjs/src/nanotdf/NanoTDF.js +1 -1
- package/dist/cjs/src/nanotdf/decrypt.js +2 -2
- package/dist/cjs/src/nanotdf/encrypt-dataset.js +2 -2
- package/dist/cjs/src/nanotdf/encrypt.js +2 -2
- package/dist/cjs/src/nanotdf/helpers/calculateByCurve.js +3 -4
- package/dist/cjs/src/nanotdf/helpers/getHkdfSalt.js +2 -2
- package/dist/cjs/src/nanotdf/models/Ciphers.js +3 -3
- package/dist/cjs/src/nanotdf/models/EcCurves.js +3 -3
- package/dist/cjs/src/nanotdf/models/Header.js +1 -1
- package/dist/cjs/src/nanotdf/models/Payload.js +1 -1
- package/dist/cjs/src/nanotdf/models/Policy/AbstractPolicy.js +1 -1
- package/dist/cjs/src/nanotdf/models/Policy/EmbeddedPolicy.js +1 -1
- package/dist/cjs/src/nanotdf/models/Policy/PolicyFactory.js +1 -1
- package/dist/cjs/src/nanotdf/models/ResourceLocator.js +1 -1
- package/dist/cjs/src/nanotdf/models/Signature.js +1 -1
- package/dist/cjs/src/nanotdf-crypto/ciphers.js +1 -1
- package/dist/cjs/src/nanotdf-crypto/decrypt.js +2 -2
- package/dist/cjs/src/nanotdf-crypto/digest.js +2 -2
- package/dist/cjs/src/nanotdf-crypto/ecdsaSignature.js +4 -5
- package/dist/cjs/src/nanotdf-crypto/encrypt.js +2 -2
- package/dist/cjs/src/nanotdf-crypto/exportCryptoKey.js +2 -2
- package/dist/cjs/src/nanotdf-crypto/generateKeyPair.js +2 -2
- package/dist/cjs/src/nanotdf-crypto/generateRandomNumber.js +2 -2
- package/dist/cjs/src/nanotdf-crypto/index.js +21 -13
- package/dist/cjs/src/nanotdf-crypto/keyAgreement.js +10 -8
- package/dist/cjs/src/nanotdf-crypto/pemPublicToCrypto.js +20 -11
- package/dist/cjs/src/opentdf.js +251 -0
- package/dist/cjs/src/policy/api.js +2 -3
- package/dist/cjs/src/policy/granter.js +3 -4
- package/dist/cjs/src/seekable.js +157 -0
- package/dist/cjs/src/tdf/AttributeObject.js +2 -4
- package/dist/cjs/src/tdf/Policy.js +1 -2
- package/dist/cjs/src/utils.js +12 -14
- package/dist/cjs/src/version.js +6 -2
- package/dist/cjs/tdf3/index.js +27 -15
- package/dist/cjs/tdf3/src/assertions.js +25 -11
- package/dist/cjs/tdf3/src/binary.js +1 -1
- package/dist/cjs/tdf3/src/ciphers/aes-gcm-cipher.js +1 -1
- package/dist/cjs/tdf3/src/ciphers/symmetric-cipher-base.js +1 -1
- package/dist/cjs/tdf3/src/client/DecoratedReadableStream.js +7 -74
- package/dist/cjs/tdf3/src/client/builders.js +26 -22
- package/dist/cjs/tdf3/src/client/index.js +88 -61
- package/dist/cjs/tdf3/src/client/validation.js +3 -3
- package/dist/cjs/tdf3/src/crypto/crypto-utils.js +1 -1
- package/dist/cjs/tdf3/src/crypto/index.js +18 -18
- package/dist/cjs/tdf3/src/index.js +22 -11
- package/dist/cjs/tdf3/src/models/attribute-set.js +1 -1
- package/dist/cjs/tdf3/src/models/encryption-information.js +3 -3
- package/dist/cjs/tdf3/src/models/key-access.js +67 -35
- package/dist/cjs/tdf3/src/models/policy.js +3 -3
- package/dist/cjs/tdf3/src/tdf.js +177 -151
- package/dist/cjs/tdf3/src/utils/buffer-crc32.js +2 -3
- package/dist/cjs/tdf3/src/utils/index.js +30 -28
- package/dist/cjs/tdf3/src/utils/keysplit.js +4 -5
- package/dist/cjs/tdf3/src/utils/unwrap.js +21 -0
- package/dist/cjs/tdf3/src/utils/zip-reader.js +4 -4
- package/dist/cjs/tdf3/src/utils/zip-writer.js +4 -4
- package/dist/types/src/access.d.ts +3 -0
- package/dist/types/src/access.d.ts.map +1 -1
- package/dist/types/src/auth/providers.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +5 -136
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/nanoclients.d.ts +107 -0
- package/dist/types/src/nanoclients.d.ts.map +1 -0
- package/dist/types/src/nanoindex.d.ts +5 -0
- package/dist/types/src/nanoindex.d.ts.map +1 -0
- package/dist/types/src/nanotdf/enum/CipherEnum.d.ts +1 -1
- package/dist/types/src/nanotdf/enum/CipherEnum.d.ts.map +1 -1
- package/dist/types/src/nanotdf/enum/PolicyTypeEnum.d.ts +1 -1
- package/dist/types/src/nanotdf/enum/PolicyTypeEnum.d.ts.map +1 -1
- package/dist/types/src/nanotdf/models/DefaultParams.d.ts +1 -1
- package/dist/types/src/nanotdf/models/ResourceLocator.d.ts.map +1 -1
- package/dist/types/src/nanotdf-crypto/generateKeyPair.d.ts +1 -1
- package/dist/types/src/nanotdf-crypto/generateKeyPair.d.ts.map +1 -1
- package/dist/types/src/nanotdf-crypto/generateRandomNumber.d.ts +1 -1
- package/dist/types/src/nanotdf-crypto/generateRandomNumber.d.ts.map +1 -1
- package/dist/types/src/nanotdf-crypto/index.d.ts +2 -3
- package/dist/types/src/nanotdf-crypto/index.d.ts.map +1 -1
- package/dist/types/src/nanotdf-crypto/keyAgreement.d.ts.map +1 -1
- package/dist/types/src/opentdf.d.ts +110 -0
- package/dist/types/src/opentdf.d.ts.map +1 -0
- package/dist/types/src/seekable.d.ts +39 -0
- package/dist/types/src/seekable.d.ts.map +1 -0
- package/dist/types/src/tdf/AttributeObject.d.ts +0 -2
- package/dist/types/src/tdf/AttributeObject.d.ts.map +1 -1
- package/dist/types/src/tdf/NanoTDF/NanoTDF.d.ts +2 -2
- package/dist/types/src/tdf/NanoTDF/NanoTDF.d.ts.map +1 -1
- package/dist/types/src/tdf/Policy.d.ts.map +1 -1
- package/dist/types/src/tdf/PolicyObject.d.ts +0 -1
- package/dist/types/src/tdf/PolicyObject.d.ts.map +1 -1
- package/dist/types/src/utils.d.ts +0 -1
- package/dist/types/src/utils.d.ts.map +1 -1
- package/dist/types/src/version.d.ts +4 -0
- package/dist/types/src/version.d.ts.map +1 -1
- package/dist/types/tdf3/index.d.ts +3 -2
- package/dist/types/tdf3/index.d.ts.map +1 -1
- package/dist/types/tdf3/src/assertions.d.ts +3 -3
- package/dist/types/tdf3/src/assertions.d.ts.map +1 -1
- package/dist/types/tdf3/src/client/DecoratedReadableStream.d.ts +1 -13
- package/dist/types/tdf3/src/client/DecoratedReadableStream.d.ts.map +1 -1
- package/dist/types/tdf3/src/client/builders.d.ts +43 -37
- package/dist/types/tdf3/src/client/builders.d.ts.map +1 -1
- package/dist/types/tdf3/src/client/index.d.ts +8 -9
- package/dist/types/tdf3/src/client/index.d.ts.map +1 -1
- package/dist/types/tdf3/src/client/validation.d.ts +3 -3
- package/dist/types/tdf3/src/client/validation.d.ts.map +1 -1
- package/dist/types/tdf3/src/crypto/crypto-utils.d.ts.map +1 -1
- package/dist/types/tdf3/src/index.d.ts +1 -1
- package/dist/types/tdf3/src/index.d.ts.map +1 -1
- package/dist/types/tdf3/src/models/key-access.d.ts +63 -15
- package/dist/types/tdf3/src/models/key-access.d.ts.map +1 -1
- package/dist/types/tdf3/src/models/manifest.d.ts +2 -0
- package/dist/types/tdf3/src/models/manifest.d.ts.map +1 -1
- package/dist/types/tdf3/src/models/policy.d.ts +0 -1
- package/dist/types/tdf3/src/models/policy.d.ts.map +1 -1
- package/dist/types/tdf3/src/tdf.d.ts +20 -24
- package/dist/types/tdf3/src/tdf.d.ts.map +1 -1
- package/dist/types/tdf3/src/utils/index.d.ts +0 -2
- package/dist/types/tdf3/src/utils/index.d.ts.map +1 -1
- package/dist/types/tdf3/src/utils/unwrap.d.ts +2 -0
- package/dist/types/tdf3/src/utils/unwrap.d.ts.map +1 -0
- package/dist/types/tdf3/src/utils/zip-reader.d.ts +1 -1
- package/dist/types/tdf3/src/utils/zip-reader.d.ts.map +1 -1
- package/dist/types/tdf3/src/utils/zip-writer.d.ts +2 -2
- package/dist/web/src/access.js +40 -7
- package/dist/web/src/auth/auth.js +1 -1
- package/dist/web/src/auth/oidc-clientcredentials-provider.js +1 -1
- package/dist/web/src/auth/oidc-externaljwt-provider.js +1 -1
- package/dist/web/src/auth/oidc-refreshtoken-provider.js +1 -1
- package/dist/web/src/auth/oidc.js +1 -1
- package/dist/web/src/auth/providers.js +1 -1
- package/dist/web/src/concurrency.js +1 -1
- package/dist/web/src/encodings/base64.js +1 -1
- package/dist/web/src/encodings/hex.js +1 -1
- package/dist/web/src/errors.js +1 -1
- package/dist/web/src/index.js +6 -310
- package/dist/web/src/nanoclients.js +280 -0
- package/dist/web/src/nanoindex.js +5 -0
- package/dist/web/src/nanotdf/Client.js +1 -1
- package/dist/web/src/nanotdf/NanoTDF.js +1 -1
- package/dist/web/src/nanotdf/encrypt-dataset.js +1 -1
- package/dist/web/src/nanotdf/encrypt.js +1 -1
- package/dist/web/src/nanotdf/models/Ciphers.js +1 -1
- package/dist/web/src/nanotdf/models/EcCurves.js +1 -1
- package/dist/web/src/nanotdf/models/Header.js +1 -1
- package/dist/web/src/nanotdf/models/Payload.js +1 -1
- package/dist/web/src/nanotdf/models/Policy/AbstractPolicy.js +1 -1
- package/dist/web/src/nanotdf/models/Policy/EmbeddedPolicy.js +1 -1
- package/dist/web/src/nanotdf/models/Policy/PolicyFactory.js +1 -1
- package/dist/web/src/nanotdf/models/ResourceLocator.js +1 -1
- package/dist/web/src/nanotdf/models/Signature.js +1 -1
- package/dist/web/src/nanotdf-crypto/ciphers.js +1 -1
- package/dist/web/src/nanotdf-crypto/ecdsaSignature.js +1 -1
- package/dist/web/src/nanotdf-crypto/generateKeyPair.js +2 -2
- package/dist/web/src/nanotdf-crypto/generateRandomNumber.js +2 -2
- package/dist/web/src/nanotdf-crypto/index.js +3 -4
- package/dist/web/src/nanotdf-crypto/keyAgreement.js +9 -6
- package/dist/web/src/nanotdf-crypto/pemPublicToCrypto.js +1 -1
- package/dist/web/src/opentdf.js +242 -0
- package/dist/web/src/policy/api.js +1 -1
- package/dist/web/src/policy/granter.js +1 -1
- package/dist/web/src/seekable.js +148 -0
- package/dist/web/src/tdf/AttributeObject.js +1 -2
- package/dist/web/src/tdf/Policy.js +1 -2
- package/dist/web/src/utils.js +2 -3
- package/dist/web/src/version.js +5 -1
- package/dist/web/tdf3/index.js +3 -2
- package/dist/web/tdf3/src/assertions.js +21 -6
- package/dist/web/tdf3/src/binary.js +1 -1
- package/dist/web/tdf3/src/ciphers/aes-gcm-cipher.js +1 -1
- package/dist/web/tdf3/src/ciphers/symmetric-cipher-base.js +1 -1
- package/dist/web/tdf3/src/client/DecoratedReadableStream.js +4 -68
- package/dist/web/tdf3/src/client/builders.js +26 -22
- package/dist/web/tdf3/src/client/index.js +69 -52
- package/dist/web/tdf3/src/client/validation.js +1 -1
- package/dist/web/tdf3/src/crypto/crypto-utils.js +1 -1
- package/dist/web/tdf3/src/crypto/index.js +1 -1
- package/dist/web/tdf3/src/index.js +2 -2
- package/dist/web/tdf3/src/models/attribute-set.js +1 -1
- package/dist/web/tdf3/src/models/encryption-information.js +3 -3
- package/dist/web/tdf3/src/models/key-access.js +47 -24
- package/dist/web/tdf3/src/models/policy.js +1 -1
- package/dist/web/tdf3/src/tdf.js +149 -130
- package/dist/web/tdf3/src/utils/buffer-crc32.js +1 -1
- package/dist/web/tdf3/src/utils/index.js +1 -5
- package/dist/web/tdf3/src/utils/keysplit.js +1 -1
- package/dist/web/tdf3/src/utils/unwrap.js +18 -0
- package/dist/web/tdf3/src/utils/zip-reader.js +1 -1
- package/dist/web/tdf3/src/utils/zip-writer.js +1 -1
- package/package.json +45 -42
- package/src/access.ts +37 -1
- package/src/index.ts +5 -435
- package/src/nanoclients.ts +405 -0
- package/src/nanoindex.ts +4 -0
- package/src/nanotdf-crypto/generateKeyPair.ts +1 -1
- package/src/nanotdf-crypto/generateRandomNumber.ts +1 -1
- package/src/nanotdf-crypto/index.ts +2 -3
- package/src/nanotdf-crypto/keyAgreement.ts +14 -7
- package/src/opentdf.ts +473 -0
- package/{tdf3/src/utils/chunkers.ts → src/seekable.ts} +69 -20
- package/src/tdf/AttributeObject.ts +0 -3
- package/src/tdf/Policy.ts +0 -1
- package/src/tdf/PolicyObject.ts +0 -1
- package/src/utils.ts +1 -3
- package/src/version.ts +5 -0
- package/tdf3/index.ts +14 -2
- package/tdf3/src/assertions.ts +33 -8
- package/tdf3/src/client/DecoratedReadableStream.ts +2 -78
- package/tdf3/src/client/builders.ts +44 -26
- package/tdf3/src/client/index.ts +101 -86
- package/tdf3/src/index.ts +1 -1
- package/tdf3/src/models/encryption-information.ts +2 -2
- package/tdf3/src/models/key-access.ts +120 -38
- package/tdf3/src/models/manifest.ts +3 -0
- package/tdf3/src/models/policy.ts +0 -1
- package/tdf3/src/tdf.ts +251 -207
- package/tdf3/src/utils/index.ts +0 -5
- package/tdf3/src/utils/unwrap.ts +17 -0
- package/tdf3/src/utils/zip-reader.ts +1 -1
- package/dist/cjs/src/nanotdf-crypto/importRawKey.js +0 -18
- package/dist/cjs/tdf3/src/templates/default.html.js +0 -98
- package/dist/cjs/tdf3/src/templates/escaper.js +0 -15
- package/dist/cjs/tdf3/src/templates/index.js +0 -12
- package/dist/cjs/tdf3/src/utils/chunkers.js +0 -114
- package/dist/cjs/tdf3/src/version.js +0 -6
- package/dist/types/src/nanotdf-crypto/importRawKey.d.ts +0 -13
- package/dist/types/src/nanotdf-crypto/importRawKey.d.ts.map +0 -1
- package/dist/types/tdf3/src/templates/default.html.d.ts +0 -8
- package/dist/types/tdf3/src/templates/default.html.d.ts.map +0 -1
- package/dist/types/tdf3/src/templates/escaper.d.ts +0 -6
- package/dist/types/tdf3/src/templates/escaper.d.ts.map +0 -1
- package/dist/types/tdf3/src/templates/index.d.ts +0 -3
- package/dist/types/tdf3/src/templates/index.d.ts.map +0 -1
- package/dist/types/tdf3/src/utils/chunkers.d.ts +0 -29
- package/dist/types/tdf3/src/utils/chunkers.d.ts.map +0 -1
- package/dist/types/tdf3/src/version.d.ts +0 -3
- package/dist/types/tdf3/src/version.d.ts.map +0 -1
- package/dist/web/src/nanotdf-crypto/importRawKey.js +0 -15
- package/dist/web/tdf3/src/templates/default.html.js +0 -96
- package/dist/web/tdf3/src/templates/escaper.js +0 -10
- package/dist/web/tdf3/src/templates/index.js +0 -3
- package/dist/web/tdf3/src/utils/chunkers.js +0 -107
- package/dist/web/tdf3/src/version.js +0 -3
- package/src/nanotdf-crypto/importRawKey.ts +0 -19
- package/tdf3/src/templates/default.html.ts +0 -105
- package/tdf3/src/templates/escaper.ts +0 -10
- package/tdf3/src/templates/index.ts +0 -2
- package/tdf3/src/version.ts +0 -2
package/tdf3/src/tdf.ts
CHANGED
|
@@ -1,35 +1,16 @@
|
|
|
1
|
-
import { unsigned } from './utils/buffer-crc32.js';
|
|
2
1
|
import { exportSPKI, importX509 } from 'jose';
|
|
3
|
-
import { DecoratedReadableStream } from './client/DecoratedReadableStream.js';
|
|
4
|
-
import { fetchKasPubKey as fetchKasPubKeyV2, fetchWrappedKey } from '../../src/access.js';
|
|
5
|
-
import { DecryptParams } from './client/builders.js';
|
|
6
|
-
import { AssertionConfig, AssertionKey, AssertionVerificationKeys } from './assertions.js';
|
|
7
|
-
import * as assertions from './assertions.js';
|
|
8
2
|
|
|
9
3
|
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
} from './models/index.js';
|
|
21
|
-
import { base64 } from '../../src/encodings/index.js';
|
|
22
|
-
import {
|
|
23
|
-
type Chunker,
|
|
24
|
-
ZipReader,
|
|
25
|
-
ZipWriter,
|
|
26
|
-
base64ToBuffer,
|
|
27
|
-
keyMerge,
|
|
28
|
-
buffToString,
|
|
29
|
-
concatUint8,
|
|
30
|
-
} from './utils/index.js';
|
|
31
|
-
import { Binary } from './binary.js';
|
|
32
|
-
import { KasPublicKeyAlgorithm, KasPublicKeyInfo, OriginAllowList } from '../../src/access.js';
|
|
4
|
+
KasPublicKeyAlgorithm,
|
|
5
|
+
KasPublicKeyInfo,
|
|
6
|
+
OriginAllowList,
|
|
7
|
+
fetchKasPubKey as fetchKasPubKeyV2,
|
|
8
|
+
fetchWrappedKey,
|
|
9
|
+
publicKeyAlgorithmToJwa,
|
|
10
|
+
} from '../../src/access.js';
|
|
11
|
+
import { type AuthProvider, reqSignature } from '../../src/auth/auth.js';
|
|
12
|
+
import { allPool, anyPool } from '../../src/concurrency.js';
|
|
13
|
+
import { base64, hex } from '../../src/encodings/index.js';
|
|
33
14
|
import {
|
|
34
15
|
ConfigurationError,
|
|
35
16
|
DecryptError,
|
|
@@ -39,17 +20,40 @@ import {
|
|
|
39
20
|
UnsafeUrlError,
|
|
40
21
|
UnsupportedFeatureError as UnsupportedError,
|
|
41
22
|
} from '../../src/errors.js';
|
|
42
|
-
import {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
import { AesGcmCipher } from './ciphers/index.js';
|
|
47
|
-
import { type AuthProvider, reqSignature } from '../../src/auth/auth.js';
|
|
23
|
+
import { generateKeyPair } from '../../src/nanotdf-crypto/generateKeyPair.js';
|
|
24
|
+
import { keyAgreement } from '../../src/nanotdf-crypto/keyAgreement.js';
|
|
25
|
+
import { pemPublicToCrypto } from '../../src/nanotdf-crypto/pemPublicToCrypto.js';
|
|
26
|
+
import { type Chunker } from '../../src/seekable.js';
|
|
48
27
|
import { PolicyObject } from '../../src/tdf/PolicyObject.js';
|
|
49
|
-
import {
|
|
50
|
-
import {
|
|
28
|
+
import { tdfSpecVersion } from '../../src/version.js';
|
|
29
|
+
import { AssertionConfig, AssertionKey, AssertionVerificationKeys } from './assertions.js';
|
|
30
|
+
import * as assertions from './assertions.js';
|
|
31
|
+
import { Binary } from './binary.js';
|
|
32
|
+
import { AesGcmCipher } from './ciphers/aes-gcm-cipher.js';
|
|
51
33
|
import { SymmetricCipher } from './ciphers/symmetric-cipher-base.js';
|
|
52
|
-
import {
|
|
34
|
+
import { DecryptParams } from './client/builders.js';
|
|
35
|
+
import { DecoratedReadableStream } from './client/DecoratedReadableStream.js';
|
|
36
|
+
import {
|
|
37
|
+
AnyKeyPair,
|
|
38
|
+
PemKeyPair,
|
|
39
|
+
type CryptoService,
|
|
40
|
+
type DecryptResult,
|
|
41
|
+
} from './crypto/declarations.js';
|
|
42
|
+
import {
|
|
43
|
+
ECWrapped,
|
|
44
|
+
KeyAccessType,
|
|
45
|
+
KeyInfo,
|
|
46
|
+
Manifest,
|
|
47
|
+
Policy,
|
|
48
|
+
SplitKey,
|
|
49
|
+
Wrapped,
|
|
50
|
+
KeyAccess,
|
|
51
|
+
KeyAccessObject,
|
|
52
|
+
SplitType,
|
|
53
|
+
} from './models/index.js';
|
|
54
|
+
import { unsigned } from './utils/buffer-crc32.js';
|
|
55
|
+
import { ZipReader, ZipWriter, keyMerge, concatUint8 } from './utils/index.js';
|
|
56
|
+
import { CentralDirectory } from './utils/zip-reader.js';
|
|
53
57
|
|
|
54
58
|
// TODO: input validation on manifest JSON
|
|
55
59
|
const DEFAULT_SEGMENT_SIZE = 1024 * 1024;
|
|
@@ -77,6 +81,7 @@ export type Metadata = {
|
|
|
77
81
|
|
|
78
82
|
export type BuildKeyAccess = {
|
|
79
83
|
type: KeyAccessType;
|
|
84
|
+
alg?: KasPublicKeyAlgorithm;
|
|
80
85
|
url?: string;
|
|
81
86
|
kid?: string;
|
|
82
87
|
publicKey: string;
|
|
@@ -86,8 +91,8 @@ export type BuildKeyAccess = {
|
|
|
86
91
|
|
|
87
92
|
type Segment = {
|
|
88
93
|
hash: string;
|
|
89
|
-
segmentSize
|
|
90
|
-
encryptedSegmentSize
|
|
94
|
+
segmentSize?: number;
|
|
95
|
+
encryptedSegmentSize?: number;
|
|
91
96
|
};
|
|
92
97
|
|
|
93
98
|
type EntryInfo = {
|
|
@@ -97,20 +102,37 @@ type EntryInfo = {
|
|
|
97
102
|
fileByteCount?: number;
|
|
98
103
|
};
|
|
99
104
|
|
|
105
|
+
type Mailbox<T> = Promise<T> & {
|
|
106
|
+
set: (value: T) => void;
|
|
107
|
+
reject: (error: Error) => void;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
function mailbox<T>(): Mailbox<T> {
|
|
111
|
+
let set: (value: T) => void;
|
|
112
|
+
let reject: (error: Error) => void;
|
|
113
|
+
|
|
114
|
+
const promise = new Promise<T>((resolve, rejectFn) => {
|
|
115
|
+
set = resolve;
|
|
116
|
+
reject = rejectFn;
|
|
117
|
+
}) as Mailbox<T>;
|
|
118
|
+
|
|
119
|
+
promise.set = set!;
|
|
120
|
+
promise.reject = reject!;
|
|
121
|
+
|
|
122
|
+
return promise;
|
|
123
|
+
}
|
|
124
|
+
|
|
100
125
|
type Chunk = {
|
|
101
126
|
hash: string;
|
|
127
|
+
plainSegmentSize?: number;
|
|
102
128
|
encryptedOffset: number;
|
|
103
129
|
encryptedSegmentSize?: number;
|
|
104
|
-
decryptedChunk
|
|
105
|
-
promise: Promise<unknown>;
|
|
106
|
-
_resolve?: (value: unknown) => void;
|
|
107
|
-
_reject?: (value: unknown) => void;
|
|
130
|
+
decryptedChunk: Mailbox<DecryptResult>;
|
|
108
131
|
};
|
|
109
132
|
|
|
110
133
|
export type IntegrityAlgorithm = 'GMAC' | 'HS256';
|
|
111
134
|
|
|
112
135
|
export type EncryptConfiguration = {
|
|
113
|
-
allowedKases?: string[];
|
|
114
136
|
allowList?: OriginAllowList;
|
|
115
137
|
cryptoService: CryptoService;
|
|
116
138
|
dpopKeys: CryptoKeyPair;
|
|
@@ -144,6 +166,7 @@ export type DecryptConfiguration = {
|
|
|
144
166
|
assertionVerificationKeys?: AssertionVerificationKeys;
|
|
145
167
|
noVerifyAssertions?: boolean;
|
|
146
168
|
concurrencyLimit?: number;
|
|
169
|
+
wrappingKeyAlgorithm?: KasPublicKeyAlgorithm;
|
|
147
170
|
};
|
|
148
171
|
|
|
149
172
|
export type UpsertConfiguration = {
|
|
@@ -180,58 +203,17 @@ export async function fetchKasPublicKey(
|
|
|
180
203
|
return fetchKasPubKeyV2(kas, algorithm || 'rsa:2048');
|
|
181
204
|
}
|
|
182
205
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
* @param transferUrl reader web-service start page
|
|
188
|
-
* @return utf-8 encoded HTML data
|
|
189
|
-
*/
|
|
190
|
-
export function wrapHtml(
|
|
191
|
-
payload: Uint8Array,
|
|
192
|
-
manifest: Manifest | string,
|
|
193
|
-
transferUrl: string
|
|
194
|
-
): Uint8Array {
|
|
195
|
-
const { origin } = new URL(transferUrl);
|
|
196
|
-
const exportManifest: string = typeof manifest === 'string' ? manifest : JSON.stringify(manifest);
|
|
197
|
-
|
|
198
|
-
const fullHtmlString = htmlWrapperTemplate({
|
|
199
|
-
transferUrl,
|
|
200
|
-
transferBaseUrl: origin,
|
|
201
|
-
manifest: base64.encode(exportManifest),
|
|
202
|
-
payload: buffToString(payload, 'base64'),
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
return new TextEncoder().encode(fullHtmlString);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
export function unwrapHtml(htmlPayload: ArrayBuffer | Uint8Array | Binary | string) {
|
|
209
|
-
let html;
|
|
210
|
-
if (htmlPayload instanceof ArrayBuffer || ArrayBuffer.isView(htmlPayload)) {
|
|
211
|
-
html = new TextDecoder().decode(htmlPayload);
|
|
212
|
-
} else {
|
|
213
|
-
html = htmlPayload.toString();
|
|
214
|
-
}
|
|
215
|
-
const payloadRe = /<input id=['"]?data-input['"]?[^>]*?value=['"]?([a-zA-Z0-9+/=]+)['"]?/;
|
|
216
|
-
const reResult = payloadRe.exec(html);
|
|
217
|
-
if (reResult === null) {
|
|
218
|
-
throw new InvalidFileError('Payload is missing');
|
|
219
|
-
}
|
|
220
|
-
const base64Payload = reResult[1];
|
|
221
|
-
try {
|
|
222
|
-
return base64ToBuffer(base64Payload);
|
|
223
|
-
} catch (e) {
|
|
224
|
-
throw new InvalidFileError('There was a problem extracting the TDF3 payload', e);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
export async function extractPemFromKeyString(keyString: string): Promise<string> {
|
|
206
|
+
export async function extractPemFromKeyString(
|
|
207
|
+
keyString: string,
|
|
208
|
+
alg: KasPublicKeyAlgorithm
|
|
209
|
+
): Promise<string> {
|
|
229
210
|
let pem: string = keyString;
|
|
230
211
|
|
|
231
212
|
// Skip the public key extraction if we find that the KAS url provides a
|
|
232
213
|
// PEM-encoded key instead of certificate
|
|
233
214
|
if (keyString.includes('CERTIFICATE')) {
|
|
234
|
-
const
|
|
215
|
+
const a = publicKeyAlgorithmToJwa(alg);
|
|
216
|
+
const cert = await importX509(keyString, a, { extractable: true });
|
|
235
217
|
pem = await exportSPKI(cert);
|
|
236
218
|
}
|
|
237
219
|
|
|
@@ -258,32 +240,34 @@ export async function buildKeyAccess({
|
|
|
258
240
|
kid,
|
|
259
241
|
metadata,
|
|
260
242
|
sid = '',
|
|
243
|
+
alg = 'rsa:2048',
|
|
261
244
|
}: BuildKeyAccess): Promise<KeyAccess> {
|
|
262
|
-
/** Internal function to keep it DRY */
|
|
263
|
-
function createKeyAccess(
|
|
264
|
-
type: KeyAccessType,
|
|
265
|
-
kasUrl: string,
|
|
266
|
-
kasKeyIdentifier: string | undefined,
|
|
267
|
-
pubKey: string,
|
|
268
|
-
metadata?: Metadata
|
|
269
|
-
) {
|
|
270
|
-
switch (type) {
|
|
271
|
-
case 'wrapped':
|
|
272
|
-
return new KeyAccessWrapped(kasUrl, kasKeyIdentifier, pubKey, metadata, sid);
|
|
273
|
-
case 'remote':
|
|
274
|
-
return new KeyAccessRemote(kasUrl, kasKeyIdentifier, pubKey, metadata, sid);
|
|
275
|
-
default:
|
|
276
|
-
throw new ConfigurationError(`buildKeyAccess: Key access type ${type} is unknown`);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
245
|
// if url and pulicKey are specified load the key access object with them
|
|
281
|
-
if (url && publicKey) {
|
|
282
|
-
|
|
246
|
+
if (!url && !publicKey) {
|
|
247
|
+
throw new ConfigurationError('TDF.buildKeyAccess: No source for kasUrl or pubKey');
|
|
248
|
+
} else if (!url) {
|
|
249
|
+
throw new ConfigurationError('TDF.buildKeyAccess: No kasUrl');
|
|
250
|
+
} else if (!publicKey) {
|
|
251
|
+
throw new ConfigurationError('TDF.buildKeyAccess: No kas public key');
|
|
283
252
|
}
|
|
284
253
|
|
|
285
|
-
|
|
286
|
-
|
|
254
|
+
let pubKey: string;
|
|
255
|
+
try {
|
|
256
|
+
pubKey = await extractPemFromKeyString(publicKey, alg);
|
|
257
|
+
} catch (e) {
|
|
258
|
+
throw new ConfigurationError(
|
|
259
|
+
`TDF.buildKeyAccess: Invalid public key [${publicKey}], caused by [${e}]`,
|
|
260
|
+
e
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
switch (type) {
|
|
264
|
+
case 'wrapped':
|
|
265
|
+
return new Wrapped(url, kid, pubKey, metadata, sid);
|
|
266
|
+
case 'ec-wrapped':
|
|
267
|
+
return new ECWrapped(url, kid, pubKey, metadata, sid);
|
|
268
|
+
default:
|
|
269
|
+
throw new ConfigurationError(`buildKeyAccess: Key access type [${type}] is unsupported`);
|
|
270
|
+
}
|
|
287
271
|
}
|
|
288
272
|
|
|
289
273
|
export function validatePolicyObject(policy: Policy): void {
|
|
@@ -312,7 +296,6 @@ async function _generateManifest(
|
|
|
312
296
|
url: '0.payload',
|
|
313
297
|
protocol: 'zip',
|
|
314
298
|
isEncrypted: true,
|
|
315
|
-
schemaVersion: '3.0.0',
|
|
316
299
|
...(mimeType && { mimeType }),
|
|
317
300
|
};
|
|
318
301
|
|
|
@@ -323,25 +306,34 @@ async function _generateManifest(
|
|
|
323
306
|
// generate the manifest first, then insert integrity information into it
|
|
324
307
|
encryptionInformation: encryptionInformationStr,
|
|
325
308
|
assertions: assertions,
|
|
309
|
+
schemaVersion: tdfSpecVersion,
|
|
326
310
|
};
|
|
327
311
|
}
|
|
328
312
|
|
|
329
313
|
async function getSignature(
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
algorithmType: IntegrityAlgorithm
|
|
333
|
-
|
|
334
|
-
) {
|
|
314
|
+
unwrappedKey: Uint8Array,
|
|
315
|
+
content: Uint8Array,
|
|
316
|
+
algorithmType: IntegrityAlgorithm
|
|
317
|
+
): Promise<Uint8Array> {
|
|
335
318
|
switch (algorithmType.toUpperCase()) {
|
|
336
319
|
case 'GMAC':
|
|
337
320
|
// use the auth tag baked into the encrypted payload
|
|
338
|
-
return
|
|
339
|
-
case 'HS256':
|
|
321
|
+
return content.slice(-16);
|
|
322
|
+
case 'HS256': {
|
|
340
323
|
// simple hmac is the default
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
324
|
+
const cryptoKey = await crypto.subtle.importKey(
|
|
325
|
+
'raw',
|
|
326
|
+
unwrappedKey,
|
|
327
|
+
{
|
|
328
|
+
name: 'HMAC',
|
|
329
|
+
hash: { name: 'SHA-256' },
|
|
330
|
+
},
|
|
331
|
+
true,
|
|
332
|
+
['sign', 'verify']
|
|
344
333
|
);
|
|
334
|
+
const signature = await crypto.subtle.sign('HMAC', cryptoKey, content);
|
|
335
|
+
return new Uint8Array(signature);
|
|
336
|
+
}
|
|
345
337
|
default:
|
|
346
338
|
throw new ConfigurationError(`Unsupported signature alg [${algorithmType}]`);
|
|
347
339
|
}
|
|
@@ -375,7 +367,7 @@ export async function writeStream(cfg: EncryptConfiguration): Promise<DecoratedR
|
|
|
375
367
|
let bytesProcessed = 0;
|
|
376
368
|
let crcCounter = 0;
|
|
377
369
|
let fileByteCount = 0;
|
|
378
|
-
|
|
370
|
+
const segmentHashList: Uint8Array[] = [];
|
|
379
371
|
|
|
380
372
|
const zipWriter = new ZipWriter();
|
|
381
373
|
const manifest = await _generateManifest(
|
|
@@ -468,14 +460,16 @@ export async function writeStream(cfg: EncryptConfiguration): Promise<DecoratedR
|
|
|
468
460
|
fileByteCount = 0;
|
|
469
461
|
|
|
470
462
|
// hash the concat of all hashes
|
|
471
|
-
const
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
cfg.
|
|
475
|
-
|
|
463
|
+
const aggregateHash = await concatenateUint8Array(segmentHashList);
|
|
464
|
+
|
|
465
|
+
const payloadSig = await getSignature(
|
|
466
|
+
new Uint8Array(cfg.keyForEncryption.unwrappedKeyBinary.asArrayBuffer()),
|
|
467
|
+
aggregateHash,
|
|
468
|
+
cfg.integrityAlgorithm
|
|
476
469
|
);
|
|
477
|
-
|
|
478
|
-
|
|
470
|
+
|
|
471
|
+
const rootSig = base64.encodeArrayBuffer(payloadSig);
|
|
472
|
+
manifest.encryptionInformation.integrityInformation.rootSignature.sig = rootSig;
|
|
479
473
|
manifest.encryptionInformation.integrityInformation.rootSignature.alg =
|
|
480
474
|
cfg.integrityAlgorithm;
|
|
481
475
|
|
|
@@ -581,18 +575,16 @@ export async function writeStream(cfg: EncryptConfiguration): Promise<DecoratedR
|
|
|
581
575
|
cfg.keyForEncryption.unwrappedKeyBinary
|
|
582
576
|
);
|
|
583
577
|
const payloadBuffer = new Uint8Array(encryptedResult.payload.asByteArray());
|
|
584
|
-
const
|
|
585
|
-
cfg.keyForEncryption.unwrappedKeyBinary,
|
|
586
|
-
encryptedResult.payload,
|
|
587
|
-
cfg.segmentIntegrityAlgorithm
|
|
588
|
-
cfg.cryptoService
|
|
578
|
+
const payloadSig = await getSignature(
|
|
579
|
+
new Uint8Array(cfg.keyForEncryption.unwrappedKeyBinary.asArrayBuffer()),
|
|
580
|
+
new Uint8Array(encryptedResult.payload.asArrayBuffer()),
|
|
581
|
+
cfg.segmentIntegrityAlgorithm
|
|
589
582
|
);
|
|
590
583
|
|
|
591
|
-
|
|
592
|
-
aggregateHash += payloadSigStr;
|
|
584
|
+
segmentHashList.push(new Uint8Array(payloadSig));
|
|
593
585
|
|
|
594
586
|
segmentInfos.push({
|
|
595
|
-
hash: base64.
|
|
587
|
+
hash: base64.encodeArrayBuffer(payloadSig),
|
|
596
588
|
segmentSize: chunk.length === segmentSizeDefault ? undefined : chunk.length,
|
|
597
589
|
encryptedSegmentSize:
|
|
598
590
|
payloadBuffer.length === encryptedSegmentSizeDefault ? undefined : payloadBuffer.length,
|
|
@@ -660,6 +652,7 @@ async function unwrapKey({
|
|
|
660
652
|
dpopKeys,
|
|
661
653
|
concurrencyLimit,
|
|
662
654
|
cryptoService,
|
|
655
|
+
wrappingKeyAlgorithm,
|
|
663
656
|
}: {
|
|
664
657
|
manifest: Manifest;
|
|
665
658
|
allowedKases: OriginAllowList;
|
|
@@ -667,6 +660,7 @@ async function unwrapKey({
|
|
|
667
660
|
concurrencyLimit?: number;
|
|
668
661
|
dpopKeys: CryptoKeyPair;
|
|
669
662
|
cryptoService: CryptoService;
|
|
663
|
+
wrappingKeyAlgorithm?: KasPublicKeyAlgorithm;
|
|
670
664
|
}) {
|
|
671
665
|
if (authProvider === undefined) {
|
|
672
666
|
throw new ConfigurationError(
|
|
@@ -678,9 +672,18 @@ async function unwrapKey({
|
|
|
678
672
|
|
|
679
673
|
async function tryKasRewrap(keySplitInfo: KeyAccessObject): Promise<RewrapResponseData> {
|
|
680
674
|
const url = `${keySplitInfo.url}/v2/rewrap`;
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
)
|
|
675
|
+
let ephemeralEncryptionKeysRaw: AnyKeyPair;
|
|
676
|
+
let ephemeralEncryptionKeys: PemKeyPair;
|
|
677
|
+
if (wrappingKeyAlgorithm === 'ec:secp256r1') {
|
|
678
|
+
ephemeralEncryptionKeysRaw = await generateKeyPair();
|
|
679
|
+
ephemeralEncryptionKeys = await cryptoService.cryptoToPemPair(ephemeralEncryptionKeysRaw);
|
|
680
|
+
} else if (wrappingKeyAlgorithm === 'rsa:2048' || !wrappingKeyAlgorithm) {
|
|
681
|
+
ephemeralEncryptionKeysRaw = await cryptoService.generateKeyPair();
|
|
682
|
+
ephemeralEncryptionKeys = await cryptoService.cryptoToPemPair(ephemeralEncryptionKeysRaw);
|
|
683
|
+
} else {
|
|
684
|
+
throw new ConfigurationError(`Unsupported wrapping key algorithm [${wrappingKeyAlgorithm}]`);
|
|
685
|
+
}
|
|
686
|
+
|
|
684
687
|
const clientPublicKey = ephemeralEncryptionKeys.publicKey;
|
|
685
688
|
|
|
686
689
|
const requestBodyStr = JSON.stringify({
|
|
@@ -693,13 +696,31 @@ async function unwrapKey({
|
|
|
693
696
|
const jwtPayload = { requestBody: requestBodyStr };
|
|
694
697
|
const signedRequestToken = await reqSignature(jwtPayload, dpopKeys.privateKey);
|
|
695
698
|
|
|
696
|
-
const { entityWrappedKey, metadata } = await fetchWrappedKey(
|
|
699
|
+
const { entityWrappedKey, metadata, sessionPublicKey } = await fetchWrappedKey(
|
|
697
700
|
url,
|
|
698
701
|
{ signedRequestToken },
|
|
699
702
|
authProvider,
|
|
700
703
|
'0.0.1'
|
|
701
704
|
);
|
|
702
705
|
|
|
706
|
+
if (wrappingKeyAlgorithm === 'ec:secp256r1') {
|
|
707
|
+
const serverEphemeralKey: CryptoKey = await pemPublicToCrypto(sessionPublicKey);
|
|
708
|
+
const ekr = ephemeralEncryptionKeysRaw as CryptoKeyPair;
|
|
709
|
+
const kek = await keyAgreement(ekr.privateKey, serverEphemeralKey, {
|
|
710
|
+
hkdfSalt: new TextEncoder().encode('salt'),
|
|
711
|
+
hkdfHash: 'SHA-256',
|
|
712
|
+
});
|
|
713
|
+
const wrappedKeyAndNonce = base64.decodeArrayBuffer(entityWrappedKey);
|
|
714
|
+
const iv = wrappedKeyAndNonce.slice(0, 12);
|
|
715
|
+
const wrappedKey = wrappedKeyAndNonce.slice(12);
|
|
716
|
+
|
|
717
|
+
const dek = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, kek, wrappedKey);
|
|
718
|
+
|
|
719
|
+
return {
|
|
720
|
+
key: new Uint8Array(dek),
|
|
721
|
+
metadata,
|
|
722
|
+
};
|
|
723
|
+
}
|
|
703
724
|
const key = Binary.fromString(base64.decode(entityWrappedKey));
|
|
704
725
|
const decryptedKeyBinary = await cryptoService.decryptWithPrivateKey(
|
|
705
726
|
key,
|
|
@@ -769,17 +790,22 @@ async function decryptChunk(
|
|
|
769
790
|
hash: string,
|
|
770
791
|
cipher: SymmetricCipher,
|
|
771
792
|
segmentIntegrityAlgorithm: IntegrityAlgorithm,
|
|
772
|
-
|
|
793
|
+
isLegacyTDF: boolean
|
|
773
794
|
): Promise<DecryptResult> {
|
|
774
795
|
if (segmentIntegrityAlgorithm !== 'GMAC' && segmentIntegrityAlgorithm !== 'HS256') {
|
|
796
|
+
throw new UnsupportedError(`Unsupported integrity alg [${segmentIntegrityAlgorithm}]`);
|
|
775
797
|
}
|
|
776
|
-
const
|
|
777
|
-
reconstructedKeyBinary,
|
|
778
|
-
|
|
779
|
-
segmentIntegrityAlgorithm
|
|
780
|
-
cryptoService
|
|
798
|
+
const segmentSig = await getSignature(
|
|
799
|
+
new Uint8Array(reconstructedKeyBinary.asArrayBuffer()),
|
|
800
|
+
encryptedChunk,
|
|
801
|
+
segmentIntegrityAlgorithm
|
|
781
802
|
);
|
|
782
|
-
|
|
803
|
+
|
|
804
|
+
const segmentHash = isLegacyTDF
|
|
805
|
+
? base64.encode(hex.encodeArrayBuffer(segmentSig))
|
|
806
|
+
: base64.encodeArrayBuffer(segmentSig);
|
|
807
|
+
|
|
808
|
+
if (hash !== segmentHash) {
|
|
783
809
|
throw new IntegrityError('Failed integrity check on segment hash');
|
|
784
810
|
}
|
|
785
811
|
return await cipher.decrypt(encryptedChunk, reconstructedKeyBinary);
|
|
@@ -792,7 +818,8 @@ async function updateChunkQueue(
|
|
|
792
818
|
reconstructedKeyBinary: Binary,
|
|
793
819
|
cipher: SymmetricCipher,
|
|
794
820
|
segmentIntegrityAlgorithm: IntegrityAlgorithm,
|
|
795
|
-
cryptoService: CryptoService
|
|
821
|
+
cryptoService: CryptoService,
|
|
822
|
+
isLegacyTDF: boolean
|
|
796
823
|
) {
|
|
797
824
|
const chunksInOneDownload = 500;
|
|
798
825
|
let requests = [];
|
|
@@ -833,6 +860,7 @@ async function updateChunkQueue(
|
|
|
833
860
|
slice,
|
|
834
861
|
cipher,
|
|
835
862
|
segmentIntegrityAlgorithm,
|
|
863
|
+
isLegacyTDF,
|
|
836
864
|
});
|
|
837
865
|
}
|
|
838
866
|
})()
|
|
@@ -845,8 +873,8 @@ export async function sliceAndDecrypt({
|
|
|
845
873
|
reconstructedKeyBinary,
|
|
846
874
|
slice,
|
|
847
875
|
cipher,
|
|
848
|
-
cryptoService,
|
|
849
876
|
segmentIntegrityAlgorithm,
|
|
877
|
+
isLegacyTDF,
|
|
850
878
|
}: {
|
|
851
879
|
buffer: Uint8Array;
|
|
852
880
|
reconstructedKeyBinary: Binary;
|
|
@@ -854,9 +882,10 @@ export async function sliceAndDecrypt({
|
|
|
854
882
|
cipher: SymmetricCipher;
|
|
855
883
|
cryptoService: CryptoService;
|
|
856
884
|
segmentIntegrityAlgorithm: IntegrityAlgorithm;
|
|
885
|
+
isLegacyTDF: boolean;
|
|
857
886
|
}) {
|
|
858
887
|
for (const index in slice) {
|
|
859
|
-
const { encryptedOffset, encryptedSegmentSize,
|
|
888
|
+
const { encryptedOffset, encryptedSegmentSize, plainSegmentSize } = slice[index];
|
|
860
889
|
|
|
861
890
|
const offset =
|
|
862
891
|
slice[0].encryptedOffset === 0 ? encryptedOffset : encryptedOffset % slice[0].encryptedOffset;
|
|
@@ -864,6 +893,10 @@ export async function sliceAndDecrypt({
|
|
|
864
893
|
buffer.slice(offset, offset + (encryptedSegmentSize as number))
|
|
865
894
|
);
|
|
866
895
|
|
|
896
|
+
if (encryptedChunk.length !== encryptedSegmentSize) {
|
|
897
|
+
throw new DecryptError('Failed to fetch entire segment');
|
|
898
|
+
}
|
|
899
|
+
|
|
867
900
|
try {
|
|
868
901
|
const result = await decryptChunk(
|
|
869
902
|
encryptedChunk,
|
|
@@ -871,18 +904,16 @@ export async function sliceAndDecrypt({
|
|
|
871
904
|
slice[index]['hash'],
|
|
872
905
|
cipher,
|
|
873
906
|
segmentIntegrityAlgorithm,
|
|
874
|
-
|
|
907
|
+
isLegacyTDF
|
|
875
908
|
);
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
909
|
+
if (plainSegmentSize && result.payload.length() !== plainSegmentSize) {
|
|
910
|
+
throw new DecryptError(
|
|
911
|
+
`incorrect segment size: found [${result.payload.length()}], expected [${plainSegmentSize}]`
|
|
912
|
+
);
|
|
879
913
|
}
|
|
914
|
+
slice[index].decryptedChunk.set(result);
|
|
880
915
|
} catch (e) {
|
|
881
|
-
|
|
882
|
-
_reject(e);
|
|
883
|
-
} else {
|
|
884
|
-
throw e;
|
|
885
|
-
}
|
|
916
|
+
slice[index].decryptedChunk.reject(e);
|
|
886
917
|
}
|
|
887
918
|
}
|
|
888
919
|
}
|
|
@@ -905,6 +936,7 @@ export async function readStream(cfg: DecryptConfiguration) {
|
|
|
905
936
|
encryptedSegmentSizeDefault: defaultSegmentSize,
|
|
906
937
|
rootSignature,
|
|
907
938
|
segmentHashAlg,
|
|
939
|
+
segmentSizeDefault,
|
|
908
940
|
segments,
|
|
909
941
|
} = manifest.encryptionInformation.integrityInformation;
|
|
910
942
|
const { metadata, reconstructedKeyBinary } = await unwrapKey({
|
|
@@ -918,25 +950,28 @@ export async function readStream(cfg: DecryptConfiguration) {
|
|
|
918
950
|
const keyForDecryption = await cfg.keyMiddleware(reconstructedKeyBinary);
|
|
919
951
|
const encryptedSegmentSizeDefault = defaultSegmentSize || DEFAULT_SEGMENT_SIZE;
|
|
920
952
|
|
|
921
|
-
// check the
|
|
922
|
-
const
|
|
953
|
+
// check if the TDF is a legacy TDF
|
|
954
|
+
const specVersion = manifest.schemaVersion || manifest.tdf_spec_version;
|
|
955
|
+
const isLegacyTDF = !specVersion || specVersion.startsWith('3.');
|
|
956
|
+
|
|
957
|
+
// Decode each hash and store it in an array of Uint8Array
|
|
958
|
+
const segmentHashList = segments.map(
|
|
959
|
+
({ hash }) => new Uint8Array(base64.decodeArrayBuffer(hash))
|
|
960
|
+
);
|
|
961
|
+
|
|
962
|
+
// Concatenate all segment hashes into a single Uint8Array
|
|
963
|
+
const aggregateHash = await concatenateUint8Array(segmentHashList);
|
|
964
|
+
|
|
923
965
|
const integrityAlgorithm = rootSignature.alg;
|
|
924
966
|
if (integrityAlgorithm !== 'GMAC' && integrityAlgorithm !== 'HS256') {
|
|
925
967
|
throw new UnsupportedError(`Unsupported integrity alg [${integrityAlgorithm}]`);
|
|
926
968
|
}
|
|
927
|
-
const payloadSigStr = await getSignature(
|
|
928
|
-
keyForDecryption,
|
|
929
|
-
Binary.fromString(aggregateHash),
|
|
930
|
-
integrityAlgorithm,
|
|
931
|
-
cfg.cryptoService
|
|
932
|
-
);
|
|
933
969
|
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
}
|
|
970
|
+
const payloadSig = await getSignature(
|
|
971
|
+
new Uint8Array(keyForDecryption.asArrayBuffer()),
|
|
972
|
+
aggregateHash,
|
|
973
|
+
integrityAlgorithm
|
|
974
|
+
);
|
|
940
975
|
|
|
941
976
|
if (!cfg.noVerifyAssertions) {
|
|
942
977
|
for (const assertion of manifest.assertions || []) {
|
|
@@ -952,31 +987,40 @@ export async function readStream(cfg: DecryptConfiguration) {
|
|
|
952
987
|
assertionKey = foundKey;
|
|
953
988
|
}
|
|
954
989
|
}
|
|
955
|
-
await assertions.verify(assertion, aggregateHash, assertionKey);
|
|
990
|
+
await assertions.verify(assertion, aggregateHash, assertionKey, isLegacyTDF);
|
|
956
991
|
}
|
|
957
992
|
}
|
|
958
993
|
|
|
994
|
+
const rootSig = isLegacyTDF
|
|
995
|
+
? base64.encode(hex.encodeArrayBuffer(payloadSig))
|
|
996
|
+
: base64.encodeArrayBuffer(payloadSig);
|
|
997
|
+
|
|
998
|
+
if (manifest.encryptionInformation.integrityInformation.rootSignature.sig !== rootSig) {
|
|
999
|
+
throw new IntegrityError('Failed integrity check on root signature');
|
|
1000
|
+
}
|
|
1001
|
+
|
|
959
1002
|
let mapOfRequestsOffset = 0;
|
|
960
1003
|
const chunkMap = new Map(
|
|
961
|
-
segments.map(
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
1004
|
+
segments.map(
|
|
1005
|
+
({
|
|
1006
|
+
hash,
|
|
1007
|
+
encryptedSegmentSize = encryptedSegmentSizeDefault,
|
|
1008
|
+
segmentSize = segmentSizeDefault,
|
|
1009
|
+
}) => {
|
|
1010
|
+
const result = (() => {
|
|
1011
|
+
const chunk: Chunk = {
|
|
1012
|
+
hash,
|
|
1013
|
+
encryptedOffset: mapOfRequestsOffset,
|
|
1014
|
+
encryptedSegmentSize,
|
|
1015
|
+
decryptedChunk: mailbox<DecryptResult>(),
|
|
1016
|
+
plainSegmentSize: segmentSize,
|
|
1017
|
+
};
|
|
1018
|
+
return chunk;
|
|
1019
|
+
})();
|
|
1020
|
+
mapOfRequestsOffset += encryptedSegmentSize;
|
|
1021
|
+
return [hash, result];
|
|
1022
|
+
}
|
|
1023
|
+
)
|
|
980
1024
|
);
|
|
981
1025
|
|
|
982
1026
|
const cipher = new AesGcmCipher(cfg.cryptoService);
|
|
@@ -993,7 +1037,8 @@ export async function readStream(cfg: DecryptConfiguration) {
|
|
|
993
1037
|
keyForDecryption,
|
|
994
1038
|
cipher,
|
|
995
1039
|
segmentIntegrityAlg,
|
|
996
|
-
cfg.cryptoService
|
|
1040
|
+
cfg.cryptoService,
|
|
1041
|
+
isLegacyTDF
|
|
997
1042
|
);
|
|
998
1043
|
|
|
999
1044
|
let progress = 0;
|
|
@@ -1005,16 +1050,11 @@ export async function readStream(cfg: DecryptConfiguration) {
|
|
|
1005
1050
|
}
|
|
1006
1051
|
|
|
1007
1052
|
const [hash, chunk] = chunkMap.entries().next().value;
|
|
1008
|
-
|
|
1009
|
-
await chunk.promise;
|
|
1010
|
-
}
|
|
1011
|
-
const decryptedSegment = chunk.decryptedChunk;
|
|
1053
|
+
const decryptedSegment = await chunk.decryptedChunk;
|
|
1012
1054
|
|
|
1013
1055
|
controller.enqueue(new Uint8Array(decryptedSegment.payload.asByteArray()));
|
|
1014
1056
|
progress += chunk.encryptedSegmentSize;
|
|
1015
1057
|
cfg.progressHandler?.(progress);
|
|
1016
|
-
|
|
1017
|
-
chunk.decryptedChunk = null;
|
|
1018
1058
|
chunkMap.delete(hash);
|
|
1019
1059
|
},
|
|
1020
1060
|
...(cfg.fileStreamServiceWorker && { fileStreamServiceWorker: cfg.fileStreamServiceWorker }),
|
|
@@ -1023,8 +1063,12 @@ export async function readStream(cfg: DecryptConfiguration) {
|
|
|
1023
1063
|
const outputStream = new DecoratedReadableStream(underlyingSource);
|
|
1024
1064
|
|
|
1025
1065
|
outputStream.manifest = manifest;
|
|
1026
|
-
outputStream.emit('manifest', manifest);
|
|
1027
1066
|
outputStream.metadata = metadata;
|
|
1028
|
-
outputStream.emit('rewrap', metadata);
|
|
1029
1067
|
return outputStream;
|
|
1030
1068
|
}
|
|
1069
|
+
|
|
1070
|
+
async function concatenateUint8Array(uint8arrays: Uint8Array[]): Promise<Uint8Array> {
|
|
1071
|
+
const blob = new Blob(uint8arrays);
|
|
1072
|
+
const buffer = await blob.arrayBuffer();
|
|
1073
|
+
return new Uint8Array(buffer);
|
|
1074
|
+
}
|