RustでECDSA署名を行う
はじめに
ECDSA(Elliptic Curve Digital Signature Algorithm)は楕円曲線暗号を用いたデジタル署名アルゴリズムです。また、secp256k1はECDSAで使用されるパラメータを決めたもので、Bitcoin、Ethereum、Internet Computerなどブロックチェーンでも使用されています。
楕円曲線は一般に
※
が使われます。
ECDSAやsecp256k1に関する詳細については触れませんが、RustからECDSA署名を行う方法について解説します。
すでに信頼できるライブラリが公開されていますので、自前では実装せずライブラリを利用します。
プロジェクトの作成
$ cargo new study
$ cd study
依存パッケージの追加
secp256k1を扱うk256を追加します。
また。本サンプルではHEX文字列リテラルをu8配列に変換するためにhex-literalを使うことにします。
$ cargo add hex-literal k256
パッケージを追加したした時点で、Cargo.tomlは概ね以下のような内容になるかと思います。
[package]
name = "study"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
hex-literal = "0.4.1"
k256 = "0.13.3"
※2024年5月6日時点の最新バージョンです
src/main.rsの編集
use hex_literal::hex;
use k256::{ecdsa::{SigningKey, Signature, signature::Signer, signature::Verifier, VerifyingKey}};
fn main() {
// Private key
let private_key: SigningKey = SigningKey::from_bytes(&hex!(
"DCDFF4B7CA287CC7BD30ECAEF0622265DB4E14054E12954225457C3A6B84F135"
).into()).unwrap();
// Public key
let public_key: &VerifyingKey = private_key.verifying_key();
// Sign message
let message = b"hello, world";
let signature: Signature = private_key.sign(message);
// Verify
let verified = public_key.verify(message, &signature).is_ok();
println!("private key: {:X?}", private_key.to_bytes());
println!("public key: {:X?}", public_key.to_encoded_point(false).to_bytes());
println!("message: {:X?}", message);
println!("Signature: {:X?}", signature.to_bytes());
println!("verified: {}", verified);
}
※プログラムに記載した秘密鍵は本記事用に導出したもので、すでに公開された状態となっていますので暗号資産の管理等には利用しないでください。
簡単な解説
- 秘密鍵
private_key
は、HEX文字列リテラルからSigningKey::from_bytes()
で設定しています。 - 公開鍵
public_key
は、秘密鍵private_key
のverifying_key()
関数で導出します。 - 任意のデータ
message
に対して、秘密鍵private_key
で署名するためsign()
関数を呼び出します。 - 署名
signature
が秘密鍵private_key
で署名されていることを確認するため、公開鍵public_key
のverify()
関数を使用します。
プログラム実行
$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.03s
Running `target/debug/study`
private key: [DC, DF, F4, B7, CA, 28, 7C, C7, BD, 30, EC, AE, F0, 62, 22, 65, DB, 4E, 14, 5, 4E, 12, 95, 42, 25, 45, 7C, 3A, 6B, 84, F1, 35]
public key: [4, BA, C6, CB, 0, F, 4, AD, 63, 97, 7, 52, C3, D7, 3B, 88, C5, C8, 6E, 3D, 88, AC, 69, 51, 18, 49, 4A, 1, 73, 2E, 2A, BD, 16, C7, 6A, CA, D3, D6, 58, 6C, 37, C8, DB, 7E, 69, C2, F8, 12, F9, 92, 75, 19, 89, 36, 95, 7D, 72, C3, 8D, 71, 9, 81, 99, 11, 23]
message: [68, 65, 6C, 6C, 6F, 2C, 20, 77, 6F, 72, 6C, 64]
Signature: [F6, 90, AF, FF, D2, 46, 23, 62, 7B, FA, 97, 99, 68, 73, A6, A9, D8, CA, 8E, 9B, B7, AB, 6A, D2, 69, C9, 29, 45, 3B, 42, 98, 55, 73, CD, C4, C0, 5B, 85, 22, 22, 40, 65, 75, 7F, EF, 6C, 18, 1, C8, 41, 8F, FD, 3D, EB, DA, B, 4B, E2, 2A, 73, B2, E5, B6, CE]
verified: true
Wasmターゲットの場合
Webブラウザ上で動作させたり、Internet ComputerのCanister向けにターゲットをwasm32-unknown-unknown
とする場合、以下のビルドエラーが出ます。
error: the wasm*-unknown-unknown targets are not supported by default, you may need to enable the "js" feature. For more information see: https://docs.rs/getrandom/#webassembly-support
このサンプルの範囲ではランダムによる秘密鍵の生成を行わずgetrandom
は使いませんので、この場合、以下のパッケージを追加することでビルドエラーは回避できるようです。
$ cargo add getrandom --features="custom"
Discussion