ฉันต้องการลงนามข้อมูลด้วยโปรแกรมลงนามระยะไกล

เราขอแนะนำให้ใช้ Primitive Prehash และ SignPrehash กับคีย์ ML_DSA_65 เมื่อ คีย์ส่วนตัวอยู่ในที่ที่ส่งข้อความไม่ได้

บางครั้งฝ่ายที่ถือคีย์การลงนามอาจรับข้อความเองไม่ได้ หรือไม่ควรรับข้อความเอง เนื่องจากคีย์อยู่ใน HSM หรือ KMS หรือข้อความมีขนาดใหญ่กว่าขีดจำกัดขนาดคำขอของผู้ลงนาม

Primitive Prehash และ SignPrehash แก้ปัญหานี้ด้วยการแบ่งการลงนามออกเป็น 2 ขั้นตอน คุณจะคำนวณค่าแฮชก่อนหน้าแบบย่อในตำแหน่งที่มีข้อความ โดยใช้เฉพาะคีย์สาธารณะ และส่งไปยังผู้ลงนาม ผู้ลงนามจะเปลี่ยนเป็นลายเซ็น โดยใช้คีย์ส่วนตัวโดยที่ไม่เห็นข้อความ เมื่อใช้ ML-DSA ในโหมด External Mu ซึ่งเป็นอัลกอริทึมที่ Tink รองรับ ค่าก่อนแฮชจะมีขนาด 69 ไบต์ ไม่ว่าข้อความจะมีขนาดเท่าใดก็ตาม

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

ก่อนเริ่มต้น

สร้างชุดคีย์ ML-DSA ที่คีย์มีข้อกำหนดรหัส ไม่ว่าจะเป็นตัวแปร TINK หรือ NO_PREFIX_WITH_PREHASH_ID หากไม่ต้องการคำนำหน้าเอาต์พุต ในลายเซ็นที่ได้ มอบชุดคีย์ส่วนตัวให้ผู้ลงนามและมอบชุดคีย์สาธารณะที่เกี่ยวข้องให้ฝั่ง การแฮชล่วงหน้า ผู้ลงนามควรมีคีย์ที่เปิดใช้สำหรับรหัสคีย์ทุกรายการที่ฝั่งการแฮชล่วงหน้าสร้างได้ ดูชุดคีย์

ขั้นตอนที่ 1: คำนวณค่าก่อนแฮช

เรียกใช้คำสั่งนี้ในตำแหน่งที่มีข้อความ โดยใช้เพียงชุดคีย์สาธารณะ

C++

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

Go

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
}

ขั้นตอนที่ 2: ส่งค่าก่อนแฮชไปยังผู้ลงนาม

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

ขั้นตอนที่ 3: ลงนามค่าก่อนแฮช

เรียกใช้คำสั่งนี้ในตำแหน่งที่มีคีย์ส่วนตัว

C++

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

Go

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
}

ขั้นตอนที่ 4: ยืนยันลายเซ็น

การยืนยันคือขั้นตอนลายเซ็นดิจิทัลปกติ ในข้อความเดิม ไม่ใช่ค่าก่อนแฮช

C++

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

Go

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

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 (ดิบ) เนื่องจากไม่มีรหัสคีย์