🦀

RustでECDSA署名を行う

2024/05/06に公開

はじめに

ECDSA(Elliptic Curve Digital Signature Algorithm)は楕円曲線暗号を用いたデジタル署名アルゴリズムです。また、secp256k1はECDSAで使用されるパラメータを決めたもので、Bitcoin、Ethereum、Internet Computerなどブロックチェーンでも使用されています。

楕円曲線は一般にy^2=x^3+ax+bで表され、secp256k1ではa=0b=7とする
  y^2 \mod p=x^3+7 \mod p
   ※p=2^{256}-2^{32}-2^9-2^8-2^7-2^6-2^4−1
が使われます。

ECDSAやsecp256k1に関する詳細については触れませんが、RustからECDSA署名を行う方法について解説します。
すでに信頼できるライブラリが公開されていますので、自前では実装せずライブラリを利用します。

プロジェクトの作成

$ cargo new study
$ cd study

依存パッケージの追加

secp256k1を扱うk256を追加します。
また。本サンプルではHEX文字列リテラルをu8配列に変換するためにhex-literalを使うことにします。

$ cargo add hex-literal k256

パッケージを追加したした時点で、Cargo.tomlは概ね以下のような内容になるかと思います。

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の編集

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_keyverifying_key()関数で導出します。
  • 任意のデータmessageに対して、秘密鍵private_keyで署名するためsign()関数を呼び出します。
  • 署名signatureが秘密鍵private_keyで署名されていることを確認するため、公開鍵public_keyverify()関数を使用します。

プログラム実行

$ 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"

参考サイト

https://forum.dfinity.org/t/rust-wasm-getrandom-custom/6351

Discussion