@ajna-inc/openbadges 0.1.0
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/LICENSE +202 -0
- package/build/OpenBadgesModule.d.ts +10 -0
- package/build/OpenBadgesModule.js +75 -0
- package/build/OpenBadgesModule.js.map +1 -0
- package/build/OpenBadgesModuleConfig.d.ts +96 -0
- package/build/OpenBadgesModuleConfig.js +50 -0
- package/build/OpenBadgesModuleConfig.js.map +1 -0
- package/build/api/OpenBadgesApi.d.ts +48 -0
- package/build/api/OpenBadgesApi.js +81 -0
- package/build/api/OpenBadgesApi.js.map +1 -0
- package/build/api/index.d.ts +1 -0
- package/build/api/index.js +18 -0
- package/build/api/index.js.map +1 -0
- package/build/constants.d.ts +12 -0
- package/build/constants.js +27 -0
- package/build/constants.js.map +1 -0
- package/build/cryptosuites/EcdsaSd2023.d.ts +143 -0
- package/build/cryptosuites/EcdsaSd2023.js +518 -0
- package/build/cryptosuites/EcdsaSd2023.js.map +1 -0
- package/build/cryptosuites/EddsaRdfc2022.d.ts +112 -0
- package/build/cryptosuites/EddsaRdfc2022.js +356 -0
- package/build/cryptosuites/EddsaRdfc2022.js.map +1 -0
- package/build/cryptosuites/constants.d.ts +14 -0
- package/build/cryptosuites/constants.js +22 -0
- package/build/cryptosuites/constants.js.map +1 -0
- package/build/cryptosuites/contextPreprocessor.d.ts +24 -0
- package/build/cryptosuites/contextPreprocessor.js +127 -0
- package/build/cryptosuites/contextPreprocessor.js.map +1 -0
- package/build/cryptosuites/dataIntegrityV2Context.d.ts +144 -0
- package/build/cryptosuites/dataIntegrityV2Context.js +86 -0
- package/build/cryptosuites/dataIntegrityV2Context.js.map +1 -0
- package/build/cryptosuites/index.d.ts +11 -0
- package/build/cryptosuites/index.js +33 -0
- package/build/cryptosuites/index.js.map +1 -0
- package/build/http/OpenBadgesHttpModule.d.ts +9 -0
- package/build/http/OpenBadgesHttpModule.js +120 -0
- package/build/http/OpenBadgesHttpModule.js.map +1 -0
- package/build/http/OpenBadgesHttpModuleConfig.d.ts +55 -0
- package/build/http/OpenBadgesHttpModuleConfig.js +78 -0
- package/build/http/OpenBadgesHttpModuleConfig.js.map +1 -0
- package/build/http/endpoints/authorize.d.ts +3 -0
- package/build/http/endpoints/authorize.js +79 -0
- package/build/http/endpoints/authorize.js.map +1 -0
- package/build/http/endpoints/consent.d.ts +3 -0
- package/build/http/endpoints/consent.js +25 -0
- package/build/http/endpoints/consent.js.map +1 -0
- package/build/http/endpoints/credentials.d.ts +4 -0
- package/build/http/endpoints/credentials.js +85 -0
- package/build/http/endpoints/credentials.js.map +1 -0
- package/build/http/endpoints/did.d.ts +3 -0
- package/build/http/endpoints/did.js +48 -0
- package/build/http/endpoints/did.js.map +1 -0
- package/build/http/endpoints/introspect.d.ts +3 -0
- package/build/http/endpoints/introspect.js +37 -0
- package/build/http/endpoints/introspect.js.map +1 -0
- package/build/http/endpoints/jwks.d.ts +3 -0
- package/build/http/endpoints/jwks.js +46 -0
- package/build/http/endpoints/jwks.js.map +1 -0
- package/build/http/endpoints/profile.d.ts +4 -0
- package/build/http/endpoints/profile.js +58 -0
- package/build/http/endpoints/profile.js.map +1 -0
- package/build/http/endpoints/refresh.d.ts +15 -0
- package/build/http/endpoints/refresh.js +134 -0
- package/build/http/endpoints/refresh.js.map +1 -0
- package/build/http/endpoints/registration.d.ts +3 -0
- package/build/http/endpoints/registration.js +42 -0
- package/build/http/endpoints/registration.js.map +1 -0
- package/build/http/endpoints/revoke.d.ts +3 -0
- package/build/http/endpoints/revoke.js +38 -0
- package/build/http/endpoints/revoke.js.map +1 -0
- package/build/http/endpoints/serviceDescription.d.ts +3 -0
- package/build/http/endpoints/serviceDescription.js +52 -0
- package/build/http/endpoints/serviceDescription.js.map +1 -0
- package/build/http/endpoints/statusList.d.ts +10 -0
- package/build/http/endpoints/statusList.js +95 -0
- package/build/http/endpoints/statusList.js.map +1 -0
- package/build/http/endpoints/token.d.ts +3 -0
- package/build/http/endpoints/token.js +147 -0
- package/build/http/endpoints/token.js.map +1 -0
- package/build/http/middleware/auth.d.ts +5 -0
- package/build/http/middleware/auth.js +48 -0
- package/build/http/middleware/auth.js.map +1 -0
- package/build/http/router.d.ts +13 -0
- package/build/http/router.js +36 -0
- package/build/http/router.js.map +1 -0
- package/build/http/tenants.d.ts +2 -0
- package/build/http/tenants.js +20 -0
- package/build/http/tenants.js.map +1 -0
- package/build/http/util/auth.d.ts +8 -0
- package/build/http/util/auth.js +43 -0
- package/build/http/util/auth.js.map +1 -0
- package/build/index.d.ts +46 -0
- package/build/index.js +71 -0
- package/build/index.js.map +1 -0
- package/build/models/ClrCredential.d.ts +112 -0
- package/build/models/ClrCredential.js +52 -0
- package/build/models/ClrCredential.js.map +1 -0
- package/build/models/EndorsementCredential.d.ts +89 -0
- package/build/models/EndorsementCredential.js +11 -0
- package/build/models/EndorsementCredential.js.map +1 -0
- package/build/models/StatusListCredential.d.ts +81 -0
- package/build/models/StatusListCredential.js +28 -0
- package/build/models/StatusListCredential.js.map +1 -0
- package/build/models/index.d.ts +8 -0
- package/build/models/index.js +25 -0
- package/build/models/index.js.map +1 -0
- package/build/repository/OpenBadgeCredentialRecord.d.ts +44 -0
- package/build/repository/OpenBadgeCredentialRecord.js +46 -0
- package/build/repository/OpenBadgeCredentialRecord.js.map +1 -0
- package/build/repository/OpenBadgeCredentialRepository.d.ts +8 -0
- package/build/repository/OpenBadgeCredentialRepository.js +38 -0
- package/build/repository/OpenBadgeCredentialRepository.js.map +1 -0
- package/build/repository/OpenBadgesAuthCodeRecord.d.ts +35 -0
- package/build/repository/OpenBadgesAuthCodeRecord.js +28 -0
- package/build/repository/OpenBadgesAuthCodeRecord.js.map +1 -0
- package/build/repository/OpenBadgesAuthCodeRepository.d.ts +6 -0
- package/build/repository/OpenBadgesAuthCodeRepository.js +32 -0
- package/build/repository/OpenBadgesAuthCodeRepository.js.map +1 -0
- package/build/repository/OpenBadgesConsentRecord.d.ts +24 -0
- package/build/repository/OpenBadgesConsentRecord.js +23 -0
- package/build/repository/OpenBadgesConsentRecord.js.map +1 -0
- package/build/repository/OpenBadgesConsentRepository.d.ts +6 -0
- package/build/repository/OpenBadgesConsentRepository.js +32 -0
- package/build/repository/OpenBadgesConsentRepository.js.map +1 -0
- package/build/repository/OpenBadgesKeyBindingRecord.d.ts +24 -0
- package/build/repository/OpenBadgesKeyBindingRecord.js +32 -0
- package/build/repository/OpenBadgesKeyBindingRecord.js.map +1 -0
- package/build/repository/OpenBadgesKeyBindingRepository.d.ts +7 -0
- package/build/repository/OpenBadgesKeyBindingRepository.js +35 -0
- package/build/repository/OpenBadgesKeyBindingRepository.js.map +1 -0
- package/build/repository/OpenBadgesOAuthRecord.d.ts +35 -0
- package/build/repository/OpenBadgesOAuthRecord.js +25 -0
- package/build/repository/OpenBadgesOAuthRecord.js.map +1 -0
- package/build/repository/OpenBadgesOAuthRepository.d.ts +8 -0
- package/build/repository/OpenBadgesOAuthRepository.js +38 -0
- package/build/repository/OpenBadgesOAuthRepository.js.map +1 -0
- package/build/repository/OpenBadgesProfileRecord.d.ts +21 -0
- package/build/repository/OpenBadgesProfileRecord.js +22 -0
- package/build/repository/OpenBadgesProfileRecord.js.map +1 -0
- package/build/repository/OpenBadgesProfileRepository.d.ts +6 -0
- package/build/repository/OpenBadgesProfileRepository.js +32 -0
- package/build/repository/OpenBadgesProfileRepository.js.map +1 -0
- package/build/repository/OpenBadgesRevocationCacheRecord.d.ts +23 -0
- package/build/repository/OpenBadgesRevocationCacheRecord.js +23 -0
- package/build/repository/OpenBadgesRevocationCacheRecord.js.map +1 -0
- package/build/repository/OpenBadgesRevocationCacheRepository.d.ts +6 -0
- package/build/repository/OpenBadgesRevocationCacheRepository.js +32 -0
- package/build/repository/OpenBadgesRevocationCacheRepository.js.map +1 -0
- package/build/repository/OpenBadgesServiceDescriptionRecord.d.ts +21 -0
- package/build/repository/OpenBadgesServiceDescriptionRecord.js +22 -0
- package/build/repository/OpenBadgesServiceDescriptionRecord.js.map +1 -0
- package/build/repository/OpenBadgesServiceDescriptionRepository.d.ts +6 -0
- package/build/repository/OpenBadgesServiceDescriptionRepository.js +32 -0
- package/build/repository/OpenBadgesServiceDescriptionRepository.js.map +1 -0
- package/build/repository/OpenBadgesTokenRecord.d.ts +39 -0
- package/build/repository/OpenBadgesTokenRecord.js +36 -0
- package/build/repository/OpenBadgesTokenRecord.js.map +1 -0
- package/build/repository/OpenBadgesTokenRepository.d.ts +9 -0
- package/build/repository/OpenBadgesTokenRepository.js +45 -0
- package/build/repository/OpenBadgesTokenRepository.js.map +1 -0
- package/build/repository/StatusListRecord.d.ts +49 -0
- package/build/repository/StatusListRecord.js +47 -0
- package/build/repository/StatusListRecord.js.map +1 -0
- package/build/repository/StatusListRepository.d.ts +24 -0
- package/build/repository/StatusListRepository.js +52 -0
- package/build/repository/StatusListRepository.js.map +1 -0
- package/build/repository/index.d.ts +18 -0
- package/build/repository/index.js +35 -0
- package/build/repository/index.js.map +1 -0
- package/build/services/AchievementValidator.d.ts +158 -0
- package/build/services/AchievementValidator.js +238 -0
- package/build/services/AchievementValidator.js.map +1 -0
- package/build/services/ConsumerService.d.ts +24 -0
- package/build/services/ConsumerService.js +143 -0
- package/build/services/ConsumerService.js.map +1 -0
- package/build/services/ContextService.d.ts +14 -0
- package/build/services/ContextService.js +54 -0
- package/build/services/ContextService.js.map +1 -0
- package/build/services/DataIntegrityService.d.ts +51 -0
- package/build/services/DataIntegrityService.js +134 -0
- package/build/services/DataIntegrityService.js.map +1 -0
- package/build/services/DidCommLinkService.d.ts +7 -0
- package/build/services/DidCommLinkService.js +20 -0
- package/build/services/DidCommLinkService.js.map +1 -0
- package/build/services/DisplayMapper.d.ts +9 -0
- package/build/services/DisplayMapper.js +26 -0
- package/build/services/DisplayMapper.js.map +1 -0
- package/build/services/IssuerService.d.ts +38 -0
- package/build/services/IssuerService.js +225 -0
- package/build/services/IssuerService.js.map +1 -0
- package/build/services/JwtService.d.ts +19 -0
- package/build/services/JwtService.js +229 -0
- package/build/services/JwtService.js.map +1 -0
- package/build/services/KeyService.d.ts +102 -0
- package/build/services/KeyService.js +439 -0
- package/build/services/KeyService.js.map +1 -0
- package/build/services/OAuthClient.d.ts +26 -0
- package/build/services/OAuthClient.js +127 -0
- package/build/services/OAuthClient.js.map +1 -0
- package/build/services/ProofService.d.ts +15 -0
- package/build/services/ProofService.js +43 -0
- package/build/services/ProofService.js.map +1 -0
- package/build/services/RevocationService.d.ts +59 -0
- package/build/services/RevocationService.js +319 -0
- package/build/services/RevocationService.js.map +1 -0
- package/build/services/VerifyService.d.ts +17 -0
- package/build/services/VerifyService.js +54 -0
- package/build/services/VerifyService.js.map +1 -0
- package/build/services/crypto/CryptoDriver.d.ts +9 -0
- package/build/services/crypto/CryptoDriver.js +7 -0
- package/build/services/crypto/CryptoDriver.js.map +1 -0
- package/build/services/crypto/JsonLdCryptoDriver.d.ts +17 -0
- package/build/services/crypto/JsonLdCryptoDriver.js +45 -0
- package/build/services/crypto/JsonLdCryptoDriver.js.map +1 -0
- package/build/services/crypto/JwtCryptoDriver.d.ts +13 -0
- package/build/services/crypto/JwtCryptoDriver.js +42 -0
- package/build/services/crypto/JwtCryptoDriver.js.map +1 -0
- package/build/services/index.d.ts +12 -0
- package/build/services/index.js +29 -0
- package/build/services/index.js.map +1 -0
- package/build/utils/validate.d.ts +17 -0
- package/build/utils/validate.js +107 -0
- package/build/utils/validate.js.map +1 -0
- package/package.json +57 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VM_TYPE_ED25519_2020 = exports.VM_TYPE_MULTIKEY = exports.PROOF_TYPE_ED25519_2020 = exports.PROOF_TYPE_DATA_INTEGRITY = exports.CRYPTOSUITE_EDDSA_RDFC_2022 = exports.CRYPTOSUITE_ECDSA_SD_2023 = exports.STATUS_LIST_2021 = exports.ONE_EDTECH_REVOCATION_LIST = exports.DI_ECDSA_SD_2023 = exports.DI_EDDSA_RDFC_2022 = exports.CLR_TYPES = exports.ENDORSEMENT_TYPES = exports.REQUIRED_TYPES = exports.DATA_INTEGRITY_V2_CONTEXT = exports.CLR_V2_CONTEXT = exports.OBV3_CONTEXT = exports.VC_V2_CONTEXT = void 0;
|
|
4
|
+
// W3C and OpenBadges context URLs
|
|
5
|
+
exports.VC_V2_CONTEXT = 'https://www.w3.org/ns/credentials/v2';
|
|
6
|
+
exports.OBV3_CONTEXT = 'https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json';
|
|
7
|
+
exports.CLR_V2_CONTEXT = 'https://purl.imsglobal.org/spec/clr/v2p0/context-2.0.1.json';
|
|
8
|
+
exports.DATA_INTEGRITY_V2_CONTEXT = 'https://w3id.org/security/data-integrity/v2';
|
|
9
|
+
// Required credential types
|
|
10
|
+
exports.REQUIRED_TYPES = ['VerifiableCredential', 'OpenBadgeCredential'];
|
|
11
|
+
exports.ENDORSEMENT_TYPES = ['VerifiableCredential', 'EndorsementCredential'];
|
|
12
|
+
exports.CLR_TYPES = ['VerifiableCredential', 'ClrCredential'];
|
|
13
|
+
// Cryptosuite identifiers
|
|
14
|
+
exports.DI_EDDSA_RDFC_2022 = 'eddsa-rdfc-2022';
|
|
15
|
+
exports.DI_ECDSA_SD_2023 = 'ecdsa-sd-2023';
|
|
16
|
+
// Revocation
|
|
17
|
+
exports.ONE_EDTECH_REVOCATION_LIST = '1EdTechRevocationList';
|
|
18
|
+
exports.STATUS_LIST_2021 = 'StatusList2021';
|
|
19
|
+
// Re-export cryptosuite constants for convenience
|
|
20
|
+
var cryptosuites_1 = require("./cryptosuites");
|
|
21
|
+
Object.defineProperty(exports, "CRYPTOSUITE_ECDSA_SD_2023", { enumerable: true, get: function () { return cryptosuites_1.CRYPTOSUITE_ECDSA_SD_2023; } });
|
|
22
|
+
Object.defineProperty(exports, "CRYPTOSUITE_EDDSA_RDFC_2022", { enumerable: true, get: function () { return cryptosuites_1.CRYPTOSUITE_EDDSA_RDFC_2022; } });
|
|
23
|
+
Object.defineProperty(exports, "PROOF_TYPE_DATA_INTEGRITY", { enumerable: true, get: function () { return cryptosuites_1.PROOF_TYPE_DATA_INTEGRITY; } });
|
|
24
|
+
Object.defineProperty(exports, "PROOF_TYPE_ED25519_2020", { enumerable: true, get: function () { return cryptosuites_1.PROOF_TYPE_ED25519_2020; } });
|
|
25
|
+
Object.defineProperty(exports, "VM_TYPE_MULTIKEY", { enumerable: true, get: function () { return cryptosuites_1.VM_TYPE_MULTIKEY; } });
|
|
26
|
+
Object.defineProperty(exports, "VM_TYPE_ED25519_2020", { enumerable: true, get: function () { return cryptosuites_1.VM_TYPE_ED25519_2020; } });
|
|
27
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":";;;AAAA,kCAAkC;AACrB,QAAA,aAAa,GAAG,sCAAsC,CAAA;AACtD,QAAA,YAAY,GAAG,4DAA4D,CAAA;AAC3E,QAAA,cAAc,GAAG,6DAA6D,CAAA;AAC9E,QAAA,yBAAyB,GAAG,6CAA6C,CAAA;AAEtF,4BAA4B;AACf,QAAA,cAAc,GAAG,CAAC,sBAAsB,EAAE,qBAAqB,CAAC,CAAA;AAChE,QAAA,iBAAiB,GAAG,CAAC,sBAAsB,EAAE,uBAAuB,CAAC,CAAA;AACrE,QAAA,SAAS,GAAG,CAAC,sBAAsB,EAAE,eAAe,CAAC,CAAA;AAElE,0BAA0B;AACb,QAAA,kBAAkB,GAAG,iBAAiB,CAAA;AACtC,QAAA,gBAAgB,GAAG,eAAe,CAAA;AAE/C,aAAa;AACA,QAAA,0BAA0B,GAAG,uBAAuB,CAAA;AACpD,QAAA,gBAAgB,GAAG,gBAAgB,CAAA;AAEhD,kDAAkD;AAClD,+CAOuB;AANrB,yHAAA,yBAAyB,OAAA;AACzB,2HAAA,2BAA2B,OAAA;AAC3B,yHAAA,yBAAyB,OAAA;AACzB,uHAAA,uBAAuB,OAAA;AACvB,gHAAA,gBAAgB,OAAA;AAChB,oHAAA,oBAAoB,OAAA"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ECDSA SD 2023 Cryptosuite
|
|
3
|
+
*
|
|
4
|
+
* Implements the ecdsa-sd-2023 cryptosuite for Data Integrity proofs with
|
|
5
|
+
* selective disclosure support.
|
|
6
|
+
*
|
|
7
|
+
* Spec: https://www.w3.org/TR/vc-di-ecdsa/#ecdsa-sd-2023
|
|
8
|
+
*
|
|
9
|
+
* Key features:
|
|
10
|
+
* - Uses ECDSA P-256 (secp256r1) signature algorithm
|
|
11
|
+
* - Supports selective disclosure of claims
|
|
12
|
+
* - Uses mandatory and selective pointers for claim selection
|
|
13
|
+
* - Produces DataIntegrityProof with cryptosuite: 'ecdsa-sd-2023'
|
|
14
|
+
*
|
|
15
|
+
* Note: This implementation provides the structure and interface.
|
|
16
|
+
* Full selective disclosure requires BBS+ style commitments which would need
|
|
17
|
+
* additional crypto libraries. For now, this provides basic ECDSA signing
|
|
18
|
+
* with the correct proof structure.
|
|
19
|
+
*/
|
|
20
|
+
import { CRYPTOSUITE_ECDSA_SD_2023, PROOF_TYPE_DATA_INTEGRITY } from './constants';
|
|
21
|
+
export interface EcdsaSd2023KeyPair {
|
|
22
|
+
id: string;
|
|
23
|
+
controller: string;
|
|
24
|
+
publicKeyMultibase: string;
|
|
25
|
+
privateKeyMultibase?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface EcdsaSd2023Proof {
|
|
28
|
+
type: typeof PROOF_TYPE_DATA_INTEGRITY;
|
|
29
|
+
cryptosuite: typeof CRYPTOSUITE_ECDSA_SD_2023;
|
|
30
|
+
created: string;
|
|
31
|
+
verificationMethod: string;
|
|
32
|
+
proofPurpose: string;
|
|
33
|
+
proofValue: string;
|
|
34
|
+
}
|
|
35
|
+
export interface SignOptions {
|
|
36
|
+
document: Record<string, unknown>;
|
|
37
|
+
keyPair: EcdsaSd2023KeyPair;
|
|
38
|
+
purpose?: string;
|
|
39
|
+
date?: Date | string;
|
|
40
|
+
mandatoryPointers?: string[];
|
|
41
|
+
}
|
|
42
|
+
export interface DeriveOptions {
|
|
43
|
+
signedDocument: Record<string, unknown>;
|
|
44
|
+
selectivePointers: string[];
|
|
45
|
+
}
|
|
46
|
+
export interface VerifyOptions {
|
|
47
|
+
document: Record<string, unknown>;
|
|
48
|
+
proof: EcdsaSd2023Proof;
|
|
49
|
+
publicKeyMultibase: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* ECDSA SD 2023 Cryptosuite implementation
|
|
53
|
+
*
|
|
54
|
+
* Note: Full selective disclosure support requires BBS+ style cryptography.
|
|
55
|
+
* This implementation provides the structure and basic ECDSA signing.
|
|
56
|
+
* For production use with actual selective disclosure, integrate with
|
|
57
|
+
* @mattrglobal/bbs-signatures or similar.
|
|
58
|
+
*/
|
|
59
|
+
export declare class EcdsaSd2023Cryptosuite {
|
|
60
|
+
static readonly proofType = "DataIntegrityProof";
|
|
61
|
+
static readonly cryptosuite = "ecdsa-sd-2023";
|
|
62
|
+
static readonly contextUrl = "https://w3id.org/security/data-integrity/v2";
|
|
63
|
+
static readonly verificationMethodTypes: string[];
|
|
64
|
+
/**
|
|
65
|
+
* Sign a document using ecdsa-sd-2023 cryptosuite
|
|
66
|
+
*
|
|
67
|
+
* @param options - Sign options including document, keyPair, and mandatoryPointers
|
|
68
|
+
* @returns Signed document with base proof
|
|
69
|
+
*/
|
|
70
|
+
sign(options: SignOptions): Promise<Record<string, unknown>>;
|
|
71
|
+
/**
|
|
72
|
+
* Derive a selective disclosure proof from a base proof
|
|
73
|
+
*
|
|
74
|
+
* @param options - Derive options including signed document and selective pointers
|
|
75
|
+
* @returns Derived document with only selected claims
|
|
76
|
+
*/
|
|
77
|
+
derive(options: DeriveOptions): Promise<Record<string, unknown>>;
|
|
78
|
+
/**
|
|
79
|
+
* Verify a document with ecdsa-sd-2023 proof
|
|
80
|
+
*/
|
|
81
|
+
verify(options: VerifyOptions): Promise<{
|
|
82
|
+
verified: boolean;
|
|
83
|
+
error?: string;
|
|
84
|
+
}>;
|
|
85
|
+
/**
|
|
86
|
+
* Check if this suite matches a given proof
|
|
87
|
+
*/
|
|
88
|
+
matchProof(proof: Record<string, unknown>): boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Create hash data for signing/verification
|
|
91
|
+
*/
|
|
92
|
+
private createHashData;
|
|
93
|
+
/**
|
|
94
|
+
* Deep sort object keys for consistent serialization
|
|
95
|
+
*/
|
|
96
|
+
private sortObject;
|
|
97
|
+
/**
|
|
98
|
+
* Get Web Crypto SubtleCrypto API if available
|
|
99
|
+
*/
|
|
100
|
+
private getSubtleCrypto;
|
|
101
|
+
/**
|
|
102
|
+
* Sign data with P-256 key using Web Crypto API with JWK import
|
|
103
|
+
*/
|
|
104
|
+
private signWithP256;
|
|
105
|
+
/**
|
|
106
|
+
* Verify signature with P-256 public key using Web Crypto API with JWK import
|
|
107
|
+
*/
|
|
108
|
+
private verifyWithP256;
|
|
109
|
+
/**
|
|
110
|
+
* SHA-256 hash using Node crypto
|
|
111
|
+
*/
|
|
112
|
+
private sha256;
|
|
113
|
+
/**
|
|
114
|
+
* Decode private key from multibase and return raw 32-byte scalar
|
|
115
|
+
*/
|
|
116
|
+
private decodePrivateKeyRaw;
|
|
117
|
+
/**
|
|
118
|
+
* Decode public key from multibase and return raw bytes
|
|
119
|
+
* Returns uncompressed format (65 bytes: 0x04 || x || y)
|
|
120
|
+
*/
|
|
121
|
+
private decodePublicKeyRaw;
|
|
122
|
+
/**
|
|
123
|
+
* Convert raw P-256 private key to JWK format for Web Crypto
|
|
124
|
+
*/
|
|
125
|
+
private privateKeyToJwk;
|
|
126
|
+
/**
|
|
127
|
+
* Convert raw P-256 public key to JWK format for Web Crypto
|
|
128
|
+
*/
|
|
129
|
+
private publicKeyToJwk;
|
|
130
|
+
/**
|
|
131
|
+
* Encode proof value (base64url encoded JSON)
|
|
132
|
+
*/
|
|
133
|
+
private encodeProofValue;
|
|
134
|
+
/**
|
|
135
|
+
* Decode proof value
|
|
136
|
+
*/
|
|
137
|
+
private decodeProofValue;
|
|
138
|
+
/**
|
|
139
|
+
* Generate a new P-256 key pair with multibase encoding
|
|
140
|
+
* Uses Web Crypto API for key generation
|
|
141
|
+
*/
|
|
142
|
+
static generateKeyPair(controller: string, keyId?: string): Promise<EcdsaSd2023KeyPair>;
|
|
143
|
+
}
|
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ECDSA SD 2023 Cryptosuite
|
|
4
|
+
*
|
|
5
|
+
* Implements the ecdsa-sd-2023 cryptosuite for Data Integrity proofs with
|
|
6
|
+
* selective disclosure support.
|
|
7
|
+
*
|
|
8
|
+
* Spec: https://www.w3.org/TR/vc-di-ecdsa/#ecdsa-sd-2023
|
|
9
|
+
*
|
|
10
|
+
* Key features:
|
|
11
|
+
* - Uses ECDSA P-256 (secp256r1) signature algorithm
|
|
12
|
+
* - Supports selective disclosure of claims
|
|
13
|
+
* - Uses mandatory and selective pointers for claim selection
|
|
14
|
+
* - Produces DataIntegrityProof with cryptosuite: 'ecdsa-sd-2023'
|
|
15
|
+
*
|
|
16
|
+
* Note: This implementation provides the structure and interface.
|
|
17
|
+
* Full selective disclosure requires BBS+ style commitments which would need
|
|
18
|
+
* additional crypto libraries. For now, this provides basic ECDSA signing
|
|
19
|
+
* with the correct proof structure.
|
|
20
|
+
*/
|
|
21
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
22
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
23
|
+
};
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.EcdsaSd2023Cryptosuite = void 0;
|
|
26
|
+
const constants_1 = require("./constants");
|
|
27
|
+
const bs58_1 = __importDefault(require("bs58"));
|
|
28
|
+
const crypto_1 = require("crypto");
|
|
29
|
+
// P-256 multicodec prefixes
|
|
30
|
+
const P256_PUB_MULTICODEC = new Uint8Array([0x80, 0x24]);
|
|
31
|
+
const P256_PRIV_MULTICODEC = new Uint8Array([0x86, 0x26]);
|
|
32
|
+
/**
|
|
33
|
+
* JSON Pointer helper to get value at path
|
|
34
|
+
*/
|
|
35
|
+
function getValueAtPointer(obj, pointer) {
|
|
36
|
+
if (!pointer.startsWith('/')) {
|
|
37
|
+
throw new Error('JSON pointer must start with /');
|
|
38
|
+
}
|
|
39
|
+
const parts = pointer.slice(1).split('/');
|
|
40
|
+
let current = obj;
|
|
41
|
+
for (const part of parts) {
|
|
42
|
+
if (current === null || current === undefined) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
const decodedPart = part.replace(/~1/g, '/').replace(/~0/g, '~');
|
|
46
|
+
if (Array.isArray(current)) {
|
|
47
|
+
const index = parseInt(decodedPart, 10);
|
|
48
|
+
current = current[index];
|
|
49
|
+
}
|
|
50
|
+
else if (typeof current === 'object') {
|
|
51
|
+
current = current[decodedPart];
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return current;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* JSON Pointer helper to set value at path
|
|
61
|
+
*/
|
|
62
|
+
function setValueAtPointer(obj, pointer, value) {
|
|
63
|
+
if (!pointer.startsWith('/')) {
|
|
64
|
+
throw new Error('JSON pointer must start with /');
|
|
65
|
+
}
|
|
66
|
+
const parts = pointer.slice(1).split('/');
|
|
67
|
+
let current = obj;
|
|
68
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
69
|
+
const part = parts[i].replace(/~1/g, '/').replace(/~0/g, '~');
|
|
70
|
+
if (Array.isArray(current)) {
|
|
71
|
+
const index = parseInt(part, 10);
|
|
72
|
+
if (current[index] === undefined) {
|
|
73
|
+
current[index] = {};
|
|
74
|
+
}
|
|
75
|
+
current = current[index];
|
|
76
|
+
}
|
|
77
|
+
else if (typeof current === 'object' && current !== null) {
|
|
78
|
+
const record = current;
|
|
79
|
+
if (record[part] === undefined) {
|
|
80
|
+
record[part] = {};
|
|
81
|
+
}
|
|
82
|
+
current = record[part];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const lastPart = parts[parts.length - 1].replace(/~1/g, '/').replace(/~0/g, '~');
|
|
86
|
+
if (Array.isArray(current)) {
|
|
87
|
+
const index = parseInt(lastPart, 10);
|
|
88
|
+
current[index] = value;
|
|
89
|
+
}
|
|
90
|
+
else if (typeof current === 'object' && current !== null) {
|
|
91
|
+
;
|
|
92
|
+
current[lastPart] = value;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* ECDSA SD 2023 Cryptosuite implementation
|
|
97
|
+
*
|
|
98
|
+
* Note: Full selective disclosure support requires BBS+ style cryptography.
|
|
99
|
+
* This implementation provides the structure and basic ECDSA signing.
|
|
100
|
+
* For production use with actual selective disclosure, integrate with
|
|
101
|
+
* @mattrglobal/bbs-signatures or similar.
|
|
102
|
+
*/
|
|
103
|
+
class EcdsaSd2023Cryptosuite {
|
|
104
|
+
/**
|
|
105
|
+
* Sign a document using ecdsa-sd-2023 cryptosuite
|
|
106
|
+
*
|
|
107
|
+
* @param options - Sign options including document, keyPair, and mandatoryPointers
|
|
108
|
+
* @returns Signed document with base proof
|
|
109
|
+
*/
|
|
110
|
+
async sign(options) {
|
|
111
|
+
const { document, keyPair, purpose = 'assertionMethod', date, mandatoryPointers = [] } = options;
|
|
112
|
+
// Create proof options
|
|
113
|
+
const proofOptions = {
|
|
114
|
+
type: constants_1.PROOF_TYPE_DATA_INTEGRITY,
|
|
115
|
+
cryptosuite: constants_1.CRYPTOSUITE_ECDSA_SD_2023,
|
|
116
|
+
created: date ? new Date(date).toISOString() : new Date().toISOString(),
|
|
117
|
+
verificationMethod: keyPair.id,
|
|
118
|
+
proofPurpose: purpose,
|
|
119
|
+
};
|
|
120
|
+
// Store mandatory pointers in proof (for derive to use later)
|
|
121
|
+
const proofWithPointers = {
|
|
122
|
+
...proofOptions,
|
|
123
|
+
// Store mandatory claim references
|
|
124
|
+
_mandatoryPointers: mandatoryPointers,
|
|
125
|
+
};
|
|
126
|
+
// Create hash data
|
|
127
|
+
const documentCopy = { ...document };
|
|
128
|
+
delete documentCopy.proof;
|
|
129
|
+
const hashData = this.createHashData(documentCopy, proofWithPointers);
|
|
130
|
+
// Sign using Web Crypto API (P-256/ECDSA)
|
|
131
|
+
const signature = await this.signWithP256(hashData, keyPair);
|
|
132
|
+
// Create proof value
|
|
133
|
+
// In ecdsa-sd-2023, the proofValue contains:
|
|
134
|
+
// 1. The base signature
|
|
135
|
+
// 2. Commitments to selective claims
|
|
136
|
+
// 3. Mandatory claim hashes
|
|
137
|
+
// For this simplified version, we just include the signature
|
|
138
|
+
const proofValue = this.encodeProofValue({
|
|
139
|
+
signature,
|
|
140
|
+
mandatoryPointers,
|
|
141
|
+
});
|
|
142
|
+
// Create final proof
|
|
143
|
+
const proof = {
|
|
144
|
+
type: constants_1.PROOF_TYPE_DATA_INTEGRITY,
|
|
145
|
+
cryptosuite: constants_1.CRYPTOSUITE_ECDSA_SD_2023,
|
|
146
|
+
created: proofOptions.created,
|
|
147
|
+
verificationMethod: proofOptions.verificationMethod,
|
|
148
|
+
proofPurpose: proofOptions.proofPurpose,
|
|
149
|
+
proofValue,
|
|
150
|
+
};
|
|
151
|
+
return {
|
|
152
|
+
...document,
|
|
153
|
+
proof,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Derive a selective disclosure proof from a base proof
|
|
158
|
+
*
|
|
159
|
+
* @param options - Derive options including signed document and selective pointers
|
|
160
|
+
* @returns Derived document with only selected claims
|
|
161
|
+
*/
|
|
162
|
+
async derive(options) {
|
|
163
|
+
const { signedDocument, selectivePointers } = options;
|
|
164
|
+
const proof = signedDocument.proof;
|
|
165
|
+
if (!proof || proof.cryptosuite !== constants_1.CRYPTOSUITE_ECDSA_SD_2023) {
|
|
166
|
+
throw new Error('Document must have an ecdsa-sd-2023 proof for derivation');
|
|
167
|
+
}
|
|
168
|
+
// Parse the original proof value
|
|
169
|
+
const { signature, mandatoryPointers } = this.decodeProofValue(proof.proofValue);
|
|
170
|
+
// Create derived document with only selected + mandatory claims
|
|
171
|
+
const allPointers = [...new Set([...mandatoryPointers, ...selectivePointers])];
|
|
172
|
+
// Build derived document
|
|
173
|
+
const derivedDoc = {
|
|
174
|
+
'@context': signedDocument['@context'],
|
|
175
|
+
type: signedDocument.type,
|
|
176
|
+
};
|
|
177
|
+
// Copy mandatory fields
|
|
178
|
+
if (signedDocument.id)
|
|
179
|
+
derivedDoc.id = signedDocument.id;
|
|
180
|
+
if (signedDocument.issuer)
|
|
181
|
+
derivedDoc.issuer = signedDocument.issuer;
|
|
182
|
+
if (signedDocument.validFrom)
|
|
183
|
+
derivedDoc.validFrom = signedDocument.validFrom;
|
|
184
|
+
// Copy selected claims
|
|
185
|
+
for (const pointer of allPointers) {
|
|
186
|
+
const value = getValueAtPointer(signedDocument, pointer);
|
|
187
|
+
if (value !== undefined) {
|
|
188
|
+
setValueAtPointer(derivedDoc, pointer, value);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// Create derived proof
|
|
192
|
+
const derivedProof = {
|
|
193
|
+
type: constants_1.PROOF_TYPE_DATA_INTEGRITY,
|
|
194
|
+
cryptosuite: constants_1.CRYPTOSUITE_ECDSA_SD_2023,
|
|
195
|
+
created: proof.created,
|
|
196
|
+
verificationMethod: proof.verificationMethod,
|
|
197
|
+
proofPurpose: proof.proofPurpose,
|
|
198
|
+
proofValue: this.encodeProofValue({
|
|
199
|
+
signature,
|
|
200
|
+
mandatoryPointers,
|
|
201
|
+
derivedFrom: proof.proofValue,
|
|
202
|
+
selectedPointers: selectivePointers,
|
|
203
|
+
}),
|
|
204
|
+
};
|
|
205
|
+
return {
|
|
206
|
+
...derivedDoc,
|
|
207
|
+
proof: derivedProof,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Verify a document with ecdsa-sd-2023 proof
|
|
212
|
+
*/
|
|
213
|
+
async verify(options) {
|
|
214
|
+
const { document, proof, publicKeyMultibase } = options;
|
|
215
|
+
try {
|
|
216
|
+
// Validate proof structure
|
|
217
|
+
if (proof.type !== constants_1.PROOF_TYPE_DATA_INTEGRITY) {
|
|
218
|
+
return { verified: false, error: `Invalid proof type: ${proof.type}` };
|
|
219
|
+
}
|
|
220
|
+
if (proof.cryptosuite !== constants_1.CRYPTOSUITE_ECDSA_SD_2023) {
|
|
221
|
+
return { verified: false, error: `Invalid cryptosuite: ${proof.cryptosuite}` };
|
|
222
|
+
}
|
|
223
|
+
// Parse proof value
|
|
224
|
+
const { signature } = this.decodeProofValue(proof.proofValue);
|
|
225
|
+
// Recreate hash data
|
|
226
|
+
const documentCopy = { ...document };
|
|
227
|
+
delete documentCopy.proof;
|
|
228
|
+
const proofOptions = {
|
|
229
|
+
type: proof.type,
|
|
230
|
+
cryptosuite: proof.cryptosuite,
|
|
231
|
+
created: proof.created,
|
|
232
|
+
verificationMethod: proof.verificationMethod,
|
|
233
|
+
proofPurpose: proof.proofPurpose,
|
|
234
|
+
};
|
|
235
|
+
const hashData = this.createHashData(documentCopy, proofOptions);
|
|
236
|
+
// Verify signature
|
|
237
|
+
const verified = await this.verifyWithP256(hashData, signature, publicKeyMultibase);
|
|
238
|
+
return { verified };
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
const err = error;
|
|
242
|
+
return { verified: false, error: err.message };
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Check if this suite matches a given proof
|
|
247
|
+
*/
|
|
248
|
+
matchProof(proof) {
|
|
249
|
+
return (proof.type === constants_1.PROOF_TYPE_DATA_INTEGRITY && proof.cryptosuite === constants_1.CRYPTOSUITE_ECDSA_SD_2023);
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Create hash data for signing/verification
|
|
253
|
+
*/
|
|
254
|
+
createHashData(document, proofOptions) {
|
|
255
|
+
const combined = {
|
|
256
|
+
document: this.sortObject(document),
|
|
257
|
+
proofOptions: this.sortObject(proofOptions),
|
|
258
|
+
};
|
|
259
|
+
const encoder = new TextEncoder();
|
|
260
|
+
return encoder.encode(JSON.stringify(combined));
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Deep sort object keys for consistent serialization
|
|
264
|
+
*/
|
|
265
|
+
sortObject(obj) {
|
|
266
|
+
if (Array.isArray(obj)) {
|
|
267
|
+
return obj.map((item) => typeof item === 'object' && item !== null
|
|
268
|
+
? this.sortObject(item)
|
|
269
|
+
: item);
|
|
270
|
+
}
|
|
271
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
272
|
+
return obj;
|
|
273
|
+
}
|
|
274
|
+
const sorted = {};
|
|
275
|
+
const keys = Object.keys(obj).sort();
|
|
276
|
+
for (const key of keys) {
|
|
277
|
+
// Skip internal fields
|
|
278
|
+
if (key.startsWith('_'))
|
|
279
|
+
continue;
|
|
280
|
+
const value = obj[key];
|
|
281
|
+
sorted[key] =
|
|
282
|
+
typeof value === 'object' && value !== null
|
|
283
|
+
? this.sortObject(value)
|
|
284
|
+
: value;
|
|
285
|
+
}
|
|
286
|
+
return sorted;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Get Web Crypto SubtleCrypto API if available
|
|
290
|
+
*/
|
|
291
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
292
|
+
getSubtleCrypto() {
|
|
293
|
+
// Try to get crypto.subtle from globalThis (works in Node.js 15+ and browsers)
|
|
294
|
+
try {
|
|
295
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
296
|
+
const globalCrypto = globalThis.crypto;
|
|
297
|
+
if (globalCrypto?.subtle) {
|
|
298
|
+
return globalCrypto.subtle;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
catch {
|
|
302
|
+
// Crypto not available
|
|
303
|
+
}
|
|
304
|
+
return undefined;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Sign data with P-256 key using Web Crypto API with JWK import
|
|
308
|
+
*/
|
|
309
|
+
async signWithP256(data, keyPair) {
|
|
310
|
+
if (!keyPair.privateKeyMultibase) {
|
|
311
|
+
throw new Error('Private key is required for signing');
|
|
312
|
+
}
|
|
313
|
+
const subtle = this.getSubtleCrypto();
|
|
314
|
+
if (!subtle) {
|
|
315
|
+
throw new Error('Web Crypto API (subtle) not available');
|
|
316
|
+
}
|
|
317
|
+
try {
|
|
318
|
+
// Decode private and public keys from multibase
|
|
319
|
+
const privateKeyRaw = this.decodePrivateKeyRaw(keyPair.privateKeyMultibase);
|
|
320
|
+
const publicKeyRaw = this.decodePublicKeyRaw(keyPair.publicKeyMultibase);
|
|
321
|
+
// Convert to JWK format
|
|
322
|
+
const jwk = this.privateKeyToJwk(privateKeyRaw, publicKeyRaw);
|
|
323
|
+
// Import the key using JWK format
|
|
324
|
+
const cryptoKey = await subtle.importKey('jwk', jwk, { name: 'ECDSA', namedCurve: 'P-256' }, false, ['sign']);
|
|
325
|
+
// Sign the data
|
|
326
|
+
const signatureBuffer = await subtle.sign({ name: 'ECDSA', hash: 'SHA-256' }, cryptoKey, data);
|
|
327
|
+
return new Uint8Array(signatureBuffer);
|
|
328
|
+
}
|
|
329
|
+
catch (error) {
|
|
330
|
+
throw new Error(`P-256 signing failed: ${error.message}`);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Verify signature with P-256 public key using Web Crypto API with JWK import
|
|
335
|
+
*/
|
|
336
|
+
async verifyWithP256(data, signature, publicKeyMultibase) {
|
|
337
|
+
const subtle = this.getSubtleCrypto();
|
|
338
|
+
if (!subtle) {
|
|
339
|
+
throw new Error('Web Crypto API (subtle) not available');
|
|
340
|
+
}
|
|
341
|
+
try {
|
|
342
|
+
// Decode public key from multibase
|
|
343
|
+
const publicKeyRaw = this.decodePublicKeyRaw(publicKeyMultibase);
|
|
344
|
+
// Convert to JWK format
|
|
345
|
+
const jwk = this.publicKeyToJwk(publicKeyRaw);
|
|
346
|
+
// Import the public key using JWK format
|
|
347
|
+
const cryptoKey = await subtle.importKey('jwk', jwk, { name: 'ECDSA', namedCurve: 'P-256' }, false, ['verify']);
|
|
348
|
+
// Verify the signature
|
|
349
|
+
return await subtle.verify({ name: 'ECDSA', hash: 'SHA-256' }, cryptoKey, signature, data);
|
|
350
|
+
}
|
|
351
|
+
catch (error) {
|
|
352
|
+
throw new Error(`P-256 verification failed: ${error.message}`);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* SHA-256 hash using Node crypto
|
|
357
|
+
*/
|
|
358
|
+
sha256(data) {
|
|
359
|
+
return (0, crypto_1.createHash)('sha256').update(data).digest();
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Decode private key from multibase and return raw 32-byte scalar
|
|
363
|
+
*/
|
|
364
|
+
decodePrivateKeyRaw(privateKeyMultibase) {
|
|
365
|
+
if (!privateKeyMultibase.startsWith('z')) {
|
|
366
|
+
throw new Error('Invalid multibase prefix: expected base58btc (z)');
|
|
367
|
+
}
|
|
368
|
+
const decoded = bs58_1.default.decode(privateKeyMultibase.slice(1));
|
|
369
|
+
// Check for P-256 private key multicodec prefix (0x8626)
|
|
370
|
+
if (decoded.length > 2 && decoded[0] === P256_PRIV_MULTICODEC[0] && decoded[1] === P256_PRIV_MULTICODEC[1]) {
|
|
371
|
+
return decoded.slice(2);
|
|
372
|
+
}
|
|
373
|
+
// If no multicodec prefix, assume raw key material
|
|
374
|
+
return decoded;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Decode public key from multibase and return raw bytes
|
|
378
|
+
* Returns uncompressed format (65 bytes: 0x04 || x || y)
|
|
379
|
+
*/
|
|
380
|
+
decodePublicKeyRaw(publicKeyMultibase) {
|
|
381
|
+
if (!publicKeyMultibase.startsWith('z')) {
|
|
382
|
+
throw new Error('Invalid multibase prefix: expected base58btc (z)');
|
|
383
|
+
}
|
|
384
|
+
const decoded = bs58_1.default.decode(publicKeyMultibase.slice(1));
|
|
385
|
+
// Check for P-256 public key multicodec prefix (0x8024)
|
|
386
|
+
if (decoded.length > 2 && decoded[0] === P256_PUB_MULTICODEC[0] && decoded[1] === P256_PUB_MULTICODEC[1]) {
|
|
387
|
+
return decoded.slice(2);
|
|
388
|
+
}
|
|
389
|
+
// If no multicodec prefix, assume raw key material
|
|
390
|
+
return decoded;
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Convert raw P-256 private key to JWK format for Web Crypto
|
|
394
|
+
*/
|
|
395
|
+
privateKeyToJwk(privateKeyRaw, publicKeyRaw) {
|
|
396
|
+
// Private key is 32 bytes (scalar d)
|
|
397
|
+
// Public key is 65 bytes uncompressed (0x04 || x || y) or 64 bytes (x || y)
|
|
398
|
+
let x;
|
|
399
|
+
let y;
|
|
400
|
+
if (publicKeyRaw.length === 65 && publicKeyRaw[0] === 0x04) {
|
|
401
|
+
// Uncompressed format with 0x04 prefix
|
|
402
|
+
x = publicKeyRaw.slice(1, 33);
|
|
403
|
+
y = publicKeyRaw.slice(33, 65);
|
|
404
|
+
}
|
|
405
|
+
else if (publicKeyRaw.length === 64) {
|
|
406
|
+
// Raw x || y without prefix
|
|
407
|
+
x = publicKeyRaw.slice(0, 32);
|
|
408
|
+
y = publicKeyRaw.slice(32, 64);
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
throw new Error(`Invalid P-256 public key length: ${publicKeyRaw.length}`);
|
|
412
|
+
}
|
|
413
|
+
return {
|
|
414
|
+
kty: 'EC',
|
|
415
|
+
crv: 'P-256',
|
|
416
|
+
x: Buffer.from(x).toString('base64url'),
|
|
417
|
+
y: Buffer.from(y).toString('base64url'),
|
|
418
|
+
d: Buffer.from(privateKeyRaw).toString('base64url'),
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Convert raw P-256 public key to JWK format for Web Crypto
|
|
423
|
+
*/
|
|
424
|
+
publicKeyToJwk(publicKeyRaw) {
|
|
425
|
+
let x;
|
|
426
|
+
let y;
|
|
427
|
+
if (publicKeyRaw.length === 65 && publicKeyRaw[0] === 0x04) {
|
|
428
|
+
// Uncompressed format with 0x04 prefix
|
|
429
|
+
x = publicKeyRaw.slice(1, 33);
|
|
430
|
+
y = publicKeyRaw.slice(33, 65);
|
|
431
|
+
}
|
|
432
|
+
else if (publicKeyRaw.length === 64) {
|
|
433
|
+
// Raw x || y without prefix
|
|
434
|
+
x = publicKeyRaw.slice(0, 32);
|
|
435
|
+
y = publicKeyRaw.slice(32, 64);
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
438
|
+
throw new Error(`Invalid P-256 public key length: ${publicKeyRaw.length}`);
|
|
439
|
+
}
|
|
440
|
+
return {
|
|
441
|
+
kty: 'EC',
|
|
442
|
+
crv: 'P-256',
|
|
443
|
+
x: Buffer.from(x).toString('base64url'),
|
|
444
|
+
y: Buffer.from(y).toString('base64url'),
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Encode proof value (base64url encoded JSON)
|
|
449
|
+
*/
|
|
450
|
+
encodeProofValue(data) {
|
|
451
|
+
const payload = {
|
|
452
|
+
s: Buffer.from(data.signature).toString('base64'),
|
|
453
|
+
m: data.mandatoryPointers,
|
|
454
|
+
d: data.derivedFrom,
|
|
455
|
+
p: data.selectedPointers,
|
|
456
|
+
};
|
|
457
|
+
// Use 'u' prefix for base64url encoding
|
|
458
|
+
return 'u' + Buffer.from(JSON.stringify(payload)).toString('base64url');
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Decode proof value
|
|
462
|
+
*/
|
|
463
|
+
decodeProofValue(proofValue) {
|
|
464
|
+
if (!proofValue.startsWith('u')) {
|
|
465
|
+
throw new Error('Invalid proof value: expected base64url (u) prefix');
|
|
466
|
+
}
|
|
467
|
+
const json = Buffer.from(proofValue.slice(1), 'base64url').toString('utf-8');
|
|
468
|
+
const payload = JSON.parse(json);
|
|
469
|
+
return {
|
|
470
|
+
signature: Buffer.from(payload.s, 'base64'),
|
|
471
|
+
mandatoryPointers: payload.m || [],
|
|
472
|
+
derivedFrom: payload.d,
|
|
473
|
+
selectedPointers: payload.p,
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Generate a new P-256 key pair with multibase encoding
|
|
478
|
+
* Uses Web Crypto API for key generation
|
|
479
|
+
*/
|
|
480
|
+
static async generateKeyPair(controller, keyId) {
|
|
481
|
+
// Use Web Crypto to generate P-256 key pair
|
|
482
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
483
|
+
const subtle = globalThis.crypto?.subtle;
|
|
484
|
+
if (!subtle) {
|
|
485
|
+
throw new Error('Web Crypto API (subtle) not available for key generation');
|
|
486
|
+
}
|
|
487
|
+
// Generate key pair
|
|
488
|
+
const keyPair = await subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-256' }, true, // extractable
|
|
489
|
+
['sign', 'verify']);
|
|
490
|
+
// Export keys to JWK format
|
|
491
|
+
const publicJwk = await subtle.exportKey('jwk', keyPair.publicKey);
|
|
492
|
+
const privateJwk = await subtle.exportKey('jwk', keyPair.privateKey);
|
|
493
|
+
// Extract raw key bytes from JWK
|
|
494
|
+
const x = Buffer.from(publicJwk.x, 'base64url');
|
|
495
|
+
const y = Buffer.from(publicJwk.y, 'base64url');
|
|
496
|
+
const d = Buffer.from(privateJwk.d, 'base64url');
|
|
497
|
+
// Create uncompressed public key (0x04 || x || y)
|
|
498
|
+
const publicKeyRaw = Buffer.concat([Buffer.from([0x04]), x, y]);
|
|
499
|
+
// Encode with multicodec prefixes
|
|
500
|
+
const pubWithHeader = Buffer.concat([Buffer.from(P256_PUB_MULTICODEC), publicKeyRaw]);
|
|
501
|
+
const privWithHeader = Buffer.concat([Buffer.from(P256_PRIV_MULTICODEC), d]);
|
|
502
|
+
const publicKeyMultibase = 'z' + bs58_1.default.encode(pubWithHeader);
|
|
503
|
+
const privateKeyMultibase = 'z' + bs58_1.default.encode(privWithHeader);
|
|
504
|
+
const id = keyId || `${controller}#key-1`;
|
|
505
|
+
return {
|
|
506
|
+
id,
|
|
507
|
+
controller,
|
|
508
|
+
publicKeyMultibase,
|
|
509
|
+
privateKeyMultibase,
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
exports.EcdsaSd2023Cryptosuite = EcdsaSd2023Cryptosuite;
|
|
514
|
+
EcdsaSd2023Cryptosuite.proofType = constants_1.PROOF_TYPE_DATA_INTEGRITY;
|
|
515
|
+
EcdsaSd2023Cryptosuite.cryptosuite = constants_1.CRYPTOSUITE_ECDSA_SD_2023;
|
|
516
|
+
EcdsaSd2023Cryptosuite.contextUrl = constants_1.DATA_INTEGRITY_V2_CONTEXT_URL;
|
|
517
|
+
EcdsaSd2023Cryptosuite.verificationMethodTypes = [constants_1.VM_TYPE_MULTIKEY];
|
|
518
|
+
//# sourceMappingURL=EcdsaSd2023.js.map
|