Add lyng.legacy_digest module with LegacyDigest.sha1()

Provides a pure Kotlin/KMP SHA-1 implementation with no extra dependencies,
exposed as `LegacyDigest.sha1(data)` in the `lyng.legacy_digest` package.

The naming deliberately signals that SHA-1 is cryptographically broken:
the object name `LegacyDigest` and prominent doc-comment warnings steer
users away from security-sensitive use while still enabling interoperability
with legacy protocols and file formats that require SHA-1.

API:
  import lyng.legacy_digest
  val hex: String = LegacyDigest.sha1("some string")   // UTF-8 input
  val hex: String = LegacyDigest.sha1(someBuffer)       // raw-byte input

Tests cover FIPS 180-4 known-answer vectors (empty, "abc", long message,
quick-brown-fox) at both the Kotlin and Lyng integration levels.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
sergeychandClaude Sonnet 4.6 committed 2026-04-07 19:51:04 +03:00
1 parent f145a90845
commit 69013392d3
4 files changed
+284

No files matched your search

@@ -0,0 +1,113 @@
/*
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals
class LegacyDigestTest {
// --- Kotlin-level unit tests for the SHA-1 implementation ---
@Test
fun sha1KotlinEmptyString() {
// SHA-1("") = da39a3ee5e6b4b0d3255bfef95601890afd80709
assertEquals(
"da39a3ee5e6b4b0d3255bfef95601890afd80709",
LegacyDigest.sha1Hex(ByteArray(0))
)
}
@Test
fun sha1KotlinAbc() {
// SHA-1("abc") = a9993e364706816aba3e25717850c26c9cd0d89d
assertEquals(
"a9993e364706816aba3e25717850c26c9cd0d89d",
LegacyDigest.sha1Hex("abc".encodeToByteArray())
)
}
@Test
fun sha1KotlinLongerMessage() {
// SHA-1("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")
// = 84983e441c3bd26ebaae4aa1f95129e5e54670f1
val msg = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
assertEquals(
"84983e441c3bd26ebaae4aa1f95129e5e54670f1",
LegacyDigest.sha1Hex(msg.encodeToByteArray())
)
}
@Test
fun sha1KotlinExactlyOneBlock() {
// "The quick brown fox jumps over the lazy dog"
// SHA-1 = 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12
val msg = "The quick brown fox jumps over the lazy dog"
assertEquals(
"2fd4e1c67a2d28fced849ee1bb76e7391b93eb12",
LegacyDigest.sha1Hex(msg.encodeToByteArray())
)
}
// --- Lyng-level integration tests ---
@Test
fun sha1LyngStringInput() = runTest {
eval(
"""
import lyng.legacy_digest
assertEquals(
"a9993e364706816aba3e25717850c26c9cd0d89d",
LegacyDigest.sha1("abc")
)
assertEquals(
"da39a3ee5e6b4b0d3255bfef95601890afd80709",
LegacyDigest.sha1("")
)
""".trimIndent()
)
}
@Test
fun sha1LyngBufferInput() = runTest {
eval(
"""
import lyng.legacy_digest
import lyng.buffer
val buf = Buffer.decodeHex("616263") // "abc" in hex
assertEquals(
"a9993e364706816aba3e25717850c26c9cd0d89d",
LegacyDigest.sha1(buf)
)
""".trimIndent()
)
}
@Test
fun sha1LyngReturnType() = runTest {
eval(
"""
import lyng.legacy_digest
val h = LegacyDigest.sha1("hello")
assert(h is String)
assertEquals(40, h.length)
""".trimIndent()
)
}
}