Các chữ ký Winternitz với Pinocchio
Dean từ nhóm Blueshift đã phát hành crate đầu tiên cho phép tương thích với Pinocchio để tạo và xác minh các chữ ký Winternitz.
Triển khai này đặc biệt có giá trị để cung cấp bảo mật kháng lượng tử cho các ứng dụng blockchain.
Giới thiệu
Triển khai này sử dụng w = 8 để cân bằng các ràng buộc của Solana: giới hạn kích thước giao dịch và hạn chế đơn vị tính toán.
Tham số Winternitz w tạo ra một sự đánh đổi cơ bản giữa kích thước chữ ký và yêu cầu tính toán:
Các giá trị w cao hơn có nghĩa là chữ ký nhỏ hơn nhưng yêu cầu tính toán nhiều hơn vì xác minh đòi hỏi nhiều phép băm hơn cho mỗi thành phần chữ ký
Các giá trị w thấp hơn có nghĩa là chữ ký lớn hơn nhưng yêu cầu tính toán ít hơn vì xác minh đòi hỏi ít phép băm hơn cho mỗi thành phần chữ ký
Solana áp đặt hai ràng buộc quan trọng định hình việc lựa chọn tham số:
Ràng buộc kích thước giao dịch (1024 byte): Với
w = 8, một triển khai đầy đủ tạo ra chính xác chữ ký 1024 byte sử dụng băm 256 bit (32 byte × 32 thành phần). Điều này tiêu thụ toàn bộ không gian giao dịch, không để lại chỗ cho chi phí giao dịch và dữ liệu bổ sung.Ràng buộc đơn vị tính toán: Chuyển sang
w = 16sẽ giảm một nửa kích thước chữ ký nhưng sẽ vượt quá giới hạn đơn vị tính toán (CU) của Solana trong quá trình xác minh, vì mỗi thành phần chữ ký sẽ yêu cầu nhiều phép băm hơn đáng kể.
Vì các giới hạn đơn vị tính toán không thể được giải quyết thông qua việc điều chỉnh tham số, vấn đề kích thước chữ ký được giải quyết bằng cách cắt ngắn chữ ký xuống 896 byte và nhị phân hóa (merklizing) các thành phần còn lại. Cách tiếp cận này bảo tồn tính bảo mật trong khi tạo ra không gian cần thiết cho chi phí giao dịch.
Đây là lý do tại sao việc triển khai này quyết định chọn w = 8: nó đại diện cho một điểm hội tụ đẹp, nơi các yêu cầu tính toán vẫn có thể đảm bảo được trong khi việc cắt ngắn chữ ký cung cấp một giải pháp thực tế cho các ràng buộc kích thước.
Sinh khóa
Sinh ra một khóa riêng và suy diễn khóa công khai tương ứng của nó bằng cách sử dụng SDK:
use winternitz::{hash::WinternitzKeccak, privkey::WinternitzPrivkey};
// Generate a new random private key
let privkey = WinternitzPrivkey::generate();
// Derive the corresponding public key
let pubkey = privkey.pubkey::<WinternitzKeccak>();Khóa công khai được suy diễn bằng cách áp dụng hàm băm nhiều lần lên các thành phần khóa riêng, tạo ra một phép biến đổi một chiều đảm bảo tính bảo mật.
Ký thông điệp
// Sign a message
let message = b"Hello, World!";
let signature = privkey.sign::<WinternitzKeccak>(message);Quá trình ký sinh ra các thành phần chữ ký dựa trên thông điệp, với mỗi thành phần yêu cầu một số phép băm cụ thể được xác định bởi các bit tương ứng của thông điệp.
Xác minh chữ ký
// Recover public key from signature and message
let recovered_pubkey = signature.recover_pubkey::<WinternitzKeccak>(message);
// Verify by comparing public keys
assert_eq!(recovered_pubkey, pubkey);Việc xác minh tái tạo khóa công khai từ chữ ký và thông điệp, sau đó so sánh với khóa công khai mong đợi để xác nhận tính xác thực.
Triển khai
Để triển khai xác minh chữ ký Winternitz trong chương trình Pinocchio của bạn, bạn cần:
Crate
solana-winternitz: Cung cấp chức năng chữ ký WinternitzTạo và xác minh PDA bằng cách sử dụng phương pháp tạo địa chỉ an toàn với lượng tử
Hãy bắt đầu bằng cách thêm crate solana-winternitz
cargo add solana-winternitzTối ưu kích thước chữ ký
Triển khai sử dụng phương pháp cắt ngắn để phù hợp với các ràng buộc giao dịch của Solana:
Chữ ký đầy đủ (
WinternitzSignature): 1024 byte (32 byte × 32 thành phần).Chữ ký cắt ngắn (
WinternitzCommitmentSignature): 896 byte (32 byte × 28 thành phần).Không gian còn lại: 128 byte cho chi phí giao dịch
Việc cắt ngắn từ băm 256-bit xuống 224-bit vẫn duy trì tính bảo mật mạnh mẽ trong khi đảm bảo tính khả dụng thực tiễn. Các thành phần chữ ký còn lại được nhị phân hóa để bảo tồn mô hình bảo mật hoàn chỉnh.
Thiết lập PDA an toàn với lượng tử
Vì các chữ ký blockchain truyền thống vẫn dễ bị tấn công lượng tử, triển khai này tận dụng các PDA để đảm bảo an toàn trước lượng tử.
PDA không có khóa riêng liên kết, giúp chúng miễn nhiễm với các cuộc tấn công mật mã.
Dưới đây là cách tạo một PDA từ một khóa công khai Winternitz:
pub struct CreateWinternitzPDA {
pub hash: [u8; 32],
pub bump: [u8; 1],
}
impl CreateWinternitzPDA {
pub fn deserialize(bytes: &[u8]) -> Result<Self, ProgramError> {
let data: [u8; 33] = bytes
.try_into()
.map_err(|_| ProgramError::InvalidInstructionData)?;
let (hash, bump) = array_refs![&data, 32, 1];
Ok(Self {
hash: *hash,
bump: *bump,
})
}
pub fn create_pda(&self, accounts: &CreatePDAAccounts) -> ProgramResult {
let seeds = [Seed::from(&self.hash), Seed::from(&self.bump)];
let signers = [Signer::from(&seeds)];
// Create the quantum-secure PDA
CreateAccount {
from: accounts.payer,
to: accounts.vault,
lamports: accounts.lamports,
space: 0,
owner: &crate::ID,
}
.invoke_signed(&signers)
}
}Cốt lõi của việc xác minh Winternitz liên quan đến việc khôi phục khóa công khai từ chữ ký và thông điệp, sau đó xác minh nó khớp với PDA mong đợi. Đây là quy trình xác minh hoàn chỉnh:
pub struct VerifyWinternitzSignature {
pub signature: WinternitzSignature,
pub bump: [u8; 1],
}
impl VerifyWinternitzSignature {
pub fn deserialize(bytes: &[u8]) -> Result<Self, ProgramError> {
if bytes.len() != 897 {
return Err(ProgramError::InvalidInstructionData);
}
let (signature_bytes, bump) = bytes.split_at(896);
Ok(Self {
signature: WinternitzSignature::from(signature_bytes.try_into().unwrap()),
bump: [bump[0]],
})
}
pub fn verify_and_execute(&self, accounts: &VerifyAccounts, message: &[u8]) -> ProgramResult {
// Recover the public key from signature and message
let recovered_pubkey = self.signature.recover_pubkey(message);
let hash = recovered_pubkey.merklize();
// Verify PDA ownership
let expected_pda = solana_nostd_sha256::hashv(&[
hash.as_ref(),
self.bump.as_ref(),
crate::ID.as_ref(),
b"ProgramDerivedAddress",
]);
if expected_pda.ne(accounts.pda.key()) {
return Err(ProgramError::MissingRequiredSignature);
}
// Execute the protected operation
self.execute_protected_operation(accounts)
}
fn execute_protected_operation(&self, accounts: &VerifyAccounts) -> ProgramResult {
// Your quantum-secure operation logic here
Ok(())
}
}Hàm recover_pubkey() tái tạo khóa công khai bằng cách chuyển đổi thông điệp đã ký thành các giá trị băm chỉ định số lượng băm bổ sung mà mỗi thành phần cần và tạo ra 28 thành phần khóa công khai chỉ có thể được tạo ra bằng khóa riêng đúng.
Hàm merklize() sau đó xây dựng một cây nhị phân từ 28 thành phần khóa công khai, tạo ra một gốc 32 byte duy nhất đại diện cho tất cả 28 thành phần
Các cân nhắc về bảo mật
Luôn luôn đính kèm các tham số quan trọng trong thông điệp đã ký để ngăn chặn việc thao túng:
// Construct message with security parameters
let message = [
accounts.recipient.key().as_ref(), // Prevent recipient substitution
&amount.to_le_bytes(), // Prevent amount manipulation
&expiry_timestamp.to_le_bytes(), // Prevent replay attacks
].concat();Kiểm tra thời hạn
Vì các chữ ký Winternitz vẫn có hiệu lực vô thời hạn, hãy triển khai dựa trên thời gian:
// Verify signature hasn't expired
let now = Clock::get()?.unix_timestamp;
let expiry = i64::from_le_bytes(
message[40..48].try_into()
.map_err(|_| ProgramError::InvalidInstructionData)?
);
if now > expiry {
return Err(ProgramError::InvalidInstructionData);
}Kiểm tra khóa công khai
Đảm bảo rằng chỉ có các bên được ủy quyền mới có thể hưởng lợi từ chữ ký:
// Verify the recipient is authorized
let intended_recipient = &message[0..32];
if accounts.recipient.key().as_ref().ne(intended_recipient) {
return Err(ProgramError::InvalidAccountOwner);
}