Skip to content

Commit

Permalink
ownership: upgrade sha1 key algorithms
Browse files Browse the repository at this point in the history
  • Loading branch information
pinheadmz committed Apr 30, 2022
1 parent 1bc5d05 commit 9c320b3
Show file tree
Hide file tree
Showing 5 changed files with 238 additions and 2 deletions.
19 changes: 18 additions & 1 deletion bin/bns-prove
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,23 @@ if (!txt)
if (!priv)
continue;

let signingKey = key;

// Upgrade SHA1 algorithms to SHA256
if (ctx.isSHA1(key.data.algorithm)) {
signingKey = dnssec.upgradeDNSKEY(key);

console.log(`Upgrading key algorithm for key ID ${key.data.keyTag()}`);

// Insert upgraded key into DNSKEY RRset and re-sign with upgraded key
zone.keys.length = 0;
zone.keys.push(key);
zone.keys.push(signingKey);
zone.keys.push(
dnssec.sign(signingKey, priv, [key, signingKey],lifespan)
);
}

const rr = new Record();
const rd = new TXTRecord();

Expand All @@ -157,7 +174,7 @@ if (!txt)

rd.txt.push(txt);

const sig = dnssec.sign(key, priv, [rr], lifespan);
const sig = dnssec.sign(signingKey, priv, [rr], lifespan);

zone.claim.push(rr);
zone.claim.push(sig);
Expand Down
15 changes: 15 additions & 0 deletions lib/dnssec.js
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,21 @@ dnssec.stripSignatures = function stripSignatures(msg) {
return msg;
};

dnssec.upgradeDNSKEY = function upgradeDNSKEY(ksk) {
assert(ksk instanceof Record);
assert(ksk.type === types.DNSKEY);
const key = ksk.deepClone();
switch(key.data.algorithm) {
case algs.RSASHA1:
case algs.RSASHA1NSEC3SHA1:
key.data.algorithm = algs.RSASHA256;
break;
default:
throw new Error('Algorithm is not SHA1.');
}
return key;
};

