Synch

Synchのエンドツーエンド暗号化はどのように動きますか?

エンドツーエンド暗号化とは、データが端末を離れる前にロックされ、もう一度ロックを外せるのも自分の端末だけである、という意味です。

Synchのサーバーはデータの保存と同期を手伝いますが、その内容を読むための秘密は受け取りません。

基本的な考え方はこうです。

自分の端末: 読めるノート -> 暗号化データ
サーバー: 暗号化データを保存
別の端末: 暗号化データ -> 読めるノート

暗号化前のノートは、たとえば次のように見えます。

Hello, this is my private note.

暗号化後はランダムなデータのように見えます。

K9sV1xQ4...unreadable bytes...

このランダムに見えるデータを元のノートに戻すには、端末が正しい鍵を持っている必要があります。

いちばん大事な問い

暗号化の多くは、ひとつの問いに集約されます。

誰が鍵を持っていますか?

Synchでは、自分の端末が鍵を持ちます。サーバーは暗号化されたデータを保存しますが、そのデータを復号するための平文の鍵は受け取りません。

Synchはアップロード前に、ファイル内容とファイルパスなどのメタデータを端末上で暗号化します。別の端末はサーバーから暗号化データをダウンロードできますが、同じvault keyをローカルで解除した後でなければ読めません。

以降では、その仕組みを説明します。

秘密はひとつではなく、ふたつ

Synchでremote vaultを作成するとき、vault passwordを選びます。

Create vault screen

このパスワードがすべてのファイルを直接暗号化している、と考えるのは自然です。

しかし実際には違います。

Synchは2つの異なる秘密を使います。

vault password: 自分が覚えて入力するパスワード
vault key: Synchが生成するランダムな鍵

vault keyが、同期されるvaultデータを実際に暗号化・復号する鍵です。

vault passwordの役割は別です。vault keyを保護し、そのvault keyを安全に保存して他の端末で解除できるようにします。

簡単に言えば、こうです。

vault key = データを読むための鍵
vault password = vault keyを解除するための鍵

この一段階が重要なのは、人間が覚えるパスワードは通常、強い暗号鍵として直接使えるほどランダムではないからです。人間には強そうに見えるパスワードでも、攻撃者が大量に推測を試せる状況ではコンピューターに破られる可能性があります。

そのため、Synchは実際のデータ暗号化用にランダムな32バイトのvault keyを生成します。

password = "my-strong-password"
vaultKey = "random-32-byte-key"

そして、そのvault keyをパスワードで保護します。

Vault Keyを保護する

Synchはvault keyを読める文字列のままサーバーに保存できません。そうすると、サーバーが暗号化データを読めてしまいます。

そのため、Synchはvault keyの暗号化済みコピーを保存します。

まず、パスワードからwrapKeyというより強い鍵を作ります。

password + salt + Argon2id settings
=> wrapKey

wrapKeyはファイルの暗号化には使いません。vault keyを暗号化、つまり「包む」ためだけに使います。

SynchはArgon2idを使って、パスワードからwrapKeyを作ります。

Argon2id(
  password = "my-strong-password",
  salt = random 16 bytes,
  memory = 64 MiB,
  iterations = 3,
  parallelism = 1
)
=> wrapKey

Argon2idはパスワードベースの鍵導出関数です。平たく言えば、パスワードを暗号鍵に変換する処理を意図的に重くする仕組みです。これにより、攻撃者のパスワード推測を遅くできます。

saltは、暗号化されたvault keyと一緒に保存されるランダムデータです。秘密ではありません。同じパスワードでも、異なるvaultで同じ結果にならないようにするのが役割です。

同じパスワード、同じsalt、同じ設定を入力すれば、Synchは同じwrapKeyを再び得ます。パスワードが間違っていれば、別のwrapKeyになります。

次に、SynchはwrapKeyでvault keyを暗号化します。

AES-GCM encrypt (
  key = wrapKey,
  nonce = random 12 bytes,
  plaintext = vaultKey
)
=> encrypted vaultKey

