@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.
@@ -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
  }
@@ -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
- if (existingKeys) {
252
- // Use existing keys and DID
253
- keyPair = existingKeys.keyPair;
254
- didDocument = existingKeys.didDocument;
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
- 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;
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
- 'email': 'https://schema.org/email',
268
- 'EmailCredential': {
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
- 'id': `urn:uuid:${uuidv4()}`,
274
- 'type': ['VerifiableCredential', 'EmailCredential'],
275
- 'issuer': {
276
- 'id': didDocument.id
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}.vc`,
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.29",
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.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",