Adding _sign_ native implementation and started writing tests

This commit is contained in:
Ugljesa Jovanovic 2020-09-14 18:51:25 +02:00
parent 1936e748ae
commit b0ce2e10fc
No known key found for this signature in database
GPG Key ID: 33A5F353387711A5
3 changed files with 315 additions and 7 deletions

View File

@ -5,12 +5,13 @@ package com.ionspin.kotlin.crypto.signature
*/
expect class SignatureState
data class SignKeyPair(val publicKey: UByteArray, val secretKey: UByteArray)
data class SignatureKeyPair(val publicKey: UByteArray, val secretKey: UByteArray)
const val crypto_sign_BYTES = 64
const val crypto_sign_SEEDBYTES = 32
const val crypto_sign_PUBLICKEYBYTES = 32
const val crypto_sign_SECRETKEY2BYTES = 64
const val crypto_sign_SECRETKEYBYTES = 64
const val crypto_scalarmult_curve25519_BYTES = 32
class InvalidSignatureException() : RuntimeException("Signature validation failed")
@ -24,14 +25,14 @@ expect object Signature {
* The crypto_sign_keypair() function randomly generates a secret key and a corresponding public key.
* The public key is put into pk (crypto_sign_PUBLICKEYBYTES bytes) and the secret key into sk (crypto_sign_SECRETKEYBYTES bytes).
*/
fun keypair(): SignKeyPair
fun keypair(): SignatureKeyPair
/**
* The crypto_sign_keypair() function randomly generates a secret key and a corresponding public key.
* The public key is put into pk (crypto_sign_PUBLICKEYBYTES bytes) and the secret key into sk (crypto_sign_SECRETKEYBYTES bytes).
* Using crypto_sign_seed_keypair(), the key pair can also be deterministically derived from a single key seed (crypto_sign_SEEDBYTES bytes).
*/
fun seedKeypair(seed: UByteArray): SignKeyPair
fun seedKeypair(seed: UByteArray): SignatureKeyPair
/**
* The crypto_sign() function prepends a signature to a message m whose length is mlen bytes, using the secret key sk.
@ -56,9 +57,16 @@ expect object Signature {
* The crypto_sign_verify_detached() function verifies that sig is a valid signature for the message m whose length
* is mlen bytes, using the signer's public key pk.
*/
fun verifyDetached(signature: UByteArray, message: UByteArray, publicKey: UByteArray): Boolean
fun ed25519PkToCurve25519()
fun ed25519SkToCurve25519()
fun verifyDetached(signature: UByteArray, message: UByteArray, publicKey: UByteArray)
/**
* The crypto_sign_ed25519_pk_to_curve25519() function converts an Ed25519 public key ed25519_pk to an X25519 public key and stores it into x25519_pk.
*/
fun ed25519PkToCurve25519(ed25519PublicKey: UByteArray) : UByteArray
/**
* The crypto_sign_ed25519_sk_to_curve25519() function converts an Ed25519 secret key ed25519_sk to an X25519 secret key and stores it into x25519_sk.
*/
fun ed25519SkToCurve25519(ed25519SecretKey: UByteArray) : UByteArray
/**
* The secret key actually includes the seed (either a random seed or the one given to crypto_sign_seed_keypair()) as well as the public key.

View File

@ -0,0 +1,32 @@
package com.ionspin.kotlin.crypto.signature
import com.ionspin.kotlin.crypto.LibsodiumInitializer
import com.ionspin.kotlin.crypto.util.encodeToUByteArray
import kotlin.test.Test
import kotlin.test.assertFailsWith
import kotlin.test.assertTrue
/**
* Created by Ugljesa Jovanovic (jovanovic.ugljesa@gmail.com) on 14/Sep/2020
*/
class SignatureTest {
@Test
fun testSignAndVerify() {
LibsodiumInitializer.initializeWithCallback {
val keys = Signature.keypair()
val message = "Some text that will be signed".encodeToUByteArray()
val signedMessage = Signature.sign(message, keys.secretKey)
val verifiedMessage = Signature.open(signedMessage, keys.publicKey)
assertTrue {
verifiedMessage.contentEquals(message)
}
assertFailsWith(InvalidSignatureException::class) {
val tamperedMessage = signedMessage.copyOf()
tamperedMessage[crypto_sign_BYTES + 1] = 0U
Signature.open(tamperedMessage, keys.publicKey)
}
}
}
}

View File

@ -0,0 +1,268 @@
package com.ionspin.kotlin.crypto.signature
import com.ionspin.kotlin.crypto.util.toPtr
import kotlinx.cinterop.*
import libsodium.*
import platform.posix.malloc
actual typealias SignatureState = crypto_sign_ed25519ph_state
actual object Signature {
actual fun init(): SignatureState {
val stateAllocated = malloc(SignatureState.size.convert())
val statePointed = stateAllocated!!.reinterpret<SignatureState>().pointed
crypto_sign_init(statePointed.ptr)
return statePointed
}
actual fun update(state: SignatureState, data: UByteArray) {
val dataPinned = data.pin()
crypto_sign_update(state.ptr, dataPinned.toPtr(), data.size.convert())
dataPinned.unpin()
}
actual fun finalCreate(
state: SignatureState,
secretKey: UByteArray
): UByteArray {
val signature = UByteArray(crypto_sign_BYTES)
val secretKeyPinned = secretKey.pin()
val signaturePinned = signature.pin()
crypto_sign_final_create(
state.ptr,
signaturePinned.toPtr(),
null,
secretKeyPinned.toPtr()
)
secretKeyPinned.unpin()
signaturePinned.unpin()
return signature
}
actual fun finalVerify(
state: SignatureState,
signature: UByteArray,
publicKey: UByteArray
) {
val signaturePinned = signature.pin()
val publicKeyPinned = publicKey.pin()
val verificationResult = crypto_sign_final_verify(
state.ptr,
signaturePinned.toPtr(),
publicKeyPinned.toPtr()
)
if (verificationResult == -1) {
throw InvalidSignatureException()
}
}
/**
* The crypto_sign_keypair() function randomly generates a secret key and a corresponding public key.
* The public key is put into pk (crypto_sign_PUBLICKEYBYTES bytes) and the secret key into sk (crypto_sign_SECRETKEYBYTES bytes).
*/
actual fun keypair(): SignatureKeyPair {
val publicKey = UByteArray(crypto_sign_PUBLICKEYBYTES)
val secretKey = UByteArray(crypto_sign_SECRETKEYBYTES)
val publicKeyPinned = publicKey.pin()
val secretKeyPinned = secretKey.pin()
crypto_sign_keypair(
publicKeyPinned.toPtr(),
secretKeyPinned.toPtr(),
)
publicKeyPinned.unpin()
secretKeyPinned.unpin()
return SignatureKeyPair(publicKey, secretKey)
}
/**
* The crypto_sign_keypair() function randomly generates a secret key and a corresponding public key.
* The public key is put into pk (crypto_sign_PUBLICKEYBYTES bytes) and the secret key into sk (crypto_sign_SECRETKEYBYTES bytes).
* Using crypto_sign_seed_keypair(), the key pair can also be deterministically derived from a single key seed (crypto_sign_SEEDBYTES bytes).
*/
actual fun seedKeypair(seed: UByteArray): SignatureKeyPair {
val seedPinned = seed.pin()
val publicKey = UByteArray(crypto_sign_PUBLICKEYBYTES)
val secretKey = UByteArray(crypto_sign_SECRETKEYBYTES)
val publicKeyPinned = publicKey.pin()
val secretKeyPinned = secretKey.pin()
crypto_sign_seed_keypair(
publicKeyPinned.toPtr(),
secretKeyPinned.toPtr(),
seedPinned.toPtr()
)
seedPinned.unpin()
publicKeyPinned.unpin()
secretKeyPinned.unpin()
return SignatureKeyPair(publicKey, secretKey)
}
/**
* The crypto_sign() function prepends a signature to a message m whose length is mlen bytes, using the secret key sk.
* The signed message, which includes the signature + a plain copy of the message, is put into sm, and is crypto_sign_BYTES + mlen bytes long.
*/
actual fun sign(message: UByteArray, secretKey: UByteArray): UByteArray {
val signedMessage = UByteArray(message.size + crypto_sign_BYTES)
val signedMessagePinned = signedMessage.pin()
val messagePinned = message.pin()
val secretKeyPinned = secretKey.pin()
crypto_sign(
signedMessagePinned.toPtr(),
null,
messagePinned.toPtr(),
message.size.convert(),
secretKeyPinned.toPtr()
)
signedMessagePinned.unpin()
messagePinned.unpin()
secretKeyPinned.unpin()
return signedMessage
}
/**
* The crypto_sign_open() function checks that the signed message sm whose length is smlen bytes has a valid signature for the public key pk.
* If the signature is doesn't appear to be valid, the function throws an exception
*/
actual fun open(signedMessage: UByteArray, publicKey: UByteArray): UByteArray {
val message = UByteArray(signedMessage.size - crypto_sign_BYTES)
val messagePinned = message.pin()
val signedMessagePinned = signedMessage.pin()
val publicKeyPinned = publicKey.pin()
val verificationResult = crypto_sign_open(
messagePinned.toPtr(),
null,
signedMessagePinned.toPtr(),
signedMessage.size.convert(),
publicKeyPinned.toPtr()
)
if (verificationResult == -1) {
throw InvalidSignatureException()
}
return message
}
/**
* In detached mode, the signature is stored without attaching a copy of the original message to it.
* The crypto_sign_detached() function signs the message m whose length is mlen bytes, using the secret key sk,
* and puts the signature into sig, which can be up to crypto_sign_BYTES bytes long.
*/
actual fun detached(message: UByteArray, secretKey: UByteArray): UByteArray {
val signature = UByteArray(crypto_sign_BYTES)
val signaturePinned = signature.pin()
val messagePinned = message.pin()
val secretKeyPinned = secretKey.pin()
crypto_sign_detached(
signaturePinned.toPtr(),
null,
messagePinned.toPtr(),
message.size.convert(),
secretKeyPinned.toPtr()
)
signaturePinned.unpin()
messagePinned.unpin()
secretKeyPinned.unpin()
return signature
}
/**
* The crypto_sign_verify_detached() function verifies that sig is a valid signature for the message m whose length
* is mlen bytes, using the signer's public key pk.
*/
actual fun verifyDetached(
signature: UByteArray,
message: UByteArray,
publicKey: UByteArray
) {
val signaturePinned = signature.pin()
val messagePinned = message.pin()
val publicKeyPinned = publicKey.pin()
val verificationResult = crypto_sign_verify_detached(
signaturePinned.toPtr(),
messagePinned.toPtr(),
message.size.convert(),
publicKeyPinned.toPtr()
)
signaturePinned.unpin()
messagePinned.unpin()
publicKeyPinned.unpin()
if (verificationResult == -1) {
throw InvalidSignatureException()
}
}
/**
* The crypto_sign_ed25519_pk_to_curve25519() function converts an Ed25519 public key ed25519_pk to an X25519 public key and stores it into x25519_pk.
*/
actual fun ed25519PkToCurve25519(ed25519PublicKey: UByteArray) : UByteArray {
val x25519PublicKey = UByteArray(crypto_scalarmult_curve25519_BYTES)
val x25519PublicKeyPinned = x25519PublicKey.pin()
val ed25519PublicKeyPinned = ed25519PublicKey.pin()
crypto_sign_ed25519_sk_to_curve25519(
x25519PublicKeyPinned.toPtr(),
ed25519PublicKeyPinned.toPtr()
)
x25519PublicKeyPinned.unpin()
ed25519PublicKeyPinned.unpin()
return x25519PublicKey
}
actual fun ed25519SkToCurve25519(ed25519SecretKey: UByteArray) : UByteArray {
val x25519SecretKey = UByteArray(crypto_scalarmult_curve25519_BYTES)
val x25519SecretKeyPinned = x25519SecretKey.pin()
val ed25519SecretKeyPinned = ed25519SecretKey.pin()
crypto_sign_ed25519_sk_to_curve25519(
x25519SecretKeyPinned.toPtr(),
ed25519SecretKeyPinned.toPtr()
)
x25519SecretKeyPinned.unpin()
ed25519SecretKeyPinned.unpin()
return x25519SecretKey
}
/**
* The secret key actually includes the seed (either a random seed or the one given to crypto_sign_seed_keypair()) as well as the public key.
* While the public key can always be derived from the seed, the precomputation saves a significant amount of CPU cycles when signing.
*/
actual fun ed25519SkToSeed(secretKey: UByteArray): UByteArray {
val seed = UByteArray(crypto_sign_SEEDBYTES)
val secretKeyPinned = secretKey.pin()
val seedPinned = seed.pin()
crypto_sign_ed25519_sk_to_seed(
seedPinned.toPtr(),
secretKeyPinned.toPtr()
)
secretKeyPinned.unpin()
seedPinned.unpin()
return seed
}
/**
* The secret key actually includes the seed (either a random seed or the one given to crypto_sign_seed_keypair()) as well as the public key.
* While the public key can always be derived from the seed, the precomputation saves a significant amount of CPU cycles when signing.
*/
actual fun ed25519SkToPk(secretKey: UByteArray): UByteArray {
val publicKey = UByteArray(crypto_sign_PUBLICKEYBYTES)
val secretKeyPinned = secretKey.pin()
val publicKeyPinned = publicKey.pin()
crypto_sign_ed25519_sk_to_pk(
publicKeyPinned.toPtr(),
secretKeyPinned.toPtr()
)
secretKeyPinned.unpin()
publicKeyPinned.unpin()
return publicKey
}
}