Index  Comments

Following from my previous three SHA1 implementations, I continue further, with SHA224 and SHA256 in the same trio. The first implementation of this pair I'll be showing will also be in APL, as that's nice practice, and is twenty-nine lines, sans function headers or footers, twelve dedicated to data.

This program is licensed under the GNU Affero General Public License version three.

⍝ SHA224/256 - Provide a pleasant interface to the US Secure Hash Algorithm 224 and 256 functions.
⍝ Copyright 2020 Prince Trippy

⍝ This program is free software: you can redistribute it and/or modify it under the terms of the
⍝ GNU Affero General Public License version 3 as published by the Free Software Foundation.

⍝ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
⍝ See the GNU Affero General Public License for more details.

⍝ You should have received a copy of the GNU Affero General Public License along with this program.
⍝ If not, see <>.

It's pleasant to first show the definition for that k vector, which is merely a vector of sixty-four constants. This pattern is the typical method for assigning such long constant vectors:

⍝ I don't calculate the sixty-four vector k due to my concerns of numerical representation:
k←  1116352408 1899447441 3049323471 3921009573 0961987163 1508970993 2453635748 2870763221
k←k,3624381080 0310598401 0607225278 1426881987 1925078388 2162078206 2614888103 3248222580
k←k,3835390401 4022224774 0264347078 0604807628 0770255983 1249150122 1555081692 1996064986
k←k,2554220882 2821834349 2952996808 3210313671 3336571891 3584528711 0113926993 0338241895
k←k,0666307205 0773529912 1294757372 1396182291 1695183700 1986661051 2177026350 2456956037
k←k,2730485921 2820302411 3259730800 3345764771 3516065817 3600352804 4094571909 0275423344
k←k,0430227734 0506948616 0659060556 0883997877 0958139571 1322822218 1537002063 1747873779
k←k,1955562222 2024104815 2227730452 2361852424 2428436474 2756734187 3204031479 3329325298

The initial values of SHA224 and SHA256 are useful to have as named variables and as with SHA1 these are bit matrices, now eight-by-thirty-two:

status224←3238371032 0914150663 0812702999 4144912697 4290775857 1750603025 1694076839 3204075428

status256←1779033703 3144134277 1013904242 2773480762 1359893119 2600822924 0528734635 1541459225

Those CH, MAJ, BSIG0, BSIG1, SSIG0, and SSIG1 functions I realized are quite unnecessary, as each is used merely once in the main hash algorithm; unlike the SHA1 F and K, they're brief enough to become inline, reasonably; I expect I may rewrite the main hash function to do so. I prefer the convention I've used in CH and MAJ, which avoids names by instead accepting a matrix, as these are clearly tiny enough to be followed along anyway; these are also easier to inline, by avoiding any initialization:

∇ z←ch y;⎕IO ⍝ X AND Y XOR Z AND NOT X
∇ ⍝ The domain is a three-by-thirty-two bit matrix.

∇ z←maj y;⎕IO ⍝ This domain is identical to ch.

∇ z←bsig0 y ⍝ The domain is a thirty-two bit vector.
∇ ⍝ Rotate right by two, thirteen, and twenty-two.

∇ z←bsig1 y ⍝ The domain is identical to bsig0.
∇ ⍝ Rotate right by six, eleven, and twenty-five.

∇ z←ssig0 y ⍝ The domain is identical to bsig0.
z←((¯7↑y),¯7↓y)≠((¯18↑y),¯18↓y)≠0 0 0,¯3↓y
∇ ⍝ Rotate right by seven, eighteen, and shift by three.

∇ z←ssig1 y ⍝ The domain is identical to bsig0.
∇ ⍝ Rotate right by seventeen, nineteen, and shift by ten.

The pad function is identical to that used in SHA1 and I recommend reading the SHA1 writing for such details pertaining to it. Following, the block size is identical to SHA1:

∇ z←x pad y ⍝ The x is that bit-length and y that remaining.
z←((2↓⍨447≥⍴y),512)⍴y,1,(0⍴⍨(↑512 0↓⍨447≥⍴y)+447-⍴y),x⊤⍨64⍴2
∇ ⍝ The y must be a five hundred and twelve vector, or less.

The hashing algorithms for SHA224 and SHA256 are identical and so this is named hash rather than for either. Being dyadic, the intermediate state is provided on the left and the block to hash given on the right. Unlike with the SHA1, I realized the values of the state are better left unnamed. Those initializations necessary before starting are given on the first line, primarily in counters and the buffer space. In the second line, the values of the buffer are collected into a matrix for use from the third line, which combines previous values to initialize the remainder of the buffer space. The fourth line actually sets the value and also tests the counter, falling through when the condition's met. For ease, the fifth line names the previous state values for those lines remaining, with those using the auxiliary functions to combine these and shuffle them amongst the remaining state, falling through to the end as with line four. The last line combines the new values with the previous state similarly to SHA1, and provides them in a form suitable for future invocations:

∇ z←x hash y;⎕IO;a;b;c;d;e;f;g;h;s;t;u;v;w
t←w[(⍉32 4⍴32×u-2 7 15 16)+4 32⍴⍳32]
t←(32⍴2)⊤(2*32)|+/2⊥⍉(ssig1 t[0;]),[0](ssig0 t[2;]),[0]t[1 3;]
s←+/k[v],2⊥⍉4 32⍴(bsig1 e),w[(⍳32)+32×v],h, ch 3 32⍴e,f,g
t←+/     2⊥⍉2 32⍴(bsig0 a)                ,maj 3 32⍴a,b,c
z←⍉(32⍴2)⊤(2*32)|(2⊥⍉8 32⍴a,b,c,d,e,f,g,h)+2⊥⍉x[⍳8;]

The design of this program was very clearly designed after the SHA1; unlike the Ada version I wrote, I took advantage of how the SHA1 intermediate state is identical in form to the final shape, writing no conversion function. That detail has harmed me here, however, as SHA224 requires dropping a part of the intermediate state upon finalization along with the necessary raveling, which can be achieved with the following, where N holds the state:

,¯1 0↓N

While operating on bits as the fundamental unit made this leagues easier, I've found it easy to make mistakes in this, as it complicates other, more mundane parts of the algorithm; I've not implemented SHA384 and SHA512, despite their similarities, because this would require more parameterization than I'm currently willing to put to effort, and I leave it for later. In any case, follows are examples in the style of the SHA1:

  '0123456789ABCDEF'[,⍉(8⍴16)⊤2⊥⍉status256 hash 0 pad 0⍴0] ⍝ ⎕IO is zero for these.
  '0123456789ABCDEF'[,¯1 0↓⍉(8⍴16)⊤2⊥⍉status224 hash 0 pad 0⍴0]
  '0123456789ABCDEF'[,⍉(8⍴16)⊤2⊥⍉status256 hash 8 pad (8⍴2)⊤⎕AV⍳'A'] ⍝ Sixty-five is eight bits.
  '0123456789ABCDEF'[,¯1 0↓⍉(8⍴16)⊤2⊥⍉status224 hash 8 pad (8⍴2)⊤⎕AV⍳'A']
  32⍴0 1 0 1 1
0 1 0 1 1 0 1 0 1 1 0 1 0 1 1 0 1 0 1 1 0 1 0 1 1 0 1 0 1 1 0 1
  bsig0 32⍴0 1 0 1 1 ⍝ This is merely a basic example of one of the auxiliary functions.
1 0 1 0 0 0 0 1 0 0 0 0 1 0 0 1 1 1 0 0 1 1 1 0 1 0 1 1 0 1 0 1