@opentdf/sdk 0.1.0-beta.1718 → 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.
- package/README.md +45 -38
- package/dist/cjs/src/access.js +99 -62
- package/dist/cjs/src/auth/auth.js +5 -26
- 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 -320
- package/dist/cjs/src/nanoclients.js +285 -0
- package/dist/cjs/src/nanoindex.js +47 -0
- package/dist/cjs/src/nanotdf/Client.js +35 -30
- 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 +243 -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 +3 -3
- package/dist/cjs/src/utils.js +13 -21
- package/dist/cjs/src/version.js +7 -3
- package/dist/cjs/tdf3/index.js +27 -16
- 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 +91 -117
- 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/index.js +1 -2
- 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 +180 -395
- package/dist/cjs/tdf3/src/utils/buffer-crc32.js +2 -3
- package/dist/cjs/tdf3/src/utils/index.js +48 -38
- 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 +10 -4
- package/dist/types/src/access.d.ts.map +1 -1
- package/dist/types/src/auth/auth.d.ts +1 -28
- package/dist/types/src/auth/auth.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/Client.d.ts +1 -13
- package/dist/types/src/nanotdf/Client.d.ts.map +1 -1
- package/dist/types/src/nanotdf/NanoTDF.d.ts +1 -1
- package/dist/types/src/nanotdf/NanoTDF.d.ts.map +1 -1
- package/dist/types/src/nanotdf/encrypt-dataset.d.ts +1 -1
- package/dist/types/src/nanotdf/encrypt-dataset.d.ts.map +1 -1
- package/dist/types/src/nanotdf/encrypt.d.ts +1 -1
- package/dist/types/src/nanotdf/encrypt.d.ts.map +1 -1
- 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/helpers/getHkdfSalt.d.ts +1 -1
- package/dist/types/src/nanotdf/helpers/getHkdfSalt.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/digest.d.ts +1 -1
- package/dist/types/src/nanotdf-crypto/digest.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 +106 -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 +1 -1
- package/dist/types/src/tdf/Policy.d.ts.map +1 -1
- package/dist/types/src/tdf/PolicyObject.d.ts +1 -2
- package/dist/types/src/tdf/PolicyObject.d.ts.map +1 -1
- package/dist/types/src/tdf/TypedArray.d.ts +1 -2
- package/dist/types/src/tdf/TypedArray.d.ts.map +1 -1
- package/dist/types/src/utils.d.ts +1 -3
- package/dist/types/src/utils.d.ts.map +1 -1
- package/dist/types/src/version.d.ts +5 -1
- package/dist/types/src/version.d.ts.map +1 -1
- package/dist/types/tdf3/index.d.ts +5 -4
- 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 +2 -15
- package/dist/types/tdf3/src/client/DecoratedReadableStream.d.ts.map +1 -1
- package/dist/types/tdf3/src/client/builders.d.ts +43 -42
- package/dist/types/tdf3/src/client/builders.d.ts.map +1 -1
- package/dist/types/tdf3/src/client/index.d.ts +12 -17
- 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/index.d.ts +0 -1
- package/dist/types/tdf3/src/models/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 +24 -37
- package/dist/types/tdf3/src/tdf.d.ts.map +1 -1
- package/dist/types/tdf3/src/utils/index.d.ts +0 -4
- 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 +93 -58
- package/dist/web/src/auth/auth.js +1 -21
- 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 -312
- package/dist/web/src/nanoclients.js +280 -0
- package/dist/web/src/nanoindex.js +5 -0
- package/dist/web/src/nanotdf/Client.js +18 -23
- 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 +234 -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 +2 -4
- package/dist/web/src/utils.js +3 -10
- package/dist/web/src/version.js +6 -2
- package/dist/web/tdf3/index.js +5 -4
- 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 +74 -105
- 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/index.js +1 -2
- 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 +153 -371
- package/dist/web/tdf3/src/utils/buffer-crc32.js +1 -1
- package/dist/web/tdf3/src/utils/index.js +19 -14
- 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 -45
- package/src/access.ts +111 -54
- package/src/auth/auth.ts +1 -31
- package/src/index.ts +5 -440
- package/src/nanoclients.ts +405 -0
- package/src/nanoindex.ts +4 -0
- package/src/nanotdf/Client.ts +18 -25
- package/src/nanotdf/NanoTDF.ts +1 -1
- package/src/nanotdf/encrypt-dataset.ts +1 -1
- package/src/nanotdf/encrypt.ts +1 -1
- package/src/nanotdf/helpers/getHkdfSalt.ts +1 -1
- package/src/nanotdf-crypto/digest.ts +1 -1
- 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 +441 -0
- package/src/seekable.ts +180 -0
- package/src/tdf/AttributeObject.ts +0 -3
- package/src/tdf/Policy.ts +1 -2
- package/src/tdf/PolicyObject.ts +1 -2
- package/src/tdf/TypedArray.ts +1 -3
- package/src/utils.ts +3 -11
- package/src/version.ts +6 -1
- package/tdf3/index.ts +15 -10
- package/tdf3/src/assertions.ts +33 -8
- package/tdf3/src/client/DecoratedReadableStream.ts +3 -80
- package/tdf3/src/client/builders.ts +44 -28
- package/tdf3/src/client/index.ts +109 -165
- package/tdf3/src/index.ts +1 -1
- package/tdf3/src/models/encryption-information.ts +2 -2
- package/tdf3/src/models/index.ts +0 -1
- 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 +266 -522
- package/tdf3/src/utils/index.ts +19 -18
- package/tdf3/src/utils/unwrap.ts +17 -0
- package/tdf3/src/utils/zip-reader.ts +1 -1
- package/dist/cjs/src/auth/Eas.js +0 -60
- package/dist/cjs/src/nanotdf-crypto/importRawKey.js +0 -18
- package/dist/cjs/src/tdf/Crypto.js +0 -47
- package/dist/cjs/src/tdf/EntityObject.js +0 -3
- package/dist/cjs/src/tdf/index.js +0 -35
- package/dist/cjs/tdf3/src/models/upsert-response.js +0 -3
- 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 -106
- package/dist/cjs/tdf3/src/version.js +0 -6
- package/dist/types/src/auth/Eas.d.ts +0 -34
- package/dist/types/src/auth/Eas.d.ts.map +0 -1
- 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/src/tdf/Crypto.d.ts +0 -37
- package/dist/types/src/tdf/Crypto.d.ts.map +0 -1
- package/dist/types/src/tdf/EntityObject.d.ts +0 -18
- package/dist/types/src/tdf/EntityObject.d.ts.map +0 -1
- package/dist/types/src/tdf/index.d.ts +0 -7
- package/dist/types/src/tdf/index.d.ts.map +0 -1
- package/dist/types/tdf3/src/models/upsert-response.d.ts +0 -16
- package/dist/types/tdf3/src/models/upsert-response.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/auth/Eas.js +0 -55
- package/dist/web/src/nanotdf-crypto/importRawKey.js +0 -15
- package/dist/web/src/tdf/Crypto.js +0 -44
- package/dist/web/src/tdf/EntityObject.js +0 -2
- package/dist/web/src/tdf/index.js +0 -4
- package/dist/web/tdf3/src/models/upsert-response.js +0 -2
- 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 -96
- package/dist/web/tdf3/src/version.js +0 -3
- package/src/auth/Eas.ts +0 -79
- package/src/nanotdf-crypto/importRawKey.ts +0 -19
- package/src/tdf/Crypto.ts +0 -42
- package/src/tdf/EntityObject.ts +0 -18
- package/src/tdf/index.ts +0 -6
- package/tdf3/src/models/upsert-response.ts +0 -17
- 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/utils/chunkers.ts +0 -118
- package/tdf3/src/version.ts +0 -2
package/tdf3/src/tdf.ts
CHANGED
|
@@ -1,71 +1,59 @@
|
|
|
1
|
-
import axios, { AxiosError } from 'axios';
|
|
2
|
-
import { unsigned } from './utils/buffer-crc32.js';
|
|
3
1
|
import { exportSPKI, importX509 } from 'jose';
|
|
4
|
-
import { DecoratedReadableStream } from './client/DecoratedReadableStream.js';
|
|
5
|
-
import { EntityObject } from '../../src/tdf/index.js';
|
|
6
|
-
import { pemToCryptoPublicKey, validateSecureUrl } from '../../src/utils.js';
|
|
7
|
-
import { DecryptParams } from './client/builders.js';
|
|
8
|
-
import { AssertionConfig, AssertionKey, AssertionVerificationKeys } from './assertions.js';
|
|
9
|
-
import * as assertions from './assertions.js';
|
|
10
2
|
|
|
11
3
|
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
KeyAccess,
|
|
23
|
-
KeyAccessObject,
|
|
24
|
-
SplitType,
|
|
25
|
-
} from './models/index.js';
|
|
26
|
-
import { base64 } from '../../src/encodings/index.js';
|
|
27
|
-
import {
|
|
28
|
-
type Chunker,
|
|
29
|
-
ZipReader,
|
|
30
|
-
ZipWriter,
|
|
31
|
-
base64ToBuffer,
|
|
32
|
-
isAppIdProviderCheck,
|
|
33
|
-
keyMerge,
|
|
34
|
-
buffToString,
|
|
35
|
-
concatUint8,
|
|
36
|
-
} from './utils/index.js';
|
|
37
|
-
import { Binary } from './binary.js';
|
|
38
|
-
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';
|
|
39
14
|
import {
|
|
40
15
|
ConfigurationError,
|
|
41
16
|
DecryptError,
|
|
42
17
|
InvalidFileError,
|
|
43
18
|
IntegrityError,
|
|
44
19
|
NetworkError,
|
|
45
|
-
PermissionDeniedError,
|
|
46
|
-
ServiceError,
|
|
47
|
-
TdfError,
|
|
48
|
-
UnauthenticatedError,
|
|
49
20
|
UnsafeUrlError,
|
|
50
21
|
UnsupportedFeatureError as UnsupportedError,
|
|
51
22
|
} from '../../src/errors.js';
|
|
52
|
-
import {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
import {
|
|
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';
|
|
27
|
+
import { PolicyObject } from '../../src/tdf/PolicyObject.js';
|
|
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';
|
|
33
|
+
import { SymmetricCipher } from './ciphers/symmetric-cipher-base.js';
|
|
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';
|
|
57
42
|
import {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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';
|
|
66
56
|
import { CentralDirectory } from './utils/zip-reader.js';
|
|
67
|
-
import { SymmetricCipher } from './ciphers/symmetric-cipher-base.js';
|
|
68
|
-
import { allPool, anyPool } from '../../src/concurrency.js';
|
|
69
57
|
|
|
70
58
|
// TODO: input validation on manifest JSON
|
|
71
59
|
const DEFAULT_SEGMENT_SIZE = 1024 * 1024;
|
|
@@ -92,20 +80,19 @@ export type Metadata = {
|
|
|
92
80
|
};
|
|
93
81
|
|
|
94
82
|
export type BuildKeyAccess = {
|
|
95
|
-
attributeSet?: AttributeSet;
|
|
96
83
|
type: KeyAccessType;
|
|
84
|
+
alg?: KasPublicKeyAlgorithm;
|
|
97
85
|
url?: string;
|
|
98
86
|
kid?: string;
|
|
99
87
|
publicKey: string;
|
|
100
|
-
attributeUrl?: string;
|
|
101
88
|
metadata?: Metadata;
|
|
102
89
|
sid?: string;
|
|
103
90
|
};
|
|
104
91
|
|
|
105
92
|
type Segment = {
|
|
106
93
|
hash: string;
|
|
107
|
-
segmentSize
|
|
108
|
-
encryptedSegmentSize
|
|
94
|
+
segmentSize?: number;
|
|
95
|
+
encryptedSegmentSize?: number;
|
|
109
96
|
};
|
|
110
97
|
|
|
111
98
|
type EntryInfo = {
|
|
@@ -115,20 +102,37 @@ type EntryInfo = {
|
|
|
115
102
|
fileByteCount?: number;
|
|
116
103
|
};
|
|
117
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
|
+
|
|
118
125
|
type Chunk = {
|
|
119
126
|
hash: string;
|
|
127
|
+
plainSegmentSize?: number;
|
|
120
128
|
encryptedOffset: number;
|
|
121
129
|
encryptedSegmentSize?: number;
|
|
122
|
-
decryptedChunk
|
|
123
|
-
promise: Promise<unknown>;
|
|
124
|
-
_resolve?: (value: unknown) => void;
|
|
125
|
-
_reject?: (value: unknown) => void;
|
|
130
|
+
decryptedChunk: Mailbox<DecryptResult>;
|
|
126
131
|
};
|
|
127
132
|
|
|
128
133
|
export type IntegrityAlgorithm = 'GMAC' | 'HS256';
|
|
129
134
|
|
|
130
135
|
export type EncryptConfiguration = {
|
|
131
|
-
allowedKases?: string[];
|
|
132
136
|
allowList?: OriginAllowList;
|
|
133
137
|
cryptoService: CryptoService;
|
|
134
138
|
dpopKeys: CryptoKeyPair;
|
|
@@ -139,9 +143,7 @@ export type EncryptConfiguration = {
|
|
|
139
143
|
contentStream: ReadableStream<Uint8Array>;
|
|
140
144
|
mimeType?: string;
|
|
141
145
|
policy: Policy;
|
|
142
|
-
|
|
143
|
-
attributeSet?: AttributeSet;
|
|
144
|
-
authProvider?: AuthProvider | AppIdAuthProvider;
|
|
146
|
+
authProvider?: AuthProvider;
|
|
145
147
|
byteLimit: number;
|
|
146
148
|
progressHandler?: (bytesProcessed: number) => void;
|
|
147
149
|
keyForEncryption: KeyInfo;
|
|
@@ -152,9 +154,8 @@ export type EncryptConfiguration = {
|
|
|
152
154
|
export type DecryptConfiguration = {
|
|
153
155
|
allowedKases?: string[];
|
|
154
156
|
allowList?: OriginAllowList;
|
|
155
|
-
authProvider: AuthProvider
|
|
157
|
+
authProvider: AuthProvider;
|
|
156
158
|
cryptoService: CryptoService;
|
|
157
|
-
entity?: EntityObject;
|
|
158
159
|
|
|
159
160
|
dpopKeys: CryptoKeyPair;
|
|
160
161
|
|
|
@@ -165,13 +166,13 @@ export type DecryptConfiguration = {
|
|
|
165
166
|
assertionVerificationKeys?: AssertionVerificationKeys;
|
|
166
167
|
noVerifyAssertions?: boolean;
|
|
167
168
|
concurrencyLimit?: number;
|
|
169
|
+
wrappingKeyAlgorithm?: KasPublicKeyAlgorithm;
|
|
168
170
|
};
|
|
169
171
|
|
|
170
172
|
export type UpsertConfiguration = {
|
|
171
173
|
allowedKases?: string[];
|
|
172
174
|
allowList?: OriginAllowList;
|
|
173
|
-
authProvider: AuthProvider
|
|
174
|
-
entity?: EntityObject;
|
|
175
|
+
authProvider: AuthProvider;
|
|
175
176
|
|
|
176
177
|
privateKey: CryptoKey;
|
|
177
178
|
|
|
@@ -186,12 +187,6 @@ export type RewrapRequest = {
|
|
|
186
187
|
|
|
187
188
|
export type KasPublicKeyFormat = 'pkcs8' | 'jwks';
|
|
188
189
|
|
|
189
|
-
type KasPublicKeyParams = {
|
|
190
|
-
algorithm?: KasPublicKeyAlgorithm;
|
|
191
|
-
fmt?: KasPublicKeyFormat;
|
|
192
|
-
v?: '1' | '2';
|
|
193
|
-
};
|
|
194
|
-
|
|
195
190
|
export type RewrapResponse = {
|
|
196
191
|
entityWrappedKey: string;
|
|
197
192
|
sessionPublicKey: string;
|
|
@@ -205,148 +200,20 @@ export async function fetchKasPublicKey(
|
|
|
205
200
|
kas: string,
|
|
206
201
|
algorithm?: KasPublicKeyAlgorithm
|
|
207
202
|
): Promise<KasPublicKeyInfo> {
|
|
208
|
-
|
|
209
|
-
throw new ConfigurationError('KAS definition not found');
|
|
210
|
-
}
|
|
211
|
-
// Logs insecure KAS. Secure is enforced in constructor
|
|
212
|
-
validateSecureUrl(kas);
|
|
213
|
-
const infoStatic = { url: kas, algorithm: algorithm || 'rsa:2048' };
|
|
214
|
-
const params: KasPublicKeyParams = {};
|
|
215
|
-
if (algorithm) {
|
|
216
|
-
params.algorithm = algorithm;
|
|
217
|
-
}
|
|
218
|
-
const v2Url = `${kas}/v2/kas_public_key`;
|
|
219
|
-
try {
|
|
220
|
-
const response: { data: string | KasPublicKeyInfo } = await axios.get(v2Url, {
|
|
221
|
-
params: {
|
|
222
|
-
...params,
|
|
223
|
-
v: '2',
|
|
224
|
-
},
|
|
225
|
-
});
|
|
226
|
-
const publicKey =
|
|
227
|
-
typeof response.data === 'string'
|
|
228
|
-
? await extractPemFromKeyString(response.data)
|
|
229
|
-
: response.data.publicKey;
|
|
230
|
-
return {
|
|
231
|
-
publicKey,
|
|
232
|
-
key: pemToCryptoPublicKey(publicKey),
|
|
233
|
-
...infoStatic,
|
|
234
|
-
...(typeof response.data !== 'string' && response.data.kid && { kid: response.data.kid }),
|
|
235
|
-
};
|
|
236
|
-
} catch (cause) {
|
|
237
|
-
const status = cause?.response?.status;
|
|
238
|
-
switch (status) {
|
|
239
|
-
case 400:
|
|
240
|
-
case 404:
|
|
241
|
-
// KAS does not yet implement v2, maybe
|
|
242
|
-
break;
|
|
243
|
-
case 401:
|
|
244
|
-
throw new UnauthenticatedError(`[${v2Url}] requires auth`, cause);
|
|
245
|
-
case 403:
|
|
246
|
-
throw new PermissionDeniedError(`[${v2Url}] permission denied`, cause);
|
|
247
|
-
default:
|
|
248
|
-
if (status && status >= 400 && status < 500) {
|
|
249
|
-
throw new ConfigurationError(
|
|
250
|
-
`[${v2Url}] request error [${status}] [${cause.name}] [${cause.message}]`,
|
|
251
|
-
cause
|
|
252
|
-
);
|
|
253
|
-
}
|
|
254
|
-
throw new NetworkError(
|
|
255
|
-
`[${v2Url}] error [${status}] [${cause.name}] [${cause.message}]`,
|
|
256
|
-
cause
|
|
257
|
-
);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
// Retry with v1 params
|
|
261
|
-
const v1Url = `${kas}/kas_public_key`;
|
|
262
|
-
try {
|
|
263
|
-
const response: { data: string | KasPublicKeyInfo } = await axios.get(v1Url, {
|
|
264
|
-
params,
|
|
265
|
-
});
|
|
266
|
-
const publicKey =
|
|
267
|
-
typeof response.data === 'string'
|
|
268
|
-
? await extractPemFromKeyString(response.data)
|
|
269
|
-
: response.data.publicKey;
|
|
270
|
-
// future proof: allow v2 response even if not specified.
|
|
271
|
-
return {
|
|
272
|
-
publicKey,
|
|
273
|
-
key: pemToCryptoPublicKey(publicKey),
|
|
274
|
-
...infoStatic,
|
|
275
|
-
...(typeof response.data !== 'string' && response.data.kid && { kid: response.data.kid }),
|
|
276
|
-
};
|
|
277
|
-
} catch (cause) {
|
|
278
|
-
const status = cause?.response?.status;
|
|
279
|
-
switch (status) {
|
|
280
|
-
case 401:
|
|
281
|
-
throw new UnauthenticatedError(`[${v1Url}] requires auth`, cause);
|
|
282
|
-
case 403:
|
|
283
|
-
throw new PermissionDeniedError(`[${v1Url}] permission denied`, cause);
|
|
284
|
-
default:
|
|
285
|
-
if (status && status >= 400 && status < 500) {
|
|
286
|
-
throw new ConfigurationError(
|
|
287
|
-
`[${v2Url}] request error [${status}] [${cause.name}] [${cause.message}]`,
|
|
288
|
-
cause
|
|
289
|
-
);
|
|
290
|
-
}
|
|
291
|
-
throw new NetworkError(
|
|
292
|
-
`[${v1Url}] error [${status}] [${cause.name}] [${cause.message}]`,
|
|
293
|
-
cause
|
|
294
|
-
);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
203
|
+
return fetchKasPubKeyV2(kas, algorithm || 'rsa:2048');
|
|
297
204
|
}
|
|
298
|
-
/**
|
|
299
|
-
*
|
|
300
|
-
* @param payload The TDF content to encode in HTML
|
|
301
|
-
* @param manifest A copy of the manifest
|
|
302
|
-
* @param transferUrl reader web-service start page
|
|
303
|
-
* @return utf-8 encoded HTML data
|
|
304
|
-
*/
|
|
305
|
-
export function wrapHtml(
|
|
306
|
-
payload: Uint8Array,
|
|
307
|
-
manifest: Manifest | string,
|
|
308
|
-
transferUrl: string
|
|
309
|
-
): Uint8Array {
|
|
310
|
-
const { origin } = new URL(transferUrl);
|
|
311
|
-
const exportManifest: string = typeof manifest === 'string' ? manifest : JSON.stringify(manifest);
|
|
312
|
-
|
|
313
|
-
const fullHtmlString = htmlWrapperTemplate({
|
|
314
|
-
transferUrl,
|
|
315
|
-
transferBaseUrl: origin,
|
|
316
|
-
manifest: base64.encode(exportManifest),
|
|
317
|
-
payload: buffToString(payload, 'base64'),
|
|
318
|
-
});
|
|
319
205
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
let html;
|
|
325
|
-
if (htmlPayload instanceof ArrayBuffer || ArrayBuffer.isView(htmlPayload)) {
|
|
326
|
-
html = new TextDecoder().decode(htmlPayload);
|
|
327
|
-
} else {
|
|
328
|
-
html = htmlPayload.toString();
|
|
329
|
-
}
|
|
330
|
-
const payloadRe = /<input id=['"]?data-input['"]?[^>]*?value=['"]?([a-zA-Z0-9+/=]+)['"]?/;
|
|
331
|
-
const reResult = payloadRe.exec(html);
|
|
332
|
-
if (reResult === null) {
|
|
333
|
-
throw new InvalidFileError('Payload is missing');
|
|
334
|
-
}
|
|
335
|
-
const base64Payload = reResult[1];
|
|
336
|
-
try {
|
|
337
|
-
return base64ToBuffer(base64Payload);
|
|
338
|
-
} catch (e) {
|
|
339
|
-
throw new InvalidFileError('There was a problem extracting the TDF3 payload', e);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
export async function extractPemFromKeyString(keyString: string): Promise<string> {
|
|
206
|
+
export async function extractPemFromKeyString(
|
|
207
|
+
keyString: string,
|
|
208
|
+
alg: KasPublicKeyAlgorithm
|
|
209
|
+
): Promise<string> {
|
|
344
210
|
let pem: string = keyString;
|
|
345
211
|
|
|
346
212
|
// Skip the public key extraction if we find that the KAS url provides a
|
|
347
213
|
// PEM-encoded key instead of certificate
|
|
348
214
|
if (keyString.includes('CERTIFICATE')) {
|
|
349
|
-
const
|
|
215
|
+
const a = publicKeyAlgorithmToJwa(alg);
|
|
216
|
+
const cert = await importX509(keyString, a, { extractable: true });
|
|
350
217
|
pem = await exportSPKI(cert);
|
|
351
218
|
}
|
|
352
219
|
|
|
@@ -360,7 +227,6 @@ export async function extractPemFromKeyString(keyString: string): Promise<string
|
|
|
360
227
|
* is missing it throws an error.
|
|
361
228
|
* @param {Object} options
|
|
362
229
|
* @param {String} options.type - enum representing how the object key is treated
|
|
363
|
-
* @param {String} options.attributeUrl - URL of the attribute to use for pubKey and kasUrl. Omit to use default.
|
|
364
230
|
* @param {String} options.url - directly set the KAS URL
|
|
365
231
|
* @param {String} options.publicKey - directly set the (KAS) public key
|
|
366
232
|
* @param {String?} options.kid - Key identifier of KAS public key
|
|
@@ -368,56 +234,40 @@ export async function extractPemFromKeyString(keyString: string): Promise<string
|
|
|
368
234
|
* @return {KeyAccess}- the key access object loaded
|
|
369
235
|
*/
|
|
370
236
|
export async function buildKeyAccess({
|
|
371
|
-
attributeSet,
|
|
372
237
|
type,
|
|
373
238
|
url,
|
|
374
239
|
publicKey,
|
|
375
240
|
kid,
|
|
376
|
-
attributeUrl,
|
|
377
241
|
metadata,
|
|
378
242
|
sid = '',
|
|
243
|
+
alg = 'rsa:2048',
|
|
379
244
|
}: BuildKeyAccess): Promise<KeyAccess> {
|
|
380
|
-
/** Internal function to keep it DRY */
|
|
381
|
-
function createKeyAccess(
|
|
382
|
-
type: KeyAccessType,
|
|
383
|
-
kasUrl: string,
|
|
384
|
-
kasKeyIdentifier: string | undefined,
|
|
385
|
-
pubKey: string,
|
|
386
|
-
metadata?: Metadata
|
|
387
|
-
) {
|
|
388
|
-
switch (type) {
|
|
389
|
-
case 'wrapped':
|
|
390
|
-
return new KeyAccessWrapped(kasUrl, kasKeyIdentifier, pubKey, metadata, sid);
|
|
391
|
-
case 'remote':
|
|
392
|
-
return new KeyAccessRemote(kasUrl, kasKeyIdentifier, pubKey, metadata, sid);
|
|
393
|
-
default:
|
|
394
|
-
throw new ConfigurationError(`buildKeyAccess: Key access type ${type} is unknown`);
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
// If an attributeUrl is provided try to load with that first.
|
|
399
|
-
if (attributeUrl && attributeSet) {
|
|
400
|
-
const attr = attributeSet.get(attributeUrl);
|
|
401
|
-
if (attr && attr.kasUrl && attr.pubKey) {
|
|
402
|
-
return createKeyAccess(type, attr.kasUrl, attr.kid, attr.pubKey, metadata);
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
|
|
406
245
|
// if url and pulicKey are specified load the key access object with them
|
|
407
|
-
if (url && publicKey) {
|
|
408
|
-
|
|
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');
|
|
409
252
|
}
|
|
410
253
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
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`);
|
|
418
270
|
}
|
|
419
|
-
// All failed. Raise an error.
|
|
420
|
-
throw new ConfigurationError('TDF.buildKeyAccess: No source for kasUrl or pubKey');
|
|
421
271
|
}
|
|
422
272
|
|
|
423
273
|
export function validatePolicyObject(policy: Policy): void {
|
|
@@ -446,7 +296,6 @@ async function _generateManifest(
|
|
|
446
296
|
url: '0.payload',
|
|
447
297
|
protocol: 'zip',
|
|
448
298
|
isEncrypted: true,
|
|
449
|
-
schemaVersion: '3.0.0',
|
|
450
299
|
...(mimeType && { mimeType }),
|
|
451
300
|
};
|
|
452
301
|
|
|
@@ -457,137 +306,39 @@ async function _generateManifest(
|
|
|
457
306
|
// generate the manifest first, then insert integrity information into it
|
|
458
307
|
encryptionInformation: encryptionInformationStr,
|
|
459
308
|
assertions: assertions,
|
|
309
|
+
schemaVersion: tdfSpecVersion,
|
|
460
310
|
};
|
|
461
311
|
}
|
|
462
312
|
|
|
463
313
|
async function getSignature(
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
algorithmType: IntegrityAlgorithm
|
|
467
|
-
|
|
468
|
-
) {
|
|
314
|
+
unwrappedKey: Uint8Array,
|
|
315
|
+
content: Uint8Array,
|
|
316
|
+
algorithmType: IntegrityAlgorithm
|
|
317
|
+
): Promise<Uint8Array> {
|
|
469
318
|
switch (algorithmType.toUpperCase()) {
|
|
470
319
|
case 'GMAC':
|
|
471
320
|
// use the auth tag baked into the encrypted payload
|
|
472
|
-
return
|
|
473
|
-
case 'HS256':
|
|
321
|
+
return content.slice(-16);
|
|
322
|
+
case 'HS256': {
|
|
474
323
|
// simple hmac is the default
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
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']
|
|
478
333
|
);
|
|
334
|
+
const signature = await crypto.subtle.sign('HMAC', cryptoKey, content);
|
|
335
|
+
return new Uint8Array(signature);
|
|
336
|
+
}
|
|
479
337
|
default:
|
|
480
338
|
throw new ConfigurationError(`Unsupported signature alg [${algorithmType}]`);
|
|
481
339
|
}
|
|
482
340
|
}
|
|
483
341
|
|
|
484
|
-
function buildRequest(method: HttpMethod, url: string, body?: unknown): HttpRequest {
|
|
485
|
-
return {
|
|
486
|
-
headers: {},
|
|
487
|
-
method: method,
|
|
488
|
-
url: url,
|
|
489
|
-
body,
|
|
490
|
-
};
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
export async function upsert({
|
|
494
|
-
allowedKases,
|
|
495
|
-
allowList,
|
|
496
|
-
authProvider,
|
|
497
|
-
entity,
|
|
498
|
-
privateKey,
|
|
499
|
-
unsavedManifest,
|
|
500
|
-
ignoreType,
|
|
501
|
-
}: UpsertConfiguration): Promise<UpsertResponse> {
|
|
502
|
-
const allowed = (() => {
|
|
503
|
-
if (allowList) {
|
|
504
|
-
return allowList;
|
|
505
|
-
}
|
|
506
|
-
if (!allowedKases) {
|
|
507
|
-
throw new ConfigurationError('Upsert cannot be done without allowlist');
|
|
508
|
-
}
|
|
509
|
-
return new OriginAllowList(allowedKases);
|
|
510
|
-
})();
|
|
511
|
-
const { keyAccess, policy } = unsavedManifest.encryptionInformation;
|
|
512
|
-
const isAppIdProvider = authProvider && isAppIdProviderCheck(authProvider);
|
|
513
|
-
if (authProvider === undefined) {
|
|
514
|
-
throw new ConfigurationError('Upsert cannot be done without auth provider');
|
|
515
|
-
}
|
|
516
|
-
return Promise.all(
|
|
517
|
-
keyAccess.map(async (keyAccessObject) => {
|
|
518
|
-
// We only care about remote key access objects for the policy sync portion
|
|
519
|
-
const isRemote = isRemoteKeyAccess(keyAccessObject);
|
|
520
|
-
if (!ignoreType && !isRemote) {
|
|
521
|
-
return;
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
if (!allowed.allows(keyAccessObject.url)) {
|
|
525
|
-
throw new UnsafeUrlError(`Unexpected KAS url: [${keyAccessObject.url}]`);
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
const url = `${keyAccessObject.url}/${isAppIdProvider ? '' : 'v2/'}upsert`;
|
|
529
|
-
|
|
530
|
-
//TODO I dont' think we need a body at all for KAS requests
|
|
531
|
-
// Do we need ANY of this if it's already embedded in the EO in the Bearer OIDC token?
|
|
532
|
-
const body: Record<string, unknown> = {
|
|
533
|
-
keyAccess: keyAccessObject,
|
|
534
|
-
policy: unsavedManifest.encryptionInformation.policy,
|
|
535
|
-
entity: isAppIdProviderCheck(authProvider) ? entity : undefined,
|
|
536
|
-
authToken: undefined,
|
|
537
|
-
clientPayloadSignature: undefined,
|
|
538
|
-
};
|
|
539
|
-
|
|
540
|
-
if (isAppIdProviderCheck(authProvider)) {
|
|
541
|
-
body.authToken = await reqSignature({}, privateKey);
|
|
542
|
-
} else {
|
|
543
|
-
body.clientPayloadSignature = await reqSignature(body, privateKey);
|
|
544
|
-
}
|
|
545
|
-
const httpReq = await authProvider.withCreds(buildRequest('POST', url, body));
|
|
546
|
-
|
|
547
|
-
try {
|
|
548
|
-
const response = await axios.post(httpReq.url, httpReq.body, {
|
|
549
|
-
headers: httpReq.headers,
|
|
550
|
-
});
|
|
551
|
-
|
|
552
|
-
// Remove additional properties which were needed to sync, but not that we want to save to
|
|
553
|
-
// the manifest
|
|
554
|
-
delete keyAccessObject.wrappedKey;
|
|
555
|
-
delete keyAccessObject.encryptedMetadata;
|
|
556
|
-
delete keyAccessObject.policyBinding;
|
|
557
|
-
|
|
558
|
-
if (isRemote) {
|
|
559
|
-
// Decode the policy and extract only the required info to save -- the uuid
|
|
560
|
-
const decodedPolicy = JSON.parse(base64.decode(policy));
|
|
561
|
-
unsavedManifest.encryptionInformation.policy = base64.encode(
|
|
562
|
-
JSON.stringify({ uuid: decodedPolicy.uuid })
|
|
563
|
-
);
|
|
564
|
-
}
|
|
565
|
-
return response.data;
|
|
566
|
-
} catch (e) {
|
|
567
|
-
if (e.response) {
|
|
568
|
-
if (e.response.status >= 500) {
|
|
569
|
-
throw new ServiceError('upsert failure', e);
|
|
570
|
-
} else if (e.response.status === 403) {
|
|
571
|
-
throw new PermissionDeniedError('upsert failure', e);
|
|
572
|
-
} else if (e.response.status === 401) {
|
|
573
|
-
throw new UnauthenticatedError('upsert auth failure', e);
|
|
574
|
-
} else if (e.response.status === 400) {
|
|
575
|
-
throw new ConfigurationError('upsert bad request; likely a configuration error', e);
|
|
576
|
-
} else {
|
|
577
|
-
throw new NetworkError('upsert server error', e);
|
|
578
|
-
}
|
|
579
|
-
} else if (e.request) {
|
|
580
|
-
throw new NetworkError('upsert request failure', e);
|
|
581
|
-
}
|
|
582
|
-
throw new TdfError(
|
|
583
|
-
`Unable to perform upsert operation on the KAS: [${e.name}: ${e.message}], response: [${e?.response?.body}]`,
|
|
584
|
-
e
|
|
585
|
-
);
|
|
586
|
-
}
|
|
587
|
-
})
|
|
588
|
-
);
|
|
589
|
-
}
|
|
590
|
-
|
|
591
342
|
export async function writeStream(cfg: EncryptConfiguration): Promise<DecoratedReadableStream> {
|
|
592
343
|
if (!cfg.authProvider) {
|
|
593
344
|
throw new ConfigurationError('No authorization middleware defined');
|
|
@@ -616,7 +367,7 @@ export async function writeStream(cfg: EncryptConfiguration): Promise<DecoratedR
|
|
|
616
367
|
let bytesProcessed = 0;
|
|
617
368
|
let crcCounter = 0;
|
|
618
369
|
let fileByteCount = 0;
|
|
619
|
-
|
|
370
|
+
const segmentHashList: Uint8Array[] = [];
|
|
620
371
|
|
|
621
372
|
const zipWriter = new ZipWriter();
|
|
622
373
|
const manifest = await _generateManifest(
|
|
@@ -630,17 +381,6 @@ export async function writeStream(cfg: EncryptConfiguration): Promise<DecoratedR
|
|
|
630
381
|
// Set in encrypt; should never be reached.
|
|
631
382
|
throw new ConfigurationError('internal: please use "loadTDFStream" first to load a manifest.');
|
|
632
383
|
}
|
|
633
|
-
const pkKeyLike = cfg.dpopKeys.privateKey;
|
|
634
|
-
|
|
635
|
-
// For all remote key access objects, sync its policy
|
|
636
|
-
const upsertResponse = await upsert({
|
|
637
|
-
allowedKases: cfg.allowList ? undefined : cfg.allowedKases,
|
|
638
|
-
allowList: cfg.allowList,
|
|
639
|
-
authProvider: cfg.authProvider,
|
|
640
|
-
entity: cfg.entity,
|
|
641
|
-
privateKey: pkKeyLike,
|
|
642
|
-
unsavedManifest: manifest,
|
|
643
|
-
});
|
|
644
384
|
|
|
645
385
|
// determine default segment size by writing empty buffer
|
|
646
386
|
const { segmentSizeDefault } = cfg;
|
|
@@ -720,14 +460,16 @@ export async function writeStream(cfg: EncryptConfiguration): Promise<DecoratedR
|
|
|
720
460
|
fileByteCount = 0;
|
|
721
461
|
|
|
722
462
|
// hash the concat of all hashes
|
|
723
|
-
const
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
cfg.
|
|
727
|
-
|
|
463
|
+
const aggregateHash = await concatenateUint8Array(segmentHashList);
|
|
464
|
+
|
|
465
|
+
const payloadSig = await getSignature(
|
|
466
|
+
new Uint8Array(cfg.keyForEncryption.unwrappedKeyBinary.asArrayBuffer()),
|
|
467
|
+
aggregateHash,
|
|
468
|
+
cfg.integrityAlgorithm
|
|
728
469
|
);
|
|
729
|
-
|
|
730
|
-
|
|
470
|
+
|
|
471
|
+
const rootSig = base64.encodeArrayBuffer(payloadSig);
|
|
472
|
+
manifest.encryptionInformation.integrityInformation.rootSignature.sig = rootSig;
|
|
731
473
|
manifest.encryptionInformation.integrityInformation.rootSignature.alg =
|
|
732
474
|
cfg.integrityAlgorithm;
|
|
733
475
|
|
|
@@ -803,12 +545,6 @@ export async function writeStream(cfg: EncryptConfiguration): Promise<DecoratedR
|
|
|
803
545
|
const plaintextStream = new DecoratedReadableStream(underlingSource);
|
|
804
546
|
plaintextStream.manifest = manifest;
|
|
805
547
|
|
|
806
|
-
if (upsertResponse) {
|
|
807
|
-
plaintextStream.upsertResponse = upsertResponse;
|
|
808
|
-
plaintextStream.tdfSize = totalByteCount;
|
|
809
|
-
plaintextStream.algorithm = manifest.encryptionInformation.method.algorithm;
|
|
810
|
-
}
|
|
811
|
-
|
|
812
548
|
return plaintextStream;
|
|
813
549
|
|
|
814
550
|
// nested helper fn's
|
|
@@ -839,18 +575,16 @@ export async function writeStream(cfg: EncryptConfiguration): Promise<DecoratedR
|
|
|
839
575
|
cfg.keyForEncryption.unwrappedKeyBinary
|
|
840
576
|
);
|
|
841
577
|
const payloadBuffer = new Uint8Array(encryptedResult.payload.asByteArray());
|
|
842
|
-
const
|
|
843
|
-
cfg.keyForEncryption.unwrappedKeyBinary,
|
|
844
|
-
encryptedResult.payload,
|
|
845
|
-
cfg.segmentIntegrityAlgorithm
|
|
846
|
-
cfg.cryptoService
|
|
578
|
+
const payloadSig = await getSignature(
|
|
579
|
+
new Uint8Array(cfg.keyForEncryption.unwrappedKeyBinary.asArrayBuffer()),
|
|
580
|
+
new Uint8Array(encryptedResult.payload.asArrayBuffer()),
|
|
581
|
+
cfg.segmentIntegrityAlgorithm
|
|
847
582
|
);
|
|
848
583
|
|
|
849
|
-
|
|
850
|
-
aggregateHash += payloadSigStr;
|
|
584
|
+
segmentHashList.push(new Uint8Array(payloadSig));
|
|
851
585
|
|
|
852
586
|
segmentInfos.push({
|
|
853
|
-
hash: base64.
|
|
587
|
+
hash: base64.encodeArrayBuffer(payloadSig),
|
|
854
588
|
segmentSize: chunk.length === segmentSizeDefault ? undefined : chunk.length,
|
|
855
589
|
encryptedSegmentSize:
|
|
856
590
|
payloadBuffer.length === encryptedSegmentSizeDefault ? undefined : payloadBuffer.length,
|
|
@@ -917,31 +651,39 @@ async function unwrapKey({
|
|
|
917
651
|
authProvider,
|
|
918
652
|
dpopKeys,
|
|
919
653
|
concurrencyLimit,
|
|
920
|
-
entity,
|
|
921
654
|
cryptoService,
|
|
655
|
+
wrappingKeyAlgorithm,
|
|
922
656
|
}: {
|
|
923
657
|
manifest: Manifest;
|
|
924
658
|
allowedKases: OriginAllowList;
|
|
925
|
-
authProvider: AuthProvider
|
|
659
|
+
authProvider: AuthProvider;
|
|
926
660
|
concurrencyLimit?: number;
|
|
927
661
|
dpopKeys: CryptoKeyPair;
|
|
928
|
-
entity: EntityObject | undefined;
|
|
929
662
|
cryptoService: CryptoService;
|
|
663
|
+
wrappingKeyAlgorithm?: KasPublicKeyAlgorithm;
|
|
930
664
|
}) {
|
|
931
665
|
if (authProvider === undefined) {
|
|
932
666
|
throw new ConfigurationError(
|
|
933
|
-
'
|
|
667
|
+
'rewrap requires auth provider; must be configured in client constructor'
|
|
934
668
|
);
|
|
935
669
|
}
|
|
936
670
|
const { keyAccess } = manifest.encryptionInformation;
|
|
937
671
|
const splitPotentials = splitLookupTableFactory(keyAccess, allowedKases);
|
|
938
|
-
const isAppIdProvider = authProvider && isAppIdProviderCheck(authProvider);
|
|
939
672
|
|
|
940
673
|
async function tryKasRewrap(keySplitInfo: KeyAccessObject): Promise<RewrapResponseData> {
|
|
941
|
-
const url = `${keySplitInfo.url}
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
)
|
|
674
|
+
const url = `${keySplitInfo.url}/v2/rewrap`;
|
|
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
|
+
|
|
945
687
|
const clientPublicKey = ephemeralEncryptionKeys.publicKey;
|
|
946
688
|
|
|
947
689
|
const requestBodyStr = JSON.stringify({
|
|
@@ -952,33 +694,33 @@ async function unwrapKey({
|
|
|
952
694
|
});
|
|
953
695
|
|
|
954
696
|
const jwtPayload = { requestBody: requestBodyStr };
|
|
955
|
-
const signedRequestToken = await reqSignature(
|
|
956
|
-
|
|
957
|
-
|
|
697
|
+
const signedRequestToken = await reqSignature(jwtPayload, dpopKeys.privateKey);
|
|
698
|
+
|
|
699
|
+
const { entityWrappedKey, metadata, sessionPublicKey } = await fetchWrappedKey(
|
|
700
|
+
url,
|
|
701
|
+
{ signedRequestToken },
|
|
702
|
+
authProvider,
|
|
703
|
+
'0.0.1'
|
|
958
704
|
);
|
|
959
705
|
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
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,
|
|
974
722
|
};
|
|
975
723
|
}
|
|
976
|
-
|
|
977
|
-
const httpReq = await authProvider.withCreds(buildRequest('POST', url, requestBody));
|
|
978
|
-
const {
|
|
979
|
-
data: { entityWrappedKey, metadata },
|
|
980
|
-
} = await axios.post(httpReq.url, httpReq.body, { headers: httpReq.headers });
|
|
981
|
-
|
|
982
724
|
const key = Binary.fromString(base64.decode(entityWrappedKey));
|
|
983
725
|
const decryptedKeyBinary = await cryptoService.decryptWithPrivateKey(
|
|
984
726
|
key,
|
|
@@ -1010,7 +752,7 @@ async function unwrapKey({
|
|
|
1010
752
|
try {
|
|
1011
753
|
return await tryKasRewrap(keySplitInfo);
|
|
1012
754
|
} catch (e) {
|
|
1013
|
-
throw handleRewrapError(e as Error
|
|
755
|
+
throw handleRewrapError(e as Error);
|
|
1014
756
|
}
|
|
1015
757
|
};
|
|
1016
758
|
}
|
|
@@ -1035,31 +777,11 @@ async function unwrapKey({
|
|
|
1035
777
|
}
|
|
1036
778
|
}
|
|
1037
779
|
|
|
1038
|
-
function handleRewrapError(error: Error
|
|
1039
|
-
if (
|
|
1040
|
-
|
|
1041
|
-
return new ServiceError('rewrap failure', error);
|
|
1042
|
-
} else if (error.response?.status === 403) {
|
|
1043
|
-
return new PermissionDeniedError('rewrap failure', error);
|
|
1044
|
-
} else if (error.response?.status === 401) {
|
|
1045
|
-
return new UnauthenticatedError('rewrap auth failure', error);
|
|
1046
|
-
} else if (error.response?.status === 400) {
|
|
1047
|
-
return new InvalidFileError(
|
|
1048
|
-
'rewrap bad request; could indicate an invalid policy binding or a configuration error',
|
|
1049
|
-
error
|
|
1050
|
-
);
|
|
1051
|
-
} else {
|
|
1052
|
-
return new NetworkError('rewrap server error', error);
|
|
1053
|
-
}
|
|
1054
|
-
} else {
|
|
1055
|
-
if (error.name === 'InvalidAccessError' || error.name === 'OperationError') {
|
|
1056
|
-
return new DecryptError('unable to unwrap key from kas', error);
|
|
1057
|
-
}
|
|
1058
|
-
return new InvalidFileError(
|
|
1059
|
-
`Unable to decrypt the response from KAS: [${error.name}: ${error.message}]`,
|
|
1060
|
-
error
|
|
1061
|
-
);
|
|
780
|
+
function handleRewrapError(error: Error) {
|
|
781
|
+
if (error.name === 'InvalidAccessError' || error.name === 'OperationError') {
|
|
782
|
+
return new DecryptError('unable to unwrap key from kas', error);
|
|
1062
783
|
}
|
|
784
|
+
return error;
|
|
1063
785
|
}
|
|
1064
786
|
|
|
1065
787
|
async function decryptChunk(
|
|
@@ -1068,17 +790,22 @@ async function decryptChunk(
|
|
|
1068
790
|
hash: string,
|
|
1069
791
|
cipher: SymmetricCipher,
|
|
1070
792
|
segmentIntegrityAlgorithm: IntegrityAlgorithm,
|
|
1071
|
-
|
|
793
|
+
isLegacyTDF: boolean
|
|
1072
794
|
): Promise<DecryptResult> {
|
|
1073
795
|
if (segmentIntegrityAlgorithm !== 'GMAC' && segmentIntegrityAlgorithm !== 'HS256') {
|
|
796
|
+
throw new UnsupportedError(`Unsupported integrity alg [${segmentIntegrityAlgorithm}]`);
|
|
1074
797
|
}
|
|
1075
|
-
const
|
|
1076
|
-
reconstructedKeyBinary,
|
|
1077
|
-
|
|
1078
|
-
segmentIntegrityAlgorithm
|
|
1079
|
-
cryptoService
|
|
798
|
+
const segmentSig = await getSignature(
|
|
799
|
+
new Uint8Array(reconstructedKeyBinary.asArrayBuffer()),
|
|
800
|
+
encryptedChunk,
|
|
801
|
+
segmentIntegrityAlgorithm
|
|
1080
802
|
);
|
|
1081
|
-
|
|
803
|
+
|
|
804
|
+
const segmentHash = isLegacyTDF
|
|
805
|
+
? base64.encode(hex.encodeArrayBuffer(segmentSig))
|
|
806
|
+
: base64.encodeArrayBuffer(segmentSig);
|
|
807
|
+
|
|
808
|
+
if (hash !== segmentHash) {
|
|
1082
809
|
throw new IntegrityError('Failed integrity check on segment hash');
|
|
1083
810
|
}
|
|
1084
811
|
return await cipher.decrypt(encryptedChunk, reconstructedKeyBinary);
|
|
@@ -1091,7 +818,8 @@ async function updateChunkQueue(
|
|
|
1091
818
|
reconstructedKeyBinary: Binary,
|
|
1092
819
|
cipher: SymmetricCipher,
|
|
1093
820
|
segmentIntegrityAlgorithm: IntegrityAlgorithm,
|
|
1094
|
-
cryptoService: CryptoService
|
|
821
|
+
cryptoService: CryptoService,
|
|
822
|
+
isLegacyTDF: boolean
|
|
1095
823
|
) {
|
|
1096
824
|
const chunksInOneDownload = 500;
|
|
1097
825
|
let requests = [];
|
|
@@ -1132,6 +860,7 @@ async function updateChunkQueue(
|
|
|
1132
860
|
slice,
|
|
1133
861
|
cipher,
|
|
1134
862
|
segmentIntegrityAlgorithm,
|
|
863
|
+
isLegacyTDF,
|
|
1135
864
|
});
|
|
1136
865
|
}
|
|
1137
866
|
})()
|
|
@@ -1144,8 +873,8 @@ export async function sliceAndDecrypt({
|
|
|
1144
873
|
reconstructedKeyBinary,
|
|
1145
874
|
slice,
|
|
1146
875
|
cipher,
|
|
1147
|
-
cryptoService,
|
|
1148
876
|
segmentIntegrityAlgorithm,
|
|
877
|
+
isLegacyTDF,
|
|
1149
878
|
}: {
|
|
1150
879
|
buffer: Uint8Array;
|
|
1151
880
|
reconstructedKeyBinary: Binary;
|
|
@@ -1153,9 +882,10 @@ export async function sliceAndDecrypt({
|
|
|
1153
882
|
cipher: SymmetricCipher;
|
|
1154
883
|
cryptoService: CryptoService;
|
|
1155
884
|
segmentIntegrityAlgorithm: IntegrityAlgorithm;
|
|
885
|
+
isLegacyTDF: boolean;
|
|
1156
886
|
}) {
|
|
1157
887
|
for (const index in slice) {
|
|
1158
|
-
const { encryptedOffset, encryptedSegmentSize,
|
|
888
|
+
const { encryptedOffset, encryptedSegmentSize, plainSegmentSize } = slice[index];
|
|
1159
889
|
|
|
1160
890
|
const offset =
|
|
1161
891
|
slice[0].encryptedOffset === 0 ? encryptedOffset : encryptedOffset % slice[0].encryptedOffset;
|
|
@@ -1163,6 +893,10 @@ export async function sliceAndDecrypt({
|
|
|
1163
893
|
buffer.slice(offset, offset + (encryptedSegmentSize as number))
|
|
1164
894
|
);
|
|
1165
895
|
|
|
896
|
+
if (encryptedChunk.length !== encryptedSegmentSize) {
|
|
897
|
+
throw new DecryptError('Failed to fetch entire segment');
|
|
898
|
+
}
|
|
899
|
+
|
|
1166
900
|
try {
|
|
1167
901
|
const result = await decryptChunk(
|
|
1168
902
|
encryptedChunk,
|
|
@@ -1170,18 +904,16 @@ export async function sliceAndDecrypt({
|
|
|
1170
904
|
slice[index]['hash'],
|
|
1171
905
|
cipher,
|
|
1172
906
|
segmentIntegrityAlgorithm,
|
|
1173
|
-
|
|
907
|
+
isLegacyTDF
|
|
1174
908
|
);
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
909
|
+
if (plainSegmentSize && result.payload.length() !== plainSegmentSize) {
|
|
910
|
+
throw new DecryptError(
|
|
911
|
+
`incorrect segment size: found [${result.payload.length()}], expected [${plainSegmentSize}]`
|
|
912
|
+
);
|
|
1178
913
|
}
|
|
914
|
+
slice[index].decryptedChunk.set(result);
|
|
1179
915
|
} catch (e) {
|
|
1180
|
-
|
|
1181
|
-
_reject(e);
|
|
1182
|
-
} else {
|
|
1183
|
-
throw e;
|
|
1184
|
-
}
|
|
916
|
+
slice[index].decryptedChunk.reject(e);
|
|
1185
917
|
}
|
|
1186
918
|
}
|
|
1187
919
|
}
|
|
@@ -1204,6 +936,7 @@ export async function readStream(cfg: DecryptConfiguration) {
|
|
|
1204
936
|
encryptedSegmentSizeDefault: defaultSegmentSize,
|
|
1205
937
|
rootSignature,
|
|
1206
938
|
segmentHashAlg,
|
|
939
|
+
segmentSizeDefault,
|
|
1207
940
|
segments,
|
|
1208
941
|
} = manifest.encryptionInformation.integrityInformation;
|
|
1209
942
|
const { metadata, reconstructedKeyBinary } = await unwrapKey({
|
|
@@ -1211,32 +944,34 @@ export async function readStream(cfg: DecryptConfiguration) {
|
|
|
1211
944
|
authProvider: cfg.authProvider,
|
|
1212
945
|
allowedKases: allowList,
|
|
1213
946
|
dpopKeys: cfg.dpopKeys,
|
|
1214
|
-
entity: cfg.entity,
|
|
1215
947
|
cryptoService: cfg.cryptoService,
|
|
1216
948
|
});
|
|
1217
949
|
// async function unwrapKey(manifest: Manifest, allowedKases: string[], authProvider: AuthProvider | AppIdAuthProvider, publicKey: string, privateKey: string, entity: EntityObject) {
|
|
1218
950
|
const keyForDecryption = await cfg.keyMiddleware(reconstructedKeyBinary);
|
|
1219
951
|
const encryptedSegmentSizeDefault = defaultSegmentSize || DEFAULT_SEGMENT_SIZE;
|
|
1220
952
|
|
|
1221
|
-
// check the
|
|
1222
|
-
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
|
+
|
|
1223
965
|
const integrityAlgorithm = rootSignature.alg;
|
|
1224
966
|
if (integrityAlgorithm !== 'GMAC' && integrityAlgorithm !== 'HS256') {
|
|
1225
967
|
throw new UnsupportedError(`Unsupported integrity alg [${integrityAlgorithm}]`);
|
|
1226
968
|
}
|
|
1227
|
-
const payloadSigStr = await getSignature(
|
|
1228
|
-
keyForDecryption,
|
|
1229
|
-
Binary.fromString(aggregateHash),
|
|
1230
|
-
integrityAlgorithm,
|
|
1231
|
-
cfg.cryptoService
|
|
1232
|
-
);
|
|
1233
969
|
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
}
|
|
970
|
+
const payloadSig = await getSignature(
|
|
971
|
+
new Uint8Array(keyForDecryption.asArrayBuffer()),
|
|
972
|
+
aggregateHash,
|
|
973
|
+
integrityAlgorithm
|
|
974
|
+
);
|
|
1240
975
|
|
|
1241
976
|
if (!cfg.noVerifyAssertions) {
|
|
1242
977
|
for (const assertion of manifest.assertions || []) {
|
|
@@ -1252,31 +987,40 @@ export async function readStream(cfg: DecryptConfiguration) {
|
|
|
1252
987
|
assertionKey = foundKey;
|
|
1253
988
|
}
|
|
1254
989
|
}
|
|
1255
|
-
await assertions.verify(assertion, aggregateHash, assertionKey);
|
|
990
|
+
await assertions.verify(assertion, aggregateHash, assertionKey, isLegacyTDF);
|
|
1256
991
|
}
|
|
1257
992
|
}
|
|
1258
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
|
+
|
|
1259
1002
|
let mapOfRequestsOffset = 0;
|
|
1260
1003
|
const chunkMap = new Map(
|
|
1261
|
-
segments.map(
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
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
|
+
)
|
|
1280
1024
|
);
|
|
1281
1025
|
|
|
1282
1026
|
const cipher = new AesGcmCipher(cfg.cryptoService);
|
|
@@ -1293,7 +1037,8 @@ export async function readStream(cfg: DecryptConfiguration) {
|
|
|
1293
1037
|
keyForDecryption,
|
|
1294
1038
|
cipher,
|
|
1295
1039
|
segmentIntegrityAlg,
|
|
1296
|
-
cfg.cryptoService
|
|
1040
|
+
cfg.cryptoService,
|
|
1041
|
+
isLegacyTDF
|
|
1297
1042
|
);
|
|
1298
1043
|
|
|
1299
1044
|
let progress = 0;
|
|
@@ -1305,16 +1050,11 @@ export async function readStream(cfg: DecryptConfiguration) {
|
|
|
1305
1050
|
}
|
|
1306
1051
|
|
|
1307
1052
|
const [hash, chunk] = chunkMap.entries().next().value;
|
|
1308
|
-
|
|
1309
|
-
await chunk.promise;
|
|
1310
|
-
}
|
|
1311
|
-
const decryptedSegment = chunk.decryptedChunk;
|
|
1053
|
+
const decryptedSegment = await chunk.decryptedChunk;
|
|
1312
1054
|
|
|
1313
1055
|
controller.enqueue(new Uint8Array(decryptedSegment.payload.asByteArray()));
|
|
1314
1056
|
progress += chunk.encryptedSegmentSize;
|
|
1315
1057
|
cfg.progressHandler?.(progress);
|
|
1316
|
-
|
|
1317
|
-
chunk.decryptedChunk = null;
|
|
1318
1058
|
chunkMap.delete(hash);
|
|
1319
1059
|
},
|
|
1320
1060
|
...(cfg.fileStreamServiceWorker && { fileStreamServiceWorker: cfg.fileStreamServiceWorker }),
|
|
@@ -1323,8 +1063,12 @@ export async function readStream(cfg: DecryptConfiguration) {
|
|
|
1323
1063
|
const outputStream = new DecoratedReadableStream(underlyingSource);
|
|
1324
1064
|
|
|
1325
1065
|
outputStream.manifest = manifest;
|
|
1326
|
-
outputStream.emit('manifest', manifest);
|
|
1327
1066
|
outputStream.metadata = metadata;
|
|
1328
|
-
outputStream.emit('rewrap', metadata);
|
|
1329
1067
|
return outputStream;
|
|
1330
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
|
+
}
|