Skip to content

Commit dc4adb3

Browse files
author
Lucas Soriano
authored
Merge pull request #1 from LLFourn/master
Implement HSM_CL
2 parents 55eb081 + cf15f9a commit dc4adb3

File tree

4 files changed

+157
-0
lines changed

4 files changed

+157
-0
lines changed

Cargo.toml

+10
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,16 @@ rand = "0.7.3"
1818
hex = "0.4.2"
1919
sha2 = "0.8"
2020

21+
[dependencies.class_group]
22+
git = "http://github.com/LLFourn/class"
23+
rev = "7288388"
24+
25+
26+
[dependencies.curv]
27+
git = "https://github.com/KZen-networks/curv"
28+
features = ["ec_secp256k1"]
29+
tag = "v0.2.3"
30+
2131
[dev-dependencies]
2232
proptest = "0.9"
2333
testcontainers = "0.9"

src/hsm_cl.rs

+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
use class_group::primitives::cl_dl_lcm::{CLDLProofPublicSetup, Witness, HSMCL, PK};
2+
use curv::arithmetic::traits::Converter;
3+
use curv::arithmetic::traits::Samplable;
4+
use curv::elliptic::curves::traits::ECPoint;
5+
use curv::elliptic::curves::traits::ECScalar;
6+
use curv::BigInt;
7+
use curv::FE;
8+
use curv::GE;
9+
10+
pub use class_group::primitives::cl_dl_lcm::Ciphertext;
11+
pub type PublicKey = PK;
12+
13+
pub type Proof = CLDLProofPublicSetup;
14+
15+
#[derive(Clone, Debug)]
16+
pub struct KeyPair(HSMCL);
17+
18+
impl KeyPair {
19+
pub fn public_key(&self) -> &PublicKey {
20+
&self.0.pk
21+
}
22+
23+
pub fn gen(public_setup: &BigInt) -> Self {
24+
Self(HSMCL::keygen_with_setup(&FE::q(), &1348, &public_setup))
25+
}
26+
}
27+
28+
pub fn encrypt(public_key: &PublicKey, message: &crate::secp256k1::KeyPair) -> (Ciphertext, Proof) {
29+
let r = BigInt::sample_below(&(&public_key.stilde * BigInt::from(2).pow(40)));
30+
let x = BigInt::from(message.secret_key().serialize().as_ref());
31+
let ciphertext = HSMCL::encrypt_predefined_randomness(&public_key, &x, &r);
32+
let pk_untagged_bytes = &message.public_key().serialize()[1..];
33+
let X = GE::from_bytes(pk_untagged_bytes).unwrap();
34+
35+
let proof = CLDLProofPublicSetup::prove(Witness { x: &x, r }, public_key, &ciphertext, &X);
36+
37+
(ciphertext, proof)
38+
}
39+
40+
#[must_use]
41+
pub fn verify(
42+
pk: &PublicKey,
43+
ciphertext: &Ciphertext,
44+
encrypts: &crate::secp256k1::PublicKey,
45+
proof: &Proof,
46+
public_setup: &BigInt,
47+
) -> bool {
48+
let pk_untagged_bytes = &encrypts.serialize()[1..];
49+
let encrypts = GE::from_bytes(pk_untagged_bytes).unwrap();
50+
proof
51+
.verify(pk, ciphertext, &encrypts, &public_setup)
52+
.is_ok()
53+
}
54+
55+
pub fn decrypt(keypair: &KeyPair, ciphertext: Ciphertext) -> secp256k1::curve::Scalar {
56+
let bytes = BigInt::to_vec(&keypair.0.decrypt(&ciphertext));
57+
58+
// Note, if this isn't true then the problem should be solved at a lower level :^)
59+
debug_assert!(
60+
bytes.len() <= 32,
61+
"decrypted value must < 32 because it was derived from discrete log of something of order q"
62+
);
63+
64+
let mut bytes_32 = [0u8; 32];
65+
// copy into the least significant bytes
66+
bytes_32[32 - bytes.len()..].copy_from_slice(&bytes[..]);
67+
68+
let mut scalar = secp256k1::curve::Scalar::default();
69+
let overflow: bool = scalar.set_b32(&bytes_32).into();
70+
debug_assert!(
71+
!overflow,
72+
"this shouldn't overflow for the same reason as above"
73+
);
74+
scalar
75+
}
76+
77+
pub fn multiply(ciphertext: &Ciphertext, sk: &secp256k1::SecretKey) -> Ciphertext {
78+
HSMCL::eval_scal(&ciphertext, &BigInt::from(&sk.serialize()[..]))
79+
}
80+
81+
#[cfg(test)]
82+
mod test {
83+
use super::*;
84+
use secp256k1::curve::Scalar;
85+
86+
#[test]
87+
fn end_to_end() {
88+
let public_setup = BigInt::from(b"A2L-PoC".as_ref());
89+
let kp = KeyPair::gen(&public_setup);
90+
let public_key = kp.public_key();
91+
let msg = crate::secp256k1::KeyPair::random(&mut rand::thread_rng());
92+
93+
let (ciphertext, proof) = encrypt(public_key, &msg);
94+
95+
assert!(verify(
96+
public_key,
97+
&ciphertext,
98+
msg.public_key(),
99+
&proof,
100+
&public_setup
101+
));
102+
103+
assert_eq!(
104+
decrypt(&kp, ciphertext.clone()),
105+
msg.secret_key().clone().into(),
106+
"decryption yields original encrypted message"
107+
);
108+
109+
let blinding = crate::secp256k1::KeyPair::random(&mut rand::thread_rng());
110+
111+
let blinded_ciphertext = multiply(&ciphertext, blinding.secret_key());
112+
113+
assert_ne!(
114+
blinded_ciphertext, ciphertext,
115+
"ciphertexts should not be equal after mutation"
116+
);
117+
118+
assert!(
119+
!verify(
120+
public_key,
121+
&blinded_ciphertext,
122+
msg.public_key(),
123+
&proof,
124+
&public_setup
125+
),
126+
"proof should not longer work on mutated ciphertext"
127+
);
128+
129+
let decrypted_blinded = decrypt(&kp, blinded_ciphertext);
130+
131+
assert_eq!(
132+
decrypted_blinded,
133+
Into::<Scalar>::into(blinding.secret_key().clone())
134+
* Into::<Scalar>::into(msg.secret_key().clone()),
135+
"cipthertext multiplication produced same result as scalar multiplication"
136+
)
137+
}
138+
}

src/lib.rs

100644100755
+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
pub mod bitcoin;
44
mod dleq;
55
pub mod dummy_hsm_cl;
6+
pub mod hsm_cl;
67
pub mod puzzle_promise;
78
pub mod puzzle_solver;
89
pub mod secp256k1;

src/secp256k1/keypair.rs

+8
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ impl KeyPair {
3434
pub fn into_sk(self) -> SecretKey {
3535
self.sk
3636
}
37+
38+
pub fn secret_key(&self) -> &SecretKey {
39+
&self.sk
40+
}
41+
42+
pub fn public_key(&self) -> &PublicKey {
43+
&self.pk
44+
}
3745
}
3846

3947
impl From<SecretKey> for KeyPair {

0 commit comments

Comments
 (0)