1use rhdl::prelude::*;
63use rhdl_fpga::core::dff::DFF;
64use rhdl_fpga::fifo::synchronous::{In as FifoIn, SyncFIFO};
65
66use crate::export::SelectedSigner;
67use crate::sha_ring::{ShaDone, ShaRingOut};
68use crate::sign_core::{ScalableSignIn, ScalableSignOut};
69use crate::wire::{ScalarWords, SignRequest, Words512};
70
71pub const MAGIC: u32 = 0x3144_4345;
73pub const BURST: u32 = 16;
75pub const MSG_FIFO_BITS: usize = 6;
77pub const SIG_FIFO_BITS: usize = 11;
79
80#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
82pub struct AxiRead {
83 pub ar_valid: bool,
85 pub ar_addr: b64,
87 pub ar_len: b8,
89 pub r_ready: bool,
91}
92
93#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
95pub struct AxiReadResp {
96 pub ar_ready: bool,
98 pub r_valid: bool,
100 pub r_data: [b64; 8],
102 pub r_resp: b2,
104 pub r_last: bool,
106}
107
108#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
110pub struct AxiWrite {
111 pub aw_valid: bool,
113 pub aw_addr: b64,
115 pub w_valid: bool,
117 pub w_data: [b64; 8],
119 pub b_ready: bool,
121}
122
123#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
125pub struct AxiWriteResp {
126 pub aw_ready: bool,
128 pub w_ready: bool,
130 pub b_valid: bool,
132 pub b_resp: b2,
134}
135
136#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
138pub struct LiteIn {
139 pub aw_valid: bool,
141 pub aw_addr: b12,
143 pub w_valid: bool,
145 pub w_data: b32,
147 pub b_ready: bool,
149 pub ar_valid: bool,
151 pub ar_addr: b12,
153 pub r_ready: bool,
155}
156
157#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
159pub struct LiteOut {
160 pub aw_ready: bool,
162 pub w_ready: bool,
164 pub b_valid: bool,
166 pub ar_ready: bool,
168 pub r_valid: bool,
170 pub r_data: b32,
172 pub interrupt: bool,
174}
175
176#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
178pub struct CardIn {
179 pub ctl: LiteIn,
181 pub rd: AxiReadResp,
183 pub wr: AxiWriteResp,
185}
186
187#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
189pub struct CardOut {
190 pub ctl: LiteOut,
192 pub rd: AxiRead,
194 pub wr: AxiWrite,
196}
197
198#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
200pub struct SigEntry {
201 pub tag: b16,
203 pub sig: [b64; 8],
205}
206
207#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
209pub struct CardControl {
210 pub ticks: b64,
212 pub messages: b64,
214 pub signatures: b64,
216 pub count: b32,
218 pub seed: [b32; 8],
220 pub running: bool,
222 pub clearing: bool,
224 pub key_taken: bool,
226 pub key_ticked: bool,
228 pub done: bool,
230 pub finished: bool,
232 pub gie: bool,
234 pub ier: bool,
236 pub isr: bool,
238 pub start_tick: b64,
240 pub key_tick: b64,
242 pub done_tick: b64,
244 pub requested: b32,
246 pub received: b32,
248 pub admitted: b32,
250 pub completed: b32,
252 pub sent: b32,
254 pub acked: b32,
256 pub ar_pending: bool,
258 pub aw_done: bool,
260 pub w_done: bool,
262 pub writing: bool,
264 pub cur: SigEntry,
266 pub fault: b4,
268 pub aw_have: bool,
270 pub aw_addr: b12,
272 pub w_have: bool,
274 pub w_data: b32,
276 pub b_valid: bool,
278 pub r_valid: bool,
280 pub r_data: b32,
282 pub ticks_hi: b32,
284}
285
286#[derive(Clone, Debug, Synchronous, SynchronousDQ)]
288pub struct Ed25519Card {
289 control: DFF<CardControl>,
290 signer: SelectedSigner,
291 msg_fifo: SyncFIFO<[b64; 8], MSG_FIFO_BITS>,
292 sig_fifo: SyncFIFO<SigEntry, SIG_FIFO_BITS>,
293}
294
295impl Default for Ed25519Card {
296 fn default() -> Self {
297 Self {
298 control: DFF::new(CardControl::default()),
299 signer: SelectedSigner::default(),
300 msg_fifo: SyncFIFO::default(),
301 sig_fifo: SyncFIFO::default(),
302 }
303 }
304}
305
306impl SynchronousIO for Ed25519Card {
307 type I = CardIn;
308 type O = CardOut;
309 type Kernel = ed25519_card_kernel;
310}
311
312#[kernel]
314pub fn set_half(v: b64, w: b32, hi: bool) -> b64 {
315 if hi {
316 (v & b64(0xffff_ffff)) | (w.resize::<64>() << b64(32))
317 } else {
318 (v & (b64(0xffff_ffff) << b64(32))) | w.resize::<64>()
319 }
320}
321
322#[allow(clippy::too_many_lines)]
324#[kernel]
325pub fn ed25519_card_kernel(_cr: ClockReset, i: CardIn, q: Ed25519CardQ) -> (CardOut, Ed25519CardD) {
326 let r = q.control;
327 let mut c = r;
328 let sig: ScalableSignOut = q.signer;
329 c.ticks = r.ticks + b64(1);
330 let mut o = CardOut {
331 ctl: LiteOut { aw_ready: false, w_ready: false, b_valid: r.b_valid, ar_ready: false, r_valid: r.r_valid, r_data: r.r_data, interrupt: r.gie && r.ier && r.isr },
332 rd: AxiRead { ar_valid: false, ar_addr: b64(0), ar_len: b8(0), r_ready: false },
333 wr: AxiWrite { aw_valid: false, aw_addr: b64(0), w_valid: false, w_data: [b64(0); 8], b_ready: true },
334 };
335
336 o.ctl.aw_ready = !r.aw_have && !r.b_valid;
338 o.ctl.w_ready = !r.w_have && !r.b_valid;
339 if i.ctl.aw_valid && !r.aw_have && !r.b_valid {
340 c.aw_have = true;
341 c.aw_addr = i.ctl.aw_addr;
342 }
343 if i.ctl.w_valid && !r.w_have && !r.b_valid {
344 c.w_have = true;
345 c.w_data = i.ctl.w_data;
346 }
347 let idle = !r.running;
348 if r.aw_have && r.w_have {
349 let a = r.aw_addr;
350 let d = r.w_data;
351 c.aw_have = false;
352 c.w_have = false;
353 c.b_valid = true;
354 if a == b12(0x00) && (d & b32(1)) == b32(1) && idle {
355 let ok = r.count != b32(0)
357 && r.count <= b32(65_536)
358 && (r.messages & b64(0xfff)) == b64(0)
359 && (r.signatures & b64(0x3f)) == b64(0);
360 if ok {
361 c.running = true;
362 c.done = false;
363 c.start_tick = r.ticks;
364 c.clearing = true;
366 c.key_taken = false;
367 c.key_ticked = false;
368 c.requested = b32(0);
369 c.received = b32(0);
370 c.admitted = b32(0);
371 c.completed = b32(0);
372 c.sent = b32(0);
373 c.acked = b32(0);
374 c.fault = b4(0);
375 } else {
376 c.fault = b4(8);
377 c.done = true;
378 c.finished = true;
379 c.isr = true;
380 }
381 }
382 if a == b12(0x04) {
383 c.gie = (d & b32(1)) == b32(1);
384 }
385 if a == b12(0x08) {
386 c.ier = (d & b32(1)) == b32(1);
387 }
388 if a == b12(0x0c) && (d & b32(1)) == b32(1) {
389 c.isr = false;
390 }
391 if idle {
392 if a == b12(0x10) {
393 c.messages = set_half(r.messages, d, false);
394 }
395 if a == b12(0x14) {
396 c.messages = set_half(r.messages, d, true);
397 }
398 if a == b12(0x18) {
399 c.signatures = set_half(r.signatures, d, false);
400 }
401 if a == b12(0x1c) {
402 c.signatures = set_half(r.signatures, d, true);
403 }
404 if a == b12(0x20) {
405 c.count = d;
406 }
407 for w in 0..8 {
408 if a == b12((0x28 + 4 * w) as u128) {
409 c.seed[w] = d;
410 }
411 }
412 }
413 }
414 if r.b_valid && i.ctl.b_ready {
415 c.b_valid = false;
416 }
417
418 o.ctl.ar_ready = !r.r_valid;
420 if i.ctl.ar_valid && !r.r_valid {
421 let a = i.ctl.ar_addr;
422 let mut v = b32(0);
423 if a == b12(0x00) {
424 v = (if r.running { b32(1) } else { b32(0) })
425 | (if r.done { b32(2) } else { b32(0) })
426 | (if !r.running { b32(4) } else { b32(0) })
427 | (if r.done { b32(8) } else { b32(0) });
428 c.done = false;
429 }
430 if a == b12(0x04) {
431 v = if r.gie { b32(1) } else { b32(0) };
432 }
433 if a == b12(0x08) {
434 v = if r.ier { b32(1) } else { b32(0) };
435 }
436 if a == b12(0x0c) {
437 v = if r.isr { b32(1) } else { b32(0) };
438 }
439 if a == b12(0x100) {
440 v = b32(0x3144_4345);
441 }
442 if a == b12(0x104) {
443 v = r.acked;
444 }
445 if a == b12(0x108) {
446 v = r.fault.resize();
447 }
448 if a == b12(0x110) {
449 v = r.start_tick.resize();
450 }
451 if a == b12(0x114) {
452 v = (r.start_tick >> b64(32)).resize();
453 }
454 if a == b12(0x118) {
455 v = r.key_tick.resize();
456 }
457 if a == b12(0x11c) {
458 v = (r.key_tick >> b64(32)).resize();
459 }
460 if a == b12(0x120) {
461 v = r.done_tick.resize();
462 }
463 if a == b12(0x124) {
464 v = (r.done_tick >> b64(32)).resize();
465 }
466 if a == b12(0x128) {
467 v = r.ticks.resize();
468 c.ticks_hi = (r.ticks >> b64(32)).resize();
469 }
470 if a == b12(0x12c) {
471 v = r.ticks_hi;
472 }
473 for w in 0..4 {
474 let pk = sig.public_key.words[w];
475 if a == b12((0x140 + 8 * w) as u128) {
476 v = pk.resize();
477 }
478 if a == b12((0x144 + 8 * w) as u128) {
479 v = (pk >> b64(32)).resize();
480 }
481 }
482 c.r_valid = true;
483 c.r_data = v;
484 }
485 if r.r_valid && i.ctl.r_ready {
486 c.r_valid = false;
487 }
488
489 let mut seed_words = [b64(0); 4];
491 for w in 0..4 {
492 seed_words[w] = r.seed[2 * w].resize::<64>() | (r.seed[2 * w + 1].resize::<64>() << b64(32));
493 }
494 c.clearing = false;
495 let offer_key = r.running && !r.clearing && !r.key_taken && !sig.key_loaded;
496 if offer_key && sig.load_key_ready {
497 c.key_taken = true;
498 }
499 if r.running && sig.key_loaded && !r.key_ticked {
500 c.key_ticked = true;
501 c.key_tick = r.ticks;
502 }
503
504 let room = (r.requested - r.admitted) <= b32((64 - 16) as u128);
509 let left = r.count - r.requested;
510 let len = if left < b32(16) { left } else { b32(16) };
511 if r.running && !r.ar_pending && left != b32(0) && room {
512 o.rd.ar_valid = true;
513 o.rd.ar_addr = r.messages + (r.requested.resize::<64>() << b64(6));
514 o.rd.ar_len = (len - b32(1)).resize();
515 if i.rd.ar_ready {
516 c.requested = r.requested + len;
517 c.ar_pending = true;
518 }
519 }
520 let msg_full = q.msg_fifo.full;
521 o.rd.r_ready = !msg_full;
522 let take_beat = i.rd.r_valid && !msg_full;
523 if take_beat {
524 c.received = r.received + b32(1);
525 if i.rd.r_resp != b2(0) {
526 c.fault = c.fault | b4(2);
527 }
528 if i.rd.r_last {
529 c.ar_pending = false;
530 }
531 }
532
533 let in_flight = r.admitted - r.sent;
536 let sig_room = in_flight < b32(((1 << SIG_FIFO_BITS) - 2) as u128);
537 let mut head_msg = [b64(0); 8];
538 let mut have_msg = false;
539 match q.msg_fifo.data {
540 Some(m) => {
541 head_msg = m;
542 have_msg = true;
543 }
544 None => {}
545 }
546 let admit = r.running && r.key_ticked && have_msg && sig.ready && sig_room && r.admitted < r.count;
547 if admit {
548 c.admitted = r.admitted + b32(1);
549 }
550 if sig.valid {
551 c.completed = r.completed + b32(1);
552 }
553 if sig.internal_error {
554 c.fault = c.fault | b4(1);
555 }
556
557 let mut pop_sig = false;
559 if !r.writing {
560 match q.sig_fifo.data {
561 Some(e) => {
562 c.cur = e;
563 c.writing = true;
564 c.aw_done = false;
565 c.w_done = false;
566 pop_sig = true;
567 }
568 None => {}
569 }
570 }
571 if r.writing {
572 o.wr.aw_valid = !r.aw_done;
573 o.wr.aw_addr = r.signatures + (r.cur.tag.resize::<64>() << b64(6));
574 o.wr.w_valid = !r.w_done;
575 o.wr.w_data = r.cur.sig;
576 let aw = r.aw_done || i.wr.aw_ready;
577 let w = r.w_done || i.wr.w_ready;
578 c.aw_done = aw;
579 c.w_done = w;
580 if aw && w {
581 c.writing = false;
582 c.sent = r.sent + b32(1);
583 }
584 }
585 if i.wr.b_valid {
586 c.acked = r.acked + b32(1);
587 if i.wr.b_resp != b2(0) {
588 c.fault = c.fault | b4(4);
589 }
590 }
591
592 if r.running && r.acked == r.count && r.count != b32(0) {
594 c.running = false;
595 c.done = true;
596 c.finished = true;
597 c.isr = true;
598 c.done_tick = r.ticks;
599 }
600
601 let mut d = Ed25519CardD::dont_care();
602 d.control = c;
603 d.signer = ScalableSignIn {
604 load_key_valid: offer_key,
605 key_seed: ScalarWords { words: seed_words },
606 clear_key: r.clearing,
607 valid: admit,
608 request: SignRequest { message: Words512 { words: head_msg }, tag: r.admitted.resize() },
609 sha_ext: ShaRingOut { ready: false, done: ShaDone { valid: false, tag: b16(0), state: [b64(0); 8] } },
610 };
611 d.msg_fifo = FifoIn::<[b64; 8]> { data: if take_beat { Some(i.rd.r_data) } else { None }, next: admit };
612 d.sig_fifo = FifoIn::<SigEntry> {
613 data: if sig.valid { Some(SigEntry { tag: sig.tag, sig: sig.signature.words }) } else { None },
614 next: pop_sig,
615 };
616 (o, d)
617}
618
619#[cfg(test)]
620mod tests {
621 use super::*;
622 use crate::simulation::CycleSim;
623 use ed25519_dalek::{Signer, SigningKey};
624
625 fn lite_write(sim: &mut CycleSim<Ed25519Card>, mem: &mut Mem, addr: u32, data: u32) {
627 let mut ctl = LiteIn { aw_valid: true, aw_addr: b12(u128::from(addr)), w_valid: true, w_data: b32(u128::from(data)), b_ready: true, ..Default::default() };
628 for _ in 0..20 {
629 let o = step(sim, mem, ctl);
630 if o.ctl.aw_ready {
631 ctl.aw_valid = false;
632 }
633 if o.ctl.w_ready {
634 ctl.w_valid = false;
635 }
636 if o.ctl.b_valid && !ctl.aw_valid && !ctl.w_valid {
637 step(sim, mem, LiteIn::default());
638 return;
639 }
640 }
641 panic!("AXI-Lite write {addr:#x} stuck");
642 }
643
644 fn lite_read(sim: &mut CycleSim<Ed25519Card>, mem: &mut Mem, addr: u32) -> u32 {
645 let ctl = LiteIn { ar_valid: true, ar_addr: b12(u128::from(addr)), r_ready: true, ..Default::default() };
646 let o = step(sim, mem, ctl);
647 assert!(o.ctl.ar_ready);
648 let o = step(sim, mem, LiteIn { r_ready: true, ..Default::default() });
649 assert!(o.ctl.r_valid);
650 o.ctl.r_data.raw() as u32
651 }
652
653 struct Mem {
656 messages: Vec<[u8; 64]>,
657 signatures: Vec<Option<[u8; 64]>>,
658 msg_base: u64,
659 sig_base: u64,
660 reads: std::collections::VecDeque<(u64, u64, bool)>,
661 now: u64,
662 b_pending: u32,
663 aw: Option<u64>,
664 w: Option<[b64; 8]>,
665 }
666
667 fn step(sim: &mut CycleSim<Ed25519Card>, mem: &mut Mem, ctl: LiteIn) -> CardOut {
668 mem.now += 1;
669 let mut i = CardIn { ctl, ..Default::default() };
670 i.rd.ar_ready = true;
671 if let Some(&(t, addr, last)) = mem.reads.front() {
672 if t <= mem.now {
673 let idx = ((addr - mem.msg_base) / 64) as usize;
674 let m = mem.messages[idx];
675 i.rd.r_valid = true;
676 i.rd.r_data = std::array::from_fn(|k| b64(u128::from(u64::from_le_bytes(m[8 * k..8 * k + 8].try_into().unwrap()))));
677 i.rd.r_last = last;
678 }
679 }
680 let stall = mem.now % 3 == 0;
681 i.wr.aw_ready = !stall && mem.aw.is_none();
682 i.wr.w_ready = !stall && mem.w.is_none();
683 i.wr.b_valid = mem.b_pending > 0;
684 let o = sim.peek(i);
685 if o.rd.ar_valid {
686 let base = o.rd.ar_addr.raw() as u64;
687 let len = o.rd.ar_len.raw() as u64;
688 for b in 0..=len {
689 mem.reads.push_back((mem.now + 64, base + 64 * b, b == len));
690 }
691 }
692 if i.rd.r_valid && o.rd.r_ready {
693 mem.reads.pop_front();
694 }
695 if o.wr.aw_valid && i.wr.aw_ready {
696 mem.aw = Some(o.wr.aw_addr.raw() as u64);
697 }
698 if o.wr.w_valid && i.wr.w_ready {
699 mem.w = Some(o.wr.w_data);
700 }
701 if i.wr.b_valid && o.wr.b_ready {
702 mem.b_pending -= 1;
703 }
704 if let (Some(addr), Some(data)) = (mem.aw, mem.w) {
705 let idx = ((addr - mem.sig_base) / 64) as usize;
706 let mut s = [0u8; 64];
707 for k in 0..8 {
708 s[8 * k..8 * k + 8].copy_from_slice(&(data[k].raw() as u64).to_le_bytes());
709 }
710 assert!(mem.signatures[idx].is_none(), "signature {idx} written twice");
711 mem.signatures[idx] = Some(s);
712 mem.aw = None;
713 mem.w = None;
714 mem.b_pending += 1;
715 }
716 sim.tick(i);
717 o
718 }
719
720 #[test]
724 #[ignore = "native simulation of the build's signer, several minutes; run with --profile sim"]
725 fn card_kernel_signs_like_dalek() {
726 let n = 24usize;
727 let seed = [0x42u8; 32];
728 let key = SigningKey::from_bytes(&seed);
729 let messages: Vec<[u8; 64]> = (0..n).map(|i| std::array::from_fn(|b| (i * 31 + b * 7) as u8)).collect();
730 let mut mem = Mem {
731 messages: messages.clone(),
732 signatures: vec![None; n],
733 msg_base: 0x1_0000,
734 sig_base: 0x8_0040,
735 reads: Default::default(),
736 now: 0,
737 b_pending: 0,
738 aw: None,
739 w: None,
740 };
741 let mut sim = CycleSim::new(Ed25519Card::default());
742 let (msg_base, sig_base) = (mem.msg_base as u32, mem.sig_base as u32);
743 lite_write(&mut sim, &mut mem, 0x10, msg_base);
744 lite_write(&mut sim, &mut mem, 0x14, 0);
745 lite_write(&mut sim, &mut mem, 0x18, sig_base);
746 lite_write(&mut sim, &mut mem, 0x1c, 0);
747 lite_write(&mut sim, &mut mem, 0x20, n as u32);
748 for w in 0..8 {
749 lite_write(&mut sim, &mut mem, 0x28 + 4 * w, u32::from_le_bytes(seed[4 * w as usize..4 * w as usize + 4].try_into().unwrap()));
750 }
751 lite_write(&mut sim, &mut mem, 0x00, 1);
752 let mut polls = 0u64;
753 loop {
754 for _ in 0..5000 {
755 step(&mut sim, &mut mem, LiteIn::default());
756 }
757 polls += 1;
758 assert!(polls < 400, "no ap_done after {} cycles", mem.now);
759 if lite_read(&mut sim, &mut mem, 0x00) & 2 != 0 {
760 break;
761 }
762 }
763 assert_eq!(lite_read(&mut sim, &mut mem, 0x108), 0, "FAULT");
764 assert_eq!(lite_read(&mut sim, &mut mem, 0x104), n as u32);
765 for (i, m) in messages.iter().enumerate() {
766 let want = key.sign(m).to_bytes();
767 assert_eq!(mem.signatures[i], Some(want), "signature {i}");
768 }
769 let mut pk = [0u8; 32];
770 for w in 0..8 {
771 pk[4 * w..4 * w + 4].copy_from_slice(&lite_read(&mut sim, &mut mem, 0x140 + 4 * w as u32).to_le_bytes());
772 }
773 assert_eq!(pk, key.verifying_key().to_bytes());
774 let mut tick = |lo: u32| u64::from(lite_read(&mut sim, &mut mem, lo)) | (u64::from(lite_read(&mut sim, &mut mem, lo + 4)) << 32);
775 let (start, keyt, done) = (tick(0x110), tick(0x118), tick(0x120));
776 assert!(start < keyt && keyt < done);
777 println!("{n} signatures: key {} cycles, signing {} cycles ({:.1} cycles/signature)", keyt - start, done - keyt, (done - keyt) as f64 / n as f64);
778 }
779
780 #[test]
782 fn bad_arguments_refuse_to_start() {
783 let mut mem = Mem { messages: vec![], signatures: vec![], msg_base: 0, sig_base: 0, reads: Default::default(), now: 0, b_pending: 0, aw: None, w: None };
784 let mut sim = CycleSim::new(Ed25519Card::default());
785 lite_write(&mut sim, &mut mem, 0x10, 0x123); lite_write(&mut sim, &mut mem, 0x20, 4);
787 lite_write(&mut sim, &mut mem, 0x00, 1);
788 assert_eq!(lite_read(&mut sim, &mut mem, 0x108), 8);
789 assert_eq!(lite_read(&mut sim, &mut mem, 0x100), MAGIC);
790 }
791}