pub[i][j] = h(seed, i, j) # i in {0,1}, j in {0, ..., 255}
总共生成 2 * 256 = 512 个公钥值
对于需要签名的 256 位消息摘要 01101110..., 按位检查每个比特位:
如果比特位为 0, 则使用 pub[0][j] 作为签名的一部分
如果比特位为 1, 则使用 pub[1][j] 作为签名的一部分
将所有 256 个公钥值作为签名输出
接收方使用公钥种子 seed 重新计算出所有 512 个公钥值
对于收到的签名, 按位检查每个比特位:
如果比特位为 0, 则检查对应的 pub[0][j] 是否与签名中的值匹配
如果比特位为 1, 则检查对应的 pub[1][j] 是否与签名中的值匹配
如果所有比特位的验证都通过, 则认为签名有效
这种方法可以扩展,只要尚未揭露私钥。我们可以生成很多组公钥用于签名。
如果要签名四次,那么一共要生成 64 KB 的公钥。 随着签名次数的增大,需要在本地存储线性级别增长的公钥。并且对于每个验证者,都需要向他出示之前生成过的所有公钥。
解决方法是 Merkle 树。
一种未在加密货币领域使用的对称加密算法。
支持盲签名(即在内容不揭露的情况下签名),过程如下:
$m' = m + r$ 对原始消息 添加一个随机数得到一个新的消息 。对消息进行随机化的方法,目的是为了提高签名的安全性。
$\mathrm{sig} (m') = s'$ 对修改后的消息进行签名,得到签名值 。签名算法可以是 RSA 签名、ECDSA 签名等。
$s = s' - r$ 通过减去之前添加的随机数 ,得到最终的签名值 。
这样做的目的是为了保证签名值 $s$ 与原始消息 $m$ 的签名一致,即 $\mathrm{sig}(m) = s$。
这个过程中,签名的人不会知道它签的原始消息是什么。
RSA 算法的 setup 步骤如下:
选择两个大素数 $p$ 和 $q$。
计算 $n = p \times q$。素数性质保证很难找到 $n$ 的 preimage。
计算 $\phi(n) = (p-1)(q-1)$, 其中 $\phi(n)$ 是 Euler 函数。
选择一个整数 $e$, 使得 $1 < e < \phi(n)$ 且 $\gcd(e, \phi(n)) = 1$。通常选择 $e = 65537$,或者 $3$。这个数可以公开。
计算 $d$ 使得 $d \times e \equiv 1 \pmod{\phi(n)}$。这可以通过扩展欧几里得算法来实现。或者写作 $d=e^{-1} \bmod (p-1) \times(q-1)$
此时,公钥为 $(e, n)$,私钥为 $(d, n)$. $p, q$ 可以丢掉。
在 RSA 算法中,签名和验证的过程如下:
使用私钥 $(d, n)$ 对消息 $m$ 进行签名:
s = m^d \mod n
其中 $s$ 就是签名结果。
使用公钥 $(e, n)$ 对签名 $s$ 进行验证:
m' = s^e \mod n
比较 $m'$ 和原始消息 $m$ 是否相等。如果相等,则说明签名有效,否则签名无效。
具体步骤如下:
发送者使用自己的私钥对消息 $m$ 进行签名,得到签名 $s$。
接收者使用发送者的公钥对签名 $s$ 进行验证,得到 $m'$。
如果 $m' = m$,则说明签名有效,消息未被篡改。
这样可以确保消息的完整性和来源的真实性。
Bitcoin 使用 $y^2=x^3+7$,在曲线上画一条割线,交于 $P,Q,-R$,满足 $P+Q-R=0$,则 $-R$ 的对称点满足 $R=P+Q$
实际计算时,计算机处理的是模运算,保持了一些性质,但曲线不会像上面这么好看。
椭圆曲线上的点运算
在椭圆曲线上,有三种基本的点运算:
点加法 (Point Addition):
给定两个不同的点 $P(x_1, y_1)$ 和 $Q(x_2, y_2)$,计算它们的和 $R = P + Q$。
计算方法是:
计算斜率 $\lambda = (y_2 - y_1) / (x_2 - x_1)$
计算 $x_R = \lambda^2 - x_1 - x_2$
计算 $y_R = \lambda(x_1 - x_R) - y_1$
点加法 ($P + Q$): 对于曲线上的两个点 $P$ 和 $Q$,作过这两点的直线与曲线的第三个交点,然后以这个交点关于 $x$ 轴对称的点作为 $P + Q$ 的结果。
点倍乘 ($k \cdot P$): 对于曲线上的一个点 $P$,作过 $P$ 的切线与曲线的第二个交点,然后以这个交点关于 $x$ 轴对称的点作为 $2 \cdot P$ 的结果。重复这一过程 $k$ 次,即可得到 $k \cdot P$ 的结果。
点相反 ($-P$): 对于曲线上的一个点 $P$,以 $P$ 关于 $x$ 轴对称的点作为 $-P$ 的结果。
椭圆曲线中点的加法满足交换律,即 $aB = bA$。
推导如下:由于加法满足交换律,所以有 $aB = a(bG) = (ab)G = b(aG) = bA$。因此,无论先将 $G$ 点乘以 $a$ 再乘以 $b$,还是先乘以 $b$ 再乘以 $a$,结果都是相同的。
随机生成一个大整数 $a$,作为用户的私钥。
一般选择一个 256 位的标量。
计算公钥 $A = a \cdot G$,其中 $G$ 是椭圆曲线 $y^2 = x^3 + 7$ 上的随机选取的基点(生成点,Generator Point)。
$x_{A},y_{A}$ 都是 32 bytes 整数,因此一共需要 64 bytes 空间存储公钥。
考虑到曲线是关于 $x$ 轴对称的,可以压缩到 33 bytes, 其中一个 byte 用来指示是在正半轴还是负半轴。
因此只需要判断 $R == sG + h(m,R)A$
所以验证等式其实就是签名等式乘以基点 $G$ 得到的。签名过程只有持有私钥 $a$ 的签名者才能完成,而任何人都可以用公钥 $A$ 进行验证。
考虑 $A=aG, B=bG, aB = bA = C$,$C$ 称为 Diffie Hellman 密钥交换点。我们可以用 $C$ 作为共享的公共密钥,在信道中不会传输 $C$,而是各自通过公开信息计算出来(比如对于 A 方而言,通过 $bA$ 来计算),因此可以加密内容并安全地交换。
在 ECC 中,可以将两个密钥组合成一个新的密钥(Key Combination)。
设 $G$ 为椭圆曲线上的一个生成元(Generator),$a$ 和 $b$ 为两个整数。我们可以计算出两个点:
\begin{aligned}
A &= aG \\
B &= bG
\end{aligned}
根据椭圆曲线上点的加法规则,我们可以将这两个点相加,得到一个新的点 $D$:
D = A + B = (a+b)G
这里的 $a+b$ 实际上是在有限域上进行模加法运算。
多方签名:假设 Alice 和 Bob 分别持有私钥 $a$ 和 $b$,他们可以各自计算出 $A=aG$ 和 $B=bG$,然后将这两个公钥相加得到组合公钥 $D$。当需要签名时,Alice 和 Bob 分别使用自己的私钥对消息进行签名,然后将两个签名相加,得到最终的组合签名。验证者可以使用组合公钥 $D$ 来验证这个签名的有效性。
密钥托管:用户可以将自己的私钥分成两部分 $a$ 和 $b$,分别托管给两个不同的机构。当需要使用私钥时,用户可以从两个机构分别获得 $aG$ 和 $bG$,然后自己相加得到完整的公钥 $D$。这样可以提高私钥的安全性,防止单点失败。
1. Signatures, Hashing, Hash Chains, e-cash, and Motivation - YouTube