2021SC@SDUSC
在加密通信过程中可以使用Diffie Hellman密钥交换来进行密钥分发,经过密钥交换之后相当于通信双方都有了相同的密钥,可以使用对称加密的方式通信。在区块链比特币中同样有密钥交换,即Elliptic Curve Diffie Hellman,实现将比特币发送到接收方的地址。下文中将介绍相关背景知识以及对应代码实现。
ECDH地址
本部分内容摘自维基百科,略有改动。
ECDH 地址也称为隐形地址、可重复使用的支付代码、可重复使用的地址或paynyms。椭圆曲线 Diffie-Hellman (ECDH) 是一种密钥协商协议,它允许两方通过不安全的通道建立共享密钥。例如,Alice 和 Bob 可以在他们之间交流密码信息并就共享秘密达成一致,窃听者 Eve 可以看到所有他们的消息,但仍然无法计算共享密钥。(相应数学背景知识参见libsecp256k1比特币密码算法开源库(五))
通过让比特币的接收者发布一些发送者可以用来计算共享密钥的 ECDH 信息,可以在比特币context中使用该概念。这个共享的密钥成为发送者汇款到的比特币地址。接收方可以计算出相应的私钥以获取资金。
工作过程:
接收方本地生成地址私钥
q
q
q: q = ECDH address privkey (generated by receiver) 接收方本地生成地址公钥
Q
Q
Q: Q = q·G = ECDH address pubkey (published by receiver)
发送方本地生成nonce串
p
p
p: p = nonce (generated by sender) 发送方根据nonce串
p
p
p生成公钥
P
P
P,发送给接收方: P = p·G = nonce point (sent from sender to receiver)
发送方接收方生成共享密钥,且该共享密钥只有这双方知道: p·Q = q.·P = p·q·G (ECDH shared secret point, known by sender and receiver but not eavesdroppers) 由于共享密钥是一个坐标点,因此经过哈希,将共享秘密点转换为标量c: c = H(p·Q) = H(q·P) (tweak, shared secret point converted to number)
发送者使用接收者公钥和共享密钥生成的标量c计算比特币接收地址
Q
′
Q'
Q′,并把币送到该地址: Q’ = Q + c·G (bitcoin pubkey, send coins here)
接收者计算自己的比特币接收地址
Q
′
Q'
Q′: Q’ = Q + c.G = (q + c)·G (bitcoin pubkey, watch for incoming coins) 相应的私钥
q
′
q'
q′用来接下来消费比特币: q’ = q + c (bitcoin privkey, for spending incoming coins)
共享密钥ECDH
在impl<D: Digest + Default> SharedSecret<D> 中定义两个函数new_with_context 和new ,其中函数new 中也是直接调用函数new_with_context 。在new_with_context 中共享密钥的实现过程通过context.ecdh_raw::<D>(&pubkey.0, &seckey.0) 来实现。
impl<D: Digest + Default> SharedSecret<D> {
pub fn new_with_context(
pubkey: &PublicKey,
seckey: &SecretKey,
context: &ECMultContext,
) -> Result<SharedSecret<D>, Error> {
let inner = match context.ecdh_raw::<D>(&pubkey.0, &seckey.0) {
Some(val) => val,
None => return Err(Error::InvalidSecretKey),
};
Ok(SharedSecret(inner))
}
#[cfg(any(feature = "static-context", feature = "lazy-static-context"))]
pub fn new(pubkey: &PublicKey, seckey: &SecretKey) -> Result<SharedSecret<D>, Error> {
Self::new_with_context(pubkey, seckey, &ECMULT_CONTEXT)
}
}
找到函数ecdh_raw ,可以看出生成共享密钥的函数为ecmult_const(&mut res, &pt, &s)
impl ECMultContext {
pub fn ecdh_raw<D: Digest + Default>(
&self,
point: &Affine,
scalar: &Scalar,
) -> Option<GenericArray<u8, D::OutputSize>> {
let mut digest: D = Default::default();
let mut pt = *point;
let s = *scalar;
if s.is_zero() {
return None;
}
let mut res = Jacobian::default();
self.ecmult_const(&mut res, &pt, &s);
pt.set_gej(&res);
pt.x.normalize();
pt.y.normalize();
let x = pt.x.b32();
let y = 0x02 | (if pt.y.is_odd() { 1 } else { 0 });
digest.update(&[y]);
digest.update(&x);
Some(digest.finalize_reset())
}
}
生成共享密钥的函数ecmult_const 的代码如下。
pub fn ecmult_const(&self, r: &mut Jacobian, a: &Affine, scalar: &Scalar) {
const WNAF_SIZE: usize = (WNAF_BITS + (WINDOW_A - 1) - 1) / (WINDOW_A - 1);
let mut tmpa = Affine::default();
let mut pre_a: [Affine; ECMULT_TABLE_SIZE_A] = Default::default();
let mut z = Field::default();
let mut wnaf_1 = [0i32; 1 + WNAF_SIZE];
let sc = *scalar;
let skew_1 = ecmult_wnaf_const(&mut wnaf_1, &sc, WINDOW_A - 1);
r.set_ge(a);
odd_multiples_table_globalz_windowa(&mut pre_a, &mut z, r);
for i in 0..ECMULT_TABLE_SIZE_A {
pre_a[i].y.normalize_weak();
}
let i = wnaf_1[WNAF_SIZE];
debug_assert!(i != 0);
table_get_ge_const(&mut tmpa, &pre_a, i, WINDOW_A);
r.set_ge(&tmpa);
for i in (0..WNAF_SIZE).rev() {
for _ in 0..(WINDOW_A - 1) {
let r2 = *r;
r.double_nonzero_in_place(&r2, None);
}
let n = wnaf_1[i];
table_get_ge_const(&mut tmpa, &pre_a, n, WINDOW_A);
debug_assert!(n != 0);
*r = r.add_ge(&tmpa);
}
r.z *= &z;
let mut correction = *a;
let mut correction_1_stor: AffineStorage;
let a2_stor: AffineStorage;
let mut tmpj = Jacobian::default();
tmpj.set_ge(&correction);
tmpj = tmpj.double_var(None);
correction.set_gej(&tmpj);
correction_1_stor = (*a).into();
a2_stor = correction.into();
correction_1_stor.cmov(&a2_stor, skew_1 == 2);
correction = correction_1_stor.into();
correction = correction.neg();
*r = r.add_ge(&correction)
}
函数set_gej 实现对给定的Jacobian坐标下的点坐标,找到其在仿射坐标中的对应点:
pub fn set_gej(&mut self, a: &Jacobian) {
self.infinity = a.infinity;
let mut a = *a;
a.z = a.z.inv();
let z2 = a.z.sqr();
let z3 = a.z * z2;
a.x *= z2;
a.y *= z3;
a.z.set_int(1);
self.x = a.x;
self.y = a.y;
}
在impl<D: Digest> AsRef<[u8]> for SharedSecret<D> 中定义函数as_ref 。
impl<D: Digest> AsRef<[u8]> for SharedSecret<D> {
fn as_ref(&self) -> &[u8] {
&self.0.as_ref()
}
}
未完待续
|