この記事の要点
- Swift エコシステム向けの新しいオープンソースパッケージ Swift Crypto が発表されました。Apple の CryptoKit と同じ API を、Swift が動作するすべてのプラットフォームで使えるようにするものです。
import Cryptoするだけで、暗号化・メッセージ認証・鍵交換などの一般的な暗号操作を、安全で扱いやすい API として利用できます。Apple プラットフォームでは CryptoKit にそのまま委譲し、それ以外のプラットフォームでは Google の BoringSSL を土台にした新規実装が使われます。- Swift Crypto は Swift Package Manager で配布され、Apache 2.0 ライセンスのもとで提供されます。
swift package updateでセキュリティ修正や API 更新を取り込めます。
何が発表されたのか
Swift Crypto は、Apple CryptoKit の API をより広い Swift コミュニティに届けるための Swift パッケージです。これにより、アプリをどのプラットフォーム上で動かすかにかかわらず、共通の暗号操作のための API に同じようにアクセスできるようになります。
Swift が動作するすべてのプラットフォームで、次のように書くだけで CryptoKit の API 一式を利用できます。
import Crypto
Apple プラットフォームでは Swift Crypto は CryptoKit に直接委譲し、それ以外のプラットフォームでは BoringSSL を土台にした新規実装を使います。これにより、どのプラットフォームでも安全で扱いやすい暗号 API を利用でき、クロスプラットフォームな暗号コードを書く際に役立ちます。
何に使えるのか
Swift Crypto は、自前で暗号方式を組み立てるときに陥りがちな落とし穴を避けながら、強力な操作を簡潔に書けるようにします。
たとえば、AES GCM による認証付き暗号化(データを隠しつつ改ざんに耐える暗号化)は次のように書けます。
func encrypt(input: [UInt8]) throws -> Data {
// 鍵はどこかに必ず保存すること!
let key = SymmetricKey(size: .bits256)
let sealedBox = try AES.GCM.seal(input, using: key)
return sealedBox.combined!
}
このコードは、ランダムに選ばれた nonce を必ず使うことや、暗号文を認証することを保証します。これらはさまざまな攻撃を防ぐうえで重要ですが、多くの暗号ライブラリでは自動的には行われません。
同様に、データが改ざんされていないことを確認するためのメッセージ認証コードも簡単に生成できます。
func authenticate(message: [UInt8]) -> [UInt8] {
// ここでも鍵の保存を忘れずに!
let key = SymmetricKey(size: .bits256)
return Array(HMAC<SHA256>.authenticationCode(for: message, using: key))
}
楕円曲線を使った鍵交換のような複雑な処理もカバーされています。たとえば Curve25519 を使って共有秘密を生成するには次のようにします。
func curve25519SharedSecret(myKey: Curve25519.KeyAgreement.PrivateKey, theirKeyBytes: [UInt8]) throws -> SharedSecret {
let theirKey = try Curve25519.KeyAgreement.PublicKey(rawRepresentation: theirKeyBytes)
return try myKey.sharedSecretFromKeyAgreement(with: theirKey)
}
このように、シンプルで強力な API のおかげで、ほとんどコードを書かずに、専門知識をそれほど必要とせずに、安全なクロスプラットフォーム暗号方式を構築できます。
仕組みと設計
Swift Crypto は本質的には次の 2 つの部分から成る、とてもシンプルなアイデアです。
- Apple CryptoKit の API を、オープンソースライセンスのもとでライブラリとして公開したもの。
- それらの API を、Google の BoringSSL を暗号プリミティブの基盤実装として用いて一から実装したもの。
このシンプルなアイデアの裏には、いくつかの込み入った実装上の事情があります。
- ハードウェア: CryptoKit の一部の API は、鍵素材を安全に保管・計算するために Apple の Secure Enclave プロセッサを利用します。Secure Enclave は Apple 以外のハードウェアでは使えないため、Swift Crypto はこれらの API を提供しません。
- 配布モデル: Apple 以外のプラットフォームで使う際に更新しやすいよう、Swift Package Manager を使って配布されます。これにより
swift package updateだけでセキュリティ修正や API 更新を取り込めます。 - 互換性: 同じ入力を同じ API に与えたとき、Swift Crypto と Apple CryptoKit が意味的に異なる結果を返すことは許されません。これを保証するため両者でテストスイートを共有しており、双方が同じ基準を満たすことが求められます。場合によっては BoringSSL 側の検証との差異を埋めるための細やかな作業や、一部アルゴリズムの完全な再実装が必要になっています。
CryptoKit と Swift Crypto でバックエンドを 1 つに統合せず、あえて 2 つの独立した実装を持つ最大の利点は 検証 です。同じ API の独立した 2 実装があることで、互いの出力を比較してテストでき、信頼性と互換性が高まり、リグレッションの混入を減らせます。
Swift Crypto はセマンティックバージョニングに従う Swift パッケージで、Apache 2.0 ライセンスのもとで提供されます。
今後の見通し
Swift Crypto の中心的な目標は、Apple CryptoKit の API をより広いプラットフォームで使えるようにすることなので、API は基本的に CryptoKit 自体の進化に追従していきます。一方でオープンソースプロジェクトとして、Swift Crypto に直接 API を提案する余地もあります。提案された API は、その内容次第で Apple CryptoKit 側での並行実装が検討されることもあります。
特殊なハードウェアを必要とする API を除けば、CryptoKit 側に実装がある場合は Swift Crypto は常にそれを使い、ない場合は Swift Crypto による実装を使えます。
ただし、あらゆる暗号プリミティブをサポートすることは明確な非目標 であるという設計方針に注意が必要です。プリミティブを増やしすぎると、利用者が(特に安全な)選択をするのが難しくなるためです。すでに、より広く使われている安全な代替手段が用意されている場合、新しいプリミティブはサポートされないことがあります。