Saya ingin menandatangani data dengan penanda tangan jarak jauh

Sebaiknya gunakan primitif Prehash dan SignPrehash dengan kunci ML_DSA_65 saat kunci pribadi berada di tempat pesan tidak dapat dikirim.

Terkadang pihak yang memegang kunci penandatanganan tidak dapat -- atau tidak boleh -- menerima pesan itu sendiri: kunci berada di HSM atau KMS, atau pesan lebih besar daripada batas ukuran permintaan penanda tangan.

Primitif Prehash dan SignPrehash memecahkan masalah ini dengan membagi penandatanganan menjadi dua langkah. Anda menghitung nilai pra-hash singkat tempat pesan berada, hanya menggunakan kunci publik, dan mengirimkannya ke penanda tangan. Penanda mengubahnya menjadi tanda tangan menggunakan kunci pribadi, tanpa pernah melihat pesan. Dengan ML-DSA dalam mode Mu Eksternal -- algoritma yang didukung Tink di sini -- nilai pra-hash adalah 69 byte, berapa pun ukuran pesannya.

Tanda tangan yang Anda dapatkan kembali adalah tanda tangan biasa pada pesan asli: verifikator menggunakan primitif Tanda Tangan Digital biasa dan tidak perlu mengetahui bahwa ada dua langkah yang terlibat.

Sebelum memulai

Buat set kunci ML-DSA yang kuncinya memiliki persyaratan ID -- baik varian TINK, atau NO_PREFIX_WITH_PREHASH_ID jika Anda tidak menginginkan awalan output pada tanda tangan yang dihasilkan. Berikan set kunci pribadi kepada penanda tangan dan set kunci publik yang sesuai kepada sisi pra-hashing. Penandatangan harus memiliki kunci yang diaktifkan untuk setiap ID kunci yang dapat dihasilkan oleh sisi pra-hashing; lihat Kumpulan Kunci.

Langkah 1: Hitung nilai pra-hash

Jalankan ini di mana pun pesan berada. Hanya memerlukan set kunci publik.

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
}

Langkah 2: Kirimkan nilai pra-hash ke penanda tangan

Kirim nilai pra-hash ke apa pun yang menyimpan kunci pribadi. Kunci ini tidak rahasia, tetapi Anda harus melindungi integritasnya dalam pengiriman: penyerang yang dapat mengubahnya dalam pengiriman akan mengontrol apa yang ditandatangani.

Langkah 3: Tandatangani nilai pra-hash

Jalankan ini di mana pun kunci pribadi berada.

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
}

Langkah 4: Verifikasi tanda tangan

Verifikasi adalah alur Tanda Tangan Digital biasa melalui pesan asli, bukan melalui nilai pra-hash.

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 dan SignPrehash

Primitif Prehash dan SignPrehash membagi komputasi tanda tangan digital menjadi dua langkah:

  1. Prehash hanya memerlukan kunci publik. Fungsi ini mengubah pesan dengan panjang arbitrer menjadi nilai pra-hash berukuran tetap yang pendek.
  2. SignPrehash memerlukan kunci pribadi. Mengubah nilai pra-hash menjadi tanda tangan.

Tanda tangan yang dihasilkan adalah tanda tangan biasa pada pesan asli. Anda memverifikasinya dengan primitif Tanda Tangan Digital PublicKeyVerify biasa, dan pemverifikasi tidak perlu mengetahui -- atau peduli -- bahwa tanda tangan dibuat dalam dua langkah.

Perlakukan nilai pra-hash sebagai byte buram. Objek ini memiliki ukuran tetap dan membawa ID kunci yang dihitungnya, tetapi tata letaknya adalah bagian dari format wire Tink, dan Anda tidak boleh mengurai atau membuat objek ini sendiri. Jika Anda mentransfer Tink atau memerlukan detail tingkat byte, lihat format wire Tink.

Gunakan pasangan primitif ini saat:

  • Kunci penandatanganan berada di tempat lain, misalnya di HSM, di KMS, atau di belakang batas RPC, dan Anda tidak ingin mengirim pesan lengkap melintasi batas tersebut.
  • Pesan berukuran besar, dan penanda tangan jarak jauh menerapkan batas ukuran permintaan.

Jika tidak ada yang berlaku, gunakan primitif Tanda Tangan Digital biasa: lebih sederhana, dan lebih sulit disalahgunakan.

Keyset

Kedua primitif memilih kunci secara berbeda, dengan cara yang sama seperti yang dilakukan penandatanganan dan verifikasi untuk primitif Tanda Tangan Digital:

  • Prehash.Compute selalu menggunakan kunci utama dari set kunci publik, dan mencatat ID kunci tersebut dalam nilai pra-hash. Sisi inilah yang memilih kunci mana yang akan digunakan untuk membuat tanda tangan.
  • SignPrehash.Sign membaca ID kunci dari nilai pra-hash dan menandatangani dengan kunci yang diaktifkan yang cocok dari set kunci pribadi. Sisi ini adalah sisi yang mengikuti pilihan yang telah dibuat orang lain. Jika tidak ada kunci yang diaktifkan dalam keyset yang memiliki ID tersebut, panggilan akan gagal.

Setiap kunci dalam set kunci SignPrehash harus memiliki persyaratan ID, jika tidak, pembuatan primitif akan gagal.

Jaminan keamanan minimal

  • Tanda tangan yang dihasilkan memiliki properti yang sama dengan tanda tangan yang dihasilkan oleh primitif Tanda Tangan Digital dengan jenis kunci yang sama.
  • Tink menambahkan awalan pada nilai pra-hash dengan 5 byte yang berisi nilai khusus yang dicadangkan dan ID kunci yang digunakan untuk menghitungnya. SignPrehash menandatangani nilai hanya dengan kunci tersebut. Apakah nilai terikat secara kriptografis ke kunci tersebut bergantung pada algoritma; untuk ML-DSA Mu Eksternal, nilai terikat secara kriptografis, lihat format kabel Tink.
  • Pesan dapat memiliki panjang yang bervariasi.

Hal yang perlu diperhatikan

  • Penandatangan tidak dapat memeriksa apa yang ditandatanganinya. Siapa pun yang dapat memanggil SignPrehash dapat menandatangani pesan arbitrer, dan penandatangan tidak dapat menerapkan kebijakan ke isi pesan. Lindungi akses ke SignPrehash persis seperti Anda melindungi akses ke PublicKeySign.
  • Lindungi nilai pra-hash dalam pengiriman. Kunci ini tidak rahasia, tetapi penyerang yang dapat mengubahnya saat dalam perjalanan mengontrol apa yang ditandatangani.

Pilih jenis kunci

ML-DSA dalam mode External Mu, seperti yang dijelaskan dalam RFC 9881, adalah satu-satunya algoritma yang didukung Tink untuk Prehash dan SignPrehash. Anda harus menggunakan kunci ML-DSA standar dengan primitif ini.

Sebaiknya gunakan ML_DSA_65 untuk sebagian besar kasus penggunaan.

Kunci harus memiliki persyaratan ID, karena setiap nilai pra-hash dimulai dengan awalan yang berisi ID kunci yang dihitung. Varian berikut diterima:

  • TINK -- tanda tangan yang dihasilkan dimulai dengan awalan output 5 byte biasa Tink.
  • NO_PREFIX_WITH_PREHASH_ID -- tanda tangan yang dihasilkan tidak memiliki awalan output, sementara kunci masih memiliki ID yang diperlukan nilai pra-hash.

Kunci yang menggunakan varian NO_PREFIX (mentah) tidak didukung, karena tidak memiliki ID kunci.