@icure/api 6.4.0 → 7.0.0-beta.1
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/icc-api/api/IccDeviceApi.d.ts +1 -1
- package/icc-api/api/IccDeviceApi.js.map +1 -1
- package/icc-api/api/IccDoctemplateApi.js.map +1 -1
- package/icc-api/api/IccDocumentTemplateApi.js.map +1 -1
- package/icc-api/api/IccHcpartyApi.d.ts +1 -1
- package/icc-api/api/IccHcpartyApi.js.map +1 -1
- package/icc-api/api/IccPatientApi.d.ts +1 -1
- package/icc-api/api/IccPatientApi.js.map +1 -1
- package/icc-api/model/MaintenanceTask.d.ts +6 -5
- package/icc-api/model/MaintenanceTask.js +5 -0
- package/icc-api/model/MaintenanceTask.js.map +1 -1
- package/icc-api/model/User.d.ts +23 -1
- package/icc-api/model/User.js +21 -0
- package/icc-api/model/User.js.map +1 -1
- package/icc-api/model/models.d.ts +14 -0
- package/icc-api/model/models.js.map +1 -1
- package/icc-x-api/crypto/AES.d.ts +2 -0
- package/icc-x-api/crypto/AES.js +12 -0
- package/icc-x-api/crypto/AES.js.map +1 -1
- package/icc-x-api/crypto/BaseExchangeKeysManager.d.ts +122 -0
- package/icc-x-api/crypto/BaseExchangeKeysManager.js +356 -0
- package/icc-x-api/crypto/BaseExchangeKeysManager.js.map +1 -0
- package/icc-x-api/crypto/ConfidentialEntities.d.ts +58 -0
- package/icc-x-api/crypto/ConfidentialEntities.js +99 -0
- package/icc-x-api/crypto/ConfidentialEntities.js.map +1 -0
- package/icc-x-api/crypto/CryptoPrimitives.d.ts +31 -0
- package/icc-x-api/crypto/CryptoPrimitives.js +55 -0
- package/icc-x-api/crypto/CryptoPrimitives.js.map +1 -0
- package/icc-x-api/crypto/CryptoStrategies.d.ts +82 -0
- package/icc-x-api/crypto/CryptoStrategies.js +3 -0
- package/icc-x-api/crypto/CryptoStrategies.js.map +1 -0
- package/icc-x-api/crypto/EntitiesEncryption.d.ts +254 -0
- package/icc-x-api/crypto/EntitiesEncryption.js +700 -0
- package/icc-x-api/crypto/EntitiesEncryption.js.map +1 -0
- package/icc-x-api/crypto/ExchangeKeysManager.d.ts +56 -0
- package/icc-x-api/crypto/ExchangeKeysManager.js +147 -0
- package/icc-x-api/crypto/ExchangeKeysManager.js.map +1 -0
- package/icc-x-api/crypto/KeyManager.d.ts +103 -0
- package/icc-x-api/crypto/KeyManager.js +288 -0
- package/icc-x-api/crypto/KeyManager.js.map +1 -0
- package/icc-x-api/crypto/KeyRecovery.d.ts +34 -0
- package/icc-x-api/crypto/KeyRecovery.js +253 -0
- package/icc-x-api/crypto/KeyRecovery.js.map +1 -0
- package/icc-x-api/crypto/LegacyCryptoStrategies.d.ts +28 -0
- package/icc-x-api/crypto/LegacyCryptoStrategies.js +22 -0
- package/icc-x-api/crypto/LegacyCryptoStrategies.js.map +1 -0
- package/icc-x-api/crypto/RSA.d.ts +19 -20
- package/icc-x-api/crypto/RSA.js +30 -1
- package/icc-x-api/crypto/RSA.js.map +1 -1
- package/icc-x-api/crypto/ShamirKeysManager.d.ts +43 -0
- package/icc-x-api/crypto/ShamirKeysManager.js +150 -0
- package/icc-x-api/crypto/ShamirKeysManager.js.map +1 -0
- package/icc-x-api/crypto/ShareMetadataBehaviour.d.ts +19 -0
- package/icc-x-api/crypto/ShareMetadataBehaviour.js +24 -0
- package/icc-x-api/crypto/ShareMetadataBehaviour.js.map +1 -0
- package/icc-x-api/crypto/TransferKeysManager.d.ts +29 -0
- package/icc-x-api/crypto/TransferKeysManager.js +121 -0
- package/icc-x-api/crypto/TransferKeysManager.js.map +1 -0
- package/icc-x-api/crypto/utils.d.ts +46 -0
- package/icc-x-api/crypto/utils.js +128 -0
- package/icc-x-api/crypto/utils.js.map +1 -1
- package/icc-x-api/filters/ContactByHcPartyTagCodeDateFilter.d.ts +11 -0
- package/icc-x-api/filters/ContactByHcPartyTagCodeDateFilter.js +11 -0
- package/icc-x-api/filters/ContactByHcPartyTagCodeDateFilter.js.map +1 -1
- package/icc-x-api/filters/DeviceByHcPartyFilter.d.ts +11 -0
- package/icc-x-api/filters/DeviceByHcPartyFilter.js +11 -0
- package/icc-x-api/filters/DeviceByHcPartyFilter.js.map +1 -1
- package/icc-x-api/filters/DeviceByIdsFilter.d.ts +11 -0
- package/icc-x-api/filters/DeviceByIdsFilter.js +11 -0
- package/icc-x-api/filters/DeviceByIdsFilter.js.map +1 -1
- package/icc-x-api/filters/HealthElementByHcPartySecretForeignKeysFilter.d.ts +11 -0
- package/icc-x-api/filters/HealthElementByHcPartySecretForeignKeysFilter.js +11 -0
- package/icc-x-api/filters/HealthElementByHcPartySecretForeignKeysFilter.js.map +1 -1
- package/icc-x-api/icc-accesslog-x-api.d.ts +49 -4
- package/icc-x-api/icc-accesslog-x-api.js +112 -102
- package/icc-x-api/icc-accesslog-x-api.js.map +1 -1
- package/icc-x-api/icc-calendar-item-x-api.d.ts +53 -5
- package/icc-x-api/icc-calendar-item-x-api.js +112 -100
- package/icc-x-api/icc-calendar-item-x-api.js.map +1 -1
- package/icc-x-api/icc-classification-x-api.d.ts +45 -2
- package/icc-x-api/icc-classification-x-api.js +89 -41
- package/icc-x-api/icc-classification-x-api.js.map +1 -1
- package/icc-x-api/icc-contact-x-api.d.ts +57 -19
- package/icc-x-api/icc-contact-x-api.js +149 -154
- package/icc-x-api/icc-contact-x-api.js.map +1 -1
- package/icc-x-api/icc-crypto-x-api.d.ts +266 -366
- package/icc-x-api/icc-crypto-x-api.js +416 -1327
- package/icc-x-api/icc-crypto-x-api.js.map +1 -1
- package/icc-x-api/icc-data-owner-x-api.d.ts +110 -11
- package/icc-x-api/icc-data-owner-x-api.js +188 -14
- package/icc-x-api/icc-data-owner-x-api.js.map +1 -1
- package/icc-x-api/icc-doctemplate-x-api.js +3 -3
- package/icc-x-api/icc-doctemplate-x-api.js.map +1 -1
- package/icc-x-api/icc-document-x-api.d.ts +76 -5
- package/icc-x-api/icc-document-x-api.js +137 -59
- package/icc-x-api/icc-document-x-api.js.map +1 -1
- package/icc-x-api/icc-form-x-api.d.ts +47 -7
- package/icc-x-api/icc-form-x-api.js +85 -86
- package/icc-x-api/icc-form-x-api.js.map +1 -1
- package/icc-x-api/icc-hcparty-x-api.d.ts +1 -1
- package/icc-x-api/icc-hcparty-x-api.js +1 -1
- package/icc-x-api/icc-hcparty-x-api.js.map +1 -1
- package/icc-x-api/icc-helement-x-api.d.ts +50 -3
- package/icc-x-api/icc-helement-x-api.js +101 -118
- package/icc-x-api/icc-helement-x-api.js.map +1 -1
- package/icc-x-api/icc-icure-maintenance-x-api.d.ts +31 -0
- package/icc-x-api/icc-icure-maintenance-x-api.js +124 -0
- package/icc-x-api/icc-icure-maintenance-x-api.js.map +1 -0
- package/icc-x-api/icc-invoice-x-api.d.ts +45 -5
- package/icc-x-api/icc-invoice-x-api.js +89 -64
- package/icc-x-api/icc-invoice-x-api.js.map +1 -1
- package/icc-x-api/icc-maintenance-task-x-api.d.ts +38 -6
- package/icc-x-api/icc-maintenance-task-x-api.js +73 -102
- package/icc-x-api/icc-maintenance-task-x-api.js.map +1 -1
- package/icc-x-api/icc-message-x-api.d.ts +55 -2
- package/icc-x-api/icc-message-x-api.js +92 -33
- package/icc-x-api/icc-message-x-api.js.map +1 -1
- package/icc-x-api/icc-patient-x-api.d.ts +67 -7
- package/icc-x-api/icc-patient-x-api.js +409 -473
- package/icc-x-api/icc-patient-x-api.js.map +1 -1
- package/icc-x-api/icc-receipt-x-api.d.ts +49 -5
- package/icc-x-api/icc-receipt-x-api.js +85 -47
- package/icc-x-api/icc-receipt-x-api.js.map +1 -1
- package/icc-x-api/icc-time-table-x-api.d.ts +34 -10
- package/icc-x-api/icc-time-table-x-api.js +61 -18
- package/icc-x-api/icc-time-table-x-api.js.map +1 -1
- package/icc-x-api/index.d.ts +30 -1
- package/icc-x-api/index.js +112 -49
- package/icc-x-api/index.js.map +1 -1
- package/icc-x-api/maintenance/KeyPairUpdateRequest.d.ts +35 -0
- package/icc-x-api/maintenance/KeyPairUpdateRequest.js +54 -0
- package/icc-x-api/maintenance/KeyPairUpdateRequest.js.map +1 -0
- package/icc-x-api/storage/DefaultStorageEntryKeysFactory.d.ts +9 -0
- package/icc-x-api/storage/DefaultStorageEntryKeysFactory.js +23 -0
- package/icc-x-api/storage/DefaultStorageEntryKeysFactory.js.map +1 -0
- package/icc-x-api/storage/IcureStorageFacade.d.ts +55 -0
- package/icc-x-api/storage/IcureStorageFacade.js +95 -0
- package/icc-x-api/storage/IcureStorageFacade.js.map +1 -0
- package/icc-x-api/storage/StorageEntryKeysFactory.d.ts +31 -0
- package/icc-x-api/storage/StorageEntryKeysFactory.js +3 -0
- package/icc-x-api/storage/StorageEntryKeysFactory.js.map +1 -0
- package/icc-x-api/utils/collection-utils.d.ts +8 -0
- package/icc-x-api/utils/collection-utils.js +26 -0
- package/icc-x-api/utils/collection-utils.js.map +1 -0
- package/icc-x-api/utils/crypto-utils.d.ts +8 -1
- package/icc-x-api/utils/crypto-utils.js +19 -3
- package/icc-x-api/utils/crypto-utils.js.map +1 -1
- package/icc-x-api/utils/graph-utils.d.ts +45 -0
- package/icc-x-api/utils/graph-utils.js +203 -0
- package/icc-x-api/utils/graph-utils.js.map +1 -0
- package/icc-x-api/utils/lru-temporised-async-cache.d.ts +27 -0
- package/icc-x-api/utils/lru-temporised-async-cache.js +120 -0
- package/icc-x-api/utils/lru-temporised-async-cache.js.map +1 -0
- package/package.json +1 -1
|
@@ -10,148 +10,224 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.IccCryptoXApi = void 0;
|
|
13
|
-
const AES_1 = require("./crypto/AES");
|
|
14
|
-
const RSA_1 = require("./crypto/RSA");
|
|
15
|
-
const shamir_1 = require("./crypto/shamir");
|
|
16
13
|
const _ = require("lodash");
|
|
17
|
-
const models_1 = require("../icc-api/model/models");
|
|
18
14
|
const binary_utils_1 = require("./utils/binary-utils");
|
|
19
15
|
const utils_1 = require("./utils");
|
|
20
16
|
class IccCryptoXApi {
|
|
17
|
+
get primitives() {
|
|
18
|
+
return this.cryptoPrimitives;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* @internal this method is for internal use only and may be changed without notice.
|
|
22
|
+
*/
|
|
23
|
+
get exchangeKeys() {
|
|
24
|
+
return this.exchangeKeysManager;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* @deprecated replace with {@link CryptoPrimitives.crypto} at {@link primitives}.
|
|
28
|
+
*/
|
|
21
29
|
get crypto() {
|
|
22
|
-
return this.
|
|
30
|
+
return this.cryptoPrimitives.crypto;
|
|
23
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* @deprecated replace with {@link CryptoPrimitives.shamir} at {@link primitives}.
|
|
34
|
+
*/
|
|
24
35
|
get shamir() {
|
|
25
|
-
return this.
|
|
36
|
+
return this.cryptoPrimitives.shamir;
|
|
26
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* @deprecated replace with {@link CryptoPrimitives.RSA} at {@link primitives}.
|
|
40
|
+
*/
|
|
27
41
|
get RSA() {
|
|
28
|
-
return this.
|
|
42
|
+
return this.cryptoPrimitives.RSA;
|
|
29
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* @deprecated replace with {@link CryptoPrimitives.AES} at {@link primitives}.
|
|
46
|
+
*/
|
|
30
47
|
get AES() {
|
|
31
|
-
return this.
|
|
48
|
+
return this.cryptoPrimitives.AES;
|
|
32
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* @internal this is for internal use only and may be changed without notice.
|
|
52
|
+
*/
|
|
33
53
|
get keyStorage() {
|
|
34
54
|
return this._keyStorage;
|
|
35
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* @internal this is for internal use only and may be changed without notice.
|
|
58
|
+
*/
|
|
36
59
|
get storage() {
|
|
37
60
|
return this._storage;
|
|
38
61
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
62
|
+
/**
|
|
63
|
+
* @internal this is for internal use only and may be changed without notice.
|
|
64
|
+
*/
|
|
65
|
+
get entities() {
|
|
66
|
+
return this.entitiesEncrypiton;
|
|
42
67
|
}
|
|
43
68
|
/**
|
|
44
|
-
*
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
69
|
+
* @internal this is for internal use only and may be changed without notice.
|
|
70
|
+
*/
|
|
71
|
+
get confidential() {
|
|
72
|
+
return this.confidentialEntities;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* @internal this is for internal use only and may be changed without notice.
|
|
49
76
|
*/
|
|
50
|
-
|
|
51
|
-
return
|
|
52
|
-
(this.hcPartyKeysRequestsCache[delegateHcPartyId] = this.forceGetEncryptedAesExchangeKeysForDelegate(delegateHcPartyId)));
|
|
77
|
+
get userKeysManager() {
|
|
78
|
+
return this.keyManager;
|
|
53
79
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
]).then(([a, b, c]) => (Object.assign(Object.assign(Object.assign({}, a), b), c)));
|
|
80
|
+
/**
|
|
81
|
+
* @internal this is for internal use only and may be changed without notice.
|
|
82
|
+
*/
|
|
83
|
+
get shamirKeysManager() {
|
|
84
|
+
return this.shamirManager;
|
|
60
85
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
//for each delegate, it stores the list of delegators and the corresponding delegateEncryptedHcPartyKey (shared HcPartyKey, from delegator to delegate, encrypted with the RSA key of the delegate)
|
|
66
|
-
this.hcPartyKeysRequestsCache = {};
|
|
67
|
-
this.cacheLastDeletionTimestamp = undefined;
|
|
68
|
-
this.dataOwnerCache = {};
|
|
86
|
+
/**
|
|
87
|
+
* @internal
|
|
88
|
+
*/
|
|
89
|
+
constructor(exchangeKeysManager, cryptoPrimitives, keyManager, dataOwnerApi, entitiesEncrypiton, shamirManager, storage, keyStorage, icureStorageFacade, hcPartyBaseApi, confidentialEntities) {
|
|
69
90
|
this.keychainLocalStoreIdPrefix = 'org.taktik.icure.ehealth.keychain.';
|
|
70
91
|
this.keychainValidityDateLocalStoreIdPrefix = 'org.taktik.icure.ehealth.keychain-date.';
|
|
71
92
|
this.hcpPreferenceKeyEhealthCert = 'eHealthCRTCrypt';
|
|
72
93
|
this.hcpPreferenceKeyEhealthCertDate = 'eHealthCRTDate';
|
|
73
94
|
this.rsaLocalStoreIdPrefix = 'org.taktik.icure.rsa.';
|
|
74
|
-
this.
|
|
75
|
-
this.
|
|
76
|
-
this.
|
|
77
|
-
this.
|
|
78
|
-
this.
|
|
79
|
-
this.
|
|
80
|
-
this._AES = new AES_1.AESUtils(crypto);
|
|
81
|
-
this._RSA = new RSA_1.RSAUtils(crypto);
|
|
82
|
-
this._shamir = new shamir_1.ShamirClass(crypto);
|
|
95
|
+
this.exchangeKeysManager = exchangeKeysManager;
|
|
96
|
+
this.cryptoPrimitives = cryptoPrimitives;
|
|
97
|
+
this.keyManager = keyManager;
|
|
98
|
+
this.dataOwnerApi = dataOwnerApi;
|
|
99
|
+
this.entitiesEncrypiton = entitiesEncrypiton;
|
|
100
|
+
this.shamirManager = shamirManager;
|
|
83
101
|
this._storage = storage;
|
|
84
102
|
this._keyStorage = keyStorage;
|
|
103
|
+
this.icureStorage = icureStorageFacade;
|
|
104
|
+
this.hcpartyBaseApi = hcPartyBaseApi;
|
|
105
|
+
this.confidentialEntities = confidentialEntities;
|
|
85
106
|
}
|
|
86
|
-
|
|
107
|
+
/**
|
|
108
|
+
* Deletes values cached by the crypto api, to allow to detect changes in stored key pairs, exchange keys and/or current data owner details.
|
|
109
|
+
* This method may be useful in cases where a user is logged in from multiple devices or in cases where other users have just shared some data with
|
|
110
|
+
* the current user for the first time.
|
|
111
|
+
*/
|
|
112
|
+
forceReload() {
|
|
87
113
|
return __awaiter(this, void 0, void 0, function* () {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (!this.rsaKeyPairs[fingerprint]) {
|
|
92
|
-
yield this.cacheKeyPair(yield this.loadKeyPairNotImported(dataOwnerId, fingerprint));
|
|
93
|
-
}
|
|
94
|
-
}
|
|
114
|
+
this.exchangeKeysManager.clearCache(true);
|
|
115
|
+
this.dataOwnerApi.clearCurrentDataOwnerIdsCache();
|
|
116
|
+
yield this.keyManager.reloadKeys();
|
|
95
117
|
});
|
|
96
118
|
}
|
|
97
|
-
|
|
98
|
-
|
|
119
|
+
/**
|
|
120
|
+
* Get a key pair with the provided fingerprint if present.
|
|
121
|
+
* @param fingerprint a key-pair/public-key fingerprint
|
|
122
|
+
* @return the pair associated to the fingerprint and a boolean indicating if the pair is verified, if present, else undefined
|
|
123
|
+
*/
|
|
124
|
+
getKeyPairForFingerprint(fingerprint) {
|
|
125
|
+
return this.keyManager.getKeyPairForFingerprint(fingerprint);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Get the public keys of available key pairs for the current user and his parents in hex-encoded spki representation (uses cached keys: no request
|
|
129
|
+
* is done to the server).
|
|
130
|
+
* Note that this will also include unverified keys.
|
|
131
|
+
* @return the spki representation of public keys of available keypairs for the current user.
|
|
132
|
+
*/
|
|
133
|
+
getCurrentUserHierarchyAvailablePublicKeysHex() {
|
|
99
134
|
return __awaiter(this, void 0, void 0, function* () {
|
|
100
|
-
|
|
101
|
-
return (_a = this.rsaKeyPairs[fingerprint]) !== null && _a !== void 0 ? _a : (yield this.cacheKeyPair(yield this.loadKeyPairNotImported(dataOwnerId, fingerprint)));
|
|
135
|
+
return this.keyManager.getCurrentUserHierarchyAvailablePublicKeysHex();
|
|
102
136
|
});
|
|
103
137
|
}
|
|
104
|
-
|
|
138
|
+
/**
|
|
139
|
+
* Get the public keys of available key pairs for the current user in hex-encoded spki representation (uses cached keys: no request is done to the
|
|
140
|
+
* server).
|
|
141
|
+
* By setting {@link verifiedOnly} to true only the public keys for verified key pairs will be returned: these will include only key pairs created
|
|
142
|
+
* on this device or which have been verified using {@link CryptoStrategies} on this device.
|
|
143
|
+
* @param verifiedOnly if true only the verified public keys will be returned.
|
|
144
|
+
* @return the spki representation of public keys of available keypairs for the current user.
|
|
145
|
+
*/
|
|
146
|
+
getCurrentUserAvailablePublicKeysHex(verifiedOnly) {
|
|
105
147
|
return __awaiter(this, void 0, void 0, function* () {
|
|
106
|
-
return
|
|
107
|
-
return (yield p).concat([(0, utils_1.jwk2spki)(yield this.RSA.exportKey(rsa.publicKey, 'jwk'))]);
|
|
108
|
-
}), Promise.resolve([]));
|
|
148
|
+
return this.keyManager.getCurrentUserAvailablePublicKeysHex(verifiedOnly);
|
|
109
149
|
});
|
|
110
150
|
}
|
|
111
|
-
|
|
151
|
+
/**
|
|
152
|
+
* @deprecated depending on your use case you should delete the calls to this method or call {@link forceReload}:
|
|
153
|
+
* - Replace with `forceReload()` if one of the following parts of the current data owner may have been modified from a different api instance:
|
|
154
|
+
* - Hcp hierarchy
|
|
155
|
+
* - Key recovery data (transfer keys or shamir)
|
|
156
|
+
* - Exchange keys (formerly hcp keys)
|
|
157
|
+
* - Remove the call if the main goal was to force reload the data owner: data owner are not cached anymore.
|
|
158
|
+
*/
|
|
159
|
+
emptyHcpCache(hcpartyId) {
|
|
160
|
+
this.exchangeKeysManager.clearCache(false);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* @deprecated you should not need this method anymore to deal with the encryption of iCure entities because everything related to entities
|
|
164
|
+
* encryption should be done either through the entity-specific extended api or through the extended apis.
|
|
165
|
+
* Note that keys returned by the current implementation of this method may not be safe for encryption/sharing.
|
|
166
|
+
* If instead you are using this method to retrieve key pairs for other purposes, for example because you want to reuse the user keys in iCure for
|
|
167
|
+
* other services consider the following alternatives:
|
|
168
|
+
* - If you want to use all iCure facilities including key recovery and key verification you can use {@link getKeyPairForFingerprint}.
|
|
169
|
+
* Note that this solution can only give access to keys for the data owner of the instantiated api and his parents.
|
|
170
|
+
* - Alternatively you can use directly your choice of {@link KeyStorageFacade} and {@link StorageEntryKeysFactory}: if these are the same you use
|
|
171
|
+
* for the iCure API client the keys will be shared with it. Note however that the iCure api client uses
|
|
172
|
+
* {@link StorageEntryKeysFactory.cachedRecoveredKeypairOfDataOwner} to cache recovered keys of a data owner which may not have originated from
|
|
173
|
+
* this device, so you should only use {@link StorageEntryKeysFactory.deviceKeypairOfDataOwner} if you want to make sure the keys you use are safe
|
|
174
|
+
* for encryption.
|
|
175
|
+
*/
|
|
176
|
+
getCachedRsaKeyPairForFingerprint(dataOwnerId, pubKeyOrFingerprint) {
|
|
112
177
|
return __awaiter(this, void 0, void 0, function* () {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
178
|
+
const fingerprint = pubKeyOrFingerprint.slice(-32);
|
|
179
|
+
const res = this.keyManager.getDecryptionKeys()[fingerprint];
|
|
180
|
+
if (!res)
|
|
181
|
+
console.warn(`Could not find any keypair for fingerprint ${fingerprint}`);
|
|
182
|
+
return res;
|
|
116
183
|
});
|
|
117
184
|
}
|
|
185
|
+
/**
|
|
186
|
+
* @deprecated You do not need this method to encrypt/decrypt data of iCure, but if you want to reuse the iCure keys for the user for other purposes
|
|
187
|
+
* you have different options to replace this method, depending on what you actually need. All options return the hex-encoded spki representation of
|
|
188
|
+
* the public keys.
|
|
189
|
+
* - If you want only the public keys for which we have a private key available
|
|
190
|
+
* - you can replicate the current behaviour using {@link getCurrentUserHierarchyAvailablePublicKeysHex}. This includes keys for the current user
|
|
191
|
+
* and his parents.
|
|
192
|
+
* - use {@link getCurrentUserAvailablePublicKeysHex} to get public keys only for the current data owner, ignoring any keys of the
|
|
193
|
+
* parent hierarchy. In this case you can also apply a filter to only get verified keys (safe for encryption).
|
|
194
|
+
* - If you need all public keys for the data owner, including those for which there is no corresponding private key available on the device use
|
|
195
|
+
* {@link IccDataOwnerXApi.getHexPublicKeysOf} with the current data owner. If you don't have it available you may get it from
|
|
196
|
+
* {@link IccDataOwnerXApi.getCurrentDataOwner}, but this will require to do a request to the iCure server (other options use only cached data).
|
|
197
|
+
*/
|
|
198
|
+
getPublicKeys() {
|
|
199
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
200
|
+
return this.userKeysManager.getCurrentUserHierarchyAvailablePublicKeysHex();
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* @deprecated replace with {@link CryptoPrimitives.randomUuid} at {@link primitives}.
|
|
205
|
+
*/
|
|
118
206
|
randomUuid() {
|
|
119
|
-
return
|
|
120
|
-
);
|
|
207
|
+
return this.primitives.randomUuid();
|
|
121
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* @deprecated replace with {@link CryptoPrimitives.sha256} at {@link primitives}.
|
|
211
|
+
*/
|
|
122
212
|
sha256(data) {
|
|
123
|
-
return this.
|
|
213
|
+
return this.primitives.sha256(data);
|
|
124
214
|
}
|
|
215
|
+
/**
|
|
216
|
+
* @deprecated Use {@link ShamirKeysManager.updateSelfSplits} from {@link shamirKeysManager} instead. Note that the new method completely replaces
|
|
217
|
+
* all current values if a split for the provided key already exists.
|
|
218
|
+
*/
|
|
125
219
|
encryptShamirRSAKey(hcp, notaries, threshold) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
return
|
|
132
|
-
|
|
133
|
-
const { owner: hcParty, aesExchangeKeys } = yield this.getOrCreateHcPartyKeys(hcp, notary.id);
|
|
134
|
-
const [publicKeyIdentifier, hcPartyKeys] = Object.entries(aesExchangeKeys)[0];
|
|
135
|
-
try {
|
|
136
|
-
const importedAESHcPartyKey = yield this.decryptHcPartyKey(notary.id, hcParty.id, notary.id, publicKeyIdentifier, hcPartyKeys, publicKeys);
|
|
137
|
-
const encryptedShamirPartition = yield this.AES.encrypt(importedAESHcPartyKey.key, shares[idx]);
|
|
138
|
-
hcParty.privateKeyShamirPartitions = hcParty.privateKeyShamirPartitions || {};
|
|
139
|
-
hcParty.privateKeyShamirPartitions[notary.id] = (0, binary_utils_1.ua2hex)(encryptedShamirPartition);
|
|
140
|
-
}
|
|
141
|
-
catch (e) {
|
|
142
|
-
console.log('Error during encryptedShamirRSAKey', notary.id, e);
|
|
143
|
-
}
|
|
144
|
-
return hcParty;
|
|
145
|
-
}));
|
|
146
|
-
}, Promise.resolve(hcp));
|
|
147
|
-
})));
|
|
220
|
+
var _a;
|
|
221
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
222
|
+
const legacyKeyFp = (_a = hcp.publicKey) === null || _a === void 0 ? void 0 : _a.slice(-32);
|
|
223
|
+
if (!legacyKeyFp)
|
|
224
|
+
throw new Error(`No legacy/default key for hcp ${hcp.id}`);
|
|
225
|
+
return (yield this.shamirKeysManager.updateSelfSplits({ [legacyKeyFp]: { notariesIds: notaries.map((x) => x.id), minShares: threshold !== null && threshold !== void 0 ? threshold : notaries.length } }, [])).dataOwner;
|
|
226
|
+
});
|
|
148
227
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
* @param hcp : the hcp whose key we want to reconstruct
|
|
153
|
-
* @param notaries : holders of the shamir shares
|
|
154
|
-
**/
|
|
228
|
+
/**
|
|
229
|
+
* @deprecated You should not need this method anymore because the api will automatically try to recover the available shamir keys on startup.
|
|
230
|
+
*/
|
|
155
231
|
decryptShamirRSAKey(hcp, notaries) {
|
|
156
232
|
return __awaiter(this, void 0, void 0, function* () {
|
|
157
233
|
try {
|
|
@@ -169,7 +245,7 @@ class IccCryptoXApi {
|
|
|
169
245
|
const decryptedShares = yield _.reduce(notaries, (queue, notary) => {
|
|
170
246
|
return queue.then((shares) => __awaiter(this, void 0, void 0, function* () {
|
|
171
247
|
try {
|
|
172
|
-
//
|
|
248
|
+
// now, we get the encrypted shares in db and decrypt them. This assumes that the
|
|
173
249
|
// the notaries' private keys are in localstorage. We should implement a way for the notaries to
|
|
174
250
|
// give hcp the decrypted shares without having to also share their private keys.
|
|
175
251
|
const encryptedAesKeyMap = yield this.getEncryptedAesExchangeKeys(hcp, notary.id);
|
|
@@ -184,11 +260,11 @@ class IccCryptoXApi {
|
|
|
184
260
|
return shares;
|
|
185
261
|
}));
|
|
186
262
|
}, Promise.resolve([]));
|
|
187
|
-
decryptedPrivatedKey = this.
|
|
263
|
+
decryptedPrivatedKey = this.primitives.shamir.combine(decryptedShares);
|
|
188
264
|
}
|
|
189
|
-
const importedPrivateKey = yield this.
|
|
190
|
-
const importedPublicKey = yield this.
|
|
191
|
-
const exportedKeyPair = yield this.
|
|
265
|
+
const importedPrivateKey = yield this.primitives.RSA.importKey('pkcs8', (0, binary_utils_1.hex2ua)(decryptedPrivatedKey), ['decrypt']);
|
|
266
|
+
const importedPublicKey = yield this.primitives.RSA.importKey('spki', (0, binary_utils_1.hex2ua)(hcp.publicKey), ['encrypt']);
|
|
267
|
+
const exportedKeyPair = yield this.primitives.RSA.exportKeys({ publicKey: importedPublicKey, privateKey: importedPrivateKey }, 'jwk', 'jwk');
|
|
192
268
|
yield this._keyStorage.storeKeyPair(`${this.rsaLocalStoreIdPrefix}${hcp.id}`, exportedKeyPair);
|
|
193
269
|
}
|
|
194
270
|
catch (e) {
|
|
@@ -197,415 +273,107 @@ class IccCryptoXApi {
|
|
|
197
273
|
});
|
|
198
274
|
}
|
|
199
275
|
/**
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
* Otherwise, decrypts it using the RSA key of the delegator or delegate (depending on the value of `encryptedForDelegator`)
|
|
204
|
-
*
|
|
205
|
-
* @param loggedHcPartyId The logged DataOwner id or the id of his parent
|
|
206
|
-
* @param delegatorId The id of Delegator eg, the DataOwner where this AES exchange key is stored
|
|
207
|
-
* @param delegateHcPartyId The id of the delegate : the data owner for whom this aes exchange key has been created
|
|
208
|
-
* @param publicKey The public key of the delegator : A unique aes exchange key is created for each public key of the delegator. This is the public key corresponding to the encryptedHcPartyKeys
|
|
209
|
-
* @param encryptedHcPartyKeys The encryptedHcPartyKeys to be decrypted
|
|
210
|
-
* @param publicKeys The public keys for which we might have a private key
|
|
211
|
-
*
|
|
212
|
-
* @returns - **delegatorId** the input param `delegatorId`
|
|
213
|
-
* - **key** the decrypted `encryptedHcPartyKey`
|
|
276
|
+
* @deprecated you should not need this method anymore because everything related to entities encryption should be done either through the
|
|
277
|
+
* entity-specific extended api.
|
|
278
|
+
* Note that currently this method does not cache results anymore (but the updated methods do).
|
|
214
279
|
*/
|
|
215
280
|
decryptHcPartyKey(loggedHcPartyId, delegatorId, delegateHcPartyId, publicKey, encryptedHcPartyKeys, publicKeys) {
|
|
216
|
-
var _a;
|
|
217
281
|
return __awaiter(this, void 0, void 0, function* () {
|
|
218
282
|
if (!publicKeys.length) {
|
|
219
283
|
const reason = `Cannot decrypt RSA encrypted AES HcPartyKey from ${delegatorId} to ${delegateHcPartyId}: no public key`;
|
|
220
284
|
console.warn(reason);
|
|
221
285
|
throw new Error(reason);
|
|
222
286
|
}
|
|
223
|
-
const
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
return delegatorAndKeys;
|
|
233
|
-
}
|
|
234
|
-
const fingerprint = pk.slice(-32);
|
|
235
|
-
const keyPair = (_b = this.rsaKeyPairs[fingerprint]) !== null && _b !== void 0 ? _b : (yield this.cacheKeyPair(yield this.loadKeyPairNotImported(loggedHcPartyId, fingerprint)));
|
|
236
|
-
if (!keyPair) {
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
// Due to past bugs the encryptedHcPartyKeys may contain the full key instead of just the fingerprint.
|
|
240
|
-
let encryptedHcPartyKey = (_c = encryptedHcPartyKeys[fingerprint]) !== null && _c !== void 0 ? _c : encryptedHcPartyKeys[pk];
|
|
241
|
-
if (!encryptedHcPartyKey) {
|
|
242
|
-
const delegate = yield this.getDataOwner(delegateHcPartyId, false); //it is faster to just try to decrypt if not in cache
|
|
243
|
-
if (!(delegate === null || delegate === void 0 ? void 0 : delegate.dataOwner) || ((_d = delegate.dataOwner.publicKey) === null || _d === void 0 ? void 0 : _d.endsWith(fingerprint))) {
|
|
244
|
-
encryptedHcPartyKey = encryptedHcPartyKeys[''];
|
|
245
|
-
}
|
|
246
|
-
else {
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
try {
|
|
251
|
-
const decryptedAesExchangeKey = yield this._RSA.decrypt(keyPair.privateKey, (0, binary_utils_1.hex2ua)(encryptedHcPartyKey));
|
|
252
|
-
const importedAesExchangeKey = yield this._AES.importKey('raw', decryptedAesExchangeKey);
|
|
253
|
-
return (this.hcPartyKeysCache[cacheKey] = {
|
|
254
|
-
delegatorId: delegatorId,
|
|
255
|
-
key: importedAesExchangeKey,
|
|
256
|
-
rawKey: (0, binary_utils_1.ua2hex)(new Uint8Array(decryptedAesExchangeKey)),
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
catch (e) {
|
|
260
|
-
const reason = `Cannot decrypt RSA encrypted AES HcPartyKey from ${delegatorId} to ${delegateHcPartyId} for pubKey ${fingerprint}: impossible to decrypt`;
|
|
261
|
-
console.log(reason);
|
|
262
|
-
}
|
|
263
|
-
}), Promise.resolve(undefined));
|
|
264
|
-
const availablePublicKeys = publicKeys.filter((pk) => this.rsaKeyPairs[pk.slice(-32)]);
|
|
265
|
-
if (!result) {
|
|
266
|
-
//Try to find another key from the transfer keys
|
|
267
|
-
const hcp = (yield this.getDataOwner(loggedHcPartyId));
|
|
268
|
-
const candidates = Object.entries((_a = hcp.dataOwner.transferKeys) !== null && _a !== void 0 ? _a : {})
|
|
269
|
-
.filter(([fp, _]) => availablePublicKeys.some((pk) => pk.slice(-32) === fp.slice(-32))) // only keep keys that we will be able to decrypt
|
|
270
|
-
.flatMap(([pk, keys]) => Object.entries(keys).map(([k, v]) => [pk, k, v]))
|
|
271
|
-
.filter(([_, k]) => !publicKeys.some((apk) => apk.slice(-32) === k.slice(-32)));
|
|
272
|
-
if (candidates.length) {
|
|
273
|
-
const newPublicKeys = yield candidates.reduce((p, [decryptionKeyFingerprint, privateKeyFingerprint, encryptedPrivateKey]) => __awaiter(this, void 0, void 0, function* () {
|
|
274
|
-
var _e, _f;
|
|
275
|
-
const newKeys = yield p;
|
|
276
|
-
const aesExchangeKeys = (_e = Object.entries(hcp.dataOwner.aesExchangeKeys).find(([fp, _]) => fp.slice(-32) === privateKeyFingerprint.slice(-32))) === null || _e === void 0 ? void 0 : _e[1][loggedHcPartyId];
|
|
277
|
-
if (aesExchangeKeys) {
|
|
278
|
-
const encryptedAesExchangeKey = (_f = Object.entries(aesExchangeKeys).find(([fp, _]) => fp.slice(-32) === decryptionKeyFingerprint.slice(-32))) === null || _f === void 0 ? void 0 : _f[1];
|
|
279
|
-
if (encryptedAesExchangeKey) {
|
|
280
|
-
const keyPair = this.rsaKeyPairs[decryptionKeyFingerprint.slice(-32)];
|
|
281
|
-
if (!keyPair) {
|
|
282
|
-
return newKeys;
|
|
283
|
-
}
|
|
284
|
-
const decryptedAesExchangeKey = yield this._RSA.decrypt(keyPair.privateKey, (0, binary_utils_1.hex2ua)(encryptedAesExchangeKey));
|
|
285
|
-
const importedAesExchangeKey = yield this._AES.importKey('raw', decryptedAesExchangeKey);
|
|
286
|
-
const decryptedPrivateKey = yield this._AES.decrypt(importedAesExchangeKey, (0, binary_utils_1.hex2ua)(encryptedPrivateKey));
|
|
287
|
-
const newPublicKey = Object.keys(hcp.dataOwner.aesExchangeKeys).find((fp) => fp.slice(-32) === privateKeyFingerprint.slice(-32));
|
|
288
|
-
yield this.cacheKeyPair({
|
|
289
|
-
publicKey: (0, utils_1.spkiToJwk)((0, binary_utils_1.hex2ua)(newPublicKey)),
|
|
290
|
-
privateKey: (0, utils_1.pkcs8ToJwk)(decryptedPrivateKey),
|
|
291
|
-
});
|
|
292
|
-
return newKeys.concat([newPublicKey]);
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
return newKeys;
|
|
296
|
-
}), Promise.resolve([]));
|
|
297
|
-
if (newPublicKeys.length) {
|
|
298
|
-
return yield this.decryptHcPartyKey(loggedHcPartyId, delegatorId, delegateHcPartyId, publicKey, encryptedHcPartyKeys, newPublicKeys);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
const reason = `Cannot decrypt RSA encrypted AES HcPartyKey from ${delegatorId} to ${delegateHcPartyId}: impossible to decrypt. No private key was found or could be used to decrypt the aes exchange keys`;
|
|
302
|
-
console.log(reason);
|
|
303
|
-
throw new Error(reason);
|
|
287
|
+
const keysFpSet = new Set(publicKeys.map((x) => x.slice(-32)));
|
|
288
|
+
const filteredKeys = Object.fromEntries(Object.entries(this.keyManager.getDecryptionKeys()).filter(([fp]) => keysFpSet.has(fp)));
|
|
289
|
+
const decrypted = yield this.exchangeKeysManager.base.tryDecryptExchangeKeys([encryptedHcPartyKeys], filteredKeys);
|
|
290
|
+
const res = decrypted.successfulDecryptions[0];
|
|
291
|
+
if (!res) {
|
|
292
|
+
const error = `Cannot decrypt RSA encrypted AES HcPartyKey from ${delegatorId} to ${delegateHcPartyId}: impossible to decrypt. ` +
|
|
293
|
+
'No private key was found or could be used to decrypt the aes exchange keys`';
|
|
294
|
+
console.warn(error);
|
|
295
|
+
throw new Error(error);
|
|
304
296
|
}
|
|
305
|
-
return
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
*/
|
|
311
|
-
cacheKeyPair(keyPairInJwk) {
|
|
312
|
-
return this._RSA.importKeyPair('jwk', keyPairInJwk.privateKey, 'jwk', keyPairInJwk.publicKey).then((importedKeyPair) => {
|
|
313
|
-
const pk = (0, utils_1.jwk2spki)(keyPairInJwk.publicKey);
|
|
314
|
-
return (this.rsaKeyPairs[pk.slice(-32)] = importedKeyPair);
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
/**
|
|
318
|
-
* Gets the secret ID (SFKs) that should be used in the prescribed context (confidential or not) from decrypted SPKs of the given `parent`, decrypted by the HcP with the given `hcpartyId` AND by its HcP parents
|
|
319
|
-
* @param parent :the object of which delegations (SPKs) to decrypt
|
|
320
|
-
* @param hcpartyId :the id of the delegate HcP
|
|
321
|
-
* @param confidential :weather the key is going to be used for a confidential piece or data or not
|
|
322
|
-
* @returns - **extractedKeys** array containing secret IDs (SFKs) from decrypted SPKs, from both given HcP and its parents ; can contain duplicates
|
|
323
|
-
* - **hcpartyId** the given `hcpartyId` OR, if a parent exist, the HcP id of the top parent in the hierarchy (even if that parent has no delegations)
|
|
324
|
-
*/
|
|
325
|
-
extractPreferredSfk(parent, hcpartyId, confidential) {
|
|
326
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
327
|
-
const secretForeignKeys = yield this.extractSFKsHierarchyFromDelegations(parent, hcpartyId);
|
|
328
|
-
const keys = secretForeignKeys
|
|
329
|
-
.filter(({ extractedKeys }) => extractedKeys.length > 0)
|
|
330
|
-
.filter((x, idx) => (confidential ? x.hcpartyId === hcpartyId : idx === 0))[0];
|
|
331
|
-
return ((keys &&
|
|
332
|
-
(confidential
|
|
333
|
-
? keys.extractedKeys.find((k) => !secretForeignKeys.some(({ extractedKeys, hcpartyId: parentHcpId }) => hcpartyId !== parentHcpId && extractedKeys.includes(k)))
|
|
334
|
-
: keys.extractedKeys[0])) ||
|
|
335
|
-
undefined);
|
|
297
|
+
return {
|
|
298
|
+
delegatorId,
|
|
299
|
+
key: res,
|
|
300
|
+
rawKey: (0, binary_utils_1.ua2hex)(yield this.primitives.AES.exportKey(res, 'raw')),
|
|
301
|
+
};
|
|
336
302
|
});
|
|
337
303
|
}
|
|
338
304
|
/**
|
|
339
|
-
*
|
|
305
|
+
* @deprecated you should not need this method anymore. The new API will automatically load on startup all keys available through the key storage
|
|
306
|
+
* facade and/or recoverable through transfer keys or shamir split. If no verified key (safe for encryption) can be found during the instantiation
|
|
307
|
+
* of the API, depending on the parameters passed to the factory one of two scenarios will happen:
|
|
308
|
+
* - A new key will be automatically created on the device and stored using the key storage facade.
|
|
309
|
+
* - The api instantiation fails with an exception. This is ideal if you want to try to recover an existing key pair before creating a new one.
|
|
340
310
|
*
|
|
341
|
-
*
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
*
|
|
345
|
-
*
|
|
346
|
-
* @param delegateHcPartyId the HcP for which the HcPs with IDs in `delegatorsHcPartyIdsSet` could have delegated something
|
|
347
|
-
* @param minCacheDurationInSeconds The minimum cache duration
|
|
348
|
-
* @returns - **delegatorId** : the id of the delegator HcP that shares the **key** with the `delegateHcPartyId`
|
|
349
|
-
* - **key** the decrypted HcPartyKey, shared between **delegatorId** and `delegateHcPartyId`
|
|
311
|
+
* If you were using this method to allow the user to recover an existing key that is not available in the storage facade nor recoverable through
|
|
312
|
+
* transfer keys or shamir split (for example by scanning a qr code, or by loading a file from the computer to the web browser's local storage)
|
|
313
|
+
* you will have to move that logic in your implementation of CryptoStrategies.
|
|
314
|
+
* It is currently not allowed to create new key pairs if a verified key pair is already available on the device, as this would be wasteful.
|
|
315
|
+
* If you want to convert a `JsonWebKey` pair to a `CryptoKey` pair you should use directly the method in `primitives.RSA`.
|
|
350
316
|
*/
|
|
351
|
-
|
|
317
|
+
cacheKeyPair(keyPairInJwk) {
|
|
352
318
|
return __awaiter(this, void 0, void 0, function* () {
|
|
353
|
-
const
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
}
|
|
365
|
-
catch (e) {
|
|
366
|
-
console.log(`failed to decrypt hcPartyKey from ${delegatorId} to ${delegateHcPartyId}`);
|
|
367
|
-
return;
|
|
368
|
-
}
|
|
369
|
-
})));
|
|
370
|
-
return decryptedKeys.filter((x) => !!x);
|
|
371
|
-
})))).reduce((acc, x) => [...acc, ...x], []);
|
|
372
|
-
}));
|
|
373
|
-
if (aesExchangeKeys.length > 0) {
|
|
374
|
-
return aesExchangeKeys;
|
|
375
|
-
}
|
|
376
|
-
const nowTimestamp = +new Date();
|
|
377
|
-
if (!this.cacheLastDeletionTimestamp || (nowTimestamp - this.cacheLastDeletionTimestamp) / 1000 >= minCacheDurationInSeconds) {
|
|
378
|
-
delete this.hcPartyKeysRequestsCache[delegateHcPartyId];
|
|
379
|
-
this.cacheLastDeletionTimestamp = nowTimestamp;
|
|
380
|
-
return this.decryptAndImportAesHcPartyKeysForDelegators(delegatorsHcPartyIdsSet, delegateHcPartyId, minCacheDurationInSeconds);
|
|
319
|
+
const cryptoKeyPair = yield this.primitives.RSA.importKeyPair('jwk', keyPairInJwk.privateKey, 'jwk', keyPairInJwk.publicKey);
|
|
320
|
+
const pubHex = (0, binary_utils_1.ua2hex)(yield this.primitives.RSA.exportKey(cryptoKeyPair.publicKey, 'spki'));
|
|
321
|
+
const fingerprint = pubHex.slice(-32);
|
|
322
|
+
if (!this.keyManager.getDecryptionKeys()[fingerprint]) {
|
|
323
|
+
const selfId = yield this.dataOwnerApi.getCurrentDataOwnerId();
|
|
324
|
+
const selfKeys = this.dataOwnerApi.getHexPublicKeysOf((yield this.dataOwnerApi.getCurrentDataOwner()).dataOwner);
|
|
325
|
+
if (!selfKeys.has(pubHex))
|
|
326
|
+
throw new Error(`Impossible to add key pair with fingerprint ${fingerprint} to data owner ${selfId}: the data owner has no matching public key`);
|
|
327
|
+
yield this.icureStorage.saveKey(selfId, fingerprint, keyPairInJwk, true);
|
|
328
|
+
// Force reload to check if more private keys can be recovered or more exchange keys become available.
|
|
329
|
+
yield this.forceReload();
|
|
381
330
|
}
|
|
382
|
-
return
|
|
331
|
+
return cryptoKeyPair;
|
|
383
332
|
});
|
|
384
333
|
}
|
|
385
334
|
/**
|
|
386
|
-
*
|
|
387
|
-
*
|
|
388
|
-
* 1. Checks whether the delegations' object has a delegation for the
|
|
389
|
-
* given healthCarePartyId.
|
|
390
|
-
* 2. Enumerates all the delegators (delegation.owner) present in
|
|
391
|
-
* the delegations.
|
|
392
|
-
* 3. Decrypt's delegators' keys and returns them.
|
|
393
|
-
*
|
|
394
|
-
* @param dataOwnerId : the id of the delegate HCP
|
|
395
|
-
* @param delegations : delegations (can be SPKs, CFKs, EKs) for all delegates
|
|
396
|
-
* @param fallbackOnParent default true; use parent's healthCarePartyId in case there's no delegation for the `healthcarePartyId`
|
|
397
|
-
* @returns - **delegatorId** : the id of the delegator HcP that shares the **key** with the `healthcarePartyId`
|
|
398
|
-
* - **key** the decrypted HcPartyKey, shared between **delegatorId** and `healthcarePartyId`
|
|
335
|
+
* @deprecated Usually you should not need this method, since the preferred sfk is automatically chosen by the extended entity apis when creating a
|
|
336
|
+
* new instance of the entity.
|
|
399
337
|
*/
|
|
400
|
-
|
|
401
|
-
var _a;
|
|
338
|
+
extractPreferredSfk(parent, hcpartyId, confidential) {
|
|
402
339
|
return __awaiter(this, void 0, void 0, function* () {
|
|
403
|
-
|
|
404
|
-
if (!delegationsArray.length && fallbackOnParent) {
|
|
405
|
-
const { dataOwner: hcp } = yield this.getDataOwner(dataOwnerId);
|
|
406
|
-
return hcp.parentId
|
|
407
|
-
? yield this.getDecryptedAesExchangeKeysOfDelegateAndParentsFromGenericDelegations(hcp.parentId, delegations)
|
|
408
|
-
: [];
|
|
409
|
-
}
|
|
410
|
-
else {
|
|
411
|
-
const delegatorIds = new Set();
|
|
412
|
-
delegationsArray.forEach((del) => delegatorIds.add(del.owner));
|
|
413
|
-
return this.decryptAndImportAesHcPartyKeysForDelegators(Array.from(delegatorIds), dataOwnerId);
|
|
414
|
-
}
|
|
340
|
+
return confidential ? this.confidential.getConfidentialSecretId(parent, hcpartyId) : this.confidential.getAnySecretIdSharedWithParents(parent);
|
|
415
341
|
});
|
|
416
342
|
}
|
|
417
343
|
/**
|
|
418
|
-
*
|
|
419
|
-
*
|
|
420
|
-
* (parentObject.id), and returns them in an object.
|
|
344
|
+
* @deprecated you should not need this method anymore because everything related to entities encryption should be done either through the
|
|
345
|
+
* entity-specific extended api.
|
|
421
346
|
*/
|
|
422
|
-
|
|
423
|
-
return __awaiter(this, arguments, void 0, function* () {
|
|
424
|
-
const publicKeys = yield this.getPublicKeys();
|
|
425
|
-
this.throwDetailedExceptionForInvalidParameter('createdObject.id', createdObject.id, 'initObjectDelegations', arguments);
|
|
426
|
-
if (parentObject)
|
|
427
|
-
this.throwDetailedExceptionForInvalidParameter('parentObject.id', parentObject.id, 'initObjectDelegations', arguments);
|
|
428
|
-
const secretId = this.randomUuid();
|
|
429
|
-
return this.getDataOwner(ownerId).then(({ dataOwner: owner }) => __awaiter(this, void 0, void 0, function* () {
|
|
430
|
-
var _a, _b;
|
|
431
|
-
const { owner: modifiedOwner, aesExchangeKeys } = yield this.getOrCreateHcPartyKeys(owner, ownerId);
|
|
432
|
-
const importedAESHcPartyKey = yield this.decryptAnyAesExchangeKeyForOwner(aesExchangeKeys, ownerId, ownerId, ownerId, publicKeys);
|
|
433
|
-
const encryptedDelegation = yield this._AES.encrypt(importedAESHcPartyKey.key, (0, binary_utils_1.string2ua)(createdObject.id + ':' + secretId).buffer, importedAESHcPartyKey.rawKey);
|
|
434
|
-
const encryptedSecretForeignKey = parentObject
|
|
435
|
-
? yield this._AES.encrypt(importedAESHcPartyKey.key, (0, binary_utils_1.string2ua)(createdObject.id + ':' + parentObject.id).buffer, importedAESHcPartyKey.rawKey)
|
|
436
|
-
: undefined;
|
|
437
|
-
return {
|
|
438
|
-
owner: modifiedOwner,
|
|
439
|
-
delegations: Object.assign(Object.assign({}, ((_a = createdObject.delegations) !== null && _a !== void 0 ? _a : {})), { [ownerId]: [
|
|
440
|
-
{
|
|
441
|
-
owner: ownerId,
|
|
442
|
-
delegatedTo: ownerId,
|
|
443
|
-
key: (0, binary_utils_1.ua2hex)(encryptedDelegation),
|
|
444
|
-
},
|
|
445
|
-
] }),
|
|
446
|
-
cryptedForeignKeys: (encryptedSecretForeignKey
|
|
447
|
-
? Object.assign(Object.assign({}, ((_b = createdObject.cryptedForeignKeys) !== null && _b !== void 0 ? _b : {})), { [ownerId]: [
|
|
448
|
-
{
|
|
449
|
-
owner: ownerId,
|
|
450
|
-
delegatedTo: ownerId,
|
|
451
|
-
key: (0, binary_utils_1.ua2hex)(encryptedSecretForeignKey),
|
|
452
|
-
},
|
|
453
|
-
] }) : {}) || {},
|
|
454
|
-
secretForeignKeys: secretForeignKeyOfParent ? [secretForeignKeyOfParent] : [],
|
|
455
|
-
secretId: secretId,
|
|
456
|
-
};
|
|
457
|
-
}));
|
|
458
|
-
});
|
|
459
|
-
}
|
|
460
|
-
decryptAnyAesExchangeKeyForOwner(aesExchangeKeys, loggedHcPartyId, delegatorId, delegateHcPartyId, publicKeys) {
|
|
347
|
+
decryptAndImportAesHcPartyKeysForDelegators(delegatorsHcPartyIdsSet, delegateHcPartyId, minCacheDurationInSeconds = 60) {
|
|
461
348
|
return __awaiter(this, void 0, void 0, function* () {
|
|
462
|
-
|
|
463
|
-
const
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
return
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
349
|
+
return yield delegatorsHcPartyIdsSet.reduce((acc, delegator) => __awaiter(this, void 0, void 0, function* () {
|
|
350
|
+
const awaitedAcc = yield acc;
|
|
351
|
+
const keys = yield this.exchangeKeysManager.getDecryptionExchangeKeysFor(delegator, delegateHcPartyId);
|
|
352
|
+
const keysForDelegator = yield keys.reduce((accForKey, cryptoKey) => __awaiter(this, void 0, void 0, function* () {
|
|
353
|
+
const awaitedAccForKey = yield accForKey;
|
|
354
|
+
const rawKey = (0, binary_utils_1.ua2hex)(yield this.primitives.RSA.exportKey(cryptoKey, 'spki'));
|
|
355
|
+
return [
|
|
356
|
+
...awaitedAccForKey,
|
|
357
|
+
{
|
|
358
|
+
key: cryptoKey,
|
|
359
|
+
delegatorId: delegator,
|
|
360
|
+
rawKey: rawKey,
|
|
361
|
+
},
|
|
362
|
+
];
|
|
363
|
+
}), Promise.resolve([]));
|
|
364
|
+
return [...awaitedAcc, ...keysForDelegator];
|
|
365
|
+
}), Promise.resolve([]));
|
|
478
366
|
});
|
|
479
367
|
}
|
|
480
368
|
/**
|
|
481
|
-
*
|
|
482
|
-
*
|
|
483
|
-
*
|
|
484
|
-
* 1. if `secretIdOfModifiedObject` is not provided, the method will throw an exception; this `secretIdOfModifiedObject` is used to generate a new delegation (SPK) in step 3;
|
|
485
|
-
* the `secretIdOfModifiedObject` is returned, unmodified, as `secretId`
|
|
486
|
-
* 2. if the owner (delegator) did not perform a delegation to the delegate, then this HcP delegation (creation of a new HcPKey) is performed now
|
|
487
|
-
* 3. creates a new delegation (Secret Primary Keys) on the `modifiedObject` encrypted with the HcPKey from owner to the delegate;
|
|
488
|
-
* 4. if `parentObject` != null, creates a new CFK on the `modifiedObject` encrypted with the HcPKey from owner to the delegate;
|
|
489
|
-
* 5. this new delegation (from step 3) is added to the list of existing delegations (Secret Primary Keys) on the `modifiedObject` for the delegate given by `delegateId`
|
|
490
|
-
* 6. if the CFK (from step 4) can be created, this new CFK is added to the list of existing CFKs on the `modifiedObject` for the delegate given by `delegateId`
|
|
491
|
-
* 7. then some duplicates delegations (SPKs) and CKFs are removed
|
|
492
|
-
*
|
|
493
|
-
* @param modifiedObject : the object of which SPKs and CFKs will be cloned, the clones will be modified and then used as returned values ; it's a 'child' of `parentObject`; will NOT be mutated
|
|
494
|
-
* @param parentObject : will NOT be mutated
|
|
495
|
-
* @param ownerId : the HcP id of the delegator
|
|
496
|
-
* @param delegateId : the HcP id of the delegate
|
|
497
|
-
* @param secretIdOfModifiedObject : the secret id used in the child object to generate its SPK
|
|
498
|
-
* @returns - **delegations** existing delegations (SPKs) of the `modifiedObject`, appended with results from step 5
|
|
499
|
-
* - **cryptedForeignKeys** existing CFKs of the `modifiedObject`, appended with results from steps 6
|
|
500
|
-
* - **secretId** which is the given input parameter `secretIdOfModifiedObject`
|
|
369
|
+
* @deprecated you should not need this method anymore because everything related to entities encryption should be done either through the
|
|
370
|
+
* entity-specific extended api.
|
|
371
|
+
* Note that currently this method does not cache results anymore (but the updated methods do).
|
|
501
372
|
*/
|
|
502
|
-
extendedDelegationsAndCryptedForeignKeys(
|
|
503
|
-
//TODO: suggested name: getExtendedChildObjectSPKandCFKwithDelegationFromDelegatorToDelegate
|
|
504
|
-
modifiedObject, parentObject, ownerId, delegateId, secretIdOfModifiedObject) {
|
|
505
|
-
this.throwDetailedExceptionForInvalidParameter('modifiedObject.id', modifiedObject.id, 'extendedDelegationsAndCryptedForeignKeys', arguments); //modifiedObject should never be null
|
|
506
|
-
if (parentObject)
|
|
507
|
-
this.throwDetailedExceptionForInvalidParameter('parentObject.id', parentObject === null || parentObject === void 0 ? void 0 : parentObject.id, 'extendedDelegationsAndCryptedForeignKeys', arguments);
|
|
508
|
-
this.throwDetailedExceptionForInvalidParameter('secretIdOfModifiedObject', secretIdOfModifiedObject, 'extendedDelegationsAndCryptedForeignKeys', arguments);
|
|
509
|
-
return this.getDataOwner(ownerId)
|
|
510
|
-
.then(({ dataOwner: owner }) => __awaiter(this, void 0, void 0, function* () {
|
|
511
|
-
const publicKeys = yield this.getPublicKeys();
|
|
512
|
-
const { owner: modifiedOwner, aesExchangeKeys } = yield this.getOrCreateHcPartyKeys(owner, delegateId);
|
|
513
|
-
const importedAESHcPartyKey = yield this.decryptAnyAesExchangeKeyForOwner(aesExchangeKeys, ownerId, delegateId, ownerId, publicKeys);
|
|
514
|
-
modifiedObject = (modifiedObject === null || modifiedObject === void 0 ? void 0 : modifiedObject.id) === owner.id ? modifiedOwner : modifiedObject;
|
|
515
|
-
return {
|
|
516
|
-
previousDecryptedDelegations: yield Promise.all(((modifiedObject.delegations || {})[delegateId] || []).map((d) => (d.key &&
|
|
517
|
-
d.owner === ownerId &&
|
|
518
|
-
this._AES.decrypt(importedAESHcPartyKey.key, (0, binary_utils_1.hex2ua)(d.key), importedAESHcPartyKey.rawKey).catch(() => {
|
|
519
|
-
console.log(`Cannot decrypt delegation from ${d.owner} to ${d.delegatedTo} for object with id ${modifiedObject.id}:`, modifiedObject);
|
|
520
|
-
return Promise.resolve();
|
|
521
|
-
})) ||
|
|
522
|
-
Promise.resolve())),
|
|
523
|
-
previousDecryptedCryptedForeignKeys: yield Promise.all(((modifiedObject.cryptedForeignKeys || {})[delegateId] || []).map((d) => (d.key &&
|
|
524
|
-
d.owner === ownerId &&
|
|
525
|
-
this._AES.decrypt(importedAESHcPartyKey.key, (0, binary_utils_1.hex2ua)(d.key), importedAESHcPartyKey.rawKey).catch(() => {
|
|
526
|
-
console.log(`Cannot decrypt cryptedForeignKeys from ${d.owner} to ${d.delegatedTo} for object with id ${modifiedObject.id}:`, modifiedObject);
|
|
527
|
-
return Promise.resolve();
|
|
528
|
-
})) ||
|
|
529
|
-
Promise.resolve())),
|
|
530
|
-
cryptedDelegation: yield this._AES.encrypt(importedAESHcPartyKey.key, (0, binary_utils_1.string2ua)(modifiedObject.id + ':' + secretIdOfModifiedObject).buffer, importedAESHcPartyKey.rawKey),
|
|
531
|
-
cryptedForeignKey: parentObject
|
|
532
|
-
? yield this._AES.encrypt(importedAESHcPartyKey.key, (0, binary_utils_1.string2ua)(modifiedObject.id + ':' + parentObject.id).buffer, importedAESHcPartyKey.rawKey)
|
|
533
|
-
: undefined,
|
|
534
|
-
};
|
|
535
|
-
}))
|
|
536
|
-
.then(({ previousDecryptedDelegations, previousDecryptedCryptedForeignKeys, cryptedDelegation, cryptedForeignKey }) => {
|
|
537
|
-
//try to limit the extent of the modifications to the delegations by preserving the redundant delegation already present and removing duplicates
|
|
538
|
-
//For delegate delegateId, we create:
|
|
539
|
-
// 1. an array of objects { d : {owner,delegatedTo,encrypted(key)}} with one object for each existing delegation and the new key concatenated
|
|
540
|
-
// 2. an array of objects { k : decrypted(key)} with one object for the existing delegations and the new key concatenated
|
|
541
|
-
// We merge them to get one array of objects: { d: {owner,delegatedTo,encrypted(key)}, k: decrypted(key)}
|
|
542
|
-
const delegationCryptedDecrypted = _.merge(((modifiedObject.delegations || {})[delegateId] || []).map((d) => ({
|
|
543
|
-
d,
|
|
544
|
-
})), (previousDecryptedDelegations || []).map((dd) => (dd ? (0, binary_utils_1.ua2string)(dd) : null)).map((k) => ({ k })))
|
|
545
|
-
.filter(({ d, k }) => !!k || d.owner !== ownerId) //Only keep the ones created by us that can still be decrypted
|
|
546
|
-
.map(({ d, k }) => ({
|
|
547
|
-
d,
|
|
548
|
-
k: k || this.randomUuid(),
|
|
549
|
-
})) // Use some unique id that ensures the delegation not created by us are going to be held
|
|
550
|
-
.concat([
|
|
551
|
-
{
|
|
552
|
-
d: {
|
|
553
|
-
owner: ownerId,
|
|
554
|
-
delegatedTo: delegateId,
|
|
555
|
-
key: (0, binary_utils_1.ua2hex)(cryptedDelegation),
|
|
556
|
-
},
|
|
557
|
-
k: modifiedObject.id + ':' + secretIdOfModifiedObject,
|
|
558
|
-
},
|
|
559
|
-
]);
|
|
560
|
-
const allDelegations = _.cloneDeep(modifiedObject.delegations || {});
|
|
561
|
-
//Only keep one version of the decrypted key
|
|
562
|
-
allDelegations[delegateId] = _.uniqBy(delegationCryptedDecrypted, (x) => x.k).map((x) => x.d);
|
|
563
|
-
const cryptedForeignKeysCryptedDecrypted = _.merge(((modifiedObject.cryptedForeignKeys || {})[delegateId] || []).map((d) => ({
|
|
564
|
-
d,
|
|
565
|
-
})), (previousDecryptedCryptedForeignKeys || []).map((dd) => (dd ? (0, binary_utils_1.ua2string)(dd) : null)).map((k) => ({ k })))
|
|
566
|
-
.filter(({ d, k }) => !!k || d.owner !== ownerId) //Only keep the ones created by us that can still be decrypted
|
|
567
|
-
.map(({ d, k }) => ({
|
|
568
|
-
d,
|
|
569
|
-
k: k || this.randomUuid(),
|
|
570
|
-
})) // Use some unique id that ensures the delegation not created by us are going to be held
|
|
571
|
-
.concat(cryptedForeignKey
|
|
572
|
-
? [
|
|
573
|
-
{
|
|
574
|
-
d: {
|
|
575
|
-
owner: ownerId,
|
|
576
|
-
delegatedTo: delegateId,
|
|
577
|
-
key: (0, binary_utils_1.ua2hex)(cryptedForeignKey),
|
|
578
|
-
},
|
|
579
|
-
k: modifiedObject.id + ':' + (parentObject === null || parentObject === void 0 ? void 0 : parentObject.id),
|
|
580
|
-
},
|
|
581
|
-
]
|
|
582
|
-
: []);
|
|
583
|
-
const allCryptedForeignKeys = _.cloneDeep(modifiedObject.cryptedForeignKeys || {});
|
|
584
|
-
if (cryptedForeignKeysCryptedDecrypted.length > 0) {
|
|
585
|
-
allCryptedForeignKeys[delegateId] = _.uniqBy(cryptedForeignKeysCryptedDecrypted, (x) => x.k).map((x) => x.d);
|
|
586
|
-
}
|
|
587
|
-
return {
|
|
588
|
-
modifiedObject,
|
|
589
|
-
delegations: allDelegations,
|
|
590
|
-
cryptedForeignKeys: allCryptedForeignKeys,
|
|
591
|
-
secretId: secretIdOfModifiedObject,
|
|
592
|
-
};
|
|
593
|
-
});
|
|
594
|
-
}
|
|
595
|
-
// What we need is to find aes exchange keys on the owner ! Even just with whom he shared information, we don't care about aes exchange keys
|
|
596
|
-
_getDelegateIdsOf(owner) {
|
|
597
|
-
var _a;
|
|
598
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
599
|
-
const mapOfAesExchKeysDelegates = Object.entries((_a = owner.aesExchangeKeys) !== null && _a !== void 0 ? _a : {}).reduce((map, [, aesExchKeys]) => map.concat(Object.keys(aesExchKeys).filter((delegateId) => !map.includes(delegateId))), []);
|
|
600
|
-
return owner.hcPartyKeys
|
|
601
|
-
? mapOfAesExchKeysDelegates.concat(Object.keys(owner.hcPartyKeys).filter((delegateId) => !mapOfAesExchKeysDelegates.includes(delegateId)))
|
|
602
|
-
: mapOfAesExchKeysDelegates;
|
|
603
|
-
});
|
|
604
|
-
}
|
|
605
373
|
getEncryptedAesExchangeKeys(owner, delegateId) {
|
|
606
|
-
var _a, _b
|
|
374
|
+
var _a, _b;
|
|
607
375
|
return __awaiter(this, void 0, void 0, function* () {
|
|
608
|
-
const publicKeys =
|
|
376
|
+
const publicKeys = Array.from(this.dataOwnerApi.getHexPublicKeysOf(owner));
|
|
609
377
|
const mapOfAesExchangeKeys = Object.entries((_a = owner.aesExchangeKeys) !== null && _a !== void 0 ? _a : {})
|
|
610
378
|
.filter((e) => e[1][delegateId] && Object.keys(e[1][delegateId]).some((k1) => publicKeys.some((pk) => pk.endsWith(k1))))
|
|
611
379
|
.reduce((map, e) => {
|
|
@@ -616,9 +384,9 @@ class IccCryptoXApi {
|
|
|
616
384
|
if (!owner.publicKey || mapOfAesExchangeKeys[owner.publicKey] || !((_b = owner.hcPartyKeys) === null || _b === void 0 ? void 0 : _b[delegateId])) {
|
|
617
385
|
return mapOfAesExchangeKeys;
|
|
618
386
|
}
|
|
619
|
-
const delegate = (yield this.getDataOwner(delegateId)).dataOwner;
|
|
387
|
+
const delegate = (yield this.dataOwnerApi.getDataOwner(delegateId)).dataOwner;
|
|
620
388
|
if (delegate.publicKey && publicKeys.includes(delegate.publicKey)) {
|
|
621
|
-
return Object.assign(Object.assign({}, mapOfAesExchangeKeys), { [owner.publicKey]: { [
|
|
389
|
+
return Object.assign(Object.assign({}, mapOfAesExchangeKeys), { [owner.publicKey]: { [delegate.publicKey.slice(-32)]: owner.hcPartyKeys[delegateId][1] } });
|
|
622
390
|
}
|
|
623
391
|
else if (publicKeys.includes(owner.publicKey)) {
|
|
624
392
|
return Object.assign(Object.assign({}, mapOfAesExchangeKeys), { [owner.publicKey]: { [owner.publicKey.slice(-32)]: owner.hcPartyKeys[delegateId][0] } });
|
|
@@ -626,455 +394,144 @@ class IccCryptoXApi {
|
|
|
626
394
|
return mapOfAesExchangeKeys;
|
|
627
395
|
});
|
|
628
396
|
}
|
|
629
|
-
getOrCreateHcPartyKeys(owner, delegateId) {
|
|
630
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
631
|
-
const aesExchangeKeys = yield this.getEncryptedAesExchangeKeys(owner, delegateId);
|
|
632
|
-
if (Object.keys(aesExchangeKeys).length) {
|
|
633
|
-
return { owner, aesExchangeKeys };
|
|
634
|
-
}
|
|
635
|
-
const modifiedOwner = yield this.generateKeyForDelegate(owner.id, delegateId);
|
|
636
|
-
return { owner: modifiedOwner, aesExchangeKeys: yield this.getEncryptedAesExchangeKeys(modifiedOwner, delegateId) };
|
|
637
|
-
});
|
|
638
|
-
}
|
|
639
397
|
/**
|
|
640
|
-
*
|
|
641
|
-
*
|
|
642
|
-
*
|
|
643
|
-
* @param createdObject
|
|
644
|
-
* @param ownerId
|
|
398
|
+
* @deprecated you should not use this method because initialisation of encrypted entities keys is done automatically by the entity-specific
|
|
399
|
+
* extended api. Also note that it is now forbidden to initialise an entity as a data owner which is not the current data owner: you should instead
|
|
400
|
+
* create the entity as the current data owner then create a delegation to the other data owner.
|
|
645
401
|
*/
|
|
646
402
|
initEncryptionKeys(createdObject, ownerId) {
|
|
647
|
-
this
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
const {
|
|
652
|
-
const importedAESHcPartyKey = yield this.decryptAnyAesExchangeKeyForOwner(aesExchangeKeys, ownerId, ownerId, ownerId, publicKeys);
|
|
653
|
-
const encryptedEncryptionKeys = yield this._AES.encrypt(importedAESHcPartyKey.key, (0, binary_utils_1.string2ua)(createdObject.id + ':' + secretId), importedAESHcPartyKey.rawKey);
|
|
403
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
404
|
+
if (ownerId !== (yield this.dataOwnerApi.getCurrentDataOwnerId())) {
|
|
405
|
+
throw new Error('Impossible to initialise keys as a data owner which is not the current data owner');
|
|
406
|
+
}
|
|
407
|
+
const { updatedEntity, rawEncryptionKey } = yield this.entities.entityWithInitialisedEncryptedMetadata(Object.assign(Object.assign({}, createdObject), { delegations: undefined, encryptionKeys: undefined, cryptedForeignKeys: undefined, secretForeignKeys: undefined }), undefined, undefined, true);
|
|
654
408
|
return {
|
|
655
|
-
encryptionKeys:
|
|
656
|
-
|
|
657
|
-
ownerId,
|
|
658
|
-
[
|
|
659
|
-
{
|
|
660
|
-
owner: ownerId,
|
|
661
|
-
delegatedTo: ownerId,
|
|
662
|
-
key: (0, binary_utils_1.ua2hex)(encryptedEncryptionKeys),
|
|
663
|
-
},
|
|
664
|
-
],
|
|
665
|
-
],
|
|
666
|
-
]),
|
|
667
|
-
secretId: secretId,
|
|
668
|
-
modifiedOwner
|
|
409
|
+
encryptionKeys: updatedEntity.encryptionKeys,
|
|
410
|
+
secretId: rawEncryptionKey,
|
|
669
411
|
};
|
|
670
|
-
})
|
|
412
|
+
});
|
|
671
413
|
}
|
|
672
414
|
/**
|
|
673
|
-
*
|
|
674
|
-
*
|
|
675
|
-
* @
|
|
676
|
-
*
|
|
677
|
-
*
|
|
678
|
-
*
|
|
679
|
-
*
|
|
680
|
-
*
|
|
681
|
-
* `secretEncryptionKeyOfObject` )
|
|
682
|
-
* - **secretId** which is the given input parameter `secretEncryptionKeyOfObject`
|
|
415
|
+
* @deprecated You should use:
|
|
416
|
+
* - {@link IccPatientXApi.decryptSecretIdsOf} or {@link IccMessageXApi.decryptSecretIdsOf} to get the delegation sfks (now caleld secret ids).
|
|
417
|
+
* - {@link IccDataOwnerXApi.getCurrentDataOwnerHierarchyIds} to get the full hierarchy for the current data owner (cached). The first element is
|
|
418
|
+
* the id of the topmost parent, while the last is the current data owner.
|
|
419
|
+
* Note that the behaviour of this method has some subtle changes compared to the past:
|
|
420
|
+
* - throws an error if the provided hcpartyId is not part of the current data owner hierarchy.
|
|
421
|
+
* - does not provide any guarantees on the ordering of the extracted keys
|
|
422
|
+
* - deduplicates extracted keys
|
|
683
423
|
*/
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
const importedAESHcPartyKey = yield this.decryptAnyAesExchangeKeyForOwner(aesExchangeKeys, ownerId, ownerId, delegateId, publicKeys);
|
|
695
|
-
const previousDecryptedEncryptionKeys = yield Promise.all(((modifiedObject.encryptionKeys || {})[delegateId] || []).map((d) => (d.key &&
|
|
696
|
-
d.owner === ownerId &&
|
|
697
|
-
this._AES.decrypt(importedAESHcPartyKey.key, (0, binary_utils_1.hex2ua)(d.key), importedAESHcPartyKey.rawKey).catch(() => {
|
|
698
|
-
console.log(`Cannot decrypt encryption key from ${d.owner} to ${d.delegatedTo} for object with id ${modifiedObject.id}:`, modifiedObject);
|
|
699
|
-
return Promise.resolve();
|
|
700
|
-
})) ||
|
|
701
|
-
Promise.resolve()));
|
|
702
|
-
const encryptedEncryptionKey = yield this._AES.encrypt(importedAESHcPartyKey.key, (0, binary_utils_1.string2ua)(modifiedObject.id + ':' + secretEncryptionKeyOfObject));
|
|
703
|
-
//try to limit the extent of the modifications to the delegations by preserving the redundant encryption keys already present and removing duplicates
|
|
704
|
-
//For delegate delegateId, we create:
|
|
705
|
-
// 1. an array of objects { d : {owner,delegatedTo,encrypted(key)}} with one object for the existing encryption keys and the new key concatenated
|
|
706
|
-
// 2. an array of objects { k : decrypted(key)} with one object for the existing delegations and the new key concatenated
|
|
707
|
-
// We merge them to get one array of objects: { d: {owner,delegatedTo,encrypted(key)}, k: decrypted(key)}
|
|
708
|
-
const encryptionKeysCryptedDecrypted = _.merge(((modifiedObject.encryptionKeys || {})[delegateId] || []).map((d) => ({
|
|
709
|
-
d,
|
|
710
|
-
})), (previousDecryptedEncryptionKeys || []).map((dd) => (dd ? (0, binary_utils_1.ua2string)(dd) : null)).map((k) => ({ k })))
|
|
711
|
-
.filter(({ d, k }) => !!k || d.owner !== ownerId) //Only keep the ones created by us that can still be decrypted
|
|
712
|
-
.map(({ d, k }) => ({ d, k: k || this.randomUuid() }))
|
|
713
|
-
.concat([
|
|
714
|
-
{
|
|
715
|
-
d: {
|
|
716
|
-
owner: ownerId,
|
|
717
|
-
delegatedTo: delegateId,
|
|
718
|
-
key: (0, binary_utils_1.ua2hex)(encryptedEncryptionKey),
|
|
719
|
-
},
|
|
720
|
-
k: modifiedObject.id + ':' + secretEncryptionKeyOfObject,
|
|
721
|
-
},
|
|
722
|
-
]);
|
|
723
|
-
const allEncryptionKeys = _.cloneDeep(modifiedObject.encryptionKeys || {});
|
|
724
|
-
allEncryptionKeys[delegateId] = _.uniqBy(encryptionKeysCryptedDecrypted, (x) => x.k).map((x) => x.d);
|
|
424
|
+
extractDelegationsSFKs(document, hcpartyId) {
|
|
425
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
426
|
+
if (!document || !hcpartyId) {
|
|
427
|
+
return Promise.resolve({ extractedKeys: [], hcpartyId: hcpartyId });
|
|
428
|
+
}
|
|
429
|
+
const delegationsForAllDelegates = document.delegations;
|
|
430
|
+
if (!delegationsForAllDelegates || !Object.keys(delegationsForAllDelegates).length) {
|
|
431
|
+
console.log(`There is no delegation in document (${document.id})`);
|
|
432
|
+
return Promise.resolve({ extractedKeys: [], hcpartyId: hcpartyId });
|
|
433
|
+
}
|
|
725
434
|
return {
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
secretId: secretEncryptionKeyOfObject,
|
|
435
|
+
extractedKeys: yield this.entities.secretIdsOf(document, hcpartyId),
|
|
436
|
+
hcpartyId: (yield this.dataOwnerApi.getCurrentDataOwnerHierarchyIds())[0],
|
|
729
437
|
};
|
|
730
438
|
});
|
|
731
439
|
}
|
|
732
440
|
/**
|
|
733
|
-
*
|
|
734
|
-
*
|
|
735
|
-
*
|
|
736
|
-
*
|
|
737
|
-
*
|
|
738
|
-
*
|
|
739
|
-
*
|
|
740
|
-
*
|
|
741
|
-
* @param secretEncryptionKey the secret Id used in the child object to generate the EK (Content Encryption Key)
|
|
742
|
-
* @returns - an updated `child` object that will contain updated SPKs, CFKs, EKs
|
|
743
|
-
* */
|
|
744
|
-
addDelegationsAndEncryptionKeys(
|
|
745
|
-
//TODO: suggested name: updateChildGenericDelegationsFromDelegatorToDelegate
|
|
746
|
-
parent, child, ownerId, delegateId, secretDelegationKey, secretEncryptionKey) {
|
|
747
|
-
return __awaiter(this, arguments, void 0, function* () {
|
|
748
|
-
if (parent)
|
|
749
|
-
this.throwDetailedExceptionForInvalidParameter('parent.id', parent.id, 'addDelegationsAndEncryptionKeys', arguments);
|
|
750
|
-
this.throwDetailedExceptionForInvalidParameter('child.id', child.id, 'addDelegationsAndEncryptionKeys', arguments);
|
|
751
|
-
const extendedChildObjectSPKsAndCFKs = secretDelegationKey
|
|
752
|
-
? yield this.extendedDelegationsAndCryptedForeignKeys(child, parent, ownerId, delegateId, secretDelegationKey)
|
|
753
|
-
: { modifiedObject: child, delegations: {}, cryptedForeignKeys: {}, secretId: null };
|
|
754
|
-
const extendedChildObjectEKs = secretEncryptionKey
|
|
755
|
-
? yield this.appendEncryptionKeys(extendedChildObjectSPKsAndCFKs.modifiedObject, ownerId, delegateId, secretEncryptionKey)
|
|
756
|
-
: { encryptionKeys: {}, modifiedObject: extendedChildObjectSPKsAndCFKs.modifiedObject };
|
|
757
|
-
const latestObject = extendedChildObjectEKs.modifiedObject;
|
|
758
|
-
return _.assign(latestObject, {
|
|
759
|
-
// Conservative version ... We might want to be more aggressive with the deduplication of keys
|
|
760
|
-
// For each delegate, we are going to concatenate to the src (the new delegations), the object in dest (the current delegations)
|
|
761
|
-
// for which we do not find an equivalent delegation (same delegator, same delegate)
|
|
762
|
-
delegations: _.assignWith(child.delegations, extendedChildObjectSPKsAndCFKs.delegations, (dest, src) => (src || []).concat(_.filter(dest, (d) => !src.some((s) => s.owner === d.owner && s.delegatedTo === d.delegatedTo)))),
|
|
763
|
-
cryptedForeignKeys: _.assignWith(child.cryptedForeignKeys, extendedChildObjectSPKsAndCFKs.cryptedForeignKeys, (dest, src) => (src || []).concat(_.filter(dest, (d) => !src.some((s) => s.owner === d.owner && s.delegatedTo === d.delegatedTo)))),
|
|
764
|
-
encryptionKeys: _.assignWith(child.encryptionKeys, extendedChildObjectEKs.encryptionKeys, (dest, src) => (src || []).concat(_.filter(dest, (d) => !src.some((s) => s.owner === d.owner && s.delegatedTo === d.delegatedTo)))),
|
|
765
|
-
});
|
|
766
|
-
});
|
|
767
|
-
}
|
|
768
|
-
/**
|
|
769
|
-
* Gets the secret IDs (SFKs) inside decrypted SPKs of the given `document`, decrypted by the HcP with the given `hcpartyId` AND by its HcP parents
|
|
770
|
-
* @param document : the object of which delegations (SPKs) to decrypt
|
|
771
|
-
* @param hcpartyId : the id of the delegate HcP
|
|
772
|
-
* @returns - **extractedKeys** array containing secret IDs (SFKs) from decrypted SPKs, from both given HcP and its parents ; can contain duplicates
|
|
773
|
-
* - **hcpartyId** the given `hcpartyId` OR, if a parent exist, the HcP id of the top parent in the hierarchy (even if that parent has no delegations)
|
|
774
|
-
*/
|
|
775
|
-
//TODO: even if there are no delegations for parent HCP (but the parent exists), the returned hcpartyId will be the one of the parent; is this ok?
|
|
776
|
-
extractDelegationsSFKs(
|
|
777
|
-
//TODO: suggested name: getSecretIDsSPKofHcpAndParentsFromDocument
|
|
778
|
-
document, hcpartyId) {
|
|
779
|
-
if (!document || !hcpartyId) {
|
|
780
|
-
return Promise.resolve({ extractedKeys: [], hcpartyId: hcpartyId }); //TODO: thow exception instead?
|
|
781
|
-
}
|
|
782
|
-
const delegationsForAllDelegates = document.delegations;
|
|
783
|
-
if (!delegationsForAllDelegates || !Object.keys(delegationsForAllDelegates).length) {
|
|
784
|
-
console.log(`There is no delegation in document (${document.id})`);
|
|
785
|
-
return Promise.resolve({ extractedKeys: [], hcpartyId: hcpartyId });
|
|
786
|
-
}
|
|
787
|
-
return this.extractKeysFromDelegationsForHcpHierarchy(hcpartyId, document.id, delegationsForAllDelegates);
|
|
788
|
-
}
|
|
789
|
-
/**
|
|
790
|
-
* Gets the secret IDs (SFKs) inside decrypted SPKs of the given `document`, decrypted by the HcP with the given `hcpartyId` AND by its HcP parents
|
|
791
|
-
* @param document : the object of which delegations (SPKs) to decrypt
|
|
792
|
-
* @param hcpartyId : the id of the delegate HcP
|
|
793
|
-
* @returns - **extractedKeys** array containing secret IDs (SFKs) from decrypted SPKs, from both given HcP and its parents ; can contain duplicates
|
|
794
|
-
* - **hcpartyId** the given `hcpartyId` OR, if a parent exist, the HcP id of the top parent in the hierarchy (even if that parent has no delegations)
|
|
795
|
-
*/
|
|
796
|
-
extractSFKsHierarchyFromDelegations(document, hcpartyId) {
|
|
797
|
-
if (!document || !hcpartyId) {
|
|
798
|
-
return Promise.resolve([]);
|
|
799
|
-
}
|
|
800
|
-
const delegationsForAllDelegates = document.delegations;
|
|
801
|
-
if (!delegationsForAllDelegates || !Object.keys(delegationsForAllDelegates).length) {
|
|
802
|
-
console.log(`There is no delegation in document (${document.id})`);
|
|
803
|
-
return Promise.resolve([]);
|
|
804
|
-
}
|
|
805
|
-
return this.extractKeysHierarchyFromDelegationLikes(hcpartyId, document.id, delegationsForAllDelegates);
|
|
806
|
-
}
|
|
807
|
-
// noinspection JSUnusedGlobalSymbols
|
|
808
|
-
extractCryptedFKs(
|
|
809
|
-
//TODO: suggested name: getSecretIDsCFKofHcpAndParentsFromDocument
|
|
810
|
-
document, hcpartyId) {
|
|
811
|
-
if (!document || !document.cryptedForeignKeys) {
|
|
812
|
-
return Promise.resolve({ extractedKeys: [], hcpartyId: hcpartyId });
|
|
813
|
-
}
|
|
814
|
-
const cfksForAllDelegates = document.cryptedForeignKeys;
|
|
815
|
-
if (!cfksForAllDelegates || !Object.keys(cfksForAllDelegates).length) {
|
|
816
|
-
console.log(`There is no cryptedForeignKeys in document (${document.id})`);
|
|
817
|
-
return Promise.resolve({ extractedKeys: [], hcpartyId: hcpartyId });
|
|
818
|
-
}
|
|
819
|
-
return this.extractKeysFromDelegationsForHcpHierarchy(hcpartyId, document.id, cfksForAllDelegates);
|
|
820
|
-
}
|
|
821
|
-
extractEncryptionsSKs(
|
|
822
|
-
//TODO: suggested name: getSecretIDsEKofHcpAndParentsFromDocument
|
|
823
|
-
document, hcpartyId) {
|
|
824
|
-
if (!document.encryptionKeys) {
|
|
825
|
-
return Promise.resolve({ extractedKeys: [], hcpartyId: hcpartyId });
|
|
826
|
-
}
|
|
827
|
-
const eckeysForAllDelegates = document.encryptionKeys;
|
|
828
|
-
if (!eckeysForAllDelegates || !Object.keys(eckeysForAllDelegates).length) {
|
|
829
|
-
return Promise.resolve({ extractedKeys: [], hcpartyId: hcpartyId });
|
|
830
|
-
}
|
|
831
|
-
return this.extractKeysFromDelegationsForHcpHierarchy(hcpartyId, document.id, eckeysForAllDelegates);
|
|
832
|
-
}
|
|
833
|
-
extractDelegationsSFKsAndEncryptionSKs(ety, ownerId) {
|
|
834
|
-
const delegationsSfksOwnerPromise = this.extractDelegationsSFKs(ety, ownerId).then((xks) => xks.extractedKeys); //Will climb up hierarchy
|
|
835
|
-
const encryptionKeysOwnerPromise = this.extractEncryptionsSKs(ety, ownerId).then((xks) => xks.extractedKeys); //Will climb up hierarchy
|
|
836
|
-
return Promise.all([delegationsSfksOwnerPromise, encryptionKeysOwnerPromise]);
|
|
837
|
-
}
|
|
838
|
-
/**
|
|
839
|
-
* Get decrypted generic secret IDs (secretIdSPKs, parentIds, secretIdEKs) from generic delegations (SPKs, CFKs, EKs)
|
|
840
|
-
* 1. Get HealthCareParty from it's Id.
|
|
841
|
-
* 2. Decrypt the keys of the given HCP.
|
|
842
|
-
* 3. Decrypt the parent's key if it has parent.
|
|
843
|
-
* 4. Return the decrypted key corresponding to the Health Care Party.
|
|
844
|
-
* @param hcpartyId : the id of the delegate HcP (including its parents) for which to decrypt `extractedKeys`
|
|
845
|
-
* @param objectId : the id of the object/document of which delegations to decrypt ; used just to log to console a message (Cryptographic mistake) in case the object id inside SPK, CFK, EK is different from this one
|
|
846
|
-
* @param delegations : generic delegations (can be SPKs, CFKs, EKs) for all delegates from where to extract `extractedKeys`
|
|
847
|
-
* @returns - **extractedKeys** array containing secret IDs from decrypted generic delegations, from both HCP with given `hcpartyId` and its parents; can contain duplicates
|
|
848
|
-
* - **hcpartyId** the given `hcpartyId` OR, if a parent exist, the HCP id of the top parent in the hierarchy (even if that parent has no delegations)
|
|
441
|
+
* @deprecated (light) You should use:
|
|
442
|
+
* - {@link IccHelementXApi.decryptPatientIdOf}, {@link IccDocumentXApi.decryptMessageIdOf}, ... to get the crypted foreign keys.
|
|
443
|
+
* - {@link IccDataOwnerXApi.getCurrentDataOwnerHierarchyIds} to get the full hierarchy for the current data owner (cached). The first element is
|
|
444
|
+
* the id of the topmost parent, while the last is the current data owner.
|
|
445
|
+
* Note that the behaviour of this method has some subtle changes compared to the past:
|
|
446
|
+
* - throws an error if the provided hcpartyId is not part of the current data owner hierarchy.
|
|
447
|
+
* - does not provide any guarantees on the ordering of the extracted keys
|
|
448
|
+
* - deduplicates extracted keys
|
|
849
449
|
*/
|
|
850
|
-
|
|
851
|
-
extractKeysHierarchyFromDelegationLikes(
|
|
852
|
-
//TODO suggested name: getSecretIdsOfHcpAndParentsFromGenericDelegations
|
|
853
|
-
hcpartyId, objectId, delegations) {
|
|
854
|
-
var _a;
|
|
450
|
+
extractCryptedFKs(document, hcpartyId) {
|
|
855
451
|
return __awaiter(this, void 0, void 0, function* () {
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
452
|
+
if (!document || !document.cryptedForeignKeys) {
|
|
453
|
+
return Promise.resolve({ extractedKeys: [], hcpartyId: hcpartyId });
|
|
454
|
+
}
|
|
455
|
+
const cfksForAllDelegates = document.cryptedForeignKeys;
|
|
456
|
+
if (!cfksForAllDelegates || !Object.keys(cfksForAllDelegates).length) {
|
|
457
|
+
console.log(`There is no cryptedForeignKeys in document (${document.id})`);
|
|
458
|
+
return Promise.resolve({ extractedKeys: [], hcpartyId: hcpartyId });
|
|
862
459
|
}
|
|
863
|
-
return
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
]
|
|
868
|
-
: [{ extractedKeys: extractedKeys, hcpartyId: hcpartyId }];
|
|
460
|
+
return {
|
|
461
|
+
extractedKeys: yield this.entities.owningEntityIdsOf(document, hcpartyId),
|
|
462
|
+
hcpartyId: (yield this.dataOwnerApi.getCurrentDataOwnerHierarchyIds())[0],
|
|
463
|
+
};
|
|
869
464
|
});
|
|
870
465
|
}
|
|
871
466
|
/**
|
|
872
|
-
*
|
|
873
|
-
*
|
|
874
|
-
*
|
|
875
|
-
*
|
|
876
|
-
*
|
|
877
|
-
* @param dataOwnerId : the id of the delegate data owner (including its parents) for which to decrypt `extractedKeys`
|
|
878
|
-
* @param objectId : the id of the object/document of which delegations to decrypt ; used just to log to console a message (Cryptographic mistake) in case the object id inside SPK, CFK, EK is different from this one
|
|
879
|
-
* @param delegations : generic delegations (can be SPKs, CFKs, EKs) for all delegates from where to extract `extractedKeys`
|
|
880
|
-
* @returns - **extractedKeys** array containing secret IDs from decrypted generic delegations, from both data owner with given `dataOwnerId` and its parents; can contain duplicates
|
|
881
|
-
* - **dataOwnerId** the given `dataOwnerId` OR, if a parent exist, the data owner id of the top parent in the hierarchy (even if that parent has no delegations)
|
|
467
|
+
* @deprecated You should use the extended apis methods to encrypt/decrypt entities or their attachments
|
|
468
|
+
* Note that the behaviour of this method has some subtle changes compared to the past:
|
|
469
|
+
* - throws an error if the provided hcpartyId is not part of the current data owner hierarchy.
|
|
470
|
+
* - does not provide any guarantees on the ordering of the extracted keys
|
|
471
|
+
* - deduplicates extracted keys
|
|
882
472
|
*/
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
decryptedAndImportedAesHcPartyKeys.forEach((k) => {
|
|
892
|
-
var _a;
|
|
893
|
-
;
|
|
894
|
-
((_a = collatedAesKeysFromDelegatorToHcpartyId[k.delegatorId]) !== null && _a !== void 0 ? _a : (collatedAesKeysFromDelegatorToHcpartyId[k.delegatorId] = [])).push(k);
|
|
895
|
-
});
|
|
896
|
-
return this.decryptKeyInDelegationLikes(delegations[dataOwnerId], collatedAesKeysFromDelegatorToHcpartyId, objectId);
|
|
897
|
-
})
|
|
898
|
-
: Promise.resolve([]))
|
|
899
|
-
.then((extractedKeys) => __awaiter(this, void 0, void 0, function* () {
|
|
900
|
-
if (extractedKeys.length == 0) {
|
|
901
|
-
return yield this._extractDelegationsKeysUsingDataOwnerDelegateAesExchangeKeys(hcp, delegations, objectId);
|
|
473
|
+
extractEncryptionsSKs(document, hcpartyId) {
|
|
474
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
475
|
+
if (!document.encryptionKeys) {
|
|
476
|
+
return Promise.resolve({ extractedKeys: [], hcpartyId: hcpartyId });
|
|
477
|
+
}
|
|
478
|
+
const eckeysForAllDelegates = document.encryptionKeys;
|
|
479
|
+
if (!eckeysForAllDelegates || !Object.keys(eckeysForAllDelegates).length) {
|
|
480
|
+
return Promise.resolve({ extractedKeys: [], hcpartyId: hcpartyId });
|
|
902
481
|
}
|
|
903
|
-
return
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
const parentExtractedKeys = hcp.parentId
|
|
908
|
-
? yield this.extractKeysFromDelegationsForHcpHierarchy(hcp.parentId, objectId, delegations)
|
|
909
|
-
: { extractedKeys: [], hcpartyId: undefined };
|
|
910
|
-
return { extractedKeys: extractedKeys.concat(parentExtractedKeys.extractedKeys), hcpartyId: (_a = parentExtractedKeys.hcpartyId) !== null && _a !== void 0 ? _a : dataOwnerId };
|
|
911
|
-
})))
|
|
912
|
-
.catch((e) => {
|
|
913
|
-
console.error(`DataOwner with id ${dataOwnerId} cannot be resolved`);
|
|
914
|
-
throw e;
|
|
482
|
+
return {
|
|
483
|
+
extractedKeys: yield this.entities.encryptionKeysOf(document, hcpartyId),
|
|
484
|
+
hcpartyId: (yield this.dataOwnerApi.getCurrentDataOwnerHierarchyIds())[0],
|
|
485
|
+
};
|
|
915
486
|
});
|
|
916
487
|
}
|
|
917
488
|
/**
|
|
918
|
-
*
|
|
919
|
-
*
|
|
920
|
-
*
|
|
921
|
-
* @param dataOwner The current data owner from whom we want to decrypt the aesExchangeKeys
|
|
922
|
-
* @param delegations The object delegations to decrypt
|
|
923
|
-
* @param objectId The object to decrypt id
|
|
924
|
-
* @private
|
|
489
|
+
* @deprecated You should not use this method anymore, depending on what you were passing as {@link delegations} you should replace this method as
|
|
490
|
+
* explained in {@link extractEncryptionsSKs} (if you were passing encryptionKeys), {@link extractCryptedFKs} (cryptedForeignKeys), or
|
|
491
|
+
* {@link extractDelegationsSFKs} (delegations).
|
|
925
492
|
*/
|
|
926
|
-
|
|
493
|
+
extractKeysFromDelegationsForHcpHierarchy(dataOwnerId, objectId, delegations) {
|
|
927
494
|
return __awaiter(this, void 0, void 0, function* () {
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
// We get AES Keys only from delegates of keys we don't currently have
|
|
933
|
-
// Otherwise, decrypted AES Keys would have previously worked
|
|
934
|
-
Object.entries(aesForPub).forEach(([delegateId, aesKeys]) => {
|
|
935
|
-
if (delegateId != dataOwner.id) {
|
|
936
|
-
const aesAcc = {};
|
|
937
|
-
Object.entries(aesKeys)
|
|
938
|
-
.filter(([encrPubKey]) => dataOwnerPubKeys.some((pubKey) => pubKey.slice(-32) == encrPubKey))
|
|
939
|
-
.forEach(([pubKeyFingerprint, aesEncr]) => {
|
|
940
|
-
aesAcc[pubKeyFingerprint] = aesEncr;
|
|
941
|
-
});
|
|
942
|
-
if (acc[delegateId] == undefined) {
|
|
943
|
-
acc[delegateId] = {};
|
|
944
|
-
}
|
|
945
|
-
acc[delegateId][pub] = aesAcc;
|
|
946
|
-
}
|
|
947
|
-
});
|
|
948
|
-
}
|
|
949
|
-
return acc;
|
|
950
|
-
});
|
|
951
|
-
const decryptedAndImportedAesHcPartyKeys = yield (0, utils_1.foldAsync)(Object.entries(keysToDecrypt), [], (delKeysAcc, [delegateId, keysForDelegate]) => __awaiter(this, void 0, void 0, function* () {
|
|
952
|
-
try {
|
|
953
|
-
delKeysAcc.push(yield this.decryptAnyAesExchangeKeyForOwner(keysForDelegate, dataOwner.id, dataOwner.id, delegateId, dataOwnerPubKeys));
|
|
954
|
-
}
|
|
955
|
-
catch (e) {
|
|
956
|
-
console.log(`Could not decrypt aesExchangeKeys for delegate ${delegateId}`);
|
|
957
|
-
}
|
|
958
|
-
return delKeysAcc;
|
|
959
|
-
}));
|
|
960
|
-
const collatedAesKeysFromDelegatorToHcpartyId = decryptedAndImportedAesHcPartyKeys.reduce((map, k) => { var _a; return (Object.assign(Object.assign({}, map), { [k.delegatorId]: ((_a = map[k.delegatorId]) !== null && _a !== void 0 ? _a : []).concat([k]) })); }, {});
|
|
961
|
-
const delegateIdsWithNewExtractedAesKeys = Object.keys(keysToDecrypt);
|
|
962
|
-
const delegationsToDecrypt = (0, utils_1.fold)(Object.entries(delegations), [], (acc, [delegateId, del]) => {
|
|
963
|
-
if (delegateIdsWithNewExtractedAesKeys.find((id) => id == delegateId) != undefined) {
|
|
964
|
-
acc.push(...delegations[delegateId]);
|
|
965
|
-
}
|
|
966
|
-
return acc;
|
|
967
|
-
});
|
|
968
|
-
return this.decryptKeyInDelegationLikes(delegationsToDecrypt, collatedAesKeysFromDelegatorToHcpartyId, objectId);
|
|
495
|
+
return {
|
|
496
|
+
extractedKeys: yield this.entities.extractMergedHierarchyFromDelegationAndOwner(delegations, dataOwnerId, (x) => !!x, () => Promise.resolve(true)),
|
|
497
|
+
hcpartyId: (yield this.dataOwnerApi.getCurrentDataOwnerHierarchyIds())[0],
|
|
498
|
+
};
|
|
969
499
|
});
|
|
970
500
|
}
|
|
971
501
|
/**
|
|
972
|
-
*
|
|
973
|
-
*
|
|
974
|
-
*
|
|
975
|
-
*
|
|
976
|
-
* @
|
|
977
|
-
* @param aesKeys : **key** HcP ids of delegators/owners in the `delegationsArray`, each with its own decryptedHcPartyKey
|
|
978
|
-
* @param masterId : is the object id to which the generic delegation belongs to
|
|
979
|
-
* - used only to check whether the object.id matches the one stored in the decrypted generic delegation item
|
|
980
|
-
* - even if there's no match, the secret ID is kept as a valid result (and a message logged to console)
|
|
981
|
-
* @returns array of generic secret IDs (secretIdSPK, parentId, secretIdEK)
|
|
502
|
+
* @deprecated you should not need this method anymore: the new API will automatically load on startup all keys available through the key storage
|
|
503
|
+
* facade and/or recoverable through transfer keys or shamir split. If you were using this method to load a key recovered through other means you
|
|
504
|
+
* will have to do so in your implementation of CryptoStrategies.
|
|
505
|
+
* You can convert the private key pkcs8 array to a jwk key using {@link pkcs8ToJwk} then you can extract the full key pair using
|
|
506
|
+
* {@link keyPairFromPrivateKeyJwk}.
|
|
982
507
|
*/
|
|
983
|
-
decryptKeyInDelegationLikes(
|
|
984
|
-
//TODO: suggested name: getSecretIdsFromGenericDelegations
|
|
985
|
-
delegationsArray, aesKeysForDataOwnerId, masterId) {
|
|
986
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
987
|
-
const decryptPromises = delegationsArray.map((genericDelegationItem) => __awaiter(this, void 0, void 0, function* () {
|
|
988
|
-
const aesKeys = aesKeysForDataOwnerId[genericDelegationItem.owner];
|
|
989
|
-
if (aesKeys === null || aesKeys === void 0 ? void 0 : aesKeys.length) {
|
|
990
|
-
return aesKeys.reduce((acc, aesKey) => __awaiter(this, void 0, void 0, function* () {
|
|
991
|
-
const accValue = yield acc;
|
|
992
|
-
if (accValue) {
|
|
993
|
-
return accValue;
|
|
994
|
-
}
|
|
995
|
-
try {
|
|
996
|
-
const decryptedGenericDelegationKey = yield this._AES.decrypt(aesKey.key, (0, binary_utils_1.hex2ua)(genericDelegationItem.key), aesKey.rawKey);
|
|
997
|
-
const results = (0, binary_utils_1.ua2string)(decryptedGenericDelegationKey).split(':');
|
|
998
|
-
const objectId = results[0]; //must be the ID of the object, for checksum
|
|
999
|
-
const genericSecretId = results[1];
|
|
1000
|
-
const details = 'object ID: ' + masterId + '; generic delegation from ' + genericDelegationItem.owner + ' to ' + genericDelegationItem.delegatedTo;
|
|
1001
|
-
if (!objectId)
|
|
1002
|
-
console.warn('Object id is empty; ' + details);
|
|
1003
|
-
if (!genericSecretId)
|
|
1004
|
-
console.warn('Secret id is empty; ' + details);
|
|
1005
|
-
if (objectId !== masterId) {
|
|
1006
|
-
/*console.log(
|
|
1007
|
-
"Cryptographic mistake: object ID is not equal to the expected concatenated id within decrypted generic delegation. This may happen when patients have been merged; " +
|
|
1008
|
-
details
|
|
1009
|
-
)*/
|
|
1010
|
-
}
|
|
1011
|
-
return genericSecretId;
|
|
1012
|
-
}
|
|
1013
|
-
catch (err) {
|
|
1014
|
-
console.log(`Could not decrypt generic delegation in object with ID: ${masterId} from ${genericDelegationItem.owner} to ${genericDelegationItem.delegatedTo}: ${err}`);
|
|
1015
|
-
return undefined;
|
|
1016
|
-
}
|
|
1017
|
-
}), Promise.resolve(undefined));
|
|
1018
|
-
}
|
|
1019
|
-
else {
|
|
1020
|
-
console.log(`Could not find aes key for object with ID: ${masterId}`);
|
|
1021
|
-
}
|
|
1022
|
-
}));
|
|
1023
|
-
return Promise.all(decryptPromises).then((genericSecretId) => genericSecretId.filter((id) => !!id));
|
|
1024
|
-
});
|
|
1025
|
-
}
|
|
1026
|
-
getPublicKeyFromPrivateKey(privateKey, dataOwner) {
|
|
1027
|
-
var _a;
|
|
1028
|
-
if (!privateKey.n || !privateKey.e) {
|
|
1029
|
-
throw new Error('No public key can be deduced from incomplete private key');
|
|
1030
|
-
}
|
|
1031
|
-
const publicKeyFromPrivateKey = (0, utils_1.jwk2spki)(privateKey);
|
|
1032
|
-
const publicKeys = [dataOwner.publicKey].concat(Object.keys((_a = dataOwner.aesExchangeKeys) !== null && _a !== void 0 ? _a : {})).filter((x) => !!x);
|
|
1033
|
-
if (!publicKeys.length) {
|
|
1034
|
-
throw new Error('No public key has been defined for hcp');
|
|
1035
|
-
}
|
|
1036
|
-
const publicKey = publicKeys.find((it) => it === publicKeyFromPrivateKey);
|
|
1037
|
-
if (!publicKey) {
|
|
1038
|
-
throw new Error('No public key can be found for this private key');
|
|
1039
|
-
}
|
|
1040
|
-
return publicKey;
|
|
1041
|
-
}
|
|
1042
508
|
loadKeyPairsAsTextInBrowserLocalStorage(healthcarePartyId, privateKey) {
|
|
1043
509
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1044
|
-
|
|
1045
|
-
const privateKeyInJwk = (0, utils_1.pkcs8ToJwk)(privateKey);
|
|
1046
|
-
const publicKey = this.getPublicKeyFromPrivateKey(privateKeyInJwk, dataOwner);
|
|
1047
|
-
const keyPair = yield this._RSA.importKeyPair('jwk', privateKeyInJwk, 'jwk', (0, utils_1.spkiToJwk)((0, binary_utils_1.hex2ua)(publicKey)));
|
|
1048
|
-
this.rsaKeyPairs[publicKey.slice(-32)] = keyPair;
|
|
1049
|
-
const exportedKeyPair = yield this._RSA.exportKeys(keyPair, 'jwk', 'jwk');
|
|
1050
|
-
return yield this._keyStorage.storeKeyPair(`${this.rsaLocalStoreIdPrefix}${healthcarePartyId}.${publicKey.slice(-32)}`, exportedKeyPair);
|
|
510
|
+
yield this.cacheKeyPair((0, utils_1.keyPairFromPrivateKeyJwk)((0, utils_1.pkcs8ToJwk)(privateKey)));
|
|
1051
511
|
});
|
|
1052
512
|
}
|
|
513
|
+
/**
|
|
514
|
+
* @deprecated you should not need this method anymore: the new API will automatically load on startup all keys available through the key storage
|
|
515
|
+
* facade and/or recoverable through transfer keys or shamir split. If you were using this method to load a key recovered through other means you
|
|
516
|
+
* will have to do so in your implementation of CryptoStrategies.
|
|
517
|
+
* You can extract the full key pair using {@link keyPairFromPrivateKeyJwk}.
|
|
518
|
+
*/
|
|
1053
519
|
loadKeyPairsAsJwkInBrowserLocalStorage(healthcarePartyId, privateKey) {
|
|
1054
520
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1055
|
-
|
|
1056
|
-
if ((!privateKey.n || !privateKey.e) && dataOwner.publicKey) {
|
|
1057
|
-
//Fallback on default publicKey
|
|
1058
|
-
console.warn('An incomplete key has been completed using the default public key of the data owner');
|
|
1059
|
-
const publicKeyInJwk = (0, utils_1.spkiToJwk)((0, binary_utils_1.hex2ua)(dataOwner.publicKey));
|
|
1060
|
-
privateKey.n = publicKeyInJwk.n;
|
|
1061
|
-
privateKey.e = publicKeyInJwk.e;
|
|
1062
|
-
}
|
|
1063
|
-
const publicKey = this.getPublicKeyFromPrivateKey(privateKey, dataOwner);
|
|
1064
|
-
const keyPair = yield this._RSA.importKeyPair('jwk', privateKey, 'jwk', (0, utils_1.spkiToJwk)((0, binary_utils_1.hex2ua)(publicKey)));
|
|
1065
|
-
this.rsaKeyPairs[publicKey.slice(-32)] = keyPair;
|
|
1066
|
-
const exportedKeyPair = yield this._RSA.exportKeys(keyPair, 'jwk', 'jwk');
|
|
1067
|
-
return yield this._keyStorage.storeKeyPair(`${this.rsaLocalStoreIdPrefix}${healthcarePartyId}.${publicKey.slice(-32)}`, exportedKeyPair);
|
|
521
|
+
yield this.cacheKeyPair((0, utils_1.keyPairFromPrivateKeyJwk)(privateKey));
|
|
1068
522
|
});
|
|
1069
523
|
}
|
|
1070
|
-
|
|
524
|
+
/**
|
|
525
|
+
* @deprecated you should not need this method anymore: the new API will automatically load on startup all keys available through the key storage
|
|
526
|
+
* facade and/or recoverable through transfer keys or shamir split. If you were using this method to load a key recovered through other means you
|
|
527
|
+
* will have to do so in your implementation of CryptoStrategies.
|
|
528
|
+
*/
|
|
1071
529
|
loadKeyPairsInBrowserLocalStorage(healthcarePartyId, file) {
|
|
1072
530
|
const fr = new FileReader();
|
|
1073
531
|
return new Promise((resolve, reject) => {
|
|
1074
532
|
fr.onerror = reject;
|
|
1075
533
|
fr.onabort = reject;
|
|
1076
534
|
fr.onload = (e) => {
|
|
1077
|
-
//TODO remove any
|
|
1078
535
|
const privateKey = e.target.result;
|
|
1079
536
|
this.loadKeyPairsAsTextInBrowserLocalStorage(healthcarePartyId, (0, binary_utils_1.hex2ua)(privateKey))
|
|
1080
537
|
.then(() => resolve())
|
|
@@ -1084,13 +541,22 @@ class IccCryptoXApi {
|
|
|
1084
541
|
});
|
|
1085
542
|
}
|
|
1086
543
|
// noinspection JSUnusedGlobalSymbols
|
|
544
|
+
/**
|
|
545
|
+
* @deprecated keychains are not part of iCure's api: this method will be removed.
|
|
546
|
+
*/
|
|
1087
547
|
saveKeychainInBrowserLocalStorage(id, keychain) {
|
|
1088
548
|
this._storage.setItem(this.keychainLocalStoreIdPrefix + id, (0, binary_utils_1.b2a)(new Uint8Array(keychain).reduce((data, byte) => data + String.fromCharCode(byte), '')));
|
|
1089
549
|
}
|
|
550
|
+
/**
|
|
551
|
+
* @deprecated keychains are not part of iCure's api: this method will be removed.
|
|
552
|
+
*/
|
|
1090
553
|
saveKeychainInBrowserLocalStorageAsBase64(id, keyChainB64) {
|
|
1091
554
|
this._storage.setItem(this.keychainLocalStoreIdPrefix + id, keyChainB64);
|
|
1092
555
|
}
|
|
1093
556
|
// noinspection JSUnusedGlobalSymbols
|
|
557
|
+
/**
|
|
558
|
+
* @deprecated keychains are not part of iCure's api: this method will be removed.
|
|
559
|
+
*/
|
|
1094
560
|
saveKeychainValidityDateInBrowserLocalStorage(id, date) {
|
|
1095
561
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1096
562
|
if (!id)
|
|
@@ -1104,6 +570,8 @@ class IccCryptoXApi {
|
|
|
1104
570
|
});
|
|
1105
571
|
}
|
|
1106
572
|
/**
|
|
573
|
+
* @deprecated keychains are not part of iCure's api: this method will be removed.
|
|
574
|
+
*
|
|
1107
575
|
* Populate the HCP.options dict with an encrypted eHealth certificate and unencryped expiry date.
|
|
1108
576
|
* Any potentially unencrypted certificates will be pruned from the HCP.
|
|
1109
577
|
* @param hcpId Id of the hcp to modify
|
|
@@ -1144,6 +612,9 @@ class IccCryptoXApi {
|
|
|
1144
612
|
}));
|
|
1145
613
|
});
|
|
1146
614
|
}
|
|
615
|
+
/**
|
|
616
|
+
* @deprecated keychains are not part of iCure's api: this method will be removed.
|
|
617
|
+
*/
|
|
1147
618
|
importKeychainInBrowserFromHCP(hcpId) {
|
|
1148
619
|
return this.hcpartyBaseApi.getHealthcareParty(hcpId).then((hcp) => __awaiter(this, void 0, void 0, function* () {
|
|
1149
620
|
let crtCryp = null;
|
|
@@ -1184,6 +655,8 @@ class IccCryptoXApi {
|
|
|
1184
655
|
}));
|
|
1185
656
|
}
|
|
1186
657
|
/**
|
|
658
|
+
* @deprecated e-health certificates and keychains are not part of iCure's api: this method will be removed.
|
|
659
|
+
*
|
|
1187
660
|
* Synchronizes the eHealth certificate from the database into the LocalStorage, returning information on the presence
|
|
1188
661
|
* of certificate data in either place.
|
|
1189
662
|
*
|
|
@@ -1205,13 +678,21 @@ class IccCryptoXApi {
|
|
|
1205
678
|
}
|
|
1206
679
|
});
|
|
1207
680
|
}
|
|
681
|
+
/**
|
|
682
|
+
* @deprecated keychains are not part of iCure's api: this method will be removed.
|
|
683
|
+
*/
|
|
1208
684
|
getKeychainInBrowserLocalStorageAsBase64(id) {
|
|
1209
685
|
return this._storage.getItem(this.keychainLocalStoreIdPrefix + id);
|
|
1210
686
|
}
|
|
687
|
+
/**
|
|
688
|
+
* @deprecated keychains are not part of iCure's api: this method will be removed.
|
|
689
|
+
*/
|
|
1211
690
|
getKeychainValidityDateInBrowserLocalStorage(id) {
|
|
1212
691
|
return this._storage.getItem(this.keychainValidityDateLocalStoreIdPrefix + id);
|
|
1213
692
|
}
|
|
1214
|
-
|
|
693
|
+
/**
|
|
694
|
+
* @deprecated keychains are not part of iCure's api: this method will be removed.
|
|
695
|
+
*/
|
|
1215
696
|
loadKeychainFromBrowserLocalStorage(id) {
|
|
1216
697
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1217
698
|
const lsItem = yield this._storage.getItem(this.keychainLocalStoreIdPrefix + id);
|
|
@@ -1219,410 +700,62 @@ class IccCryptoXApi {
|
|
|
1219
700
|
});
|
|
1220
701
|
}
|
|
1221
702
|
/**
|
|
1222
|
-
*
|
|
1223
|
-
*
|
|
1224
|
-
* @
|
|
1225
|
-
* @param publicKeyFingerPrint the 32 last characters of public key this private key is associated with
|
|
1226
|
-
* @returns {Object} it is in JWK - not imported
|
|
703
|
+
* @deprecated you should not need this method anymore to deal with the encryption of iCure entities because everything related to entities
|
|
704
|
+
* encryption should be done through the entity-specific extended api.
|
|
705
|
+
* If instead you were using the method for other reasons check {@link getCachedRsaKeyPairForFingerprint} to get an idea of possible replacements.
|
|
1227
706
|
*/
|
|
1228
707
|
loadKeyPairNotImported(id, publicKeyFingerPrint) {
|
|
1229
708
|
var _a;
|
|
1230
709
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
if (!item) {
|
|
1236
|
-
console.warn(`No key can be found in local storage for id ${id} and publicKeyFingerPrint ${publicKeyFingerPrint}`);
|
|
1237
|
-
}
|
|
1238
|
-
return item;
|
|
1239
|
-
});
|
|
1240
|
-
}
|
|
1241
|
-
/**
|
|
1242
|
-
* Loads and imports the RSA key pair (hcparty) from local storage
|
|
1243
|
-
*
|
|
1244
|
-
* @param id doc id - hcPartyId
|
|
1245
|
-
* @returns {Promise} -> {CryptoKey} - imported RSA
|
|
1246
|
-
*/
|
|
1247
|
-
loadKeyPairImported(id) {
|
|
1248
|
-
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
1249
|
-
try {
|
|
1250
|
-
const jwkKeyPair = yield this._keyStorage.getKeypair(this.rsaLocalStoreIdPrefix + id);
|
|
1251
|
-
if (jwkKeyPair !== undefined) {
|
|
1252
|
-
if (jwkKeyPair.publicKey && jwkKeyPair.privateKey) {
|
|
1253
|
-
this._RSA.importKeyPair('jwk', jwkKeyPair.privateKey, 'jwk', jwkKeyPair.publicKey).then(resolve, (err) => {
|
|
1254
|
-
console.error('Error in RSA.importKeyPair: ' + err);
|
|
1255
|
-
reject(err);
|
|
1256
|
-
});
|
|
1257
|
-
}
|
|
1258
|
-
else {
|
|
1259
|
-
const message = 'Error in RSA.importKeyPair: Invalid key';
|
|
1260
|
-
console.error(message);
|
|
1261
|
-
reject(Error(message));
|
|
1262
|
-
}
|
|
1263
|
-
}
|
|
1264
|
-
else {
|
|
1265
|
-
const message = 'Error in RSA.importKeyPair: Missing key';
|
|
1266
|
-
console.error(message);
|
|
1267
|
-
reject(Error(message));
|
|
710
|
+
if (publicKeyFingerPrint) {
|
|
711
|
+
const cached = (_a = this.keyManager.getKeyPairForFingerprint(publicKeyFingerPrint)) === null || _a === void 0 ? void 0 : _a.pair;
|
|
712
|
+
if (cached) {
|
|
713
|
+
return this.primitives.RSA.exportKeys(cached, 'jwk', 'jwk');
|
|
1268
714
|
}
|
|
1269
715
|
}
|
|
1270
|
-
|
|
1271
|
-
|
|
716
|
+
else {
|
|
717
|
+
const defaultKey = yield this._keyStorage.getKeypair(this.rsaLocalStoreIdPrefix + id);
|
|
718
|
+
if (defaultKey)
|
|
719
|
+
return defaultKey;
|
|
1272
720
|
}
|
|
1273
|
-
|
|
721
|
+
console.warn(`No key can be found in local storage for id ${id} and publicKeyFingerPrint ${publicKeyFingerPrint}`);
|
|
722
|
+
});
|
|
1274
723
|
}
|
|
1275
724
|
/**
|
|
1276
|
-
*
|
|
1277
|
-
* AES key using the new user public key.
|
|
1278
|
-
*
|
|
1279
|
-
* @param delegateUser Delegate Data Owner User, in charge of giving access back to the person who previously gave him some access
|
|
1280
|
-
* @param ownerId Id of the data owner to which we would like to give access back
|
|
1281
|
-
* @param ownerNewPublicKey New Data Owner Public Key we want to use to re-encrypt previously created AES key
|
|
1282
|
-
*
|
|
1283
|
-
* @return The DataOwner, updated by the delegateUser to add the new encrypted AES Key using the new provided public key
|
|
725
|
+
* @deprecated use {@link IccIcureMaintenanceXApi.applyKeyPairUpdate} instead.
|
|
1284
726
|
*/
|
|
1285
727
|
giveAccessBackTo(delegateUser, ownerId, ownerNewPublicKey) {
|
|
1286
|
-
var _a, _b, _c, _d;
|
|
1287
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1288
|
-
const delegateId = (_b = (_a = delegateUser.healthcarePartyId) !== null && _a !== void 0 ? _a : delegateUser.patientId) !== null && _b !== void 0 ? _b : delegateUser.deviceId;
|
|
1289
|
-
if (!delegateId) {
|
|
1290
|
-
throw new Error(`DelegateUser ${delegateUser.id} must be a data Owner`);
|
|
1291
|
-
}
|
|
1292
|
-
const delegatePublicKeys = yield this.getPublicKeys();
|
|
1293
|
-
const newPubKeyCryptoKey = yield this._RSA.importKey('jwk', (0, utils_1.spkiToJwk)((0, binary_utils_1.hex2ua)(ownerNewPublicKey)), ['encrypt']);
|
|
1294
|
-
const dataOwnerToUpdate = yield this.getDataOwner(ownerId);
|
|
1295
|
-
const newAesExchangeKeys = yield (0, utils_1.foldAsync)(Object.entries((_c = dataOwnerToUpdate.dataOwner.aesExchangeKeys) !== null && _c !== void 0 ? _c : {}), (_d = dataOwnerToUpdate.dataOwner.aesExchangeKeys) !== null && _d !== void 0 ? _d : {}, (pubAcc, [pubKey, newAesExcKeys]) => __awaiter(this, void 0, void 0, function* () {
|
|
1296
|
-
var _e;
|
|
1297
|
-
const existingKeys = (_e = pubAcc[pubKey]) !== null && _e !== void 0 ? _e : {};
|
|
1298
|
-
pubAcc[pubKey] = yield (0, utils_1.foldAsync)(Object.entries(newAesExcKeys), existingKeys, (delAcc, [delId, delKeys]) => __awaiter(this, void 0, void 0, function* () {
|
|
1299
|
-
if (delId == delegateId && pubKey != ownerNewPublicKey) {
|
|
1300
|
-
// Add the AES Key encrypted with the new public key in the aesExchangeKeys
|
|
1301
|
-
try {
|
|
1302
|
-
// First, we decrypt it using the delegate RSA Public Key
|
|
1303
|
-
const encrAesKeyInfo = yield this.decryptHcPartyKey(delegateId, dataOwnerToUpdate.dataOwner.id, delegateId, pubKey, delKeys, delegatePublicKeys).then((delegatorAndKeys) => __awaiter(this, void 0, void 0, function* () {
|
|
1304
|
-
// Then, we encrypt it using the owner new RSA Public Key (provided in argument)
|
|
1305
|
-
return yield this.encryptAesKeyOnlyForProvidedKeys(delegatorAndKeys === null || delegatorAndKeys === void 0 ? void 0 : delegatorAndKeys.rawKey, dataOwnerToUpdate, [newPubKeyCryptoKey])
|
|
1306
|
-
.then((encrAes) => Object.values(encrAes)[0])
|
|
1307
|
-
.then((encrAesInfo) => {
|
|
1308
|
-
return { pubKeyUsedToEncryptAes: Object.keys(encrAesInfo)[0], encryptedAes: Object.values(encrAesInfo)[0] };
|
|
1309
|
-
});
|
|
1310
|
-
}));
|
|
1311
|
-
delKeys[encrAesKeyInfo.pubKeyUsedToEncryptAes] = encrAesKeyInfo.encryptedAes;
|
|
1312
|
-
}
|
|
1313
|
-
catch (e) {
|
|
1314
|
-
console.log(`${delegateId} could not re-encrypt AES Key of ${dataOwnerToUpdate.dataOwner.id}`);
|
|
1315
|
-
}
|
|
1316
|
-
finally {
|
|
1317
|
-
delAcc[delId] = delKeys;
|
|
1318
|
-
}
|
|
1319
|
-
}
|
|
1320
|
-
else {
|
|
1321
|
-
// Otherwise, we don't transform the aesExchangeKeys for this delegate
|
|
1322
|
-
delAcc[delId] = delKeys;
|
|
1323
|
-
}
|
|
1324
|
-
return delAcc;
|
|
1325
|
-
}));
|
|
1326
|
-
return pubAcc;
|
|
1327
|
-
}));
|
|
1328
|
-
// After adding the potential new aesExchangeKeys, we save the updated data owner
|
|
1329
|
-
return this._saveDataOwner({
|
|
1330
|
-
type: dataOwnerToUpdate.type,
|
|
1331
|
-
dataOwner: Object.assign(Object.assign({}, dataOwnerToUpdate.dataOwner), { aesExchangeKeys: newAesExchangeKeys }),
|
|
1332
|
-
});
|
|
1333
|
-
});
|
|
1334
|
-
}
|
|
1335
|
-
addNewKeyPairForOwnerId(maintenanceTasksApi, user, ownerId, generateTransferKey = true, sendMaintenanceTasks = true) {
|
|
1336
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1337
|
-
return this.addNewKeyPairForOwner(maintenanceTasksApi, user, yield this.getDataOwner(ownerId), generateTransferKey, sendMaintenanceTasks);
|
|
1338
|
-
});
|
|
1339
|
-
}
|
|
1340
|
-
addNewKeyPairForOwner(maintenanceTasksApi, user, cdo, generateTransferKey = true, sendMaintenanceTasks = true) {
|
|
1341
728
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1342
|
-
|
|
1343
|
-
return this.
|
|
729
|
+
yield this.exchangeKeys.base.giveAccessBackTo(ownerId, ownerNewPublicKey, this.userKeysManager.getDecryptionKeys());
|
|
730
|
+
return this.dataOwnerApi.getDataOwner(ownerId);
|
|
1344
731
|
});
|
|
1345
732
|
}
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
const importedPublicKey = yield this._RSA.importKey('spki', (0, binary_utils_1.hex2ua)(keypair.publicKey), ['encrypt']);
|
|
1355
|
-
return this.addKeyPairForOwner(maintenanceTasksApi, user, cdo, { publicKey: importedPublicKey, privateKey: importedPrivateKey }, generateTransferKey, sendMaintenanceTasks);
|
|
1356
|
-
});
|
|
1357
|
-
}
|
|
1358
|
-
addKeyPairForOwner(maintenanceTasksApi, user, cdo, keypair, generateTransferKey = true, sendMaintenanceTasks = true) {
|
|
1359
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1360
|
-
const publicKeyHex = (0, binary_utils_1.ua2hex)(yield this.RSA.exportKey(keypair.publicKey, 'spki'));
|
|
1361
|
-
const gen = (yield this._AES.generateCryptoKey(true));
|
|
1362
|
-
yield this.cacheKeyPair({
|
|
1363
|
-
publicKey: yield this.RSA.exportKey(keypair.publicKey, 'jwk'),
|
|
1364
|
-
privateKey: yield this.RSA.exportKey(keypair.privateKey, 'jwk'),
|
|
1365
|
-
});
|
|
1366
|
-
const { type: ownerType, dataOwner: ownerToUpdate } = yield this.createOrUpdateAesExchangeKeysFor(cdo, gen, {
|
|
1367
|
-
pubKey: keypair.publicKey,
|
|
1368
|
-
privKey: keypair.privateKey,
|
|
1369
|
-
}).then((dataOwnerWithUpdatedAesKeys) => __awaiter(this, void 0, void 0, function* () {
|
|
1370
|
-
return generateTransferKey
|
|
1371
|
-
? yield this.createOrUpdateTransferKeysFor(dataOwnerWithUpdatedAesKeys, gen, { pubKey: keypair.publicKey, privKey: keypair.privateKey })
|
|
1372
|
-
: dataOwnerWithUpdatedAesKeys;
|
|
1373
|
-
}));
|
|
1374
|
-
const modifiedDataOwnerAndType = yield this._saveDataOwner({ type: ownerType, dataOwner: ownerToUpdate });
|
|
1375
|
-
const sentMaintenanceTasks = sendMaintenanceTasks
|
|
1376
|
-
? yield this.sendMaintenanceTasks(maintenanceTasksApi, user, modifiedDataOwnerAndType.dataOwner, keypair.publicKey)
|
|
1377
|
-
: [];
|
|
1378
|
-
return {
|
|
1379
|
-
dataOwner: sentMaintenanceTasks.length
|
|
1380
|
-
? yield this.retrieveDataOwnerInfoAfterPotentialUpdate(modifiedDataOwnerAndType.dataOwner)
|
|
1381
|
-
: modifiedDataOwnerAndType.dataOwner,
|
|
1382
|
-
publicKey: publicKeyHex,
|
|
1383
|
-
privateKey: (0, binary_utils_1.ua2hex)((yield this.RSA.exportKey(keypair.privateKey, 'pkcs8'))),
|
|
1384
|
-
};
|
|
1385
|
-
});
|
|
1386
|
-
}
|
|
1387
|
-
_saveDataOwner(dataOwner) {
|
|
1388
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1389
|
-
const ownerType = dataOwner.type;
|
|
1390
|
-
const ownerToUpdate = dataOwner.dataOwner;
|
|
1391
|
-
return ownerType === 'hcp'
|
|
1392
|
-
? yield (this.dataOwnerCache[ownerToUpdate.id] = this.hcpartyBaseApi
|
|
1393
|
-
.modifyHealthcareParty(ownerToUpdate)
|
|
1394
|
-
.then((x) => ({ type: 'hcp', dataOwner: x })))
|
|
1395
|
-
: ownerType === 'patient'
|
|
1396
|
-
? yield (this.dataOwnerCache[ownerToUpdate.id] = this.patientBaseApi
|
|
1397
|
-
.modifyPatient(ownerToUpdate)
|
|
1398
|
-
.then((x) => ({ type: 'patient', dataOwner: x })))
|
|
1399
|
-
: yield (this.dataOwnerCache[ownerToUpdate.id] = this.deviceBaseApi
|
|
1400
|
-
.updateDevice(ownerToUpdate)
|
|
1401
|
-
.then((x) => ({ type: 'device', dataOwner: x })));
|
|
1402
|
-
});
|
|
1403
|
-
}
|
|
1404
|
-
createOrUpdateAesExchangeKeysFor(cdo, decryptedAesExchangeKey, keyToEncrypt) {
|
|
1405
|
-
var _a;
|
|
1406
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1407
|
-
const publicKeyHex = (0, binary_utils_1.ua2hex)(yield this.RSA.exportKey(keyToEncrypt.pubKey, 'spki'));
|
|
1408
|
-
const existingAesExchangeKeys = (_a = cdo.dataOwner.aesExchangeKeys) !== null && _a !== void 0 ? _a : {};
|
|
1409
|
-
existingAesExchangeKeys[publicKeyHex] = yield this.encryptAesKeyFor(decryptedAesExchangeKey, cdo.dataOwner, keyToEncrypt.pubKey);
|
|
1410
|
-
return { type: cdo.type, dataOwner: Object.assign(Object.assign({}, cdo.dataOwner), { aesExchangeKeys: existingAesExchangeKeys }) };
|
|
1411
|
-
});
|
|
1412
|
-
}
|
|
1413
|
-
createOrUpdateTransferKeysFor(dataOwner, decryptedAesExchangeKey, keyToEncrypt) {
|
|
1414
|
-
var _a;
|
|
1415
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1416
|
-
const pubKeyToEncryptHex = (0, binary_utils_1.ua2hex)(yield this.RSA.exportKey(keyToEncrypt.pubKey, 'spki'));
|
|
1417
|
-
const encryptedKey = (0, binary_utils_1.ua2hex)(yield this._AES.encrypt(yield this._AES.importKey('raw', (0, binary_utils_1.hex2ua)(decryptedAesExchangeKey)), (yield this.RSA.exportKey(keyToEncrypt.privKey, 'pkcs8')), decryptedAesExchangeKey));
|
|
1418
|
-
const dataOwnerExistingPubKeys = Array.from(yield this.getDataOwnerHexPublicKeys(dataOwner.dataOwner));
|
|
1419
|
-
const transferKeys = (0, utils_1.fold)(dataOwnerExistingPubKeys, (_a = dataOwner.dataOwner.transferKeys) !== null && _a !== void 0 ? _a : {}, (pubAcc, pubKeyHex) => {
|
|
1420
|
-
var _a;
|
|
1421
|
-
if (pubKeyHex !== pubKeyToEncryptHex) {
|
|
1422
|
-
const existingKeys = (_a = pubAcc[pubKeyHex]) !== null && _a !== void 0 ? _a : {};
|
|
1423
|
-
existingKeys[pubKeyToEncryptHex] = encryptedKey;
|
|
1424
|
-
pubAcc[pubKeyHex] = existingKeys;
|
|
1425
|
-
}
|
|
1426
|
-
return pubAcc;
|
|
1427
|
-
});
|
|
1428
|
-
return { type: dataOwner.type, dataOwner: Object.assign(Object.assign({}, dataOwner.dataOwner), { transferKeys: transferKeys }) };
|
|
1429
|
-
});
|
|
1430
|
-
}
|
|
1431
|
-
encryptAesKeyFor(aesKey, dataOwner, doNewPublicKey) {
|
|
1432
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1433
|
-
const dataOwnerAllPubKeys = [doNewPublicKey].concat(yield this.getDataOwnerPublicKeys(dataOwner));
|
|
1434
|
-
return this.encryptAesKeyOnlyForProvidedKeys(aesKey, dataOwner, dataOwnerAllPubKeys);
|
|
1435
|
-
});
|
|
1436
|
-
}
|
|
1437
|
-
encryptAesKeyOnlyForProvidedKeys(aesKey, dataOwner, dataOwnerPubKeys) {
|
|
1438
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1439
|
-
const encryptedAesForDataOwner = yield (0, utils_1.foldAsync)(dataOwnerPubKeys, {}, (encrAes, pubKey) => __awaiter(this, void 0, void 0, function* () {
|
|
1440
|
-
encrAes[(0, binary_utils_1.ua2hex)(yield this.RSA.exportKey(pubKey, 'spki')).slice(-32)] = (0, binary_utils_1.ua2hex)(yield this._RSA.encrypt(pubKey, (0, binary_utils_1.hex2ua)(aesKey)));
|
|
1441
|
-
return encrAes;
|
|
1442
|
-
}));
|
|
1443
|
-
return { [dataOwner.id]: encryptedAesForDataOwner };
|
|
1444
|
-
});
|
|
1445
|
-
}
|
|
1446
|
-
retrieveDataOwnerInfoAfterPotentialUpdate(dataOwnerToUpdate) {
|
|
1447
|
-
this.emptyHcpCache(dataOwnerToUpdate.id);
|
|
1448
|
-
return this.getDataOwner(dataOwnerToUpdate.id).then(({ type, dataOwner }) => {
|
|
1449
|
-
var _a, _b, _c, _d;
|
|
1450
|
-
return {
|
|
1451
|
-
type: type,
|
|
1452
|
-
dataOwner: Object.assign(Object.assign({}, dataOwner), { transferKeys: dataOwnerToUpdate.transferKeys, hcPartyKeys: (0, utils_1.fold)(Object.entries((_a = dataOwnerToUpdate.hcPartyKeys) !== null && _a !== void 0 ? _a : {}), (_b = dataOwner.hcPartyKeys) !== null && _b !== void 0 ? _b : {}, (acc, [delegateId, hcKeys]) => {
|
|
1453
|
-
acc[delegateId] = hcKeys;
|
|
1454
|
-
return acc;
|
|
1455
|
-
}), aesExchangeKeys: (0, utils_1.fold)(Object.entries((_c = dataOwnerToUpdate.aesExchangeKeys) !== null && _c !== void 0 ? _c : {}), (_d = dataOwner.aesExchangeKeys) !== null && _d !== void 0 ? _d : {}, (pubAcc, [pubKey, newAesExcKeys]) => {
|
|
1456
|
-
var _a;
|
|
1457
|
-
const existingKeys = (_a = pubAcc[pubKey]) !== null && _a !== void 0 ? _a : {};
|
|
1458
|
-
pubAcc[pubKey] = (0, utils_1.fold)(Object.entries(newAesExcKeys), existingKeys, (delAcc, [delId, delKeys]) => {
|
|
1459
|
-
delAcc[delId] = delKeys;
|
|
1460
|
-
return delAcc;
|
|
1461
|
-
});
|
|
1462
|
-
return pubAcc;
|
|
1463
|
-
}) }),
|
|
1464
|
-
};
|
|
1465
|
-
});
|
|
1466
|
-
}
|
|
1467
|
-
sendMaintenanceTasks(maintenanceTaskApi, user, dataOwner, newPublicKey) {
|
|
733
|
+
/**
|
|
734
|
+
* @deprecated You don't need to manually generate exchange keys as they will be automatically created by the api when needed.
|
|
735
|
+
* Note that this method has some changes compared to previous version:
|
|
736
|
+
* - The method may return any data owner (including devices)
|
|
737
|
+
* - The method will throw an exception if the provided ownerId does not match the current data owner
|
|
738
|
+
*/
|
|
739
|
+
generateKeyForDelegate(ownerId, delegateId) {
|
|
740
|
+
var _a, _b;
|
|
1468
741
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
.filter((existingPubKey) => existingPubKey != hexNewPubKey)
|
|
1472
|
-
.filter((existingPubKey) => __awaiter(this, void 0, void 0, function* () { return (yield this.getPublicKeysAsSpki()).find((pubKey) => pubKey == existingPubKey) == undefined; }));
|
|
1473
|
-
if (nonAccessiblePubKeys.length) {
|
|
1474
|
-
const tasksForDelegates = Object.entries(yield this.getEncryptedAesExchangeKeysForDelegate(dataOwner.id))
|
|
1475
|
-
.filter(([delegatorId]) => delegatorId != dataOwner.id)
|
|
1476
|
-
.flatMap(([delegatorId, delegatorKeys]) => {
|
|
1477
|
-
return Object.entries(delegatorKeys).flatMap(([, aesExchangeKeys]) => {
|
|
1478
|
-
return Object.keys(aesExchangeKeys).map((delegatePubKey) => {
|
|
1479
|
-
return { delegateId: delegatorId, maintenanceTask: this.createMaintenanceTask(dataOwner, delegatePubKey) };
|
|
1480
|
-
});
|
|
1481
|
-
});
|
|
1482
|
-
});
|
|
1483
|
-
const tasksForDelegator = (yield this._getDelegateIdsOf(dataOwner))
|
|
1484
|
-
.filter((delegateId) => delegateId != dataOwner.id)
|
|
1485
|
-
.map((delegateId) => {
|
|
1486
|
-
return { delegateId: delegateId, maintenanceTask: this.createMaintenanceTask(dataOwner, hexNewPubKey) };
|
|
1487
|
-
});
|
|
1488
|
-
return yield tasksForDelegates.concat(tasksForDelegator).reduce((existingTasks, task) => __awaiter(this, void 0, void 0, function* () {
|
|
1489
|
-
const taskToCreate = yield (maintenanceTaskApi === null || maintenanceTaskApi === void 0 ? void 0 : maintenanceTaskApi.newInstance(user, task.maintenanceTask, [task.delegateId]));
|
|
1490
|
-
const createdTask = taskToCreate ? yield (maintenanceTaskApi === null || maintenanceTaskApi === void 0 ? void 0 : maintenanceTaskApi.createMaintenanceTaskWithUser(user, taskToCreate)) : undefined;
|
|
1491
|
-
return createdTask ? (yield existingTasks).concat(createdTask) : yield existingTasks;
|
|
1492
|
-
}), Promise.resolve([]));
|
|
1493
|
-
}
|
|
1494
|
-
else {
|
|
1495
|
-
return [];
|
|
742
|
+
if (ownerId !== (yield this.dataOwnerApi.getCurrentDataOwnerId())) {
|
|
743
|
+
throw new Error('You can only create delegation where the delegator is the current data owner');
|
|
1496
744
|
}
|
|
745
|
+
return ((_b = (_a = (yield this.exchangeKeysManager.getOrCreateEncryptionExchangeKeysTo(delegateId)).updatedDelegator) === null || _a === void 0 ? void 0 : _a.dataOwner) !== null && _b !== void 0 ? _b : (yield this.dataOwnerApi.getDataOwner(ownerId)).dataOwner);
|
|
1497
746
|
});
|
|
1498
747
|
}
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
taskType: 'KEY_PAIR_UPDATE',
|
|
1503
|
-
status: models_1.MaintenanceTask.StatusEnum.Pending,
|
|
1504
|
-
properties: [
|
|
1505
|
-
new models_1.PropertyStub({
|
|
1506
|
-
id: 'dataOwnerConcernedId',
|
|
1507
|
-
type: new models_1.PropertyTypeStub({ type: models_1.PropertyTypeStub.TypeEnum.STRING }),
|
|
1508
|
-
typedValue: new models_1.TypedValueObject({
|
|
1509
|
-
type: models_1.TypedValueObject.TypeEnum.STRING,
|
|
1510
|
-
stringValue: concernedDataOwner.id,
|
|
1511
|
-
}),
|
|
1512
|
-
}),
|
|
1513
|
-
new models_1.PropertyStub({
|
|
1514
|
-
id: 'dataOwnerConcernedPubKey',
|
|
1515
|
-
type: new models_1.PropertyTypeStub({ type: models_1.PropertyTypeStub.TypeEnum.STRING }),
|
|
1516
|
-
typedValue: new models_1.TypedValueObject({
|
|
1517
|
-
type: models_1.TypedValueObject.TypeEnum.STRING,
|
|
1518
|
-
stringValue: concernedPubKey,
|
|
1519
|
-
}),
|
|
1520
|
-
}),
|
|
1521
|
-
],
|
|
1522
|
-
});
|
|
1523
|
-
}
|
|
1524
|
-
getDataOwnerHexPublicKeys(dataOwner) {
|
|
1525
|
-
return new Set((dataOwner.publicKey ? [dataOwner.publicKey] : [])
|
|
1526
|
-
.concat(dataOwner.aesExchangeKeys ? Object.keys(dataOwner.aesExchangeKeys) : [])
|
|
1527
|
-
.filter((pubKey) => !!pubKey));
|
|
1528
|
-
}
|
|
1529
|
-
getDataOwnerPublicKeys(dataOwner) {
|
|
1530
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1531
|
-
return yield Promise.all(Array.from(this.getDataOwnerHexPublicKeys(dataOwner)).map((pubKey) => __awaiter(this, void 0, void 0, function* () { return yield this._RSA.importKey('jwk', (0, utils_1.spkiToJwk)((0, binary_utils_1.hex2ua)(pubKey)), ['encrypt']); })));
|
|
1532
|
-
});
|
|
1533
|
-
}
|
|
1534
|
-
generateKeyForDelegate(ownerId, delegateId) {
|
|
1535
|
-
//Preload hcp and patient because we need them and they are going to be invalidated from the caches
|
|
1536
|
-
return (0, utils_1.notConcurrent)(this.generateKeyConcurrencyMap, ownerId, () => __awaiter(this, void 0, void 0, function* () {
|
|
1537
|
-
var _a, _b, _c, _d, _e;
|
|
1538
|
-
const [{ type: ownerType, dataOwner: owner }, { dataOwner: delegate }] = yield Promise.all([
|
|
1539
|
-
this.getDataOwner(ownerId),
|
|
1540
|
-
this.getDataOwner(delegateId),
|
|
1541
|
-
]);
|
|
1542
|
-
const availablePublicKeysFingerprints = Object.keys(this.rsaKeyPairs);
|
|
1543
|
-
const ownerLegacyPublicKey = owner.publicKey;
|
|
1544
|
-
const isOwnerLegacyPublicKeyAvailable = ownerLegacyPublicKey
|
|
1545
|
-
? availablePublicKeysFingerprints.some((fp) => ownerLegacyPublicKey.endsWith(fp))
|
|
1546
|
-
: false;
|
|
1547
|
-
const availableOwnerPublicKeys = [
|
|
1548
|
-
ownerLegacyPublicKey,
|
|
1549
|
-
...Object.keys(owner.aesExchangeKeys || {}).filter((x) => x !== ownerLegacyPublicKey),
|
|
1550
|
-
].filter((k) => !!k && availablePublicKeysFingerprints.some((fp) => k.endsWith(fp)));
|
|
1551
|
-
const selectedPublicKey = availableOwnerPublicKeys[0];
|
|
1552
|
-
if (!selectedPublicKey) {
|
|
1553
|
-
throw new Error(`Invalid owner, no public key, keypairs have not be set for ${ownerId}`);
|
|
1554
|
-
}
|
|
1555
|
-
if (((owner.hcPartyKeys || {})[delegateId] && owner.publicKey && availablePublicKeysFingerprints.includes(owner.publicKey.slice(-32))) ||
|
|
1556
|
-
Object.values(owner.aesExchangeKeys || {}).some((x) => x[delegateId] && Object.keys(x[delegateId]).some((k) => availablePublicKeysFingerprints.includes(k)))) {
|
|
1557
|
-
return owner;
|
|
1558
|
-
}
|
|
1559
|
-
let ownerCombinedAesExchangeKeys = (_a = owner.aesExchangeKeys) !== null && _a !== void 0 ? _a : {};
|
|
1560
|
-
if (ownerLegacyPublicKey && !((_b = owner.aesExchangeKeys) !== null && _b !== void 0 ? _b : {})[ownerLegacyPublicKey]) {
|
|
1561
|
-
//Transfer keys from old structure (hcparty keys) to new one (aesExchangeKeys)
|
|
1562
|
-
const unknownDataOwnerCounterPartIds = Object.keys((_c = owner.hcPartyKeys) !== null && _c !== void 0 ? _c : {}).filter((x) => x !== ownerId && x !== delegateId);
|
|
1563
|
-
const counterParts = [
|
|
1564
|
-
owner,
|
|
1565
|
-
delegate,
|
|
1566
|
-
...(yield Promise.all(unknownDataOwnerCounterPartIds.map((cpid) => this.getDataOwner(cpid).then((dot) => dot.dataOwner)))),
|
|
1567
|
-
];
|
|
1568
|
-
ownerCombinedAesExchangeKeys = Object.assign({ [ownerLegacyPublicKey]: Object.entries((_d = owner.hcPartyKeys) !== null && _d !== void 0 ? _d : {}).reduce((map, [hcpId, keys]) => {
|
|
1569
|
-
var _a, _b;
|
|
1570
|
-
return (Object.assign(Object.assign(Object.assign({}, map), { [hcpId]: { [ownerLegacyPublicKey.slice(-32)]: keys[0], [(_b = (_a = counterParts.find((x) => x.id === hcpId)) === null || _a === void 0 ? void 0 : _a.publicKey) !== null && _b !== void 0 ? _b : '']: keys[1] } }), {}));
|
|
1571
|
-
}, {}) }, ownerCombinedAesExchangeKeys);
|
|
1572
|
-
}
|
|
1573
|
-
const delegatePublicKeys = [delegate.publicKey, ...Object.keys((_e = delegate.aesExchangeKeys) !== null && _e !== void 0 ? _e : {}).filter((x) => x !== delegate.publicKey)].filter((x) => !!x);
|
|
1574
|
-
if (!delegatePublicKeys.length) {
|
|
1575
|
-
throw new Error(`Invalid delegate, no public key, keypairs have not be set for ${delegateId}`);
|
|
1576
|
-
}
|
|
1577
|
-
const genProm = this._AES.generateCryptoKey(true).then((AESKey) => __awaiter(this, void 0, void 0, function* () {
|
|
1578
|
-
var _f, _g;
|
|
1579
|
-
const allPubKeys = [...availableOwnerPublicKeys, ...delegatePublicKeys];
|
|
1580
|
-
const encryptedAesKeys = yield allPubKeys.reduce((map, pubK) => __awaiter(this, void 0, void 0, function* () {
|
|
1581
|
-
return (Object.assign(Object.assign({}, (yield map)), { [pubK.slice(-32)]: (0, binary_utils_1.ua2hex)(yield this._RSA.encrypt(yield this._RSA.importKey('jwk', (0, utils_1.spkiToJwk)((0, binary_utils_1.hex2ua)(pubK)), ['encrypt']), (0, binary_utils_1.hex2ua)(AESKey))) }));
|
|
1582
|
-
}), Promise.resolve({}));
|
|
1583
|
-
if (delegate.publicKey && ownerLegacyPublicKey && isOwnerLegacyPublicKeyAvailable) {
|
|
1584
|
-
owner.hcPartyKeys[delegateId] = [encryptedAesKeys[ownerLegacyPublicKey.slice(-32)], encryptedAesKeys[delegate.publicKey.slice(-32)]];
|
|
1585
|
-
}
|
|
1586
|
-
owner.aesExchangeKeys = this.fixAesExchangeKeyEntriesToFingerprints(Object.assign(Object.assign({}, (ownerCombinedAesExchangeKeys !== null && ownerCombinedAesExchangeKeys !== void 0 ? ownerCombinedAesExchangeKeys : {})), { [selectedPublicKey]: Object.assign(Object.assign({}, ((_g = (_f = owner.aesExchangeKeys) === null || _f === void 0 ? void 0 : _f[selectedPublicKey]) !== null && _g !== void 0 ? _g : {})), { [delegateId]: encryptedAesKeys }) }));
|
|
1587
|
-
return new Promise((resolve, reject) => {
|
|
1588
|
-
ownerType === 'hcp'
|
|
1589
|
-
? (this.dataOwnerCache[owner.id] = this.hcpartyBaseApi
|
|
1590
|
-
.modifyHealthcareParty(owner)
|
|
1591
|
-
.then((x) => ({ type: 'hcp', dataOwner: x })))
|
|
1592
|
-
.then((x) => resolve(['hcp', x.dataOwner]))
|
|
1593
|
-
.catch((e) => reject(e))
|
|
1594
|
-
: ownerType === 'patient'
|
|
1595
|
-
? (this.dataOwnerCache[owner.id] = this.patientBaseApi.modifyPatient(owner).then((x) => ({ type: 'patient', dataOwner: x })))
|
|
1596
|
-
.then((x) => resolve(['patient', x.dataOwner]))
|
|
1597
|
-
.catch((e) => reject(e))
|
|
1598
|
-
: (this.dataOwnerCache[owner.id] = this.deviceBaseApi.updateDevice(owner).then((x) => ({ type: 'device', dataOwner: x })))
|
|
1599
|
-
.then((x) => resolve(['device', x.dataOwner]))
|
|
1600
|
-
.catch((e) => reject(e));
|
|
1601
|
-
});
|
|
1602
|
-
}));
|
|
1603
|
-
this.hcPartyKeysRequestsCache[delegateId] = genProm.then(() => {
|
|
1604
|
-
return this.forceGetEncryptedAesExchangeKeysForDelegate(delegateId);
|
|
1605
|
-
});
|
|
1606
|
-
return genProm.then((res) => {
|
|
1607
|
-
return res[1];
|
|
1608
|
-
});
|
|
1609
|
-
}));
|
|
1610
|
-
}
|
|
748
|
+
/**
|
|
749
|
+
* @deprecated replace with {@link IccDataOwnerXApi.getDataOwner}. Note that data owners are not cached anymore.
|
|
750
|
+
*/
|
|
1611
751
|
getDataOwner(ownerId, loadIfMissingFromCache = true) {
|
|
1612
|
-
|
|
1613
|
-
return ((_a = this.dataOwnerCache[ownerId]) !== null && _a !== void 0 ? _a : (loadIfMissingFromCache
|
|
1614
|
-
? (this.dataOwnerCache[ownerId] = this.patientBaseApi
|
|
1615
|
-
.getPatient(ownerId)
|
|
1616
|
-
.then((x) => ({ type: 'patient', dataOwner: x }))
|
|
1617
|
-
.catch(() => this.deviceBaseApi.getDevice(ownerId).then((x) => ({ type: 'device', dataOwner: x })))
|
|
1618
|
-
.catch(() => this.hcpartyBaseApi.getHealthcareParty(ownerId).then((x) => ({ type: 'hcp', dataOwner: x })))
|
|
1619
|
-
.catch((e) => {
|
|
1620
|
-
delete this.dataOwnerCache[ownerId];
|
|
1621
|
-
throw e;
|
|
1622
|
-
}))
|
|
1623
|
-
: undefined));
|
|
752
|
+
return this.dataOwnerApi.getDataOwner(ownerId);
|
|
1624
753
|
}
|
|
1625
|
-
|
|
754
|
+
/**
|
|
755
|
+
* @deprecated the crypto api will automatically verify on startup the validity of private keys, but in some cases you may want to verify the
|
|
756
|
+
* validity of keys recovered in your implementation of {@link CryptoStrategies}: in this case the method has been replaced with
|
|
757
|
+
* {@link RSA.checkKeyPairValidity}
|
|
758
|
+
*/
|
|
1626
759
|
checkPrivateKeyValidity(dataOwner) {
|
|
1627
760
|
var _a;
|
|
1628
761
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -1630,10 +763,10 @@ class IccCryptoXApi {
|
|
|
1630
763
|
return yield publicKeys.reduce((pres, publicKey) => __awaiter(this, void 0, void 0, function* () {
|
|
1631
764
|
const res = yield pres;
|
|
1632
765
|
try {
|
|
1633
|
-
const k = yield this.
|
|
1634
|
-
const cipher = yield this.
|
|
766
|
+
const k = yield this.primitives.RSA.importKey('jwk', (0, utils_1.spkiToJwk)((0, binary_utils_1.hex2ua)(publicKey)), ['encrypt']);
|
|
767
|
+
const cipher = yield this.primitives.RSA.encrypt(k, (0, binary_utils_1.utf8_2ua)('shibboleth'));
|
|
1635
768
|
const ikp = yield this.getCachedRsaKeyPairForFingerprint(dataOwner.id, publicKey.slice(-32));
|
|
1636
|
-
const plainText = (0, binary_utils_1.ua2utf8)(yield this.
|
|
769
|
+
const plainText = (0, binary_utils_1.ua2utf8)(yield this.primitives.RSA.decrypt(ikp.privateKey, new Uint8Array(cipher)));
|
|
1637
770
|
return plainText === 'shibboleth' || res;
|
|
1638
771
|
}
|
|
1639
772
|
catch (e) {
|
|
@@ -1642,61 +775,34 @@ class IccCryptoXApi {
|
|
|
1642
775
|
}), Promise.resolve(false));
|
|
1643
776
|
});
|
|
1644
777
|
}
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
let details = '\nMethod name: icc-crypto-x-api.' + methodName + '()\nArguments:';
|
|
1649
|
-
if (methodArgs) {
|
|
1650
|
-
try {
|
|
1651
|
-
const argsArray = [...methodArgs];
|
|
1652
|
-
_.each(argsArray, (arg, index) => (details += '\n[' + index + ']: ' + JSON.stringify(arg)));
|
|
1653
|
-
}
|
|
1654
|
-
catch (ex) {
|
|
1655
|
-
details += '; a problem occured while logging arguments details: ' + ex;
|
|
1656
|
-
}
|
|
1657
|
-
}
|
|
1658
|
-
throw '### THIS SHOULD NOT HAPPEN: ' + argName + ' has an invalid value: ' + argValue + details;
|
|
1659
|
-
}
|
|
778
|
+
/**
|
|
779
|
+
* @deprecated (See {@link extractEncryptionsSKs} for a detailed explanation).
|
|
780
|
+
*/
|
|
1660
781
|
getEncryptionDecryptionKeys(dataOwnerId, document) {
|
|
1661
782
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1662
|
-
|
|
1663
|
-
return !document.id
|
|
1664
|
-
? undefined
|
|
1665
|
-
: _.uniq((yield this.extractKeysFromDelegationsForHcpHierarchy(dataOwnerId, document.id, (document.encryptionKeys && Object.keys(document.encryptionKeys).length && document.encryptionKeys) || document.delegations)).extractedKeys);
|
|
1666
|
-
}
|
|
1667
|
-
catch (e) {
|
|
1668
|
-
return undefined;
|
|
1669
|
-
}
|
|
1670
|
-
});
|
|
1671
|
-
}
|
|
1672
|
-
dataOwnerCanDecryptPatient(dataOwnerId, p) {
|
|
1673
|
-
var _a, _b;
|
|
1674
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1675
|
-
if (p.encryptionKeys && ((_a = p.encryptionKeys[dataOwnerId]) === null || _a === void 0 ? void 0 : _a.length))
|
|
1676
|
-
return true;
|
|
1677
|
-
const dataOwner = yield this.getDataOwner(dataOwnerId);
|
|
1678
|
-
if (dataOwner.type === 'hcp' && p.encryptionKeys && ((_b = p.encryptionKeys[dataOwner.dataOwner.parentId]) === null || _b === void 0 ? void 0 : _b.length))
|
|
1679
|
-
return true;
|
|
1680
|
-
return false;
|
|
783
|
+
return this.entities.encryptionKeysOf(document, dataOwnerId);
|
|
1681
784
|
});
|
|
1682
785
|
}
|
|
786
|
+
/**
|
|
787
|
+
* @deprecated For the encryption/decryption of iCure entities and attachments you should rely solely on the extended apis methods.
|
|
788
|
+
*/
|
|
1683
789
|
encryptDecrypt(method, content, edKey, user, documentObject) {
|
|
1684
790
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1685
791
|
if (!content || !(edKey || ((user === null || user === void 0 ? void 0 : user.healthcarePartyId) && documentObject)))
|
|
1686
792
|
return content;
|
|
1687
793
|
if (edKey) {
|
|
1688
|
-
const importedEdKey = yield this.
|
|
794
|
+
const importedEdKey = yield this.primitives.AES.importKey('raw', (0, binary_utils_1.hex2ua)(edKey.replace(/-/g, '')));
|
|
1689
795
|
try {
|
|
1690
|
-
return yield this.
|
|
796
|
+
return yield this.primitives.AES[method](importedEdKey, content);
|
|
1691
797
|
}
|
|
1692
798
|
catch (e) {
|
|
1693
799
|
return content;
|
|
1694
800
|
}
|
|
1695
801
|
}
|
|
1696
|
-
const
|
|
1697
|
-
const importedEdKey = yield this.
|
|
802
|
+
const encryptionKeys = yield this.entities.encryptionKeysOf(documentObject, user === null || user === void 0 ? void 0 : user.healthcarePartyId);
|
|
803
|
+
const importedEdKey = yield this.primitives.AES.importKey('raw', (0, binary_utils_1.hex2ua)(encryptionKeys[0].replace(/-/g, '')));
|
|
1698
804
|
try {
|
|
1699
|
-
return yield this.
|
|
805
|
+
return yield this.primitives.AES[method](importedEdKey, content);
|
|
1700
806
|
}
|
|
1701
807
|
catch (e) {
|
|
1702
808
|
return content;
|
|
@@ -1704,28 +810,11 @@ class IccCryptoXApi {
|
|
|
1704
810
|
});
|
|
1705
811
|
}
|
|
1706
812
|
/**
|
|
1707
|
-
*
|
|
1708
|
-
*
|
|
1709
|
-
* @param id
|
|
1710
|
-
* @param keyPair should be JWK
|
|
1711
|
-
*
|
|
1712
|
-
* @deprecated use storage.setItem instead
|
|
813
|
+
* @deprecated you should not need to interact directly with the storage instance used by the iCure sdk.
|
|
1713
814
|
*/
|
|
1714
815
|
storeKeyPair(id, keyPair) {
|
|
1715
816
|
this._storage.setItem(this.rsaLocalStoreIdPrefix + id, JSON.stringify(keyPair));
|
|
1716
817
|
}
|
|
1717
|
-
fixAesExchangeKeyEntriesToFingerprints(aesExchangeKeys) {
|
|
1718
|
-
return Object.fromEntries(Object.entries(aesExchangeKeys).map(([delegatorPubKey, allDelegates]) => [
|
|
1719
|
-
delegatorPubKey,
|
|
1720
|
-
Object.fromEntries(Object.entries(allDelegates).map(([delegateId, keyEntries]) => [
|
|
1721
|
-
delegateId,
|
|
1722
|
-
Object.fromEntries(Object.entries(keyEntries).map(([publicKey, encryptedValue]) => [
|
|
1723
|
-
publicKey.slice(-32),
|
|
1724
|
-
encryptedValue
|
|
1725
|
-
]))
|
|
1726
|
-
]))
|
|
1727
|
-
]));
|
|
1728
|
-
}
|
|
1729
818
|
}
|
|
1730
819
|
exports.IccCryptoXApi = IccCryptoXApi;
|
|
1731
820
|
//# sourceMappingURL=icc-crypto-x-api.js.map
|