আমি একজন রিমোট সাইনার দিয়ে ডেটা সাইন করতে চাই।

যখন প্রাইভেট কী এমন কোনো স্থানে থাকে যেখান থেকে মেসেজ পাঠানো সম্ভব নয়, তখন আমরা ML_DSA_65 কী- সহ Prehash এবং SignPrehash প্রিমিটিভগুলো ব্যবহারের পরামর্শ দিই।

কখনও কখনও যে পক্ষের কাছে স্বাক্ষর করার কী (signing key) থাকে, তারা বার্তাটি সরাসরি গ্রহণ করতে পারে না—অথবা তাদের তা করা উচিত নয়: কারণ কী-টি একটি HSM বা KMS-এ থাকে, অথবা বার্তাটি স্বাক্ষরকারীর অনুরোধের আকারের সীমার চেয়ে বড় হয়।

Prehash এবং SignPrehash প্রিমিটিভগুলো স্বাক্ষর করার প্রক্রিয়াকে দুটি ধাপে বিভক্ত করে এই সমস্যার সমাধান করে। আপনি শুধুমাত্র পাবলিক কী ব্যবহার করে মেসেজটির স্থানে একটি সংক্ষিপ্ত প্রিহ্যাশ ভ্যালু গণনা করেন এবং সেটি স্বাক্ষরকারীর কাছে পাঠান। স্বাক্ষরকারী মেসেজটি না দেখেই প্রাইভেট কী ব্যবহার করে সেটিকে একটি স্বাক্ষরে পরিণত করেন। এক্সটার্নাল মু মোডে ML-DSA-এর ক্ষেত্রে—যে অ্যালগরিদমটি টিঙ্ক এখানে সমর্থন করে—মেসেজের আকার যাই হোক না কেন, প্রিহ্যাশ ভ্যালুটি ৬৯ বাইটের হয়।

আপনি যে স্বাক্ষরটি ফেরত পান তা মূল বার্তার উপর একটি সাধারণ স্বাক্ষর: যাচাইকারীরা নিয়মিত ডিজিটাল স্বাক্ষর পদ্ধতি ব্যবহার করেন এবং এতে যে দুটি ধাপ জড়িত ছিল তা তাদের জানার প্রয়োজন হয় না।

শুরু করার আগে

একটি ML-DSA কীসেট তৈরি করুন যার কী-গুলোর জন্য একটি ID আবশ্যক — হয় TINK ভ্যারিয়েন্টটি, অথবা NO_PREFIX_WITH_PREHASH_ID যদি আপনি চূড়ান্ত সিগনেচারে কোনো আউটপুট প্রিফিক্স না চান। সাইনারকে প্রাইভেট কীসেট এবং প্রিহ্যাশিং সাইডকে সংশ্লিষ্ট পাবলিক কীসেটটি দিন। প্রিহ্যাশিং সাইড যতগুলো কী ID তৈরি করতে পারে, তার প্রত্যেকটির জন্য সাইনারের একটি সক্রিয় কী থাকা উচিত; কীসেটস দেখুন।

ধাপ ১: প্রিহ্যাশ মান গণনা করুন

যেখানে মেসেজটি আছে, সেখানেই এটি চালান। এর জন্য শুধু পাবলিক কীসেট প্রয়োজন।

সি++

#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. প্রিহ্যাশের জন্য শুধুমাত্র পাবলিক কী প্রয়োজন। এটি যেকোনো দৈর্ঘ্যের একটি মেসেজকে একটি সংক্ষিপ্ত, নির্দিষ্ট আকারের প্রিহ্যাশ ভ্যালুতে রূপান্তরিত করে।
  2. SignPrahash-এর জন্য প্রাইভেট কী প্রয়োজন। এটি একটি প্রিহ্যাশ ভ্যালুকে সিগনেচারে রূপান্তরিত করে।

যে সিগনেচারটি তৈরি হয়, তা মূল মেসেজের উপর করা একটি সাধারণ সিগনেচার। আপনি সাধারণ ডিজিটাল সিগনেচার PublicKeyVerify প্রিমিটিভ ব্যবহার করে এটি ভেরিফাই করেন, এবং ভেরিফায়ারদের এটা জানার বা এতে মাথা ঘামানোর প্রয়োজন নেই যে সিগনেচারটি দুটি ধাপে তৈরি করা হয়েছে।

প্রিহ্যাশ ভ্যালুটিকে অপাক বাইট হিসেবে বিবেচনা করুন। এর একটি নির্দিষ্ট আকার আছে এবং এটি যে কী-এর জন্য গণনা করা হয়েছে তার আইডি বহন করে, কিন্তু এর লেআউটটি টিঙ্ক-এর ওয়্যার ফরম্যাটের একটি অংশ, এবং আপনার নিজে থেকে এটি পার্স বা তৈরি করা উচিত নয়। আপনি যদি টিঙ্ক পোর্ট করেন বা বাইট-স্তরের বিশদ বিবরণের প্রয়োজন হয়, তবে টিঙ্ক ওয়্যার ফরম্যাট দেখুন।

এই জোড়া প্রিমিটিভ কখন ব্যবহার করবেন:

  • সাইনিং কী-টি অন্য কোথাও থাকে , যেমন একটি HSM-এ, একটি KMS-এ, বা একটি RPC বাউন্ডারির ​​পিছনে, এবং আপনি সেই বাউন্ডারির ​​ওপার থেকে সম্পূর্ণ মেসেজটি পাঠাতে চাইবেন না।
  • বার্তাটি বড় , এবং দূরবর্তী স্বাক্ষরকারী অনুরোধের আকারের উপর একটি সীমা আরোপ করে।

