[Rust] rust-security-frameworkでmacのkeychainを操作する

rust-security-frameworkでmacOSのキーチェーンの読み書きをしてみました。
2021.07.26

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

「ここにいるものは誰か!」「\善良なるmacユーザー!!/」(古代ギリシャのmacユーザーの挨拶)

というわけでRustでmacのkeychainの読み書きをしたかったので試してみました。

キーチェーンとは

macユーザーにはおなじみのキーチェーンですが「Macのキーチェーンアクセスとは」より説明を引用すると以下のように紹介されています。

キーチェーンは、キーチェーンアクセスでアプリケーション、サーバ、AirMacベースステーション、およびWebサイトのアカウント名およびパスワードの保管に使用される、ロックおよび暗号化されたコンテナです。キーチェーンを使用して、クレジットカード番号や銀行口座のPIN(個人識別番号)を保管することもできます。

rust-security-framework

今回はrust-security-framework というcrateを使いました。keychain を扱えるcrateは他にもあったのですが、このcrateは頻繁に更新されているようなので試してみました。

今回使ったライブラリのバージョンなど

# cargo -V
# cargo 1.51.0 (43b129a20 2021-03-16)
[dependencies]
security-framework = "2.3.1"

コード

以下がサンプルコードになります。

SecKeyChain::openで既存のキーチェーンファイルのオープン、CreateOptions#create で新規作成が行えます。作成時にはビルダーパターンでキーチェーンの詳細が指定できます。

作成したキーチェーンファイルはファイルのパスを相対パスで指定した場合は~/Library/Keychainsに、絶対パスで指定した場合はそのディレクトリに作成されるようです。

生成したSecKeyChainに対してadd_generic_passwordおよび add_internet_password でパスワードの保存が行えます。各関数の引数の詳細はset_generic_password および set_internet_password に詳しいです。あるいはこれらの引数はkSecClassGenericPassword および kSecClassInternetPassword に対応しているようです。パスワードの検索、取得も同様にfind_generic_password またはset_internet_passwordを使います。

パスワードのエンコード

SecKeyChainの各APIに対して指定、取得するパスワード文字列の型は&[u8]になっています。 strからはstr::as_bytes()で変換し、Vec[u8]を経由してString::from_utf8でStringへ戻すことができます。

use security_framework::os::macos::keychain::{CreateOptions, SecKeychain};
use std::path::PathBuf;
use std::env;

fn main() {
    let mut path:PathBuf = env::current_dir().unwrap();
    path.push("keychain_sample.keychain");

    let keychain: SecKeychain = if path.exists() {
      SecKeychain::open(path).unwrap()
    }else {
      //なければ作る
      CreateOptions::default()
      .prompt_user(true) //パスワードをユーザーに入力させる
      .create(path) //パスを指定
      .unwrap()
    };

    //パスワードを書き込み
    //passwordは&[u8]で指定する必要があるのでas_bytes()で変換する
    keychain
        .add_generic_password("service", "account", "password".as_bytes())
        .unwrap();

    //パスワードを読み込み
    let (password, item) = keychain
        .find_generic_password("service", "account")
        .unwrap();

    print!("item: {:?}\n", item);
    //password.as_ref() で&[u8]に変換 or to_owned()で Vec[u8]に変換する
    print!(
        "password: {}\n",
        String::from_utf8(password.as_ref().to_vec()).unwrap()
    );
}

キーチェインファイル

作成したキーチェインはFinderおよびキーチェーンアクセスアプリから内容を参照、表示できます。

またここでロックなどの設定も変更できます。

まとめ

rust-security-frameworkでmacOSのキーチェーンの読み書きをしてみました。macOSのAPIの知識がないので難しいかなと思いましたが、簡単な操作であればそれほど苦労せずに行えました。