-
+ 53945EA441AF37B53C009C3B31826BB688CBA5525176D55ED76B934FA34702A86E31041CF4D125FA9FC827C41C242A06E472A0AD2D80FB33DDA9F9AB08260458
smg_comms/src/keccak.adb
(0 . 0)(1 . 271)
375 -- S.MG, 2018
376 with System; use System; -- for Bit_Order
377
378 package body Keccak is
379
380 -- public function, sponge
381 procedure Sponge( Input : in Bitstream;
382 Output : out Bitstream;
383 Block_Len : in Keccak_Rate := Default_Bitrate ) is
384 Internal : State := (others => (others => 0));
385 begin
386 --absorb input into sponge in a loop on available blocks, including padding
387 declare
388 -- number of input blocks after padding (2 to block_len bits pad)
389 Padded_Blocks : constant Positive := 1 + (Input'Length + 1) / Block_Len;
390 Padded : Bitstream ( 1 .. Padded_Blocks * Block_Len );
391 Block : Bitstream ( 1 .. Block_Len );
392 begin
393 -- initialise Padded with 0 everywhere
394 Padded := ( others => 0 );
395 -- copy and pad input with rule 10*1
396 Padded( Padded'First .. Padded'First + Input'Length - 1 ) := Input;
397 Padded( Padded'First + Input'Length ) := 1;
398 Padded( Padded'Last ) := 1;
399
400 -- loop through padded input and absorb block by block into sponge
401 -- padded input IS a multiple of blocks, so no stray bits left
402 for B in 0 .. Padded_Blocks - 1 loop
403 -- first get the current block to absorb
404 Block := Padded( Padded'First + B * Block_Len ..
405 Padded'First + (B+1) * Block_Len - 1 );
406 AbsorbBlock( Block, Internal );
407 -- scramble state with Keccak function
408 Internal := Keccak_Function( Internal );
409
410 end loop; -- end absorb loop for blocks
411 end; -- end absorb stage
412
413 --squeeze required bits from sponge in a loop as needed
414 declare
415 -- full blocks per output
416 BPO : constant Natural := Output'Length / Block_Len;
417 -- stray bits per output
418 SPO : constant Natural := Output'Length mod Block_Len;
419 Block : Bitstream( 1 .. Block_Len );
420 begin
421 -- squeeze block by block (if at least one full block is needed)
422 for I in 0 .. BPO - 1 loop
423 SqueezeBlock( Block, Internal );
424 Output( Output'First + I * Block_Len ..
425 Output'First + (I + 1) * Block_Len -1) := Block;
426
427 -- scramble state
428 Internal := Keccak_Function( Internal );
429 end loop; -- end squeezing full blocks
430
431 -- squeeze any partial block needed (stray bits)
432 if SPO > 0 then
433 SqueezeBlock( Block, Internal );
434 Output( Output'Last - SPO + 1 .. Output'Last ) :=
435 Block( Block'First .. Block'First + SPO - 1 );
436 end if; -- end squeezing partial last block (stray bits)
437
438 end; -- end squeeze stage
439 end Sponge;
440
441 -- convert from a bitstream of ZWord size to an actual ZWord number
442 function BitsToWord( BWord: in Bitword ) return ZWord is
443 W : ZWord;
444 Bits: Bitword;
445 begin
446 -- just copy octets if machine is little endian
447 -- flip octets if machine is big endian
448 if Default_Bit_Order = Low_Order_First then
449 Bits := BWord;
450 else
451 Bits := FlipOctets( BWord );
452 end if;
453 -- actual bits to word conversion
454 W := 0;
455 -- LSB bit order (inside octet) as per Keccak spec
456 for I in reverse Bitword'Range loop
457 W := Shift_Left( W, 1 ) + ZWord( Bits( I ) );
458 end loop;
459 return W;
460 end BitsToWord;
461
462 -- convert from a ZWord (lane of state) to a bitstream of ZWord size
463 function WordToBits( Word: in ZWord ) return Bitword is
464 Bits: Bitword := (others => 0);
465 W: ZWord;
466 begin
467 W := Word;
468 for I in Bitword'Range loop
469 Bits( I ) := Bit( W mod 2 );
470 W := Shift_Right( W, 1 );
471 end loop;
472
473 -- flip octets if machine is big endian
474 if Default_Bit_Order = High_Order_First then
475 Bits := FlipOctets( Bits );
476 end if;
477
478 return Bits;
479 end WordToBits;
480
481 -- flip given octets (i.e. groups of 8 bits)
482 function FlipOctets( BWord : in Bitword ) return Bitword is
483 Bits : Bitword;
484 begin
485 -- copy groups of 8 octets changing their order in the array
486 -- i.e. 1st octet in BWord becomes last octet in Bits and so on
487 for I in 0 .. ( Bitword'Length / 8 - 1 ) loop
488 Bits ( Bits'First + I * 8 .. Bits'First + I * 8 + 7 ) :=
489 BWord( BWord'Last - I * 8 - 7 .. BWord'Last - I * 8);
490 end loop;
491 return Bits;
492 end FlipOctets;
493
494 -- helper procedures for sponge absorb/squeeze
495
496 -- NO scramble here, this will absorb ALL given block, make sure it fits!
497 procedure AbsorbBlock( Block: in Bitstream; S: in out State ) is
498 WPB: constant Natural := Block'Length / Z_Length; -- words per block
499 SBB: constant Natural := Block'Length mod Z_Length; -- stray bits
500 FromPos, ToPos : Natural;
501 X, Y : XYCoord;
502 Word : ZWord;
503 BWord : Bitword;
504 begin
505 -- xor current block into first Block'Length bits of state
506 -- a block can consist in more than one word
507 X := 0;
508 Y := 0;
509 for I in 0..WPB-1 loop
510 FromPos := Block'First + I * Z_Length;
511 ToPos := FromPos + Z_Length - 1;
512 Word := BitsToWord( Block( FromPos .. ToPos ) );
513 S( X, Y ) := S( X, Y ) xor Word;
514 -- move on to next word in state
515 X := X + 1;
516 if X = 0 then
517 Y := Y + 1;
518 end if;
519 end loop;
520 -- absorb also any remaining bits from block
521 if SBB > 0 then
522 ToPos := Block'Last;
523 FromPos := ToPos - SBB + 1;
524 BWord := (others => 0);
525 BWord(Bitword'First .. Bitword'First + SBB - 1) := Block(FromPos..ToPos);
526 Word := BitsToWord( BWord );
527 S( X, Y ) := S( X, Y ) xor Word;
528 end if;
529 end AbsorbBlock;
530
531 --NO scramble here, this will squeeze Block'Length bits out of *same* state S
532 procedure SqueezeBlock( Block: out Bitstream; S: in State) is
533 X, Y : XYCoord;
534 BWord : Bitword;
535 FromPos : Natural;
536 Len : Natural;
537 begin
538 X := 0;
539 Y := 0;
540 FromPos := Block'First;
541
542 while FromPos <= Block'Last loop
543 BWord := WordToBits( S(X, Y) );
544
545 X := X + 1;
546 if X = 0 then
547 Y := Y + 1;
548 end if;
549
550 -- copy full word if it fits or
551 -- only as many bits as are still needed to fill the block
552 Len := Block'Last - FromPos + 1;
553 if Len > Z_Length then
554 Len := Z_Length;
555 end if;
556
557 Block(FromPos..FromPos+Len-1) := BWord(BWord'First..BWord'First+Len-1);
558 FromPos := FromPos + Len;
559 end loop;
560 end SqueezeBlock;
561
562
563 -- private, internal transformations
564 function Theta(Input : in State) return State is
565 Output : State;
566 C : Plane;
567 W : ZWord;
568 begin
569 for X in XYCoord loop
570 C(X) := Input(X, 0);
571 for Y in 1..XYCoord'Last loop
572 C(X) := C(X) xor Input(X, Y);
573 end loop;
574 end loop;
575
576 for X in XYCoord loop
577 W := C(X-1) xor Rotate_Left(C(X+1), 1);
578 for Y in XYCoord loop
579 Output(X,Y) := Input(X,Y) xor W;
580 end loop;
581 end loop;
582
583 return Output;
584 end Theta;
585
586 function Rho(Input : in State) return State is
587 Output : State;
588 X, Y, Old_Y : XYCoord;
589 begin
590 Output(0,0) := Input(0,0);
591 X := 1;
592 Y := 0;
593
594 for T in 0..23 loop
595 Output(X, Y) := Rotate_Left(Input(X,Y), ((T+1)*(T+2)/2) mod Z_Length);
596 Old_Y := Y;
597 Y := 2*X + 3*Y;
598 X := Old_Y;
599 end loop;
600 return Output;
601 end rho;
602
603 function Pi(Input : in State) return State is
604 Output: State;
605 begin
606 for X in XYCoord loop
607 for Y in XYCoord loop
608 Output(Y, 2*X + 3*Y) := Input(X, Y);
609 end loop;
610 end loop;
611 return Output;
612 end pi;
613
614 function Chi(Input : in State) return State is
615 Output: State;
616 begin
617 for Y in XYCoord loop
618 for X in XYCoord loop
619 Output(X, Y) := Input(X, Y) xor
620 ( (not Input(X + 1, Y)) and Input(X + 2, Y) );
621 end loop;
622 end loop;
623 return Output;
624 end chi;
625
626 function Iota(Round_Const : in ZWord; Input : in State) return State is
627 Output: State;
628 begin
629 Output := Input;
630 Output(0,0) := Input(0,0) xor Round_Const;
631 return Output;
632 end iota;
633
634 function Keccak_Function(Input: in State) return State is
635 Output: State;
636 begin
637 Output := Input;
638 for I in Round_Index loop
639 Output := Iota(RC(I), Chi(Pi(Rho(Theta(Output)))));
640 end loop;
641
642 return Output;
643 end Keccak_Function;
644
645 end Keccak;