Kotlin API Reference

Complete API documentation for MetaMUI Crypto Primitives in Kotlin.

Installation

Gradle (Kotlin DSL)

dependencies {
    implementation("id.metamui:crypto:3.0.0")
}

Gradle (Groovy)

dependencies {
    implementation 'id.metamui:crypto:3.0.0'
}

Maven

<dependency>
    <groupId>id.metamui</groupId>
    <artifactId>crypto</artifactId>
    <version>3.0.0</version>
</dependency>

Quick Start

import id.metamui.crypto.*

// Generate keypair
val keypair = Ed25519.generateKeypair()

// Sign message
val message = "Hello".toByteArray()
val signature = Ed25519.sign(message, keypair.privateKey)

// Verify signature
val isValid = Ed25519.verify(signature, message, keypair.publicKey)

API Documentation

For detailed API documentation, see:

Available Modules

Hash Functions

import id.metamui.crypto.hash.*

// Simple hashing
val hash = SHA256.hash("Hello".toByteArray())
val blake3Hash = Blake3.hash("Hello".toByteArray())

// Streaming hash
val hasher = Blake3.createHasher()
hasher.update(chunk1)
hasher.update(chunk2)
val finalHash = hasher.finalize()

Digital Signatures

// Ed25519
val keypair = Ed25519.generateKeypair()
val signature = keypair.sign(message)
val isValid = keypair.verify(signature, message)

// Post-quantum signatures
val pqKeypair = Dilithium.generateKeypair()
val pqSignature = pqKeypair.sign(message)

Encryption

// ChaCha20-Poly1305
val key = ChaCha20Poly1305.generateKey()
val encrypted = ChaCha20Poly1305.encrypt(plaintext, key)
val decrypted = ChaCha20Poly1305.decrypt(
    encrypted.ciphertext,
    encrypted.nonce,
    key
)

// AES-256-GCM
val aesKey = AES256.generateKey()
val aesEncrypted = AES256.encrypt(plaintext, aesKey)

Key Exchange

// X25519 ECDH
val alice = X25519.generateKeypair()
val bob = X25519.generateKeypair()
val sharedSecret = alice.computeSharedSecret(bob.publicKey)

// Post-quantum KEM
val kemKeypair = MLKem768.generateKeypair()
val (ciphertext, sharedSecret) = MLKem768.encapsulate(kemKeypair.publicKey)
val sharedSecret2 = kemKeypair.decapsulate(ciphertext)

Key Derivation

// Password hashing
val salt = Argon2.generateSalt()
val hash = Argon2.hash(
    password = password,
    salt = salt,
    memory = 256 * 1024, // 256MB
    iterations = 4,
    parallelism = 2
)

// Key derivation
val derivedKey = HKDF.derive(
    inputKey = masterKey,
    salt = salt,
    info = "encryption key".toByteArray(),
    length = 32
)

Coroutines Support

All CPU-intensive operations support coroutines:

import kotlinx.coroutines.*

suspend fun encryptAsync(data: ByteArray): EncryptedData {
    return withContext(Dispatchers.Default) {
        val key = ChaCha20Poly1305.generateKey()
        ChaCha20Poly1305.encrypt(data, key)
    }
}

// Parallel operations
suspend fun hashMultiple(files: List<File>): List<ByteArray> {
    return coroutineScope {
        files.map { file ->
            async {
                Blake3.hashFile(file)
            }
        }.awaitAll()
    }
}

Java Interoperability

The library is fully compatible with Java:

import id.metamui.crypto.Ed25519;
import id.metamui.crypto.KeyPair;

// Java usage
KeyPair keypair = Ed25519.generateKeypair();
byte[] signature = Ed25519.sign(message, keypair.getPrivateKey());
boolean isValid = Ed25519.verify(signature, message, keypair.getPublicKey());

Android Integration

Android Keystore

import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import java.security.KeyStore

class AndroidSecureStorage(private val context: Context) {
    private val keyAlias = "MetaMUIKey"
    
    init {
        generateKey()
    }
    
    private fun generateKey() {
        val keyGenerator = KeyGenerator.getInstance(
            KeyProperties.KEY_ALGORITHM_AES,
            "AndroidKeyStore"
        )
        
        val spec = KeyGenParameterSpec.Builder(
            keyAlias,
            KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
        )
            .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
            .build()
            
        keyGenerator.init(spec)
        keyGenerator.generateKey()
    }
    
    suspend fun encryptData(data: ByteArray): ByteArray {
        return withContext(Dispatchers.IO) {
            // Use MetaMUI crypto with Android Keystore key
            val key = getKeyFromKeystore()
            AES256.encrypt(data, key).ciphertext
        }
    }
}

Biometric Authentication

import androidx.biometric.BiometricPrompt

class BiometricCrypto(private val activity: FragmentActivity) {
    private lateinit var keypair: Ed25519.Keypair
    
