EuCrypt addition: Keccak File Hashing



July 8th, 2020 by Diana Coman

Since the client data model includes Keccak hashes for files received from Eulora's server and the files themselves may be of any size whatsoever, it follows that both client and server have to be able to use EuCrypt's Keccak sponge sequentially too - basically feeding it data in chunks as opposed to all in one go, as a single input (be it bitstream or bytestream).1 So I got my Ada reference book out on the desk again and it turns out that it's not even all that difficult really - even though this addition has to be yet another package on top of the existing ones in EuCrypt, mainly because such type of use breaks by definition the stateless guarantee provided by the "single input" use and in turn, this would then propagate to all code that uses the Keccak package (and that's the encryption scheme, mainly). Therefore, rather than forcing now stateful code everywhere just because there's a need to calculate hashes for files on the disk, I simply provide this file-hashing as a separate package using the same underlying sponge. Quite as it should even be, I would say: there is only one sponge implementation but there are now two options to using it for hashing, namely a stateless one for data that is held entirely in memory (the one that existed already) and a stateful one for data that is fed sequentially (my new code). As this is now implemented, tested and integrated into the respective parts on both client and server, I'd rather take the time and write it down as well, to unload it and have it all in one place.

The approach here is very straightforward: keep a sponge's state locally, read from the input file blocks as big as the sponge can handle in one go (and add if needed padding to the last block), pass each block as soon as read on to the sponge, scramble the state and repeat until the whole file has been processed; then squeeze out of the sponge a block and return its first 8 octets given that the hash is meant to be that size. Ada's Sequential_IO package simply needs to be provided with the type of element to read and then it works like any other file input/output, without any trouble. In principle, the most effective implementation would be to read a whole block (ie as many octets as the sponge can absorb in one go) each time but this means that one has to handle at file reading time the special case of the last block that may be incomplete. For now at least I preferred to sidestep this and I went instead for the cheap and angry solution that seems however perfectly adequate for current needs: simply read a file octet by octet, so that there is no special case at all. Here's the code that does it all:

  -- for reading files one Octet at a time
  package Octet_IO is new Ada.Sequential_IO(Element_Type =>
                                              Interfaces.Unsigned_8);

  function Hash_File(Filename: in String; Hash: out Raw_Types.Octets_8)
      return Boolean is
    F: Octet_IO.File_Type;
    S: Keccak.State := (others => (others => 0));
    Block_Len: Keccak.Keccak_Rate := Keccak.Default_Byterate;
    Block: Keccak.Bytestream(1..Block_Len);
    Pos: Keccak.Keccak_Rate;
  begin
    Octet_IO.Open(F, Octet_IO.In_File, Filename);
    -- check that this is not an empty file as hashing of that is nonsense
    if Octet_IO.End_Of_File(F) then
      Octet_IO.Close(F); -- close it before returning!
      return False;
    end if;

    -- read from file and absorb into the sponge
    while not Octet_IO.End_Of_File(F) loop
      -- read block by block
      Pos := 1;
      while Pos <= Block_Len and (not Octet_IO.End_Of_File(F)) loop
        Octet_IO.Read(F, Block(Pos));
        Pos := Pos + 1;
      end loop; -- single block loop
      -- if it's an incomplete block, it needs padding
      if Pos <= Block_Len then
        -- pad it with 10*1
        Block(Pos..Block'Last) := (others => 0);
        Block(Pos) := 1;
        Block(Block'Last) := Block(Block'Last) + 16#80#;
      end if;
      -- here the block is complete, padded if needed.
      -- absorb it into the state
      Keccak.AbsorbBlock( Block, S);
      -- scramble state
      S := Keccak.Keccak_Function( S );
    end loop; -- full file loop
    Octet_IO.Close(F);

    -- now squeeze a block and get the 8 octets required
    Keccak.SqueezeBlock( Block, S);
    Hash := Block(1..8);

    -- if it got here, all is well, return true.
    return True;
  exception
    when others =>
      Octet_IO.Close(F);
      return False;
  end Hash_File;

Note that the above returns the *raw* Keccak hash, meaning the direct output of the sponge, as a set of octets. This is normally fine and well but if one specifies the hash as "unsigned 64", it means that the above set of octets has to be interpreted as a number - and in turn, this means that byte/bit order matters. Since this can and does create confusion quite easily, I'll state it here plainly again: the raw output of the Keccak sponge is MSB/b, meaning that on a little endian machine, you'll need to flip both bytes and bits if you want to get the exact same number as you would on a big endian machine! Since this is however something that I already sorted out before, I added to the above convenient wrappers to do this properly so that the whole code should work seamlessly on both big and little endian computers anyway:

  function Hash_File(Filename: in String; Hash: out Interfaces.Unsigned_64)
      return Boolean is
    Raw: Raw_Types.Octets_8;
  begin
    -- calculate the raw hash and then convert it
    if Hash_File(Filename, Raw) then
      Hash := Hash2Val(Raw);
      return True;
    else
      return False;
    end if;
  end Hash_File;
  function Hash2Val( Raw: in Raw_Types.Octets_8 )
      return Interfaces.Unsigned_64 is
    B8: Raw_Types.Octets_8;
    U64: Interfaces.Unsigned_64;
  begin
    -- convert to U64 (NB: no need to squeeze etc, as block_len has to be > 8)
    -- for this to remain consistent on both little and big endian machines:
    -- on little endian, octets and bits need to be flipped before conversion
    if Default_Bit_Order = Low_Order_First then
      for BI in 1..8 loop
        B8(BI) := Keccak.Reverse_Table(Natural(Raw(Raw'First+8-BI)));
      end loop;
    else --simply copy as it is
      B8 := Raw(1..8);
    end if;
    -- convert and return
    U64 := Raw_Types.Cast(B8);
    return U64;
  end Hash2Val;

Using the above, one can now test Keccak hashes of files both raw and in the more usual numerical format (e.g. hex as given for instance by the keksum implementation). More importantly for me, Eulora's client can now check the hash of a received file when it gets the last chunk of it and therefore it can decide whether the whole thing is any good or not! I'm quite happy to inform as well that initial tests are running fine - the data acquisition part of the client successfully requests files and receives them apparently unmolested (even when there are quite a few chunks, so far I tested with several thousands meaning up to 10MB files), writing them neatly to disk exactly as and where intended. Hooray!

The above out of the way, the uglier next step is to get that hideous gui-code to actually *use* those files properly, too!


  1. This is not something entirely new, since vtools for instance has previously adapted my Keccak implementation to a similar use, in order to calculate the hashes for vpatches. However, the approach taken there (by phf) apparently aimed to wrap the Ada implementation for C/CPP use and I don't want this at all. First, I'd much rather move C/CPP code to Ada if/when possible than the other way around. Second, there is absolutely no good reason in this case to force any C/CPP code in the mix since Ada actually provides all that is needed to interact with files on the disk without any trouble whatsoever. 

Comments feed: RSS 2.0

13 Responses to “EuCrypt addition: Keccak File Hashing”

  1. Diana Coman says:

    Just to check here: if I recall correctly, we had decided that *all* values sent through the communication protocol are considered Big Endian (MSB/b), is this correct? Hence, for the above, the server would send the *raw* hash essentially (and the client is free to convert it to a U64 value or not, in whatever format it wants to, as well).

  2. That's what I remember ; nor do I see any great impediment to sticking to BE over the wire.

  3. > simply read a file octet by octet, so that there is no special case at all

    This promises to be slow, not to mention aren't we wasting most of the benefits ? How about : 1. always read the file in memory ; 2. as a ring buffer ; 3 such that there's no "padding" or problems : if the last block is "too short" such length will be read from the beginning as satisfies, wouldn't that be both better from a computing pov (let alone ideologically closer) ?

  4. Diana Coman says:

    Re BE - ok; I'll have to keep this in mind actually also for *all* values since otherwise locally they are not all BE for sure.

    >This promises to be slow, not to mention aren't we wasting most of the benefits ?
    Uhm, I don't quite follow this. Specifically:

    1. if the file is 1GB or 10GB, do you mean that the client & server should read all that in memory each time they need to recalculate the hash? Or keep it in memory at all times? If yes, then indeed, there's no need for this stateful package, can just use the stateless existing code and be done with it but I am not quite sure it helps a lot.

    2. the padding is from the keccak-spec, meaning that if we decide now to fill it from the beginning instead, the result will *not* be keccak hash of the file really - it would be more-ideologically-closer-keccak-spec I guess, but that's about it and it will require creating and maintaining that ring buffer ie certainly no implementation gain as such. (For that matter, the existing stateless keccak-hashing does handle the padding of last block if incomplete since it is part of the original spec so if reading the whole file in memory, it could just handle it directly - it's only that I didn't think it was meant to keep files in memory; and hm, changing that part now again, ugh).

    3. re speed, the *only* gain would be based on difference in reading speed from disk ie how much faster it is to read a block than an octet (or the full file if I figure out exactly how to ask it that ie how to get upfront the size without too much cost); otherwise there is no speed gain at hashing really because the sponge has a capacity and that is fixed - ie in *all* cases keccak itself (the sponge) reads/absorbs one block at a time, not more nor less, there is no difference there at all, whether stateless or stateful, whether padded or not padded whether whatever in the world.

  5. Diana Coman says:

    Just to clarify as maybe it got confused in there: it reads as many octets as needed to fill a block and then passes *that* to the keccak sponge; so yes, there can be in principle some speed-gain in reading block-size-octets in one go (with the drawback of having to check /handle last possibly incomplete read) but the keccak-part is always exactly the same, no speed gained or lost there in either case.

  6. Diana Coman says:

    Meanwhile and since it was grating anyway, I implemented also the block-sized read version of this (+ octet-based only for the last, incomplete block, since apparently on incomplete element it doesn't guarantee reading as much as there is) and a rough timing test on an AMD fx-8350, 16G RAM: the octet-based version takes reliably 0.1 seconds per MB (hence 4MB file takes ~0.4 seconds, 8MB file takes ~0.8 seconds, 680MB file takes ~68 seconds etc); the block-sized read version is trickier to pin down (as it depends in a more complex way on file size) but in any case, 4MB file takes ~0.2 seconds and 680MB file takes ~35 seconds.

    Clearly block-read + octet-for-last is an improvement (and since ahem, I just proved it can be done, probably now I *have to* use it, too). Whether it's enough or not, I can't tell - it depends I suppose on what sort of files we end up sending, though either way the potential problems start anyway at the same end aka on large files so not sure what is gained, as such.

  7. Aite, so then this discussion was indeed quite productive, and I'm quite happy with using the blocks-read-octets-last approach.

  8. -----BEGIN PGP MESSAGE-----
    Version: GnuPG v1.4.10 (GNU/Linux)

    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
    wd6OlmFUfAu2Gjuyd1W+81Ahj6S29d4bILNPqzs=
    =ghIz
    -----END PGP MESSAGE-----
  9. Diana Coman says:
    -----BEGIN PGP MESSAGE-----

    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
    DMS+pFottUoamKx6DaUHFGL41g==
    =lm2g
    -----END PGP MESSAGE-----
  10. Nua ca copiasem io airuea. Ty!

  11. -----BEGIN PGP MESSAGE-----
    Version: GnuPG v1.4.10 (GNU/Linux)

    hQIMA3U2qif5BrDFARAAiq4smGjkioOUi+R63dWShKEHN0kOrkTTBLNlGS8M4aDZ
    4SaGZ425DDnJ9EFXNrZKpvTQVWW77eoch2Knwppaxqr1gVmUnHQAOT0EfCTnz9Lo
    +G7qHDmM3TBSNq1k7ysjYBZC+27ur+1PVECNF4QlNYD9/9KxrLDx+bCFvg7Sx3QQ
    SjNQdJoiUdzMvKxo0jlpwaHQ6TBJ3wVnwF2AKQmeYQX8qK3WaefUOyrq33ZNWbFb
    tmKwb2/sMCAi97Sk56VWyMO6T5szxUtM+tVlCdVCtn7HuTDhD/7JKLzW96MrDfa+
    N/pv8EmOR2e1CccxKTuoBXCzqj5uJhg2Q5V74gtS2I3Jw6l/pnwJIb6KE4+HsIwd
    VNj0Aqw/Sw/lRZrcunR8sIdYYq8fyrZVRMvmJS6G/GPupnMWlZZsAW0Thp+oCNJP
    Reoh7Vx9ZzgC0+Gp7PbWoYsNr0Hu0ezeYn4htrxKx3e+xr3JSNVjxTQY6Wx7j3UU
    Eje4/PF4ygpMrifxAagpMBG/JBnECCF+XsVEfpKQ+TgHKg0SZVOYDftpm7RXd5q9
    0dLdOrvpTCUG78QKt2ezVVdC4PpSrH0o3loGUqfV7RF23baUnDBM9wlm/FaSre8x
    8HrQfT54QLXtuIa261fMpCS4oRCzOhcq7fF9L9MMV6O0uBQpqsWHHB8UCottaGqF
    AgwDxtnOzha44y4BEAC1VUxkQ1E6w4t4qC0FlAqk6O1hzijDXsA2X78KcNoVn5oN
    2ZdAC5oXU/2e4q1e1UIZ2e44+vWx8mVpVgGBPDccJ9IBPYWKx2kQsU2eR3Ox8ba3
    1qHukreEcgTcbeZd4IOdhl3fGWxuzHUzQop/aK0ug5e1QmohIi+MM7QD4PClPH//
    qxoPSyKucNCs/E7lMEbpdSDld47DKJWSB7Z5pDbLXcLyp9HaM8eRsOW9XiQIzq+D
    wS3lJKHlRUL1asN69A56xWXkRTP6glTRSRG2fXySpbbKfKpIRWkfTCB2lYOYxrs3
    6T6MKbFxKeQSCnwoQIZhzohgA9tFuOf1/TZ2cunfbklzAEqtgw/KqEN4DU0n4aG9
    9Z2I68RErEqolKOf3ldO4nLN0jodYN37/qKIddEzrZprFJGyTifX3pFEFGT5Jw1X
    pvkwkbBtqMS4aOqTH0EOeGopmRhyTYlDG2gwnagYtHoRkFVth48d3ctlCV4wkn+7
    NNZtehS5LsViNZVdzY43cjmbEqejN7xlxJu5baIEgWJiLY5Z/71tQNJmwvIlFpmP
    5waMQVPOIsbvBHGc4kLIOuQtqGHPM5Ph/l92vlx24MZFPE+ONjiPGBjoTv1q49Ps
    BF5ndfhR9cFQFasMxwjPxzvUonp+/f+t5jj0eufn0nzGqtd2oOtKPlTY6P8FXIUC
    DAPR7/VUPiNyEQEP/1vZqSoz7VhyqrkR3vGDLO4MIqzrsbkXxryq8057ogFfCRFp
    1vrtbRh7WUz2Ik0a4I/EIPzugq6KkSUow+lxiaGhEj0vpXkX0EffZVTem3pmTFOD
    uNtprOmYzL46Pb0pDOmBDqzxraTPhrrJVfO/bN0SrVpCsBOsOUmX+QsjLWKedsa4
    VwWccTyPr1tsIasBNSZCAuXny2YH/DVuUNFh9+uD6Zbo7nbNc9tsnyqM8LmVCuj2
    Plm7jBzjEYZDzDjKs9D2e/lbmFEIss7SNrR6a+YLngyKBmsxiP8G4PHrwu4QWCej
    6UBm1K0ASkBMn7TMUXp541YvVh9hboT2G5/1zQod7a9YfSEh/DE2CZWedsMQWbqX
    NBFpuumvEsomJXkzkWDbMtShFfQ5JTgUMj08Jf6kyXYcWyX3+M1eGl1lMGWA5USm
    w9pwykR5KVS9/J2Yb9Nl0dzIgFA2RTRW7H864XR6B2OglAdignWAxTCwJK99CZ4v
    CiAtL30k4sZW9TQe6hNQf7oRlASE8RUx47vPTLZ1cKrUMAf1XTxj1Zix31vFHYsK
    2HBVMSePaCEMJlzMGleBVRc3cwFWpzzT3d5+buigLFbtdAqztfhz1Cr2dYoZnqkB
    NBmsXskwM8H5rpaDa4NDE6in/8donks1hgOnm5uB5mQ8uEpUUXJMIWRtvyDX0pUB
    V4sYWq0YkYUVE3AnI8kOf55WuZTjJ648ijBbvBnRk7ctJR6AunltDKPgnSIzi0B8
    LQnbqCoCDn0aMrjf9GKRy5Bvf9Mg0W47kfXEtf5SKYwVFXXE2EASN0InukKcXvyZ
    EjRCA6gHUaGnN/x/2tpqUOIr0Yuv7IYjRWtR+L7ikShRRKA3vxceN1lfcNB2UZCe
    hFFhZA==
    =EhsA
    -----END PGP MESSAGE-----
  12. Diana Coman says:
    -----BEGIN PGP MESSAGE-----

    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
    btltDKdG4cOtSaMbaxykZGuig4xlvMMv
    =k+jc
    -----END PGP MESSAGE-----

Leave a Reply