Skip to content

Commit 23b8fdb

Browse files
committed
Implemented block hashing
1 parent a6c7801 commit 23b8fdb

File tree

8 files changed

+526
-498
lines changed

8 files changed

+526
-498
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
All notable changes to this project will be documented in this file.
44

55
## [unreleased]
6+
- Implemented block hashing.
67

78
## [1.0.0] - 2021-07-11
89
- The first public release as dedicated project.

README.md

+28
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,39 @@ The latest version is ![maven-central]
2929
API is pretty simple and quite limited :)
3030
```
3131
scala> import ky.korins.sha._
32+
import ky.korins.sha._
3233
3334
scala> Sha2_256.hash("abc".getBytes())
3435
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)
3536
3637
scala>
3738
```
3839

40+
You may also create a new object from specified hash to `update` it, and at some
41+
point `finish` it like this:
42+
```
43+
scala> import ky.korins.sha._
44+
import ky.korins.sha._
45+
46+
scala> val sha1 = new Sha1()
47+
val sha1: ky.korins.sha.Sha1 = ky.korins.sha.Sha1@1224e1b6
48+
49+
scala> sha1.update("abc".getBytes(), 0, 2)
50+
51+
scala> sha1.update("abc".getBytes(), 2, 1)
52+
53+
scala> val hashed = new Array[Byte](20)
54+
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)
55+
56+
scala> sha1.finish(hashed, 0)
57+
58+
scala> hashed
59+
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)
60+
61+
scala>
62+
```
63+
64+
All these objects aren't thread safe. After `finish` it should be treated as
65+
broken.
66+
3967
[maven-central]: https://img.shields.io/maven-central/v/ky.korins/sha_2.13?style=flat-square
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
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 <[email protected]>
5+
*
6+
* This work is released into the public domain with CC0 1.0.
7+
*/
8+
9+
package ky.korins.sha
10+
11+
trait Hash {
12+
def update(bytes: Array[Byte], off: Int, len: Int): Unit
13+
14+
def finish(hashed: Array[Byte], off: Int): Unit
15+
16+
def finish(hashed: Array[Byte], off: Int, len: Int): Unit
17+
}
18+
19+
private[sha] trait BlockedHash[T <: Array[_]] extends Hash {
20+
21+
protected val words: T
22+
protected val block: Array[Byte]
23+
private var blockPos = 0
24+
25+
protected var messageLen = 0
26+
27+
protected def finishBlock(block: Array[Byte], off: Int): Unit
28+
29+
def update(bytes: Array[Byte], off: Int, len: Int): Unit = {
30+
var rem = len
31+
var pos = off
32+
if (blockPos > 0) {
33+
val use = math.min(block.length - blockPos, rem)
34+
System.arraycopy(bytes, pos, block, blockPos, use)
35+
rem -= use
36+
pos += use
37+
blockPos += use
38+
}
39+
if (blockPos == block.length) {
40+
finishBlock(block, 0)
41+
blockPos = 0
42+
}
43+
while (rem >= block.length) {
44+
finishBlock(bytes, pos)
45+
pos += block.length
46+
rem -= block.length
47+
}
48+
if (rem > 0) {
49+
System.arraycopy(bytes, pos, block, 0, rem)
50+
blockPos = rem
51+
}
52+
messageLen += len
53+
}
54+
55+
final def finish(hashed: Array[Byte], off: Int, len: Int): Unit =
56+
throw new NotImplementedError("XOF doesn't defined")
57+
58+
protected def padding_32bit(messageLen: Int): Array[Byte] = {
59+
val tailLength = messageLen & 0x3f
60+
val padLength = if (tailLength < 56) 64 - tailLength else 128 - tailLength
61+
62+
val padded = new Array[Byte](padLength)
63+
padded(0) = 0x80.toByte
64+
65+
val lengthInBits = messageLen.toLong * 8
66+
var i = 0
67+
while (i < 8) {
68+
padded(padded.length - 1 - i) = ((lengthInBits >>> (8 * i)) & 0xff).toByte
69+
i += 1
70+
}
71+
72+
padded
73+
}
74+
75+
protected def padding_64bit(messageLen: Int): Array[Byte] = {
76+
val tailLength = messageLen & 0x7f
77+
val padLength = if (tailLength < 112) 128 - tailLength else 256 - tailLength
78+
79+
val padded = new Array[Byte](padLength)
80+
padded(0) = 0x80.toByte
81+
82+
val lengthInBits = messageLen.toLong * 8
83+
var i = 0
84+
while (i < 8) {
85+
padded(padded.length - 1 - i) = ((lengthInBits >>> (8 * i)) & 0xff).toByte
86+
i += 1
87+
}
88+
89+
padded
90+
}
91+
}

shared/src/main/scala/ky/korins/sha/Paddings.scala

-56
This file was deleted.

shared/src/main/scala/ky/korins/sha/Sha0.scala