    fun authenticateAndSign(
        message: ByteArray,
        onSuccess: (ByteArray) -> Unit
    ) {
        val executor = ContextCompat.getMainExecutor(activity)
        val biometricPrompt = BiometricPrompt(
            activity,
            executor,
            object : BiometricPrompt.AuthenticationCallback() {
                override fun onAuthenticationSucceeded(
                    result: BiometricPrompt.AuthenticationResult
                ) {
                    val signature = Ed25519.sign(message, keypair.privateKey)
                    onSuccess(signature)
                }
            }
        )
        
        val promptInfo = BiometricPrompt.PromptInfo.Builder()
            .setTitle("Sign Document")
            .setSubtitle("Authenticate to sign")
            .setNegativeButtonText("Cancel")
            .build()
            
        biometricPrompt.authenticate(promptInfo)
    }
}

Error Handling

sealed class CryptoError : Exception() {
    object InvalidKey : CryptoError()
    object DecryptionFailed : CryptoError()
    object VerificationFailed : CryptoError()
    data class Unknown(override val message: String) : CryptoError()
}

fun safeDecrypt(ciphertext: ByteArray, key: ByteArray): Result<ByteArray> {
    return try {
        val decrypted = ChaCha20Poly1305.decrypt(ciphertext, nonce, key)
        Result.success(decrypted)
    } catch (e: Exception) {
        when (e) {
            is InvalidKeyException -> Result.failure(CryptoError.InvalidKey)
            is DecryptionException -> Result.failure(CryptoError.DecryptionFailed)
            else -> Result.failure(CryptoError.Unknown(e.message ?: "Unknown error"))
        }
    }
}

Extension Functions

// Convenient extension functions
fun String.sha256(): ByteArray = SHA256.hash(this.toByteArray())
fun String.blake3(): ByteArray = Blake3.hash(this.toByteArray())

fun ByteArray.toHex(): String = joinToString("") { "%02x".format(it) }
fun String.fromHex(): ByteArray = chunked(2).map { it.toInt(16).toByte() }.toByteArray()

// Secure random
fun secureRandom(size: Int): ByteArray = SecureRandom.generate(size)

Flow Integration

import kotlinx.coroutines.flow.*

class CryptoStream {
    fun hashFileStream(file: File): Flow<HashProgress> = flow {
        val hasher = Blake3.createHasher()
        var processed = 0L
        val totalSize = file.length()
        
        file.inputStream().use { input ->
            val buffer = ByteArray(8192)
            while (true) {
                val read = input.read(buffer)
                if (read == -1) break
                
                hasher.update(buffer, 0, read)
                processed += read
                
                emit(HashProgress(
                    bytesProcessed = processed,
                    totalBytes = totalSize,
                    percentage = (processed * 100 / totalSize).toInt()
                ))
            }
        }
        
        emit(HashProgress(
            bytesProcessed = totalSize,
            totalBytes = totalSize,
            percentage = 100,
            hash = hasher.finalize()
        ))
    }
}

data class HashProgress(
    val bytesProcessed: Long,
    val totalBytes: Long,
    val percentage: Int,
    val hash: ByteArray? = null
)

Examples

Secure Preferences

class SecurePreferences(
    context: Context,
    password: String
) {
    private val prefs = context.getSharedPreferences("secure_prefs", Context.MODE_PRIVATE)
    private val key: ByteArray
    
    init {
        val salt = prefs.getString("salt", null)?.fromHex() ?: run {
            val newSalt = Argon2.generateSalt()
            prefs.edit().putString("salt", newSalt.toHex()).apply()
            newSalt
        }
        
        key = Argon2.deriveKey(password, salt, 32)
    }
    
    fun putString(key: String, value: String) {
        val encrypted = AES256.encrypt(value.toByteArray(), this.key)
        prefs.edit()
            .putString(key, encrypted.ciphertext.toHex())
            .putString("${key}_nonce", encrypted.nonce.toHex())
            .apply()
    }
    
    fun getString(key: String): String? {
        val ciphertext = prefs.getString(key, null)?.fromHex() ?: return null
        val nonce = prefs.getString("${key}_nonce", null)?.fromHex() ?: return null
        
        return try {
            val decrypted = AES256.decrypt(ciphertext, nonce, this.key)
            String(decrypted)
        } catch (e: Exception) {
            null
        }
    }
}

Retrofit Integration

import retrofit2.Retrofit
import okhttp3.Interceptor

class CryptoInterceptor(
    private val signingKey: Ed25519.Keypair
) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        val body = request.body?.let { requestBody ->
            val buffer = Buffer()
            requestBody.writeTo(buffer)
            buffer.readByteArray()
        }
        
        val signedRequest = if (body != null) {
            val signature = Ed25519.sign(body, signingKey.privateKey)
            request.newBuilder()
                .header("X-Signature", signature.toHex())
                .header("X-Public-Key", signingKey.publicKey.toHex())
                .build()
        } else {
            request
        }
        
        return chain.proceed(signedRequest)
    }
}

// Usage
val client = OkHttpClient.Builder()
    .addInterceptor(CryptoInterceptor(keypair))
    .build()

val retrofit = Retrofit.Builder()
    .client(client)
    .baseUrl("https://api.example.com")
    .build()

See Also