anon-identity
Version:
Decentralized identity framework with DIDs, Verifiable Credentials, and privacy-preserving selective disclosure
182 lines • 7.69 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.SelectiveDisclosure = void 0;
exports.getPublicKeyFromPrivate = getPublicKeyFromPrivate;
const crypto = __importStar(require("crypto"));
const crypto_1 = require("../core/crypto");
const jose_1 = require("jose");
class SelectiveDisclosure {
/**
* Create a selectively disclosed credential revealing only specified attributes
*/
static async createSelectivelyDisclosedCredential(originalCredential, attributesToDisclose, holderPrivateKey, holderDID) {
// Validate that requested attributes exist in the credential
const availableAttributes = Object.keys(originalCredential.credentialSubject)
.filter(key => key !== 'id');
for (const attr of attributesToDisclose) {
if (!availableAttributes.includes(attr)) {
throw new Error(`Attribute '${attr}' not found in credential`);
}
}
// Create disclosed credential subject with only requested attributes
const disclosedSubject = {
id: originalCredential.credentialSubject.id
};
for (const attr of attributesToDisclose) {
disclosedSubject[attr] = originalCredential.credentialSubject[attr];
}
// Generate a nonce for this disclosure
const nonce = crypto.randomBytes(32).toString('base64');
// Create proof of selective disclosure
const disclosureData = {
originalCredentialId: originalCredential.id,
issuer: originalCredential.issuer,
issuanceDate: originalCredential.issuanceDate,
disclosedAttributes: attributesToDisclose,
nonce,
timestamp: new Date().toISOString()
};
// Sign the disclosure proof with holder's private key
const proofValue = await this.createDisclosureProof(disclosureData, holderPrivateKey, holderDID);
// Create the selectively disclosed credential
const disclosedCredential = {
"@context": originalCredential["@context"],
id: `${originalCredential.id}#disclosed-${Date.now()}`,
type: [...originalCredential.type, "SelectivelyDisclosedCredential"],
issuer: originalCredential.issuer,
issuanceDate: originalCredential.issuanceDate,
credentialSubject: disclosedSubject,
proof: originalCredential.proof, // Keep original issuer's proof
disclosureProof: {
type: "SelectiveDisclosureProof2024",
originalCredentialId: originalCredential.id,
disclosedAttributes: attributesToDisclose,
nonce,
proofValue
}
};
return disclosedCredential;
}
/**
* Create a cryptographic proof for the selective disclosure
*/
static async createDisclosureProof(disclosureData, privateKey, holderDID) {
// Convert private key to JWK format
const publicKey = await crypto_1.CryptoService.getPublicKeyFromPrivate(privateKey);
const privateKeyJwk = {
kty: 'OKP',
crv: 'Ed25519',
x: Buffer.from(publicKey).toString('base64url'),
d: Buffer.from(privateKey).toString('base64url')
};
const key = await (0, jose_1.importJWK)(privateKeyJwk, 'EdDSA');
// Create JWT proof
const jwt = await new jose_1.SignJWT(disclosureData)
.setProtectedHeader({
alg: 'EdDSA',
typ: 'DisclosureProof',
kid: `${holderDID}#key-1`
})
.setIssuedAt()
.setIssuer(holderDID)
.sign(key);
return jwt;
}
/**
* Verify a selective disclosure proof
*/
static async verifySelectiveDisclosure(disclosedCredential, holderPublicKey) {
try {
if (!disclosedCredential.disclosureProof) {
return false;
}
// Verify the disclosure proof JWT
const publicKeyJwk = {
kty: 'OKP',
crv: 'Ed25519',
x: Buffer.from(holderPublicKey).toString('base64url')
};
const key = await (0, jose_1.importJWK)(publicKeyJwk, 'EdDSA');
const { payload } = await (0, jose_1.jwtVerify)(disclosedCredential.disclosureProof.proofValue, key, { algorithms: ['EdDSA'] });
// Verify the disclosed attributes match what's claimed
const claimedAttributes = disclosedCredential.disclosureProof.disclosedAttributes;
const actualAttributes = Object.keys(disclosedCredential.credentialSubject)
.filter(key => key !== 'id');
if (!this.arraysEqual(claimedAttributes.sort(), actualAttributes.sort())) {
return false;
}
// Verify the original credential ID matches
if (payload.originalCredentialId !== disclosedCredential.disclosureProof.originalCredentialId) {
return false;
}
return true;
}
catch (error) {
return false;
}
}
/**
* Create a commitment to a value (for future ZKP enhancement)
*/
static createCommitment(value, salt) {
const actualSalt = salt || crypto.randomBytes(32).toString('base64');
const data = JSON.stringify({ value, salt: actualSalt });
return crypto.createHash('sha256').update(data).digest('base64');
}
/**
* Verify a commitment (for future ZKP enhancement)
*/
static verifyCommitment(value, salt, commitment) {
const computedCommitment = this.createCommitment(value, salt);
return computedCommitment === commitment;
}
static arraysEqual(a, b) {
if (a.length !== b.length)
return false;
for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i])
return false;
}
return true;
}
}
exports.SelectiveDisclosure = SelectiveDisclosure;
// Add helper to CryptoService
async function getPublicKeyFromPrivate(privateKey) {
const ed = await Promise.resolve().then(() => __importStar(require('@noble/ed25519')));
return await ed.getPublicKey(privateKey);
}
//# sourceMappingURL=selective-disclosure.js.map