Skip to content

Commit 65651d5

Browse files
authored
feat: CRP-2826 CRP-2827 Provide a more consistent interface in the TS API regarding serialization and deserialization (#156)
1 parent 6ab878c commit 65651d5

5 files changed

Lines changed: 77 additions & 33 deletions

File tree

backend/rs/ic_vetkeys/src/utils/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,17 @@ impl EncryptedVetKey {
396396
}
397397
}
398398

399+
/// Serialize the encrypted VetKey
400+
pub fn serialize(&self) -> Vec<u8> {
401+
let mut result = vec![];
402+
403+
result.extend_from_slice(&self.c1.to_compressed());
404+
result.extend_from_slice(&self.c2.to_compressed());
405+
result.extend_from_slice(&self.c3.to_compressed());
406+
407+
result
408+
}
409+
399410
/// Deserializes an encrypted key from a byte vector
400411
pub fn deserialize(bytes: &[u8]) -> Result<EncryptedVetKey, String> {
401412
let ek_bytes: &[u8; Self::BYTES] = bytes.try_into().map_err(|_e: TryFromSliceError| {

frontend/ic_vetkeys/src/encrypted_maps/index.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,8 @@ export class EncryptedMaps {
570570
...mapName,
571571
]);
572572

573-
const encryptedVetKey = new EncryptedVetKey(encryptedKeyBytes);
573+
const encryptedVetKey =
574+
EncryptedVetKey.deserialize(encryptedKeyBytes);
574575
const derivedPublicKey =
575576
DerivedPublicKey.deserialize(verificationKey);
576577
const vetKey = encryptedVetKey.decryptAndVerify(
@@ -638,7 +639,9 @@ export class EncryptedMaps {
638639
mapName,
639640
]);
640641
if (cachedRawDerivedKeyMaterial) {
641-
return new DerivedKeyMaterial(cachedRawDerivedKeyMaterial);
642+
return DerivedKeyMaterial.fromCryptoKey(
643+
cachedRawDerivedKeyMaterial,
644+
);
642645
}
643646

644647
const derivedKeyMaterial = await this.getDerivedKeyMaterial(

frontend/ic_vetkeys/src/key_manager/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,7 @@ export class KeyManager {
123123
vetkeyName: Uint8Array,
124124
): Promise<Uint8Array> {
125125
// create a random transport key
126-
const seed = globalThis.crypto.getRandomValues(new Uint8Array(32));
127-
const tsk = new TransportSecretKey(seed);
126+
const tsk = TransportSecretKey.random();
128127
const encryptedVetkey = await this.canisterClient.get_encrypted_vetkey(
129128
keyOwner,
130129
arrayToByteBuf(vetkeyName),
@@ -143,7 +142,8 @@ export class KeyManager {
143142
...keyOwner.toUint8Array(),
144143
...vetkeyName,
145144
]);
146-
const encryptedDetkey = new EncryptedVetKey(encryptedKeyBytes);
145+
const encryptedDetkey =
146+
EncryptedVetKey.deserialize(encryptedKeyBytes);
147147
const vetkey = encryptedDetkey.decryptAndVerify(
148148
tsk,
149149
derivedPublicKey,

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ test("BLS signature verification", () => {
149149
});
150150

151151
test("protocol flow with precomputed data", () => {
152-
const tsk = new TransportSecretKey(
152+
const tsk = TransportSecretKey.deserialize(
153153
hexToBytes(
154154
"167b736e44a1c134bd46ca834220c75c186768612568ac264a01554c46633e76",
155155
),
@@ -170,7 +170,7 @@ test("protocol flow with precomputed data", () => {
170170
),
171171
);
172172

173-
const ek = new EncryptedVetKey(
173+
const ek = EncryptedVetKey.deserialize(
174174
hexToBytes(
175175
"b1a13757eaae15a3c8884fc1a3453f8a29b88984418e65f1bd21042ce1d6809b2f8a49f7326c1327f2a3921e8ff1d6c3adde2a801f1f88de98ccb40c62e366a279e7aec5875a0ce2f2a9f3e109d9cb193f0197eadb2c5f5568ee4d6a87e115910662e01e604087246be8b081fc6b8a06b4b0100ed1935d8c8d18d9f70d61718c5dba23a641487e72b3b25884eeede8feb3c71599bfbcebe60d29408795c85b4bdf19588c034d898e7fc513be8dbd04cac702a1672f5625f5833d063b05df7503",
176176
),

frontend/ic_vetkeys/src/utils/utils.ts

Lines changed: 56 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -25,29 +25,24 @@ export class TransportSecretKey {
2525
readonly #pk: G1Point;
2626

2727
/**
28-
* Construct a new TransportSecretKey from a bytestring
29-
*
30-
* The bytestring should be randomly generated by a cryptographically
31-
* secure random number generator.
28+
* Create a random transport secret key
29+
*/
30+
static random() {
31+
return new TransportSecretKey(bls12_381.utils.randomPrivateKey());
32+
}
33+
34+
/**
35+
* Deserialize TransportSecretKey from a bytestring
3236
*
33-
* For most applications, prefer using the static method random
37+
* The passed value would typically be a string previously returned
38+
* by calling serialize on a randomly-created TransportSecretKey.
3439
*/
35-
constructor(sk: Uint8Array) {
40+
static deserialize(sk: Uint8Array) {
3641
if (sk.length !== 32) {
3742
throw new Error("Invalid size for transport secret key");
3843
}
3944

40-
this.#sk = sk;
41-
42-
const pk = bls12_381.G1.ProjectivePoint.fromPrivateKey(this.#sk);
43-
this.#pk = pk;
44-
}
45-
46-
/**
47-
* Create a random transport secret key
48-
*/
49-
static random() {
50-
return new TransportSecretKey(bls12_381.utils.randomPrivateKey());
45+
return new TransportSecretKey(sk);
5146
}
5247

5348
/**
@@ -63,9 +58,18 @@ export class TransportSecretKey {
6358
*
6459
* Applications would not normally need to call this
6560
*/
66-
getSecretKey(): Uint8Array {
61+
serialize(): Uint8Array {
6762
return this.#sk;
6863
}
64+
65+
/**
66+
* @internal constructor
67+
*/
68+
private constructor(sk: Uint8Array) {
69+
this.#sk = sk;
70+
const pk = bls12_381.G1.ProjectivePoint.fromPrivateKey(this.#sk);
71+
this.#pk = pk;
72+
}
6973
}
7074

7175
/**
@@ -164,7 +168,7 @@ export class MasterPublicKey {
164168
/**
165169
* @internal constructor
166170
*/
167-
constructor(pk: G2Point) {
171+
private constructor(pk: G2Point) {
168172
this.#pk = pk;
169173
}
170174
}
@@ -244,6 +248,9 @@ export class DerivedPublicKey {
244248

245249
/**
246250
* @internal constructor
251+
*
252+
* This is public for typing reasons but there should be no need
253+
* for an application to call this.
247254
*/
248255
constructor(pk: G2Point) {
249256
this.#pk = pk;
@@ -392,6 +399,15 @@ export class VetKey {
392399
return this.#bytes;
393400
}
394401

402+
/**
403+
* Return the serialization of the VetKey
404+
*
405+
* This is the byte encoding of the unencrypted VetKey.
406+
*/
407+
serialize(): Uint8Array {
408+
return this.#bytes;
409+
}
410+
395411
/**
396412
* Derive a symmetric key of the requested length from the VetKey
397413
*
@@ -422,7 +438,7 @@ export class VetKey {
422438
/**
423439
* Deserialize a VetKey from the 48 byte encoding of the BLS signature
424440
*
425-
* This deserializes the same value as returned by signatureBytes
441+
* This deserializes the same value as returned by serialize (or signatureBytes)
426442
*/
427443
static deserialize(bytes: Uint8Array): VetKey {
428444
return new VetKey(bls12_381.G1.ProjectivePoint.fromHex(bytes));
@@ -458,10 +474,14 @@ export class DerivedKeyMaterial {
458474
/**
459475
* @internal constructor
460476
*/
461-
constructor(cryptokey: CryptoKey) {
477+
private constructor(cryptokey: CryptoKey) {
462478
this.#hkdf = cryptokey;
463479
}
464480

481+
static fromCryptoKey(cryptokey: CryptoKey): DerivedKeyMaterial {
482+
return new DerivedKeyMaterial(cryptokey);
483+
}
484+
465485
/**
466486
* @internal constructor
467487
*/
@@ -593,20 +613,21 @@ export class EncryptedVetKey {
593613
* Parse an encrypted key returned by the `vetkd_derive_encrypted_key`
594614
* managment canister interface
595615
*/
596-
constructor(bytes: Uint8Array) {
616+
static deserialize(bytes: Uint8Array): EncryptedVetKey {
597617
if (bytes.length !== G1_BYTES + G2_BYTES + G1_BYTES) {
598618
throw new Error("Invalid EncryptedVetKey serialization");
599619
}
600620

601-
this.#c1 = bls12_381.G1.ProjectivePoint.fromHex(
621+
const c1 = bls12_381.G1.ProjectivePoint.fromHex(
602622
bytes.subarray(0, G1_BYTES),
603623
);
604-
this.#c2 = bls12_381.G2.ProjectivePoint.fromHex(
624+
const c2 = bls12_381.G2.ProjectivePoint.fromHex(
605625
bytes.subarray(G1_BYTES, G1_BYTES + G2_BYTES),
606626
);
607-
this.#c3 = bls12_381.G1.ProjectivePoint.fromHex(
627+
const c3 = bls12_381.G1.ProjectivePoint.fromHex(
608628
bytes.subarray(G1_BYTES + G2_BYTES),
609629
);
630+
return new EncryptedVetKey(c1, c2, c3);
610631
}
611632

612633
/**
@@ -635,7 +656,7 @@ export class EncryptedVetKey {
635656
// Compute the purported vetKey k
636657
const k = this.#c3.subtract(
637658
this.#c1.multiply(
638-
bls12_381.G1.normPrivateKeyToScalar(tsk.getSecretKey()),
659+
bls12_381.G1.normPrivateKeyToScalar(tsk.serialize()),
639660
),
640661
);
641662

@@ -646,6 +667,15 @@ export class EncryptedVetKey {
646667
throw new Error("Invalid VetKey");
647668
}
648669
}
670+
671+
/**
672+
* @internal constructor
673+
*/
674+
private constructor(c1: G1Point, c2: G2Point, c3: G1Point) {
675+
this.#c1 = c1;
676+
this.#c2 = c2;
677+
this.#c3 = c3;
678+
}
649679
}
650680

651681
/* IBE (Identity Based Encryption) helper functions, not exported */

0 commit comments

Comments
 (0)