Skip to content

Commit 5852eeb

Browse files
authored
chore(crypto): CRP-2794 Modify VetKD key derivation scheme (#97)
1 parent 303a8ae commit 5852eeb

File tree

4 files changed

+94
-7
lines changed

4 files changed

+94
-7
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/ic_vetkeys/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
"make:docs": "mkdir -p $(git rev-parse --show-toplevel)/docs/tools && typedoc --out $(git rev-parse --show-toplevel)/docs",
8282
"prettier": "prettier --write .",
8383
"prettier-check": "prettier --check .",
84+
"test_utils": "vitest utils",
8485
"test": "npm run test:deploy_all && export $(cat .test1.env .test2.env | xargs) && vitest",
8586
"test:deploy_all": "npm run test:deploy_key_manager_canister && npm run test:deploy_encrypted_maps_canister",
8687
"test:deploy_key_manager_canister": "cd $(git rev-parse --show-toplevel)/backend/rs/canisters/ic_vetkeys_manager_canister && dfx start --clean --background; dfx deploy && grep CANISTER_ID .env > $(git rev-parse --show-toplevel)/frontend/ic_vetkeys/.test1.env",

frontend/ic_vetkeys/src/utils/utils.test.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ import {
22
DerivedPublicKey,
33
EncryptedVetKey,
44
IdentityBasedEncryptionCiphertext,
5+
MasterPublicKey,
56
TransportSecretKey,
7+
VetKey,
68
augmentedHashToG1,
7-
hashToScalar,
89
deriveSymmetricKey,
9-
VetKey,
10+
hashToScalar,
1011
} from "./utils";
1112
import { expect, test } from "vitest";
1213

@@ -48,20 +49,37 @@ test("parsing DerivedPublicKey", () => {
4849
assertEqual(valid, key.publicKeyBytes());
4950
});
5051

52+
test("MasterPublicKey derivation", () => {
53+
const masterKey = MasterPublicKey.deserialize(
54+
hexToBytes(
55+
"9183b871aa141d15ba2efc5bc58a49cb6a167741364804617f48dfe11e0285696b7018f172dad1a87ed81abf27ea4c320995041e2ee4a47b2226a2439d92a38557a7e2acc72fd157283b20f1f37ba872be235214c6a9cbba1eb2ef39deec72a5",
56+
),
57+
);
58+
59+
const canisterId = new TextEncoder().encode("test-canister-id");
60+
61+
const derivedKey = masterKey.deriveKey(canisterId);
62+
63+
assertEqual(
64+
bytesToHex(derivedKey.publicKeyBytes()),
65+
"af78a908589d332fc8b9d042807c483e73872e2aea7620bdb985b9289d5a99ebfd5ac0ec4844a4c542f6d0f12a716d941674953cef4f38dde601ce9792db8832557eaa051733c5541fa5017465d69b62cc4d93f2079fb8c050b4bd735ef75859",
66+
);
67+
});
68+
5169
test("DerivedPublicKey subderivation", () => {
5270
const canisterKey = DerivedPublicKey.deserialize(
5371
hexToBytes(
54-
"972c4c6cc184b56121a1d27ef1ca3a2334d1a51be93573bd18e168f78f8fe15ce44fb029ffe8e9c3ee6bea2660f4f35e0774a35a80d6236c050fd8f831475b5e145116d3e83d26c533545f64b08464e4bcc755f990a381efa89804212d4eef5f",
72+
"8bf165ea580742abf5fd5123eb848aa116dcf75c3ddb3cd3540c852cf99f0c5394e72dfc2f25dbcb5f9220f251cd04040a508a0bcb8b2543908d6626b46f09d614c924c5deb63a9949338ae4f4ac436bd77f8d0a392fd29de0f392a009fa61f3",
5573
),
5674
);
5775

58-
const context = hexToBytes("f00fee");
76+
const context = new TextEncoder().encode("test-context");
5977

6078
const derivedKey = canisterKey.deriveKey(context);
6179

6280
assertEqual(
6381
bytesToHex(derivedKey.publicKeyBytes()),
64-
"8bf4d77b519852e5bd4bf9b7dd236737112e9da12f982b61f7d474a99642f2da2b76d2910efd24e3cd1a12e6fa9b45890dd3f8a2a600d80cb8d13ea7057e29ba675924377f4cc6083b141bcf396d9c6e29efee56638a9c7bc1bc3832c07853c8",
82+
"80b4f1e11766d32bed0ea4e8b05e82bf84519de4a63eca0213d9e3603a946ea2968150882d1e9508701f34048fcec80919b4f493a2a254fc13dc956f1d82c6b8e641f962e1c0342c95eb58e168327d5e51e9337627ac9f1aa93d2e3058a1ff09",
6583
);
6684
});
6785

frontend/ic_vetkeys/src/utils/utils.ts

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,69 @@ function prefixWithLen(input: Uint8Array): Uint8Array {
8585
return result;
8686
}
8787

88+
/**
89+
* VetKD master key
90+
*
91+
* The VetKD subnet contains a small number of master keys, from which canister
92+
* keys are derived. In turn, many keys can be derived from the canister keys
93+
* using a context string.
94+
*/
95+
export class MasterPublicKey {
96+
readonly #pk: G2Point;
97+
98+
/**
99+
* Read a MasterPublicKey from the bytestring encoding
100+
*
101+
* Normally the bytes provided here will have been returned by
102+
* the `vetkd_public_key` management canister interface.
103+
*/
104+
static deserialize(bytes: Uint8Array): MasterPublicKey {
105+
return new MasterPublicKey(bls12_381.G2.ProjectivePoint.fromHex(bytes));
106+
}
107+
108+
/**
109+
* Derive a canister master key from the subnet master key
110+
*
111+
* To create the derived public key in VetKD, a two step derivation is performed. The first step
112+
* creates a key that is specific to the canister that is making VetKD requests to the
113+
* management canister, sometimes called canister master key.
114+
*
115+
* This function can be used to compute canister master keys knowing just the subnet master key
116+
* plus the canister identity. This avoids having to interact with the IC for performing this
117+
* computation.
118+
*/
119+
deriveKey(canister_id: Uint8Array): DerivedPublicKey {
120+
const dst = "ic-vetkd-bls12-381-g2-canister-id";
121+
const pkbytes = this.publicKeyBytes();
122+
const ro_input = new Uint8Array([
123+
...prefixWithLen(pkbytes),
124+
...prefixWithLen(canister_id),
125+
]);
126+
const offset = hashToScalar(ro_input, dst);
127+
const g2_offset = bls12_381.G2.ProjectivePoint.BASE.multiply(offset);
128+
return new DerivedPublicKey(this.#pk.add(g2_offset));
129+
}
130+
131+
/**
132+
* Return the bytestring encoding of the master public key
133+
*/
134+
publicKeyBytes(): Uint8Array {
135+
return this.#pk.toRawBytes(true);
136+
}
137+
138+
/**
139+
* TODO CRP-2797 add getter for the production subnet key once this has been
140+
* generated.
141+
*/
142+
143+
/**
144+
* @internal constructor
145+
*/
146+
constructor(pk: G2Point) {
147+
this.#pk = pk;
148+
}
149+
}
150+
88151
/**
89152
* VetKD derived public key
90153
*
@@ -127,7 +190,12 @@ export class DerivedPublicKey {
127190
return this;
128191
} else {
129192
const dst = "ic-vetkd-bls12-381-g2-context";
130-
const offset = hashToScalar(prefixWithLen(context), dst);
193+
const pkbytes = this.publicKeyBytes();
194+
const ro_input = new Uint8Array([
195+
...prefixWithLen(pkbytes),
196+
...prefixWithLen(context),
197+
]);
198+
const offset = hashToScalar(ro_input, dst);
131199
const g2_offset =
132200
bls12_381.G2.ProjectivePoint.BASE.multiply(offset);
133201
return new DerivedPublicKey(this.getPoint().add(g2_offset));

0 commit comments

Comments
 (0)