Skip to content

Commit

Permalink
chore(crypto): CRP-2682 Change key derivation used in VetKD to BLS12-…
Browse files Browse the repository at this point in the history
…381 hash_to_scalar (#3736)

The approach originally used for the vetKD prototype for key derivation
used SHAKE, largely as an artifact that we implemented an IBE demo as
part of the same prototype, and SHAKE happened to be convenient for
implementing IBE. However for production use of vetKD we will switch to
using `hash_to_scalar`, a variant of RFC 9380's `hash_to_field`, since
we already depend on the security of `hash_to_field` for implementing
the hash to curve operation, upon which the security of vetKD depends.
Thus we slightly reduce the number of cryptographic primitives vetKD
depends on for its security.
  • Loading branch information
randombit authored Feb 13, 2025
1 parent 483f053 commit 2fd87b6
Show file tree
Hide file tree
Showing 9 changed files with 78 additions and 82 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 13 additions & 1 deletion rs/crypto/internal/crypto_lib/bls12_381/type/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub type NodeIndex = u32;
#[cfg(test)]
mod tests;

use ic_bls12_381::hash_to_curve::{ExpandMsgXmd, HashToCurve};
use ic_bls12_381::hash_to_curve::{ExpandMsgXmd, HashToCurve, HashToField};
use itertools::multiunzip;
use pairing::group::{ff::Field, Group};
use paste::paste;
Expand Down Expand Up @@ -234,6 +234,18 @@ impl Scalar {
Self::new(ic_bls12_381::Scalar::one())
}

/// Hash to scalar
///
/// Uses the same mechanism as RFC 9380's hash_to_field except
/// targeting the scalar group.
pub fn hash(domain_sep: &[u8], input: &[u8]) -> Self {
let mut s = [ic_bls12_381::Scalar::zero()];
<ic_bls12_381::Scalar as HashToField>::hash_to_field::<ExpandMsgXmd<sha2::Sha256>>(
input, domain_sep, &mut s,
);
Self::new(s[0])
}

/// Return true iff this value is zero
pub fn is_zero(&self) -> bool {
bool::from(self.value.is_zero())
Expand Down
33 changes: 33 additions & 0 deletions rs/crypto/internal/crypto_lib/bls12_381/type/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1332,6 +1332,39 @@ fn test_verify_bls_signature_batch_with_same_pk() {
}
}

#[test]
fn test_hash_to_scalar_matches_known_values() {
// I was not able to locate any official test vectors for BLS12-381 hash_to_scalar
// so these were just generated using ic_bls12_381 itself.

let dst = b"QUUX-V01-CS02-with-BLS12381SCALAR_XMD:SHA-256_SSWU_RO_";

scalar_test_encoding(
Scalar::hash(&dst[..], b""),
"3b3fdf74b194c0a0f683d67a312a4e72d663d74b8478dc7b56be41e0ce11caa1",
);

scalar_test_encoding(
Scalar::hash(&dst[..], b"abc"),
"47e7a8839695a3df27f202cf71e295a8554b47cef75c1e316b1865317720e188",
);

scalar_test_encoding(
Scalar::hash(&dst[..], b"abcdef0123456789"),
"3dff572f262e702f2ee8fb79b70e3225f5ee543a389eea2e58eec7b2bfd6afeb",
);

scalar_test_encoding(
Scalar::hash(&dst[..], format!("q128_{}", "q".repeat(128)).as_bytes()),
"2874c0e7814fcf42a5f63258417d4be8ea0465ff7352691493d0eca2dd5a9729",
);

scalar_test_encoding(
Scalar::hash(&dst[..], format!("a512_{}", "a".repeat(512)).as_bytes()),
"3cf6864b1a81fba0798c370f6daf9c23a838f9dbb96ea3a3a1145899ddf259b4",
);
}

#[test]
fn test_hash_to_g1_matches_draft() {
/*
Expand Down
1 change: 0 additions & 1 deletion rs/crypto/internal/crypto_lib/bls12_381/vetkd/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ package(default_visibility = ["//rs/crypto:__subpackages__"])

DEPENDENCIES = [
# Keep sorted.
"//packages/ic-sha3",
"//rs/crypto/internal/crypto_lib/bls12_381/type",
"@crate_index//:rand",
]
Expand Down
1 change: 0 additions & 1 deletion rs/crypto/internal/crypto_lib/bls12_381/vetkd/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ documentation.workspace = true

[dependencies]
ic-crypto-internal-bls12-381-type = { path = "../type" }
ic-sha3 = { path = "../../../../../../packages/ic-sha3" }
rand = { workspace = true }

[dev-dependencies]
Expand Down
20 changes: 13 additions & 7 deletions rs/crypto/internal/crypto_lib/bls12_381/vetkd/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ use ic_crypto_internal_bls12_381_type::{G2Prepared, Gt, LagrangeCoefficients};

use rand::{CryptoRng, RngCore};

mod ro;

/// The index of a node
pub type NodeIndex = u32;

Expand All @@ -21,18 +19,26 @@ pub struct DerivationPath {
delta: Scalar,
}

const DERIVATION_PATH_DOMAIN_SEP: &[u8; 44] = b"ic-crypto-vetkd-bls12-381-g2-derivation-path";

impl DerivationPath {
/// Create a new derivation path
pub fn new<U: AsRef<[u8]>>(canister_id: &[u8], extra_paths: &[U]) -> Self {
let mut ro = ro::RandomOracle::new("ic-crypto-vetkd-bls12-381-derivation-path");
let mut combined_inputs = vec![];

ro.update_bin(canister_id);
// Each input is prefixed with an 8 byte length field
let len = canister_id.len() as u64;
combined_inputs.extend_from_slice(&len.to_be_bytes());
combined_inputs.extend_from_slice(canister_id);

for path in extra_paths {
ro.update_bin(path.as_ref());
for input in extra_paths {
let len = input.as_ref().len() as u64;
combined_inputs.extend_from_slice(&len.to_be_bytes()); // 8 bytes length
combined_inputs.extend_from_slice(input.as_ref());
}

let delta = ro.finalize_to_scalar();
let delta = Scalar::hash(DERIVATION_PATH_DOMAIN_SEP, &combined_inputs);

Self { delta }
}

Expand Down
43 changes: 0 additions & 43 deletions rs/crypto/internal/crypto_lib/bls12_381/vetkd/src/ro.rs

This file was deleted.

41 changes: 16 additions & 25 deletions rs/crypto/internal/crypto_lib/bls12_381/vetkd/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,15 +110,6 @@ fn random_derivation_path<R: RngCore + CryptoRng>(rng: &mut R) -> DerivationPath
DerivationPath::new(&canister_id, &extra_paths)
}

fn shake256(bytes: &[u8]) -> [u8; 32] {
let mut output = [0u8; 32];
let mut shake = ic_sha3::Shake256::new();
shake.update(bytes);
let mut xof_reader = shake.finalize_xof();
xof_reader.read(&mut output);
output
}

#[test]
fn encrypted_key_share_creation_is_stable() {
let mut rng = rand_chacha::ChaCha20Rng::seed_from_u64(1);
Expand All @@ -136,27 +127,27 @@ fn encrypted_key_share_creation_is_stable() {
let derivation_path = random_derivation_path(&mut rng);
let did = rng.gen::<[u8; 28]>();

const EXPECTED_EKS_HASH: [&str; NODES] = [
"cb3075ecd2c5cd946dcfaf8486031cf7bbca656092aada97644307c598af5073",
"fbfaa432bd0fc9c60b456742368034d35d5fefceae1cdd027c27147ecc7f012c",
"7d6bd8ef201443fe5b570b62d244fc8192819f53783a1ae81e3fe747a793decd",
"12f894d3ddce41e5fa0cb1cd05e77d50ed214248cba809a9fe6fccf5515f5c13",
"d187e7bf4defab92ce9c82c692a9b3c3de65994cd4bf422438cbc515a8a48501",
"9aa47ad2dcf06a6a308152d935698684799714c772dfaf2b6654e6642a11937b",
"d96c4f897ba7e7ac657d363171b324adbad2faf13ef395bd0efa28a60273583d",
"fc3b2d0eae4c7d96212058d815d48701267a673c20197a08f28762301043405d",
"85ba21972c3a7717922d6bc734217d5dae12be7df42ffb93604e0ae7228ac9ed",
"3f7b6c16d35118519c92d31e074633f00fe30b6e4b522cda47d81088dc4011bb",
"658383824de7771db64a87b884ffbefe92567baeb851229785d9f3717c91bdda",
"e7a69b641278a5a8084938d1c03de2850a6639a882713a190fe0417f678e118e",
"d59283b0a48181d93c6e1a22e5ff9130df68a01bcadf96a25d37a2a092cb1a1a",
const EXPECTED_EKS_VALUE: [&str; NODES] = [
"a9940d0137c71275b4d11bf9a80bcf484a93e777a20734b95dd57e835ac484661f4056bbd4fa3ed218f50d967617c2b781516e7bd2baace117b77803686953aaebba83589a10a15b7a139da6dc3f1093561ccce5ded1ad7bfed4e35a198a0a0e14936d6a37ebba930bfae338185ac104c4ef67f899c0cc25467f80f09a886574dbc0642a6ed1a2516d4f7c90c9128610ae6d24d5bea60cdc939f5ebe2a98ce274cbb57003db162bac370a255eeab6ba1ff639d19fe97d61045a671162b15fe60",
"b9d2a21b758fc961c8a98dc7aa5a5597bcbc365134d3da90527a6b60a30831803afd52fc5e0163c5fd43154c0d2177f49317ddf48515edd84ccf2ecd8724f808b24ca44f10a684cdc4eb56f23c2f2a5152c44cbb59a0c70155aecda3f95710770582abdf6febb54b078a90b96af6fb1b27073dd10cd5366e1e6ce378d882b7251f361d4979388d0a8efe288811adb3b5a24f833bdb6fa7dda962970410ab4364c2a837781176187fa62ff51841f81a8f6bbea3e7b171ebb93d4ce11bd9be7b4d",
"811d9cd064cd7c6ab4a448c68b8b5fc8a5f08cd17c78d46469e810e8df3d1f8d3f1431ccf627e2198b72d850eb99b2e2b2dfe2c43ddf86e5403ba474a3a0ba596ddabecfe303eddfd70444057baaae733f92eca70509425f87633ce9d78a28040b559446e22f73d637b04c6eca6ecff71fb3d1a662d86ef22fa6c3ef8157c582e4afdc7a0047d4535c6e01e9c65eae7890de3f7362e9d027f847484e5e3a2734c57f02030c44425012f17277c825d8093bef8862bd69753c7ac0d80b45c4257c",
"aca469bb1dd96297c86044e6032c15fbf2a2c380764085be21d50cff4b9f52f5e32e8c7fd718d64b435334b4aa9f8016a4dbcedc7da27a8b23d4b254850a26e75cb7e744585f0bcf5fb4b7ffb7befa0a983cbff9de8d22be23f6674a32fd058a03f31140af7af5934a23ef4494fe1bd2ff3947dae35f4bc653388878fdea0a451d7bdd1ecaabec217faa1efcf24dd5a68aaae8ce07882503721cbbb0a6afed9bdfe611b6e6f70cf1f36300d296d97c284bdc1c06f2fad6c3b9b3f2bed39f6f6d",
"abc9b461b441ce17b4d581f5a82f0f21dfb0091c9c097e2f4ce7962adf7c14d14d86c9765dc2e296c66a02187699fbe396767f7fdbe594a787368bb59ef5d43b70bd0c7a39e07b27107cdc7fb06de7da15a2a33cd10593b65764c98a7591726a0f603451396769fdaf5bd3b69ff0b7f515b4fd1dd0629b3762bc3916b8c2e01d2bfd5ddebc9f638191cc919da5a5d619864bc64eaa58a9af0a48487c12040619f1d45e5c42959b7cc89e036caeb207e9e33def185af6b4f11c06ba9d7505b800",
"92ef0d8f00e18f23879783c51c72b9706c7ad0df2a2ba44d651c5c3d903431ca49732637a579eb8c423e2b79faf9fb0b95fb96958149013e2932286ccf93bcbb94386df13e0519927b231a39f9a40b82bcd7fea94490e574493fa38a7a809a2211f4f2296639157bfe932518a5b4d8ccee3ac4997dce11aab4ff31633f715c1b1c3a6cc3a9ee14b879d0c890721d0e3f833c9383b01a183c11076f08c6e70fa1d4e14189878614ba5c49631f013f2b680ec56d582bf1ca5ac17a96bee0c80925",
"8efef76050abe03984f8b22b1cd6075a89e361c8f70bd2b493185d710ff284cca2d32c41d05735f5d9f39a4ea9b5e2cb8b4b207ec0d535c835768f95a0a1e13018b185b77f6e506fe85c2014989113b2c59935f2e323db5913bdddff8b56aa5a000a6f4e4c409a9ead3945979e3914e903b75bf754587ffbcead720960dfbcb6407d193acc91734cf61ef47d8b8684c59575f5590e3a5cc065f065f9adf4e30cbf6fe3f2469eeacb0c8baae349f86f1c0d0ac6dafb600c7a8feb05cbd8490fb5",
"80ac9ea725ec6101b18e316e7f8ff9cdbf7619549b5b9cea767203deb4b0e0496c1c84e828e74268d2368937e7f9a34195b3697321df62a084bcc2cc2bf079ce2638625bbf243d906130a700908ce481dbbe1d32b014e89baef97cc10aa6f914092a904efd5d904a1ed3687361a186126858f14bc84cdd00cc0c4671313874f1ebedf1bddc3bb3d0484e23dfc754a6768b55ea732ff9c02a374c8e1fef793c01b37c464099a2863cb4c15469c4d867083db6924fb4cceda762d10a01777cdc97",
"92b3a643f608fd4ccbb7644cbb1b38ac39a6cb4461846c6cf79c3610a411fd4e8709b960260966586f67e2e0d6cf6c5b85b45c442c3f78bda0666389631a8a8c8b1c642eaa4c0214781c6ada600fd1a7663b43b844a6f7a96741315674090b7f10534d4789de9fc1829b7a97f30dedd86b8f801ac01c1f8a8e1fb247cec9f726dabe43a6f246e7248b8a6675370c47318a3c4788b4eb5092b283f7f81f6a5903c35f024159b8fc5bd2a01a4dc118b1855bcbd409abe6d54bc3daaf795694a580",
"807e2fa6fe3e4bec12953acc2b57bf4cd49614cfbccb393bbaa4a04d614f1b85a7bbae48ed5c147e05786a7a29494c2092cfd5bd41d088213fa676abd137ad6e4511ff65811c7622068d995c1d38e36f5a90284c7a5769510c1e71fb46bace121742873aa75b1956f331d13e7541fdd862d617965ed541376598a541bf2bc47e370f728e86c909b943b1db0cf6e38dba8b4ef50a2cb42410b5ccd9c2788a49df08585284c4dadeb7ff93befe0beb46ca998041ece1dd36832e5e44783f0e6005",
"b90e01a902b1af0a9039d32b1325c030424791d2a7c72dbb68cf17c962b80e2897b29bccf1bda9587f62d019098938b1b68aaf0628576dd28569de2bbc705ac51ae9e69552e912894b47d87d3af588466ad30316de574dd7663129ee342c949f101501911075cb27af2255635e9ea68401ccda1edf127ec3472d1018b0e1a012d352849ef40b835ab5357785798bbd15a90db5211cc61dd44b1660a6a2d68deb708d772b8b32fa29e9c5890ffd812709913c0488c159fe44bb8582677d171950",
"b653a21c480fccfb9b69419240cdb530e18f23b8a2bc5296073e8bea44f49104172158f2e2deec0cc1f6ed44d667bab5ab0cabfb9bd23b775fc903c2d2ddb3874c522c54e3465ed7ebbb8cec4f3e0bbca5537c8e4792a4bd0ce0e81196c49e14046ea5482f08f760d3be6b524a86f81771934cc91e1f6ed5aafc38f236ae74eba2d3d21458c0d19907b762a8fa2b860dac60873dda48bca15a4363b1271354e6623438ca5075e19a6f967c4912d8ce193e3f1f9de3f20c26a4d3bc2f7041b319",
"8489576ac848031c6e90ad9e0477fc72e766ccf00894db8ae9536415d817a4478726e4e1247ffa8656209a23ca261fa088312490c4d7b4f029f9b944196e4e3f1eb52c6a89e1ba9fc87f42c09ed3ae1490d10cdbda013863c775f00f3d070de413af98b3b688a397ab4124bc316876a4c015059607040c9faed27191db1e875e072c923fb4575f771cdb70f4ef0fbd7390b77f611ca8840b31d5d353a0039b011fc798ba380c99ed00ab4a3fedba0ded56dd630655431784d1a02767f44bfa85",
];

for (node_idx, expected_eks_hash) in EXPECTED_EKS_HASH.iter().enumerate() {
for (node_idx, expected_eks) in EXPECTED_EKS_VALUE.iter().enumerate() {
let node_sk = poly.evaluate_at(&Scalar::from_node_index(node_idx as u32));
let eks =
EncryptedKeyShare::create(&mut rng, &master_pk, &node_sk, &tpk, &derivation_path, &did);
assert_eq!(hex::encode(shake256(&eks.serialize())), *expected_eks_hash,);
assert_eq!(*expected_eks, hex::encode(eks.serialize()));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ fn should_correctly_create_encrypted_vetkd_key_share_for_smoke_test_vector() {
f3e68e13f62604d027660883213c90ea72810bcecee58b883fb62118e538243\
03718e6876ea400d083beb0439d3934122a4c4b2e58e3f145305b9a0c0a00e3\
2dd808574dec2605dbc7f122fe593ca0c07ca92720d0f17b7d53c9c68dbb93d\
489078859e5e5fe2b6612ac9536fe7f8b463cac948e6db97908b7f5a67b33b3\
e60a1c160889ef49448519a84be1aba0611829a7cca180cbbdf94f7b2cda2db\
6b14c65",
489078859e5e5fe2b6612ac9536fe7f8b463cafcc003efe88f1cddd26499322\
9229edb5ec96ea3fd2d083b815776b6cc7e03586d864374ba54d44c09201db8\
d33dd44",
)
.expect("invalid test vector")
))
Expand Down

0 comments on commit 2fd87b6

Please sign in to comment.