fix(ts_server): Update DnsSec class to fully implement key generation and DNSKEY record creation.
This commit is contained in:
parent
1536475306
commit
439d08b023
@ -1,5 +1,12 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2024-09-19 - 6.1.1 - fix(ts_server)
|
||||||
|
Update DnsSec class to fully implement key generation and DNSKEY record creation.
|
||||||
|
|
||||||
|
- Added complete support for ECDSA and ED25519 algorithms in the DnsSec class.
|
||||||
|
- Implemented DNSKEY generation and KeyTag computation methods.
|
||||||
|
- Improved error handling and initialized the appropriate cryptographic instances based on the algorithm.
|
||||||
|
|
||||||
## 2024-09-18 - 6.1.0 - feat(smartdns)
|
## 2024-09-18 - 6.1.0 - feat(smartdns)
|
||||||
Add DNS Server and DNSSEC tools with comprehensive unit tests
|
Add DNS Server and DNSSEC tools with comprehensive unit tests
|
||||||
|
|
||||||
|
@ -1,83 +1,172 @@
|
|||||||
|
// Import necessary plugins from plugins.ts
|
||||||
import * as plugins from './plugins.js';
|
import * as plugins from './plugins.js';
|
||||||
|
|
||||||
interface DnssecZone {
|
interface DnssecZone {
|
||||||
zone: string;
|
zone: string;
|
||||||
algorithm: string;
|
algorithm: 'ECDSA' | 'ED25519' | 'RSA';
|
||||||
keySize: number;
|
keySize: number;
|
||||||
days: number;
|
days: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DnssecKeyPair {
|
interface DnssecKeyPair {
|
||||||
private: string;
|
privateKey: string;
|
||||||
public: string;
|
publicKey: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
class DnsSec {
|
export class DnsSec {
|
||||||
private zone: DnssecZone;
|
private zone: DnssecZone;
|
||||||
private keyPair: DnssecKeyPair;
|
private keyPair: DnssecKeyPair;
|
||||||
private ec: any; // declare the ec instance
|
private ec?: plugins.elliptic.ec; // For ECDSA algorithms
|
||||||
|
private eddsa?: plugins.elliptic.eddsa; // For EdDSA algorithms
|
||||||
|
|
||||||
constructor(zone: DnssecZone) {
|
constructor(zone: DnssecZone) {
|
||||||
this.zone = zone;
|
this.zone = zone;
|
||||||
this.ec = new plugins.elliptic.ec('secp256k1'); // Create an instance of the secp256k1 curve
|
|
||||||
|
// 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();
|
this.keyPair = this.generateKeyPair();
|
||||||
}
|
}
|
||||||
|
|
||||||
private generateKeyPair(): DnssecKeyPair {
|
private generateKeyPair(): DnssecKeyPair {
|
||||||
const key = this.ec.genKeyPair();
|
let privateKey: string;
|
||||||
const privatePem = key.getPrivate().toString('hex'); // get private key in hex format
|
let publicKey: string;
|
||||||
// @ts-ignore
|
|
||||||
const publicPem = key.getPublic().toString('hex'); // get public key in hex format
|
|
||||||
|
|
||||||
return {
|
|
||||||
private: privatePem,
|
|
||||||
public: publicPem
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private formatPEM(pem: string, type: string): string {
|
|
||||||
const start = `-----BEGIN ${type}-----`;
|
|
||||||
const end = `-----END ${type}-----`;
|
|
||||||
|
|
||||||
const formatted = [start];
|
|
||||||
for (let i = 0; i < pem.length; i += 64) {
|
|
||||||
formatted.push(pem.slice(i, i + 64));
|
|
||||||
}
|
|
||||||
formatted.push(end);
|
|
||||||
return formatted.join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
public getDSRecord(): string {
|
|
||||||
const publicPem = this.keyPair.public;
|
|
||||||
const publicKey = this.ec.keyFromPublic(publicPem); // Create a public key from the publicPEM
|
|
||||||
|
|
||||||
const digest = publicKey.getPublic(); // get public point
|
|
||||||
return `DS {id} 8 {algorithm} {digest} {hash-algorithm}\n`
|
|
||||||
.replace('{id}', '256') // zone hash
|
|
||||||
.replace('{algorithm}', this.getAlgorithm())
|
|
||||||
.replace('{digest}', `0x${digest.getX()}${digest.getY()}`)
|
|
||||||
.replace('{hash-algorithm}', '2');
|
|
||||||
}
|
|
||||||
|
|
||||||
private getAlgorithm(): string {
|
|
||||||
switch (this.zone.algorithm) {
|
switch (this.zone.algorithm) {
|
||||||
case 'ECDSA':
|
case 'ECDSA':
|
||||||
return '8';
|
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':
|
case 'ED25519':
|
||||||
return '15';
|
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':
|
case 'RSA':
|
||||||
return '1';
|
// RSA key generation would be implemented here
|
||||||
|
throw new Error('RSA key generation is not yet implemented.');
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unsupported algorithm: ${this.zone.algorithm}`);
|
throw new Error(`Unsupported algorithm: ${this.zone.algorithm}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return { privateKey, publicKey };
|
||||||
|
}
|
||||||
|
|
||||||
|
private 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 {
|
public getKeyPair(): DnssecKeyPair {
|
||||||
return this.keyPair;
|
return this.keyPair;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getDsAndKeyPair(): [DnssecKeyPair, string] {
|
public getDsAndKeyPair(): { keyPair: DnssecKeyPair; dsRecord: string; dnskeyRecord: string } {
|
||||||
const dsRecord = this.getDSRecord();
|
const dsRecord = this.getDSRecord();
|
||||||
return [this.keyPair, dsRecord];
|
const dnskeyRecord = this.getDNSKEYRecord();
|
||||||
|
return { keyPair: this.keyPair, dsRecord, dnskeyRecord };
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,10 +1,12 @@
|
|||||||
// node native
|
// node native
|
||||||
|
import crypto from 'crypto';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import http from 'http';
|
import http from 'http';
|
||||||
import https from 'https';
|
import https from 'https';
|
||||||
import dgram from 'dgram';
|
import dgram from 'dgram';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
crypto,
|
||||||
fs,
|
fs,
|
||||||
http,
|
http,
|
||||||
https,
|
https,
|
||||||
|
Loading…
Reference in New Issue
Block a user