├── docs └── CODEOWNERS ├── project ├── build.properties ├── plugins.sbt └── LicenseDefinition.scala ├── local.sh ├── publish.sh ├── .git-blame-ignore-revs ├── .gitignore ├── .github └── workflows │ └── scala.yml ├── sonatype.sbt ├── .scalafmt.conf ├── shared └── src │ ├── main │ └── scala │ │ └── pt │ │ └── kcry │ │ └── sha │ │ ├── Sha0.scala │ │ ├── Hash.scala │ │ ├── Sha1.scala │ │ ├── Sha3.scala │ │ └── Sha2.scala │ └── test │ └── scala │ └── pt │ └── kcry │ └── sha │ └── HashTestVectors.scala ├── CHANGELOG.md ├── bench └── src │ └── main │ └── scala │ └── pt │ └── kcry │ └── sha │ └── benchmark │ └── ShaBenchmark.scala ├── README.md └── LICENSE.txt /docs/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * k@kcry.pt 2 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.11.7 2 | -------------------------------------------------------------------------------- /local.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | sbt +clean \ 4 | +test \ 5 | +publishLocal 6 | -------------------------------------------------------------------------------- /publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | sbt +clean +test \ 4 | +publishSigned \ 5 | sonatypeBundleRelease 6 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # Scala Steward: Reformat with scalafmt 3.8.2 2 | 3222478cf580eb434ec3c38cbd866284b3685de9 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # scala 2 | *.class 3 | *.log 4 | 5 | # sbt specific 6 | .cache 7 | .history 8 | .lib/ 9 | dist/* 10 | target/ 11 | lib_managed/ 12 | src_managed/ 13 | project/boot/ 14 | project/plugins/project/ 15 | .bsp/ 16 | 17 | # IDEA stuff 18 | .idea/ 19 | 20 | # scala-native 21 | lowered.hnir 22 | 23 | # dotty site 24 | _site 25 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.3.2") 2 | addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.3.2") 3 | 4 | addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.20.1") 5 | addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.9") 6 | 7 | addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.8") 8 | addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.3.1") 9 | addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.12.2") 10 | addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.13.1") 11 | addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.3.1") 12 | addSbtPlugin("com.github.sbt" % "sbt-dynver" % "5.1.1") 13 | addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.6") 14 | addSbtPlugin("com.github.sbt" % "sbt-header" % "5.11.0") 15 | -------------------------------------------------------------------------------- /.github/workflows/scala.yml: -------------------------------------------------------------------------------- 1 | name: Scala CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | branches: [ master ] 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | env: 14 | JAVA_OPTS: "-XX:+UseG1GC -Xmx6g" 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | - name: Set up JDK 11 19 | uses: actions/setup-java@v2 20 | with: 21 | java-version: '11' 22 | distribution: 'adopt' 23 | - name: Install SBT 24 | uses: sbt/setup-sbt@v1 25 | - name: Check headers 26 | run: sbt +headerCheck 27 | - name: Check scalafmt 28 | run: sbt +scalafmtCheckAll scalafmtSbtCheck 29 | - name: Compile everything 30 | run: sbt +compile +test:compile 31 | - name: Run tests 32 | run: sbt +test 33 | -------------------------------------------------------------------------------- /project/LicenseDefinition.scala: -------------------------------------------------------------------------------- 1 | import sbtheader.HeaderPlugin.autoImport.HeaderLicense 2 | import sbtheader.License 3 | import sbt.url 4 | 5 | object LicenseDefinition { 6 | val template: Option[License.Custom] = Some( 7 | HeaderLicense.Custom( 8 | """scala-sha - Secure Hash Algorithms family for scala, scala-js and scala-native. 9 | | 10 | |Written in 2020, 2021 by Kirill A. Korinsky 11 | | 12 | |Supported since 2022 by Kcrypt Lab UG 13 | | 14 | |This work is released into the public domain with CC0 1.0. 15 | |""".stripMargin 16 | ) 17 | ) 18 | 19 | val licenses = Seq( 20 | "CC0 1.0 Universal" -> 21 | url("https://github.com/kcrypt/scala-sha/blob/master/LICENSE.txt") 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /sonatype.sbt: -------------------------------------------------------------------------------- 1 | import sbt.url 2 | import xerial.sbt.Sonatype.GitHubHosting 3 | 4 | ThisBuild / sonatypeProfileName := "pt.kcry" 5 | ThisBuild / publishMavenStyle := true 6 | ThisBuild / sonatypeProjectHosting := 7 | Some(GitHubHosting("kcrypt", "scala-sha", "k@kcry.pt")) 8 | ThisBuild / licenses := LicenseDefinition.licenses 9 | ThisBuild / homepage := Some(url("https://github.com/kcrypt/scala-sha")) 10 | ThisBuild / scmInfo := Some( 11 | ScmInfo( 12 | url("https://github.com/kcrypt/scala-sha"), 13 | "scm:git@github.com:kcrypt/scala-sha.git" 14 | ) 15 | ) 16 | ThisBuild / developers := List( 17 | Developer( 18 | id = "catap", name = "Kirill A. Korinsky", email = "kirill@korins.ky", 19 | url = url("https://github.com/catap") 20 | ) 21 | ) 22 | ThisBuild / sonatypeCredentialHost := "s01.oss.sonatype.org" 23 | -------------------------------------------------------------------------------- /.scalafmt.conf: -------------------------------------------------------------------------------- 1 | version = 3.10.2 2 | 3 | runner.dialect = scala211 4 | 5 | continuationIndent.callSite = 2 6 | continuationIndent.defnSite = 2 7 | 8 | docstrings.style = Asterisk 9 | 10 | indentOperator.include = ".*" 11 | indentOperator.exclude = "^$" 12 | 13 | newlines.avoidForSimpleOverflow=[tooLong, punct, slc] 14 | newlines.avoidInResultType = true 15 | newlines.neverBeforeJsNative = true 16 | newlines.sometimesBeforeColonInMethodReturnType = false 17 | newlines.neverBeforeJsNative = true 18 | 19 | newlines.source = fold 20 | binPack.parentConstructors = Oneline 21 | binPack.unsafeCallSite = oneline 22 | binPack.unsafeDefnSite = oneline 23 | 24 | rewrite.rules = [RedundantBraces] 25 | rewrite.redundantBraces.stringInterpolation = true 26 | rewrite.redundantBraces.ifElseExpressions = true 27 | 28 | rewriteTokens = { 29 | "⇒": "=>" 30 | "→": "->" 31 | "←": "<-" 32 | } 33 | 34 | fileOverride { 35 | "glob:**/build.sbt" { 36 | runner.dialect = sbt1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /shared/src/main/scala/pt/kcry/sha/Sha0.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * scala-sha - Secure Hash Algorithms family for scala, scala-js and scala-native. 3 | * 4 | * Written in 2020, 2021 by Kirill A. Korinsky 5 | * 6 | * Supported since 2022 by Kcrypt Lab UG 7 | * 8 | * This work is released into the public domain with CC0 1.0. 9 | */ 10 | 11 | package pt.kcry.sha 12 | 13 | /** 14 | * Keep in mind that SHA-0 is broken and I implemented it just for fun :) 15 | * 16 | * This implementation also isn't thread safe. 17 | */ 18 | class Sha0 extends Sha1 { 19 | override protected def processBlockWord(j: Int): Int = words(j - 3) ^ 20 | words(j - 8) ^ words(j - 14) ^ words(j - 16) 21 | } 22 | 23 | object Sha0 { 24 | val HASH_SIZE: Int = Sha1.HASH_SIZE 25 | 26 | def hash(message: Array[Byte]): Array[Byte] = { 27 | val sha0 = new Sha0() 28 | sha0.update(message, 0, message.length) 29 | val hashed = new Array[Byte](HASH_SIZE) 30 | sha0.finish(hashed, 0) 31 | hashed 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## [unreleased] 6 | - Switched to scala-2.12.21 7 | - Switched to scala-2.13.18 8 | - Switched to scala-3.7.4 9 | - Switched to scalajs-1.20.1 10 | - Switched to scala-native-0.5.9 11 | 12 | ## [2.0.2] - 2024-04-24 13 | - Drop support scala-2.12 for Scala.JS and Scala-Native 14 | - Switched to scala-2.12.19 15 | - Switched to scala-2.13.13 16 | - Switched to scala-3.4.1 17 | - Switched to scalajs-1.16.0 18 | - Switched to scala-native-0.5.1 19 | 20 | ## [2.0.1] - 2022-05-11 21 | - Switched to scalajs-1.10.0 22 | - Switched to scala-native-0.4.4 23 | - Switched to scala-3.1.2 24 | 25 | ## [2.0.0] - 2021-12-23 26 | - Migrated to package `pt.kcry.sha` 27 | - Switched to scalajs-1.8.0 28 | - Switched to scala-native-0.4.2 29 | - Switched to scala-2.13.7 30 | 31 | ## [1.2.1] - 2021-08-06 32 | - Switched to scalajs-1.7.0 33 | 34 | ## [1.2.0] - 2021-07-15 35 | - Improved performance of Keccak / SHA3 36 | 37 | ## [1.1.1] - 2021-07-14 38 | - Added missed SHA3 objects 39 | - Improved memory footprint of Keccak / SHA3 40 | 41 | ## [1.1.0] - 2021-07-14 42 | - Implemented block hashing. 43 | 44 | ## [1.0.0] - 2021-07-11 45 | - The first public release as dedicated project. 46 | 47 | [unreleased]: https://github.com/kcrypt/scala-sha/compare/v2.0.2...HEAD 48 | [2.0.2]: https://github.com/kcrypt/scala-sha/compare/v2.0.1...v2.0.2 49 | [2.0.1]: https://github.com/kcrypt/scala-sha/compare/v2.0.0...v2.0.1 50 | [2.0.0]: https://github.com/kcrypt/scala-sha/compare/v1.2.1...v2.0.0 51 | [1.2.1]: https://github.com/kcrypt/scala-sha/compare/v1.2.0...v1.2.1 52 | [1.2.0]: https://github.com/kcrypt/scala-sha/compare/v1.1.1...v1.2.0 53 | [1.1.1]: https://github.com/kcrypt/scala-sha/compare/v1.1.0...v1.1.1 54 | [1.1.0]: https://github.com/kcrypt/scala-sha/compare/v1.0.0...v1.1.0 55 | [1.0.0]: https://github.com/kcrypt/scala-sha/releases/tagv1.0.0 56 | 57 | -------------------------------------------------------------------------------- /shared/src/main/scala/pt/kcry/sha/Hash.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * scala-sha - Secure Hash Algorithms family for scala, scala-js and scala-native. 3 | * 4 | * Written in 2020, 2021 by Kirill A. Korinsky 5 | * 6 | * Supported since 2022 by Kcrypt Lab UG 7 | * 8 | * This work is released into the public domain with CC0 1.0. 9 | */ 10 | 11 | package pt.kcry.sha 12 | 13 | trait Hash { 14 | def update(bytes: Array[Byte], off: Int, len: Int): Unit 15 | 16 | def finish(hashed: Array[Byte], off: Int): Unit 17 | 18 | def finish(hashed: Array[Byte], off: Int, len: Int): Unit 19 | } 20 | 21 | private[sha] trait BlockedHash[T <: Array[_]] extends Hash { 22 | 23 | protected val words: T 24 | protected val block: Array[Byte] 25 | private var blockPos = 0 26 | 27 | protected var messageLen = 0 28 | 29 | protected def finishBlock(block: Array[Byte], off: Int): Unit 30 | 31 | def update(bytes: Array[Byte], off: Int, len: Int): Unit = { 32 | var rem = len 33 | var pos = off 34 | if (blockPos > 0) { 35 | val use = math.min(block.length - blockPos, rem) 36 | System.arraycopy(bytes, pos, block, blockPos, use) 37 | rem -= use 38 | pos += use 39 | blockPos += use 40 | } 41 | if (blockPos == block.length) { 42 | finishBlock(block, 0) 43 | blockPos = 0 44 | } 45 | while (rem >= block.length) { 46 | finishBlock(bytes, pos) 47 | pos += block.length 48 | rem -= block.length 49 | } 50 | if (rem > 0) { 51 | System.arraycopy(bytes, pos, block, 0, rem) 52 | blockPos = rem 53 | } 54 | messageLen += len 55 | } 56 | 57 | final def finish(hashed: Array[Byte], off: Int, len: Int): Unit = 58 | throw new NotImplementedError("XOF doesn't defined") 59 | 60 | protected def padding_32bit(messageLen: Int): Array[Byte] = { 61 | val tailLength = messageLen & 0x3f 62 | val padLength = if (tailLength < 56) 64 - tailLength else 128 - tailLength 63 | 64 | val padded = new Array[Byte](padLength) 65 | padded(0) = 0x80.toByte 66 | 67 | val lengthInBits = messageLen.toLong * 8 68 | var i = 0 69 | while (i < 8) { 70 | padded(padded.length - 1 - i) = ((lengthInBits >>> (8 * i)) & 0xff).toByte 71 | i += 1 72 | } 73 | 74 | padded 75 | } 76 | 77 | protected def padding_64bit(messageLen: Int): Array[Byte] = { 78 | val tailLength = messageLen & 0x7f 79 | val padLength = if (tailLength < 112) 128 - tailLength else 256 - tailLength 80 | 81 | val padded = new Array[Byte](padLength) 82 | padded(0) = 0x80.toByte 83 | 84 | val lengthInBits = messageLen.toLong * 8 85 | var i = 0 86 | while (i < 8) { 87 | padded(padded.length - 1 - i) = ((lengthInBits >>> (8 * i)) & 0xff).toByte 88 | i += 1 89 | } 90 | 91 | padded 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /bench/src/main/scala/pt/kcry/sha/benchmark/ShaBenchmark.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * scala-sha - Secure Hash Algorithms family for scala, scala-js and scala-native. 3 | * 4 | * Written in 2020, 2021 by Kirill A. Korinsky 5 | * 6 | * Supported since 2022 by Kcrypt Lab UG 7 | * 8 | * This work is released into the public domain with CC0 1.0. 9 | */ 10 | 11 | package pt.kcry.sha 12 | package benchmark 13 | 14 | import org.openjdk.jmh.annotations._ 15 | 16 | import java.security.MessageDigest 17 | import scala.util.Random 18 | 19 | @State(Scope.Benchmark) 20 | class ShaBenchmark { 21 | 22 | private val hashedSha1: Array[Byte] = new Array[Byte](Sha1.HASH_SIZE) 23 | 24 | private val hashedSha256: Array[Byte] = new Array[Byte](Sha2_256.HASH_SIZE) 25 | 26 | private val hashedSha512: Array[Byte] = new Array[Byte](Sha2_512.HASH_SIZE) 27 | 28 | @Param(Array("1024", "16384")) 29 | private var len: Int = 0 30 | 31 | private var data: Array[Byte] = _ 32 | 33 | @Setup 34 | def prepare(): Unit = { 35 | data = new Array[Byte](len) 36 | Random.nextBytes(data) 37 | } 38 | 39 | @Benchmark 40 | def sha1(): Unit = { 41 | val hasher = new Sha1() 42 | hasher.update(data, 0, len) 43 | hasher.finish(hashedSha1, 0) 44 | } 45 | 46 | @Benchmark 47 | def jvmSha1(): Unit = { 48 | val md = MessageDigest.getInstance("SHA-1") 49 | md.update(data) 50 | md.digest(hashedSha1) 51 | } 52 | 53 | @Benchmark 54 | def sha256(): Unit = { 55 | val hasher = new Sha2_256() 56 | hasher.update(data, 0, len) 57 | hasher.finish(hashedSha256, 0) 58 | } 59 | 60 | @Benchmark 61 | def jvmSha256(): Unit = { 62 | val md = MessageDigest.getInstance("SHA-256") 63 | md.update(data) 64 | md.digest(hashedSha256) 65 | } 66 | 67 | @Benchmark 68 | def sha512(): Unit = { 69 | val hasher = new Sha2_512() 70 | hasher.update(data, 0, len) 71 | hasher.finish(hashedSha512, 0) 72 | } 73 | 74 | @Benchmark 75 | def jvmSha512(): Unit = { 76 | val md = MessageDigest.getInstance("SHA-512") 77 | md.update(data) 78 | md.digest(hashedSha512) 79 | } 80 | 81 | @Benchmark 82 | def sha3_256(): Unit = { 83 | val hasher = new Sha3_256() 84 | hasher.update(data, 0, len) 85 | hasher.finish(hashedSha256, 0) 86 | } 87 | 88 | @Benchmark 89 | def jvmSha3_256(): Unit = { 90 | val md = MessageDigest.getInstance("SHA3-256") 91 | md.update(data) 92 | md.digest(hashedSha256) 93 | } 94 | 95 | @Benchmark 96 | def sha3_512(): Unit = { 97 | val hasher = new Sha3_512() 98 | hasher.update(data, 0, len) 99 | hasher.finish(hashedSha512, 0) 100 | } 101 | 102 | @Benchmark 103 | def jvmSha3_512(): Unit = { 104 | val md = MessageDigest.getInstance("SHA3-512") 105 | md.update(data) 106 | md.digest(hashedSha512) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /shared/src/main/scala/pt/kcry/sha/Sha1.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * scala-sha - Secure Hash Algorithms family for scala, scala-js and scala-native. 3 | * 4 | * Written in 2020, 2021 by Kirill A. Korinsky 5 | * 6 | * Supported since 2022 by Kcrypt Lab UG 7 | * 8 | * This work is released into the public domain with CC0 1.0. 9 | */ 10 | 11 | package pt.kcry.sha 12 | 13 | /** 14 | * Quite ugly but fast enough implementation of SHA-1. 15 | * 16 | * This implementation isn't thread safe. 17 | */ 18 | class Sha1() extends BlockedHash[Array[Int]] { 19 | import java.lang.Integer.rotateLeft 20 | 21 | private var H0 = 0x67452301 22 | private var H1 = 0xefcdab89 23 | private var H2 = 0x98badcfe 24 | private var H3 = 0x10325476 25 | private var H4 = 0xc3d2e1f0 26 | 27 | protected val words = new Array[Int](80) 28 | protected val block = new Array[Byte](64) 29 | 30 | // the only reason to keep it as function is easy way to implement SHA-0 :) 31 | protected def processBlockWord(j: Int): Int = 32 | rotateLeft(words(j - 3) ^ words(j - 8) ^ words(j - 14) ^ words(j - 16), 1) 33 | 34 | protected def finishBlock(block: Array[Byte], off: Int): Unit = { 35 | var j = 0 36 | while (j < 16) { 37 | words(j) = 0 38 | var k = 0 39 | while (k < 4) { 40 | words(j) |= ((block(j * 4 + k + off) & 0x000000ff) << (24 - k * 8)) 41 | k += 1 42 | } 43 | j += 1 44 | } 45 | 46 | while (j < 80) { 47 | words(j) = processBlockWord(j) 48 | j += 1 49 | } 50 | 51 | var A = H0 52 | var B = H1 53 | var C = H2 54 | var D = H3 55 | var E = H4 56 | var F = 0 57 | var G = 0 58 | j = 0 59 | while (j < 80) { 60 | if (j < 20) { 61 | F = (B & C) | (~B & D) 62 | G = 0x5a827999 63 | } else if (19 < j && j < 40) { 64 | F = B ^ C ^ D 65 | G = 0x6ed9eba1 66 | } else if (39 < j && j < 60) { 67 | F = (B & C) | (B & D) | (C & D) 68 | G = 0x8f1bbcdc 69 | } else { 70 | F = B ^ C ^ D 71 | G = 0xca62c1d6 72 | } 73 | 74 | val temp = rotateLeft(A, 5) + F + E + G + words(j) 75 | 76 | E = D 77 | D = C 78 | C = rotateLeft(B, 30) 79 | B = A 80 | A = temp 81 | j += 1 82 | } 83 | 84 | H0 += A 85 | H1 += B 86 | H2 += C 87 | H3 += D 88 | H4 += E 89 | } 90 | 91 | def finish(hashed: Array[Byte], off: Int): Unit = { 92 | val padded = padding_32bit(messageLen) 93 | update(padded, 0, padded.length) 94 | 95 | hashed(0 + off) = ((H0 >>> 24) & 0xff).toByte 96 | hashed(1 + off) = ((H0 >>> 16) & 0xff).toByte 97 | hashed(2 + off) = ((H0 >>> 8) & 0xff).toByte 98 | hashed(3 + off) = (H0 & 0xff).toByte 99 | hashed(4 + off) = ((H1 >>> 24) & 0xff).toByte 100 | hashed(5 + off) = ((H1 >>> 16) & 0xff).toByte 101 | hashed(6 + off) = ((H1 >>> 8) & 0xff).toByte 102 | hashed(7 + off) = (H1 & 0xff).toByte 103 | hashed(8 + off) = ((H2 >>> 24) & 0xff).toByte 104 | hashed(9 + off) = ((H2 >>> 16) & 0xff).toByte 105 | hashed(10 + off) = ((H2 >>> 8) & 0xff).toByte 106 | hashed(11 + off) = (H2 & 0xff).toByte 107 | hashed(12 + off) = ((H3 >>> 24) & 0xff).toByte 108 | hashed(13 + off) = ((H3 >>> 16) & 0xff).toByte 109 | hashed(14 + off) = ((H3 >>> 8) & 0xff).toByte 110 | hashed(15 + off) = (H3 & 0xff).toByte 111 | hashed(16 + off) = ((H4 >>> 24) & 0xff).toByte 112 | hashed(17 + off) = ((H4 >>> 16) & 0xff).toByte 113 | hashed(18 + off) = ((H4 >>> 8) & 0xff).toByte 114 | hashed(19 + off) = (H4 & 0xff).toByte 115 | } 116 | } 117 | 118 | object Sha1 { 119 | val HASH_SIZE: Int = 20 120 | 121 | def hash(message: Array[Byte]): Array[Byte] = { 122 | val sha1 = new Sha1() 123 | sha1.update(message, 0, message.length) 124 | val hashed = new Array[Byte](HASH_SIZE) 125 | sha1.finish(hashed, 0) 126 | hashed 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SHA and Shake for scala 2 | 3 | This is Secure Hash Algorithms family which is implemented for scala, scala-js 4 | and scala-native, without any dependencies. 5 | 6 | This code base implements SHA-0, SHA-1, SHA-2 and SHA-3. Keep in mind that SHA-0 7 | is broken and I've implemented it just for fun :) 8 | 9 | The propose of this code to be fast enough to hash something up to a few 10 | megabytes at the worst case and at few kilobytes as usual case. Where? At any 11 | scala-based application where hashing isn't the bottleneck. This code hasn't got 12 | any CPU related optimizations, nor multithreading features for Keccak / SHA-3. 13 | 14 | If you need very fast and secure hash function for scala I suggest to use 15 | [blake3](https://github.com/catap/scala-blake3) which is 3 time faster. 16 | 17 | You can use it as 18 | ``` 19 | libraryDependencies += "pt.kcry" %%% "sha" % "x.x.x" 20 | ``` 21 | The latest version is ![maven-central] 22 | 23 | API is pretty simple and quite limited :) 24 | ``` 25 | scala> import pt.kcry.sha._ 26 | import pt.kcry.sha._ 27 | 28 | scala> Sha2_256.hash("abc".getBytes()) 29 | val res1: Array[Byte] = Array(-70, 120, 22, -65, -113, 1, -49, -22, 65, 65, 64, -34, 93, -82, 34, 35, -80, 3, 97, -93, -106, 23, 122, -100, -76, 16, -1, 97, -14, 0, 21, -83) 30 | 31 | scala> 32 | ``` 33 | 34 | You may also create a new object from specified hash to `update` it, and at some 35 | point `finish` it like this: 36 | ``` 37 | scala> import pt.kcry.sha._ 38 | import pt.kcry.sha._ 39 | 40 | scala> val sha1 = new Sha1() 41 | val sha1: pt.kcry.sha.Sha1 = pt.kcry.sha.Sha1@1224e1b6 42 | 43 | scala> sha1.update("abc".getBytes(), 0, 2) 44 | 45 | scala> sha1.update("abc".getBytes(), 2, 1) 46 | 47 | scala> val hashed = new Array[Byte](20) 48 | val hashed: Array[Byte] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) 49 | 50 | scala> sha1.finish(hashed, 0) 51 | 52 | scala> hashed 53 | val res3: Array[Byte] = Array(-87, -103, 62, 54, 71, 6, -127, 106, -70, 62, 37, 113, 120, 80, -62, 108, -100, -48, -40, -99) 54 | 55 | scala> 56 | ``` 57 | 58 | All these objects aren't thread safe. After `finish` it should be treated as 59 | broken. 60 | 61 | Anyway, I did a few benchmarks to compare this implementation with JVM one 62 | which was run on `JDK 11.0.11, OpenJDK 64-Bit Server VM, 11.0.11+9-LTS`: 63 | ``` 64 | Benchmark (len) Mode Cnt Score Error Units 65 | ShaBenchmark.jvmSha1 1024 avgt 5 3,686 ± 0,005 us/op 66 | ShaBenchmark.jvmSha1 16384 avgt 5 55,556 ± 1,103 us/op 67 | ShaBenchmark.jvmSha256 1024 avgt 5 4,326 ± 0,012 us/op 68 | ShaBenchmark.jvmSha256 16384 avgt 5 61,924 ± 0,025 us/op 69 | ShaBenchmark.jvmSha3_256 1024 avgt 5 4,129 ± 0,045 us/op 70 | ShaBenchmark.jvmSha3_256 16384 avgt 5 61,627 ± 1,422 us/op 71 | ShaBenchmark.jvmSha3_512 1024 avgt 5 7,974 ± 0,091 us/op 72 | ShaBenchmark.jvmSha3_512 16384 avgt 5 111,711 ± 0,372 us/op 73 | ShaBenchmark.jvmSha512 1024 avgt 5 3,172 ± 0,001 us/op 74 | ShaBenchmark.jvmSha512 16384 avgt 5 44,205 ± 0,024 us/op 75 | ShaBenchmark.sha1 1024 avgt 5 4,285 ± 0,002 us/op 76 | ShaBenchmark.sha1 16384 avgt 5 64,380 ± 0,022 us/op 77 | ShaBenchmark.sha256 1024 avgt 5 4,330 ± 0,005 us/op 78 | ShaBenchmark.sha256 16384 avgt 5 63,293 ± 0,024 us/op 79 | ShaBenchmark.sha3_256 1024 avgt 5 5,919 ± 0,019 us/op 80 | ShaBenchmark.sha3_256 16384 avgt 5 84,952 ± 0,468 us/op 81 | ShaBenchmark.sha3_512 1024 avgt 5 8,114 ± 0,078 us/op 82 | ShaBenchmark.sha3_512 16384 avgt 5 118,706 ± 0,924 us/op 83 | ShaBenchmark.sha512 1024 avgt 5 3,168 ± 0,007 us/op 84 | ShaBenchmark.sha512 16384 avgt 5 44,630 ± 0,017 us/op 85 | ``` 86 | for easy compare I've performed the same benchmark on blake3: 87 | 88 | ``` 89 | Benchmark (dataLen) (hashLen) Mode Cnt Score Error Units 90 | Blake3Benchmark.newHasher 1024 256 avgt 5 2,143 ± 0,037 us/op 91 | Blake3Benchmark.newHasher 1024 512 avgt 5 2,564 ± 0,019 us/op 92 | Blake3Benchmark.newHasher 16384 256 avgt 5 26,472 ± 0,023 us/op 93 | Blake3Benchmark.newHasher 16384 512 avgt 5 26,641 ± 0,131 us/op 94 | ``` 95 | 96 | [maven-central]: https://img.shields.io/maven-central/v/pt.kcry/sha_2.13?style=flat-square 97 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | This work is released into the public domain with CC0 1.0. 2 | 3 | ------------------------------------------------------------------------------- 4 | 5 | Creative Commons Legal Code 6 | 7 | CC0 1.0 Universal 8 | 9 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 10 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 11 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 12 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 13 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 14 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 15 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 16 | HEREUNDER. 17 | 18 | Statement of Purpose 19 | 20 | The laws of most jurisdictions throughout the world automatically confer 21 | exclusive Copyright and Related Rights (defined below) upon the creator 22 | and subsequent owner(s) (each and all, an "owner") of an original work of 23 | authorship and/or a database (each, a "Work"). 24 | 25 | Certain owners wish to permanently relinquish those rights to a Work for 26 | the purpose of contributing to a commons of creative, cultural and 27 | scientific works ("Commons") that the public can reliably and without fear 28 | of later claims of infringement build upon, modify, incorporate in other 29 | works, reuse and redistribute as freely as possible in any form whatsoever 30 | and for any purposes, including without limitation commercial purposes. 31 | These owners may contribute to the Commons to promote the ideal of a free 32 | culture and the further production of creative, cultural and scientific 33 | works, or to gain reputation or greater distribution for their Work in 34 | part through the use and efforts of others. 35 | 36 | For these and/or other purposes and motivations, and without any 37 | expectation of additional consideration or compensation, the person 38 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 39 | is an owner of Copyright and Related Rights in the Work, voluntarily 40 | elects to apply CC0 to the Work and publicly distribute the Work under its 41 | terms, with knowledge of his or her Copyright and Related Rights in the 42 | Work and the meaning and intended legal effect of CC0 on those rights. 43 | 44 | 1. Copyright and Related Rights. A Work made available under CC0 may be 45 | protected by copyright and related or neighboring rights ("Copyright and 46 | Related Rights"). Copyright and Related Rights include, but are not 47 | limited to, the following: 48 | 49 | i. the right to reproduce, adapt, distribute, perform, display, 50 | communicate, and translate a Work; 51 | ii. moral rights retained by the original author(s) and/or performer(s); 52 | iii. publicity and privacy rights pertaining to a person's image or 53 | likeness depicted in a Work; 54 | iv. rights protecting against unfair competition in regards to a Work, 55 | subject to the limitations in paragraph 4(a), below; 56 | v. rights protecting the extraction, dissemination, use and reuse of data 57 | in a Work; 58 | vi. database rights (such as those arising under Directive 96/9/EC of the 59 | European Parliament and of the Council of 11 March 1996 on the legal 60 | protection of databases, and under any national implementation 61 | thereof, including any amended or successor version of such 62 | directive); and 63 | vii. other similar, equivalent or corresponding rights throughout the 64 | world based on applicable law or treaty, and any national 65 | implementations thereof. 66 | 67 | 2. Waiver. To the greatest extent permitted by, but not in contravention 68 | of, applicable law, Affirmer hereby overtly, fully, permanently, 69 | irrevocably and unconditionally waives, abandons, and surrenders all of 70 | Affirmer's Copyright and Related Rights and associated claims and causes 71 | of action, whether now known or unknown (including existing as well as 72 | future claims and causes of action), in the Work (i) in all territories 73 | worldwide, (ii) for the maximum duration provided by applicable law or 74 | treaty (including future time extensions), (iii) in any current or future 75 | medium and for any number of copies, and (iv) for any purpose whatsoever, 76 | including without limitation commercial, advertising or promotional 77 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 78 | member of the public at large and to the detriment of Affirmer's heirs and 79 | successors, fully intending that such Waiver shall not be subject to 80 | revocation, rescission, cancellation, termination, or any other legal or 81 | equitable action to disrupt the quiet enjoyment of the Work by the public 82 | as contemplated by Affirmer's express Statement of Purpose. 83 | 84 | 3. Public License Fallback. Should any part of the Waiver for any reason 85 | be judged legally invalid or ineffective under applicable law, then the 86 | Waiver shall be preserved to the maximum extent permitted taking into 87 | account Affirmer's express Statement of Purpose. In addition, to the 88 | extent the Waiver is so judged Affirmer hereby grants to each affected 89 | person a royalty-free, non transferable, non sublicensable, non exclusive, 90 | irrevocable and unconditional license to exercise Affirmer's Copyright and 91 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 92 | maximum duration provided by applicable law or treaty (including future 93 | time extensions), (iii) in any current or future medium and for any number 94 | of copies, and (iv) for any purpose whatsoever, including without 95 | limitation commercial, advertising or promotional purposes (the 96 | "License"). The License shall be deemed effective as of the date CC0 was 97 | applied by Affirmer to the Work. Should any part of the License for any 98 | reason be judged legally invalid or ineffective under applicable law, such 99 | partial invalidity or ineffectiveness shall not invalidate the remainder 100 | of the License, and in such case Affirmer hereby affirms that he or she 101 | will not (i) exercise any of his or her remaining Copyright and Related 102 | Rights in the Work or (ii) assert any associated claims and causes of 103 | action with respect to the Work, in either case contrary to Affirmer's 104 | express Statement of Purpose. 105 | 106 | 4. Limitations and Disclaimers. 107 | 108 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 109 | surrendered, licensed or otherwise affected by this document. 110 | b. Affirmer offers the Work as-is and makes no representations or 111 | warranties of any kind concerning the Work, express, implied, 112 | statutory or otherwise, including without limitation warranties of 113 | title, merchantability, fitness for a particular purpose, non 114 | infringement, or the absence of latent or other defects, accuracy, or 115 | the present or absence of errors, whether or not discoverable, all to 116 | the greatest extent permissible under applicable law. 117 | c. Affirmer disclaims responsibility for clearing rights of other persons 118 | that may apply to the Work or any use thereof, including without 119 | limitation any person's Copyright and Related Rights in the Work. 120 | Further, Affirmer disclaims responsibility for obtaining any necessary 121 | consents, permissions or other rights required for any use of the 122 | Work. 123 | d. Affirmer understands and acknowledges that Creative Commons is not a 124 | party to this document and has no duty or obligation with respect to 125 | this CC0 or use of the Work. 126 | -------------------------------------------------------------------------------- /shared/src/main/scala/pt/kcry/sha/Sha3.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * scala-sha - Secure Hash Algorithms family for scala, scala-js and scala-native. 3 | * 4 | * Written in 2020, 2021 by Kirill A. Korinsky 5 | * 6 | * Supported since 2022 by Kcrypt Lab UG 7 | * 8 | * This work is released into the public domain with CC0 1.0. 9 | */ 10 | 11 | package pt.kcry.sha 12 | 13 | /** 14 | * This implementation is based on https://keccak.team/keccak_specs_summary.html 15 | * 16 | * Keccak is quite universal and requires the sate which I kept inside an 17 | * object. 18 | */ 19 | class Keccak(private var len: Int) extends Hash { 20 | import java.lang.Long.rotateLeft 21 | 22 | private var length: Long = 0 23 | 24 | private val rate: Int = 200 - 2 * len 25 | 26 | private val S: Array[Long] = new Array[Long](25) 27 | 28 | def update(bytes: Array[Byte], off: Int, len: Int): Unit = { 29 | var i = 0 30 | while (i < len) { 31 | process(bytes(i + off).toInt) 32 | i += 1 33 | } 34 | } 35 | 36 | private def process(byt: Int): Unit = { 37 | var cnt = (length % rate).toInt 38 | val b = cnt % 8 39 | cnt /= 8 40 | val i = cnt % 5 41 | val j = cnt / 5 42 | 43 | S(i * 5 + j) ^= ((byt & 0xff).toLong << (8 * b)) 44 | length += 1 45 | 46 | if ((length % rate) == 0) transform() 47 | } 48 | 49 | @inline 50 | private def transformRound(): Unit = { 51 | val C_0 = S(0) ^ S(1) ^ S(2) ^ S(3) ^ S(4) 52 | val C_1 = S(5) ^ S(6) ^ S(7) ^ S(8) ^ S(9) 53 | val C_2 = S(10) ^ S(11) ^ S(12) ^ S(13) ^ S(14) 54 | val C_3 = S(15) ^ S(16) ^ S(17) ^ S(18) ^ S(19) 55 | val C_4 = S(20) ^ S(21) ^ S(22) ^ S(23) ^ S(24) 56 | val D_0 = C_4 ^ rotateLeft(C_1, 1) 57 | val D_1 = C_0 ^ rotateLeft(C_2, 1) 58 | val D_2 = C_1 ^ rotateLeft(C_3, 1) 59 | val D_3 = C_2 ^ rotateLeft(C_4, 1) 60 | val D_4 = C_3 ^ rotateLeft(C_0, 1) 61 | val B_0_0 = S(0) ^ D_0 62 | val B_1_3 = rotateLeft(S(1) ^ D_0, 36) 63 | val B_2_1 = rotateLeft(S(2) ^ D_0, 3) 64 | val B_3_4 = rotateLeft(S(3) ^ D_0, 41) 65 | val B_4_2 = rotateLeft(S(4) ^ D_0, 18) 66 | val B_0_2 = rotateLeft(S(5) ^ D_1, 1) 67 | val B_1_0 = rotateLeft(S(6) ^ D_1, 44) 68 | val B_2_3 = rotateLeft(S(7) ^ D_1, 10) 69 | val B_3_1 = rotateLeft(S(8) ^ D_1, 45) 70 | val B_4_4 = rotateLeft(S(9) ^ D_1, 2) 71 | val B_0_4 = rotateLeft(S(10) ^ D_2, 62) 72 | val B_1_2 = rotateLeft(S(11) ^ D_2, 6) 73 | val B_2_0 = rotateLeft(S(12) ^ D_2, 43) 74 | val B_3_3 = rotateLeft(S(13) ^ D_2, 15) 75 | val B_4_1 = rotateLeft(S(14) ^ D_2, 61) 76 | val B_0_1 = rotateLeft(S(15) ^ D_3, 28) 77 | val B_1_4 = rotateLeft(S(16) ^ D_3, 55) 78 | val B_2_2 = rotateLeft(S(17) ^ D_3, 25) 79 | val B_3_0 = rotateLeft(S(18) ^ D_3, 21) 80 | val B_4_3 = rotateLeft(S(19) ^ D_3, 56) 81 | val B_0_3 = rotateLeft(S(20) ^ D_4, 27) 82 | val B_1_1 = rotateLeft(S(21) ^ D_4, 20) 83 | val B_2_4 = rotateLeft(S(22) ^ D_4, 39) 84 | val B_3_2 = rotateLeft(S(23) ^ D_4, 8) 85 | val B_4_0 = rotateLeft(S(24) ^ D_4, 14) 86 | S(0) = B_0_0 ^ (~B_1_0 & B_2_0) 87 | S(1) = B_0_1 ^ (~B_1_1 & B_2_1) 88 | S(2) = B_0_2 ^ (~B_1_2 & B_2_2) 89 | S(3) = B_0_3 ^ (~B_1_3 & B_2_3) 90 | S(4) = B_0_4 ^ (~B_1_4 & B_2_4) 91 | S(5) = B_1_0 ^ (~B_2_0 & B_3_0) 92 | S(6) = B_1_1 ^ (~B_2_1 & B_3_1) 93 | S(7) = B_1_2 ^ (~B_2_2 & B_3_2) 94 | S(8) = B_1_3 ^ (~B_2_3 & B_3_3) 95 | S(9) = B_1_4 ^ (~B_2_4 & B_3_4) 96 | S(10) = B_2_0 ^ (~B_3_0 & B_4_0) 97 | S(11) = B_2_1 ^ (~B_3_1 & B_4_1) 98 | S(12) = B_2_2 ^ (~B_3_2 & B_4_2) 99 | S(13) = B_2_3 ^ (~B_3_3 & B_4_3) 100 | S(14) = B_2_4 ^ (~B_3_4 & B_4_4) 101 | S(15) = B_3_0 ^ (~B_4_0 & B_0_0) 102 | S(16) = B_3_1 ^ (~B_4_1 & B_0_1) 103 | S(17) = B_3_2 ^ (~B_4_2 & B_0_2) 104 | S(18) = B_3_3 ^ (~B_4_3 & B_0_3) 105 | S(19) = B_3_4 ^ (~B_4_4 & B_0_4) 106 | S(20) = B_4_0 ^ (~B_0_0 & B_1_0) 107 | S(21) = B_4_1 ^ (~B_0_1 & B_1_1) 108 | S(22) = B_4_2 ^ (~B_0_2 & B_1_2) 109 | S(23) = B_4_3 ^ (~B_0_3 & B_1_3) 110 | S(24) = B_4_4 ^ (~B_0_4 & B_1_4) 111 | } 112 | 113 | private def transform(): Unit = { 114 | transformRound() 115 | S(0) ^= 0x0000000000000001L 116 | transformRound() 117 | S(0) ^= 0x0000000000008082L 118 | transformRound() 119 | S(0) ^= 0x800000000000808aL 120 | transformRound() 121 | S(0) ^= 0x8000000080008000L 122 | transformRound() 123 | S(0) ^= 0x000000000000808bL 124 | transformRound() 125 | S(0) ^= 0x0000000080000001L 126 | transformRound() 127 | S(0) ^= 0x8000000080008081L 128 | transformRound() 129 | S(0) ^= 0x8000000000008009L 130 | transformRound() 131 | S(0) ^= 0x000000000000008aL 132 | transformRound() 133 | S(0) ^= 0x0000000000000088L 134 | transformRound() 135 | S(0) ^= 0x0000000080008009L 136 | transformRound() 137 | S(0) ^= 0x000000008000000aL 138 | transformRound() 139 | S(0) ^= 0x000000008000808bL 140 | transformRound() 141 | S(0) ^= 0x800000000000008bL 142 | transformRound() 143 | S(0) ^= 0x8000000000008089L 144 | transformRound() 145 | S(0) ^= 0x8000000000008003L 146 | transformRound() 147 | S(0) ^= 0x8000000000008002L 148 | transformRound() 149 | S(0) ^= 0x8000000000000080L 150 | transformRound() 151 | S(0) ^= 0x000000000000800aL 152 | transformRound() 153 | S(0) ^= 0x800000008000000aL 154 | transformRound() 155 | S(0) ^= 0x8000000080008081L 156 | transformRound() 157 | S(0) ^= 0x8000000000008080L 158 | transformRound() 159 | S(0) ^= 0x0000000080000001L 160 | transformRound() 161 | S(0) ^= 0x8000000080008008L 162 | } 163 | 164 | def finish(hashed: Array[Byte], off: Int): Unit = 165 | squeeze(mask = 0x06, hashed = hashed, off = off, len = len) 166 | 167 | def finish(hashed: Array[Byte], off: Int, len: Int): Unit = 168 | squeeze(mask = 0x1f, hashed = hashed, off = off, len = len) 169 | 170 | def squeeze(mask: Int, hashed: Array[Byte], off: Int, len: Int): Unit = { 171 | val q: Int = rate - (length % rate).toInt 172 | if (q == 1) process(0x80 + mask) 173 | else { 174 | process(mask) 175 | while (length % rate != rate - 1) process(0x00) 176 | process(0x80) 177 | } 178 | squeeze(hashed = hashed, off = off, len = len) 179 | } 180 | 181 | def squeeze(hashed: Array[Byte], off: Int, len: Int): Unit = { 182 | var done = false 183 | var i = 0 184 | var j = 0 185 | var k = 0 186 | var m = 0 187 | 188 | while (!done) { 189 | j = 0 190 | while (j < 5 && !done) { 191 | i = 0 192 | while (i < 5 && !done) { 193 | var el = S(i * 5 + j) 194 | k = 0 195 | while (k < 8 && !done) { 196 | hashed(m + off) = (el & 0xff).toByte 197 | m += 1 198 | if (m >= len || (m % rate) == 0) done = true 199 | el >>>= 8 200 | k += 1 201 | } 202 | i += 1 203 | } 204 | j += 1 205 | } 206 | 207 | if (m < len) done = false 208 | 209 | transform() 210 | } 211 | } 212 | } 213 | 214 | sealed trait Sha3 { 215 | val HASH_SIZE: Int 216 | 217 | def hash(message: Array[Byte]): Array[Byte] = { 218 | val keccak = new Keccak(HASH_SIZE) 219 | keccak.update(message, 0, message.length) 220 | val hashed = new Array[Byte](HASH_SIZE) 221 | keccak.finish(hashed, 0) 222 | hashed 223 | } 224 | } 225 | 226 | sealed trait Shake { 227 | val HASH_SIZE: Int 228 | 229 | def hash(message: Array[Byte], outputLen: Int): Array[Byte] = { 230 | val keccak = new Keccak(HASH_SIZE) 231 | keccak.update(message, 0, message.length) 232 | val hashed = new Array[Byte](outputLen) 233 | keccak.finish(hashed, 0, outputLen) 234 | hashed 235 | } 236 | } 237 | 238 | class Sha3_224 extends Keccak(Sha3_224.HASH_SIZE) 239 | 240 | object Sha3_224 extends Sha3 { 241 | val HASH_SIZE: Int = 28 242 | } 243 | 244 | class Sha3_256 extends Keccak(Sha3_256.HASH_SIZE) 245 | 246 | object Sha3_256 extends Sha3 { 247 | val HASH_SIZE: Int = 32 248 | } 249 | 250 | class Sha3_384 extends Keccak(Sha3_384.HASH_SIZE) 251 | 252 | object Sha3_384 extends Sha3 { 253 | val HASH_SIZE: Int = 48 254 | } 255 | 256 | class Sha3_512 extends Keccak(Sha3_512.HASH_SIZE) 257 | 258 | object Sha3_512 extends Sha3 { 259 | val HASH_SIZE: Int = 64 260 | } 261 | 262 | class Shake_128 extends Keccak(Shake_128.HASH_SIZE) 263 | 264 | object Shake_128 extends Shake { 265 | val HASH_SIZE: Int = 16 266 | } 267 | 268 | class Shake_256 extends Keccak(Shake_256.HASH_SIZE) 269 | 270 | object Shake_256 extends Shake { 271 | val HASH_SIZE: Int = 32 272 | } 273 | -------------------------------------------------------------------------------- /shared/src/test/scala/pt/kcry/sha/HashTestVectors.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * scala-sha - Secure Hash Algorithms family for scala, scala-js and scala-native. 3 | * 4 | * Written in 2020, 2021 by Kirill A. Korinsky 5 | * 6 | * Supported since 2022 by Kcrypt Lab UG 7 | * 8 | * This work is released into the public domain with CC0 1.0. 9 | */ 10 | 11 | package pt.kcry.sha 12 | 13 | import org.scalatest._ 14 | import org.scalatest.matchers.should 15 | 16 | import scala.language.implicitConversions 17 | 18 | class HashTestVectors extends wordspec.AnyWordSpec with should.Matchers { 19 | 20 | /** 21 | * Source of test vectors: https://www.di-mgt.com.au/sha_testvectors.html 22 | * 23 | * This hash wasn't implemented to hash something huge. 24 | * 25 | * This mean that I skip the extremely-long message vector. 26 | * 27 | * Also, SHA-0 and Shakes aren't a part of this test vector => I've computed 28 | * it by hand. 29 | */ 30 | "DI Management test vectors" when { 31 | "length 24 bits" in { 32 | "abc" sha0_shouldBe "0164b8a9 14cd2a5e 74c4f7ff 082c4d97 f1edf880" 33 | "abc" sha1_shouldBe "a9993e36 4706816a ba3e2571 7850c26c 9cd0d89d" 34 | "abc" sha2_224_ShouldBe 35 | "23097d22 3405d822 8642a477 bda255b3 2aadbce4 bda0b3f7 e36c9da7" 36 | "abc" sha2_256_ShouldBe 37 | "ba7816bf 8f01cfea 414140de 5dae2223 b00361a3 96177a9c b410ff61 f20015ad" 38 | "abc" sha2_384_ShouldBe 39 | "cb00753f45a35e8b b5a03d699ac65007 272c32ab0eded163 1a8b605a43ff5bed 8086072ba1e7cc23 58baeca134c825a7" 40 | "abc" sha2_512_ShouldBe 41 | "ddaf35a193617aba cc417349ae204131 12e6fa4e89a97ea2 0a9eeee64b55d39a 2192992a274fc1a8 36ba3c23a3feebbd 454d4423643ce80e 2a9ac94fa54ca49f" 42 | "abc" sha3_224_ShouldBe 43 | "e642824c3f8cf24a d09234ee7d3c766f c9a3a5168d0c94ad 73b46fdf" 44 | "abc" sha3_256_ShouldBe 45 | "3a985da74fe225b2 045c172d6bd390bd 855f086e3e9d525b 46bfe24511431532" 46 | "abc" sha3_384_ShouldBe 47 | "ec01498288516fc9 26459f58e2c6ad8d f9b473cb0fc08c25 96da7cf0e49be4b2 98d88cea927ac7f5 39f1edf228376d25" 48 | "abc" sha3_512_ShouldBe 49 | "b751850b1a57168a 5693cd924b6b096e 08f621827444f70d 884f5d0240d2712e 10e116e9192af3c9 1a7ec57647e39340 57340b4cf408d5a5 6592f8274eec53f0" 50 | "abc" shake_128_ShouldBe "5881092dd818bf5cf8a3ddb793" 51 | "abc" shake_256_ShouldBe 52 | "483366601360a8771c6863080cc4114d8db44530f8f1e1ee4f94ea37e78b57" 53 | } 54 | 55 | "length 0 bits" in { 56 | "" sha0_shouldBe "f96cea19 8ad1dd56 17ac084a 3d92c610 7708c0ef" 57 | "" sha1_shouldBe "da39a3ee 5e6b4b0d 3255bfef 95601890 afd80709" 58 | "" sha2_224_ShouldBe 59 | "d14a028c 2a3a2bc9 476102bb 288234c4 15a2b01f 828ea62a c5b3e42f" 60 | "" sha2_256_ShouldBe 61 | "e3b0c442 98fc1c14 9afbf4c8 996fb924 27ae41e4 649b934c a495991b 7852b855" 62 | "" sha2_384_ShouldBe 63 | "38b060a751ac9638 4cd9327eb1b1e36a 21fdb71114be0743 4c0cc7bf63f6e1da 274edebfe76f65fb d51ad2f14898b95b" 64 | "" sha2_512_ShouldBe 65 | "cf83e1357eefb8bd f1542850d66d8007 d620e4050b5715dc 83f4a921d36ce9ce 47d0d13c5d85f2b0 ff8318d2877eec2f 63b931bd47417a81 a538327af927da3e" 66 | "" sha3_224_ShouldBe 67 | "6b4e03423667dbb7 3b6e15454f0eb1ab d4597f9a1b078e3f 5b5a6bc7" 68 | "" sha3_256_ShouldBe 69 | "a7ffc6f8bf1ed766 51c14756a061d662 f580ff4de43b49fa 82d80a4b80f8434a" 70 | "" sha3_384_ShouldBe 71 | "0c63a75b845e4f7d 01107d852e4c2485 c51a50aaaa94fc61 995e71bbee983a2a c3713831264adb47 fb6bd1e058d5f004" 72 | "" sha3_512_ShouldBe 73 | "a69f73cca23a9ac5 c8b567dc185a756e 97c982164fe25859 e0d1dcc1475c80a6 15b2123af1f5f94c 11e3e9402c3ac558 f500199d95b6d3e3 01758586281dcd26" 74 | "" shake_128_ShouldBe "7f9c2ba4e88f827d6160455076" 75 | "" shake_256_ShouldBe 76 | "46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed576" 77 | } 78 | 79 | "length 448 bits" in { 80 | val input = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" 81 | input sha0_shouldBe "d2516ee1 acfa5baf 33dfc1c4 71e43844 9ef134c8" 82 | input sha1_shouldBe "84983e44 1c3bd26e baae4aa1 f95129e5 e54670f1" 83 | input sha2_224_ShouldBe 84 | "75388b16 512776cc 5dba5da1 fd890150 b0c6455c b4f58b19 52522525" 85 | input sha2_256_ShouldBe 86 | "248d6a61 d20638b8 e5c02693 0c3e6039 a33ce459 64ff2167 f6ecedd4 19db06c1" 87 | input sha2_384_ShouldBe 88 | "3391fdddfc8dc739 3707a65b1b470939 7cf8b1d162af05ab fe8f450de5f36bc6 b0455a8520bc4e6f 5fe95b1fe3c8452b" 89 | input sha2_512_ShouldBe 90 | "204a8fc6dda82f0a 0ced7beb8e08a416 57c16ef468b228a8 279be331a703c335 96fd15c13b1b07f9 aa1d3bea57789ca0 31ad85c7a71dd703 54ec631238ca3445" 91 | input sha3_224_ShouldBe 92 | "8a24108b154ada21 c9fd5574494479ba 5c7e7ab76ef264ea d0fcce33" 93 | input sha3_256_ShouldBe 94 | "41c0dba2a9d62408 49100376a8235e2c 82e1b9998a999e21 db32dd97496d3376" 95 | input sha3_384_ShouldBe 96 | "991c665755eb3a4b 6bbdfb75c78a492e 8c56a22c5c4d7e42 9bfdbc32b9d4ad5a a04a1f076e62fea1 9eef51acd0657c22" 97 | input sha3_512_ShouldBe 98 | "04a371e84ecfb5b8 b77cb48610fca818 2dd457ce6f326a0f d3d7ec2f1e91636d ee691fbe0c985302 ba1b0d8dc78c0863 46b533b49c030d99 a27daf1139d6e75e" 99 | input shake_128_ShouldBe "1a96182b50fb8c7e74e0a70778" 100 | input shake_256_ShouldBe 101 | "4d8c2dd2435a0128eefbb8c36f6f87133a7911e18d979ee1ae6be5d4fd2e33" 102 | } 103 | 104 | "length 896 bits" in { 105 | val input = 106 | "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" 107 | input sha0_shouldBe "459f83b9 5db2dc87 bb0f5b51 3a28f900 ede83237" 108 | input sha1_shouldBe "a49b2446 a02c645b f419f995 b6709125 3a04a259" 109 | input sha2_224_ShouldBe 110 | "c97ca9a5 59850ce9 7a04a96d ef6d99a9 e0e0e2ab 14e6b8df 265fc0b3" 111 | input sha2_256_ShouldBe 112 | "cf5b16a7 78af8380 036ce59e 7b049237 0b249b11 e8f07a51 afac4503 7afee9d1" 113 | input sha2_384_ShouldBe 114 | "09330c33f71147e8 3d192fc782cd1b47 53111b173b3b05d2 2fa08086e3b0f712 fcc7c71a557e2db9 66c3e9fa91746039" 115 | input sha2_512_ShouldBe 116 | "8e959b75dae313da 8cf4f72814fc143f 8f7779c6eb9f7fa1 7299aeadb6889018 501d289e4900f7e4 331b99dec4b5433a c7d329eeb6dd2654 5e96e55b874be909" 117 | input sha3_224_ShouldBe 118 | "543e6868e1666c1a 643630df77367ae5 a62a85070a51c14c bf665cbc" 119 | input sha3_256_ShouldBe 120 | "916f6061fe879741 ca6469b43971dfdb 28b1a32dc36cb325 4e812be27aad1d18" 121 | input sha3_384_ShouldBe 122 | "79407d3b5916b59c 3e30b09822974791 c313fb9ecc849e40 6f23592d04f625dc 8c709b98b43b3852 b337216179aa7fc7" 123 | input sha3_512_ShouldBe 124 | "afebb2ef542e6579 c50cad06d2e578f9 f8dd6881d7dc824d 26360feebf18a4fa 73e3261122948efc fd492e74e82e2189 ed0fb440d187f382 270cb455f21dd185" 125 | input shake_128_ShouldBe "7b6df6ff181173b6d7898d7ff6" 126 | input shake_256_ShouldBe 127 | "98be04516c04cc73593fef3ed0352ea9f6443942d6950e29a372a681c3deaf" 128 | } 129 | 130 | "one million repetitions of the a" in { 131 | val input = (0 until 1000000).map(_ => 'a').mkString 132 | input sha0_shouldBe "3232affa 48628a26 653b5aaa 44541fd9 0d690603" 133 | input sha1_shouldBe "34aa973c d4c4daa4 f61eeb2b dbad2731 6534016f" 134 | input sha2_224_ShouldBe 135 | "20794655 980c91d8 bbb4c1ea 97618a4b f03f4258 1948b2ee 4ee7ad67" 136 | input sha2_256_ShouldBe 137 | "cdc76e5c 9914fb92 81a1c7e2 84d73e67 f1809a48 a497200e 046d39cc c7112cd0" 138 | input sha2_384_ShouldBe 139 | "9d0e1809716474cb 086e834e310a4a1c ed149e9c00f24852 7972cec5704c2a5b 07b8b3dc38ecc4eb ae97ddd87f3d8985" 140 | input sha2_512_ShouldBe 141 | "e718483d0ce76964 4e2e42c7bc15b463 8e1f98b13b204428 5632a803afa973eb de0ff244877ea60a 4cb0432ce577c31b eb009c5c2c49aa2e 4eadb217ad8cc09b" 142 | input sha3_224_ShouldBe 143 | "d69335b93325192e 516a912e6d19a15c b51c6ed5c15243e7 a7fd653c" 144 | input sha3_256_ShouldBe 145 | "5c8875ae474a3634 ba4fd55ec85bffd6 61f32aca75c6d699 d0cdcb6c115891c1" 146 | input sha3_384_ShouldBe 147 | "eee9e24d78c18553 37983451df97c8ad 9eedf256c6334f8e 948d252d5e0e7684 7aa0774ddb90a842 190d2c558b4b8340" 148 | input sha3_512_ShouldBe 149 | "3c3a876da14034ab 60627c077bb98f7e 120a2a5370212dff b3385a18d4f38859 ed311d0a9d5141ce 9cc5c66ee689b266 a8aa18ace8282a0e 0db596c90b0a7b87" 150 | input shake_128_ShouldBe "9d222c79c4ff9d092cf6ca8614" 151 | input shake_256_ShouldBe 152 | "3578a7a4ca9137569cdf76ed617d31bb994fca9c1bbf8b184013de8234dfd1" 153 | } 154 | } 155 | 156 | case class RichString(str: String) { 157 | def sha0_shouldBe(expected: Array[Byte]): Assertion = Sha0 158 | .hash(str.getBytes()) shouldBe expected 159 | 160 | def sha1_shouldBe(expected: Array[Byte]): Assertion = Sha1 161 | .hash(str.getBytes()) shouldBe expected 162 | 163 | def sha2_224_ShouldBe(expected: Array[Byte]): Assertion = Sha2_224 164 | .hash(str.getBytes()) shouldBe expected 165 | 166 | def sha2_256_ShouldBe(expected: Array[Byte]): Assertion = Sha2_256 167 | .hash(str.getBytes()) shouldBe expected 168 | 169 | def sha2_384_ShouldBe(expected: Array[Byte]): Assertion = Sha2_384 170 | .hash(str.getBytes()) shouldBe expected 171 | 172 | def sha2_512_ShouldBe(expected: Array[Byte]): Assertion = Sha2_512 173 | .hash(str.getBytes()) shouldBe expected 174 | 175 | def sha3_224_ShouldBe(expected: Array[Byte]): Assertion = Sha3_224 176 | .hash(str.getBytes()) shouldBe expected 177 | 178 | def sha3_256_ShouldBe(expected: Array[Byte]): Assertion = Sha3_256 179 | .hash(str.getBytes()) shouldBe expected 180 | 181 | def sha3_384_ShouldBe(expected: Array[Byte]): Assertion = Sha3_384 182 | .hash(str.getBytes()) shouldBe expected 183 | 184 | def sha3_512_ShouldBe(expected: Array[Byte]): Assertion = Sha3_512 185 | .hash(str.getBytes()) shouldBe expected 186 | 187 | def shake_128_ShouldBe(expected: Array[Byte]): Assertion = Shake_128 188 | .hash(str.getBytes(), 13) shouldBe expected 189 | 190 | def shake_256_ShouldBe(expected: Array[Byte]): Assertion = Shake_256 191 | .hash(str.getBytes(), 31) shouldBe expected 192 | } 193 | 194 | implicit def string2richString(str: String): RichString = RichString(str) 195 | 196 | implicit def hex2bytes(hex: String): Array[Byte] = { 197 | val chunks = hex.replace(" ", "").sliding(2, 2).toArray 198 | chunks.map(Integer.parseInt(_, 16).toByte) 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /shared/src/main/scala/pt/kcry/sha/Sha2.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * scala-sha - Secure Hash Algorithms family for scala, scala-js and scala-native. 3 | * 4 | * Written in 2020, 2021 by Kirill A. Korinsky 5 | * 6 | * Supported since 2022 by Kcrypt Lab UG 7 | * 8 | * This work is released into the public domain with CC0 1.0. 9 | */ 10 | 11 | package pt.kcry.sha 12 | 13 | /** 14 | * Quite ugly but fast enough implementation of SHA-2 for 32 bit case. 15 | * 16 | * This implementation isn't thread safe. 17 | */ 18 | sealed trait Sha2_32bit extends BlockedHash[Array[Int]] { 19 | import Sha2._ 20 | 21 | import java.lang.Integer.rotateRight 22 | 23 | protected val H: Array[Int] 24 | 25 | protected val len: Int 26 | 27 | protected val words = new Array[Int](64) 28 | protected val block = new Array[Byte](64) 29 | 30 | override def finish(hashed: Array[Byte], off: Int): Unit = { 31 | val padded = padding_32bit(messageLen) 32 | update(padded, 0, padded.length) 33 | 34 | var i = 0 35 | while (i < len) { 36 | val c = 4 * i 37 | hashed(c + off) = ((H(i) >>> 56) & 0xff).toByte 38 | hashed(c + 1 + off) = ((H(i) >>> (56 - 8)) & 0xff).toByte 39 | hashed(c + 2 + off) = ((H(i) >>> (56 - 16)) & 0xff).toByte 40 | hashed(c + 3 + off) = ((H(i) >>> (56 - 24)) & 0xff).toByte 41 | i += 1 42 | } 43 | } 44 | 45 | protected def finishBlock(block: Array[Byte], off: Int): Unit = { 46 | var j = 0 47 | while (j < 16) { 48 | words(j) = 0 49 | var k = 0 50 | while (k < 4) { 51 | words(j) |= ((block(j * 4 + k + off) & 0x000000ff) << (24 - k * 8)) 52 | k += 1 53 | } 54 | j += 1 55 | } 56 | 57 | while (j < 64) { 58 | val s0 = rotateRight(words(j - 15), 7) ^ rotateRight(words(j - 15), 18) ^ 59 | (words(j - 15) >>> 3) 60 | val s1 = rotateRight(words(j - 2), 17) ^ rotateRight(words(j - 2), 19) ^ 61 | (words(j - 2) >>> 10) 62 | words(j) = words(j - 16) + s0 + words(j - 7) + s1 63 | j += 1 64 | } 65 | 66 | var a = H(0) 67 | var b = H(1) 68 | var c = H(2) 69 | var d = H(3) 70 | var e = H(4) 71 | var f = H(5) 72 | var g = H(6) 73 | var h = H(7) 74 | j = 0 75 | while (j < 64) { 76 | val s0 = rotateRight(a, 2) ^ rotateRight(a, 13) ^ rotateRight(a, 22) 77 | val maj = (a & b) ^ (a & c) ^ (b & c) 78 | val t2 = s0 + maj 79 | val s1 = rotateRight(e, 6) ^ rotateRight(e, 11) ^ rotateRight(e, 25) 80 | val ch = (e & f) ^ (~e & g) 81 | val t1 = h + s1 + ch + K_32bit(j) + words(j) 82 | h = g 83 | g = f 84 | f = e 85 | e = d + t1 86 | d = c 87 | c = b 88 | b = a 89 | a = t1 + t2 90 | j += 1 91 | } 92 | 93 | H(0) += a 94 | H(1) += b 95 | H(2) += c 96 | H(3) += d 97 | H(4) += e 98 | H(5) += f 99 | H(6) += g 100 | H(7) += h 101 | } 102 | } 103 | 104 | /** 105 | * Quite ugly but fast enough implementation of SHA-2 for 64 bit case. 106 | * 107 | * This implementation isn't thread safe. 108 | */ 109 | sealed trait Sha2_64bit extends BlockedHash[Array[Long]] { 110 | import Sha2._ 111 | 112 | import java.lang.Long.rotateRight 113 | 114 | protected val H: Array[Long] 115 | 116 | protected val len: Int 117 | 118 | protected val words = new Array[Long](80) 119 | protected val block = new Array[Byte](128) 120 | 121 | override def finish(hashed: Array[Byte], off: Int): Unit = { 122 | val padded = padding_64bit(messageLen) 123 | update(padded, 0, padded.length) 124 | 125 | var i = 0 126 | while (i < len) { 127 | val c = 8 * i 128 | hashed(c + off) = ((H(i) >>> 56) & 0xff).toByte 129 | hashed(c + 1 + off) = ((H(i) >>> (56 - 8)) & 0xff).toByte 130 | hashed(c + 2 + off) = ((H(i) >>> (56 - 16)) & 0xff).toByte 131 | hashed(c + 3 + off) = ((H(i) >>> (56 - 24)) & 0xff).toByte 132 | hashed(c + 4 + off) = ((H(i) >>> (56 - 32)) & 0xff).toByte 133 | hashed(c + 5 + off) = ((H(i) >>> (56 - 40)) & 0xff).toByte 134 | hashed(c + 6 + off) = ((H(i) >>> (56 - 48)) & 0xff).toByte 135 | hashed(c + 7 + off) = (H(i) & 0xff).toByte 136 | i += 1 137 | } 138 | } 139 | 140 | protected def finishBlock(block: Array[Byte], off: Int): Unit = { 141 | var j = 0 142 | while (j < 16) { 143 | words(j) = 0 144 | var k = 0 145 | while (k < 8) { 146 | words(j) |= 147 | ((block(j * 8 + k + off) & 0x00000000000000ffL) << (56 - k * 8)) 148 | k += 1 149 | } 150 | j += 1 151 | } 152 | 153 | while (j < 80) { 154 | val s0 = rotateRight(words(j - 15), 1) ^ rotateRight(words(j - 15), 8) ^ 155 | (words(j - 15) >>> 7) 156 | val s1 = rotateRight(words(j - 2), 19) ^ rotateRight(words(j - 2), 61) ^ 157 | (words(j - 2) >>> 6) 158 | words(j) = words(j - 16) + s0 + words(j - 7) + s1 159 | j += 1 160 | } 161 | 162 | var a = H(0) 163 | var b = H(1) 164 | var c = H(2) 165 | var d = H(3) 166 | var e = H(4) 167 | var f = H(5) 168 | var g = H(6) 169 | var h = H(7) 170 | j = 0 171 | while (j < 80) { 172 | val s0 = rotateRight(a, 28) ^ rotateRight(a, 34) ^ rotateRight(a, 39) 173 | val maj = (a & b) ^ (a & c) ^ (b & c) 174 | val t2 = s0 + maj 175 | val s1 = rotateRight(e, 14) ^ rotateRight(e, 18) ^ rotateRight(e, 41) 176 | val ch = (e & f) ^ (~e & g) 177 | val t1 = h + s1 + ch + K_64bit(j) + words(j) 178 | h = g 179 | g = f 180 | f = e 181 | e = d + t1 182 | d = c 183 | c = b 184 | b = a 185 | a = t1 + t2 186 | j += 1 187 | } 188 | 189 | H(0) += a 190 | H(1) += b 191 | H(2) += c 192 | H(3) += d 193 | H(4) += e 194 | H(5) += f 195 | H(6) += g 196 | H(7) += h 197 | } 198 | } 199 | 200 | private[sha] object Sha2 { 201 | val K_32bit: Array[Int] = Array( 202 | 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 203 | 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 204 | 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 205 | 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 206 | 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 207 | 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 208 | 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 209 | 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 210 | 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 211 | 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 212 | 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 213 | ) 214 | 215 | val K_64bit: Array[Long] = Array( 216 | 0x428a2f98d728ae22L, 0x7137449123ef65cdL, 0xb5c0fbcfec4d3b2fL, 217 | 0xe9b5dba58189dbbcL, 0x3956c25bf348b538L, 0x59f111f1b605d019L, 218 | 0x923f82a4af194f9bL, 0xab1c5ed5da6d8118L, 0xd807aa98a3030242L, 219 | 0x12835b0145706fbeL, 0x243185be4ee4b28cL, 0x550c7dc3d5ffb4e2L, 220 | 0x72be5d74f27b896fL, 0x80deb1fe3b1696b1L, 0x9bdc06a725c71235L, 221 | 0xc19bf174cf692694L, 0xe49b69c19ef14ad2L, 0xefbe4786384f25e3L, 222 | 0x0fc19dc68b8cd5b5L, 0x240ca1cc77ac9c65L, 0x2de92c6f592b0275L, 223 | 0x4a7484aa6ea6e483L, 0x5cb0a9dcbd41fbd4L, 0x76f988da831153b5L, 224 | 0x983e5152ee66dfabL, 0xa831c66d2db43210L, 0xb00327c898fb213fL, 225 | 0xbf597fc7beef0ee4L, 0xc6e00bf33da88fc2L, 0xd5a79147930aa725L, 226 | 0x06ca6351e003826fL, 0x142929670a0e6e70L, 0x27b70a8546d22ffcL, 227 | 0x2e1b21385c26c926L, 0x4d2c6dfc5ac42aedL, 0x53380d139d95b3dfL, 228 | 0x650a73548baf63deL, 0x766a0abb3c77b2a8L, 0x81c2c92e47edaee6L, 229 | 0x92722c851482353bL, 0xa2bfe8a14cf10364L, 0xa81a664bbc423001L, 230 | 0xc24b8b70d0f89791L, 0xc76c51a30654be30L, 0xd192e819d6ef5218L, 231 | 0xd69906245565a910L, 0xf40e35855771202aL, 0x106aa07032bbd1b8L, 232 | 0x19a4c116b8d2d0c8L, 0x1e376c085141ab53L, 0x2748774cdf8eeb99L, 233 | 0x34b0bcb5e19b48a8L, 0x391c0cb3c5c95a63L, 0x4ed8aa4ae3418acbL, 234 | 0x5b9cca4f7763e373L, 0x682e6ff3d6b2b8a3L, 0x748f82ee5defb2fcL, 235 | 0x78a5636f43172f60L, 0x84c87814a1f0ab72L, 0x8cc702081a6439ecL, 236 | 0x90befffa23631e28L, 0xa4506cebde82bde9L, 0xbef9a3f7b2c67915L, 237 | 0xc67178f2e372532bL, 0xca273eceea26619cL, 0xd186b8c721c0c207L, 238 | 0xeada7dd6cde0eb1eL, 0xf57d4f7fee6ed178L, 0x06f067aa72176fbaL, 239 | 0x0a637dc5a2c898a6L, 0x113f9804bef90daeL, 0x1b710b35131c471bL, 240 | 0x28db77f523047d84L, 0x32caab7b40c72493L, 0x3c9ebe0a15c9bebcL, 241 | 0x431d67c49c100d4cL, 0x4cc5d4becb3e42b6L, 0x597f299cfc657e2aL, 242 | 0x5fcb6fab3ad6faecL, 0x6c44198c4a475817L 243 | ) 244 | 245 | } 246 | 247 | class Sha2_224 extends Sha2_32bit { 248 | override protected val H: Array[Int] = Array( 249 | 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 250 | 0x64f98fa7, 0xbefa4fa4 251 | ) 252 | 253 | override protected val len: Int = 7 254 | } 255 | 256 | object Sha2_224 { 257 | val HASH_SIZE: Int = 28 258 | 259 | def hash(message: Array[Byte]): Array[Byte] = { 260 | val sha2_224 = new Sha2_224() 261 | sha2_224.update(message, 0, message.length) 262 | val hashed = new Array[Byte](HASH_SIZE) 263 | sha2_224.finish(hashed, 0) 264 | hashed 265 | } 266 | } 267 | 268 | class Sha2_256 extends Sha2_32bit { 269 | override protected val H: Array[Int] = Array( 270 | 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 271 | 0x1f83d9ab, 0x5be0cd19 272 | ) 273 | 274 | override protected val len: Int = 8 275 | } 276 | 277 | object Sha2_256 { 278 | val HASH_SIZE: Int = 32 279 | 280 | def hash(message: Array[Byte]): Array[Byte] = { 281 | val sha2_256 = new Sha2_256() 282 | sha2_256.update(message, 0, message.length) 283 | val hashed = new Array[Byte](HASH_SIZE) 284 | sha2_256.finish(hashed, 0) 285 | hashed 286 | } 287 | } 288 | 289 | class Sha2_384 extends Sha2_64bit { 290 | override protected val H: Array[Long] = Array( 291 | 0xcbbb9d5dc1059ed8L, 0x629a292a367cd507L, 0x9159015a3070dd17L, 292 | 0x152fecd8f70e5939L, 0x67332667ffc00b31L, 0x8eb44a8768581511L, 293 | 0xdb0c2e0d64f98fa7L, 0x47b5481dbefa4fa4L 294 | ) 295 | 296 | override protected val len: Int = 6 297 | } 298 | 299 | object Sha2_384 { 300 | val HASH_SIZE: Int = 48 301 | 302 | def hash(message: Array[Byte]): Array[Byte] = { 303 | val sha2_384 = new Sha2_384() 304 | sha2_384.update(message, 0, message.length) 305 | val hashed = new Array[Byte](HASH_SIZE) 306 | sha2_384.finish(hashed, 0) 307 | hashed 308 | } 309 | } 310 | 311 | class Sha2_512 extends Sha2_64bit { 312 | override protected val H: Array[Long] = Array( 313 | 0x6a09e667f3bcc908L, 0xbb67ae8584caa73bL, 0x3c6ef372fe94f82bL, 314 | 0xa54ff53a5f1d36f1L, 0x510e527fade682d1L, 0x9b05688c2b3e6c1fL, 315 | 0x1f83d9abfb41bd6bL, 0x5be0cd19137e2179L 316 | ) 317 | 318 | override protected val len: Int = 8 319 | } 320 | 321 | object Sha2_512 { 322 | val HASH_SIZE: Int = 64 323 | 324 | def hash(message: Array[Byte]): Array[Byte] = { 325 | val sha2_512 = new Sha2_512() 326 | sha2_512.update(message, 0, message.length) 327 | val hashed = new Array[Byte](HASH_SIZE) 328 | sha2_512.finish(hashed, 0) 329 | hashed 330 | } 331 | } 332 | --------------------------------------------------------------------------------