Skip to content

Commit e389204

Browse files
authored
Add (half)SipHash implementation (#108)
* Add (half)SipHash implementation * Fix unit tests * Include Review Comments * Update README with supported targets
1 parent ec75ce9 commit e389204

File tree

11 files changed

+672
-0
lines changed

11 files changed

+672
-0
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
## [Unreleased]
44

5+
### Added
6+
7+
* Siphash implementation
8+
* Halfsiphash implementation
9+
510
## [0.1.5] - 2023-08-16
611

712
### Changed

README-zh.md

+32
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# Diglol Crypto
22

3+
[![badge-license]][url-license]
4+
[![badge-latest-release]][url-latest-release]
5+
6+
![badge-platform-android]
7+
![badge-platform-jvm]
8+
![badge-platform-js]
9+
![badge-platform-linux]
10+
![badge-platform-ios]
11+
![badge-platform-tvos]
12+
![badge-platform-watchos]
13+
![badge-platform-windows]
14+
315
Diglol Crypto for Kotlin Multiplatform.
416

517
当前支持:
@@ -33,6 +45,9 @@ Diglol Crypto for Kotlin Multiplatform.
3345
- otp
3446
- [HOTP][hotp]
3547
- [TOTP][totp]
48+
- [siphash][siphash]
49+
- siphash
50+
- halfsiphash
3651

3752
### 发布
3853

@@ -82,3 +97,20 @@ implementation("com.diglol.crypto:${submodule}:0.1.5")
8297
[encryptthenmac]: https://datatracker.ietf.org/doc/html/draft-mcgrew-aead-aes-cbc-hmac-sha2-05
8398
[hotp]: https://datatracker.ietf.org/doc/html/rfc4226
8499
[totp]: https://datatracker.ietf.org/doc/html/rfc6238
100+
[siphash]: https://datatracker.ietf.org/doc/rfc9231/
101+
102+
<!-- TAG_VERSION -->
103+
[badge-latest-release]: https://img.shields.io/badge/latest--release-0.1.5-blue.svg?style=flat
104+
[badge-license]: https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat
105+
[url-latest-release]: https://github.com/diglol/crypto/releases/latest
106+
[url-license]: https://www.apache.org/licenses/LICENSE-2.0.txt
107+
108+
<!-- TAG_PLATFORMS -->
109+
[badge-platform-android]: http://img.shields.io/badge/-android-6EDB8D.svg?style=flat
110+
[badge-platform-jvm]: http://img.shields.io/badge/-jvm-DB413D.svg?style=flat
111+
[badge-platform-js]: http://img.shields.io/badge/-js-F8DB5D.svg?style=flat
112+
[badge-platform-linux]: http://img.shields.io/badge/-linux-2D3F6C.svg?style=flat
113+
[badge-platform-tvos]: http://img.shields.io/badge/-tvos-808080.svg?style=flat
114+
[badge-platform-ios]: http://img.shields.io/badge/-ios-808080.svg?style=flat
115+
[badge-platform-watchos]: http://img.shields.io/badge/-watchos-C0C0C0.svg?style=flat
116+
[badge-platform-windows]: http://img.shields.io/badge/-windows-4D76CD.svg?style=flat

README.md

+32
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# Diglol Crypto
22

3+
[![badge-license]][url-license]
4+
[![badge-latest-release]][url-latest-release]
5+
6+
![badge-platform-android]
7+
![badge-platform-jvm]
8+
![badge-platform-js]
9+
![badge-platform-linux]
10+
![badge-platform-ios]
11+
![badge-platform-tvos]
12+
![badge-platform-watchos]
13+
![badge-platform-windows]
14+
315
Diglol Crypto for Kotlin Multiplatform.
416

517
Currently supported:
@@ -33,6 +45,9 @@ Currently supported:
3345
- otp
3446
- [HOTP][hotp]
3547
- [TOTP][totp]
48+
- [siphash][siphash]
49+
- siphash
50+
- halfsiphash
3651

3752
### Releases
3853

@@ -82,3 +97,20 @@ implementation("com.diglol.crypto:${submodule}:0.1.5")
8297
[encryptthenmac]: https://datatracker.ietf.org/doc/html/draft-mcgrew-aead-aes-cbc-hmac-sha2-05
8398
[hotp]: https://datatracker.ietf.org/doc/html/rfc4226
8499
[totp]: https://datatracker.ietf.org/doc/html/rfc6238
100+
[siphash]: https://datatracker.ietf.org/doc/rfc9231/
101+
102+
<!-- TAG_VERSION -->
103+
[badge-latest-release]: https://img.shields.io/badge/latest--release-0.1.5-blue.svg?style=flat
104+
[badge-license]: https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat
105+
[url-latest-release]: https://github.com/diglol/crypto/releases/latest
106+
[url-license]: https://www.apache.org/licenses/LICENSE-2.0.txt
107+
108+
<!-- TAG_PLATFORMS -->
109+
[badge-platform-android]: http://img.shields.io/badge/-android-6EDB8D.svg?style=flat
110+
[badge-platform-jvm]: http://img.shields.io/badge/-jvm-DB413D.svg?style=flat
111+
[badge-platform-js]: http://img.shields.io/badge/-js-F8DB5D.svg?style=flat
112+
[badge-platform-linux]: http://img.shields.io/badge/-linux-2D3F6C.svg?style=flat
113+
[badge-platform-tvos]: http://img.shields.io/badge/-tvos-808080.svg?style=flat
114+
[badge-platform-ios]: http://img.shields.io/badge/-ios-808080.svg?style=flat
115+
[badge-platform-watchos]: http://img.shields.io/badge/-watchos-C0C0C0.svg?style=flat
116+
[badge-platform-windows]: http://img.shields.io/badge/-windows-4D76CD.svg?style=flat

settings.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ if (!System.getProperty("os.name").startsWith("Windows")) {
1515
}
1616
include(":cipher")
1717
include(":otp")
18+
include(":siphash")
1819

1920
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")

siphash/build.gradle.kts

+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import com.vanniktech.maven.publish.JavadocJar
2+
import com.vanniktech.maven.publish.KotlinMultiplatform
3+
import com.vanniktech.maven.publish.MavenPublishBaseExtension
4+
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
5+
6+
plugins {
7+
kotlin("multiplatform")
8+
id("com.android.library")
9+
id("org.jetbrains.dokka")
10+
id("com.vanniktech.maven.publish.base")
11+
}
12+
13+
kotlin {
14+
androidTarget {
15+
publishLibraryVariants("release")
16+
}
17+
jvm()
18+
js(IR) {
19+
browser()
20+
nodejs()
21+
}
22+
23+
macosX64()
24+
macosArm64()
25+
iosX64()
26+
iosArm64()
27+
iosSimulatorArm64()
28+
watchosArm32()
29+
watchosArm64()
30+
watchosSimulatorArm64()
31+
watchosX64()
32+
tvosArm64()
33+
tvosSimulatorArm64()
34+
tvosX64()
35+
36+
sourceSets {
37+
all {
38+
languageSettings.optIn("kotlin.RequiresOptIn")
39+
}
40+
matching { it.name.endsWith("Test") }.all {
41+
languageSettings {
42+
optIn("kotlin.RequiresOptIn")
43+
}
44+
}
45+
46+
val commonMain by sourceSets.getting {
47+
dependencies {
48+
api(libs.kotlinx.coroutines.core)
49+
api(projects.common)
50+
}
51+
}
52+
val commonTest by sourceSets.getting {
53+
dependencies {
54+
implementation(libs.kotlinx.coroutines.test)
55+
implementation(libs.diglol.encoding)
56+
implementation(kotlin("test"))
57+
}
58+
}
59+
60+
val commonJvmMain by sourceSets.creating {
61+
dependsOn(commonMain)
62+
}
63+
val commonJvmTest by sourceSets.creating {
64+
dependsOn(commonJvmMain)
65+
dependsOn(commonTest)
66+
}
67+
68+
val jvmMain by sourceSets.getting {
69+
dependsOn(commonJvmMain)
70+
}
71+
val jvmTest by sourceSets.getting {
72+
dependsOn(jvmMain)
73+
dependsOn(commonJvmTest)
74+
}
75+
76+
val androidMain by sourceSets.getting {
77+
dependsOn(commonJvmMain)
78+
}
79+
val androidUnitTest by sourceSets.getting {
80+
dependsOn(androidMain)
81+
dependsOn(commonJvmTest)
82+
}
83+
84+
val jsMain by sourceSets.getting
85+
val jsTest by sourceSets.getting
86+
87+
val darwinMain by sourceSets.creating {
88+
dependsOn(commonMain)
89+
}
90+
val darwinTest by sourceSets.creating {
91+
dependsOn(commonTest)
92+
}
93+
94+
targets.withType<KotlinNativeTarget>().all {
95+
val main by compilations.getting
96+
val test by compilations.getting
97+
98+
main.defaultSourceSet.dependsOn(
99+
when {
100+
konanTarget.family.isAppleFamily -> darwinMain
101+
else -> TODO("Not yet implemented")
102+
}
103+
)
104+
105+
test.defaultSourceSet.dependsOn(
106+
if (konanTarget.family.isAppleFamily) {
107+
darwinTest
108+
} else {
109+
commonTest
110+
}
111+
)
112+
}
113+
}
114+
}
115+
116+
android {
117+
namespace = "diglol.crypto.siphash"
118+
119+
defaultConfig {
120+
consumerProguardFiles("proguard-rules.pro")
121+
}
122+
}
123+
124+
dependencies {
125+
androidTestImplementation(libs.junit)
126+
androidTestImplementation(libs.androidx.test.runner)
127+
}
128+
129+
configure<MavenPublishBaseExtension> {
130+
configure(
131+
KotlinMultiplatform(
132+
javadocJar = JavadocJar.Dokka("dokkaGfm")
133+
)
134+
)
135+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package diglol.crypto
2+
3+
/**
4+
* HalfSipHash works with 32-bit words instead of 64-bit,
5+
* takes a 64-bit key, and returns 32-bit tag.
6+
*/
7+
object HalfSipHash {
8+
9+
fun hash(key: SipKey, data: ByteArray): Int {
10+
var m: Int
11+
val s = State(key)
12+
val iter = data.size / 4
13+
14+
for (i in 0 until iter) {
15+
m = data.toInt(i * 4)
16+
s.processBlock(m)
17+
}
18+
19+
m = lastBlock(data, iter)
20+
s.processBlock(m)
21+
s.finish()
22+
return s.digest()
23+
}
24+
25+
private fun lastBlock(data: ByteArray, iter: Int): Int {
26+
var last: Int = data.size shl 24
27+
val off = iter * 4
28+
29+
when (data.size % 4) {
30+
3 -> {
31+
last = last or (data[off + 2].toUByte().toInt() shl 16)
32+
last = last or (data[off + 1].toUByte().toInt() shl 8)
33+
last = last or data[off].toUByte().toInt()
34+
}
35+
36+
2 -> {
37+
last = last or (data[off + 1].toUByte().toInt() shl 8)
38+
last = last or data[off].toUByte().toInt()
39+
}
40+
41+
1 -> last = last or data[off].toUByte().toInt()
42+
0 -> {}
43+
}
44+
return last
45+
}
46+
47+
private class State(
48+
key: SipKey
49+
) {
50+
51+
private val k0: Int = key.leftInt()
52+
private val k1: Int = key.rightInt()
53+
54+
private var v0: Int = 0 xor k0
55+
private var v1: Int = 0 xor k1
56+
private var v2: Int = 0x6c796765 xor k0
57+
private var v3: Int = 0x74656462 xor k1
58+
59+
private fun compress() {
60+
v0 += v1
61+
v1 = rotateLeft(v1, 5)
62+
v1 = v1 xor v0
63+
v0 = rotateLeft(v0, 16)
64+
v2 += v3
65+
v3 = rotateLeft(v3, 8)
66+
v3 = v3 xor v2
67+
v0 += v3
68+
v3 = rotateLeft(v3, 7)
69+
v3 = v3 xor v0
70+
v2 += v1
71+
v1 = rotateLeft(v1, 13)
72+
v1 = v1 xor v2
73+
v2 = rotateLeft(v2, 16)
74+
}
75+
76+
private fun compressTimes(times: Int) {
77+
for (i in 0 until times) {
78+
compress()
79+
}
80+
}
81+
82+
fun processBlock(m: Int) {
83+
v3 = v3 xor m
84+
compressTimes(2)
85+
v0 = v0 xor m
86+
}
87+
88+
fun finish() {
89+
v2 = v2 xor 0xff
90+
compressTimes(4)
91+
}
92+
93+
fun digest(): Int = v1 xor v3
94+
}
95+
96+
private fun rotateLeft(value: Int, shift: Int): Int = value shl shift or (value ushr 32 - shift)
97+
}

0 commit comments

Comments
 (0)