Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decryption of validator key from P2P API #2088

Merged
merged 13 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"@openzeppelin/defender-relay-client": "^1.54.4",
"@openzeppelin/defender-sdk": "^1.13.1",
"@openzeppelin/hardhat-upgrades": "^1.10.0",
"@rigidity/bls-signatures": "^2.0.5",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^15.2.3",
Expand Down
104 changes: 104 additions & 0 deletions contracts/tasks/crypto.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
const bls = require("@rigidity/bls-signatures");
const {
subtle,
createCipheriv,
createECDH,
createHash,
createHmac,
} = require("node:crypto");

const ecdhCurveName = "prime256v1";

const genECDHKey = async ({ privateKey }) => {
const ecdh = createECDH(ecdhCurveName);

if (privateKey) {
ecdh.setPrivateKey(Buffer.from(privateKey, "hex"));
} else {
ecdh.generateKeys();
}

const publicKeyBase64 = ecdh.getPublicKey("base64");

console.log(`Public key: ${publicKeyBase64}`);

const subtleKey = await subtle.importKey(
"raw",
ecdh.getPublicKey(),
{ name: "ECDH", namedCurve: "P-256" },
true,
[]
);
const fmtKey = await subtle.exportKey("spki", subtleKey);
const publicKeyPEM = `-----BEGIN PUBLIC KEY-----\n${Buffer.from(fmtKey)
.toString("base64")
.replace(/.{64}/g, "$&\n")
.replace(/\n$/g, "")}\n-----END PUBLIC KEY-----\n`;
console.log(`Public key in PEM format:\n${publicKeyPEM.toString()}`);

// base64 encode the PEM format again to get P2P format
const p2pPublicKey = Buffer.from(publicKeyPEM).toString("base64");
console.log(`Encoded public key for P2P API:\n${p2pPublicKey}`);
};

const decryptValidatorKey = async ({ privateKey, message }) => {
const ecdh = createECDH(ecdhCurveName);
ecdh.setPrivateKey(privateKey, "hex");

const validatorPrivateKey = decrypt(ecdh, Buffer.from(message, "base64"));

const vsk = bls.PrivateKey.fromBytes(validatorPrivateKey);
console.log(`Validator public key: ${vsk.getG1().toHex()}`);
};

const decrypt = (ecdh, msg) => {
const epk = msg.slice(0, 65);
const message = msg.slice(65, msg.length - 32);
const sharedSecret = ecdh.computeSecret(epk);
const { encKey, macKey } = deriveKeys(sharedSecret, Buffer.alloc(0), 16);
const tag = messageTag(macKey, message, Buffer.alloc(0));
if (tag.toString("hex") !== msg.slice(msg.length - 32).toString("hex")) {
throw new Error("tag mismatch");
}
return symDecrypt(encKey, message);
};

const deriveKeys = (secret, s1, keyLen) => {
const keys = concatKDF(secret, s1, keyLen * 2);
const encKey = keys.slice(0, keyLen);
const macKey = createHash("sha256")
.update(keys.slice(keyLen, keyLen * 2))
.digest();
return { encKey, macKey };
};

const messageTag = (macKey, message, s2) => {
return createHmac("sha256", macKey).update(message).update(s2).digest();
};

const symDecrypt = (key, ct) => {
const c = createCipheriv("aes-128-ctr", key, ct.slice(0, 16));
const m = Buffer.alloc(ct.length - 16);
c.update(ct.slice(16)).copy(m);
return m;
};

const concatKDF = (secret, s1, keyLen) => {
let hashSum = Buffer.from("");
for (let ctr = 1; hashSum.length < keyLen; ctr++) {
const ctrs = Buffer.from([ctr >> 24, ctr >> 16, ctr >> 8, ctr]); // Buffer.from([ctr >> 24, ctr >> 16, ctr >> 8, ctr])
const tmp = [
hashSum,
createHash("sha256")
.update(Buffer.concat([ctrs, secret, s1]))
.digest(),
];
hashSum = Buffer.concat(tmp);
}
return hashSum.slice(0, keyLen);
};

module.exports = {
genECDHKey,
decryptValidatorKey,
};
38 changes: 37 additions & 1 deletion contracts/tasks/tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ const { execute, executeOnFork, proposal, governors } = require("./governance");
const { smokeTest, smokeTestCheck } = require("./smokeTest");
const addresses = require("../utils/addresses");
const { networkMap } = require("../utils/hardhat-helpers");
const { resolveContract } = require("../utils/resolvers");
const { genECDHKey, decryptValidatorKey } = require("./crypto.js");
const { getSigner } = require("../utils/signers");

