Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add (half)SipHash implementation #108

Merged
merged 4 commits into from
Jan 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## [Unreleased]

### Added

* Siphash implementation
* Halfsiphash implementation

## [0.1.5] - 2023-08-16

### Changed
Expand Down
32 changes: 32 additions & 0 deletions README-zh.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Diglol Crypto

[![badge-license]][url-license]
[![badge-latest-release]][url-latest-release]

![badge-platform-android]
![badge-platform-jvm]
![badge-platform-js]
![badge-platform-linux]
![badge-platform-ios]
![badge-platform-tvos]
![badge-platform-watchos]
![badge-platform-windows]

Diglol Crypto for Kotlin Multiplatform.

当前支持:
Expand Down Expand Up @@ -33,6 +45,9 @@ Diglol Crypto for Kotlin Multiplatform.
- otp
- [HOTP][hotp]
- [TOTP][totp]
- [siphash][siphash]
- siphash
- halfsiphash

### 发布

Expand Down Expand Up @@ -82,3 +97,20 @@ implementation("com.diglol.crypto:${submodule}:0.1.5")
[encryptthenmac]: https://datatracker.ietf.org/doc/html/draft-mcgrew-aead-aes-cbc-hmac-sha2-05
[hotp]: https://datatracker.ietf.org/doc/html/rfc4226
[totp]: https://datatracker.ietf.org/doc/html/rfc6238
[siphash]: https://datatracker.ietf.org/doc/rfc9231/

<!-- TAG_VERSION -->
[badge-latest-release]: https://img.shields.io/badge/latest--release-0.1.5-blue.svg?style=flat
[badge-license]: https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat
[url-latest-release]: https://github.com/diglol/crypto/releases/latest
[url-license]: https://www.apache.org/licenses/LICENSE-2.0.txt

<!-- TAG_PLATFORMS -->
[badge-platform-android]: http://img.shields.io/badge/-android-6EDB8D.svg?style=flat
[badge-platform-jvm]: http://img.shields.io/badge/-jvm-DB413D.svg?style=flat
[badge-platform-js]: http://img.shields.io/badge/-js-F8DB5D.svg?style=flat
[badge-platform-linux]: http://img.shields.io/badge/-linux-2D3F6C.svg?style=flat
[badge-platform-tvos]: http://img.shields.io/badge/-tvos-808080.svg?style=flat
[badge-platform-ios]: http://img.shields.io/badge/-ios-808080.svg?style=flat
[badge-platform-watchos]: http://img.shields.io/badge/-watchos-C0C0C0.svg?style=flat
[badge-platform-windows]: http://img.shields.io/badge/-windows-4D76CD.svg?style=flat
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Diglol Crypto

[![badge-license]][url-license]
[![badge-latest-release]][url-latest-release]

![badge-platform-android]
![badge-platform-jvm]
![badge-platform-js]
![badge-platform-linux]
![badge-platform-ios]
![badge-platform-tvos]
![badge-platform-watchos]
![badge-platform-windows]

Diglol Crypto for Kotlin Multiplatform.

Currently supported:
Expand Down Expand Up @@ -33,6 +45,9 @@ Currently supported:
- otp
- [HOTP][hotp]
- [TOTP][totp]
- [siphash][siphash]
- siphash
- halfsiphash

### Releases

Expand Down Expand Up @@ -82,3 +97,20 @@ implementation("com.diglol.crypto:${submodule}:0.1.5")
[encryptthenmac]: https://datatracker.ietf.org/doc/html/draft-mcgrew-aead-aes-cbc-hmac-sha2-05
[hotp]: https://datatracker.ietf.org/doc/html/rfc4226
[totp]: https://datatracker.ietf.org/doc/html/rfc6238
[siphash]: https://datatracker.ietf.org/doc/rfc9231/

<!-- TAG_VERSION -->
[badge-latest-release]: https://img.shields.io/badge/latest--release-0.1.5-blue.svg?style=flat
[badge-license]: https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat
[url-latest-release]: https://github.com/diglol/crypto/releases/latest
[url-license]: https://www.apache.org/licenses/LICENSE-2.0.txt

<!-- TAG_PLATFORMS -->
[badge-platform-android]: http://img.shields.io/badge/-android-6EDB8D.svg?style=flat
[badge-platform-jvm]: http://img.shields.io/badge/-jvm-DB413D.svg?style=flat
[badge-platform-js]: http://img.shields.io/badge/-js-F8DB5D.svg?style=flat
[badge-platform-linux]: http://img.shields.io/badge/-linux-2D3F6C.svg?style=flat
[badge-platform-tvos]: http://img.shields.io/badge/-tvos-808080.svg?style=flat
[badge-platform-ios]: http://img.shields.io/badge/-ios-808080.svg?style=flat
[badge-platform-watchos]: http://img.shields.io/badge/-watchos-C0C0C0.svg?style=flat
[badge-platform-windows]: http://img.shields.io/badge/-windows-4D76CD.svg?style=flat
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ if (!System.getProperty("os.name").startsWith("Windows")) {
}
include(":cipher")
include(":otp")
include(":siphash")

enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
135 changes: 135 additions & 0 deletions siphash/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import com.vanniktech.maven.publish.JavadocJar
import com.vanniktech.maven.publish.KotlinMultiplatform
import com.vanniktech.maven.publish.MavenPublishBaseExtension
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget

