Prehash と SignPrehash

Prehash プリミティブと SignPrehash プリミティブは、デジタル署名の計算を次の 2 つのステップに分割します。

  1. Prehash に必要なのは公開鍵のみです。任意の長さのメッセージを、短い固定サイズのプリハッシュ値に変換します。
  2. SignPrehash には秘密鍵が必要です。プリハッシュ値を署名に変換します。

出力される署名は、元のメッセージに対する通常の署名です。通常のデジタル署名 PublicKeyVerify プリミティブで検証します。検証者は、署名が 2 つのステップで生成されたことを知る必要も気にする必要もありません。

事前ハッシュ値を不透明なバイトとして扱います。サイズは固定で、計算された鍵の ID を保持していますが、レイアウトは Tink のワイヤー形式の一部であり、自分で解析したり構築したりしないでください。Tink を移植する場合や、バイトレベルの詳細が必要な場合は、Tink のワイヤー形式をご覧ください。

このプリミティブのペアは、次の場合に使用します。

  • 署名鍵が別の場所にある(HSM、KMS、RPC 境界の背後など)。その境界を越えてメッセージ全体を送信したくない場合。
  • メッセージが大きい。リモート署名者がリクエスト サイズの上限を適用している。

どちらにも該当しない場合は、代わりにプレーンな Digital Signature プリミティブを使用します。これはよりシンプルで、不正使用しにくいものです。

鍵セット

2 つのプリミティブは、デジタル署名プリミティブの署名と検証と同様に、鍵を異なる方法で選択します。

  • Prehash.Compute は常に公開鍵セットの主キーを使用し、その鍵の ID をプリハッシュ値に記録します。署名に使用する鍵を選択する側です。
  • SignPrehash.Sign は、事前ハッシュ値から鍵 ID を読み取り、一致する有効な秘密鍵セットの鍵で署名します。これは、他のユーザーがすでに選択した内容に従う側です。鍵セット内の有効な鍵にその ID がない場合、呼び出しは失敗します。

SignPrehash キーセット内のすべての鍵には ID 要件が必要です。そうでない場合、プリミティブの作成は失敗します。

最小限のセキュリティ保証

  • 結果として得られる署名は、同じ鍵タイプで Digital Signature プリミティブによって生成された署名と同じプロパティを持ちます。
  • Tink は、特別な予約値と、計算対象の鍵の ID を含む 5 バイトの接頭辞をプリハッシュ値に付加します。SignPrehash は、その鍵でのみ値を署名します。値が その鍵に暗号的にバインドされているかどうかは、アルゴリズムによって異なります。External Mu ML-DSA の場合はバインドされています。 Tink のワイヤー形式をご覧ください。
  • メッセージの長さは任意です。

注意点

  • 署名者は署名対象を検査できません。SignPrehash を呼び出すことができるユーザーは誰でも任意のメッセージに署名できます。署名者はメッセージの内容にポリシーを適用できません。PublicKeySign へのアクセスを保護する場合と同様に、SignPrehash へのアクセスを保護します。
  • 転送中のプリハッシュ値を保護します。シークレットではありませんが、転送中に変更できる攻撃者は、署名されるものを制御できます。

キーの種類を選択する

RFC 9881 で説明されているように、外部 Mu モードの ML-DSA は、Tink が Prehash と SignPrehash でサポートする唯一のアルゴリズムです。これらのプリミティブでは、標準の ML-DSA 鍵を使用する必要があります。

ほとんどのユースケースでは ML_DSA_65 をおすすめします。

すべてのプリハッシュ値は、計算対象の鍵の ID を含む接頭辞で始まるため、鍵には ID 要件が必要です。次のバリアントを使用できます。

  • TINK - 結果の署名は、Tink の通常の 5 バイトの出力接頭辞で始まります。
  • NO_PREFIX_WITH_PREHASH_ID - 結果の署名には出力接頭辞がありませんが、キーにはプリハッシュ値に必要な ID があります。

NO_PREFIX(raw)バリアントを使用するキーは、キー ID がないためサポートされていません。

次の例では、2 つのステップでメッセージに署名し、通常の デジタル署名プリミティブを使用して結果の署名を検証します。

読みやすくするため、1 つのブロックとして表示されます。実際のデプロイでは、この 2 つのステップは異なる場所で実行されます。手順については、リモート署名者を使用してデータに署名するをご覧ください。

C++

#include "tink/keyset_handle.h"
#include "tink/public_key_verify.h"
#include "tink/signature/config_2026.h"
#include "tink/signature/prehash.h"
#include "tink/signature/sign_prehash.h"

using ::crypto::tink::ConfigSignature2026;
using ::crypto::tink::Prehash;
using ::crypto::tink::PublicKeyVerify;
using ::crypto::tink::SignPrehash;

// 1. Wherever the message is: compute the prehash value. This needs only
//    the public keyset.
absl::StatusOr<std::unique_ptr<Prehash>> prehasher =
    public_handle.GetPrimitive<Prehash>(ConfigSignature2026());
if (!prehasher.ok()) return prehasher.status();

absl::StatusOr<std::string> prehash = (*prehasher)->Compute(message);
if (!prehash.ok()) return prehash.status();

// 2. Wherever the private key is: turn the prehash value into a signature.
absl::StatusOr<std::unique_ptr<SignPrehash>> signer =
    private_handle.GetPrimitive<SignPrehash>(ConfigSignature2026());
if (!signer.ok()) return signer.status();

absl::StatusOr<std::string> signature = (*signer)->Sign(*prehash);
if (!signature.ok()) return signature.status();

// 3. Anywhere: verify the signature over the original message.
absl::StatusOr<std::unique_ptr<PublicKeyVerify>> verifier =
    public_handle.GetPrimitive<PublicKeyVerify>(ConfigSignature2026());
if (!verifier.ok()) return verifier.status();

absl::Status verified = (*verifier)->Verify(*signature, message);

Go

import (
    "github.com/tink-crypto/tink-go/v2/signature"
    "github.com/tink-crypto/tink-go/v2/signprehash"
)

// 1. Wherever the message is: compute the prehash value. This needs only
//    the public keyset.
prehasher, err := signprehash.NewPrehash(publicHandle)
if err != nil {
    return err
}
prehash, err := prehasher.ComputePrehash(message)
if err != nil {
    return err
}

// 2. Wherever the private key is: turn the prehash value into a signature.
signer, err := signprehash.NewPrehashSigner(privateHandle)
if err != nil {
    return err
}
sig, err := signer.SignPrehash(prehash)
if err != nil {
    return err
}

// 3. Anywhere: verify the signature over the original message.
verifier, err := signature.NewVerifier(publicHandle)
if err != nil {
    return err
}
if err := verifier.Verify(sig, message); err != nil {
    return err
}