+13-99
Original file line numberDiff line numberDiff line change
@@ -9,109 +9,23 @@
99
package ky.korins.sha
1010

1111
/**
12-
* Quite ugly but fast enough implementation of SHA-0
13-
*
1412
* Keep in mind that SHA-0 is broken and I implemented it just for fun :)
13+
*
14+
* This implementation also isn't thread safe.
1515
*/
16-
object Sha0 {
17-
def hash(message: Array[Byte]): Array[Byte] = {
18-
import java.lang.Integer.rotateLeft
19-
20-
var H0 = 0x67452301
21-
var H1 = 0xefcdab89
22-
var H2 = 0x98badcfe
23-
var H3 = 0x10325476
24-
var H4 = 0xc3d2e1f0
25-
26-
val words = new Array[Int](80)
27-
val block = new Array[Byte](64)
28-
var padded = new Array[Byte](message.length + 64 - (message.length % 64))
29-
val hashed = new Array[Byte](20)
30-
31-
padded = Paddings.padding_32(message)
32-
val blocks = padded.length / 64
33-
34-
var i = 0
35-
while (i < blocks) {
36-
System.arraycopy(padded, 64 * i, block, 0, 64)
37-
var j = 0
38-
while (j < 16) {
39-
words(j) = 0
40-
var k = 0
41-
while (k < 4) {
42-
words(j) |= ((block(j * 4 + k) & 0x000000ff) << (24 - k * 8))
43-
k += 1
44-
}
45-
j += 1
46-
}
47-
48-
while (j < 80) {
49-
words(j) = words(j - 3) ^ words(j - 8) ^ words(j - 14) ^ words(j - 16)
50-
j += 1
51-
}
52-
53-
var A = H0
54-
var B = H1
55-
var C = H2
56-
var D = H3
57-
var E = H4
58-
var F = 0
59-
var G = 0
60-
j = 0
61-
while (j < 80) {
62-
if (j < 20) {
63-
F = (B & C) | (~B & D)
64-
G = 0x5a827999
65-
} else if (19 < j && j < 40) {
66-
F = B ^ C ^ D
67-
G = 0x6ed9eba1
68-
} else if (39 < j && j < 60) {
69-
F = (B & C) | (B & D) | (C & D)
70-
G = 0x8f1bbcdc
71-
} else {
72-
F = B ^ C ^ D
73-
G = 0xca62c1d6
74-
}
75-
76-
val temp = rotateLeft(A, 5) + F + E + G + words(j)
77-
78-
E = D
79-
D = C
80-
C = rotateLeft(B, 30)
81-
B = A
82-
A = temp
83-
j += 1
84-
}
85-
86-
H0 += A
87-
H1 += B
88-
H2 += C
89-
H3 += D
90-
H4 += E
91-
i += 1
92-
}
16+
class Sha0 extends Sha1 {
17+
override protected def processBlockWord(j: Int): Int =
18+
words(j - 3) ^ words(j - 8) ^ words(j - 14) ^ words(j - 16)
19+
}
9320

94-
hashed(0) = ((H0 >>> 24) & 0xff).toByte
95-
hashed(1) = ((H0 >>> 16) & 0xff).toByte
96-
hashed(2) = ((H0 >>> 8) & 0xff).toByte
97-
hashed(3) = (H0 & 0xff).toByte
98-
hashed(4) = ((H1 >>> 24) & 0xff).toByte
99-
hashed(5) = ((H1 >>> 16) & 0xff).toByte
100-
hashed(6) = ((H1 >>> 8) & 0xff).toByte
101-
hashed(7) = (H1 & 0xff).toByte
102-
hashed(8) = ((H2 >>> 24) & 0xff).toByte
103-
hashed(9) = ((H2 >>> 16) & 0xff).toByte
104-
hashed(10) = ((H2 >>> 8) & 0xff).toByte
105-
hashed(11) = (H2 & 0xff).toByte
106-
hashed(12) = ((H3 >>> 24) & 0xff).toByte
107-
hashed(13) = ((H3 >>> 16) & 0xff).toByte
108-
hashed(14) = ((H3 >>> 8) & 0xff).toByte
109-
hashed(15) = (H3 & 0xff).toByte
110-
hashed(16) = ((H4 >>> 24) & 0xff).toByte
111-
hashed(17) = ((H4 >>> 16) & 0xff).toByte
112-
hashed(18) = ((H4 >>> 8) & 0xff).toByte
113-
hashed(19) = (H4 & 0xff).toByte
21+
object Sha0 {
22+
val HASH_SIZE: Int = Sha1.HASH_SIZE
11423

24+
def hash(message: Array[Byte]): Array[Byte] = {
25+
val sha0 = new Sha0()
26+
sha0.update(message, 0, message.length)
27+
val hashed = new Array[Byte](HASH_SIZE)
28+
sha0.finish(hashed, 0)
11529
hashed
11630
}
11731
}

0 commit comments

Comments
 (0)