const {
Expand Down Expand Up @@ -81,7 +83,6 @@ const {
fixAccounting,
pauseStaking,
} = require("./validator");
const { resolveContract } = require("../utils/resolvers");
const { harvestAndSwap } = require("./harvest");

// can not import from utils/deploy since that imports hardhat globally
Expand Down Expand Up @@ -1169,6 +1170,41 @@ task("pauseStaking").setAction(async (_, __, runSuper) => {
return runSuper();
});

// Encryption

subtask("genECDHKey", "Generate Elliptic-curve Diffie–Hellman (ECDH) key pair")
.addOptionalParam(
"privateKey",
"Private key to encrypt the message with in base64 format",
undefined,
types.string
)
.setAction(genECDHKey);
task("genECDHKey").setAction(async (_, __, runSuper) => {
return runSuper();
});

subtask(
"decrypt",
"Decrypt a message using a Elliptic-curve Diffie–Hellman (ECDH) key pair"
)
.addParam(
"privateKey",
"Private key to decrypt the message with in hex format without the 0x prefix",
undefined,
types.string
)
.addParam(
"message",
"Encrypted validator key returned form P2P API",
undefined,
types.string
)
.setAction(decryptValidatorKey);
task("decrypt").setAction(async (_, __, runSuper) => {
return runSuper();
});

// Defender
subtask(
"setActionVars",
Expand Down
50 changes: 48 additions & 2 deletions contracts/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1294,6 +1294,15 @@
path-browserify "^1.0.0"
url "^0.11.0"

"@rigidity/bls-signatures@^2.0.5":
version "2.0.5"
resolved "https://registry.yarnpkg.com/@rigidity/bls-signatures/-/bls-signatures-2.0.5.tgz#4bd32a4594bb0c61a54101e4e1736ffc4de17e49"
integrity sha512-ybuB+z1+Duyy7wuyBbuM0xRc+pEbMqDQ7UjjkzceYQRWg78RmsFcNs0tNvZbmThYc/IFvb7sWmdgzbOcvtevYA==
dependencies:
chai "^4.3.6"
jssha "^3.2.0"
randombytes "^2.1.0"

"@rollup/plugin-commonjs@^25.0.7":
version "25.0.7"
resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.7.tgz#145cec7589ad952171aeb6a585bbeabd0fd3b4cf"
Expand Down Expand Up @@ -2757,6 +2766,19 @@ chai@^4.3.4:
pathval "^1.1.1"
type-detect "^4.0.5"

chai@^4.3.6:
version "4.4.1"
resolved "https://registry.yarnpkg.com/chai/-/chai-4.4.1.tgz#3603fa6eba35425b0f2ac91a009fe924106e50d1"
integrity sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==
dependencies:
assertion-error "^1.1.0"
check-error "^1.0.3"
deep-eql "^4.1.3"
get-func-name "^2.0.2"
loupe "^2.3.6"
pathval "^1.1.1"
type-detect "^4.0.8"

chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
Expand Down Expand Up @@ -2789,6 +2811,13 @@ check-error@^1.0.2:
resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82"
integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==

check-error@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694"
integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==
dependencies:
get-func-name "^2.0.2"

chokidar@3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.0.tgz#12c0714668c55800f659e262d4962a97faf554a6"
Expand Down Expand Up @@ -3311,7 +3340,7 @@ decompress-response@^6.0.0:
dependencies:
mimic-response "^3.1.0"

deep-eql@^4.1.2:
deep-eql@^4.1.2, deep-eql@^4.1.3:
version "4.1.3"
resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d"
integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==
Expand Down Expand Up @@ -4694,6 +4723,11 @@ get-func-name@^2.0.0:
resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41"
integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==

get-func-name@^2.0.1, get-func-name@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41"
integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==

get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0:
version "1.2.1"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82"
Expand Down Expand Up @@ -5886,6 +5920,11 @@ jsprim@^1.2.2:
json-schema "0.4.0"
verror "1.10.0"

jssha@^3.2.0:
version "3.3.1"
resolved "https://registry.yarnpkg.com/jssha/-/jssha-3.3.1.tgz#c5b7fc7fb9aa745461923b87df0e247dd59c7ea8"
integrity sha512-VCMZj12FCFMQYcFLPRm/0lOBbLi8uM2BhXPTqw3U4YAfs4AZfiApOoBLoN8cQE60Z50m1MYMTQVCfgF/KaCVhQ==

jszip@^3.10.1, jszip@^3.5.0:
version "3.10.1"
resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2"
Expand Down Expand Up @@ -6164,6 +6203,13 @@ loupe@^2.3.1:
dependencies:
get-func-name "^2.0.0"

loupe@^2.3.6:
version "2.3.7"
resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.7.tgz#6e69b7d4db7d3ab436328013d37d1c8c3540c697"
integrity sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==
dependencies:
get-func-name "^2.0.1"

lowercase-keys@^1.0.0, lowercase-keys@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
Expand Down Expand Up @@ -8598,7 +8644,7 @@ type-check@~0.3.2:
dependencies:
prelude-ls "~1.1.2"

type-detect@^4.0.0, type-detect@^4.0.5:
type-detect@^4.0.0, type-detect@^4.0.5, type-detect@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
Expand Down