@docknetwork/wallet-sdk-core 1.5.6 → 1.5.9
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/lib/biometric-provider.d.ts +46 -19
- package/lib/biometric-provider.d.ts.map +1 -1
- package/lib/biometric-provider.js +71 -41
- package/lib/biometric-provider.js.map +1 -1
- package/lib/cloud-wallet.d.ts +102 -3
- package/lib/cloud-wallet.d.ts.map +1 -1
- package/lib/cloud-wallet.js +211 -17
- package/lib/cloud-wallet.js.map +1 -1
- package/lib/credential-provider.d.ts +10 -0
- package/lib/credential-provider.d.ts.map +1 -1
- package/lib/credential-provider.js +21 -1
- package/lib/credential-provider.js.map +1 -1
- package/lib/credentials/oidvc.js +1 -1
- package/lib/credentials/oidvc.js.map +1 -1
- package/package.json +5 -4
- package/src/biometric-provider.ts +139 -57
- package/src/cloud-wallet.ts +312 -16
- package/src/credential-provider.ts +31 -0
- package/src/credentials/oidvc.test.ts +75 -15
- package/src/credentials/oidvc.ts +1 -1
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -1,91 +1,173 @@
|
|
|
1
1
|
import {WalletDocument} from '@docknetwork/wallet-sdk-wasm/src/types';
|
|
2
2
|
import {IWallet} from './types';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
createCredentialProvider,
|
|
5
|
+
Credential,
|
|
6
|
+
CredentialStatus,
|
|
7
|
+
} from './credential-provider';
|
|
4
8
|
import assert from 'assert';
|
|
9
|
+
import {EventEmitter} from 'events';
|
|
10
|
+
import {createDIDProvider} from './did-provider';
|
|
5
11
|
|
|
6
|
-
export type
|
|
7
|
-
|
|
8
|
-
did: string;
|
|
9
|
-
apiKey: string;
|
|
10
|
-
apiUrl: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export type BiometricsPluginConfigs = {
|
|
12
|
+
export type BiometricsProviderConfigs<E> = {
|
|
13
|
+
// Generic configs used by the biometric provider
|
|
14
14
|
enrollmentCredentialType: string;
|
|
15
15
|
biometricMatchCredentialType: string;
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
// IDV specific configs, it depends on the IDV provider and its implementation
|
|
17
|
+
idvConfigs: E;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export interface IDVProcessOptions {
|
|
21
|
+
onDeepLink?: () => void;
|
|
22
|
+
onMessage?: () => void;
|
|
23
|
+
onError?: (error: Error) => void;
|
|
24
|
+
onCancel?: () => void;
|
|
25
|
+
onComplete?: (credential: any) => void;
|
|
18
26
|
}
|
|
19
27
|
|
|
20
|
-
|
|
28
|
+
export interface BiometricPlugin {
|
|
29
|
+
onEnroll(walletDID: string): Promise<WalletDocument>;
|
|
30
|
+
onMatch(
|
|
31
|
+
walletDID: string,
|
|
32
|
+
enrollmentCredential: Credential,
|
|
33
|
+
): Promise<WalletDocument>;
|
|
34
|
+
}
|
|
21
35
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
36
|
+
let currentConfigs: BiometricsProviderConfigs<unknown> = null;
|
|
37
|
+
|
|
38
|
+
export function setConfigs(configs: BiometricsProviderConfigs<unknown>) {
|
|
39
|
+
currentConfigs = configs;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function isBiometricPluginEnabled() {
|
|
43
|
+
return !!currentConfigs?.biometricMatchCredentialType;
|
|
44
|
+
}
|
|
25
45
|
|
|
26
46
|
export function assertConfigs() {
|
|
27
|
-
assert(!!
|
|
47
|
+
assert(!!currentConfigs, 'Missing biometric provider configs');
|
|
28
48
|
}
|
|
29
49
|
|
|
30
50
|
export function getBiometricConfigs() {
|
|
31
51
|
assertConfigs();
|
|
32
|
-
return
|
|
52
|
+
return currentConfigs;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function hasProofOfBiometrics(proofRequest) {
|
|
56
|
+
const fields = proofRequest.input_descriptors
|
|
57
|
+
?.map(input => input.constraints?.fields)
|
|
58
|
+
.flat();
|
|
59
|
+
const paths = fields.map(field => field.path).flat();
|
|
60
|
+
return (
|
|
61
|
+
paths?.includes('$.credentialSubject.biometric.id') &&
|
|
62
|
+
paths?.includes('$.credentialSubject.biometric.created')
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// map for events
|
|
67
|
+
export const IDV_EVENTS = {
|
|
68
|
+
onDeepLink: 'onDeepLink',
|
|
69
|
+
onMessage: 'onMessage',
|
|
70
|
+
onError: 'onError',
|
|
71
|
+
onCancel: 'onCancel',
|
|
72
|
+
onComplete: 'onComplete',
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export interface IDVProvider {
|
|
76
|
+
enroll(
|
|
77
|
+
walletDID: string,
|
|
78
|
+
proofRequest: any,
|
|
79
|
+
): Promise<{enrollmentCredential: Credential; matchCredential: Credential}>;
|
|
80
|
+
match(
|
|
81
|
+
walletDID: string,
|
|
82
|
+
enrollmentCredential: Credential,
|
|
83
|
+
proofRequest: any,
|
|
84
|
+
): Promise<{
|
|
85
|
+
matchCredential: Credential;
|
|
86
|
+
}>;
|
|
33
87
|
}
|
|
34
88
|
|
|
35
|
-
export
|
|
36
|
-
|
|
89
|
+
export interface IDVProviderFactory {
|
|
90
|
+
create(eventEmitter: EventEmitter, wallet: IWallet): IDVProvider;
|
|
37
91
|
}
|
|
38
92
|
|
|
39
|
-
export function
|
|
93
|
+
export function createBiometricProvider({
|
|
40
94
|
wallet,
|
|
41
|
-
|
|
42
|
-
onMatch,
|
|
43
|
-
onCheckBiometryRequired,
|
|
95
|
+
idvProviderFactory,
|
|
44
96
|
}: {
|
|
45
97
|
wallet: IWallet;
|
|
46
|
-
|
|
47
|
-
onMatch: (biometricTemplate: WalletDocument) => Promise<WalletDocument>;
|
|
48
|
-
onCheckBiometryRequired: (request) => boolean;
|
|
98
|
+
idvProviderFactory: IDVProviderFactory;
|
|
49
99
|
}) {
|
|
50
100
|
const credentialProvider = createCredentialProvider({wallet});
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
101
|
+
const didProvider = createDIDProvider({wallet});
|
|
102
|
+
const eventEmitter = new EventEmitter();
|
|
103
|
+
const idvProvider = idvProviderFactory.create(eventEmitter, wallet);
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
async function startIDV(proofRequest: any): Promise<{
|
|
107
|
+
enrollmentCredential: Credential;
|
|
108
|
+
matchCredential: Credential;
|
|
109
|
+
}> {
|
|
110
|
+
const walletDID = await didProvider.getDefaultDID();
|
|
111
|
+
let [enrollmentCredential] = await credentialProvider.getCredentials(
|
|
112
|
+
currentConfigs.enrollmentCredentialType,
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
// Remove any existing match credentials
|
|
116
|
+
const existingMatchCredentials = await credentialProvider.getCredentials(
|
|
117
|
+
currentConfigs.biometricMatchCredentialType,
|
|
118
|
+
);
|
|
119
|
+
for (const credential of existingMatchCredentials) {
|
|
120
|
+
await credentialProvider.removeCredential(credential.id);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
let matchCredential: Credential;
|
|
124
|
+
|
|
125
|
+
if (!enrollmentCredential) {
|
|
126
|
+
// call IDV to start enrollment process and issue the enrollment credential + match credential
|
|
127
|
+
const credentials = await idvProvider.enroll(walletDID, proofRequest);
|
|
128
|
+
|
|
129
|
+
// check if credential is already in the credential store
|
|
130
|
+
const receivedViaDistribution = await credentialProvider.getById(
|
|
131
|
+
credentials.matchCredential.id,
|
|
60
132
|
);
|
|
61
133
|
|
|
62
|
-
if (!
|
|
63
|
-
|
|
134
|
+
if (!receivedViaDistribution) {
|
|
135
|
+
await credentialProvider.addCredential(
|
|
136
|
+
credentials.enrollmentCredential,
|
|
137
|
+
);
|
|
138
|
+
await credentialProvider.addCredential(credentials.matchCredential);
|
|
64
139
|
}
|
|
65
140
|
|
|
66
|
-
|
|
67
|
-
|
|
141
|
+
matchCredential = credentials.matchCredential;
|
|
142
|
+
enrollmentCredential = credentials.enrollmentCredential;
|
|
143
|
+
} else {
|
|
144
|
+
// call IDV to match the enrollment credential and issue the match credential
|
|
145
|
+
const credentials = await idvProvider.match(
|
|
146
|
+
walletDID,
|
|
147
|
+
enrollmentCredential,
|
|
148
|
+
proofRequest,
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
// check if credential is already in the credential store
|
|
152
|
+
const receivedViaDistribution = await credentialProvider.getById(
|
|
153
|
+
credentials.matchCredential.id,
|
|
68
154
|
);
|
|
69
155
|
|
|
70
|
-
if (
|
|
71
|
-
|
|
72
|
-
for (let i = 0; i < biometricMatchCredentials.length; i++) {
|
|
73
|
-
await wallet.removeDocument(biometricMatchCredentials[0].id);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
await wallet.addDocument(matchConfirmationCredential);
|
|
77
|
-
// make the biometric credential valid by default
|
|
78
|
-
await wallet.addDocument({
|
|
79
|
-
id: `${matchConfirmationCredential.id}#status`,
|
|
80
|
-
status: CredentialStatus.Verified,
|
|
81
|
-
type: 'CredentialStatus',
|
|
82
|
-
createdAt: new Date().toISOString(),
|
|
83
|
-
updatedAt: new Date().toISOString(),
|
|
84
|
-
})
|
|
156
|
+
if (!receivedViaDistribution) {
|
|
157
|
+
await credentialProvider.addCredential(credentials.matchCredential);
|
|
85
158
|
}
|
|
86
159
|
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
|
|
160
|
+
matchCredential = credentials.matchCredential;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
enrollmentCredential,
|
|
165
|
+
matchCredential,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
startIDV,
|
|
171
|
+
eventEmitter,
|
|
90
172
|
};
|
|
91
173
|
}
|
package/src/cloud-wallet.ts
CHANGED
|
@@ -2,13 +2,311 @@ import {
|
|
|
2
2
|
DataStore,
|
|
3
3
|
DataStoreEvents,
|
|
4
4
|
} from '@docknetwork/wallet-sdk-data-store/src/types';
|
|
5
|
-
import {logger} from '@docknetwork/wallet-sdk-data-store/src/logger';
|
|
6
|
-
import {edvService} from '@docknetwork/wallet-sdk-wasm/src/services/edv';
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
import {utilCryptoService} from '@docknetwork/wallet-sdk-wasm/src/services/util-crypto';
|
|
5
|
+
import { logger } from '@docknetwork/wallet-sdk-data-store/src/logger';
|
|
6
|
+
import { edvService, EDVService } from '@docknetwork/wallet-sdk-wasm/src/services/edv/service';
|
|
7
|
+
import hkdf from 'futoin-hkdf';
|
|
8
|
+
import crypto from '@docknetwork/universal-wallet/crypto';
|
|
9
|
+
import { utilCryptoService } from '@docknetwork/wallet-sdk-wasm/src/services/util-crypto';
|
|
10
10
|
|
|
11
11
|
export const SYNC_MARKER_TYPE = 'SyncMarkerDocument';
|
|
12
|
+
export const MNEMONIC_WORD_COUNT = 12;
|
|
13
|
+
export const KEY_MAPPING_TYPE = 'KeyMappingDocument';
|
|
14
|
+
export const HKDF_LENGTH = 32;
|
|
15
|
+
export const HKDF_HASH = 'SHA-256';
|
|
16
|
+
const MASTER_KEY_SUFFIX = 'master-key';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Derives a key from biometric data using HKDF
|
|
20
|
+
* @param biometricData Biometric data from provider
|
|
21
|
+
* @param identifier User's identifier as salt (email, phone number, etc.)
|
|
22
|
+
* @returns Derived key
|
|
23
|
+
*/
|
|
24
|
+
export function deriveBiometricKey(
|
|
25
|
+
biometricData: Buffer,
|
|
26
|
+
identifier: string,
|
|
27
|
+
): Buffer {
|
|
28
|
+
const salt = identifier;
|
|
29
|
+
|
|
30
|
+
return hkdf(biometricData, HKDF_LENGTH, { salt, hash: HKDF_HASH });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Derives EDV keys from biometric data for the KeyMappingVault
|
|
35
|
+
* @param biometricData Biometric data from the provider
|
|
36
|
+
* @param identifier User's identifier as additional entropy (email, phone number, etc.)
|
|
37
|
+
* @returns Keys for accessing the KeyMappingVault
|
|
38
|
+
*/
|
|
39
|
+
export async function deriveKeyMappingVaultKeys(
|
|
40
|
+
biometricData: Buffer,
|
|
41
|
+
identifier: string
|
|
42
|
+
): Promise<{ hmacKey: string; agreementKey: string; verificationKey: string }> {
|
|
43
|
+
const seedBuffer = deriveBiometricKey(biometricData, identifier);
|
|
44
|
+
|
|
45
|
+
return edvService.deriveKeys(new Uint8Array(seedBuffer));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Generates a key for encrypting/decrypting the master key
|
|
50
|
+
* @param biometricData Biometric data from provider
|
|
51
|
+
* @param identifier User's identifier as salt (email, phone number, etc.)
|
|
52
|
+
* @returns Encryption key and IV for AES encryption
|
|
53
|
+
*/
|
|
54
|
+
export async function deriveBiometricEncryptionKey(
|
|
55
|
+
biometricData: Buffer,
|
|
56
|
+
identifier: string
|
|
57
|
+
): Promise<{ key: Buffer; iv: Buffer }> {
|
|
58
|
+
const key = deriveBiometricKey(biometricData, identifier);
|
|
59
|
+
|
|
60
|
+
const randomBytes = crypto.getRandomValues(new Uint8Array(16));
|
|
61
|
+
const iv = Buffer.from(randomBytes);
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
key,
|
|
65
|
+
iv
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Encrypts the master key using a key derived from biometric data
|
|
71
|
+
* @param masterKey The CloudWalletVault master key to encrypt
|
|
72
|
+
* @param encryptionKey Key derived from biometric data
|
|
73
|
+
* @param iv Initialization vector
|
|
74
|
+
* @returns Encrypted master key
|
|
75
|
+
*/
|
|
76
|
+
export async function encryptMasterKey(
|
|
77
|
+
masterKey: Uint8Array,
|
|
78
|
+
encryptionKey: Buffer,
|
|
79
|
+
iv: Buffer
|
|
80
|
+
): Promise<Uint8Array> {
|
|
81
|
+
const keyData = new Uint8Array(encryptionKey);
|
|
82
|
+
const ivData = new Uint8Array(iv);
|
|
83
|
+
|
|
84
|
+
const key = await crypto.subtle.importKey(
|
|
85
|
+
'raw',
|
|
86
|
+
keyData,
|
|
87
|
+
{ name: 'AES-GCM' },
|
|
88
|
+
false,
|
|
89
|
+
['encrypt']
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const encryptedBuffer = await crypto.subtle.encrypt(
|
|
93
|
+
{ name: 'AES-GCM', iv: ivData },
|
|
94
|
+
key,
|
|
95
|
+
masterKey
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
return new Uint8Array(encryptedBuffer);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Decrypts the master key using biometric-derived key
|
|
103
|
+
* @param encryptedKey The encrypted master key
|
|
104
|
+
* @param decryptionKey Key derived from biometric data
|
|
105
|
+
* @param iv Initialization vector
|
|
106
|
+
* @returns The decrypted master key
|
|
107
|
+
*/
|
|
108
|
+
export async function decryptMasterKey(
|
|
109
|
+
encryptedKey: Uint8Array,
|
|
110
|
+
decryptionKey: Buffer,
|
|
111
|
+
iv: Buffer
|
|
112
|
+
): Promise<Uint8Array> {
|
|
113
|
+
try {
|
|
114
|
+
const keyData = new Uint8Array(decryptionKey);
|
|
115
|
+
const ivData = new Uint8Array(iv);
|
|
116
|
+
|
|
117
|
+
const key = await crypto.subtle.importKey(
|
|
118
|
+
'raw',
|
|
119
|
+
keyData,
|
|
120
|
+
{ name: 'AES-GCM' },
|
|
121
|
+
false,
|
|
122
|
+
['decrypt']
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const decryptedBuffer = await crypto.subtle.decrypt(
|
|
126
|
+
{ name: 'AES-GCM', iv: ivData },
|
|
127
|
+
key,
|
|
128
|
+
encryptedKey
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
return new Uint8Array(decryptedBuffer);
|
|
132
|
+
} catch (error) {
|
|
133
|
+
throw new Error('Decryption failed: Invalid key or corrupted data');
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Initializes the KeyMappingVault using biometric data
|
|
139
|
+
* @param edvUrl URL for the edv
|
|
140
|
+
* @param authKey Auth key for the edv
|
|
141
|
+
* @param biometricData User's biometric data
|
|
142
|
+
* @param identifier User's identifier (email, phone number, etc.)
|
|
143
|
+
* @returns Initialized EDV service
|
|
144
|
+
*/
|
|
145
|
+
export async function initializeKeyMappingVault(
|
|
146
|
+
edvUrl: string,
|
|
147
|
+
authKey: string,
|
|
148
|
+
biometricData: Buffer,
|
|
149
|
+
identifier: string
|
|
150
|
+
): Promise<EDVService> {
|
|
151
|
+
const {
|
|
152
|
+
hmacKey,
|
|
153
|
+
agreementKey,
|
|
154
|
+
verificationKey
|
|
155
|
+
} = await deriveKeyMappingVaultKeys(biometricData, identifier);
|
|
156
|
+
|
|
157
|
+
const keyMappingEdvService = new EDVService();
|
|
158
|
+
await keyMappingEdvService.initialize({
|
|
159
|
+
hmacKey,
|
|
160
|
+
agreementKey,
|
|
161
|
+
verificationKey,
|
|
162
|
+
edvUrl,
|
|
163
|
+
authKey
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
return keyMappingEdvService;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Enrolls a user by creating necessary vaults and keys
|
|
171
|
+
* @param edvUrl URL for the edv
|
|
172
|
+
* @param authKey Auth key for the edv
|
|
173
|
+
* @param biometricData Biometric data from provider
|
|
174
|
+
* @param identifier User's identifier (email, phone number, etc.)
|
|
175
|
+
* @returns The master key and mnemonic for backup
|
|
176
|
+
*/
|
|
177
|
+
export async function enrollUserWithBiometrics(
|
|
178
|
+
edvUrl: string,
|
|
179
|
+
authKey: string,
|
|
180
|
+
biometricData: Buffer,
|
|
181
|
+
identifier: string
|
|
182
|
+
): Promise<{ masterKey: Uint8Array; mnemonic: string }> {
|
|
183
|
+
const keyMappingEdv = await initializeKeyMappingVault(
|
|
184
|
+
edvUrl,
|
|
185
|
+
authKey,
|
|
186
|
+
biometricData,
|
|
187
|
+
identifier
|
|
188
|
+
);
|
|
189
|
+
const { mnemonic, masterKey } = await generateCloudWalletMasterKey();
|
|
190
|
+
const { key: encryptionKey, iv } = await deriveBiometricEncryptionKey(biometricData, identifier);
|
|
191
|
+
const encryptedMasterKey = await encryptMasterKey(masterKey, encryptionKey, iv);
|
|
192
|
+
|
|
193
|
+
const encryptedData = {
|
|
194
|
+
data: Array.from(encryptedMasterKey),
|
|
195
|
+
iv: Array.from(iv)
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const contentId = `${await keyMappingEdv.getController()}#${MASTER_KEY_SUFFIX}`;
|
|
199
|
+
|
|
200
|
+
await keyMappingEdv.insert({
|
|
201
|
+
document: {
|
|
202
|
+
content: {
|
|
203
|
+
id: contentId,
|
|
204
|
+
type: KEY_MAPPING_TYPE,
|
|
205
|
+
encryptedKey: encryptedData
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
return { masterKey, mnemonic };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Gets the master key from the key mapping vault using provided decryption keys
|
|
215
|
+
* @param keyMappingEdv Initialized key mapping vault service
|
|
216
|
+
* @param identifier User's identifier (email, phone number, etc.)
|
|
217
|
+
* @param decryptionKey Key for decrypting the master key
|
|
218
|
+
* @param iv Initialization vector for decryption
|
|
219
|
+
* @returns The decrypted master key for CloudWalletVault
|
|
220
|
+
*/
|
|
221
|
+
export async function getKeyMappingMasterKey(
|
|
222
|
+
keyMappingEdv: EDVService,
|
|
223
|
+
identifier: string,
|
|
224
|
+
decryptionKey: Buffer,
|
|
225
|
+
): Promise<Uint8Array> {
|
|
226
|
+
const result = await keyMappingEdv.find({
|
|
227
|
+
equals: {
|
|
228
|
+
'content.id': identifier
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
if (!result.documents || result.documents.length === 0) {
|
|
233
|
+
throw new Error('Authentication failed: Invalid identifier');
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// The KeyMappingVault keys are derived from the biometric data so each
|
|
237
|
+
// vault should have a unique key for the user
|
|
238
|
+
|
|
239
|
+
const keyMappingDoc = result.documents[0];
|
|
240
|
+
const { data: encryptedKey, iv: storedIv } = keyMappingDoc.content.encryptedKey;
|
|
241
|
+
const encryptedKeyArray = new Uint8Array(encryptedKey);
|
|
242
|
+
const ivBuffer = Buffer.from(storedIv);
|
|
243
|
+
|
|
244
|
+
try {
|
|
245
|
+
const masterKey = await decryptMasterKey(encryptedKeyArray, decryptionKey, ivBuffer);
|
|
246
|
+
|
|
247
|
+
return masterKey;
|
|
248
|
+
} catch (error) {
|
|
249
|
+
throw new Error('Authentication failed: Invalid decryption key');
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Authenticates a user with biometric data and identifier
|
|
255
|
+
* @param edvUrl URL for the edv
|
|
256
|
+
* @param authKey Auth key for the edv
|
|
257
|
+
* @param biometricData Biometric data from the provider
|
|
258
|
+
* @param identifier User's identifier (email, phone number, etc.)
|
|
259
|
+
* @returns The decrypted master key for CloudWalletVault
|
|
260
|
+
*/
|
|
261
|
+
export async function authenticateWithBiometrics(
|
|
262
|
+
edvUrl: string,
|
|
263
|
+
authKey: string,
|
|
264
|
+
biometricData: Buffer,
|
|
265
|
+
identifier: string
|
|
266
|
+
): Promise<Uint8Array> {
|
|
267
|
+
const keyMappingEdv = await initializeKeyMappingVault(
|
|
268
|
+
edvUrl,
|
|
269
|
+
authKey,
|
|
270
|
+
biometricData,
|
|
271
|
+
identifier
|
|
272
|
+
);
|
|
273
|
+
const { key: decryptionKey } = await deriveBiometricEncryptionKey(biometricData, identifier);
|
|
274
|
+
|
|
275
|
+
const contentId = `${await keyMappingEdv.getController()}#${MASTER_KEY_SUFFIX}`;
|
|
276
|
+
|
|
277
|
+
return getKeyMappingMasterKey(keyMappingEdv, contentId, decryptionKey);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Initializes the Cloud Wallet using biometric authentication
|
|
282
|
+
* @param edvUrl Cloud wallet vault URL
|
|
283
|
+
* @param authKey Cloud wallet auth key
|
|
284
|
+
* @param biometricData User's biometric data
|
|
285
|
+
* @param identifier User's identifier (email, phone number, etc.)
|
|
286
|
+
* @param dataStore Optional data store for the wallet
|
|
287
|
+
* @returns Initialized cloud wallet
|
|
288
|
+
*/
|
|
289
|
+
export async function initializeCloudWalletWithBiometrics(
|
|
290
|
+
edvUrl: string,
|
|
291
|
+
authKey: string,
|
|
292
|
+
biometricData: Buffer,
|
|
293
|
+
identifier: string,
|
|
294
|
+
dataStore?: any
|
|
295
|
+
): Promise<any> {
|
|
296
|
+
const masterKey = await authenticateWithBiometrics(
|
|
297
|
+
edvUrl,
|
|
298
|
+
authKey,
|
|
299
|
+
biometricData,
|
|
300
|
+
identifier
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
return initializeCloudWallet({
|
|
304
|
+
dataStore,
|
|
305
|
+
edvUrl,
|
|
306
|
+
authKey,
|
|
307
|
+
masterKey
|
|
308
|
+
});
|
|
309
|
+
}
|
|
12
310
|
|
|
13
311
|
interface QueuedOperation {
|
|
14
312
|
operation: () => Promise<any>;
|
|
@@ -21,11 +319,10 @@ interface DocumentQueue {
|
|
|
21
319
|
isProcessing: boolean;
|
|
22
320
|
}
|
|
23
321
|
|
|
24
|
-
export async function generateCloudWalletMasterKey(): Promise<{ mnemonic: string; masterKey:
|
|
25
|
-
const mnemonic = await utilCryptoService.mnemonicGenerate(
|
|
322
|
+
export async function generateCloudWalletMasterKey(): Promise<{ mnemonic: string; masterKey: Uint8Array }> {
|
|
323
|
+
const mnemonic = await utilCryptoService.mnemonicGenerate(MNEMONIC_WORD_COUNT);
|
|
26
324
|
|
|
27
|
-
const
|
|
28
|
-
const masterKey = base64url.encode(Buffer.from(seedBytes));
|
|
325
|
+
const masterKey = await utilCryptoService.mnemonicToMiniSecret(mnemonic);
|
|
29
326
|
|
|
30
327
|
return {
|
|
31
328
|
mnemonic,
|
|
@@ -33,9 +330,8 @@ export async function generateCloudWalletMasterKey(): Promise<{ mnemonic: string
|
|
|
33
330
|
};
|
|
34
331
|
}
|
|
35
332
|
|
|
36
|
-
export async function recoverCloudWalletMasterKey(mnemonic: string): Promise<
|
|
37
|
-
const
|
|
38
|
-
const masterKey = base64url.encode(Buffer.from(seedBytes));
|
|
333
|
+
export async function recoverCloudWalletMasterKey(mnemonic: string): Promise<Uint8Array> {
|
|
334
|
+
const masterKey = await utilCryptoService.mnemonicToMiniSecret(mnemonic);
|
|
39
335
|
|
|
40
336
|
return masterKey;
|
|
41
337
|
}
|
|
@@ -49,7 +345,7 @@ export async function initializeCloudWallet({
|
|
|
49
345
|
dataStore?: DataStore;
|
|
50
346
|
edvUrl: string;
|
|
51
347
|
authKey: string;
|
|
52
|
-
masterKey:
|
|
348
|
+
masterKey: Uint8Array;
|
|
53
349
|
}) {
|
|
54
350
|
const {
|
|
55
351
|
hmacKey,
|
|
@@ -174,7 +470,7 @@ export async function initializeCloudWallet({
|
|
|
174
470
|
},
|
|
175
471
|
});
|
|
176
472
|
logger.debug(`Document added to EDV: ${content.id}`);
|
|
177
|
-
} catch(error) {
|
|
473
|
+
} catch (error) {
|
|
178
474
|
logger.error(`Unable to add document ${content.id}: ${error.message}`);
|
|
179
475
|
}
|
|
180
476
|
});
|
|
@@ -185,7 +481,7 @@ export async function initializeCloudWallet({
|
|
|
185
481
|
try {
|
|
186
482
|
logger.debug(`Removing document from EDV: ${documentId}`);
|
|
187
483
|
const edvDocument = await findDocumentByContentId(documentId);
|
|
188
|
-
await edvService.delete({document: edvDocument});
|
|
484
|
+
await edvService.delete({ document: edvDocument });
|
|
189
485
|
// TODO: Remove this once we figure out why the data store is empty after deleting a document
|
|
190
486
|
await pullDocuments();
|
|
191
487
|
logger.debug(`Document removed from EDV: ${documentId}`);
|
|
@@ -259,7 +555,7 @@ export async function initializeCloudWallet({
|
|
|
259
555
|
const allDocs = await edvService.find({});
|
|
260
556
|
|
|
261
557
|
for (const doc of allDocs.documents) {
|
|
262
|
-
await edvService.delete({document: doc});
|
|
558
|
+
await edvService.delete({ document: doc });
|
|
263
559
|
}
|
|
264
560
|
}
|
|
265
561
|
|
|
@@ -24,6 +24,7 @@ export interface ICredentialProvider {
|
|
|
24
24
|
getCredentialStatus(
|
|
25
25
|
credential: Credential,
|
|
26
26
|
): Promise<{status: string; error?: string}>;
|
|
27
|
+
removeCredential(credential: Credential): Promise<void>;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
export function isBBSPlusCredential(credential) {
|
|
@@ -275,6 +276,35 @@ async function syncCredentialStatus({
|
|
|
275
276
|
return statusDocs;
|
|
276
277
|
}
|
|
277
278
|
|
|
279
|
+
/**
|
|
280
|
+
* Removes a credential and its related documents from the wallet
|
|
281
|
+
* @param param0
|
|
282
|
+
* @returns
|
|
283
|
+
*/
|
|
284
|
+
export async function removeCredential({
|
|
285
|
+
wallet,
|
|
286
|
+
credential,
|
|
287
|
+
}: {
|
|
288
|
+
wallet: IWallet;
|
|
289
|
+
credential: Credential | string;
|
|
290
|
+
}): Promise<void> {
|
|
291
|
+
// Allow passing either a credential object or a credential ID
|
|
292
|
+
const credentialId = typeof credential === 'string' ? credential : credential.id;
|
|
293
|
+
|
|
294
|
+
assert(!!credentialId, 'credential ID is required');
|
|
295
|
+
|
|
296
|
+
// Remove the main credential document
|
|
297
|
+
await wallet.removeDocument(credentialId);
|
|
298
|
+
|
|
299
|
+
if (await wallet.getDocumentById(`${credentialId}#witness`)) {
|
|
300
|
+
await wallet.removeDocument(`${credentialId}#witness`);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (await wallet.getDocumentById(`${credentialId}#status`)) {
|
|
304
|
+
await wallet.removeDocument(`${credentialId}#status`);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
278
308
|
export function createCredentialProvider({
|
|
279
309
|
wallet,
|
|
280
310
|
}: {
|
|
@@ -320,6 +350,7 @@ export function createCredentialProvider({
|
|
|
320
350
|
return syncCredentialStatus({wallet, ...props});
|
|
321
351
|
},
|
|
322
352
|
addCredential: credential => addCredential({wallet, credential}),
|
|
353
|
+
removeCredential: credential => removeCredential({wallet, credential}),
|
|
323
354
|
// TODO: move import credential from json or URL to this provider
|
|
324
355
|
};
|
|
325
356
|
}
|