[Dev] Bun.shで公開鍵復元してみる

in #japanese2 days ago (edited)

こんにちは、@yasuです。

Keychainの使い方を勉強している。

Keychainが出力したメッセージと署名から公開鍵を復元する方法。

sha256関連の処理は、bun環境ではdsteemが使用できなかった。

しかたなく、dsteemを使用せずに、公開鍵復元の方法を探した。

しかし、@noble/secp256k1はバージョン2やバージョン3はうまく動かず、

結局、古いが@noble/secp256k1はバージョン1で実現できた。

image.png

完全なソースコード

import * as secp from "@noble/secp256k1";
import { createHash } from "crypto";
import bs58 from "bs58";

function ripemd160(data: Uint8Array): Uint8Array { return new Uint8Array(createHash("ripemd160").update(data).digest()); }
function pubkeyToSteem(pubkey: Uint8Array): string {
  const checksum = ripemd160(pubkey).slice(0, 4);// compressed pubkey → RIPEMD160 ハッシュ
  const full = new Uint8Array(pubkey.length + 4);// pubkey + checksum
  full.set(pubkey, 0);
  full.set(checksum, pubkey.length);
  return "STM" + bs58.encode(full);// Base58 エンコード
}

try {
  const signature = "20356757cb7d303ece5852aa84dc1d4e1ca27099ee99023a216e3b2e43f13ead677f69be476b6004a027b77a743ab113ec3ba168a6d758e2c13ce6cc80ac2d0406";
  const message = "Login to mysite with nonce: 14a31bde6b422d0f3a1bfe47758cc7f0";
  // SHA256
  const digest = Bun.SHA256.hash(new TextEncoder().encode(message));
  // Steem署名解析
  const sigBytes = Buffer.from(signature, "hex");
  const recovery = (sigBytes[0] - 27) & 3;
  const compactSig = sigBytes.slice(1, 65);
  // 公開鍵復元
  const pubkey = secp.recoverPublicKey(digest, compactSig, recovery, true);
  // Steem形式に変換
  const steemPubkey = pubkeyToSteem(pubkey);
  console.log("Steem PubKey:", steemPubkey);
  // 署名検証
  const isValid = secp.verify(compactSig, digest, pubkey);
  console.log("Signature valid:", isValid);
} catch (err) {
  console.error(err);
}

Sort:  

Konnichiwa, @yasu! This is a fantastic, practical breakdown of recovering a public key from a Keychain signature. The detail about troubleshooting the @noble/secp256k1 library versions is super valuable – I'm sure many developers will appreciate that insight and the working version 1 solution.

The inclusion of the complete source code is a huge plus, making it easy for others to learn from and implement this solution. Thanks for sharing your journey and providing such a clear, concise explanation.

I'm curious, @yasu, have you considered wrapping this into a reusable function or library for easier integration? Anyone else working with Keychain and public key recovery, please share your experiences and any alternative approaches you might have! Let's learn together!

■修正前(Node.js 標準 crypto)
function sha256(data: Uint8Array): Uint8Array { return new Uint8Array(createHash("sha256").update(data).digest()); }

const digest = sha256(new TextEncoder().encode(message));

■修正後(Bun 独自 Bun.SHA256.hash)
const digest = Bun.SHA256.hash(new TextEncoder().encode(message));