plugins {
kotlin("multiplatform")
id("com.android.library")
id("org.jetbrains.dokka")
id("com.vanniktech.maven.publish.base")
}

kotlin {
androidTarget {
publishLibraryVariants("release")
}
jvm()
js(IR) {
browser()
nodejs()
}

macosX64()
macosArm64()
iosX64()
iosArm64()
iosSimulatorArm64()
watchosArm32()
watchosArm64()
watchosSimulatorArm64()
watchosX64()
tvosArm64()
tvosSimulatorArm64()
tvosX64()

sourceSets {
all {
languageSettings.optIn("kotlin.RequiresOptIn")
}
matching { it.name.endsWith("Test") }.all {
languageSettings {
optIn("kotlin.RequiresOptIn")
}
}

val commonMain by sourceSets.getting {
dependencies {
api(libs.kotlinx.coroutines.core)
api(projects.common)
}
}
val commonTest by sourceSets.getting {
dependencies {
implementation(libs.kotlinx.coroutines.test)
implementation(libs.diglol.encoding)
implementation(kotlin("test"))
}
}

val commonJvmMain by sourceSets.creating {
dependsOn(commonMain)
}
val commonJvmTest by sourceSets.creating {
dependsOn(commonJvmMain)
dependsOn(commonTest)
}

val jvmMain by sourceSets.getting {
dependsOn(commonJvmMain)
}
val jvmTest by sourceSets.getting {
dependsOn(jvmMain)
dependsOn(commonJvmTest)
}

val androidMain by sourceSets.getting {
dependsOn(commonJvmMain)
}
val androidUnitTest by sourceSets.getting {
dependsOn(androidMain)
dependsOn(commonJvmTest)
}

val jsMain by sourceSets.getting
val jsTest by sourceSets.getting

val darwinMain by sourceSets.creating {
dependsOn(commonMain)
}
val darwinTest by sourceSets.creating {
dependsOn(commonTest)
}

targets.withType<KotlinNativeTarget>().all {
val main by compilations.getting
val test by compilations.getting

main.defaultSourceSet.dependsOn(
when {
konanTarget.family.isAppleFamily -> darwinMain
else -> TODO("Not yet implemented")
}
)

test.defaultSourceSet.dependsOn(
if (konanTarget.family.isAppleFamily) {
darwinTest
} else {
commonTest
}
)
}
}
}

android {
namespace = "diglol.crypto.siphash"

defaultConfig {
consumerProguardFiles("proguard-rules.pro")
}
}

dependencies {
androidTestImplementation(libs.junit)
androidTestImplementation(libs.androidx.test.runner)
}

configure<MavenPublishBaseExtension> {
configure(
KotlinMultiplatform(
javadocJar = JavadocJar.Dokka("dokkaGfm")
)
)
}
97 changes: 97 additions & 0 deletions siphash/src/commonMain/kotlin/diglol/crypto/HalfSipHash.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package diglol.crypto

/**
* HalfSipHash works with 32-bit words instead of 64-bit,
* takes a 64-bit key, and returns 32-bit tag.
*/
object HalfSipHash {

fun hash(key: SipKey, data: ByteArray): Int {
var m: Int
val s = State(key)
val iter = data.size / 4

for (i in 0 until iter) {
m = data.toInt(i * 4)
s.processBlock(m)
}

m = lastBlock(data, iter)
s.processBlock(m)
s.finish()
return s.digest()
}

private fun lastBlock(data: ByteArray, iter: Int): Int {
var last: Int = data.size shl 24
val off = iter * 4

when (data.size % 4) {
3 -> {
last = last or (data[off + 2].toUByte().toInt() shl 16)
last = last or (data[off + 1].toUByte().toInt() shl 8)
last = last or data[off].toUByte().toInt()
}

2 -> {
last = last or (data[off + 1].toUByte().toInt() shl 8)
last = last or data[off].toUByte().toInt()
}

1 -> last = last or data[off].toUByte().toInt()
0 -> {}
}
return last
}

private class State(
key: SipKey
) {

private val k0: Int = key.leftInt()
private val k1: Int = key.rightInt()

private var v0: Int = 0 xor k0
private var v1: Int = 0 xor k1
private var v2: Int = 0x6c796765 xor k0
private var v3: Int = 0x74656462 xor k1

private fun compress() {
v0 += v1
v1 = rotateLeft(v1, 5)
v1 = v1 xor v0
v0 = rotateLeft(v0, 16)
v2 += v3
v3 = rotateLeft(v3, 8)
v3 = v3 xor v2
v0 += v3
v3 = rotateLeft(v3, 7)
v3 = v3 xor v0
v2 += v1
v1 = rotateLeft(v1, 13)
v1 = v1 xor v2
v2 = rotateLeft(v2, 16)
}

private fun compressTimes(times: Int) {
for (i in 0 until times) {
compress()
}
}

fun processBlock(m: Int) {
v3 = v3 xor m
compressTimes(2)
v0 = v0 xor m
}

fun finish() {
v2 = v2 xor 0xff
compressTimes(4)
}

fun digest(): Int = v1 xor v3
}

private fun rotateLeft(value: Int, shift: Int): Int = value shl shift or (value ushr 32 - shift)
}
Loading