pemファイルからPrincipalを取得
Principalとは
分散型クラウドを実現するInternet Computerでは、Principalという識別子が使われています。ユーザーやCanisterを識別できます。
Principalはユーザーの公開鍵(または秘密鍵)から導出されますが、どのように組み立てられているかアルゴリズムの解説サイトがありましたので、実際に検証してみることにします。
参考元サイト
https://5n2bt-lqaaa-aaaae-aajfa-cai.raw.icp0.io/
We convert a DER public key to a principal IDs by computing its SHA224 hash then appending the byte 02, indicating a self-authenticating ID in Internet Computer parlance. Like other principal IDs, we can cook self-authenticating IDs by prepending a CRC32 checksum, encoding with base32, and inserting dashes. Some tools expect these
Principalの導出
IC SDKに含まれているdfx
コマンドを使っている方であれば、defaultのIdentityが以下に格納されているかと思います。
~/.config/dfx/identity/default/identity.pem
(ECDSA secp256k1秘密鍵)
自分のテスト環境のPrincipalは以下の値でしたので、この値を 秘密鍵 → 公開鍵 → Principal という流れで求めてみることにします。
$ dfx identity get-principal
rjfvh-5kjv6-svpk2-vf3sl-h24gi-qwmfe-22o2e-jcxqg-im5h6-2apbx-hqe
1. 秘密鍵(PEM形式)から公開鍵(DER形式)を出力
$ openssl ec -in ~/.config/dfx/identity/default/identity.pem -pubout -outform DER
バイナリですので、16進数ダンプ形式で中身を確認したい場合には| hexdump -C
のようにパイプでつなげるとよいでしょう。
2. sha224sum
公開鍵(DER形式)のsha224ハッシュを求めます。1.の標準出力をパイプでつなげます。
$ openssl ec -in ~/.config/dfx/identity/default/identity.pem -pubout -outform DER | sha224sum | cut -d ' ' -f 1
49afa557ab552ee4b3eb86442cc2935a7688915e06433a7f680f0dcf
3. 末尾に'02'追加 (Principal ID raw)
2.の標準出力に1バイトの16進数02
を追加します。
$ openssl ec -in ~/.config/dfx/identity/default/identity.pem -pubout -outform DER | sha224sum | cut -d ' ' -f 1 | sed s/$/02/
49afa557ab552ee4b3eb86442cc2935a7688915e06433a7f680f0dcf02
末尾に付与するbyteは以下のようです。
byte | Description |
---|---|
0x01 | canister |
0x02 | public key (self-authenticating ID) |
0x04 | anonymous |
4. CRC32計算
3.の結果に対して、Principal ID (raw)のCRC32を計算します。
$ echo -n 49afa557ab552ee4b3eb86442cc2935a7688915e06433a7f680f0dcf02 | xxd -r -p |python3 -c 'import sys;import zlib;print("{:x}".format(zlib.crc32(sys.stdin.buffer.read())%(1<<32)))'
8a4b53f5
5. 先頭にCRC32を追加してBase32
CRC32値 (8a4b53f
)をPrincipal ID (raw)の先頭に追加した値をBase32エンコードします。
echo -n 8a4b53f549afa557ab552ee4b3eb86442cc2935a7688915e06433a7f680f0dcf02 | xxd -r -p | base32
RJFVH5KJV6SVPK2VF3SLH24GIQWMFE22O2EJCXQGIM5H62APBXHQE===
6. 小文字化して、5桁ずつ区切る
Base32エンコード値を見やすいように整形します。
$ echo -n 8a4b53f549afa557ab552ee4b3eb86442cc2935a7688915e06433a7f680f0dcf02 | xxd -r -p | base32 | tr A-Z a-z | tr -d "=" | sed -E 's/(.{5})/\1-/g'
rjfvh-5kjv6-svpk2-vf3sl-h24gi-qwmfe-22o2e-jcxqg-im5h6-2apbx-hqe
dfx identity get-principal
コマンドの結果と同じ値が取得できました。
Rustアプリケーションでの導出
ic-agent
クレートを利用すれば、暗号化されていないpemファイルからPrincipalを簡単に取得することができます。
1. プロジェクトの作成
$ cargo new client
$ cd client
$ cargo add ic-agent
2. Rustプログラムの作成
~/.config/dfx/identity/default/identity.pem
を読み込んでPrincipalを取得するサンプルです。
dfx identity new
コマンド等で追加したIdentityはデフォルトで暗号化されていますので秘密鍵を復元できません。
Identity追加時に--storage-mode=plaintext
オプションを付与するなどして非暗号化した状態とする必要があります。
use ic_agent::Identity;
use ic_agent::identity::Secp256k1Identity;
use std::env;
fn main() {
// Identity
let home = env::var("HOME").unwrap();
let pem_path = format!("{}/.config/dfx/identity/default/identity.pem", home);
let identity = Secp256k1Identity::from_pem_file(pem_path).unwrap();
let principal = identity.sender().unwrap();
println!("principal: {}", principal.to_string());
}
3. プログラムの実行
$ cargo run
principal: rjfvh-5kjv6-svpk2-vf3sl-h24gi-qwmfe-22o2e-jcxqg-im5h6-2apbx-hqe
補足事項
もし、dfx identity
コマンドで追加された暗号化されたpemファイルをRustで復号したい場合には、以下のdecrypt()
関数を参考にするとよいでしょう。
https://github.com/dfinity/sdk/blob/master/src/dfx-core/src/identity/pem_safekeeping.rs
Discussion