diff --git a/README.md b/README.md index 53bc042..acd903e 100644 --- a/README.md +++ b/README.md @@ -155,12 +155,120 @@ Component accesses can be arbitrarily nested: Very similar techniques apply to writing return values. See [`examples/args`](examples/args) and [`examples/returns`](examples/returns) for more. +### SHA-1 + +[SHA-1](https://en.wikipedia.org/wiki/SHA-1) is an excellent example of how powerful this kind of technique can be. The following is a (hopefully) clearly structured implementation of SHA-1 in `avo`, which ultimately generates a [1000+ line impenetrable assembly file](examples/sha1/sha1.s). + +[embedmd]:# (examples/sha1/asm.go /func main/ /^}/) +```go +func main() { + TEXT("block", "func(h *[5]uint32, m []byte)") + Doc("block SHA-1 hashes the 64-byte message m into the running state h.") + h := Mem{Base: Load(Param("h"), GP64())} + m := Mem{Base: Load(Param("m").Base(), GP64())} + + // Store message values on the stack. + w := AllocLocal(64) + W := func(r int) Mem { return w.Offset((r % 16) * 4) } + + // Load initial hash. + h0, h1, h2, h3, h4 := GP32(), GP32(), GP32(), GP32(), GP32() + + MOVL(h.Offset(0), h0) + MOVL(h.Offset(4), h1) + MOVL(h.Offset(8), h2) + MOVL(h.Offset(12), h3) + MOVL(h.Offset(16), h4) + + // Initialize registers. + a, b, c, d, e := GP32(), GP32(), GP32(), GP32(), GP32() + + MOVL(h0, a) + MOVL(h1, b) + MOVL(h2, c) + MOVL(h3, d) + MOVL(h4, e) + + // Generate round updates. + quarter := []struct { + F func(Register, Register, Register) Register + K uint32 + }{ + {choose, 0x5a827999}, + {xor, 0x6ed9eba1}, + {majority, 0x8f1bbcdc}, + {xor, 0xca62c1d6}, + } + + for r := 0; r < 80; r++ { + q := quarter[r/20] + + // Load message value. + u := GP32() + if r < 16 { + MOVL(m.Offset(4*r), u) + BSWAPL(u) + } else { + MOVL(W(r-3), u) + XORL(W(r-8), u) + XORL(W(r-14), u) + XORL(W(r-16), u) + ROLL(U8(1), u) + } + MOVL(u, W(r)) + + // Compute the next state register. + t := GP32() + MOVL(a, t) + ROLL(U8(5), t) + ADDL(q.F(b, c, d), t) + ADDL(e, t) + ADDL(U32(q.K), t) + ADDL(u, t) + + // Update registers. + ROLL(Imm(30), b) + a, b, c, d, e = t, a, b, c, d + } + + // Final add. + ADDL(a, h0) + ADDL(b, h1) + ADDL(c, h2) + ADDL(d, h3) + ADDL(e, h4) + + // Store results back. + MOVL(h0, h.Offset(0)) + MOVL(h1, h.Offset(4)) + MOVL(h2, h.Offset(8)) + MOVL(h3, h.Offset(12)) + MOVL(h4, h.Offset(16)) + RET() + + Generate() +} +``` + +This relies on the bitwise functions that are defined as subroutines. For example here is bitwise `choose`; the others are similar. + +[embedmd]:# (examples/sha1/asm.go /func choose/ /^}/) +```go +func choose(b, c, d Register) Register { + r := GP32() + MOVL(d, r) + XORL(c, r) + ANDL(b, r) + XORL(d, r) + return r +} +``` + ### Real Examples * **[fnv1a](examples/fnv1a):** [FNV-1a](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash) hash function. * **[dot](examples/dot):** Vector dot product. * **[geohash](examples/geohash):** Integer [geohash](https://en.wikipedia.org/wiki/Geohash) encoding. -* **[sha1](examples/sha1):** [SHA-1](https://en.wikipedia.org/wiki/SHA-1) cryptographic hash. * **[stadtx](examples/stadtx):** [`StadtX` hash](https://github.com/demerphq/BeagleHash) port from [dgryski/go-stadtx](https://github.com/dgryski/go-stadtx). ## Contributing