Prehash และ SignPrehash

Primitive Prehash และ SignPrehash จะแบ่งการคำนวณลายเซ็นดิจิทัล ออกเป็น 2 ขั้นตอน ดังนี้

  1. Prehash ต้องการเพียงคีย์สาธารณะ โดยจะเปลี่ยนข้อความที่มีความยาวเท่าใดก็ได้ ให้เป็นค่าก่อนแฮชแบบสั้นที่มีขนาดคงที่
  2. SignPrehash ต้องใช้คีย์ส่วนตัว โดยจะเปลี่ยนค่าก่อนแฮชเป็น ลายเซ็น

ลายเซ็นที่ออกมาคือลายเซ็นธรรมดาในข้อความต้นฉบับ คุณยืนยันด้วย ลายเซ็นดิจิทัล PublicKeyVerifyแบบปกติ และ ผู้ยืนยันไม่จำเป็นต้องทราบหรือสนใจว่าลายเซ็นสร้างขึ้นใน 2 ขั้นตอน

ถือว่าค่าก่อนแฮชเป็นไบต์ทึบ โดยมีขนาดคงที่และมี รหัสของคีย์ที่ใช้คำนวณ แต่เลย์เอาต์เป็นส่วนหนึ่งของรูปแบบการส่งผ่านข้อมูลของ Tink และคุณไม่ควรแยกวิเคราะห์หรือสร้างด้วยตนเอง หากคุณกำลังพอร์ต Tink หรือต้องการรายละเอียดระดับไบต์ โปรดดูรูปแบบการส่งผ่านข้อมูลของ Tink

ใช้คู่ดั้งเดิมนี้ในกรณีต่อไปนี้

  • คีย์การลงนามอยู่ในที่อื่น เช่น ใน HSM, ใน KMS หรืออยู่หลังขอบเขต RPC และคุณไม่ต้องการส่งข้อความทั้งหมด ข้ามขอบเขตนั้น
  • ข้อความมีขนาดใหญ่ และโปรแกรมลงนามระยะไกลบังคับใช้ขีดจำกัดขนาดคำขอ

หากไม่ตรงกับกรณีใดกรณีหนึ่ง ให้ใช้ Primitive Digital Signature แบบธรรมดาแทน ซึ่งจะง่ายกว่า และใช้งานในทางที่ผิดได้ยากกว่า

ชุดคีย์

Primitive ทั้ง 2 รายการเลือกคีย์ต่างกัน ในลักษณะเดียวกับการลงนามและการยืนยันสำหรับ Primitive Digital Signature

  • Prehash.Compute จะใช้คีย์หลักของชุดคีย์สาธารณะเสมอ และ บันทึกรหัสของคีย์นั้นในค่าก่อนแฮช ซึ่งเป็นฝ่ายที่เลือก คีย์ที่จะใช้สร้างลายเซ็น
  • SignPrehash.Sign อ่านรหัสคีย์จากค่าก่อนแฮชและลงนามด้วยคีย์ที่เปิดใช้ที่ตรงกันของชุดคีย์ส่วนตัว ซึ่งเป็นด้านที่ทำตามตัวเลือกที่ผู้อื่นเลือกไว้แล้ว หากไม่มีคีย์ที่เปิดใช้ใน ชุดคีย์ที่มีรหัสดังกล่าว การเรียกจะล้มเหลว

คีย์ทุกรายการในชุดคีย์ SignPrehash ต้องมีข้อกำหนดรหัส ไม่เช่นนั้นการสร้าง Primitive จะล้มเหลว

การรับประกันความปลอดภัยขั้นต่ำ

  • ลายเซ็นที่ได้จะมีคุณสมบัติเหมือนกับลายเซ็นที่สร้างโดย องค์ประกอบพื้นฐานลายเซ็นดิจิทัลที่มีคีย์ประเภทเดียวกัน
  • Tink จะนำหน้าค่าก่อนแฮชด้วย 5 ไบต์ที่มีค่าพิเศษที่สงวนไว้ และรหัสของคีย์ที่ใช้คำนวณ SignPrehash จะลงนามใน ค่าด้วยคีย์นั้นเท่านั้น ค่าจะผูกกับคีย์โดยใช้การเข้ารหัสหรือไม่นั้นขึ้นอยู่กับอัลกอริทึม สำหรับ External Mu ML-DSA จะผูกกัน ดูรูปแบบการเชื่อมต่อ Tink
  • ข้อความมีความยาวเท่าใดก็ได้

สิ่งที่ต้องระวัง

  • ผู้ลงนามตรวจสอบสิ่งที่ตนลงนามไม่ได้ ทุกคนที่โทรหา SignPrehash ได้จะรับข้อความที่ลงนามโดยพลการได้ และผู้ลงนามจะไม่มีวิธีใช้ นโยบายกับเนื้อหาของข้อความ ปกป้องสิทธิ์เข้าถึง SignPrehash เหมือนกับที่คุณปกป้องสิทธิ์เข้าถึง PublicKeySign
  • ปกป้องค่าก่อนแฮชระหว่างการส่ง แม้จะไม่ใช่ข้อมูลลับ แต่ผู้โจมตี ที่แก้ไขข้อมูลนี้ในระหว่างการส่งได้จะควบคุมสิ่งที่ได้รับการลงนาม

เลือกประเภทคีย์

ML-DSA ในโหมด External Mu ตามที่อธิบายไว้ใน RFC 9881 เป็นอัลกอริทึมเดียวที่ Tink รองรับสำหรับ Prehash และ SignPrehash คุณควรใช้คีย์ ML-DSA มาตรฐานกับ Primitive เหล่านี้

เราขอแนะนำให้ใช้ ML_DSA_65 สำหรับกรณีการใช้งานส่วนใหญ่

คีย์ต้องมีข้อกำหนดรหัส เนื่องจากค่าก่อนแฮชทุกค่าจะขึ้นต้นด้วยคำนำหน้าที่มีรหัสของคีย์ที่ใช้คำนวณ เรายอมรับรูปแบบต่อไปนี้

  • TINK -- ลายเซ็นที่ได้จะขึ้นต้นด้วยเอาต์พุต 5 ไบต์ปกติของ Tink
  • NO_PREFIX_WITH_PREHASH_ID -- ลายเซ็นที่ได้จะไม่มีคำนำหน้าเอาต์พุต ในขณะที่คีย์ยังคงมีรหัสที่ค่าก่อนแฮชต้องการ

ไม่รองรับคีย์ที่ใช้ตัวแปร NO_PREFIX (ดิบ) เนื่องจากไม่มีรหัสคีย์

ตัวอย่าง

ตัวอย่างต่อไปนี้จะลงนามข้อความใน 2 ขั้นตอน จากนั้นจะยืนยัน ลายเซ็นที่ได้ด้วย Primitive Digital Signature ทั่วไป

โดยจะแสดงเป็นบล็อกเดียวเพื่อให้ง่ายต่อการอ่าน ในการติดตั้งใช้งานจริง ขั้นตอนทั้ง 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
}