@cooperation/vc-storage 1.0.29 → 1.0.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/models/CredentialEngine.js +48 -48
- package/dist/tests/email.test.js +31 -0
- package/dist/tests/seed.test.js +75 -0
- package/dist/types/tests/email.test.d.ts +1 -0
- package/dist/types/tests/seed.test.d.ts +1 -0
- package/dist/types/utils/decodedSeed.d.ts +18 -0
- package/dist/utils/decodedSeed.js +77 -0
- package/package.json +6 -2
@@ -6,7 +6,7 @@ import { extractKeyPairFromCredential, generateDIDSchema, generateUnsignedRecomm
|
|
6
6
|
import { customDocumentLoader } from '../utils/digitalbazaar.js';
|
7
7
|
import { saveToGoogleDrive } from '../utils/google.js';
|
8
8
|
function delay(ms) {
|
9
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
9
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
10
10
|
}
|
11
11
|
/**
|
12
12
|
* Class representing the Credential Engine.
|
@@ -50,12 +50,19 @@ export class CredentialEngine {
|
|
50
50
|
this.keyPair = key;
|
51
51
|
return key;
|
52
52
|
}
|
53
|
-
generateKeyPair = async (address) => {
|
54
|
-
|
53
|
+
generateKeyPair = async (address, seed) => {
|
54
|
+
// Generate the key pair using the library's method
|
55
|
+
const keyPair = seed
|
56
|
+
? await Ed25519VerificationKey2020.generate({
|
57
|
+
seed: Buffer.from(seed).toString('hex'),
|
58
|
+
})
|
59
|
+
: await Ed25519VerificationKey2020.generate();
|
60
|
+
// Configure key pair attributes
|
55
61
|
const a = address || keyPair.publicKeyMultibase;
|
56
62
|
keyPair.controller = `did:key:${a}`;
|
57
63
|
keyPair.id = `${keyPair.controller}#${a}`;
|
58
64
|
keyPair.revoked = false;
|
65
|
+
// The `signer` is already provided by the `Ed25519VerificationKey2020` instance
|
59
66
|
return keyPair;
|
60
67
|
};
|
61
68
|
async verifyCreds(creds) {
|
@@ -74,11 +81,6 @@ export class CredentialEngine {
|
|
74
81
|
async createDID() {
|
75
82
|
try {
|
76
83
|
const keyPair = await this.generateKeyPair();
|
77
|
-
// const keyFile = await saveToGoogleDrive({
|
78
|
-
// storage: this.storage,
|
79
|
-
// data: keyPair,
|
80
|
-
// type: 'KEYPAIR',
|
81
|
-
// });
|
82
84
|
const didDocument = await generateDIDSchema(keyPair);
|
83
85
|
return { didDocument, keyPair };
|
84
86
|
}
|
@@ -244,43 +246,50 @@ export class CredentialEngine {
|
|
244
246
|
*/
|
245
247
|
async generateAndSignEmailVC(email) {
|
246
248
|
try {
|
247
|
-
// Try to find existing keys and DIDs
|
248
|
-
const existingKeys = await this.findKeysAndDIDs();
|
249
249
|
let keyPair;
|
250
250
|
let didDocument;
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
251
|
+
// Require SEED from environment
|
252
|
+
const encodedSeed = process.env.SEED;
|
253
|
+
if (!encodedSeed) {
|
254
|
+
throw new Error('SEED environment variable not set. Cannot generate or use any DID.');
|
255
255
|
}
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
256
|
+
// Use deterministic keys from environment seed
|
257
|
+
const { getDidFromEnvSeed } = await import('../utils/decodedSeed');
|
258
|
+
const result = await getDidFromEnvSeed();
|
259
|
+
keyPair = result.keyPair;
|
260
|
+
didDocument = result.didDocument;
|
261
|
+
console.log('Using DID from environment seed:', didDocument.id);
|
262
|
+
// Ensure the key has proper ID and controller
|
263
|
+
if (!keyPair.id || !keyPair.controller) {
|
264
|
+
const verificationMethod = didDocument.verificationMethod?.[0] || didDocument.authentication?.[0];
|
265
|
+
if (verificationMethod) {
|
266
|
+
keyPair.id = typeof verificationMethod === 'string' ? verificationMethod : verificationMethod.id;
|
267
|
+
keyPair.controller = didDocument.id;
|
268
|
+
}
|
261
269
|
}
|
270
|
+
console.log('Creating email VC with DID:', didDocument.id);
|
262
271
|
// Generate unsigned email VC
|
263
272
|
const unsignedCredential = {
|
264
273
|
'@context': [
|
265
274
|
'https://www.w3.org/2018/credentials/v1',
|
266
275
|
{
|
267
|
-
|
268
|
-
|
269
|
-
'@id': 'https://example.com/EmailCredential'
|
270
|
-
}
|
271
|
-
}
|
276
|
+
email: 'https://schema.org/email',
|
277
|
+
EmailCredential: {
|
278
|
+
'@id': 'https://example.com/EmailCredential',
|
279
|
+
},
|
280
|
+
},
|
272
281
|
],
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
282
|
+
id: `urn:uuid:${uuidv4()}`,
|
283
|
+
type: ['VerifiableCredential', 'EmailCredential'],
|
284
|
+
issuer: {
|
285
|
+
id: didDocument.id,
|
286
|
+
},
|
287
|
+
issuanceDate: new Date().toISOString(),
|
288
|
+
expirationDate: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000).toISOString(), // 1 year from now
|
289
|
+
credentialSubject: {
|
290
|
+
id: `did:email:${email}`,
|
291
|
+
email: email,
|
277
292
|
},
|
278
|
-
'issuanceDate': new Date().toISOString(),
|
279
|
-
'expirationDate': new Date(Date.now() + 365 * 24 * 60 * 60 * 1000).toISOString(), // 1 year from now
|
280
|
-
'credentialSubject': {
|
281
|
-
'id': `did:email:${email}`,
|
282
|
-
'email': email
|
283
|
-
}
|
284
293
|
};
|
285
294
|
// Sign the VC
|
286
295
|
const suite = new Ed25519Signature2020({
|
@@ -292,14 +301,13 @@ export class CredentialEngine {
|
|
292
301
|
suite,
|
293
302
|
documentLoader: customDocumentLoader,
|
294
303
|
});
|
295
|
-
// Get root folders
|
296
304
|
const rootFolders = await this.storage.findFolders();
|
297
305
|
// Find or create Credentials folder
|
298
306
|
let credentialsFolder = rootFolders.find((f) => f.name === 'Credentials');
|
299
307
|
if (!credentialsFolder) {
|
300
308
|
credentialsFolder = await this.storage.createFolder({
|
301
309
|
folderName: 'Credentials',
|
302
|
-
parentFolderId: 'root'
|
310
|
+
parentFolderId: 'root',
|
303
311
|
});
|
304
312
|
// Wait and re-check to avoid duplicates
|
305
313
|
await delay(1500);
|
@@ -314,7 +322,7 @@ export class CredentialEngine {
|
|
314
322
|
if (!emailVcFolder) {
|
315
323
|
emailVcFolder = await this.storage.createFolder({
|
316
324
|
folderName: 'EMAIL_VC',
|
317
|
-
parentFolderId: credentialsFolder.id
|
325
|
+
parentFolderId: credentialsFolder.id,
|
318
326
|
});
|
319
327
|
// Wait and re-check to avoid duplicates
|
320
328
|
await delay(1500);
|
@@ -326,20 +334,12 @@ export class CredentialEngine {
|
|
326
334
|
// Save the VC in the EMAIL_VC folder
|
327
335
|
const file = await this.storage.saveFile({
|
328
336
|
data: {
|
329
|
-
fileName: `${email}
|
337
|
+
fileName: `${email}`,
|
330
338
|
mimeType: 'application/json',
|
331
|
-
body: signedVC
|
339
|
+
body: signedVC,
|
332
340
|
},
|
333
|
-
folderId: emailVcFolder.id
|
341
|
+
folderId: emailVcFolder.id,
|
334
342
|
});
|
335
|
-
// Only save key pair if it's new
|
336
|
-
if (!existingKeys) {
|
337
|
-
await saveToGoogleDrive({
|
338
|
-
storage: this.storage,
|
339
|
-
data: keyPair,
|
340
|
-
type: 'KEYPAIR',
|
341
|
-
});
|
342
|
-
}
|
343
343
|
return { signedVC, fileId: file.id };
|
344
344
|
}
|
345
345
|
catch (error) {
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import { CredentialEngine } from '../models/CredentialEngine.js';
|
2
|
+
import { GoogleDriveStorage } from '../models/GoogleDriveStorage.js';
|
3
|
+
async function testEmailVC() {
|
4
|
+
try {
|
5
|
+
// Get Google Drive access token from environment
|
6
|
+
const accessToken = 'your access token';
|
7
|
+
// Initialize storage and engine
|
8
|
+
const storage = new GoogleDriveStorage(accessToken);
|
9
|
+
const engine = new CredentialEngine(storage);
|
10
|
+
// Test email
|
11
|
+
const testEmail = 'test@example.com';
|
12
|
+
console.log('Starting email VC generation test...');
|
13
|
+
console.log('Test email:', testEmail);
|
14
|
+
// Generate and sign the email VC
|
15
|
+
const result = await engine.generateAndSignEmailVC(testEmail);
|
16
|
+
console.log('\nTest Results:');
|
17
|
+
console.log('-------------');
|
18
|
+
console.log('File ID:', result.fileId);
|
19
|
+
console.log('Signed VC:', JSON.stringify(result.signedVC, null, 2));
|
20
|
+
// Test retrieving the VC from storage
|
21
|
+
console.log('\nRetrieving VC from storage...');
|
22
|
+
const retrievedVC = await storage.retrieve(result.fileId);
|
23
|
+
console.log('Retrieved VC:', retrievedVC ? 'Success' : 'Failed');
|
24
|
+
console.log('\nTest completed successfully!');
|
25
|
+
}
|
26
|
+
catch (error) {
|
27
|
+
console.error('Test failed:', error);
|
28
|
+
}
|
29
|
+
}
|
30
|
+
// Run the test
|
31
|
+
testEmailVC().catch(console.error);
|
@@ -0,0 +1,75 @@
|
|
1
|
+
// @ts-nocheck
|
2
|
+
import { decodeSecretKeySeed } from 'bnid';
|
3
|
+
import { Ed25519VerificationKey2020 } from '@digitalbazaar/ed25519-verification-key-2020';
|
4
|
+
import { driver as keyDriver } from '@digitalbazaar/did-method-key';
|
5
|
+
import { CryptoLD } from 'crypto-ld';
|
6
|
+
// EXACT SAME SETUP AS YOUR WORKING SCRIPT
|
7
|
+
const cryptoLd = new CryptoLD();
|
8
|
+
cryptoLd.use(Ed25519VerificationKey2020);
|
9
|
+
// Reference decodeSeed implementation
|
10
|
+
const decodeSeed = async (secretKeySeed) => {
|
11
|
+
let secretKeySeedBytes;
|
12
|
+
if (secretKeySeed.startsWith('z')) {
|
13
|
+
secretKeySeedBytes = decodeSecretKeySeed({ secretKeySeed });
|
14
|
+
}
|
15
|
+
else if (secretKeySeed.length >= 32) {
|
16
|
+
secretKeySeedBytes = new TextEncoder().encode(secretKeySeed).slice(0, 32);
|
17
|
+
}
|
18
|
+
else {
|
19
|
+
throw TypeError('"secretKeySeed" must be at least 32 bytes, preferably multibase-encoded.');
|
20
|
+
}
|
21
|
+
return secretKeySeedBytes;
|
22
|
+
};
|
23
|
+
// EXACT SAME FUNCTION AS YOUR WORKING generateSeed BUT WITH PREDEFINED SEEDS
|
24
|
+
async function testGenerateSeed(encodedSeed) {
|
25
|
+
const seed = await decodeSeed(encodedSeed);
|
26
|
+
let didDocument;
|
27
|
+
// EXACT SAME CODE AS YOUR WORKING SCRIPT
|
28
|
+
const didKeyDriver = keyDriver();
|
29
|
+
didKeyDriver.use({
|
30
|
+
multibaseMultikeyHeader: 'z6Mk',
|
31
|
+
fromMultibase: Ed25519VerificationKey2020.from,
|
32
|
+
});
|
33
|
+
const verificationKeyPair = await Ed25519VerificationKey2020.generate({
|
34
|
+
seed,
|
35
|
+
});
|
36
|
+
({ didDocument } = await didKeyDriver.fromKeyPair({ verificationKeyPair }));
|
37
|
+
const did = didDocument.id;
|
38
|
+
return { seed: encodedSeed, decodedSeed: seed, did, didDocument, publicKey: verificationKeyPair.publicKeyMultibase };
|
39
|
+
}
|
40
|
+
// Test with different seeds
|
41
|
+
async function testDifferentSeeds() {
|
42
|
+
console.log('Testing different seeds with EXACT working script approach:\n');
|
43
|
+
const testSeeds = [
|
44
|
+
'z1AjjnVSNmZot5TJcgtFYhn83WASAcphrqgE95AQ436hrGR',
|
45
|
+
'z1AibHGc5Zek2kvdSyC22xGLZCvV7b77KWBFWiQmZGnD6rV',
|
46
|
+
'z1AgfwVqaN3zX1kw2iTvLLekCbXNRJA7XbiFZCQv9TfgKCT',
|
47
|
+
];
|
48
|
+
const results = [];
|
49
|
+
for (const testSeed of testSeeds) {
|
50
|
+
console.log(`Testing seed: ${testSeed}`);
|
51
|
+
console.log(`Decoded hex: ${Buffer.from(await decodeSeed(testSeed)).toString('hex')}`);
|
52
|
+
try {
|
53
|
+
const result = await testGenerateSeed(testSeed);
|
54
|
+
console.log(`Generated DID: ${result.did}`);
|
55
|
+
console.log(`Public key: ${result.publicKey}`);
|
56
|
+
results.push(result);
|
57
|
+
}
|
58
|
+
catch (error) {
|
59
|
+
console.error(`Error: ${error.message}`);
|
60
|
+
}
|
61
|
+
console.log('---');
|
62
|
+
}
|
63
|
+
console.log('\nResults:');
|
64
|
+
const uniqueDids = new Set(results.map((r) => r.did));
|
65
|
+
const uniqueKeys = new Set(results.map((r) => r.publicKey));
|
66
|
+
console.log(`Total results: ${results.length}`);
|
67
|
+
console.log(`Unique DIDs: ${uniqueDids.size}`);
|
68
|
+
console.log(`Unique public keys: ${uniqueKeys.size}`);
|
69
|
+
console.log(`Expected unique: ${testSeeds.length}`);
|
70
|
+
console.log(`Success: ${uniqueDids.size === testSeeds.length ? '✓ YES' : '✗ NO'}`);
|
71
|
+
results.forEach((result, i) => {
|
72
|
+
console.log(`${i + 1}. ${result.seed} -> ${result.did}`);
|
73
|
+
});
|
74
|
+
}
|
75
|
+
testDifferentSeeds().catch(console.error);
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1,18 @@
|
|
1
|
+
export declare function decodeSeed(encodedSeed: string): Promise<Uint8Array>;
|
2
|
+
export declare const getDidFromEnvSeed: () => Promise<{
|
3
|
+
keyPair: any;
|
4
|
+
didDocument: {
|
5
|
+
'@context': string[];
|
6
|
+
id: string;
|
7
|
+
verificationMethod: {
|
8
|
+
id: string;
|
9
|
+
type: string;
|
10
|
+
controller: string;
|
11
|
+
publicKeyMultibase: any;
|
12
|
+
}[];
|
13
|
+
authentication: string[];
|
14
|
+
assertionMethod: string[];
|
15
|
+
capabilityDelegation: string[];
|
16
|
+
capabilityInvocation: string[];
|
17
|
+
};
|
18
|
+
}>;
|
@@ -0,0 +1,77 @@
|
|
1
|
+
import { Ed25519VerificationKey2020 } from '@digitalbazaar/ed25519-verification-key-2020';
|
2
|
+
import { base58btc } from 'multiformats/bases/base58';
|
3
|
+
export async function decodeSeed(encodedSeed) {
|
4
|
+
try {
|
5
|
+
// Ensure the seed has the 'z' prefix
|
6
|
+
const seedWithPrefix = encodedSeed.startsWith('z') ? encodedSeed : `z${encodedSeed}`;
|
7
|
+
// Decode the entire string including the prefix
|
8
|
+
const decoded = base58btc.decode(seedWithPrefix);
|
9
|
+
// The decoded data includes a multicodec header (2 bytes: 0x00 0x20)
|
10
|
+
// We need to remove this header to get the actual 32-byte seed
|
11
|
+
if (decoded.length === 34 && decoded[0] === 0x00 && decoded[1] === 0x20) {
|
12
|
+
// Skip the first 2 bytes to get the actual seed
|
13
|
+
const seed = new Uint8Array(decoded.slice(2));
|
14
|
+
console.log('Decoded seed (removed 2-byte header):', Buffer.from(seed).toString('hex'));
|
15
|
+
return seed;
|
16
|
+
}
|
17
|
+
// If it's already 32 bytes, return as is
|
18
|
+
if (decoded.length === 32) {
|
19
|
+
console.log('Decoded seed (already 32 bytes):', Buffer.from(decoded).toString('hex'));
|
20
|
+
return new Uint8Array(decoded);
|
21
|
+
}
|
22
|
+
throw new Error(`Invalid seed length: ${decoded.length} bytes (expected 32 bytes after removing any headers)`);
|
23
|
+
}
|
24
|
+
catch (error) {
|
25
|
+
console.error('Error decoding seed:', error);
|
26
|
+
throw error;
|
27
|
+
}
|
28
|
+
}
|
29
|
+
export const getDidFromEnvSeed = async () => {
|
30
|
+
// Get seed from environment variable
|
31
|
+
const encodedSeed = process.env.SEED;
|
32
|
+
if (!encodedSeed) {
|
33
|
+
throw new Error('SEED environment variable not set');
|
34
|
+
}
|
35
|
+
console.log('Using seed from environment:', encodedSeed);
|
36
|
+
// Decode the seed (this will remove the 2-byte header if present)
|
37
|
+
const seed = await decodeSeed(encodedSeed);
|
38
|
+
console.log('Decoded seed length:', seed.length);
|
39
|
+
// Create key pair from seed
|
40
|
+
const verificationKeyPair = await Ed25519VerificationKey2020.generate({
|
41
|
+
seed: seed,
|
42
|
+
type: 'Ed25519VerificationKey2020',
|
43
|
+
});
|
44
|
+
console.log('Generated public key:', verificationKeyPair.publicKeyMultibase);
|
45
|
+
// Create DID manually to avoid key agreement issues
|
46
|
+
const fingerprint = verificationKeyPair.fingerprint();
|
47
|
+
const did = `did:key:${fingerprint}`;
|
48
|
+
// Create a proper DID document
|
49
|
+
const didDocument = {
|
50
|
+
'@context': [
|
51
|
+
'https://www.w3.org/ns/did/v1',
|
52
|
+
'https://w3id.org/security/suites/ed25519-2020/v1',
|
53
|
+
'https://w3id.org/security/suites/x25519-2020/v1',
|
54
|
+
],
|
55
|
+
id: did,
|
56
|
+
verificationMethod: [
|
57
|
+
{
|
58
|
+
id: `${did}#${fingerprint}`,
|
59
|
+
type: 'Ed25519VerificationKey2020',
|
60
|
+
controller: did,
|
61
|
+
publicKeyMultibase: verificationKeyPair.publicKeyMultibase,
|
62
|
+
},
|
63
|
+
],
|
64
|
+
authentication: [`${did}#${fingerprint}`],
|
65
|
+
assertionMethod: [`${did}#${fingerprint}`],
|
66
|
+
capabilityDelegation: [`${did}#${fingerprint}`],
|
67
|
+
capabilityInvocation: [`${did}#${fingerprint}`],
|
68
|
+
};
|
69
|
+
// Set the key ID and controller on the key pair
|
70
|
+
verificationKeyPair.id = `${did}#${fingerprint}`;
|
71
|
+
verificationKeyPair.controller = did;
|
72
|
+
console.log('Generated DID:', did);
|
73
|
+
return {
|
74
|
+
keyPair: verificationKeyPair,
|
75
|
+
didDocument,
|
76
|
+
};
|
77
|
+
};
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@cooperation/vc-storage",
|
3
3
|
"type": "module",
|
4
|
-
"version": "1.0.
|
4
|
+
"version": "1.0.30",
|
5
5
|
"description": "Sign and store your verifiable credentials.",
|
6
6
|
"main": "dist/index.js",
|
7
7
|
"types": "dist/types/index.d.ts",
|
@@ -17,12 +17,16 @@
|
|
17
17
|
"license": "ISC",
|
18
18
|
"dependencies": {
|
19
19
|
"@digitalbazaar/did-method-key": "^5.2.0",
|
20
|
-
"@digitalbazaar/ed25519-signature-2020": "^5.
|
20
|
+
"@digitalbazaar/ed25519-signature-2020": "^5.4.0",
|
21
21
|
"@digitalbazaar/ed25519-verification-key-2020": "^4.1.0",
|
22
22
|
"@digitalbazaar/vc": "^6.3.0",
|
23
|
+
"add": "^2.0.6",
|
24
|
+
"bnid": "^3.0.0",
|
23
25
|
"crypto-js": "^4.2.0",
|
26
|
+
"crypto-ld": "^7.0.0",
|
24
27
|
"ethers": "^6.13.2",
|
25
28
|
"jest": "^29.7.0",
|
29
|
+
"multiformats": "^13.3.6",
|
26
30
|
"ts-jest": "^29.2.5",
|
27
31
|
"ts-node": "^10.9.2",
|
28
32
|
"tsc": "^2.0.4",
|