/*
* Helpers
*/
Expand Down
4 changes: 3 additions & 1 deletion lib/ownership.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ const {
*/

const rootAnchors = [
Record.fromString(KSK_2017)
Record.fromString(
'. DS 28750 15 2 '+
'3B194170CF9EDF967B50CA146386F08E573E52919983BAF03B8E66E2A43D7900')
];

/**
Expand Down
13 changes: 13 additions & 0 deletions test/data/Kweakkeytld.+005+03810.private
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Private-key-format: v1.3
Algorithm: 5 (RSASHA1)
Modulus: vExWE8PxRiW0zoaec/vit/SiAq2qr0lI9Se4ZbyVbBGVRckLGqeXx1t6z1yAvfUUGc/UXiz6EtZ8kPrVIx4RfRpur+V0pLjHtw9VEA6ocjm9tGirsYcR+HDPiT+ebJn4YxcLjqQ3rMNGpIOon9oNErpdtLJT88Piif0+Ix5Zgt8x7OCCr4dB9vfDIJU+JwkdaXaIJmxNqrXL23q3uZrdL8n8GurzYqb+OGj+kpVa9htxrJ9IPPhV1+IzXLGtMsm91uZgV29BnhWKS2I7IRnvP48izPPUaYmZNKNaXEl7PLfukJB/3jeBaHb3UR36p1y9tlj82FuiCsNZ82OgLMBMeQ==
PublicExponent: AQAB
PrivateExponent: gVUVUmIlWH8Rn0ELmLKL2LoohRpvwBHgELMFjqtnHmE9XD9oZxhxwbZttfvdWZv6AHilb0IJSeMUkCgZOROwA3OiTyKVaYaZdn0Legn3XO2YBVBXjYoup6wkgCq4T/O5jaYIhCL7Mqi13s2nhpQoopJrRm+uBiKneQv+H/T6mk4wwJH9mX9OrDgtHWXlSm1Pn+axmdo4QOmitLoSHYGb0bHC8QuDp3jQqrjdyKHaZjQalhxjI3J/YU9Gcqg/7zV3G+/z9/57SIXiT3pYhrKyHtXVPAnEkY7Mlv4mChBB2kXVz+sjQBzRWxLwEupzOt/MTciy2zU3BmTaCqmzRx9g4Q==
Prime1: 7pM3ICtPxu2TyUo0BBFP/MC1x/4dtthGNb9u/NvtaDz7hebbVpivma6sUYy2Cr/qU6LKtla8S4n/2ZFDDBZ/m7dOYH7S4pTamR1sg31utYaaXXz/NaOqvflLhL9zHIG0cVFXBJbBk1TxQCPnPMBxxIAO71YqlxpkJDpmc7dSaQ0=
Prime2: yg0QNP0BiBjU+K3+2OD3TILotg3UKuh9vPNVP4yAThBb8Tj42T+v5Lp0ngAbjorNTcwwFHcWaNkW+xIERt7Qszpc168aidTkBtzKHq8auPUXjYLTqmPOMTjkuKQpJA8mkSFrK0EXOD+PzEbTXT9hf2tJmwQYqpnzfksDTgIOfh0=
Exponent1: FZRrYBWK2cuTmpmDqvqF6a7kwnpR5cAaWbI/L9AU4WDiv3HqVSLqf1Q1cgrwTHifkYEgJO4jihGpetyQ2/8M++DQzhmqqaQdQcxPjHVEFldejyZHT2rGfviVxCQtHV7+G2HoJRod2F3OlKaqu+wMXmHW9/8rSw+wDzbZHY5vfeE=
Exponent2: sw4MlMi5+L/4zEB1ngEPZvCEBgIPSc8qzq3dmCmM8qEwuX7BmNUFrW9HNVXnFdUPx6fx29EFPPlVkj4PKDcBQDOyXzlADXFqemJjg/mtdsdMjg2oQlGcQqammrJv+xKqHOc+r+BJEYHSjzcczIAEiQStAAELUvSHgh472X+dFEk=
Coefficient: s0ZyxTFjS5t/G95NNUd4eUpX6rfw4NIl93O6ZfEB0VLoqw3B0XOShE4jR5bDx5kQv0y023jVct69rrrw9qsJP7dYor5JnjJq7Wx6RGYEYXs0crD9JLI8Jk+teVGkxA0iFbhEp/cScS48D+cWHW4wC7aTIOyaciYVZTnaarZuLhA=
Created: 20220429130726
Publish: 20220429130726
Activate: 20220429130726
189 changes: 189 additions & 0 deletions test/proof-key-upgrade-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/* eslint-env mocha */
/* eslint prefer-arrow-callback: "off" */

'use strict';

const assert = require('bsert');
const path = require('path');
const Ownership = require('../lib/ownership');
const dnssec = require('../lib/dnssec');
const {
types,
Message,
Record
} = require('../lib/wire');

describe('Ownership Proof Key Upgrade', function () {
// Create a fake stub resolver with our own root zone and one TLD.
// Root zone is signed with ED25519 but weakkeytld is signed with RSASHA1.
// The RRSIGs in these zones expire on Dec. 31 2080. So, you know. Heads up.
class Stub {
lookup (name, type) {
const msg = new Message();
switch (name) {
case 'weakkeytld.':
switch (type) {
case types.DS:
msg.answer.push(Record.fromString(
'weakkeytld. DS 3810 5 2 ' +
'BE06B4A3C4E50914DDE8670D68EFE6E04' +
'24941B3F9BD364210BDFAFD6AF23526'
));
msg.answer.push(Record.fromString(
'weakkeytld. 300 RRSIG DS 15 1 300 ' +
'20801231000000 20220429134556 28750 . ' +
'3LfCEc+Yyx9OjWONSx41iphYjqJSKN2mUXM2' +
'A1MCLBqx93b+T7FGPJUxAmumxR6T5v/VZ37+' +
'9d6LwTfOxgVfBg=='

));
break;
case types.TXT:
msg.answer.push(Record.fromString(
'weakkeytld. 300 TXT "hns-claim"'
));
msg.answer.push(Record.fromString(
'weakkeytld. 300 RRSIG TXT 5 1 300 ' +
'20801231000000 20220429134807 3810 weakkeytld. ' +
'K9VKxanJnr8jI9zGtStG37TIIBmks9w2LH2Y' +
'jD5SKkKiBPB4Orc9sLLlEm1FL5eOl8er4xYK' +
'wue68e5xJn3njkuwmEx2SpalDKCubQrxuIQJ' +
'dEHiAr2+bl++vvVn4V30s6rcU5rK4XmnjzUt' +
'z12dB+++EMW3K17tbbhzVgFB5T/iBeCgrsjw' +
'M+iKc0DYH8xo/yE4y/y64SjzfNy9Q2zxA1Zm' +
'wkgqxVTKxw1W1GwuQiUkmy8xTTpSwFBpadOJ' +
'GHmjUTiopRMr46aqO1p9VB/vO5V1hNzVa9Zp' +
'G7aoAOANhqYSQ7um2//oCPr4xRQHgaCf0wdz' +
'yczVBX4oLOLk4uvUeg=='
));
break;
case types.DNSKEY:
msg.answer.push(Record.fromString(
'weakkeytld. 300 DNSKEY 256 3 5 ' +
'AwEAAbxMVhPD8UYltM6GnnP74rf0ogKtqq9J' +
'SPUnuGW8lWwRlUXJCxqnl8dbes9cgL31FBnP' +
'1F4s+hLWfJD61SMeEX0abq/ldKS4x7cPVRAO' +
'qHI5vbRoq7GHEfhwz4k/nmyZ+GMXC46kN6zD' +
'RqSDqJ/aDRK6XbSyU/PD4on9PiMeWYLfMezg' +
'gq+HQfb3wyCVPicJHWl2iCZsTaq1y9t6t7ma' +
'3S/J/Brq82Km/jho/pKVWvYbcayfSDz4Vdfi' +
'M1yxrTLJvdbmYFdvQZ4ViktiOyEZ7z+PIszz' +
'1GmJmTSjWlxJezy37pCQf943gWh291Ed+qdc' +
'vbZY/NhbogrDWfNjoCzATHk='
));
msg.answer.push(Record.fromString(
'weakkeytld. 300 RRSIG DNSKEY 5 1 300 ' +
'20801231000000 20220429134807 3810 weakkeytld. ' +
'kiTnUHoXHB8MC42iJImgIi2U4+xrUILV7sKw' +
'f3KA6NK4AaMgReRqMCo67IOt/vwu6g47qeaq' +
'/47AYXGA4vXYXdv49EOeUQvwB7AD/tAMHSy0' +
'+TwyJOHgja0Fl0kgkGoIhkWB8MSWHT86E0qN' +
'OnoO9rFFgHx91hgxZM5Nll/pEr/kQuDTJK/o' +
'ixjluxGkkN+AgIN3spZTWx4fN2hOvWriLv8+' +
'CwFGdqZuOa+lSpXh+EcnVBOJhagCrrxpsyN8' +
'8NI+E5nsxyNS8rWRWnE7gX3GQi+xBFbe2RzJ' +
'pQW+D+MDS8ofo9QWTIV7ho6xSjzfq8uKi/nt' +
'2PBkdBNo35e1c30YLw=='
));
break;
}
break;

case '.':
switch (type) {
case types.DNSKEY:
msg.answer.push(Record.fromString(
'. 300 DNSKEY 257 3 15 '+
'2cUGwCbFjpVkvUS1ZLH0sA+K4K4nExUtMjr7iCvoTWQ='
));
msg.answer.push(Record.fromString(
'. 300 RRSIG DNSKEY 15 0 300 ' +
'20801231000000 20220429134556 28750 . ' +
'KeXA54XobZC8MV3OTgtUdTNvd5nt40lFDech' +
'LUt+ngF5cvZV5CnSoFWgMc4/LEKRVVIhAKzc' +
'v6PqrzgJCuTTDA=='
));
break;
}
}
return msg;
}
};

const ownership = new Ownership();

// Trust anchor for our fake local root zone
ownership.anchors = [Record.fromString(
'. DS 28750 15 2 '+
'3B194170CF9EDF967B50CA146386F08E573E52919983BAF03B8E66E2A43D7900'
)];

// Hack instanceof assertion in ownership._prove()
ownership.Resolver = Stub;

it('should fail by default to generate RSASHA1 proof', async () => {
// They don't even count as actual RRSIGs
await assert.rejects(
ownership._prove(new Stub(), 'weakkeytld.', false),
{message: 'No RRSIG(TXT) records for weakkeytld.'}
);

// Sanity check
const res = new Stub().lookup('weakkeytld.', types.TXT);
assert(res.answer[1].type === types.RRSIG);
assert(ownership.isSHA1(res.answer[1].data.algorithm));
});

it('should generate RSASHA1 proof with secure=false', async () => {
try {
ownership.secure = false;
const proof = await ownership._prove(new Stub(), 'weakkeytld.', false);
assert(proof.zones[1].claim[1].type === types.RRSIG);
assert(ownership.isSHA1(proof.zones[1].claim[1].data.algorithm));
} finally {
ownership.secure = true; // default
}
});

it('should upgrade weak key algorithm', async () => {
let proof, target;
try {
ownership.secure = false; // need secure=false just to get proof template
proof = await ownership._prove(new Stub(), 'weakkeytld.', true);
target = proof.zones[1];
const key = target.keys[0];
const txtRR = proof.zones[1].claim[0];

assert(key.type === types.DNSKEY);
assert(txtRR.type === types.TXT);

// Kweakkeytld.+005+03810.private
const priv = await dnssec.readPrivateAsync(
path.join(__dirname, 'data'),
key
);

// Here's the sneaky magic: create a duplicate key with better algorithm.
const key256 = dnssec.upgradeDNSKEY(key);

// Sign DNSKEY RRset now including both old and new keys.
const keySig = dnssec.sign(key256, priv, [key256, key], 24 * 60 * 60);
target.keys[0] = key;
target.keys[1] = key256;
target.keys[2] = keySig;

// Now sign the claim TXT with the new key
const txtSig = dnssec.sign(key256, priv, [txtRR], 24 * 60 * 60);
target.claim[1] = txtSig;
} finally {
ownership.secure = true; // default, and required by HNS consenus rules
}

assert(ownership.isSane(proof));
assert(ownership.verifySignatures(proof));

// Sanity check
assert(target.claim[1].type === types.RRSIG);
assert(!ownership.isSHA1(target.claim[1].data.algorithm));
});
});

0 comments on commit 9c320b3

Please sign in to comment.