edwards25519 curve表示为:
−
x
2
+
y
2
=
1
+
d
x
2
y
2
,
d
=
−
(
121665
/
121666
)
,
q
=
2
255
−
19
-x^2+y^2=1+dx^2y^2,d=-(121665/121666),q=2^{255}-19
−x2+y2=1+dx2y2,d=−(121665/121666),q=2255−19
⇒
x
2
=
(
y
2
−
1
)
/
(
d
y
2
+
1
)
(
m
o
d
q
)
\Rightarrow x^2=(y^2-1)/(dy^2+1)\ (mod\ q)
⇒x2=(y2−1)/(dy2+1) (mod q)
0. 为何edwards curve point可以压缩
根据论文《Twisted Edwards Curves Revisited》有:
可知
(
−
x
,
y
)
+
(
x
,
y
)
=
(
0
,
1
)
=
∞
(-x,y)+(x,y)=(0,1)=\infty
(−x,y)+(x,y)=(0,1)=∞
所以,
(
0
,
1
)
(0,1)
(0,1)不在
F
p
F_p
Fp subgroup中(其中,对于Curve25519,
p
=
8
q
,
q
=
2
252
+
27742317777372353535851937790883648493
p=8q,q=2^{252} + 27742317777372353535851937790883648493
p=8q,q=2252+27742317777372353535851937790883648493)。
因此:
(
−
x
,
y
)
和
(
x
,
y
)
(-x,y)和(x,y)
(−x,y)和(x,y)不可以同时在同一个subgroup中,于是可以直接取
y
y
y坐标和
x
x
x坐标的符号位来表示唯一点。
以上内容参考https://ethresear.ch/t/some-snarky-tricks-from-ethboston-zeropool-under-the-hood/6115:(dalek-Curve25519中的压缩算法采用是
(
−
x
,
y
)
和
(
x
,
y
)
(-x,y)和(x,y)
(−x,y)和(x,y),github.com/snjax/circomlib中采用的压缩算法是
(
x
,
y
)
和
(
x
,
−
y
)
(x,y)和(x,-y)
(x,y)和(x,−y))。
1. compression压缩算法
将edwards25519的point
(
x
,
y
)
(x,y)
(x,y),其中每个
x
,
y
x,y
x,y均可以little-endian数组[u8;32]表示,表示一个point 需要2个[u8;32]数组。
通过压缩算法,可用一个[u8;32]数组来表示point
(
x
,
y
)
(x,y)
(x,y)。具体的压缩算法为:
E
n
c
(
x
,
y
)
=
(
y
∣
x
<
<
255
)
Enc(x,y)=(y|x<<255)
Enc(x,y)=(y∣x<<255)
根据ed25519中约定可知, x < < 255 x<<255 x<<255即为 x x x的最低有效位,若为1代表的是负数,若为0则代表的是非负数。
通过压缩算法,可有效减少存储空间。
python脚本为:
def point_compress(P):
zinv = modp_inv(P[2])
x = P[0] * zinv % p
y = P[1] * zinv % p
return int.to_bytes(y | ((x & 1) << 255), 32, "little")
curve25519-dalek中的代码为:
/// Compress this point to `CompressedEdwardsY` format.
pub fn compress(&self) -> CompressedEdwardsY {
let recip = self.Z.invert();
let x = &self.X * &recip;
let y = &self.Y * &recip;
let mut s: [u8; 32];
s = y.to_bytes();
s[31] ^= x.is_negative().unwrap_u8() << 7;
CompressedEdwardsY(s)
}
2. decompression解压缩算法
对于收到的
e
=
E
n
c
(
x
,
y
)
=
(
y
∣
x
<
<
255
)
e=Enc(x,y)=(y|x<<255)
e=Enc(x,y)=(y∣x<<255),其中
e
e
e的最高位即为
x
0
x_0
x0值,有:
x
0
=
e
>
>
255
x_0=e>>255
x0=e>>255
y
=
e
&
(
2
255
−
1
)
y=e\&(2^{255}-1)
y=e&(2255−1)
由此可见 y y y值直接可知,需要根据公式 x 2 = ( y 2 − 1 ) / ( d y 2 + 1 ) ( m o d q ) x^2=(y^2-1)/(dy^2+1)\ (mod\ q) x2=(y2−1)/(dy2+1) (mod q)来恢复 x x x值。正常有两个根,根据 x 0 x_0 x0来选择正确的那个根。
根据博客有限域内的平方根求解原理解析及curve25519-dalek中的实现来求解即可。
python脚本为:
/// Attempt to decompress to an `EdwardsPoint`.
///
/// Returns `None` if the input is not the \\(y\\)-coordinate of a
/// curve point.
pub fn decompress(&self) -> Option<EdwardsPoint> {
let Y = FieldElement::from_bytes(self.as_bytes());
let Z = FieldElement::one();
let YY = Y.square();
let u = &YY - &Z; // u = y²-1
let v = &(&YY * &constants::EDWARDS_D) + &Z; // v = dy²+1
let (is_valid_y_coord, mut X) = FieldElement::sqrt_ratio_i(&u, &v);
if is_valid_y_coord.unwrap_u8() != 1u8 { return None; }
// Flip the sign of X if it's not correct
let compressed_sign_bit = Choice::from(self.as_bytes()[31] >> 7);
let current_sign_bit = X.is_negative();
X.conditional_negate(current_sign_bit ^ compressed_sign_bit);
Some(EdwardsPoint{ X: X, Y: Y, Z: Z, T: &X * &Y })
}
参考资料:
[1] Edwards-Curve Digital Signature Algorithm (EdDSA)
[2] ed25519
[3] 有限域内的平方根求解原理解析及curve25519-dalek中的实现
[4] Extended twisted Edwards curve坐标系
[6] https://ethresear.ch/t/some-snarky-tricks-from-ethboston-zeropool-under-the-hood/6115