Sign
Signing produces a threshold signature over a message hash using the device's keyshare and the server's keyshare. The full private key is never reconstructed — and each party must contribute its share to the MPC protocol in order to produce valid signature.
For the SDK contract see Sign (Kotlin) and Sign (Swift).
How the example does it
- Android
- iOS / macOS
suspend fun sign(keyId: String, message: ByteArray, derivationPath: String): ByteArray {
val dao = readDao(keyId)
val keyshare = dao.currentKeyshare
?: throw Exception("No active keyshare found for keyId: $keyId")
val signature = sessionFor(dao.keyType)
.signature(keyshare = keyshare, message = message.toHex(), derivationPath = derivationPath)
.getOrThrow()
return signature
}
func sign(keyId: String, message: Data, derivationPath: String) async throws -> Data {
let record = try loadRecord(keyId: keyId)
let keyshare = try requireActiveKeyshare(record)
let messageHex = message.map { String(format: "%02x", $0) }.joined()
let sig = try await sessionForKeyType(record.keyType)
.signature(keyshare: keyshare, message: messageHex, chainPath: derivationPath).get()
return sig
}
The example loads the keyshare from encrypted storage via readDao(keyId) / loadRecord(keyId:), which returns the storage client's VaultReconcileStoreDao / VaultReconcileRecord wrapper. The keyType on that wrapper is how the example knows which session (ECDSA or EdDSA) to dispatch to.
The message parameter is the transaction hash (hex-encoded). The derivationPath is "m" in this example (the root BIP-32 path). For child key derivation see BIP-32.