173 lines
5.5 KiB
TypeScript
173 lines
5.5 KiB
TypeScript
// Import necessary plugins from plugins.ts
|
|
import * as plugins from './plugins.js';
|
|
|
|
interface DnssecZone {
|
|
zone: string;
|
|
algorithm: 'ECDSA' | 'ED25519' | 'RSA';
|
|
keySize: number;
|
|
days: number;
|
|
}
|
|
|
|
interface DnssecKeyPair {
|
|
privateKey: string;
|
|
publicKey: string;
|
|
}
|
|
|
|
export class DnsSec {
|
|
private zone: DnssecZone;
|
|
private keyPair: DnssecKeyPair;
|
|
private ec?: plugins.elliptic.ec; // For ECDSA algorithms
|
|
private eddsa?: plugins.elliptic.eddsa; // For EdDSA algorithms
|
|
|
|
constructor(zone: DnssecZone) {
|
|
this.zone = zone;
|
|
|
|
// Initialize the appropriate cryptographic instance based on the algorithm
|
|
switch (this.zone.algorithm) {
|
|
case 'ECDSA':
|
|
this.ec = new plugins.elliptic.ec('p256'); // Use P-256 curve for ECDSA
|
|
break;
|
|
case 'ED25519':
|
|
this.eddsa = new plugins.elliptic.eddsa('ed25519');
|
|
break;
|
|
case 'RSA':
|
|
// RSA implementation would go here
|
|
throw new Error('RSA algorithm is not yet implemented.');
|
|
default:
|
|
throw new Error(`Unsupported algorithm: ${this.zone.algorithm}`);
|
|
}
|
|
|
|
// Generate the key pair
|
|
this.keyPair = this.generateKeyPair();
|
|
}
|
|
|
|
private generateKeyPair(): DnssecKeyPair {
|
|
let privateKey: string;
|
|
let publicKey: string;
|
|
|
|
switch (this.zone.algorithm) {
|
|
case 'ECDSA':
|
|
if (!this.ec) throw new Error('EC instance is not initialized.');
|
|
const ecKeyPair = this.ec.genKeyPair();
|
|
privateKey = ecKeyPair.getPrivate('hex');
|
|
publicKey = ecKeyPair.getPublic(false, 'hex'); // Uncompressed format
|
|
break;
|
|
case 'ED25519':
|
|
if (!this.eddsa) throw new Error('EdDSA instance is not initialized.');
|
|
const secret = plugins.crypto.randomBytes(32);
|
|
const edKeyPair = this.eddsa.keyFromSecret(secret);
|
|
privateKey = edKeyPair.getSecret('hex');
|
|
publicKey = edKeyPair.getPublic('hex');
|
|
break;
|
|
case 'RSA':
|
|
// RSA key generation would be implemented here
|
|
throw new Error('RSA key generation is not yet implemented.');
|
|
default:
|
|
throw new Error(`Unsupported algorithm: ${this.zone.algorithm}`);
|
|
}
|
|
|
|
return { privateKey, publicKey };
|
|
}
|
|
|
|
public getAlgorithmNumber(): number {
|
|
switch (this.zone.algorithm) {
|
|
case 'ECDSA':
|
|
return 13; // ECDSAP256SHA256
|
|
case 'ED25519':
|
|
return 15;
|
|
case 'RSA':
|
|
return 8; // RSASHA256
|
|
default:
|
|
throw new Error(`Unsupported algorithm: ${this.zone.algorithm}`);
|
|
}
|
|
}
|
|
|
|
public signData(data: Buffer): Buffer {
|
|
// Sign the data using the private key
|
|
const keyPair = this.ec!.keyFromPrivate(this.keyPair.privateKey, 'hex');
|
|
const signature = keyPair.sign(plugins.crypto.createHash('sha256').update(data).digest());
|
|
return Buffer.from(signature.toDER());
|
|
}
|
|
|
|
private generateDNSKEY(): Buffer {
|
|
const flags = 256; // 256 indicates a Zone Signing Key (ZSK)
|
|
const protocol = 3; // Must be 3 according to RFC
|
|
const algorithm = this.getAlgorithmNumber();
|
|
|
|
let publicKeyData: Buffer;
|
|
|
|
switch (this.zone.algorithm) {
|
|
case 'ECDSA':
|
|
if (!this.ec) throw new Error('EC instance is not initialized.');
|
|
const ecPublicKey = this.ec.keyFromPublic(this.keyPair.publicKey, 'hex').getPublic();
|
|
const x = ecPublicKey.getX().toArrayLike(Buffer, 'be', 32);
|
|
const y = ecPublicKey.getY().toArrayLike(Buffer, 'be', 32);
|
|
publicKeyData = Buffer.concat([x, y]);
|
|
break;
|
|
case 'ED25519':
|
|
publicKeyData = Buffer.from(this.keyPair.publicKey, 'hex');
|
|
break;
|
|
case 'RSA':
|
|
// RSA public key extraction would go here
|
|
throw new Error('RSA public key extraction is not yet implemented.');
|
|
default:
|
|
throw new Error(`Unsupported algorithm: ${this.zone.algorithm}`);
|
|
}
|
|
|
|
// Construct the DNSKEY RDATA
|
|
const dnskeyRdata = Buffer.concat([
|
|
Buffer.from([flags >> 8, flags & 0xff]), // Flags (2 bytes)
|
|
Buffer.from([protocol]), // Protocol (1 byte)
|
|
Buffer.from([algorithm]), // Algorithm (1 byte)
|
|
publicKeyData, // Public Key
|
|
]);
|
|
|
|
return dnskeyRdata;
|
|
}
|
|
|
|
private computeKeyTag(dnskeyRdata: Buffer): number {
|
|
// Key Tag calculation as per RFC 4034, Appendix B
|
|
let acc = 0;
|
|
for (let i = 0; i < dnskeyRdata.length; i++) {
|
|
acc += i & 1 ? dnskeyRdata[i] : dnskeyRdata[i] << 8;
|
|
}
|
|
acc += (acc >> 16) & 0xffff;
|
|
return acc & 0xffff;
|
|
}
|
|
|
|
private getDNSKEYRecord(): string {
|
|
const dnskeyRdata = this.generateDNSKEY();
|
|
const flags = 256;
|
|
const protocol = 3;
|
|
const algorithm = this.getAlgorithmNumber();
|
|
const publicKeyData = dnskeyRdata.slice(4); // Skip flags, protocol, algorithm bytes
|
|
const publicKeyBase64 = publicKeyData.toString('base64');
|
|
|
|
return `${this.zone.zone}. IN DNSKEY ${flags} ${protocol} ${algorithm} ${publicKeyBase64}`;
|
|
}
|
|
|
|
public getDSRecord(): string {
|
|
const dnskeyRdata = this.generateDNSKEY();
|
|
const keyTag = this.computeKeyTag(dnskeyRdata);
|
|
const algorithm = this.getAlgorithmNumber();
|
|
const digestType = 2; // SHA-256
|
|
const digest = plugins.crypto
|
|
.createHash('sha256')
|
|
.update(dnskeyRdata)
|
|
.digest('hex')
|
|
.toUpperCase();
|
|
|
|
return `${this.zone.zone}. IN DS ${keyTag} ${algorithm} ${digestType} ${digest}`;
|
|
}
|
|
|
|
public getKeyPair(): DnssecKeyPair {
|
|
return this.keyPair;
|
|
}
|
|
|
|
public getDsAndKeyPair(): { keyPair: DnssecKeyPair; dsRecord: string; dnskeyRecord: string } {
|
|
const dsRecord = this.getDSRecord();
|
|
const dnskeyRecord = this.getDNSKEYRecord();
|
|
return { keyPair: this.keyPair, dsRecord, dnskeyRecord };
|
|
}
|
|
}
|