对于只有 X25519 密钥对可用但我需要使用我所拥有的创建和验证签名的情况,我正在尝试将 X25519 公钥转换为 Ed25519 验证密钥,该密钥可以成功验证使用 X25519 所做的签名私钥。
虽然普遍不赞成(我知道原因,但如上所述,X25519 密钥对是我所拥有的一切),但有一个野外实现可以做到这一点,可以 C 代码的形式。我正在尝试遵循这个例子。
由于周围的软件使用 dalek 包,我也使用这个库来进行转换。我的公钥转换函数如下:
pub(crate) fn x25519_pub_to_ed25519_ver(
public_key: [u8; 32],
) -> Result<VerifyingKey, SignatureError> {
match MontgomeryPoint(public_key).to_edwards(SIGN_BIT) {
None => Err(SignatureError::new()),
Some(key) => VerifyingKey::from_bytes(key.compress().as_bytes()),
}
}
这从蒙哥马利点计算爱德华兹点,我已经验证了to_edwards代码完全按照上面的参考代码linekd所做的事情。
对于签名方,我认为我需要确保使用能够产生正确符号的私钥,因为 X25519 和 Ed25519 公钥之间的转换不是 1:1。我的参考库还有一个如何生成匹配私钥的示例,我尝试模仿:
pub(crate) fn x25519_priv_to_ed25519_sig(private_key: &StaticSecret) -> SigningKey {
let clamped = clamp_integer(private_key.to_bytes());
let signing_key = SigningKey::from_bytes(&clamped);
let test_public_key = signing_key.verifying_key().to_bytes();
if (test_public_key[31] & 0x80) >> 7 == SIGN_BIT {
signing_key
} else {
let scalar = Scalar::from_bytes_mod_order(clamped);
let neg = scalar.invert();
SigningKey::from_bytes(&neg.to_bytes())
}
}
遗憾的是,我的测试代码不起作用,生成的公钥与直接从计算的 Ed25519 密钥导出的 Ed25519 验证密钥不匹配:
const TEST_MESSAGE: &[u8] = b"Das Pferd frisst keinen Gurkensalat.";
fn main() {
let private_key = StaticSecret::random();
println!("X25519 private key: {:02x?}", private_key.to_bytes());
let public_key = PublicKey::from(&private_key);
let signing_key = x25519_priv_to_ed25519_sig(&private_key);
println!("Ed25519 signing key: {:02x?}", signing_key.to_bytes());
let signature = signing_key.sign(TEST_MESSAGE);
let verifying_key = x25519_pub_to_ed25519_ver(public_key.to_bytes()).unwrap();
println!(
"Transformed verifying key: {:02x?}",
verifying_key.to_bytes()
);
println!(
"Real verifying key: {:02x?}",
signing_key.verifying_key().to_bytes()
);
let result = verifying_key.verify(TEST_MESSAGE, &signature);
assert!(result.is_ok());
}
我在哪一点进行了错误的转换?
可以在 Codeberg
上找到可以使用
cargo
运行的最小工作示例。
由
verifying_key()
生成的 ed25519_dalek
并不是直接从标量乘以曲线得出的点。这是因为在真实的 Ed25519 中用于签名的秘密密钥不是标量本身,而是通过秘密标量进行 SHA-512 密钥派生的结果,产生 32 字节作为新标量和 32 字节作为伪随机哈希输入签名。
为了使签名和验证部分独立密钥派生工作,需要不同的密钥派生算法,如 Signal 的 XEdDSA 规范所述。