Adding _sign_ native implementation and started writing tests
This commit is contained in:
		
							parent
							
								
									1936e748ae
								
							
						
					
					
						commit
						b0ce2e10fc
					
				@ -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.
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user