1use bytemuck::bytes_of;
6use bytemuck_derive::{Pod, Zeroable};
7use ed25519_dalek1::{ed25519::signature::Signature, Signer, Verifier};
8use rialo_s_feature_set::{ed25519_precompile_verify_strict, FeatureSet};
9use rialo_s_instruction::Instruction;
10use rialo_s_precompile_error::PrecompileError;
11
12pub const PUBKEY_SERIALIZED_SIZE: usize = 32;
13pub const SIGNATURE_SERIALIZED_SIZE: usize = 64;
14pub const SIGNATURE_OFFSETS_SERIALIZED_SIZE: usize = 14;
15pub const SIGNATURE_OFFSETS_START: usize = 2;
17pub const DATA_START: usize = SIGNATURE_OFFSETS_SERIALIZED_SIZE + SIGNATURE_OFFSETS_START;
18
19#[derive(Default, Debug, Copy, Clone, Zeroable, Pod, Eq, PartialEq)]
20#[repr(C)]
21pub struct Ed25519SignatureOffsets {
22 signature_offset: u16, signature_instruction_index: u16, public_key_offset: u16, public_key_instruction_index: u16, message_data_offset: u16, message_data_size: u16, message_instruction_index: u16, }
30
31pub fn offsets_to_ed25519_instruction(offsets: &[Ed25519SignatureOffsets]) -> Instruction {
41 let mut instruction_data = Vec::with_capacity(
42 SIGNATURE_OFFSETS_START
43 .saturating_add(SIGNATURE_OFFSETS_SERIALIZED_SIZE.saturating_mul(offsets.len())),
44 );
45
46 let num_signatures = offsets.len() as u16;
47 instruction_data.extend_from_slice(&num_signatures.to_le_bytes());
48
49 for offsets in offsets {
50 instruction_data.extend_from_slice(bytes_of(offsets));
51 }
52
53 Instruction {
54 program_id: rialo_s_sdk_ids::ed25519_program::id(),
55 accounts: vec![],
56 data: instruction_data,
57 }
58}
59
60pub fn new_ed25519_instruction(keypair: &ed25519_dalek1::Keypair, message: &[u8]) -> Instruction {
61 let signature = keypair.sign(message).to_bytes();
62 let pubkey = keypair.public.to_bytes();
63
64 assert_eq!(pubkey.len(), PUBKEY_SERIALIZED_SIZE);
65 assert_eq!(signature.len(), SIGNATURE_SERIALIZED_SIZE);
66
67 let mut instruction_data = Vec::with_capacity(
68 DATA_START
69 .saturating_add(SIGNATURE_SERIALIZED_SIZE)
70 .saturating_add(PUBKEY_SERIALIZED_SIZE)
71 .saturating_add(message.len()),
72 );
73
74 let num_signatures: u8 = 1;
75 let public_key_offset = DATA_START;
76 let signature_offset = public_key_offset.saturating_add(PUBKEY_SERIALIZED_SIZE);
77 let message_data_offset = signature_offset.saturating_add(SIGNATURE_SERIALIZED_SIZE);
78
79 instruction_data.extend_from_slice(bytes_of(&[num_signatures, 0]));
81
82 let offsets = Ed25519SignatureOffsets {
83 signature_offset: signature_offset as u16,
84 signature_instruction_index: u16::MAX,
85 public_key_offset: public_key_offset as u16,
86 public_key_instruction_index: u16::MAX,
87 message_data_offset: message_data_offset as u16,
88 message_data_size: message.len() as u16,
89 message_instruction_index: u16::MAX,
90 };
91
92 instruction_data.extend_from_slice(bytes_of(&offsets));
93
94 debug_assert_eq!(instruction_data.len(), public_key_offset);
95
96 instruction_data.extend_from_slice(&pubkey);
97
98 debug_assert_eq!(instruction_data.len(), signature_offset);
99
100 instruction_data.extend_from_slice(&signature);
101
102 debug_assert_eq!(instruction_data.len(), message_data_offset);
103
104 instruction_data.extend_from_slice(message);
105
106 Instruction {
107 program_id: rialo_s_sdk_ids::ed25519_program::id(),
108 accounts: vec![],
109 data: instruction_data,
110 }
111}
112
113pub fn verify(
114 data: &[u8],
115 instruction_datas: &[&[u8]],
116 feature_set: &FeatureSet,
117) -> Result<(), PrecompileError> {
118 if data.len() < SIGNATURE_OFFSETS_START {
119 return Err(PrecompileError::InvalidInstructionDataSize);
120 }
121 let num_signatures = data[0] as usize;
122 if num_signatures == 0 && data.len() > SIGNATURE_OFFSETS_START {
123 return Err(PrecompileError::InvalidInstructionDataSize);
124 }
125 let expected_data_size = num_signatures
126 .saturating_mul(SIGNATURE_OFFSETS_SERIALIZED_SIZE)
127 .saturating_add(SIGNATURE_OFFSETS_START);
128 if data.len() < expected_data_size {
130 return Err(PrecompileError::InvalidInstructionDataSize);
131 }
132 for i in 0..num_signatures {
133 let start = i
134 .saturating_mul(SIGNATURE_OFFSETS_SERIALIZED_SIZE)
135 .saturating_add(SIGNATURE_OFFSETS_START);
136 let end = start.saturating_add(SIGNATURE_OFFSETS_SERIALIZED_SIZE);
137
138 let offsets: &Ed25519SignatureOffsets = bytemuck::try_from_bytes(&data[start..end])
140 .map_err(|_| PrecompileError::InvalidDataOffsets)?;
141
142 let signature = get_data_slice(
144 data,
145 instruction_datas,
146 offsets.signature_instruction_index,
147 offsets.signature_offset,
148 SIGNATURE_SERIALIZED_SIZE,
149 )?;
150
151 let signature =
152 Signature::from_bytes(signature).map_err(|_| PrecompileError::InvalidSignature)?;
153
154 let pubkey = get_data_slice(
156 data,
157 instruction_datas,
158 offsets.public_key_instruction_index,
159 offsets.public_key_offset,
160 PUBKEY_SERIALIZED_SIZE,
161 )?;
162
163 let publickey = ed25519_dalek1::PublicKey::from_bytes(pubkey)
164 .map_err(|_| PrecompileError::InvalidPublicKey)?;
165
166 let message = get_data_slice(
168 data,
169 instruction_datas,
170 offsets.message_instruction_index,
171 offsets.message_data_offset,
172 offsets.message_data_size as usize,
173 )?;
174
175 if feature_set.is_active(&ed25519_precompile_verify_strict::id()) {
176 publickey
177 .verify_strict(message, &signature)
178 .map_err(|_| PrecompileError::InvalidSignature)?;
179 } else {
180 publickey
181 .verify(message, &signature)
182 .map_err(|_| PrecompileError::InvalidSignature)?;
183 }
184 }
185 Ok(())
186}
187
188fn get_data_slice<'a>(
189 data: &'a [u8],
190 instruction_datas: &'a [&[u8]],
191 instruction_index: u16,
192 offset_start: u16,
193 size: usize,
194) -> Result<&'a [u8], PrecompileError> {
195 let instruction = if instruction_index == u16::MAX {
196 data
197 } else {
198 let signature_index = instruction_index as usize;
199 if signature_index >= instruction_datas.len() {
200 return Err(PrecompileError::InvalidDataOffsets);
201 }
202 instruction_datas[signature_index]
203 };
204
205 let start = offset_start as usize;
206 let end = start.saturating_add(size);
207 if end > instruction.len() {
208 return Err(PrecompileError::InvalidDataOffsets);
209 }
210
211 Ok(&instruction[start..end])
212}
213
214#[cfg(test)]
215pub mod test {
216 use ed25519_dalek1::Signer as EdSigner;
217 use hex;
218 use rand0_7::{thread_rng, Rng};
219 use rialo_s_feature_set::FeatureSet;
220 use rialo_s_keypair::Keypair;
221 use rialo_s_sdk::transaction::Transaction;
222 use rialo_s_signer::Signer;
223
224 use super::*;
225
226 pub fn new_ed25519_instruction_raw(
227 pubkey: &[u8],
228 signature: &[u8],
229 message: &[u8],
230 ) -> Instruction {
231 assert_eq!(pubkey.len(), PUBKEY_SERIALIZED_SIZE);
232 assert_eq!(signature.len(), SIGNATURE_SERIALIZED_SIZE);
233
234 let mut instruction_data = Vec::with_capacity(
235 DATA_START
236 .saturating_add(SIGNATURE_SERIALIZED_SIZE)
237 .saturating_add(PUBKEY_SERIALIZED_SIZE)
238 .saturating_add(message.len()),
239 );
240
241 let num_signatures: u8 = 1;
242 let public_key_offset = DATA_START;
243 let signature_offset = public_key_offset.saturating_add(PUBKEY_SERIALIZED_SIZE);
244 let message_data_offset = signature_offset.saturating_add(SIGNATURE_SERIALIZED_SIZE);
245
246 instruction_data.extend_from_slice(bytes_of(&[num_signatures, 0]));
248
249 let offsets = Ed25519SignatureOffsets {
250 signature_offset: signature_offset as u16,
251 signature_instruction_index: u16::MAX,
252 public_key_offset: public_key_offset as u16,
253 public_key_instruction_index: u16::MAX,
254 message_data_offset: message_data_offset as u16,
255 message_data_size: message.len() as u16,
256 message_instruction_index: u16::MAX,
257 };
258
259 instruction_data.extend_from_slice(bytes_of(&offsets));
260
261 debug_assert_eq!(instruction_data.len(), public_key_offset);
262
263 instruction_data.extend_from_slice(pubkey);
264
265 debug_assert_eq!(instruction_data.len(), signature_offset);
266
267 instruction_data.extend_from_slice(signature);
268
269 debug_assert_eq!(instruction_data.len(), message_data_offset);
270
271 instruction_data.extend_from_slice(message);
272
273 Instruction {
274 program_id: rialo_s_sdk_ids::ed25519_program::id(),
275 accounts: vec![],
276 data: instruction_data,
277 }
278 }
279
280 fn test_case(
281 num_signatures: u16,
282 offsets: &Ed25519SignatureOffsets,
283 ) -> Result<(), PrecompileError> {
284 assert_eq!(
285 bytemuck::bytes_of(offsets).len(),
286 SIGNATURE_OFFSETS_SERIALIZED_SIZE
287 );
288
289 let mut instruction_data = vec![0u8; DATA_START];
290 instruction_data[0..SIGNATURE_OFFSETS_START].copy_from_slice(bytes_of(&num_signatures));
291 instruction_data[SIGNATURE_OFFSETS_START..DATA_START].copy_from_slice(bytes_of(offsets));
292
293 verify(
294 &instruction_data,
295 &[&[0u8; 100]],
296 &FeatureSet::all_enabled(),
297 )
298 }
299
300 #[test]
301 fn test_invalid_offsets() {
302 rialo_s_logger::setup();
303
304 let mut instruction_data = vec![0u8; DATA_START];
305 let offsets = Ed25519SignatureOffsets::default();
306 instruction_data[0..SIGNATURE_OFFSETS_START].copy_from_slice(bytes_of(&1u16));
307 instruction_data[SIGNATURE_OFFSETS_START..DATA_START].copy_from_slice(bytes_of(&offsets));
308 instruction_data.truncate(instruction_data.len() - 1);
309
310 assert_eq!(
311 verify(
312 &instruction_data,
313 &[&[0u8; 100]],
314 &FeatureSet::all_enabled(),
315 ),
316 Err(PrecompileError::InvalidInstructionDataSize)
317 );
318
319 let offsets = Ed25519SignatureOffsets {
320 signature_instruction_index: 1,
321 ..Ed25519SignatureOffsets::default()
322 };
323 assert_eq!(
324 test_case(1, &offsets),
325 Err(PrecompileError::InvalidDataOffsets)
326 );
327
328 let offsets = Ed25519SignatureOffsets {
329 message_instruction_index: 1,
330 ..Ed25519SignatureOffsets::default()
331 };
332 assert_eq!(
333 test_case(1, &offsets),
334 Err(PrecompileError::InvalidDataOffsets)
335 );
336
337 let offsets = Ed25519SignatureOffsets {
338 public_key_instruction_index: 1,
339 ..Ed25519SignatureOffsets::default()
340 };
341 assert_eq!(
342 test_case(1, &offsets),
343 Err(PrecompileError::InvalidDataOffsets)
344 );
345 }
346
347 #[test]
348 fn test_message_data_offsets() {
349 let offsets = Ed25519SignatureOffsets {
350 message_data_offset: 99,
351 message_data_size: 1,
352 ..Ed25519SignatureOffsets::default()
353 };
354 assert_eq!(
355 test_case(1, &offsets),
356 Err(PrecompileError::InvalidSignature)
357 );
358
359 let offsets = Ed25519SignatureOffsets {
360 message_data_offset: 100,
361 message_data_size: 1,
362 ..Ed25519SignatureOffsets::default()
363 };
364 assert_eq!(
365 test_case(1, &offsets),
366 Err(PrecompileError::InvalidDataOffsets)
367 );
368
369 let offsets = Ed25519SignatureOffsets {
370 message_data_offset: 100,
371 message_data_size: 1000,
372 ..Ed25519SignatureOffsets::default()
373 };
374 assert_eq!(
375 test_case(1, &offsets),
376 Err(PrecompileError::InvalidDataOffsets)
377 );
378
379 let offsets = Ed25519SignatureOffsets {
380 message_data_offset: u16::MAX,
381 message_data_size: u16::MAX,
382 ..Ed25519SignatureOffsets::default()
383 };
384 assert_eq!(
385 test_case(1, &offsets),
386 Err(PrecompileError::InvalidDataOffsets)
387 );
388 }
389
390 #[test]
391 fn test_pubkey_offset() {
392 let offsets = Ed25519SignatureOffsets {
393 public_key_offset: u16::MAX,
394 ..Ed25519SignatureOffsets::default()
395 };
396 assert_eq!(
397 test_case(1, &offsets),
398 Err(PrecompileError::InvalidDataOffsets)
399 );
400
401 let offsets = Ed25519SignatureOffsets {
402 public_key_offset: 100 - PUBKEY_SERIALIZED_SIZE as u16 + 1,
403 ..Ed25519SignatureOffsets::default()
404 };
405 assert_eq!(
406 test_case(1, &offsets),
407 Err(PrecompileError::InvalidDataOffsets)
408 );
409 }
410
411 #[test]
412 fn test_signature_offset() {
413 let offsets = Ed25519SignatureOffsets {
414 signature_offset: u16::MAX,
415 ..Ed25519SignatureOffsets::default()
416 };
417 assert_eq!(
418 test_case(1, &offsets),
419 Err(PrecompileError::InvalidDataOffsets)
420 );
421
422 let offsets = Ed25519SignatureOffsets {
423 signature_offset: 100 - SIGNATURE_SERIALIZED_SIZE as u16 + 1,
424 ..Ed25519SignatureOffsets::default()
425 };
426 assert_eq!(
427 test_case(1, &offsets),
428 Err(PrecompileError::InvalidDataOffsets)
429 );
430 }
431
432 #[test]
433 fn test_ed25519() {
434 rialo_s_logger::setup();
435
436 let privkey = ed25519_dalek1::Keypair::generate(&mut thread_rng());
437 let message_arr = b"hello";
438 let mut instruction = new_ed25519_instruction(&privkey, message_arr);
439 let mint_keypair = Keypair::new();
440 let feature_set = FeatureSet::all_enabled();
441 let valid_from = 0;
442
443 let tx = Transaction::new_signed_with_payer(
444 &[instruction.clone()],
445 Some(&mint_keypair.pubkey()),
446 &[&mint_keypair],
447 valid_from,
448 );
449
450 assert!(tx.verify_precompiles(&feature_set).is_ok());
451
452 let index = loop {
453 let index = thread_rng().gen_range(0, instruction.data.len());
454 if index != 1 {
456 break index;
457 }
458 };
459
460 instruction.data[index] = instruction.data[index].wrapping_add(12);
461 let tx = Transaction::new_signed_with_payer(
462 &[instruction],
463 Some(&mint_keypair.pubkey()),
464 &[&mint_keypair],
465 valid_from,
466 );
467 assert!(tx.verify_precompiles(&feature_set).is_err());
468 }
469
470 #[test]
471 fn test_offsets_to_ed25519_instruction() {
472 rialo_s_logger::setup();
473
474 let privkey = ed25519_dalek1::Keypair::generate(&mut thread_rng());
475 let messages: [&[u8]; 3] = [b"hello", b"IBRL", b"goodbye"];
476 let data_start =
477 messages.len() * SIGNATURE_OFFSETS_SERIALIZED_SIZE + SIGNATURE_OFFSETS_START;
478 let mut data_offset = data_start + PUBKEY_SERIALIZED_SIZE;
479 let (offsets, messages): (Vec<_>, Vec<_>) = messages
480 .into_iter()
481 .map(|message| {
482 let signature_offset = data_offset;
483 let message_data_offset = signature_offset + SIGNATURE_SERIALIZED_SIZE;
484 data_offset += SIGNATURE_SERIALIZED_SIZE + message.len();
485
486 let offsets = Ed25519SignatureOffsets {
487 signature_offset: signature_offset as u16,
488 signature_instruction_index: u16::MAX,
489 public_key_offset: data_start as u16,
490 public_key_instruction_index: u16::MAX,
491 message_data_offset: message_data_offset as u16,
492 message_data_size: message.len() as u16,
493 message_instruction_index: u16::MAX,
494 };
495
496 (offsets, message)
497 })
498 .unzip();
499
500 let mut instruction = offsets_to_ed25519_instruction(&offsets);
501
502 let pubkey = privkey.public.as_ref();
503 instruction.data.extend_from_slice(pubkey);
504
505 for message in messages {
506 let signature = privkey.sign(message).to_bytes();
507 instruction.data.extend_from_slice(&signature);
508 instruction.data.extend_from_slice(message);
509 }
510
511 let mint_keypair = Keypair::new();
512 let feature_set = FeatureSet::all_enabled();
513 let valid_from = 0;
514
515 let tx = Transaction::new_signed_with_payer(
516 &[instruction.clone()],
517 Some(&mint_keypair.pubkey()),
518 &[&mint_keypair],
519 valid_from,
520 );
521
522 assert!(tx.verify_precompiles(&feature_set).is_ok());
523
524 let index = loop {
525 let index = thread_rng().gen_range(0, instruction.data.len());
526 if index != 1 {
528 break index;
529 }
530 };
531
532 instruction.data[index] = instruction.data[index].wrapping_add(12);
533 let tx = Transaction::new_signed_with_payer(
534 &[instruction],
535 Some(&mint_keypair.pubkey()),
536 &[&mint_keypair],
537 valid_from,
538 );
539 assert!(tx.verify_precompiles(&feature_set).is_err());
540 }
541
542 #[test]
543 fn test_ed25519_malleability() {
544 rialo_s_logger::setup();
545 let mint_keypair = Keypair::new();
546
547 let privkey = ed25519_dalek1::Keypair::generate(&mut thread_rng());
549 let message_arr = b"hello";
550 let valid_from = 0;
551 let instruction = new_ed25519_instruction(&privkey, message_arr);
552 let tx = Transaction::new_signed_with_payer(
553 std::slice::from_ref(&instruction),
554 Some(&mint_keypair.pubkey()),
555 &[&mint_keypair],
556 valid_from,
557 );
558
559 let feature_set = FeatureSet::default();
560 assert!(tx.verify_precompiles(&feature_set).is_ok());
561
562 let feature_set = FeatureSet::all_enabled();
563 assert!(tx.verify_precompiles(&feature_set).is_ok());
564
565 let pubkey =
570 &hex::decode("10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f")
571 .unwrap();
572 let signature = &hex::decode("00000000000000000000000000000000000000000000000000000000000000009472a69cd9a701a50d130ed52189e2455b23767db52cacb8716fb896ffeeac09").unwrap();
573 let message = b"ed25519vectors 3";
574 let valid_from = 0;
575 let instruction = new_ed25519_instruction_raw(pubkey, signature, message);
576 let tx = Transaction::new_signed_with_payer(
577 std::slice::from_ref(&instruction),
578 Some(&mint_keypair.pubkey()),
579 &[&mint_keypair],
580 valid_from,
581 );
582
583 let feature_set = FeatureSet::default();
584 assert!(tx.verify_precompiles(&feature_set).is_ok());
585
586 let feature_set = FeatureSet::all_enabled();
587 assert!(tx.verify_precompiles(&feature_set).is_err()); }
589}