Implemented crypto module; enhanced time module.
h3rald h3rald@h3rald.com
Sun, 28 Aug 2016 17:13:10 +0200
16 files changed,
1358 insertions(+),
4 deletions(-)
M
core/utils.nim
→
core/utils.nim
@@ -365,6 +365,11 @@ a = i.pop
if not a.isInt: raiseInvalid("An integer is required on the stack") +proc reqNumber*(i: var MinInterpreter, a: var MinValue) = + a = i.pop + if not a.isNumber: + raiseInvalid("A number is required on the stack") + proc reqTwoInts*(i: var MinInterpreter, a, b: var MinValue) = a = i.pop b = i.pop
A
lib/min_crypto.nim
@@ -0,0 +1,72 @@
+import + md5, + base64, + strutils, + times +import + ../core/types, + ../core/parser, + ../core/interpreter, + ../core/utils, + ../vendor/sha1, + ../vendor/nimSHA2, + ../vendor/nimAES + +proc crypto_module*(i: In)= + i.define("crypto") + + .symbol("md5") do (i: In): + var s: MinValue + i.reqStringLike s + i.push s.getString.getMD5.newVal + + .symbol("sha1") do (i: In): + var s: MinValue + i.reqStringLike s + i.push compute(s.getString).toHex.newVal + + .symbol("sha224") do (i: In): + var s: MinValue + i.reqStringLike s + i.push computeSHA224(s.getString).hex.toLower.newVal + + .symbol("sha256") do (i: In): + var s: MinValue + i.reqStringLike s + i.push computeSHA256(s.getString).hex.toLower.newVal + + .symbol("sha384") do (i: In): + var s: MinValue + i.reqStringLike s + i.push computeSHA384(s.getString).hex.toLower.newVal + + .symbol("sha512") do (i: In): + var s: MinValue + i.reqStringLike s + i.push computeSHA512(s.getString).hex.toLower.newVal + + .symbol("encode") do (i: In): + var s: MinValue + i.reqStringLike s + i.push s.getString.encode.newVal + + .symbol("decode") do (i: In): + var s: MinValue + i.reqStringLike s + i.push s.getString.decode.newVal + + .symbol("aes") do (i: In): + var s, k: MinValue + i.reqTwoStrings k, s + var ctx: AESContext + var text = s.getString + var length = text.len + if length div 16 == 0: + text &= " ".repeat(16 - length) + elif length mod 16 != 0 and length div 16 >= 1: + text &= " ".repeat((length div 16 + 1) * 16 - length) + var key = k.getString.compute.toHex # SHA1 of key, to make sure it's long enough + var nonce = key[0..15] + i.push ctx.cryptOFB(nonce, text).newVal + + .finalize()
M
lib/min_fs.nim
→
lib/min_fs.nim
@@ -40,6 +40,7 @@ var s: MinValue
i.reqStringLike s let fi = s.getString.getFileInfo var info = newSeq[MinValue](0).newVal + info.qVal.add @["name".newSym, s].newVal info.qVal.add @["device".newSym, fi.id.device.newVal].newVal info.qVal.add @["file".newSym, fi.id.file.newVal].newVal info.qVal.add @["type".newSym, fi.kind.filetype.newVal].newVal
M
lib/min_num.nim
→
lib/min_num.nim
@@ -1,4 +1,6 @@
-import tables +import + tables, + random import ../core/types, ../core/parser,@@ -76,5 +78,5 @@ .symbol("mod") do (i: In):
var a, b: MinValue i.reqTwoInts b, a i.push(newVal(a.intVal mod b.intVal)) - + .finalize()
M
lib/min_str.nim
→
lib/min_str.nim
@@ -11,6 +11,11 @@
proc str_module*(i: In) = i.define("str") + .symbol("strip") do (i: In): + var s: MinValue + i.reqStringLike s + i.push s.getString.strip.newVal + .symbol("split") do (i: In): var sep, s: MinValue i.reqTwoStrings sep, s
M
lib/min_time.nim
→
lib/min_time.nim
@@ -17,5 +17,46 @@
.symbol("now") do (i: In): i.push epochTime().newVal + .symbol("timeinfo") do (i: In): + var t: MinValue + i.reqNumber t + var time: Time + if t.kind == minInt: + time = t.intVal.fromSeconds + else: + time = t.floatVal.fromSeconds + let tinfo = time.timeToTimeInfo + var info = newSeq[MinValue](0).newVal + info.qVal.add @["year".newSym, tinfo.year.newVal].newVal + info.qVal.add @["month".newSym, (tinfo.month.int+1).newVal].newVal + info.qVal.add @["day".newSym, tinfo.monthday.newVal].newVal + info.qVal.add @["weekday".newSym, (tinfo.weekday.int+1).newVal].newVal + info.qVal.add @["yearday".newSym, tinfo.yearday.newVal].newVal + info.qVal.add @["hour".newSym, tinfo.hour.newVal].newVal + info.qVal.add @["minute".newSym, tinfo.minute.newVal].newVal + info.qVal.add @["second".newSym, tinfo.second.newVal].newVal + i.push info + + .symbol("datetime") do (i: In): + var t: MinValue + i.reqNumber t + var time: Time + if t.kind == minInt: + time = t.intVal.fromSeconds + else: + time = t.floatVal.fromSeconds + i.push time.timeToTimeInfo.format("yyyy-MM-dd'T'HH:mm:ss'Z'").newVal + + .symbol("tformat") do (i: In): + var t, s: MinValue + i.reqString s + i.reqNumber t + var time: Time + if t.kind == minInt: + time = t.intVal.fromSeconds + else: + time = t.floatVal.fromSeconds + i.push time.timeToTimeInfo.format(s.getString).newVal + .finalize()
M
lib/prelude.min
→
lib/prelude.min
@@ -9,6 +9,7 @@ #stack
#sys #time #fs +#crypto ; Common sigils (bind) (.) sigil
M
minim.nim
→
minim.nim
@@ -14,6 +14,7 @@ lib/min_logic,
lib/min_time, lib/min_io, lib/min_sys, + lib/min_crypto, lib/min_fs const version* = "1.0.0-dev"@@ -69,6 +70,7 @@ i.str_module
i.sys_module i.time_module i.fs_module + i.crypto_module i.eval PRELUDE proc minimStream(s: Stream, filename: string, debugging = false) =
A
tests/crypto.min
@@ -0,0 +1,24 @@
+@test +#test + +"crypto" describe + + ("test" md5 "098f6bcd4621d373cade4e832627b4f6" ==) assert + + ("test" sha1 "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3" ==) assert + + ("test" sha224 "90a3ed9e32b2aaf4c61c410eb925426119e1a9dc53d4286ade99a809" ==) assert + + ("test" sha256 "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08" ==) assert + + ("test" sha384 "768412320f7b0aa5812fce428dc4706b3cae50e02a64caa16a782249bfe8efc4b7ef1ccb126255d196047dfedf17a0a9" ==) assert + + ("test" sha512 "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff" ==) assert + + ("test" encode decode "test" ==) assert + + ("test" "test" aes "test" aes strip "test" ==) assert + + report + clear + "test.txt" rm
M
tests/fs.min
→
tests/fs.min
@@ -14,8 +14,6 @@
("test.txt" hidden? false ==) assert ("test.txt" fstats 'type dget "file" ==) assert - - report clear
M
tests/str.min
→
tests/str.min
@@ -3,6 +3,8 @@ #test
"str" describe + (" test " strip "test" ==) assert + ("a,b,c" "," split ("a" "b" "c") ==) assert ("test #1" "[0-9]" search ("1") ==) assert
M
tests/time.min
→
tests/time.min
@@ -7,5 +7,11 @@ (timestamp 1464951736 >) assert
(now 1464951736 >) assert + (1464951736 datetime "2016-06-03T12:02:16Z" ==) assert + + (1464951736 "yy-MM-dd" tformat "16-06-03" ==) assert + + (1464951736 timeinfo 'second dget 16 ==) assert + report clear
A
vendor/nimAES.nim
@@ -0,0 +1,578 @@
+# AES, Rijndael Algorithm implementation written in nim +# +# Copyright (c) 2015 Andri Lim +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# +#------------------------------------- + +import strutils + +type + AESTable = object + FSb, RSb: array[0..255, uint8] + FT0, FT1, FT2, FT3, RT0, RT1, RT2, RT3: array[0..255, uint32] + RCON: array[0..9, uint32] + + AESContext* = object + nr: int + rk: int + buf: array[0..67, uint32] + +proc initAES*(): AESContext = + result.nr = 0 + result.rk = 0 + for i in 0..result.buf.len-1: result.buf[i] = 0 + +proc ROTL8(x: uint32): uint32 = + result = (x shl 8) or (x shr 24) + +proc XTIME[T](x: T): T = + result = x shl T(1) + if (x and T(0x80)) != T(0): result = result xor T(0x1B) + else: result = result xor T(0x00) + +proc computeRoundConstant(): array[0..9, uint32] = + var x = 1'u32 + for i in 0..9: + result[i] = x + x = XTIME(x) and 0xFF + +#compute pow and log tables over GF(2xor8) +proc computePowLog(): tuple[pow: array[0..255, int], log: array[0..255, int]] = + var x = 1 + for i in 0..255: + result.pow[i] = x + result.log[x] = i + x = (x xor XTIME(x)) and 0xFF + +proc MUL(x, y: uint8, pow, log: array[0..255, int]): uint32 = + result = 0 + if x != 0 and y != 0: result = uint32(pow[((log[x]+log[y]) mod 255)]) + +proc computeTable*(): AESTable = + let (pow, log) = computePowLog() + result.RCON = computeRoundConstant() + + template srl(x, y: typed, s: untyped): untyped = + y = ((y shl 1) or (y shr 7)) and 0xFF + s + + result.FSb[0] = 0x63 + result.RSb[0x63] = 0 + for i in 1..255: + var x = pow[255 - log[i]] + var y = x + srl(x, y): x = x xor y + srl(x, y): x = x xor y + srl(x, y): x = x xor y + srl(x, y): x = x xor y xor 0x63 + result.FSb[i] = uint8(x) + result.RSb[x] = uint8(i) + + # generate the forward and reverse tables + for i in 0..255: + let x = result.FSb[i] + let y = XTIME(x) and 0xFF + let z = (y xor x) and 0xFF + + result.FT0[i] = uint32(y) xor (uint32(x) shl 8) xor + (uint32(x) shl 16) xor (uint32(z) shl 24) + + result.FT1[i] = ROTL8(result.FT0[i]) + result.FT2[i] = ROTL8(result.FT1[i]) + result.FT3[i] = ROTL8(result.FT2[i]) + + let w = result.RSb[i] + result.RT0[i] = MUL(0x0E, w, pow, log) xor (MUL(0x09, w, pow, log) shl 8) xor + (MUL(0x0D, w, pow, log) shl 16) xor (MUL(0x0B, w, pow, log) shl 24) + + result.RT1[i] = ROTL8(result.RT0[i]) + result.RT2[i] = ROTL8(result.RT1[i]) + result.RT3[i] = ROTL8(result.RT2[i]) + +proc version(major, minor, patch: int): int {.compiletime.} = + result = major+minor+patch + +const compilerVersion = version(NimMajor,NimMinor,NimPatch) + +when compilerVersion <= version(0,11,2): + let SBOX = computeTable() +elif compilerVersion >= version(0,11,3): + const SBOX = computeTable() + +proc GET_ULONG_LE(b: cstring, i: int): uint32 = + result = cast[uint32](ord(b[i]) or (ord(b[i+1]) shl 8) or (ord(b[i+2]) shl 16) or (ord(b[i+3]) shl 24)) + +proc PUT_ULONG_LE(n: uint32, b: var cstring, i: int) = + b[i] = chr(int(n and 0xFF)) + b[i+1] = chr(int((n shr 8) and 0xFF)) + b[i+2] = chr(int((n shr 16) and 0xFF)) + b[i+3] = chr(int((n shr 24) and 0xFF)) + +proc setEncodeKey*(ctx: var AESContext, key: string): bool = + var keySize = key.len * 8 + zeroMem(addr(ctx), sizeof(ctx)) + + case keySize: + of 128: ctx.nr = 10 + of 192: ctx.nr = 12 + of 256: ctx.nr = 14 + else: return false + + let len = keySize div 32 + for i in 0..len-1: ctx.buf[i] = GET_ULONG_LE(cstring(key), i * 4) + + var RK = 0 + if ctx.nr == 10: + for i in 0..9: + ctx.buf[RK+4] = ctx.buf[RK+0] xor SBOX.RCON[i] xor + uint32(SBOX.FSb[(int(ctx.buf[RK+3] shr 8) and 0xFF)]) xor + (uint32(SBOX.FSb[(int(ctx.buf[RK+3] shr 16) and 0xFF)]) shl 8) xor + (uint32(SBOX.FSb[(int(ctx.buf[RK+3] shr 24) and 0xFF)]) shl 16) xor + (uint32(SBOX.FSb[(int(ctx.buf[RK+3]) and 0xFF)]) shl 24) + + ctx.buf[RK+5] = ctx.buf[RK+1] xor ctx.buf[RK+4] + ctx.buf[RK+6] = ctx.buf[RK+2] xor ctx.buf[RK+5] + ctx.buf[RK+7] = ctx.buf[RK+3] xor ctx.buf[RK+6] + inc(RK, 4) + + elif ctx.nr == 12: + for i in 0..7: + ctx.buf[RK+6] = ctx.buf[RK+0] xor SBOX.RCON[i] xor + uint32(SBOX.FSb[int(ctx.buf[RK+5] shr 8) and 0xFF]) xor + (uint32(SBOX.FSb[int(ctx.buf[RK+5] shr 16) and 0xFF]) shl 8) xor + (uint32(SBOX.FSb[int(ctx.buf[RK+5] shr 24) and 0xFF]) shl 16) xor + (uint32(SBOX.FSb[int(ctx.buf[RK+5]) and 0xFF]) shl 24) + + ctx.buf[RK+7] = ctx.buf[RK+1] xor ctx.buf[RK+6] + ctx.buf[RK+8] = ctx.buf[RK+2] xor ctx.buf[RK+7] + ctx.buf[RK+9] = ctx.buf[RK+3] xor ctx.buf[RK+8] + ctx.buf[RK+10] = ctx.buf[RK+4] xor ctx.buf[RK+9] + ctx.buf[RK+11] = ctx.buf[RK+5] xor ctx.buf[RK+10] + inc(RK, 6) + + elif ctx.nr == 14: + for i in 0..6: + ctx.buf[RK+8] = ctx.buf[RK+0] xor SBOX.RCON[i] xor + uint32(SBOX.FSb[int(ctx.buf[RK+7] shr 8) and 0xFF]) xor + (uint32(SBOX.FSb[int(ctx.buf[RK+7] shr 16) and 0xFF]) shl 8) xor + (uint32(SBOX.FSb[int(ctx.buf[RK+7] shr 24) and 0xFF]) shl 16) xor + (uint32(SBOX.FSb[int(ctx.buf[RK+7]) and 0xFF]) shl 24) + + ctx.buf[RK+9] = ctx.buf[RK+1] xor ctx.buf[RK+8] + ctx.buf[RK+10] = ctx.buf[RK+2] xor ctx.buf[RK+9] + ctx.buf[RK+11] = ctx.buf[RK+3] xor ctx.buf[RK+10] + + ctx.buf[RK+12] = ctx.buf[RK+4] xor + uint32(SBOX.FSb[int(ctx.buf[RK+11]) and 0xFF]) xor + (uint32(SBOX.FSb[int(ctx.buf[RK+11] shr 8) and 0xFF]) shl 8) xor + (uint32(SBOX.FSb[int(ctx.buf[RK+11] shr 16) and 0xFF]) shl 16) xor + (uint32(SBOX.FSb[int(ctx.buf[RK+11] shr 24) and 0xFF]) shl 24) + + ctx.buf[RK+13] = ctx.buf[RK+5] xor ctx.buf[RK+12] + ctx.buf[RK+14] = ctx.buf[RK+6] xor ctx.buf[RK+13] + ctx.buf[RK+15] = ctx.buf[RK+7] xor ctx.buf[RK+14] + inc(RK, 8) + + result = true + +proc setDecodeKey*(ctx: var AESContext, key: string): bool = + var keySize = key.len * 8 + zeroMem(addr(ctx), sizeof(ctx)) + + case keySize: + of 128: ctx.nr = 10 + of 192: ctx.nr = 12 + of 256: ctx.nr = 14 + else: return false + var cty: AESContext + if not cty.setEncodeKey(key): return false + var SK = cty.nr * 4 + var RK = 0 + + ctx.buf[RK] = cty.buf[SK] + ctx.buf[RK+1] = cty.buf[SK+1] + ctx.buf[RK+2] = cty.buf[SK+2] + ctx.buf[RK+3] = cty.buf[SK+3] + inc(RK, 4) + dec(SK, 4) + + for i in countdown(ctx.nr-1, 1): + for j in 0..3: + let YSK = cty.buf[SK] + ctx.buf[RK] = SBOX.RT0[SBOX.FSb[int(YSK) and 0xFF]] xor + SBOX.RT1[SBOX.FSb[int(YSK shr 8) and 0xFF]] xor + SBOX.RT2[SBOX.FSb[int(YSK shr 16) and 0xFF]] xor + SBOX.RT3[SBOX.FSb[int(YSK shr 24) and 0xFF]] + inc SK + inc RK + dec(SK, 8) + + ctx.buf[RK] = cty.buf[SK] + ctx.buf[RK+1] = cty.buf[SK+1] + ctx.buf[RK+2] = cty.buf[SK+2] + ctx.buf[RK+3] = cty.buf[SK+3] + result = true + +template AES_FROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3: typed): untyped = + X0 = ctx.buf[RK] xor SBOX.FT0[int(Y0 and 0xFF)] xor + SBOX.FT1[int((Y1 shr 8) and 0xFF)] xor + SBOX.FT2[int((Y2 shr 16) and 0xFF)] xor + SBOX.FT3[int((Y3 shr 24) and 0xFF)] + inc RK + + X1 = ctx.buf[RK] xor SBOX.FT0[int(Y1 and 0xFF)] xor + SBOX.FT1[int((Y2 shr 8) and 0xFF)] xor + SBOX.FT2[int((Y3 shr 16) and 0xFF)] xor + SBOX.FT3[int((Y0 shr 24) and 0xFF)] + inc RK + + X2 = ctx.buf[RK] xor SBOX.FT0[int(Y2 and 0xFF)] xor + SBOX.FT1[int((Y3 shr 8) and 0xFF)] xor + SBOX.FT2[int((Y0 shr 16) and 0xFF)] xor + SBOX.FT3[int((Y1 shr 24) and 0xFF)] + inc RK + + X3 = ctx.buf[RK] xor SBOX.FT0[int(Y3 and 0xFF)] xor + SBOX.FT1[int((Y0 shr 8) and 0xFF)] xor + SBOX.FT2[int((Y1 shr 16) and 0xFF)] xor + SBOX.FT3[int((Y2 shr 24) and 0xFF)] + inc RK + +template AES_RROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3: typed): untyped = + X0 = ctx.buf[RK] xor SBOX.RT0[int(Y0 and 0xFF)] xor + SBOX.RT1[int((Y3 shr 8) and 0xFF)] xor + SBOX.RT2[int((Y2 shr 16) and 0xFF)] xor + SBOX.RT3[int((Y1 shr 24) and 0xFF)] + inc RK + + X1 = ctx.buf[RK] xor SBOX.RT0[int(Y1 and 0xFF)] xor + SBOX.RT1[int((Y0 shr 8) and 0xFF)] xor + SBOX.RT2[int((Y3 shr 16) and 0xFF)] xor + SBOX.RT3[int((Y2 shr 24) and 0xFF)] + inc RK + + X2 = ctx.buf[RK] xor SBOX.RT0[int(Y2 and 0xFF)] xor + SBOX.RT1[int((Y1 shr 8) and 0xFF)] xor + SBOX.RT2[int((Y0 shr 16) and 0xFF)] xor + SBOX.RT3[int((Y3 shr 24) and 0xFF)] + inc RK + + X3 = ctx.buf[RK] xor SBOX.RT0[int(Y3 and 0xFF)] xor + SBOX.RT1[int((Y2 shr 8) and 0xFF)] xor + SBOX.RT2[int((Y1 shr 16) and 0xFF)] xor + SBOX.RT3[int((Y0 shr 24) and 0xFF)] + inc RK + +proc encryptECB*(ctx: AESContext, input: cstring, output: var cstring) = + var X0, X1, X2, X3, Y0, Y1, Y2, Y3: uint32 + var RK = 0 + + X0 = GET_ULONG_LE(input, 0) + X1 = GET_ULONG_LE(input, 4) + X2 = GET_ULONG_LE(input, 8) + X3 = GET_ULONG_LE(input, 12) + + X0 = X0 xor ctx.buf[RK] + X1 = X1 xor ctx.buf[RK+1] + X2 = X2 xor ctx.buf[RK+2] + X3 = X3 xor ctx.buf[RK+3] + inc(RK, 4) + + for i in countdown((ctx.nr shr 1) - 1, 1): + AES_FROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3) + AES_FROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3) + + AES_FROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3) + + X0 = ctx.buf[RK] xor uint32(SBOX.FSb[int(Y0 and 0xFF)]) xor + (uint32(SBOX.FSb[int((Y1 shr 8) and 0xFF)]) shl 8) xor + (uint32(SBOX.FSb[int((Y2 shr 16) and 0xFF)]) shl 16) xor + (uint32(SBOX.FSb[int((Y3 shr 24) and 0xFF)]) shl 24) + inc RK + + X1 = ctx.buf[RK] xor uint32(SBOX.FSb[int(Y1 and 0xFF)]) xor + (uint32(SBOX.FSb[int((Y2 shr 8) and 0xFF)]) shl 8) xor + (uint32(SBOX.FSb[int((Y3 shr 16) and 0xFF)]) shl 16) xor + (uint32(SBOX.FSb[int((Y0 shr 24) and 0xFF)]) shl 24) + inc RK + + X2 = ctx.buf[RK] xor uint32(SBOX.FSb[int(Y2 and 0xFF)]) xor + (uint32(SBOX.FSb[int((Y3 shr 8) and 0xFF)]) shl 8) xor + (uint32(SBOX.FSb[int((Y0 shr 16) and 0xFF)]) shl 16) xor + (uint32(SBOX.FSb[int((Y1 shr 24) and 0xFF)]) shl 24) + inc RK + + X3 = ctx.buf[RK] xor uint32(SBOX.FSb[int(Y3 and 0xFF)]) xor + (uint32(SBOX.FSb[int((Y0 shr 8) and 0xFF)]) shl 8) xor + (uint32(SBOX.FSb[int((Y1 shr 16) and 0xFF)]) shl 16) xor + (uint32(SBOX.FSb[int((Y2 shr 24) and 0xFF)]) shl 24) + + PUT_ULONG_LE(X0, output, 0) + PUT_ULONG_LE(X1, output, 4) + PUT_ULONG_LE(X2, output, 8) + PUT_ULONG_LE(X3, output, 12) + +proc encryptECB*(ctx: AESContext, input: string): string = + assert input.len == 16 + result = newString(16) + var output = cstring(result) + ctx.encryptECB(cstring(input), output) + +proc decryptECB*(ctx: AESContext, input: cstring, output: var cstring) = + var X0, X1, X2, X3, Y0, Y1, Y2, Y3: uint32 + var RK = 0 + + X0 = GET_ULONG_LE(input, 0) + X1 = GET_ULONG_LE(input, 4) + X2 = GET_ULONG_LE(input, 8) + X3 = GET_ULONG_LE(input, 12) + + X0 = X0 xor ctx.buf[RK] + X1 = X1 xor ctx.buf[RK+1] + X2 = X2 xor ctx.buf[RK+2] + X3 = X3 xor ctx.buf[RK+3] + inc(RK, 4) + + for i in countdown((ctx.nr shr 1) - 1, 1): + AES_RROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3) + AES_RROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3) + + AES_RROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3) + + X0 = ctx.buf[RK] xor uint32(SBOX.RSb[int(Y0 and 0xFF)]) xor + (uint32(SBOX.RSb[int((Y3 shr 8) and 0xFF)]) shl 8) xor + (uint32(SBOX.RSb[int((Y2 shr 16) and 0xFF)]) shl 16) xor + (uint32(SBOX.RSb[int((Y1 shr 24) and 0xFF)]) shl 24) + inc RK + + X1 = ctx.buf[RK] xor uint32(SBOX.RSb[int(Y1 and 0xFF)]) xor + (uint32(SBOX.RSb[int((Y0 shr 8) and 0xFF)]) shl 8) xor + (uint32(SBOX.RSb[int((Y3 shr 16) and 0xFF)]) shl 16) xor + (uint32(SBOX.RSb[int((Y2 shr 24) and 0xFF)]) shl 24) + inc RK + + X2 = ctx.buf[RK] xor uint32(SBOX.RSb[int(Y2 and 0xFF)]) xor + (uint32(SBOX.RSb[int((Y1 shr 8) and 0xFF)]) shl 8) xor + (uint32(SBOX.RSb[int((Y0 shr 16) and 0xFF)]) shl 16) xor + (uint32(SBOX.RSb[int((Y3 shr 24) and 0xFF)]) shl 24) + inc RK + + X3 = ctx.buf[RK] xor uint32(SBOX.RSb[int(Y3 and 0xFF)]) xor + (uint32(SBOX.RSb[int((Y2 shr 8) and 0xFF)]) shl 8) xor + (uint32(SBOX.RSb[int((Y1 shr 16) and 0xFF)]) shl 16) xor + (uint32(SBOX.RSb[int((Y0 shr 24) and 0xFF)]) shl 24) + + PUT_ULONG_LE(X0, output, 0) + PUT_ULONG_LE(X1, output, 4) + PUT_ULONG_LE(X2, output, 8) + PUT_ULONG_LE(X3, output, 12) + +proc decryptECB*(ctx: AESContext, input: string): string = + assert input.len == 16 + result = newString(16) + var output = cstring(result) + ctx.decryptECB(cstring(input), output) + +proc cryptOFB*(ctx: AESContext, nonce: var cstring, input: string): string = + var len = input.len + if (len mod 16) != 0: return nil + + result = newString(len) + var x = 0 + while len > 0: + var output = cast[cstring](addr(result[x])) + encryptECB(ctx, nonce, output) + copyMem(addr(nonce[0]), output, 16) + + for i in 0..15: + output[i] = chr(ord(output[i]) xor ord(input[x+i])) + + inc(x, 16) + dec(len, 16) + +proc cryptOFB*(ctx: AESContext, nonce: var string, input: string): string = + assert(nonce.len == 16) + assert((input.len mod 16) == 0) + var counter = cstring(nonce) + result = ctx.cryptOFB(counter, input) + +proc encryptCBC*(ctx: AESContext, iv: cstring, input: string): string = + var len = input.len + if (len mod 16) != 0: return nil + + result = newString(len) + var x = 0 + while len > 0: + var output = cast[cstring](addr(result[x])) + + for i in 0..15: + output[i] = chr(ord(input[x+i]) xor ord(iv[i])) + + encryptECB(ctx, output, output) + copyMem(iv, output, 16) + + inc(x, 16) + dec(len, 16) + +proc encryptCBC*(ctx: AESContext, iv: string, input: string): string = + assert iv.len == 16 + result = ctx.encryptCBC(cstring(iv), input) + +proc decryptCBC*(ctx: AESContext, iv: cstring, inp: string): string = + var len = inp.len + if (len mod 16) != 0: return nil + + var data = cstring(inp) + result = newString(len) + var x = 0 + var temp: array[0..15, char] + while len > 0: + var input = cast[cstring](addr(data[x])) + var output = cast[cstring](addr(result[x])) + copyMem(addr(temp[0]), input, 16) + ctx.decryptECB(input, output) + + for i in 0..15: + output[i] = chr(ord(output[i]) xor ord(iv[i])) + + copyMem(iv, addr(temp[0]), 16) + + inc(x, 16) + dec(len, 16) + +proc decryptCBC*(ctx: AESContext, iv: string, input: string): string = + assert iv.len == 16 + result = ctx.decryptCBC(cstring(iv), input) + +proc encryptCFB128*(ctx: AESContext, iv_off: var int, iv: var cstring, input: string): string = + var n = iv_off + var len = input.len + var i = 0 + result = newString(len) + + while len > 0: + if n == 0: encryptECB(ctx, iv, iv) + iv[n] = chr( ord(iv[n]) xor ord(input[i]) ) + result[i] = iv[n] + + n = ( n + 1 ) and 0x0F + dec len + inc i + + iv_off = n + +proc encryptCFB128*(ctx: AESContext, iv_off: var int, iv: var string, input: string): string = + assert iv.len == 16 + var initVector = cstring(iv) + result = ctx.encryptCFB128(iv_off, initVector, input) + +proc decryptCFB128*(ctx: AESContext, iv_off: var int, iv: var cstring, input: string): string = + var n = iv_off + var len = input.len + var i = 0 + result = newString(len) + + while len > 0: + if n == 0: encryptECB(ctx, iv, iv) + result[i] = chr(ord(input[i]) xor ord(iv[n])) + iv[n] = input[i] + + n = ( n + 1 ) and 0x0F + dec len + inc i + + iv_off = n + +proc decryptCFB128*(ctx: AESContext, iv_off: var int, iv: var string, input: string): string = + assert iv.len == 16 + var initVector = cstring(iv) + result = ctx.decryptCFB128(iv_off, initVector, input) + +proc encryptCFB8*(ctx: AESContext, iv: var cstring, input: string): string = + var len = input.len + var i = 0 + result = newString(len) + var ov: array[0..16, char] + + while len > 0: + copyMem(addr(ov), iv, 16) + encryptECB(ctx, iv, iv) + result[i] = chr(ord(iv[0]) xor ord(input[i])) + ov[16] = result[i] + copyMem(iv, addr(ov[1]), 16) + inc i + dec len + +proc encryptCFB8*(ctx: AESContext, iv: var string, input: string): string = + assert iv.len == 16 + var initVector = cstring(iv) + result = ctx.encryptCFB8(initVector, input) + +proc decryptCFB8*(ctx: AESContext, iv: var cstring, input: string): string = + var len = input.len + var i = 0 + result = newString(len) + var ov: array[0..16, char] + + while len > 0: + copyMem(addr(ov), iv, 16) + encryptECB(ctx, iv, iv) + ov[16] = input[i] + result[i] = chr(ord(iv[0]) xor ord(input[i])) + copyMem(iv, addr(ov[1]), 16) + inc i + dec len + +proc decryptCFB8*(ctx: AESContext, iv: var string, input: string): string = + assert iv.len == 16 + var initVector = cstring(iv) + result = ctx.decryptCFB8(initVector, input) + +proc cryptCTR*(ctx: AESContext, nc_off: var int, nonce: var cstring, input: string): string = + var n = nc_off + var x = 0 + var len = input.len + var counter = cast[ptr array[0..15, uint8]](nonce) + + var temp: array[0..15, uint8] + var stream_block = cast[cstring](addr(temp[0])) + result = newString(len) + + while len > 0: + if n == 0: + encryptECB(ctx, nonce, stream_block) + for i in countdown(16, 1): + counter[][i-1] += 1 + if counter[][i-1] != 0: break + + result[x] = chr(ord(input[x]) xor ord(stream_block[n])) + + n = ( n + 1 ) and 0x0F + dec len + inc x + + nc_off = n + +proc cryptCTR*(ctx: AESContext, nc_off: var int, nonce: var string, input: string): string = + assert nonce.len == 16 + var initVector = cstring(nonce) + result = ctx.cryptCTR(nc_off, initVector, input)
A
vendor/nimSHA2.nim
@@ -0,0 +1,405 @@
+# SHA-2 implementation written in nim +# +# Copyright (c) 2015 Andri Lim +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# +#------------------------------------- + +import endians, strutils + +const + SHA256_K = [ + 0x428A2F98'u32, 0x71374491'u32, 0xB5C0FBCF'u32, 0xE9B5DBA5'u32, + 0x3956C25B'u32, 0x59F111F1'u32, 0x923F82A4'u32, 0xAB1C5ED5'u32, + 0xD807AA98'u32, 0x12835B01'u32, 0x243185BE'u32, 0x550C7DC3'u32, + 0x72BE5D74'u32, 0x80DEB1FE'u32, 0x9BDC06A7'u32, 0xC19BF174'u32, + 0xE49B69C1'u32, 0xEFBE4786'u32, 0x0FC19DC6'u32, 0x240CA1CC'u32, + 0x2DE92C6F'u32, 0x4A7484AA'u32, 0x5CB0A9DC'u32, 0x76F988DA'u32, + 0x983E5152'u32, 0xA831C66D'u32, 0xB00327C8'u32, 0xBF597FC7'u32, + 0xC6E00BF3'u32, 0xD5A79147'u32, 0x06CA6351'u32, 0x14292967'u32, + 0x27B70A85'u32, 0x2E1B2138'u32, 0x4D2C6DFC'u32, 0x53380D13'u32, + 0x650A7354'u32, 0x766A0ABB'u32, 0x81C2C92E'u32, 0x92722C85'u32, + 0xA2BFE8A1'u32, 0xA81A664B'u32, 0xC24B8B70'u32, 0xC76C51A3'u32, + 0xD192E819'u32, 0xD6990624'u32, 0xF40E3585'u32, 0x106AA070'u32, + 0x19A4C116'u32, 0x1E376C08'u32, 0x2748774C'u32, 0x34B0BCB5'u32, + 0x391C0CB3'u32, 0x4ED8AA4A'u32, 0x5B9CCA4F'u32, 0x682E6FF3'u32, + 0x748F82EE'u32, 0x78A5636F'u32, 0x84C87814'u32, 0x8CC70208'u32, + 0x90BEFFFA'u32, 0xA4506CEB'u32, 0xBEF9A3F7'u32, 0xC67178F2'u32] + + SHA512_K = [ + 0x428A2F98D728AE22'u64, 0x7137449123EF65CD'u64, + 0xB5C0FBCFEC4D3B2F'u64, 0xE9B5DBA58189DBBC'u64, + 0x3956C25BF348B538'u64, 0x59F111F1B605D019'u64, + 0x923F82A4AF194F9B'u64, 0xAB1C5ED5DA6D8118'u64, + 0xD807AA98A3030242'u64, 0x12835B0145706FBE'u64, + 0x243185BE4EE4B28C'u64, 0x550C7DC3D5FFB4E2'u64, + 0x72BE5D74F27B896F'u64, 0x80DEB1FE3B1696B1'u64, + 0x9BDC06A725C71235'u64, 0xC19BF174CF692694'u64, + 0xE49B69C19EF14AD2'u64, 0xEFBE4786384F25E3'u64, + 0x0FC19DC68B8CD5B5'u64, 0x240CA1CC77AC9C65'u64, + 0x2DE92C6F592B0275'u64, 0x4A7484AA6EA6E483'u64, + 0x5CB0A9DCBD41FBD4'u64, 0x76F988DA831153B5'u64, + 0x983E5152EE66DFAB'u64, 0xA831C66D2DB43210'u64, + 0xB00327C898FB213F'u64, 0xBF597FC7BEEF0EE4'u64, + 0xC6E00BF33DA88FC2'u64, 0xD5A79147930AA725'u64, + 0x06CA6351E003826F'u64, 0x142929670A0E6E70'u64, + 0x27B70A8546D22FFC'u64, 0x2E1B21385C26C926'u64, + 0x4D2C6DFC5AC42AED'u64, 0x53380D139D95B3DF'u64, + 0x650A73548BAF63DE'u64, 0x766A0ABB3C77B2A8'u64, + 0x81C2C92E47EDAEE6'u64, 0x92722C851482353B'u64, + 0xA2BFE8A14CF10364'u64, 0xA81A664BBC423001'u64, + 0xC24B8B70D0F89791'u64, 0xC76C51A30654BE30'u64, + 0xD192E819D6EF5218'u64, 0xD69906245565A910'u64, + 0xF40E35855771202A'u64, 0x106AA07032BBD1B8'u64, + 0x19A4C116B8D2D0C8'u64, 0x1E376C085141AB53'u64, + 0x2748774CDF8EEB99'u64, 0x34B0BCB5E19B48A8'u64, + 0x391C0CB3C5C95A63'u64, 0x4ED8AA4AE3418ACB'u64, + 0x5B9CCA4F7763E373'u64, 0x682E6FF3D6B2B8A3'u64, + 0x748F82EE5DEFB2FC'u64, 0x78A5636F43172F60'u64, + 0x84C87814A1F0AB72'u64, 0x8CC702081A6439EC'u64, + 0x90BEFFFA23631E28'u64, 0xA4506CEBDE82BDE9'u64, + 0xBEF9A3F7B2C67915'u64, 0xC67178F2E372532B'u64, + 0xCA273ECEEA26619C'u64, 0xD186B8C721C0C207'u64, + 0xEADA7DD6CDE0EB1E'u64, 0xF57D4F7FEE6ED178'u64, + 0x06F067AA72176FBA'u64, 0x0A637DC5A2C898A6'u64, + 0x113F9804BEF90DAE'u64, 0x1B710B35131C471B'u64, + 0x28DB77F523047D84'u64, 0x32CAAB7B40C72493'u64, + 0x3C9EBE0A15C9BEBC'u64, 0x431D67C49C100D4C'u64, + 0x4CC5D4BECB3E42B6'u64, 0x597F299CFC657E2A'u64, + 0x5FCB6FAB3AD6FAEC'u64, 0x6C44198C4A475817'u64] + +type + SHA2Ctx = object of RootObj + count: array[0..1, uint32] + + SHA224* = object of SHA2Ctx + state: array[0..7, uint32] + buffer: array[0..63, uint8] + + SHA256* = object of SHA224 + + SHA384* = object of SHA2Ctx + state: array[0..7, uint64] + buffer: array[0..127, uint8] + + SHA512* = object of SHA384 + + SHA224Digest* = array[0..27, char] + SHA256Digest* = array[0..31, char] + SHA384Digest* = array[0..47, char] + SHA512Digest* = array[0..63, char] + +proc initSHA*(ctx: var SHA224) = + ctx.count[0] = 0 + ctx.count[1] = 0 + ctx.state[0] = 0xC1059ED8'u32 + ctx.state[1] = 0x367CD507'u32 + ctx.state[2] = 0x3070DD17'u32 + ctx.state[3] = 0xF70E5939'u32 + ctx.state[4] = 0xFFC00B31'u32 + ctx.state[5] = 0x68581511'u32 + ctx.state[6] = 0x64F98FA7'u32 + ctx.state[7] = 0xBEFA4FA4'u32 + +proc initSHA*(ctx: var SHA256) = + ctx.count[0] = 0 + ctx.count[1] = 0 + ctx.state[0] = 0x6A09E667'u32 + ctx.state[1] = 0xBB67AE85'u32 + ctx.state[2] = 0x3C6EF372'u32 + ctx.state[3] = 0xA54FF53A'u32 + ctx.state[4] = 0x510E527F'u32 + ctx.state[5] = 0x9B05688C'u32 + ctx.state[6] = 0x1F83D9AB'u32 + ctx.state[7] = 0x5BE0CD19'u32 + +proc initSHA*(ctx: var SHA384) = + ctx.count[0] = 0 + ctx.count[1] = 0 + ctx.state[0] = 0xCBBB9D5DC1059ED8'u64 + ctx.state[1] = 0x629A292A367CD507'u64 + ctx.state[2] = 0x9159015A3070DD17'u64 + ctx.state[3] = 0x152FECD8F70E5939'u64 + ctx.state[4] = 0x67332667FFC00B31'u64 + ctx.state[5] = 0x8EB44A8768581511'u64 + ctx.state[6] = 0xDB0C2E0D64F98FA7'u64 + ctx.state[7] = 0x47B5481DBEFA4FA4'u64 + +proc initSHA*(ctx: var SHA512) = + ctx.count[0] = 0 + ctx.count[1] = 0 + ctx.state[0] = 0x6A09E667F3BCC908'u64 + ctx.state[1] = 0xBB67AE8584CAA73B'u64 + ctx.state[2] = 0x3C6EF372FE94F82B'u64 + ctx.state[3] = 0xA54FF53A5F1D36F1'u64 + ctx.state[4] = 0x510E527FADE682D1'u64 + ctx.state[5] = 0x9B05688C2B3E6C1F'u64 + ctx.state[6] = 0x1F83D9ABFB41BD6B'u64 + ctx.state[7] = 0x5BE0CD19137E2179'u64 + +proc initSHA*[T](): T = + result.initSHA() + +proc GET_UINT32_BE(b: cstring, i: int): uint32 = + var val = b + bigEndian32(addr(result), addr(val[i])) + +proc PUT_UINT32_BE(n: uint32, b: var cstring, i: int) = + var val = n + bigEndian32(addr(b[i]), addr(val)) + +proc GET_UINT64_BE(b: cstring, i: int): uint64 = + var val = b + bigEndian64(addr(result), addr(val[i])) + +proc PUT_UINT64_BE(n: uint64, b: var cstring, i: int) = + var val = n + bigEndian64(addr(b[i]), addr(val)) + +template a(i:int):untyped = T[(0 - i) and 7] +template b(i:int):untyped = T[(1 - i) and 7] +template c(i:int):untyped = T[(2 - i) and 7] +template d(i:int):untyped = T[(3 - i) and 7] +template e(i:int):untyped = T[(4 - i) and 7] +template f(i:int):untyped = T[(5 - i) and 7] +template g(i:int):untyped = T[(6 - i) and 7] +template h(i:int):untyped = T[(7 - i) and 7] + +proc Ch[T: uint32|uint64](x, y, z: T): T {.inline.} = (z xor (x and (y xor z))) +proc Maj[T: uint32|uint64](x, y, z: T): T {.inline.} = ((x and y) or (z and (x or y))) +proc rotr[T: uint32|uint64](num: T, amount: int): T {.inline.} = + result = (num shr T(amount)) or (num shl T(8 * sizeof(num) - amount)) + +template R(i: int): untyped = + h(i) += S1(e(i)) + Ch(e(i), f(i), g(i)) + K[i + j] + + if j != 0: + W[i and 15] += S3(W[(i - 2) and 15]) + W[(i - 7) and 15] + S2(W[(i - 15) and 15]) + h(i) += W[i and 15] + else: + W[i] = data[i] + h(i) += W[i] + + d(i) += h(i) + h(i) += S0(a(i)) + Maj(a(i), b(i), c(i)) + +proc transform256(state: var array[0..7, uint32], input: cstring) = + let K = SHA256_K + var W, data: array[0..15, uint32] + for i in countup(0, 15): data[i] = GET_UINT32_BE(input, i * 4) + + var T: array[0..7, uint32] + for i in 0..7: T[i] = state[i] + + proc S0(x:uint32): uint32 {.inline.} = (rotr(x, 2) xor rotr(x, 13) xor rotr(x, 22)) + proc S1(x:uint32): uint32 {.inline.} = (rotr(x, 6) xor rotr(x, 11) xor rotr(x, 25)) + proc S2(x:uint32): uint32 {.inline.} = (rotr(x, 7) xor rotr(x, 18) xor (x shr 3)) + proc S3(x:uint32): uint32 {.inline.} = (rotr(x, 17) xor rotr(x, 19) xor (x shr 10)) + + for j in countup(0, 63, 16): + R( 0); R( 1); R( 2); R( 3) + R( 4); R( 5); R( 6); R( 7) + R( 8); R( 9); R(10); R(11) + R(12); R(13); R(14); R(15) + + state[0] += a(0) + state[1] += b(0) + state[2] += c(0) + state[3] += d(0) + state[4] += e(0) + state[5] += f(0) + state[6] += g(0) + state[7] += h(0) + +proc update*(ctx: var SHA224, input: string) = + var len = input.len + var data = cstring(input) + var pos = 0 + while len > 0: + let copy_start = int(ctx.count[0] and 0x3F) + let copy_size = min(64 - copy_start, len) + copyMem(addr(ctx.buffer[copy_start]), addr(data[pos]), copy_size) + + inc(pos, copy_size) + dec(len, copy_size) + + ctx.count[0] += uint32(copy_size) + # carry overflow from low to high + if ctx.count[0] < uint32(copy_size): ctx.count[1] += 1'u32 + + if (ctx.count[0] and 0x3F) == 0: + transform256(ctx.state, cast[cstring](addr(ctx.buffer[0]))) + +proc update*(ctx: var SHA256, input: string) {.inline.} = + SHA224(ctx).update(input) + +proc transform512(state: var array[0..7, uint64], input: cstring) = + let K = SHA512_K + var W, data: array[0..15, uint64] + for i in countup(0, 15): data[i] = GET_UINT64_BE(input, i * 8) + + var T: array[0..7, uint64] + for i in 0..7: T[i] = state[i] + + proc S0(x:uint64):uint64 {.inline.} = (rotr(x, 28) xor rotr(x, 34) xor rotr(x, 39)) + proc S1(x:uint64):uint64 {.inline.} = (rotr(x, 14) xor rotr(x, 18) xor rotr(x, 41)) + proc S2(x:uint64):uint64 {.inline.} = (rotr(x, 1) xor rotr(x, 8) xor (x shr 7)) + proc S3(x:uint64):uint64 {.inline.} = (rotr(x, 19) xor rotr(x, 61) xor (x shr 6)) + + # 80 operations, partially loop unrolled + for j in countup(0, 79, 16): + R( 0); R( 1); R( 2); R( 3) + R( 4); R( 5); R( 6); R( 7) + R( 8); R( 9); R(10); R(11) + R(12); R(13); R(14); R(15) + + # Add the working vars back into state[]. + state[0] += a(0) + state[1] += b(0) + state[2] += c(0) + state[3] += d(0) + state[4] += e(0) + state[5] += f(0) + state[6] += g(0) + state[7] += h(0) + +proc update*(ctx: var SHA384, input: string) = + var len = input.len + var data = cstring(input) + var pos = 0 + while len > 0: + let copy_start = int(ctx.count[0] and 0x7F) + let copy_size = min(128 - copy_start, len) + copyMem(addr(ctx.buffer[copy_start]), addr(data[pos]), copy_size) + + inc(pos, copy_size) + dec(len, copy_size) + + ctx.count[0] += uint32(copy_size) + # carry overflow from low to high + if ctx.count[0] < uint32(copy_size): ctx.count[1] += 1'u32 + + if (ctx.count[0] and 0x7F) == 0: + transform512(ctx.state, cast[cstring](addr(ctx.buffer[0]))) + +proc update*(ctx: var SHA512, input: string) {.inline.} = + SHA384(ctx).update(input) + +proc final224_256(ctx: var SHA224) = + var buffer = cast[cstring](addr(ctx.buffer[0])) + # Add padding as described in RFC 3174 (it describes SHA-1 but + # the same padding style is used for SHA-256 too). + var j = int(ctx.count[0] and 0x3F) + ctx.buffer[j] = 0x80 + inc j + + while j != 56: + if j == 64: + transform256(ctx.state, buffer) + j = 0 + ctx.buffer[j] = 0x00 + inc j + + # Convert the message size from bytes to bits. + ctx.count[1] = (ctx.count[1] shl 3) + (ctx.count[0] shr 29) + ctx.count[0] = ctx.count[0] shl 3 + + PUT_UINT32_BE(ctx.count[1], buffer, 14 * 4) + PUT_UINT32_BE(ctx.count[0], buffer, 15 * 4) + transform256(ctx.state, buffer) + +proc final*(ctx: var SHA224): SHA224Digest = + ctx.final224_256() + var output = cast[cstring](addr(result[0])) + for i in 0..6: + PUT_UINT32_BE(ctx.state[i], output, i * 4) + +proc final*(ctx: var SHA256): SHA256Digest = + SHA224(ctx).final224_256() + var output = cast[cstring](addr(result[0])) + for i in 0..7: + PUT_UINT32_BE(ctx.state[i], output, i * 4) + +proc final384_512(ctx: var SHA384) = + var buffer = cast[cstring](addr(ctx.buffer[0])) + + # Add padding as described in RFC 3174 (it describes SHA-1 but + # the same padding style is used for SHA-512 too). + var j = int(ctx.count[0] and 0x7F) + ctx.buffer[j] = 0x80 + inc j + + while j != 112: + if j == 128: + transform512(ctx.state, buffer) + j = 0 + ctx.buffer[j] = 0x00 + inc j + + # Convert the message size from bytes to bits. + ctx.count[1] = (ctx.count[1] shl 3) + (ctx.count[0] shr 29) + ctx.count[0] = ctx.count[0] shl 3 + + PUT_UINT64_BE(ctx.count[1], buffer, 14 * 8) + PUT_UINT64_BE(ctx.count[0], buffer, 15 * 8) + transform512(ctx.state, buffer) + +proc final*(ctx: var SHA384): SHA384Digest = + ctx.final384_512() + var output = cast[cstring](addr(result[0])) + for i in 0..5: + PUT_UINT64_BE(ctx.state[i], output, i * 8) + +proc final*(ctx: var SHA512): SHA512Digest = + ctx.final384_512() + var output = cast[cstring](addr(result[0])) + for i in 0..7: + PUT_UINT64_BE(ctx.state[i], output, i * 8) + +proc computeSHA[T, R](input: string, rep: int): R = + var ctx: T + ctx.initSHA() + for i in 0..rep-1: ctx.update(input) + result = ctx.final() + +proc computeSHA224*(input: string, rep: int = 1): SHA224Digest = computeSHA[SHA224, SHA224Digest](input, rep) +proc computeSHA256*(input: string, rep: int = 1): SHA256Digest = computeSHA[SHA256, SHA256Digest](input, rep) +proc computeSHA384*(input: string, rep: int = 1): SHA384Digest = computeSHA[SHA384, SHA384Digest](input, rep) +proc computeSHA512*(input: string, rep: int = 1): SHA512Digest = computeSHA[SHA512, SHA512Digest](input, rep) + +proc toString[T](input: T): string = + result = newString(input.len) + for i in 0..input.len-1: result[i] = input[i] + +proc `$`*(sha: SHA224Digest): string = toString(sha) +proc `$`*(sha: SHA256Digest): string = toString(sha) +proc `$`*(sha: SHA384Digest): string = toString(sha) +proc `$`*(sha: SHA512Digest): string = toString(sha) + +proc toHexImpl[T](input: T): string = + result = "" + for c in input: + result.add toHex(ord(c), 2) + +proc hex*(sha: SHA224Digest): string = toHexImpl(sha) +proc hex*(sha: SHA256Digest): string = toHexImpl(sha) +proc hex*(sha: SHA384Digest): string = toHexImpl(sha) +proc hex*(sha: SHA512Digest): string = toHexImpl(sha)
A
vendor/sha1.nim
@@ -0,0 +1,211 @@
+#Copyright (c) 2011, Micael Hildenborg +#All rights reserved. + +#Redistribution and use in source and binary forms, with or without +#modification, are permitted provided that the following conditions are met: +#* Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +#* Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +#* Neither the name of Micael Hildenborg nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. + +#THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY +#EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +#DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY +#DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +#(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +#LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +#ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +#(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +#SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +#Ported to Nimrod by Erik O'Leary +# + +## Imports +import unsigned, strutils, base64 + +## Fields +const sha_digest_size = 20 + +## Types +type + SHA1State = array[0 .. 5-1, uint32] + SHA1Buffer = array[0 .. 80-1, uint32] + SHA1Digest* = array[0 .. sha_digest_size-1, uint8] + +## Templates & Procedures +template clearBuffer(w: SHA1Buffer, len = 16) = + zeroMem(addr(w), len * sizeof(uint32)) + +proc toHex*(digest: SHA1Digest): string = + const digits = "0123456789abcdef" + + var arr: array[0 .. sha_digest_size*2, char] + + for hashByte in countdown(20-1, 0): + arr[hashByte shl 1] = digits[(digest[hashByte] shr 4) and 0xf] + arr[(hashByte shl 1) + 1] = digits[(digest[hashByte]) and 0xf] + + return $arr + +proc toBase64*(digest: SHA1Digest): string = base64.encode(digest) + +proc init(result: var SHA1State) = + result[0] = 0x67452301'u32 + result[1] = 0xefcdab89'u32 + result[2] = 0x98badcfe'u32 + result[3] = 0x10325476'u32 + result[4] = 0xc3d2e1f0'u32 + +proc innerHash(state: var SHA1State, w: var SHA1Buffer) = + var + a = state[0] + b = state[1] + c = state[2] + d = state[3] + e = state[4] + + var round = 0 + + template rot(value, bits: uint32): uint32 {.immediate.} = + (value shl bits) or (value shr (32 - bits)) + + template sha1(fun, val: uint32): stmt = + let t = rot(a, 5) + fun + e + val + w[round] + e = d + d = c + c = rot(b, 30) + b = a + a = t + + template process(body: stmt): stmt = + w[round] = rot(w[round - 3] xor w[round - 8] xor w[round - 14] xor w[round - 16], 1) + body + inc(round) + + template wrap(dest, value: expr): stmt {.immediate.} = + let v = dest + value + dest = v + + while round < 16: + sha1((b and c) or (not b and d), 0x5a827999'u32) + inc(round) + + while round < 20: + process: + sha1((b and c) or (not b and d), 0x5a827999'u32) + + while round < 40: + process: + sha1(b xor c xor d, 0x6ed9eba1'u32) + + while round < 60: + process: + sha1((b and c) or (b and d) or (c and d), 0x8f1bbcdc'u32) + + while round < 80: + process: + sha1(b xor c xor d, 0xca62c1d6'u32) + + wrap state[0], a + wrap state[1], b + wrap state[2], c + wrap state[3], d + wrap state[4], e + +template computeInternal(src: expr): stmt {.immediate.} = + #Initialize state + var state: SHA1State + init(state) + + #Create w buffer + var w: SHA1Buffer + + #Loop through all complete 64byte blocks. + let byteLen = src.len + let endOfFullBlocks = byteLen - 64 + var endCurrentBlock = 0 + var currentBlock = 0 + + while currentBlock <= endOfFullBlocks: + endCurrentBlock = currentBlock + 64 + + var i = 0 + while currentBlock < endCurrentBlock: + w[i] = uint32(src[currentBlock+3]) or + uint32(src[currentBlock+2]) shl 8'u32 or + uint32(src[currentBlock+1]) shl 16'u32 or + uint32(src[currentBlock]) shl 24'u32 + currentBlock += 4 + inc(i) + + innerHash(state, w) + + #Handle last and not full 64 byte block if existing + endCurrentBlock = byteLen - currentBlock + clearBuffer(w) + var lastBlockBytes = 0 + + while lastBlockBytes < endCurrentBlock: + + var value = uint32(src[lastBlockBytes + currentBlock]) shl + ((3'u32 - (lastBlockBytes and 3)) shl 3) + + w[lastBlockBytes shr 2] = w[lastBlockBytes shr 2] or value + inc(lastBlockBytes) + + w[lastBlockBytes shr 2] = w[lastBlockBytes shr 2] or ( + 0x80'u32 shl ((3'u32 - (lastBlockBytes and 3)) shl 3) + ) + + if endCurrentBlock >= 56: + innerHash(state, w) + clearBuffer(w) + + w[15] = uint32(byteLen) shl 3 + innerHash(state, w) + + # Store hash in result pointer, and make sure we get in in the correct order on both endian models. + for i in 0 .. sha_digest_size-1: + result[i] = uint8((int(state[i shr 2]) shr ((3-(i and 3)) * 8)) and 255) + +proc compute*(src: string) : SHA1Digest = + ## Calculate SHA1 from input string + computeInternal(src) + +proc compute*(src: openarray[TInteger|char]) : SHA1Digest = + ## Calculate SHA1 from input array + computeInternal(src) + +when isMainModule: + var result: string + + #test sha1 - char array input + result = compute(@['s','h','o','r','t','e','r']).toHex() + echo result + assert(result == "c966b463b67c6424fefebcfcd475817e379065c7", "SHA1 result did not match") + + #test sha1 - 60 char input + result = compute("JhWAN0ZTmRS2maaZmDfLyQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11").toHex() + echo result + assert(result == "e3571af6b12bcb49c87012a5bb5fdd2bada788a4", "SHA1 result did not match") + + #test sha1 - longer input + result = compute("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz").toHex() + echo result + assert(result == "f2090afe4177d6f288072a474804327d0f481ada", "SHA1 result did not match") + + #test sha1 - shorter input + result = compute("shorter").toHex() + echo result + assert(result == "c966b463b67c6424fefebcfcd475817e379065c7", "SHA1 result did not match") + + #test base64 encoding + result = compute("dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11").toBase64() + echo result + assert(result == "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", "SHA1 base64 result did not match")