যদি এর কোনোটিই প্রযোজ্য না হয়, তাহলে এর পরিবর্তে সাধারণ ডিজিটাল সিগনেচার প্রিমিটিভটি ব্যবহার করুন: এটি আরও সরল এবং এর অপব্যবহার করাও কঠিন।

কীসেট

এই দুটি প্রিমিটিভ ভিন্নভাবে কী নির্বাচন করে, ঠিক যেমনভাবে ডিজিটাল সিগনেচার প্রিমিটিভের জন্য সাইনিং এবং ভেরিফিকেশন কাজ করে থাকে:

  • Prehash.Compute সর্বদা পাবলিক কীসেটের প্রাইমারি কী ব্যবহার করে এবং প্রিহ্যাশ ভ্যালুতে সেই কী-এর আইডি রেকর্ড করে। কোন কী দিয়ে সিগনেচার তৈরি করা হবে, তা এই পক্ষই নির্বাচন করে
  • SignPrehash.Sign প্রিহ্যাশ ভ্যালু থেকে কী আইডিটি পড়ে নেয় এবং প্রাইভেট কীসেটের সাথে মিলে যাওয়া সক্রিয় কী দিয়ে সাইন করে। এটি সেই অংশ যা অন্য কারো করা পছন্দকে অনুসরণ করে । যদি কীসেটের কোনো সক্রিয় কী-তে সেই আইডিটি না থাকে, তাহলে কলটি ব্যর্থ হয়।

একটি SignPreahash কীসেটের প্রতিটি কী-এর একটি আইডি থাকা আবশ্যক, অন্যথায় প্রিমিটিভটি তৈরি করা ব্যর্থ হয়।

ন্যূনতম নিরাপত্তা গ্যারান্টি

  • ফলস্বরূপ প্রাপ্ত স্বাক্ষরটির বৈশিষ্ট্যগুলো, একই কী টাইপ ব্যবহার করে ডিজিটাল সিগনেচার প্রিমিটিভ দ্বারা তৈরি স্বাক্ষরের বৈশিষ্ট্যের অনুরূপ হয়।
  • Tink প্রিহ্যাশ ভ্যালুর শুরুতে ৫ বাইট যোগ করে, যার মধ্যে একটি বিশেষ সংরক্ষিত ভ্যালু এবং যে কী-এর জন্য এটি গণনা করা হয়েছে তার আইডি থাকে। SignPrehash শুধুমাত্র সেই কী দিয়েই ভ্যালুটিকে সাইন করে। ভ্যালুটি ক্রিপ্টোগ্রাফিকভাবে সেই কী-এর সাথে আবদ্ধ থাকবে কিনা তা অ্যালগরিদমের উপর নির্ভর করে; External Mu ML-DSA-এর ক্ষেত্রে এটি আবদ্ধ থাকে, Tink wire format দেখুন।
  • বার্তার দৈর্ঘ্য ইচ্ছামতো হতে পারে।

যেসব বিষয়ে সতর্ক থাকতে হবে

  • স্বাক্ষরকারী যা স্বাক্ষর করে তা পরিদর্শন করতে পারে না। যে কেউ SignPrehash কল করতে পারলেই যেকোনো বার্তা স্বাক্ষর করিয়ে নিতে পারে, এবং স্বাক্ষরকারীর কাছে বার্তার বিষয়বস্তুর উপর কোনো নীতি প্রয়োগ করার উপায় থাকে না। SignPrehash এর অ্যাক্সেসকে ঠিক সেভাবেই সুরক্ষিত করুন, যেভাবে আপনি PublicKeySign এর অ্যাক্সেসকে সুরক্ষিত করেন।
  • স্থানান্তরের সময় প্রিহ্যাশ মানটি সুরক্ষিত রাখুন। এটি গোপনীয় নয়, কিন্তু একজন আক্রমণকারী যিনি স্থানান্তরের সময় এটি পরিবর্তন করতে পারেন, তিনি নিয়ন্ত্রণ করেন যে কী স্বাক্ষরিত হবে।

একটি কী-টাইপ বেছে নিন

RFC 9881- এ বর্ণিত এক্সটার্নাল মু মোডে থাকা ML-DSA-ই একমাত্র অ্যালগরিদম যা Tink, Prehash এবং SignPrehash-এর জন্য সমর্থন করে। এই প্রিমিটিভগুলোর সাথে আপনার একটি স্ট্যান্ডার্ড ML-DSA কী ব্যবহার করা উচিত।

অধিকাংশ ব্যবহারের ক্ষেত্রে আমরা ML_DSA_65 সুপারিশ করি।

কী-টির একটি আইডি থাকা আবশ্যক, কারণ প্রতিটি প্রিহ্যাশ ভ্যালু একটি প্রিফিক্স দিয়ে শুরু হয়, যেটিতে সেই কী-টির আইডি থাকে যার জন্য এটি গণনা করা হয়েছে। নিম্নলিখিত ভ্যারিয়েন্টগুলো গ্রহণযোগ্য:

  • TINK -- ফলস্বরূপ সিগনেচারটি Tink-এর সাধারণ ৫-বাইট আউটপুট প্রিফিক্স দিয়ে শুরু হয়।
  • NO_PREFIX_WITH_PREHASH_ID -- এর ফলে প্রাপ্ত সিগনেচারে কোনো আউটপুট প্রিফিক্স থাকে না, কিন্তু কী-টিতে সেই আইডিটি থাকে যা প্রিহ্যাশ ভ্যালুর জন্য প্রয়োজন।

NO_PREFIX (raw) ভ্যারিয়েন্ট ব্যবহার করা কীগুলো সমর্থিত নয়, কারণ সেগুলোর কোনো কী আইডি নেই।