LegacyIdentityDerivation.kt

2.4 KB · raw

1package com.peachpasswords.sync23import com.peachpasswords.vault.VaultCrypto4import org.bouncycastle.crypto.digests.SHA256Digest5import org.bouncycastle.crypto.generators.HKDFBytesGenerator6import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters7import org.bouncycastle.crypto.params.HKDFParameters8import org.bouncycastle.crypto.params.X25519PrivateKeyParameters910/**11 * Root→device key derivation, retained **for tests only** — campaign task 2.4.12 *13 * Production no longer derives any device key material from the vault root:14 * device identities are random per installation (`randomLocalIdentityCredentials`)15 * and route capabilities come from `Rendezvous.deriveMeshSecret`. Nothing in16 * `app/src/main` may reference this file; fixtures use it only to build17 * deterministic keypairs.18 */19private val LEGACY_SYNC_IDENTITY_SALT = "peach-sync-identity-v1".encodeToByteArray()20private val LEGACY_SYNC_SIGNING_SALT = "peach-sync-signing-v1".encodeToByteArray()2122internal fun deriveSyncIdentityKeypair(23    masterKey: ByteArray,24    deviceId: String,25): SyncIdentityKeypair {26    require(masterKey.isNotEmpty()) { "masterKey must not be empty" }27    require(deviceId.isNotEmpty()) { "deviceId must not be empty" }28    val seed = legacyHkdf(masterKey, LEGACY_SYNC_IDENTITY_SALT, deviceId.encodeToByteArray(), 32)29    val priv = X25519PrivateKeyParameters(seed, 0)30    return SyncIdentityKeypair(publicKey = priv.generatePublicKey().encoded, secretKey = seed)31}3233internal fun deriveSyncSigningKeypair(34    masterKey: ByteArray,35    deviceId: String,36): SyncSigningKeypair {37    require(masterKey.isNotEmpty()) { "masterKey must not be empty" }38    require(deviceId.isNotEmpty()) { "deviceId must not be empty" }39    val seed = legacyHkdf(masterKey, LEGACY_SYNC_SIGNING_SALT, deviceId.encodeToByteArray(), 32)40    return try {41        val privateKey = Ed25519PrivateKeyParameters(seed, 0)42        SyncSigningKeypair(43            privateKey = privateKey,44            publicKeyRaw = privateKey.generatePublicKey().encoded,45        )46    } finally {47        VaultCrypto.zero(seed)48    }49}5051private fun legacyHkdf(52    ikm: ByteArray,53    salt: ByteArray,54    info: ByteArray,55    length: Int,56): ByteArray {57    val generator = HKDFBytesGenerator(SHA256Digest())58    generator.init(HKDFParameters(ikm, salt, info))59    val out = ByteArray(length)60    generator.generateBytes(out, 0, length)61    return out62}