پیش‌هش و نشانه‌پیشهش

توابع اولیه‌ی Prehash و SignPrehash محاسبه‌ی امضای دیجیتال را به دو مرحله تقسیم می‌کنند:

  1. پیش‌هش (Prehash) فقط به کلید عمومی نیاز دارد. این روش، پیامی با طول دلخواه را به یک مقدار پیش‌هش کوتاه و با اندازه ثابت تبدیل می‌کند.
  2. SignPrehash به کلید خصوصی نیاز دارد. این الگوریتم یک مقدار prehash را به یک امضا تبدیل می‌کند.

امضایی که بیرون می‌آید، یک امضای معمولی روی پیام اصلی است. شما آن را با تابع اولیه‌ی Digital Signature PublicKeyVerify تأیید می‌کنید و تأییدکنندگان نیازی به دانستن یا اهمیت دادن به این ندارند که امضا در دو مرحله تولید شده است.

مقدار پیش‌هش را به عنوان بایت‌های مات در نظر بگیرید. اندازه ثابتی دارد و شناسه کلیدی را که برای آن محاسبه شده است، حمل می‌کند، اما طرح‌بندی آن بخشی از قالب سیمی Tink است و شما نباید خودتان آن را تجزیه یا بسازید. اگر در حال انتقال Tink هستید یا به جزئیات سطح بایت نیاز دارید، به قالب سیمی Tink مراجعه کنید.

از این جفت از متغیرهای اولیه زمانی استفاده کنید که:

  • کلید امضا در جای دیگری ، مثلاً در یک HSM، در یک KMS یا پشت مرز RPC قرار دارد و شما نمی‌خواهید کل پیام را از آن مرز عبور دهید.
  • پیام بزرگ است و امضاکننده از راه دور محدودیت اندازه درخواست را اعمال می‌کند.

اگر هیچ‌کدام از این‌ها صدق نمی‌کند، از امضای دیجیتال ساده‌ی اولیه استفاده کنید: این روش ساده‌تر است و سوءاستفاده از آن دشوارتر است.

مجموعه کلیدها

این دو عنصر اولیه، کلیدها را به طور متفاوتی انتخاب می‌کنند، همانطور که امضا و تأیید برای عنصر اولیه امضای دیجیتال انجام می‌شود:

  • Prehash.Compute همیشه از کلید اصلی مجموعه کلید عمومی استفاده می‌کند و شناسه آن کلید را در مقدار prehash ثبت می‌کند. این طرف است که انتخاب می‌کند امضا با کدام کلید ساخته شود.
  • SignPrehash.Sign شناسه کلید را از مقدار پیش‌هش می‌خواند و با کلید فعال‌شده‌ی منطبق از مجموعه کلید خصوصی امضا می‌کند. این سمتی است که از انتخابی که شخص دیگری قبلاً انجام داده است، پیروی می‌کند . اگر هیچ کلید فعال‌شده‌ای در مجموعه کلید، آن شناسه را نداشته باشد، فراخوانی ناموفق خواهد بود.

هر کلید در مجموعه کلید SignPrehash باید یک الزام شناسه داشته باشد، در غیر این صورت ایجاد اولیه با شکست مواجه می‌شود.

حداقل تضمین‌های امنیتی

  • امضای حاصل، همان ویژگی‌های امضایی را دارد که توسط امضای دیجیتال اولیه با همان نوع کلید تولید می‌شود.
  • Tink مقدار prehash را با ۵ بایت پیشوند می‌کند که حاوی یک مقدار رزرو شده ویژه و شناسه کلیدی است که برای آن محاسبه شده است. SignPrehash مقدار را فقط با آن کلید امضا می‌کند. اینکه آیا مقدار از نظر رمزنگاری به آن کلید متصل است یا خیر، به الگوریتم بستگی دارد. برای External Mu ML-DSA، به Tink wire format مراجعه کنید.
  • پیام‌ها می‌توانند طول دلخواه داشته باشند.

مواردی که باید مراقب آنها باشید

  • امضاکننده نمی‌تواند آنچه را که امضا می‌کند بررسی کند. هر کسی که بتواند SignPrehash را فراخوانی کند، می‌تواند یک پیام دلخواه را امضا کند و امضاکننده هیچ راهی برای اعمال سیاستی بر محتوای پیام ندارد. از دسترسی به SignPrehash دقیقاً همانطور که از دسترسی به PublicKeySign محافظت می‌کنید، محافظت کنید.
  • از مقدار پیش‌هش در حین انتقال محافظت کنید. این مخفی نیست، اما مهاجمی که می‌تواند آن را در حین انتقال تغییر دهد، آنچه را که امضا می‌شود کنترل می‌کند.

نوع کلید را انتخاب کنید

ML-DSA در حالت External Mu ، همانطور که در RFC 9881 توضیح داده شده است، تنها الگوریتمی است که Tink برای Prehash و SignPrehash پشتیبانی می‌کند. شما باید از یک کلید استاندارد ML-DSA با این مقادیر اولیه استفاده کنید.

ما ML_DSA_65 را برای اکثر موارد استفاده توصیه می‌کنیم.

کلید باید یک شناسه داشته باشد، زیرا هر مقدار پیش‌هش با پیشوندی شروع می‌شود که حاوی شناسه کلیدی است که برای آن محاسبه شده است. انواع زیر پذیرفته می‌شوند:

  • TINK -- امضای حاصل با پیشوند خروجی معمول ۵ بایتی Tink شروع می‌شود.
  • NO_PREFIX_WITH_PREHASH_ID -- امضای حاصل هیچ پیشوند خروجی ندارد، در حالی که کلید همچنان شناسه‌ای را دارد که مقدار پیش‌هش به آن نیاز دارد.

کلیدهایی که از نوع NO_PREFIX (خام) استفاده می‌کنند پشتیبانی نمی‌شوند، زیرا شناسه کلید ندارند.

مثال‌ها

مثال‌های زیر یک پیام را در دو مرحله امضا می‌کنند و سپس امضای حاصل را با امضای دیجیتال اولیه‌ی معمولی تأیید می‌کنند.

برای خوانایی بیشتر، آنها به صورت یک بلوک نشان داده شده‌اند. در یک پیاده‌سازی واقعی، این دو مرحله در مکان‌های مختلفی اجرا می‌شوند؛ برای مشاهده‌ی راهنمای گام به گام، به بخش «من می‌خواهم داده‌ها را با یک امضاکننده‌ی از راه دور امضا کنم» مراجعه کنید.

سی++

#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);

برو

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
}