ちょっと、そこ。 Ockamの CTO である Mrinal Wadhwa です。 Ockam の初期の頃、私たちは C ライブラリを開発していました。これは、何ヶ月も経って、なぜ私たちが C の数万行を放棄し、 Rustで書き直すことにしたのかについての物語です。
始める前に、私は今週、InfluxData の CTO である Paul Dix と一緒に録画ウェビナーに参加し、InfluxDB と Ockam の Rust での書き換えについて二人で議論しました。 2 つのオープンソース プロジェクトが書き直すことを選択した理由、新しい言語として Rust を選択した理由、その過程で学んだ教訓など。ぜひ録画をチェックしてみてください。洞察力に富んだ議論でした。
Ockam を使用すると、開発者は移動中のデータを信頼できるアプリケーションを構築できます。あらゆる環境で実行されるあらゆるアプリケーションに、エンドツーエンドの暗号化および相互認証通信を追加するためのシンプルなツールを提供します。アプリは、プライベート ネットワーク全体、複数のクラウド間、 Kafkaのメッセージ ストリームを通じて、あらゆるマルチホップ、マルチプロトコル トポロジを通じて、データの整合性、信頼性、機密性がエンドツーエンドで保証されます。すべての通信はエンドツーエンドで認証され、プライベートになります。
また、信頼関係のブートストラップ、キーの安全な管理、有効期限の短い認証情報のローテーション/取り消し、属性ベースの認可ポリシーの強制など、難しい部分のスケールを非常に簡単にします。最終結果は、詳細に制御できるアプリを構築できることです。あらゆる信頼とアクセスの決定 - アプリはプライベートで安全な設計になっています。
2019 年に、これらすべてを C で構築し始めました。制約のあるエッジ デバイスから強力なクラウド サーバーに至るまで、Ockam をあらゆる場所で実行できるようにしたいと考えていました。また、アプリケーションが組み込まれている言語に関係なく、Ockam をあらゆる種類のアプリケーションで使用できるようにしたいとも考えました。
これにより、C が明確な候補となりました。これは 99% のコンピュータ用にコンパイルでき、ほぼどこでも実行できます (ターゲット固有のツールチェーンをすべて処理する方法を理解すれば)。また、他のすべての一般的な言語は、何らかの形式のネイティブ関数インターフェイスを通じて C ライブラリを呼び出すことができます。そのため、後で Typescript、Python、Elixir、Java などの他のすべての言語に慣用的な言語ラッパーを提供できます。
そのアイデアは、通信中心のプロトコルのコアをハードウェア固有の動作から切り離し、サポートしたいハードウェアに接続可能なアダプターを用意するというものでした。たとえば、秘密キーをさまざまな HSM に保存するためのアダプター、さまざまなトランスポート プロトコル用のアダプターなどが存在します。
私たちの計画は、コアを C ライブラリとして実装することでした。次に、この C ライブラリを他の言語のラッパーでラップし、プラグイン可能なハードウェア アダプターを使用してどこでも実行します。
しかし、私たちはシンプルさも大切にしています。それは私たちの名前に込められています。私たちは、Ockam を使いやすく、構築し、保守しやすいものにしたいと考えています。
Ockam の中核となるのは、Ockam Secure Channelsや Ockam Routingなどの暗号化およびメッセージベースのプロトコルの階層化されたスタックです。これらは非同期、マルチステップ、ステートフルな通信プロトコルであり、私たちはこれらのプロトコルの詳細をすべてアプリケーション開発者から抽象化したいと考えていました。私たちは、ユーザー エクスペリエンスを、エンドツーエンドで認証および暗号化された安全なチャネルを作成するための 1 行の関数呼び出しであると想像しました。
暗号化関連のコードには、ちょっとした間違いで多くのフットガンが発生し、システムが安全でなくなる傾向があります。したがって、シンプルさは私たちにとって単なる美的理想ではなく、誰もが安全なシステムを構築できるようにするための重要な要件であると考えています。関連するすべてのプロトコルの核心を知る必要はありません。私たちは、これらのフットガンを隠し、正しく使用するのが簡単で、アプリケーションを攻撃するような方法で使用するのが不可能または困難な開発者インターフェイスを提供したいと考えていました。
そこがCに大きく欠けていたところだ。
C で安全でシンプルなインターフェイスを公開する試みは成功しませんでした。反復のたびに、アプリケーション開発者はプロトコルの状態と状態遷移についてあまりにも多くの詳細を知る必要があることがわかりました。
その頃、私は Elixir で Ockam Routing上に Ockam Secure Channelを作成するプロトタイプを作成しました。
Elixir プログラムは、Erlang 仮想マシンである BEAM 上で実行されます。 BEAM は Erlang プロセスを提供します。 Erlang プロセスは、軽量のステートフルな同時アクターです。アクターは内部状態を維持しながら同時に実行できるため、ステートフル プロトコルである Ockam Transports + Ockam Routing + Ockam Secure Channel の同時スタックを実行することが容易になりました。
すべてのステートフル レイヤーを非表示にして、さまざまなマルチホップ、マルチプロトコル ルート上でエンドツーエンドの暗号化された安全なチャネルを作成するために呼び出すことができるシンプルな 1 行関数を作成することができました。
{:ok, channel} = Ockam.SecureChannel.create(route, vault, keypair)
アプリケーション開発者はこの単純な関数を呼び出し、複数の同時アクターがステートフル プロトコルの基礎となる層を実行します。この関数は、チャネルが確立された場合、またはエラーが発生した場合に戻ります。これはまさに私たちがインターフェースに求めていたものです。
しかし、Elixir は C とは異なります。Elixir は、小規模または制約のあるコンピューターではあまりうまく動作せず、言語固有の慣用的なラッパーでラップするのには適していません。
この時点で、軽量のアクターを実装したいことはわかっていましたが、C ではそれが簡単ではないこともわかっていました。このとき私は Rust を深く掘り下げ始め、Rust を非常に魅力的なものにしているいくつかの点にすぐに気づきました。
Rust ライブラリは、C の呼び出し規約と互換性のあるインターフェイスをエクスポートできます。これは、C ライブラリ内の関数を静的または動的にリンクして呼び出すことができる言語またはランタイムは、まったく同じ方法で Rust ライブラリ内の関数をリンクして呼び出すこともできることを意味します。ほとんどの言語は C のネイティブ関数をサポートしているため、Rust のネイティブ関数もすでにサポートしています。これにより、コア ライブラリの周囲に言語固有のラッパーを配置するという要件の観点から、Rust は C と同等になりました。
Rust は LLVM を使用してコンパイルします。これは、非常に多くのコンピュータを対象にすることができることを意味します。このセットは、C が GCC やさまざまな独自の GCC フォークでターゲットにできるすべてのものほど大きくはないと思われますが、それでも非常に大きなサブセットであり、Rust を GCC でコンパイルできるようにする作業が進行中です。新しい LLVM ターゲットのサポートが拡大しており、Rust では GCC サポートがサポートされる可能性があるため、どこでも実行できるという要件の観点から、これは良い賭けのように思えました。
Rust の型システムを使用すると、不変条件をコンパイル時エラーに変えることができます。これにより、開発時に発見しやすくなり、本番環境に送り込まれる可能性のある一連のミスが減少します。私たちのチームと Rust ライブラリのユーザーは、動作上のバグやセキュリティの脆弱性を本番環境に送り込む可能性が低くなります。
Rust のメモリ安全機能は、解放後の使用、二重解放、オーバーフロー、範囲外アクセス、データ競合、および大規模な C 言語における重大度の高い脆弱性の 60 ~ 70% を引き起こすことが知られているその他の多くの一般的なミスの可能性を排除します。または C++ コードベース。 Rust は、ガベージ コレクターを使用して実行時にメモリを安全に管理するパフォーマンス コストを発生させることなく、コンパイル時にこの安全性を提供します。これにより、Rust には、高パフォーマンス、制約された環境での実行、および高度な安全性が必要なコードを作成する上で大きな利点が得られます。
Rust が Ockam に最適であることを私に確信させた最後の部分は、 async/await
でした。私たちは、シンプルで安全なインターフェイスである Ockam のプロトコル スタックを作成するには軽量のアクターが必要であることをすでに特定していました。 async/await
アクターを作成するための多くの大変な作業が tokio や async-std などのプロジェクトですでに行われていることを意味します。この基盤の上に Ockam のアクター実装を構築できます。
目立ったもう 1 つの重要な側面は、Rust のasync/await
には、JavaScript などの他の言語のasync/await
との大きな違いが 1 つあるということでした。 Javascript では、ブラウザ エンジンまたは nodejs が非同期関数を実行する方法を選択します。しかし、Rust では、独自に選択したメカニズムをプラグインできます。これらは非同期ランタイムと呼ばれます。tokio は、拡張性の高いシステム向けに最適化されたランタイムの一般的な例です。ただし、常に tokio を使用する必要はなく、代わりに小さな組み込みデバイスやマイクロコントローラー用に最適化された非同期ランタイムを選択できます。
これは、Ockam のアクター実装 (後に Ockam Workersと呼ばれる) が、Rust のasync/await
をベースにしている場合、実行場所 (大きなコンピューターでも小さなコンピューターでも) に関係なく、まったく同じインターフェイスをユーザーに提供できることを意味します。 Ockam Workers の上に位置するすべてのプロトコル インターフェイスも、実行場所に関係なく、まったく同じ単純なインターフェイスを提供できます。
この時点で、Ockam を Rust で書き直す必要があると確信しました。先ほど述べたウェビナーでの会話の中で、Paul Dix と私は、各プロジェクトが Rust への移行を決定した後、Ockam と InfluxDB のチームにとって移行がどのようなものになるかについて話し合いました。 InfluxDB がどのように Go から Rust に移行したか、そして Ockam がどのように C から Rust に移行したかについて説明しました。興味があれば、私たちの旅のその部分で録音を聞いてください。
何度も繰り返した結果、誰でもRust で Ockam クレートを使用して、簡単な関数呼び出しでエンドツーエンドで暗号化され相互認証された安全なチャネルを作成できるようになりました。
開始時に想像していた一行は次のとおりです。
let channel = node.create_secure_channel(&identity, route, options).await?;
プライベート ネットワークやクラウドにまたがる任意のマルチホップ、マルチプロトコル ルート上に、認証および暗号化されたチャネルを作成します。このシンプルで安全な関数呼び出しの背後に、根底にある複雑さと強力な機能をすべて隠すことができます。コードは、 スケーラブルなサーバーや小さなマイクロコントローラーなど、使用方法に関係なく同じままです。
詳細については、 GitHub で kout Ockam を確認するか、 Ockam Rust ライブラリとOckam Commandの段階的なウォークスルーを試してください。
Rust で書き直されたプロジェクトに参加したことがある場合は、 Discord サーバーでチームのストーリーを共有してください。また、Rust と Elixir の両方の役割を募集しています。ぜひビルダー チームに参加してください。私たちは、ソフトウェアをより安全でプライバシーに配慮した設計にすることを目指しています。