ここで使う暗号方式はAES-GCMです。nonceは暗号化に必要なランダムに見えるデータで、一意である必要がありますが、秘密である必要はありません。

この時点で、サーバーは暗号化されたvault keyパッケージを保存できます。

{
  "kdf": {
    "name": "argon2id",
    "memoryKiB": 65536,
    "iterations": 3,
    "parallelism": 1,
    "salt": "b64_salt"
  },
  "wrap": {
    "algorithm": "aes-256-gcm",
    "nonce": "b64_nonce",
    "ciphertext": "b64_encrypted_vaultKey"
  }
}

このパッケージは、後でSynchクライアントがvault keyの解除を試すための情報です。サーバーにパスワードやvault keyを渡すものではありません。

サーバーが持つもの:

salt
Argon2id settings
nonce
encrypted vaultKey

サーバーが持たないもの:

password
wrapKey
vaultKey

この違いが、Synchのエンドツーエンド暗号化設計の中心です。

サーバーに見えるもの、見えないもの

サーバーはvault keyを持たないため、ファイル内容や復号済みのファイルパスを読むことはできません。

サーバーは暗号化データと、自分の端末が正しいvault passwordを入力した後にそれを解除するための情報を保存します。

ただし、エンドツーエンド暗号化はすべてを隠すわけではありません。同期サービスを運用するために必要な情報、たとえばアカウント、vault識別子、暗号化オブジェクトのサイズ、更新時刻、同期アクティビティなどはサーバーに見える場合があります。

重要な境界は、サーバーだけでは暗号化されたvaultデータを読めるノートに戻せないことです。

ファイルとメタデータの暗号化

端末がvault keyを解除すると、Synchはそのvault keyを同期データのルート秘密として使います。

ファイル内容はアップロード前に暗号化されます。ファイルパスなどのメタデータもアップロード前に暗号化されます。暗号化された各項目はそれぞれnonceを持ち、そのnonceは暗号化データと一緒に保存され、復号時に使われます。

サーバーが保存するのは暗号化データだけです。平文のファイル内容、平文のファイルパス、vault keyは保存しません。

別の端末でVaultを開く

Connect vault screen

別の端末が同じremote vaultに接続すると、サーバーから暗号化されたvault keyパッケージをダウンロードします。

次に、その端末でvault passwordを入力します。

Synchは保存されていたsaltとArgon2id設定を使って、同じwrapKeyを導出します。

Argon2id(password, same salt, same settings)
=> same wrapKey

パスワードが正しければ、端末はそのwrapKeyで暗号化されたvault keyを復号します。

AES-GCM decrypt(
  key = wrapKey,
  nonce = stored nonce,
  ciphertext = encrypted vaultKey
)
=> vaultKey

端末がvault keyを得ると、同期されたファイルとメタデータをローカルで復号できます。

パスワードが間違っていれば、端末は別のwrapKeyを導出し、vault keyの復号は失敗します。

Vault Passwordが重要な理由

vault passwordはvault内のすべてのファイルを直接暗号化するわけではありません。vault keyを解除し、そのvault keyが実際の同期データを暗号化します。

それでも、パスワードは非常に重要です。

誰かが暗号化されたvault keyパッケージのコピーを入手すると、それに対してオフラインでパスワード推測を試せます。Argon2idは各推測を重くしますが、推測しやすいパスワードを完全に守ることはできません。

vault passwordを忘れた場合、Synchはvaultを復旧できません。wrapKeyを導出するにはパスワードが必要で、vault keyを解除するにはwrapKeyが必要です。どちらかがなければ、暗号化されたvaultデータは読めません。

パスワードを失っても、サーバーはそれを復旧できません。wrapKeyの導出は自分のパスワードから始まり、そのパスワード自体はSynchに送信されないからです。

要するに、サーバーの役割は暗号化されたvaultデータを保存して同期することです。それを読めるノートに戻す処理は、完全に自分の端末上で行われます。データを読むために必要な秘密は、サーバーには存在しません。

Obsidian vaultを同期しますか?

エンドツーエンド暗号化同期を無料で始めるか、より多いストレージや長いバージョン履歴が必要な場合はプランを比較できます。