/** * Digital Signatures System for LocalGreenChain * * Provides cryptographic verification for: * - Transport handoffs * - Plant ownership transfers * - Data integrity verification * - Multi-party attestations */ import * as crypto from 'crypto'; import * as fs from 'fs'; import * as path from 'path'; // Signature Types export type SignatureType = | 'OWNERSHIP_TRANSFER' | 'TRANSPORT_HANDOFF' | 'HARVEST_ATTESTATION' | 'QUALITY_CERTIFICATION' | 'ORIGIN_VERIFICATION' | 'DATA_INTEGRITY'; export interface KeyPair { publicKey: string; privateKey: string; algorithm: 'RSA' | 'ECDSA'; createdAt: string; } export interface SignatureIdentity { id: string; name: string; publicKey: string; type: 'GROWER' | 'TRANSPORTER' | 'CERTIFIER' | 'CONSUMER' | 'SYSTEM'; verified: boolean; createdAt: string; metadata?: Record; } export interface DigitalSignature { id: string; type: SignatureType; signerId: string; signerPublicKey: string; timestamp: string; data: string; // JSON stringified data that was signed signature: string; // Base64 encoded signature algorithm: string; resourceType: string; resourceId: string; metadata?: Record; } export interface MultiSignature { id: string; type: SignatureType; resourceType: string; resourceId: string; requiredSigners: string[]; signatures: DigitalSignature[]; threshold: number; complete: boolean; createdAt: string; completedAt?: string; expiresAt?: string; } export interface SignatureVerification { valid: boolean; signerId: string; timestamp: string; resourceId: string; errors?: string[]; } export interface CertificateOfAuthenticity { id: string; plantId: string; plantName: string; variety: string; origin: { grower: string; location: string; registeredAt: string; }; lineage: { generation: number; parentId?: string; childCount: number; }; signatures: Array<{ type: SignatureType; signer: string; timestamp: string; verified: boolean; }>; transportHistory: Array<{ from: string; to: string; date: string; verified: boolean; }>; certifications: string[]; qrCode?: string; generatedAt: string; hash: string; } class SignatureManager { private identities: Map = new Map(); private signatures: Map = new Map(); private multiSignatures: Map = new Map(); private dataDir: string; private dataFile: string; constructor() { this.dataDir = path.join(process.cwd(), 'data'); this.dataFile = path.join(this.dataDir, 'signatures.json'); this.load(); } private load(): void { try { if (fs.existsSync(this.dataFile)) { const data = JSON.parse(fs.readFileSync(this.dataFile, 'utf-8')); if (data.identities) { data.identities.forEach((id: SignatureIdentity) => { this.identities.set(id.id, id); }); } if (data.signatures) { data.signatures.forEach((sig: DigitalSignature) => { this.signatures.set(sig.id, sig); }); } if (data.multiSignatures) { data.multiSignatures.forEach((ms: MultiSignature) => { this.multiSignatures.set(ms.id, ms); }); } console.log(`[SignatureManager] Loaded ${this.identities.size} identities, ${this.signatures.size} signatures`); } } catch (error) { console.error('[SignatureManager] Error loading data:', error); } } private save(): void { try { if (!fs.existsSync(this.dataDir)) { fs.mkdirSync(this.dataDir, { recursive: true }); } const data = { identities: Array.from(this.identities.values()), signatures: Array.from(this.signatures.values()), multiSignatures: Array.from(this.multiSignatures.values()), savedAt: new Date().toISOString() }; fs.writeFileSync(this.dataFile, JSON.stringify(data, null, 2)); } catch (error) { console.error('[SignatureManager] Error saving data:', error); } } /** * Generate a new key pair */ generateKeyPair(algorithm: 'RSA' | 'ECDSA' = 'ECDSA'): KeyPair { if (algorithm === 'RSA') { const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', { modulusLength: 2048, publicKeyEncoding: { type: 'spki', format: 'pem' }, privateKeyEncoding: { type: 'pkcs8', format: 'pem' } }); return { publicKey, privateKey, algorithm: 'RSA', createdAt: new Date().toISOString() }; } else { const { publicKey, privateKey } = crypto.generateKeyPairSync('ec', { namedCurve: 'secp256k1', publicKeyEncoding: { type: 'spki', format: 'pem' }, privateKeyEncoding: { type: 'pkcs8', format: 'pem' } }); return { publicKey, privateKey, algorithm: 'ECDSA', createdAt: new Date().toISOString() }; } } /** * Register a new identity */ registerIdentity( name: string, type: SignatureIdentity['type'], publicKey: string, metadata?: Record ): SignatureIdentity { const id = `id_${crypto.randomBytes(16).toString('hex')}`; const identity: SignatureIdentity = { id, name, publicKey, type, verified: false, createdAt: new Date().toISOString(), metadata }; this.identities.set(id, identity); this.save(); return identity; } /** * Get identity by ID */ getIdentity(id: string): SignatureIdentity | undefined { return this.identities.get(id); } /** * Get all identities */ getAllIdentities(): SignatureIdentity[] { return Array.from(this.identities.values()); } /** * Verify an identity */ verifyIdentity(id: string): boolean { const identity = this.identities.get(id); if (identity) { identity.verified = true; this.save(); return true; } return false; } /** * Sign data */ sign( type: SignatureType, resourceType: string, resourceId: string, data: any, privateKey: string, signerId: string, metadata?: Record ): DigitalSignature { const dataString = JSON.stringify(data); // Create signature const sign = crypto.createSign('SHA256'); sign.update(dataString); const signature = sign.sign(privateKey, 'base64'); const id = `sig_${Date.now()}_${crypto.randomBytes(8).toString('hex')}`; const identity = this.identities.get(signerId); const digitalSignature: DigitalSignature = { id, type, signerId, signerPublicKey: identity?.publicKey || '', timestamp: new Date().toISOString(), data: dataString, signature, algorithm: 'SHA256', resourceType, resourceId, metadata }; this.signatures.set(id, digitalSignature); this.save(); return digitalSignature; } /** * Verify a signature */ verify(signatureId: string): SignatureVerification { const sig = this.signatures.get(signatureId); if (!sig) { return { valid: false, signerId: '', timestamp: '', resourceId: '', errors: ['Signature not found'] }; } try { const verify = crypto.createVerify('SHA256'); verify.update(sig.data); const isValid = verify.verify(sig.signerPublicKey, sig.signature, 'base64'); return { valid: isValid, signerId: sig.signerId, timestamp: sig.timestamp, resourceId: sig.resourceId, errors: isValid ? undefined : ['Signature verification failed'] }; } catch (error) { return { valid: false, signerId: sig.signerId, timestamp: sig.timestamp, resourceId: sig.resourceId, errors: [`Verification error: ${error}`] }; } } /** * Verify data with signature directly */ verifyData(data: any, signature: string, publicKey: string): boolean { try { const dataString = JSON.stringify(data); const verify = crypto.createVerify('SHA256'); verify.update(dataString); return verify.verify(publicKey, signature, 'base64'); } catch { return false; } } /** * Create a multi-signature request */ createMultiSignature( type: SignatureType, resourceType: string, resourceId: string, requiredSigners: string[], threshold?: number, expiresIn?: number ): MultiSignature { const id = `msig_${Date.now()}_${crypto.randomBytes(8).toString('hex')}`; const multiSig: MultiSignature = { id, type, resourceType, resourceId, requiredSigners, signatures: [], threshold: threshold || requiredSigners.length, complete: false, createdAt: new Date().toISOString(), expiresAt: expiresIn ? new Date(Date.now() + expiresIn).toISOString() : undefined }; this.multiSignatures.set(id, multiSig); this.save(); return multiSig; } /** * Add a signature to a multi-signature request */ addToMultiSignature( multiSigId: string, signature: DigitalSignature ): MultiSignature | null { const multiSig = this.multiSignatures.get(multiSigId); if (!multiSig) return null; // Check if signer is required if (!multiSig.requiredSigners.includes(signature.signerId)) { return null; } // Check if already signed if (multiSig.signatures.some(s => s.signerId === signature.signerId)) { return multiSig; } // Check expiration if (multiSig.expiresAt && new Date(multiSig.expiresAt) < new Date()) { return null; } multiSig.signatures.push(signature); // Check if complete if (multiSig.signatures.length >= multiSig.threshold) { multiSig.complete = true; multiSig.completedAt = new Date().toISOString(); } this.save(); return multiSig; } /** * Get multi-signature status */ getMultiSignature(id: string): MultiSignature | undefined { return this.multiSignatures.get(id); } /** * Get signatures for a resource */ getSignaturesForResource(resourceType: string, resourceId: string): DigitalSignature[] { return Array.from(this.signatures.values()).filter( s => s.resourceType === resourceType && s.resourceId === resourceId ); } /** * Generate a Certificate of Authenticity */ async generateCertificate(plantId: string): Promise { try { // Load plant data const chainFile = path.join(this.dataDir, 'plantchain.json'); if (!fs.existsSync(chainFile)) { return null; } const chainData = JSON.parse(fs.readFileSync(chainFile, 'utf-8')); const chain = chainData.chain || []; // Find plant const plantBlock = chain.find((b: any) => b.plant?.id === plantId); if (!plantBlock) { return null; } const plant = plantBlock.plant; // Get signatures for this plant const plantSignatures = this.getSignaturesForResource('plant', plantId); // Build certificate const certificate: CertificateOfAuthenticity = { id: `cert_${crypto.randomBytes(16).toString('hex')}`, plantId, plantName: plant.name || 'Unknown', variety: plant.variety || 'Unknown', origin: { grower: plant.ownerId || 'Unknown', location: plant.location?.region || 'Unknown', registeredAt: plantBlock.timestamp }, lineage: { generation: plant.generation || 1, parentId: plant.parentId, childCount: plant.childPlants?.length || 0 }, signatures: plantSignatures.map(sig => ({ type: sig.type, signer: sig.signerId, timestamp: sig.timestamp, verified: this.verify(sig.id).valid })), transportHistory: [], certifications: [], generatedAt: new Date().toISOString(), hash: '' }; // Calculate certificate hash certificate.hash = crypto .createHash('sha256') .update(JSON.stringify({ plantId: certificate.plantId, origin: certificate.origin, lineage: certificate.lineage, signatures: certificate.signatures })) .digest('hex'); return certificate; } catch (error) { console.error('[SignatureManager] Error generating certificate:', error); return null; } } /** * Verify a certificate */ verifyCertificate(certificate: CertificateOfAuthenticity): { valid: boolean; errors: string[] } { const errors: string[] = []; // Verify hash const expectedHash = crypto .createHash('sha256') .update(JSON.stringify({ plantId: certificate.plantId, origin: certificate.origin, lineage: certificate.lineage, signatures: certificate.signatures })) .digest('hex'); if (certificate.hash !== expectedHash) { errors.push('Certificate hash mismatch - data may have been tampered'); } // Verify each signature for (const sig of certificate.signatures) { if (!sig.verified) { errors.push(`Signature from ${sig.signer} is not verified`); } } return { valid: errors.length === 0, errors }; } /** * Create a transport handoff signature */ createTransportHandoff( plantId: string, fromParty: string, toParty: string, location: { lat: number; lng: number }, fromPrivateKey: string ): DigitalSignature { const handoffData = { plantId, fromParty, toParty, location, timestamp: new Date().toISOString(), type: 'TRANSPORT_HANDOFF' }; return this.sign( 'TRANSPORT_HANDOFF', 'transport', plantId, handoffData, fromPrivateKey, fromParty, { toParty, location } ); } /** * Get statistics */ getStats(): { totalIdentities: number; verifiedIdentities: number; totalSignatures: number; signaturesByType: Record; pendingMultiSigs: number; completedMultiSigs: number; } { const signaturesByType: Record = {}; for (const sig of this.signatures.values()) { signaturesByType[sig.type] = (signaturesByType[sig.type] || 0) + 1; } const multiSigArray = Array.from(this.multiSignatures.values()); return { totalIdentities: this.identities.size, verifiedIdentities: Array.from(this.identities.values()).filter(i => i.verified).length, totalSignatures: this.signatures.size, signaturesByType, pendingMultiSigs: multiSigArray.filter(ms => !ms.complete).length, completedMultiSigs: multiSigArray.filter(ms => ms.complete).length }; } } // Singleton instance let signatureManagerInstance: SignatureManager | null = null; export function getSignatureManager(): SignatureManager { if (!signatureManagerInstance) { signatureManagerInstance = new SignatureManager(); } return signatureManagerInstance; } export default SignatureManager;