├── doc └── fips-180-4.pdf ├── tests ├── nim.cfg └── test.nim ├── .gitattributes ├── nimSHA2.nimble ├── .gitignore ├── readme.md ├── LICENSE ├── .github └── workflows │ └── ci.yml └── nimSHA2.nim /doc/fips-180-4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jangko/nimSHA2/HEAD/doc/fips-180-4.pdf -------------------------------------------------------------------------------- /tests/nim.cfg: -------------------------------------------------------------------------------- 1 | # testvcc stuff 2 | vcc.exe = "cl.exe" 3 | vcc.cpp.exe = "cl.exe" 4 | vcc.linkerexe = "cl.exe" 5 | vcc.cpp.linkerexe = "cl.exe" 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /nimSHA2.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | version = "0.1.1" 3 | author = "Andri Lim" 4 | description = "SHA2 secure hash algorithm - 2 - [224, 256, 384, 512 bits]" 5 | license = "MIT" 6 | skipDirs = @["tests", "docs"] 7 | 8 | # Deps 9 | requires "nim >= 0.11.2" 10 | 11 | ### Helper functions 12 | proc test(env, path: string) = 13 | # Compilation language is controlled by TEST_LANG 14 | var lang = "c" 15 | if existsEnv"TEST_LANG": 16 | lang = getEnv"TEST_LANG" 17 | 18 | exec "nim " & lang & " " & env & 19 | " -r --hints:off --warnings:off " & path 20 | 21 | task test, "Run tests": 22 | test "-d:release", "tests/test" 23 | 24 | task testvcc, "Run tests with vcc compiler": 25 | test "--cc:vcc -d:release", "tests/test" 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | nimcache 45 | *.exe -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # nimSHA2 2 | 3 | ![Github action](https://github.com/jangko/nimSHA2/workflows/nimSHA2%20CI/badge.svg) 4 | 5 | ### Secure Hash Algorithm 2 6 | ##### 224, 256, 384, and 512 bits 7 | 8 | initially this was part of [nimPDF](https://github.com/jangko/nimpdf) project, then I decided to release it separately 9 | 10 | example: 11 | 12 | ```nimrod 13 | var sha = initSHA[SHA256]() 14 | sha.update("test SHA256") 15 | let digest = sha.final() 16 | ``` 17 | 18 | or: 19 | ```nimrod 20 | let digest = computeSHA256("test SHA256") 21 | ``` 22 | 23 | **conversion:** 24 | 25 | - to convert digest into string, use proc $ 26 | - to convert digest into hex string, use toHex 27 | 28 | each version of SHA2 hash function will produce output according to it's name: SHA224 produce 224 bits hash, SHA256 produce 256 bits hash, and so on 29 | 30 | already tested with test vector from [here](http://www.di-mgt.com.au/sha_testvectors.html), see `tests/test.nim` 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Andri Lim 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /tests/test.nim: -------------------------------------------------------------------------------- 1 | import ../nimSHA2, strutils, unittest 2 | 3 | type 4 | SHATest = tuple[text,v224,v256,v384,v512:string] 5 | 6 | const 7 | testVector: array[0..4, SHATest] = 8 | [("abc", 9 | "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", 10 | "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", 11 | "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7", 12 | "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"), 13 | 14 | ("", 15 | "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", 16 | "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", 17 | "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b", 18 | "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"), 19 | 20 | ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 21 | "75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525", 22 | "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1", 23 | "3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b", 24 | "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445"), 25 | 26 | (repeat('a', 1000), # one million 'a' 27 | "20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67", 28 | "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0", 29 | "9d0e1809716474cb086e834e310a4a1ced149e9c00f248527972cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985", 30 | "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b"), 31 | 32 | ("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno", # 16,777,216 times 33 | "b5989713ca4fe47a009f8621980b34e6d63ed3063b2a0a2c867d8a85", 34 | "50e72a0e26442fe2552dc3938ac58658228c0cbfb1d2ca872ae435266fcd055e", 35 | "5441235cc0235341ed806a64fb354742b5e5c02a3c5cb71b5f63fb793458d8fdae599c8cd8884943c04f11b31b89f023", 36 | "b47c933421ea2db149ad6e10fce6c7f93d0752380180ffd7f4629a712134831d77be6091b819ed352c2967a2e2d4fa5050723c9630691f1a05a7281dbe6c1086")] 37 | 38 | template toSHA(input: string, len: int): untyped = 39 | for i in 0..len-1: 40 | result[i] = uint8(parseHexInt(input[i*2] & input[i*2 + 1])) 41 | 42 | proc toSHA224(input: string): SHA224Digest = input.toSHA(result.len) 43 | proc toSHA256(input: string): SHA256Digest = input.toSHA(result.len) 44 | proc toSHA384(input: string): SHA384Digest = input.toSHA(result.len) 45 | proc toSHA512(input: string): SHA512Digest = input.toSHA(result.len) 46 | 47 | proc testVec(i: int, rep: int = 1) = 48 | let vec224 = toSHA224(testVector[i].v224) 49 | let vec256 = toSHA256(testVector[i].v256) 50 | let vec384 = toSHA384(testVector[i].v384) 51 | let vec512 = toSHA512(testVector[i].v512) 52 | let in224 = computeSHA224(testVector[i].text, rep) 53 | let in256 = computeSHA256(testVector[i].text, rep) 54 | let in384 = computeSHA384(testVector[i].text, rep) 55 | let in512 = computeSHA512(testVector[i].text, rep) 56 | doAssert vec224 == in224 57 | doAssert vec256 == in256 58 | doAssert vec384 == in384 59 | doAssert vec512 == in512 60 | 61 | proc test() = 62 | suite "nimSHA2 test suite": 63 | test "test vector": 64 | for i in 0..2: testVec(i) 65 | testVec(3, 1000) #one million 'a' 66 | #echo "OK1" 67 | #testVec(4, 16_777_216) #take a while 68 | #echo "OK2" 69 | 70 | test "computeSHA vs init..final": 71 | var sha = initSHA[SHA256]() 72 | sha.update("test SHA256") 73 | let digest = sha.final() 74 | 75 | let digest2 = computeSHA256("test SHA256") 76 | doAssert digest == digest2 77 | 78 | test() 79 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: nimSHA2 CI 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | build: 6 | strategy: 7 | fail-fast: false 8 | max-parallel: 20 9 | matrix: 10 | test_lang: [c, cpp] 11 | target: 12 | - os: linux 13 | cpu: amd64 14 | - os: linux 15 | cpu: i386 16 | - os: macos 17 | cpu: amd64 18 | - os: windows 19 | cpu: amd64 20 | - os: windows 21 | cpu: i386 22 | include: 23 | - target: 24 | os: linux 25 | builder: ubuntu-18.04 26 | - target: 27 | os: macos 28 | builder: macos-10.15 29 | - target: 30 | os: windows 31 | builder: windows-2019 32 | 33 | name: '${{ matrix.target.os }}-${{ matrix.target.cpu }}-${{ matrix.test_lang}}' 34 | runs-on: ${{ matrix.builder }} 35 | steps: 36 | - name: Checkout nimSHA2 37 | uses: actions/checkout@v2 38 | with: 39 | path: nimSHA2 40 | submodules: false 41 | 42 | - name: Install build dependencies (Linux i386) 43 | if: runner.os == 'Linux' && matrix.target.cpu == 'i386' 44 | run: | 45 | sudo dpkg --add-architecture i386 46 | sudo apt-fast update -qq 47 | sudo DEBIAN_FRONTEND='noninteractive' apt-fast install \ 48 | --no-install-recommends -yq gcc-multilib g++-multilib \ 49 | libssl-dev:i386 50 | mkdir -p external/bin 51 | cat << EOF > external/bin/gcc 52 | #!/bin/bash 53 | exec $(which gcc) -m32 "\$@" 54 | EOF 55 | cat << EOF > external/bin/g++ 56 | #!/bin/bash 57 | exec $(which g++) -m32 "\$@" 58 | EOF 59 | chmod 755 external/bin/gcc external/bin/g++ 60 | echo '${{ github.workspace }}/external/bin' >> $GITHUB_PATH 61 | 62 | - name: Restore MinGW-W64 (Windows) from cache 63 | if: runner.os == 'Windows' 64 | id: windows-mingw-cache 65 | uses: actions/cache@v2 66 | with: 67 | path: external/mingw-${{ matrix.target.cpu }} 68 | key: 'mingw-${{ matrix.target.cpu }}' 69 | 70 | - name: Restore Nim DLLs dependencies (Windows) from cache 71 | if: runner.os == 'Windows' 72 | id: windows-dlls-cache 73 | uses: actions/cache@v2 74 | with: 75 | path: external/dlls-${{ matrix.target.cpu }} 76 | key: 'dlls-${{ matrix.target.cpu }}' 77 | 78 | - name: Install MinGW64 dependency (Windows) 79 | if: > 80 | steps.windows-mingw-cache.outputs.cache-hit != 'true' && 81 | runner.os == 'Windows' 82 | shell: bash 83 | run: | 84 | mkdir -p external 85 | if [[ '${{ matrix.target.cpu }}' == 'amd64' ]]; then 86 | MINGW_URL="https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/8.1.0/threads-posix/seh/x86_64-8.1.0-release-posix-seh-rt_v6-rev0.7z" 87 | ARCH=64 88 | else 89 | MINGW_URL="https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/8.1.0/threads-posix/dwarf/i686-8.1.0-release-posix-dwarf-rt_v6-rev0.7z" 90 | ARCH=32 91 | fi 92 | curl -L "$MINGW_URL" -o "external/mingw-${{ matrix.target.cpu }}.7z" 93 | 7z x -y "external/mingw-${{ matrix.target.cpu }}.7z" -oexternal/ 94 | mv external/mingw$ARCH external/mingw-${{ matrix.target.cpu }} 95 | 96 | - name: Install DLLs dependencies (Windows) 97 | if: > 98 | steps.windows-dlls-cache.outputs.cache-hit != 'true' && 99 | runner.os == 'Windows' 100 | shell: bash 101 | run: | 102 | mkdir -p external 103 | curl -L "https://nim-lang.org/download/windeps.zip" -o external/windeps.zip 104 | 7z x -y external/windeps.zip -oexternal/dlls-${{ matrix.target.cpu }} 105 | 106 | - name: Path to cached dependencies (Windows) 107 | if: > 108 | runner.os == 'Windows' 109 | shell: bash 110 | run: | 111 | echo '${{ github.workspace }}'"/external/mingw-${{ matrix.target.cpu }}/bin" >> $GITHUB_PATH 112 | echo '${{ github.workspace }}'"/external/dlls-${{ matrix.target.cpu }}" >> $GITHUB_PATH 113 | 114 | - name: Setup environment 115 | shell: bash 116 | run: echo '${{ github.workspace }}/nim/bin' >> $GITHUB_PATH 117 | 118 | - name: Get latest Nim commit hash 119 | id: versions 120 | shell: bash 121 | run: | 122 | getHash() { 123 | git ls-remote "https://github.com/$1" "${2:-HEAD}" | cut -f 1 124 | } 125 | nimHash=$(getHash nim-lang/Nim devel) 126 | csourcesHash=$(getHash nim-lang/csources_v1) 127 | echo "::set-output name=nim::$nimHash" 128 | echo "::set-output name=csources::$csourcesHash" 129 | 130 | - name: Restore prebuilt Nim from cache 131 | id: nim-cache 132 | uses: actions/cache@v1 133 | with: 134 | path: nim 135 | key: 'nim-${{ matrix.target.os }}-${{ matrix.target.cpu }}-${{ steps.versions.outputs.nim }}' 136 | 137 | - name: Restore prebuilt csources from cache 138 | if: steps.nim-cache.outputs.cache-hit != 'true' 139 | id: csources-cache 140 | uses: actions/cache@v1 141 | with: 142 | path: csources/bin 143 | key: 'csources-${{ matrix.target.os }}-${{ matrix.target.cpu }}-${{ steps.versions.outputs.csources }}' 144 | 145 | - name: Checkout Nim csources 146 | if: > 147 | steps.csources-cache.outputs.cache-hit != 'true' && 148 | steps.nim-cache.outputs.cache-hit != 'true' 149 | uses: actions/checkout@v2 150 | with: 151 | repository: nim-lang/csources_v1 152 | path: csources 153 | ref: ${{ steps.versions.outputs.csources }} 154 | 155 | - name: Checkout Nim 156 | if: steps.nim-cache.outputs.cache-hit != 'true' 157 | uses: actions/checkout@v2 158 | with: 159 | repository: nim-lang/Nim 160 | path: nim 161 | ref: ${{ steps.versions.outputs.nim }} 162 | 163 | - name: Build Nim and associated tools 164 | if: steps.nim-cache.outputs.cache-hit != 'true' 165 | shell: bash 166 | run: | 167 | ncpu= 168 | ext= 169 | case '${{ runner.os }}' in 170 | 'Linux') 171 | ncpu=$(nproc) 172 | ;; 173 | 'macOS') 174 | ncpu=$(sysctl -n hw.ncpu) 175 | ;; 176 | 'Windows') 177 | ncpu=$NUMBER_OF_PROCESSORS 178 | ext=.exe 179 | ;; 180 | esac 181 | [[ -z "$ncpu" || $ncpu -le 0 ]] && ncpu=1 182 | if [[ ! -e csources/bin/nim$ext ]]; then 183 | make -C csources -j $ncpu CC=gcc ucpu='${{ matrix.target.cpu }}' 184 | else 185 | echo 'Using prebuilt csources' 186 | fi 187 | cp -v csources/bin/nim$ext nim/bin 188 | cd nim 189 | nim c koch 190 | ./koch boot -d:release 191 | ./koch nimble -d:release 192 | # clean up to save cache space 193 | rm koch 194 | rm -rf nimcache 195 | rm -rf dist 196 | rm -rf .git 197 | 198 | - name: Run nimSHA2 tests 199 | shell: bash 200 | working-directory: nimSHA2 201 | run: | 202 | nimble install -y --depsOnly 203 | env TEST_LANG="${{ matrix.test_lang }}" nimble test 204 | 205 | - name: Setup VCC (Windows-i386) 206 | if: runner.os == 'Windows' && matrix.target.cpu == 'i386' 207 | uses: ilammy/msvc-dev-cmd@v1.5.0 208 | with: 209 | arch: amd64_x86 210 | 211 | - name: Setup VCC (Windows-amd64) 212 | if: runner.os == 'Windows' && matrix.target.cpu == 'amd64' 213 | uses: ilammy/msvc-dev-cmd@v1.5.0 214 | with: 215 | arch: x64 216 | 217 | - name: Test using VCC 218 | if: runner.os == 'Windows' 219 | shell: cmd 220 | working-directory: nimSHA2 221 | run: | 222 | env TEST_LANG="${{ matrix.test_lang }}" nimble testvcc 223 | -------------------------------------------------------------------------------- /nimSHA2.nim: -------------------------------------------------------------------------------- 1 | # SHA-2 implementation written in nim 2 | # 3 | # Copyright (c) 2015 Andri Lim 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | # 23 | # 24 | #------------------------------------- 25 | 26 | import endians, strutils 27 | 28 | const 29 | SHA256_K = [ 30 | 0x428A2F98'u32, 0x71374491'u32, 0xB5C0FBCF'u32, 0xE9B5DBA5'u32, 31 | 0x3956C25B'u32, 0x59F111F1'u32, 0x923F82A4'u32, 0xAB1C5ED5'u32, 32 | 0xD807AA98'u32, 0x12835B01'u32, 0x243185BE'u32, 0x550C7DC3'u32, 33 | 0x72BE5D74'u32, 0x80DEB1FE'u32, 0x9BDC06A7'u32, 0xC19BF174'u32, 34 | 0xE49B69C1'u32, 0xEFBE4786'u32, 0x0FC19DC6'u32, 0x240CA1CC'u32, 35 | 0x2DE92C6F'u32, 0x4A7484AA'u32, 0x5CB0A9DC'u32, 0x76F988DA'u32, 36 | 0x983E5152'u32, 0xA831C66D'u32, 0xB00327C8'u32, 0xBF597FC7'u32, 37 | 0xC6E00BF3'u32, 0xD5A79147'u32, 0x06CA6351'u32, 0x14292967'u32, 38 | 0x27B70A85'u32, 0x2E1B2138'u32, 0x4D2C6DFC'u32, 0x53380D13'u32, 39 | 0x650A7354'u32, 0x766A0ABB'u32, 0x81C2C92E'u32, 0x92722C85'u32, 40 | 0xA2BFE8A1'u32, 0xA81A664B'u32, 0xC24B8B70'u32, 0xC76C51A3'u32, 41 | 0xD192E819'u32, 0xD6990624'u32, 0xF40E3585'u32, 0x106AA070'u32, 42 | 0x19A4C116'u32, 0x1E376C08'u32, 0x2748774C'u32, 0x34B0BCB5'u32, 43 | 0x391C0CB3'u32, 0x4ED8AA4A'u32, 0x5B9CCA4F'u32, 0x682E6FF3'u32, 44 | 0x748F82EE'u32, 0x78A5636F'u32, 0x84C87814'u32, 0x8CC70208'u32, 45 | 0x90BEFFFA'u32, 0xA4506CEB'u32, 0xBEF9A3F7'u32, 0xC67178F2'u32] 46 | 47 | SHA512_K = [ 48 | 0x428A2F98D728AE22'u64, 0x7137449123EF65CD'u64, 49 | 0xB5C0FBCFEC4D3B2F'u64, 0xE9B5DBA58189DBBC'u64, 50 | 0x3956C25BF348B538'u64, 0x59F111F1B605D019'u64, 51 | 0x923F82A4AF194F9B'u64, 0xAB1C5ED5DA6D8118'u64, 52 | 0xD807AA98A3030242'u64, 0x12835B0145706FBE'u64, 53 | 0x243185BE4EE4B28C'u64, 0x550C7DC3D5FFB4E2'u64, 54 | 0x72BE5D74F27B896F'u64, 0x80DEB1FE3B1696B1'u64, 55 | 0x9BDC06A725C71235'u64, 0xC19BF174CF692694'u64, 56 | 0xE49B69C19EF14AD2'u64, 0xEFBE4786384F25E3'u64, 57 | 0x0FC19DC68B8CD5B5'u64, 0x240CA1CC77AC9C65'u64, 58 | 0x2DE92C6F592B0275'u64, 0x4A7484AA6EA6E483'u64, 59 | 0x5CB0A9DCBD41FBD4'u64, 0x76F988DA831153B5'u64, 60 | 0x983E5152EE66DFAB'u64, 0xA831C66D2DB43210'u64, 61 | 0xB00327C898FB213F'u64, 0xBF597FC7BEEF0EE4'u64, 62 | 0xC6E00BF33DA88FC2'u64, 0xD5A79147930AA725'u64, 63 | 0x06CA6351E003826F'u64, 0x142929670A0E6E70'u64, 64 | 0x27B70A8546D22FFC'u64, 0x2E1B21385C26C926'u64, 65 | 0x4D2C6DFC5AC42AED'u64, 0x53380D139D95B3DF'u64, 66 | 0x650A73548BAF63DE'u64, 0x766A0ABB3C77B2A8'u64, 67 | 0x81C2C92E47EDAEE6'u64, 0x92722C851482353B'u64, 68 | 0xA2BFE8A14CF10364'u64, 0xA81A664BBC423001'u64, 69 | 0xC24B8B70D0F89791'u64, 0xC76C51A30654BE30'u64, 70 | 0xD192E819D6EF5218'u64, 0xD69906245565A910'u64, 71 | 0xF40E35855771202A'u64, 0x106AA07032BBD1B8'u64, 72 | 0x19A4C116B8D2D0C8'u64, 0x1E376C085141AB53'u64, 73 | 0x2748774CDF8EEB99'u64, 0x34B0BCB5E19B48A8'u64, 74 | 0x391C0CB3C5C95A63'u64, 0x4ED8AA4AE3418ACB'u64, 75 | 0x5B9CCA4F7763E373'u64, 0x682E6FF3D6B2B8A3'u64, 76 | 0x748F82EE5DEFB2FC'u64, 0x78A5636F43172F60'u64, 77 | 0x84C87814A1F0AB72'u64, 0x8CC702081A6439EC'u64, 78 | 0x90BEFFFA23631E28'u64, 0xA4506CEBDE82BDE9'u64, 79 | 0xBEF9A3F7B2C67915'u64, 0xC67178F2E372532B'u64, 80 | 0xCA273ECEEA26619C'u64, 0xD186B8C721C0C207'u64, 81 | 0xEADA7DD6CDE0EB1E'u64, 0xF57D4F7FEE6ED178'u64, 82 | 0x06F067AA72176FBA'u64, 0x0A637DC5A2C898A6'u64, 83 | 0x113F9804BEF90DAE'u64, 0x1B710B35131C471B'u64, 84 | 0x28DB77F523047D84'u64, 0x32CAAB7B40C72493'u64, 85 | 0x3C9EBE0A15C9BEBC'u64, 0x431D67C49C100D4C'u64, 86 | 0x4CC5D4BECB3E42B6'u64, 0x597F299CFC657E2A'u64, 87 | 0x5FCB6FAB3AD6FAEC'u64, 0x6C44198C4A475817'u64] 88 | 89 | type 90 | SHA2Ctx = object of RootObj 91 | count: array[0..1, uint32] 92 | 93 | SHA224* = object of SHA2Ctx 94 | state: array[0..7, uint32] 95 | buffer: array[0..63, uint8] 96 | 97 | SHA256* = object of SHA224 98 | 99 | SHA384* = object of SHA2Ctx 100 | state: array[0..7, uint64] 101 | buffer: array[0..127, uint8] 102 | 103 | SHA512* = object of SHA384 104 | 105 | SHA224Digest* = array[0..27, uint8] 106 | SHA256Digest* = array[0..31, uint8] 107 | SHA384Digest* = array[0..47, uint8] 108 | SHA512Digest* = array[0..63, uint8] 109 | 110 | proc initSHA*(ctx: var SHA224) = 111 | ctx.count[0] = 0 112 | ctx.count[1] = 0 113 | ctx.state[0] = 0xC1059ED8'u32 114 | ctx.state[1] = 0x367CD507'u32 115 | ctx.state[2] = 0x3070DD17'u32 116 | ctx.state[3] = 0xF70E5939'u32 117 | ctx.state[4] = 0xFFC00B31'u32 118 | ctx.state[5] = 0x68581511'u32 119 | ctx.state[6] = 0x64F98FA7'u32 120 | ctx.state[7] = 0xBEFA4FA4'u32 121 | 122 | proc initSHA*(ctx: var SHA256) = 123 | ctx.count[0] = 0 124 | ctx.count[1] = 0 125 | ctx.state[0] = 0x6A09E667'u32 126 | ctx.state[1] = 0xBB67AE85'u32 127 | ctx.state[2] = 0x3C6EF372'u32 128 | ctx.state[3] = 0xA54FF53A'u32 129 | ctx.state[4] = 0x510E527F'u32 130 | ctx.state[5] = 0x9B05688C'u32 131 | ctx.state[6] = 0x1F83D9AB'u32 132 | ctx.state[7] = 0x5BE0CD19'u32 133 | 134 | proc initSHA*(ctx: var SHA384) = 135 | ctx.count[0] = 0 136 | ctx.count[1] = 0 137 | ctx.state[0] = 0xCBBB9D5DC1059ED8'u64 138 | ctx.state[1] = 0x629A292A367CD507'u64 139 | ctx.state[2] = 0x9159015A3070DD17'u64 140 | ctx.state[3] = 0x152FECD8F70E5939'u64 141 | ctx.state[4] = 0x67332667FFC00B31'u64 142 | ctx.state[5] = 0x8EB44A8768581511'u64 143 | ctx.state[6] = 0xDB0C2E0D64F98FA7'u64 144 | ctx.state[7] = 0x47B5481DBEFA4FA4'u64 145 | 146 | proc initSHA*(ctx: var SHA512) = 147 | ctx.count[0] = 0 148 | ctx.count[1] = 0 149 | ctx.state[0] = 0x6A09E667F3BCC908'u64 150 | ctx.state[1] = 0xBB67AE8584CAA73B'u64 151 | ctx.state[2] = 0x3C6EF372FE94F82B'u64 152 | ctx.state[3] = 0xA54FF53A5F1D36F1'u64 153 | ctx.state[4] = 0x510E527FADE682D1'u64 154 | ctx.state[5] = 0x9B05688C2B3E6C1F'u64 155 | ctx.state[6] = 0x1F83D9ABFB41BD6B'u64 156 | ctx.state[7] = 0x5BE0CD19137E2179'u64 157 | 158 | proc initSHA*[T](): T = 159 | result.initSHA() 160 | 161 | proc GET_UINT32_BE(b: cstring, i: int): uint32 = 162 | var val = b 163 | bigEndian32(addr(result), addr(val[i])) 164 | 165 | proc PUT_UINT32_BE(n: uint32, b: var cstring, i: int) = 166 | var val = n 167 | bigEndian32(addr(b[i]), addr(val)) 168 | 169 | proc GET_UINT64_BE(b: cstring, i: int): uint64 = 170 | var val = b 171 | bigEndian64(addr(result), addr(val[i])) 172 | 173 | proc PUT_UINT64_BE(n: uint64, b: var cstring, i: int) = 174 | var val = n 175 | bigEndian64(addr(b[i]), addr(val)) 176 | 177 | template a(i:int):untyped = T[(0 - i) and 7] 178 | template b(i:int):untyped = T[(1 - i) and 7] 179 | template c(i:int):untyped = T[(2 - i) and 7] 180 | template d(i:int):untyped = T[(3 - i) and 7] 181 | template e(i:int):untyped = T[(4 - i) and 7] 182 | template f(i:int):untyped = T[(5 - i) and 7] 183 | template g(i:int):untyped = T[(6 - i) and 7] 184 | template h(i:int):untyped = T[(7 - i) and 7] 185 | 186 | proc Ch[T: uint32|uint64](x, y, z: T): T {.inline.} = (z xor (x and (y xor z))) 187 | proc Maj[T: uint32|uint64](x, y, z: T): T {.inline.} = ((x and y) or (z and (x or y))) 188 | proc rotr[T: uint32|uint64](num: T, amount: int): T {.inline.} = 189 | result = (num shr T(amount)) or (num shl T(8 * sizeof(num) - amount)) 190 | 191 | template R(i: int): untyped = 192 | h(i) += S1(e(i)) + Ch(e(i), f(i), g(i)) + K[i + j] 193 | 194 | if j != 0: 195 | W[i and 15] += S3(W[(i - 2) and 15]) + W[(i - 7) and 15] + S2(W[(i - 15) and 15]) 196 | h(i) += W[i and 15] 197 | else: 198 | W[i] = data[i] 199 | h(i) += W[i] 200 | 201 | d(i) += h(i) 202 | h(i) += S0(a(i)) + Maj(a(i), b(i), c(i)) 203 | 204 | proc transform256(state: var array[0..7, uint32], input: cstring) = 205 | let K = SHA256_K 206 | var W, data: array[0..15, uint32] 207 | for i in countup(0, 15): data[i] = GET_UINT32_BE(input, i * 4) 208 | 209 | var T: array[0..7, uint32] 210 | for i in 0..7: T[i] = state[i] 211 | 212 | proc S0(x:uint32): uint32 {.inline.} = (rotr(x, 2) xor rotr(x, 13) xor rotr(x, 22)) 213 | proc S1(x:uint32): uint32 {.inline.} = (rotr(x, 6) xor rotr(x, 11) xor rotr(x, 25)) 214 | proc S2(x:uint32): uint32 {.inline.} = (rotr(x, 7) xor rotr(x, 18) xor (x shr 3)) 215 | proc S3(x:uint32): uint32 {.inline.} = (rotr(x, 17) xor rotr(x, 19) xor (x shr 10)) 216 | 217 | for j in countup(0, 63, 16): 218 | R( 0); R( 1); R( 2); R( 3) 219 | R( 4); R( 5); R( 6); R( 7) 220 | R( 8); R( 9); R(10); R(11) 221 | R(12); R(13); R(14); R(15) 222 | 223 | state[0] += a(0) 224 | state[1] += b(0) 225 | state[2] += c(0) 226 | state[3] += d(0) 227 | state[4] += e(0) 228 | state[5] += f(0) 229 | state[6] += g(0) 230 | state[7] += h(0) 231 | 232 | func update*[T:char|byte](ctx: var (SHA224|SHA256), data: openarray[T]) = 233 | var len = data.len 234 | var pos = 0 235 | while len > 0: 236 | let copy_start = int(ctx.count[0] and 0x3F) 237 | let copy_size = min(64 - copy_start, len) 238 | copyMem(addr(ctx.buffer[copy_start]), unsafeAddr(data[pos]), copy_size) 239 | 240 | inc(pos, copy_size) 241 | dec(len, copy_size) 242 | 243 | ctx.count[0] += uint32(copy_size) 244 | # carry overflow from low to high 245 | if ctx.count[0] < uint32(copy_size): ctx.count[1] += 1'u32 246 | 247 | if (ctx.count[0] and 0x3F) == 0: 248 | transform256(ctx.state, cast[cstring](addr(ctx.buffer[0]))) 249 | 250 | proc transform512(state: var array[0..7, uint64], input: cstring) = 251 | let K = SHA512_K 252 | var W, data: array[0..15, uint64] 253 | for i in countup(0, 15): data[i] = GET_UINT64_BE(input, i * 8) 254 | 255 | var T: array[0..7, uint64] 256 | for i in 0..7: T[i] = state[i] 257 | 258 | proc S0(x:uint64):uint64 {.inline.} = (rotr(x, 28) xor rotr(x, 34) xor rotr(x, 39)) 259 | proc S1(x:uint64):uint64 {.inline.} = (rotr(x, 14) xor rotr(x, 18) xor rotr(x, 41)) 260 | proc S2(x:uint64):uint64 {.inline.} = (rotr(x, 1) xor rotr(x, 8) xor (x shr 7)) 261 | proc S3(x:uint64):uint64 {.inline.} = (rotr(x, 19) xor rotr(x, 61) xor (x shr 6)) 262 | 263 | # 80 operations, partially loop unrolled 264 | for j in countup(0, 79, 16): 265 | R( 0); R( 1); R( 2); R( 3) 266 | R( 4); R( 5); R( 6); R( 7) 267 | R( 8); R( 9); R(10); R(11) 268 | R(12); R(13); R(14); R(15) 269 | 270 | # Add the working vars back into state[]. 271 | state[0] += a(0) 272 | state[1] += b(0) 273 | state[2] += c(0) 274 | state[3] += d(0) 275 | state[4] += e(0) 276 | state[5] += f(0) 277 | state[6] += g(0) 278 | state[7] += h(0) 279 | 280 | func update*[T:char|byte](ctx: var (SHA384|SHA512), data: openarray[T]) = 281 | var len = data.len 282 | var pos = 0 283 | while len > 0: 284 | let copy_start = int(ctx.count[0] and 0x7F) 285 | let copy_size = min(128 - copy_start, len) 286 | copyMem(addr(ctx.buffer[copy_start]), unsafeAddr(data[pos]), copy_size) 287 | 288 | inc(pos, copy_size) 289 | dec(len, copy_size) 290 | 291 | ctx.count[0] += uint32(copy_size) 292 | # carry overflow from low to high 293 | if ctx.count[0] < uint32(copy_size): ctx.count[1] += 1'u32 294 | 295 | if (ctx.count[0] and 0x7F) == 0: 296 | transform512(ctx.state, cast[cstring](addr(ctx.buffer[0]))) 297 | 298 | proc final224_256(ctx: var SHA224) = 299 | var buffer = cast[cstring](addr(ctx.buffer[0])) 300 | # Add padding as described in RFC 3174 (it describes SHA-1 but 301 | # the same padding style is used for SHA-256 too). 302 | var j = int(ctx.count[0] and 0x3F) 303 | ctx.buffer[j] = 0x80 304 | inc j 305 | 306 | while j != 56: 307 | if j == 64: 308 | transform256(ctx.state, buffer) 309 | j = 0 310 | ctx.buffer[j] = 0x00 311 | inc j 312 | 313 | # Convert the message size from bytes to bits. 314 | ctx.count[1] = (ctx.count[1] shl 3) + (ctx.count[0] shr 29) 315 | ctx.count[0] = ctx.count[0] shl 3 316 | 317 | PUT_UINT32_BE(ctx.count[1], buffer, 14 * 4) 318 | PUT_UINT32_BE(ctx.count[0], buffer, 15 * 4) 319 | transform256(ctx.state, buffer) 320 | 321 | proc final*(ctx: var SHA224): SHA224Digest = 322 | ctx.final224_256() 323 | var output = cast[cstring](addr(result[0])) 324 | for i in 0..6: 325 | PUT_UINT32_BE(ctx.state[i], output, i * 4) 326 | 327 | proc final*(ctx: var SHA256): SHA256Digest = 328 | SHA224(ctx).final224_256() 329 | var output = cast[cstring](addr(result[0])) 330 | for i in 0..7: 331 | PUT_UINT32_BE(ctx.state[i], output, i * 4) 332 | 333 | proc final384_512(ctx: var SHA384) = 334 | var buffer = cast[cstring](addr(ctx.buffer[0])) 335 | 336 | # Add padding as described in RFC 3174 (it describes SHA-1 but 337 | # the same padding style is used for SHA-512 too). 338 | var j = int(ctx.count[0] and 0x7F) 339 | ctx.buffer[j] = 0x80 340 | inc j 341 | 342 | while j != 112: 343 | if j == 128: 344 | transform512(ctx.state, buffer) 345 | j = 0 346 | ctx.buffer[j] = 0x00 347 | inc j 348 | 349 | # Convert the message size from bytes to bits. 350 | ctx.count[1] = (ctx.count[1] shl 3) + (ctx.count[0] shr 29) 351 | ctx.count[0] = ctx.count[0] shl 3 352 | 353 | PUT_UINT64_BE(ctx.count[1], buffer, 14 * 8) 354 | PUT_UINT64_BE(ctx.count[0], buffer, 15 * 8) 355 | transform512(ctx.state, buffer) 356 | 357 | proc final*(ctx: var SHA384): SHA384Digest = 358 | ctx.final384_512() 359 | var output = cast[cstring](addr(result[0])) 360 | for i in 0..5: 361 | PUT_UINT64_BE(ctx.state[i], output, i * 8) 362 | 363 | proc final*(ctx: var SHA512): SHA512Digest = 364 | ctx.final384_512() 365 | var output = cast[cstring](addr(result[0])) 366 | for i in 0..7: 367 | PUT_UINT64_BE(ctx.state[i], output, i * 8) 368 | 369 | proc computeSHA[T, R](input: string, rep: int): R = 370 | var ctx: T 371 | ctx.initSHA() 372 | for i in 0..rep-1: ctx.update(input) 373 | result = ctx.final() 374 | 375 | proc computeSHA224*(input: string, rep: int = 1): SHA224Digest = computeSHA[SHA224, SHA224Digest](input, rep) 376 | proc computeSHA256*(input: string, rep: int = 1): SHA256Digest = computeSHA[SHA256, SHA256Digest](input, rep) 377 | proc computeSHA384*(input: string, rep: int = 1): SHA384Digest = computeSHA[SHA384, SHA384Digest](input, rep) 378 | proc computeSHA512*(input: string, rep: int = 1): SHA512Digest = computeSHA[SHA512, SHA512Digest](input, rep) 379 | 380 | proc toString[T](input: T): string = 381 | result = newString(input.len) 382 | for i in 0..input.len-1: result[i] = input[i].char 383 | 384 | proc `$`*(sha: SHA224Digest): string = toString(sha) 385 | proc `$`*(sha: SHA256Digest): string = toString(sha) 386 | proc `$`*(sha: SHA384Digest): string = toString(sha) 387 | proc `$`*(sha: SHA512Digest): string = toString(sha) 388 | 389 | proc toHexImpl[T](input: T): string = 390 | result = "" 391 | for c in input: 392 | result.add toHex(ord(c), 2) 393 | 394 | proc hex*(sha: SHA224Digest): string = toHexImpl(sha) 395 | proc hex*(sha: SHA256Digest): string = toHexImpl(sha) 396 | proc hex*(sha: SHA384Digest): string = toHexImpl(sha) 397 | proc hex*(sha: SHA512Digest): string = toHexImpl(sha) 398 | 399 | proc toHex*(sha: SHA224Digest): string = toHexImpl(sha) 400 | proc toHex*(sha: SHA256Digest): string = toHexImpl(sha) 401 | proc toHex*(sha: SHA384Digest): string = toHexImpl(sha) 402 | proc toHex*(sha: SHA512Digest): string = toHexImpl(sha) 403 | --------------------------------------------------------------------------------