Skip to content
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
32 changes: 32 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,38 @@ env:
JAVA_VERSION: 17

jobs:
compile-check:
name: Compile check (no creds required)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1

- name: Setup Java env
uses: actions/setup-java@v3
with:
java-version: '21'
distribution: 'corretto'
cache: 'gradle'

- name: Gradle build cache
uses: actions/cache@v4
with:
path: |
~/.gradle/caches/build-cache-1
.gradle/configuration-cache
key: gradle-build-cache-${{ hashFiles('**/*.gradle.kts', 'gradle.properties') }}
restore-keys: |
gradle-build-cache-

# compileDebugSources runs all Kotlin/Java compilation tasks but stops before
# processDebugGoogleServices (which needs secrets). This catches KMP interop
# regressions (missing @JvmStatic, internal visibility leaking across modules)
# that only surface when consumers try to compile against the shared library.
- name: Compile app sources
run: ./gradlew :apps:flipcash:app:compileDebugSources --continue --no-daemon

flipcash-tests:
name: Run Flipcash Tests
runs-on: ubuntu-latest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ public MnemonicCode(InputStream wordstream, String wordListDigest) throws IOExce
if (wordstream == null) return;
BufferedReader br = new BufferedReader(new InputStreamReader(wordstream, StandardCharsets.UTF_8));
this.wordList = new ArrayList<>(2048);
MessageDigest md = Sha256Hash.newDigest();
MessageDigest md;
try { md = MessageDigest.getInstance("SHA-256"); }
catch (java.security.NoSuchAlgorithmException e) { throw new RuntimeException(e); }
String word;
while ((word = br.readLine()) != null) {
md.update(word.getBytes());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.getcode.crypt

import kotlin.jvm.JvmStatic
import org.kotlincrypto.hash.sha2.SHA256

/*
Expand All @@ -24,7 +25,7 @@ import org.kotlincrypto.hash.sha2.SHA256
* making it safe to use as a map key. Provides factory methods for computing
* single and double SHA-256 hashes.
*/
class Sha256Hash private constructor(internal val bytes: ByteArray) : Comparable<Sha256Hash> {
class Sha256Hash private constructor(val bytes: ByteArray) : Comparable<Sha256Hash> {

companion object {
/** The byte length of a SHA-256 hash output. */
Expand All @@ -34,6 +35,7 @@ class Sha256Hash private constructor(internal val bytes: ByteArray) : Comparable
val ZERO_HASH: Sha256Hash = wrap(ByteArray(LENGTH))

/** Creates a new instance that wraps the given raw hash bytes (must be exactly 32 bytes). */
@JvmStatic
fun wrap(rawHashBytes: ByteArray): Sha256Hash {
require(rawHashBytes.size == LENGTH) {
"Expected $LENGTH bytes but got ${rawHashBytes.size}"
Expand All @@ -45,44 +47,53 @@ class Sha256Hash private constructor(internal val bytes: ByteArray) : Comparable
* Creates a new instance that wraps the given hex-encoded hash value
* (must represent exactly 32 bytes, i.e., 64 hex characters).
*/
@JvmStatic
fun wrap(hexString: String): Sha256Hash = wrap(decodeHex(hexString))

/** Creates a new instance wrapping the given bytes with their order reversed. */
@JvmStatic
fun wrapReversed(rawHashBytes: ByteArray): Sha256Hash = wrap(reverseBytes(rawHashBytes))

/** Computes a single SHA-256 hash of [contents] and wraps the result. */
@JvmStatic
fun of(contents: ByteArray): Sha256Hash = wrap(hash(contents))

/**
* Computes a double SHA-256 hash (SHA-256(SHA-256(contents))) of [contents]
* and wraps the result.
*/
@JvmStatic
fun twiceOf(contents: ByteArray): Sha256Hash = wrap(hashTwice(contents))

/**
* Computes a double SHA-256 hash over the concatenation of [content1] and [content2]
* and wraps the result.
*/
@JvmStatic
fun twiceOf(content1: ByteArray, content2: ByteArray): Sha256Hash =
wrap(hashTwice(content1, content2))

/** Calculates the SHA-256 hash of [input]. */
@JvmStatic
fun hash(input: ByteArray): ByteArray = hash(input, 0, input.size)

/** Calculates the SHA-256 hash of [length] bytes from [input] starting at [offset]. */
@JvmStatic
fun hash(input: ByteArray, offset: Int, length: Int): ByteArray {
val digest = SHA256()
digest.update(input, offset, length)
return digest.digest()
}

/** Calculates SHA-256(SHA-256([input])). */
@JvmStatic
fun hashTwice(input: ByteArray): ByteArray = hashTwice(input, 0, input.size)

/**
* Calculates SHA-256(SHA-256([input1] || [input2])), equivalent to concatenating
* the two arrays and calling [hashTwice].
*/
@JvmStatic
fun hashTwice(input1: ByteArray, input2: ByteArray): ByteArray {
val first = SHA256().run {
update(input1)
Expand All @@ -93,6 +104,7 @@ class Sha256Hash private constructor(internal val bytes: ByteArray) : Comparable
}

/** Calculates SHA-256(SHA-256([length] bytes of [input] starting at [offset])). */
@JvmStatic
fun hashTwice(input: ByteArray, offset: Int, length: Int): ByteArray {
val first = SHA256().also { it.update(input, offset, length) }.digest()
return SHA256().also { it.update(first) }.digest()
Expand All @@ -101,6 +113,7 @@ class Sha256Hash private constructor(internal val bytes: ByteArray) : Comparable
/**
* Calculates SHA-256(SHA-256([input1][offset1]..[length1] || [input2][offset2]..[length2])).
*/
@JvmStatic
fun hashTwice(
input1: ByteArray, offset1: Int, length1: Int,
input2: ByteArray, offset2: Int, length2: Int,
Expand Down Expand Up @@ -133,9 +146,6 @@ class Sha256Hash private constructor(internal val bytes: ByteArray) : Comparable
}
}

/** Returns the raw hash bytes. Do NOT modify the returned array. */
fun getBytes(): ByteArray = bytes

/** Returns a reversed copy of the internal byte array. */
fun getReversedBytes(): ByteArray {
val buf = ByteArray(bytes.size)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.getcode.crypt

import kotlin.jvm.JvmStatic
import org.kotlincrypto.macs.hmac.sha2.HmacSHA512

/*
Expand Down Expand Up @@ -36,6 +37,7 @@ object PBKDF2SHA512 {
* Derives a key of [dkLen] bytes from password [P] and salt [S] using [c]
* PBKDF2 iterations with HMAC-SHA-512 as the pseudorandom function.
*/
@JvmStatic
fun derive(P: String, S: String, c: Int, dkLen: Int): ByteArray {
require(dkLen > 0) { "dkLen must be positive" }
require(dkLen.toLong() <= (0xFFFFFFFFL) * H_LEN) { "derived key too long" }
Expand Down
Loading