می‌خواهم داده‌ها را با یک امضاکننده از راه دور امضا کنم

ما Prehash و SignPrehash اولیه را با کلید ML_DSA_65 توصیه می‌کنیم وقتی کلید خصوصی در جایی قرار دارد که پیام قابل ارسال نیست.

گاهی اوقات طرفی که کلید امضا را در اختیار دارد نمی‌تواند - یا نباید - خود پیام را دریافت کند: کلید در یک HSM یا KMS قرار دارد، یا پیام بزرگتر از محدودیت اندازه درخواست امضاکننده است.

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

امضایی که دریافت می‌کنید، یک امضای معمولی روی پیام اصلی است: تأییدکنندگان از امضای دیجیتال اولیه معمولی استفاده می‌کنند و نیازی به دانستن اینکه دو مرحله انجام شده است، ندارند.

قبل از اینکه شروع کنی

یک مجموعه کلید ML-DSA ایجاد کنید که کلیدهای آن نیاز به شناسه داشته باشند -- یا نوع TINK ، یا NO_PREFIX_WITH_PREHASH_ID اگر نمی‌خواهید پیشوند خروجی روی امضای حاصل باشد. مجموعه کلید خصوصی را به امضاکننده و مجموعه کلید عمومی مربوطه را به طرف پیش‌هشینگ بدهید. امضاکننده باید برای هر شناسه کلیدی که طرف پیش‌هشینگ می‌تواند تولید کند، یک کلید فعال داشته باشد؛ به Keysets مراجعه کنید.

مرحله ۱: محاسبه مقدار پیش‌هش

این دستور را هر جا که پیام هست اجرا کنید. فقط به مجموعه کلید عمومی نیاز دارد.

سی++

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

absl::StatusOr<std::unique_ptr<crypto::tink::Prehash>> prehasher =
    public_handle.GetPrimitive<crypto::tink::Prehash>(
        crypto::tink::ConfigSignature2026());
if (!prehasher.ok()) return prehasher.status();

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

برو

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

prehasher, err := signprehash.NewPrehash(publicHandle)
if err != nil {
    return err
}
prehash, err := prehasher.ComputePrehash(message)
if err != nil {
    return err
}

مرحله ۲: ارسال مقدار پیش‌هش به امضاکننده

مقدار پیش‌هش را به هر کسی که کلید خصوصی را در اختیار دارد ارسال کنید. این کلید مخفی نیست، اما شما باید از یکپارچگی آن در حین انتقال محافظت کنید: مهاجمی که می‌تواند آن را در حین انتقال تغییر دهد، آنچه را که امضا می‌شود کنترل می‌کند.

مرحله ۳: مقدار پیش‌هش را امضا کنید

این را هر جا که کلید خصوصی وجود دارد، اجرا کنید.

سی++

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

absl::StatusOr<std::unique_ptr<crypto::tink::SignPrehash>> signer =
    private_handle.GetPrimitive<crypto::tink::SignPrehash>(
        crypto::tink::ConfigSignature2026());
if (!signer.ok()) return signer.status();

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

برو

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

signer, err := signprehash.NewPrehashSigner(privateHandle)
if err != nil {
    return err
}
sig, err := signer.SignPrehash(prehash)
if err != nil {
    return err
}

مرحله ۴: تأیید امضا

تأیید، جریان عادی امضای دیجیتال بر روی پیام اصلی است، نه بر روی مقدار پیش‌هش.

سی++

absl::StatusOr<std::unique_ptr<crypto::tink::PublicKeyVerify>> verifier =
    public_handle.GetPrimitive<crypto::tink::PublicKeyVerify>(
        crypto::tink::ConfigSignature2026());
if (!verifier.ok()) return verifier.status();

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

برو

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

verifier, err := signature.NewVerifier(publicHandle)
if err != nil {
    return err
}
if err := verifier.Verify(sig, message); err != nil {
    return err
}

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

توابع اولیه‌ی 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 (خام) استفاده می‌کنند پشتیبانی نمی‌شوند، زیرا شناسه کلید ندارند.