@cooperation/vc-storage 1.0.29 → 1.0.31

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.
@@ -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
- const keyPair = await Ed25519VerificationKey2020.generate();
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
  }
@@ -242,45 +244,51 @@ export class CredentialEngine {
242
244
  * @param {string} email - The email address to create the VC for
243
245
  * @returns {Promise<{signedVC: any, fileId: string}>} The signed VC and its Google Drive file ID
244
246
  */
245
- async generateAndSignEmailVC(email) {
247
+ async generateAndSignEmailVC(email, encodedSeed) {
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
- if (existingKeys) {
252
- // Use existing keys and DID
253
- keyPair = existingKeys.keyPair;
254
- didDocument = existingKeys.didDocument;
251
+ // Require SEED from environment
252
+ if (!encodedSeed) {
253
+ throw new Error('SEED environment variable not set. Cannot generate or use any DID.');
255
254
  }
256
- else {
257
- // Generate new key pair and DID if none exist
258
- keyPair = await this.generateKeyPair();
259
- const result = await this.createDID();
260
- didDocument = result.didDocument;
255
+ // Use deterministic keys from environment seed
256
+ const { getDidFromEnvSeed } = await import('../utils/decodedSeed');
257
+ const result = await getDidFromEnvSeed();
258
+ keyPair = result.keyPair;
259
+ didDocument = result.didDocument;
260
+ console.log('Using DID from environment seed:', didDocument.id);
261
+ // Ensure the key has proper ID and controller
262
+ if (!keyPair.id || !keyPair.controller) {
263
+ const verificationMethod = didDocument.verificationMethod?.[0] || didDocument.authentication?.[0];
264
+ if (verificationMethod) {
265
+ keyPair.id = typeof verificationMethod === 'string' ? verificationMethod : verificationMethod.id;
266
+ keyPair.controller = didDocument.id;
267
+ }
261
268
  }
269
+ console.log('Creating email VC with DID:', didDocument.id);
262
270
  // Generate unsigned email VC
263
271
  const unsignedCredential = {
264
272
  '@context': [
265
273
  'https://www.w3.org/2018/credentials/v1',
266
274
  {
267
- 'email': 'https://schema.org/email',
268
- 'EmailCredential': {
269
- '@id': 'https://example.com/EmailCredential'
270
- }
271
- }
275
+ email: 'https://schema.org/email',
276
+ EmailCredential: {
277
+ '@id': 'https://example.com/EmailCredential',
278
+ },
279
+ },
272
280
  ],
273
- 'id': `urn:uuid:${uuidv4()}`,
274
- 'type': ['VerifiableCredential', 'EmailCredential'],
275
- 'issuer': {
276
- 'id': didDocument.id
281
+ id: `urn:uuid:${uuidv4()}`,
282
+ type: ['VerifiableCredential', 'EmailCredential'],
283
+ issuer: {
284
+ id: didDocument.id,
285
+ },
286
+ issuanceDate: new Date().toISOString(),
287
+ expirationDate: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000).toISOString(), // 1 year from now
288
+ credentialSubject: {
289
+ id: `did:email:${email}`,
290
+ email: email,
277
291
  },
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
292
  };
285
293
  // Sign the VC
286
294
  const suite = new Ed25519Signature2020({
@@ -292,14 +300,13 @@ export class CredentialEngine {
292
300
  suite,
293
301
  documentLoader: customDocumentLoader,
294
302
  });
295
- // Get root folders
296
303
  const rootFolders = await this.storage.findFolders();
297
304
  // Find or create Credentials folder
298
305
  let credentialsFolder = rootFolders.find((f) => f.name === 'Credentials');
299
306
  if (!credentialsFolder) {
300
307
  credentialsFolder = await this.storage.createFolder({
301
308
  folderName: 'Credentials',
302
- parentFolderId: 'root'
309
+ parentFolderId: 'root',
303
310
  });
304
311
  // Wait and re-check to avoid duplicates
305
312
  await delay(1500);
@@ -314,7 +321,7 @@ export class CredentialEngine {
314
321
  if (!emailVcFolder) {
315
322
  emailVcFolder = await this.storage.createFolder({
316
323
  folderName: 'EMAIL_VC',
317
- parentFolderId: credentialsFolder.id
324
+ parentFolderId: credentialsFolder.id,
318
325
  });
319
326
  // Wait and re-check to avoid duplicates
320
327
  await delay(1500);
@@ -326,20 +333,12 @@ export class CredentialEngine {
326
333
  // Save the VC in the EMAIL_VC folder
327
334
  const file = await this.storage.saveFile({
328
335
  data: {
329
- fileName: `${email}.vc`,
336
+ fileName: `${email}`,
330
337
  mimeType: 'application/json',
331
- body: signedVC
338
+ body: signedVC,
332
339
  },
333
- folderId: emailVcFolder.id
340
+ folderId: emailVcFolder.id,
334
341
  });
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
342
  return { signedVC, fileId: file.id };
344
343
  }
345
344
  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.29",
4
+ "version": "1.0.31",
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.3.0",
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",