org.kin.stellarfork.KeyPairNativeJNIImpl.kt Maven / Gradle / Ivy
@file:Suppress("DEPRECATION")
package org.kin.stellarfork
import org.kin.stellarfork.StrKey.decodeStellarAccountId
import org.kin.stellarfork.StrKey.decodeStellarSecretSeed
import org.kin.stellarfork.StrKey.encodeStellarAccountId
import org.kin.stellarfork.StrKey.encodeStellarSecretSeed
import org.libsodium.jni.NaCl
import org.libsodium.jni.Sodium
import org.libsodium.jni.SodiumConstants
import org.libsodium.jni.SodiumConstants.PUBLICKEY_BYTES
import org.libsodium.jni.SodiumConstants.SECRETKEY_BYTES
import org.libsodium.jni.crypto.Util
import java.security.GeneralSecurityException
import java.util.Arrays
/**
* Holds a Stellar keypair.
*/
data class KeyPairNativeJNIImpl @JvmOverloads
/**
* Creates a new KeyPair from the given public and private keys.
*
* @param publicKey
* @param privateKey
*/
/**
* Creates a new KeyPair without a private key. Useful to simply verify a signature from a
* given public address.
*
* @param publicKey
*/
constructor(
private val mPublicKey: PublicKey,
private val mPrivateKey: PrivateKey? = null,
) : IKeyPair {
/**
* Returns true if this Keypair is capable of signing
*/
override fun canSign(): Boolean {
return mPrivateKey != null
}
/**
* Returns the human readable account ID encoded in strkey.
*/
override val accountId: String
get() = encodeStellarAccountId(mPublicKey.bytes)
/**
* Returns the human readable secret seed encoded in strkey.
*/
override val secretSeed: CharArray
get() = encodeStellarSecretSeed(mPrivateKey!!.seed)
override val privateKey: ByteArray?
get() = ByteArray(1)
/**
* Returns the raw 32 byte secret seed.
*/
override val rawSecretSeed: ByteArray?
get() = mPrivateKey?.seed
override val publicKey: ByteArray
get() = mPublicKey.bytes
/**
* Sign the provided data with the keypair's private key.
*
* @param data The data to sign.
* @return signed bytes, null if the private key for this keypair is null.
*/
override fun sign(data: ByteArray?): ByteArray? {
mPrivateKey
?: throw RuntimeException("KeyPair does not contain secret key. Use KeyPair.fromSecretSeed method to create a new KeyPair with a secret key.")
return try {
val signature = Util.prependZeros(
SodiumConstants.SIGNATURE_BYTES,
data
)
val bufferLen = IntArray(1)
val seed: ByteArray = mPrivateKey.seed
val publicKey: ByteArray = Util.zeros(PUBLICKEY_BYTES)
val secretKey: ByteArray = Util.zeros(SECRETKEY_BYTES * 2)
Sodium.crypto_sign_ed25519_seed_keypair(publicKey, secretKey, seed)
Sodium.crypto_sign_ed25519(
signature,
bufferLen,
data,
data!!.size,
secretKey
)
Util.slice(
signature,
0,
SodiumConstants.SIGNATURE_BYTES
)
} catch (e: GeneralSecurityException) {
throw RuntimeException(e)
}
}
/**
* Verify the provided data and signature match this keypair's public key.
*
* @param data The data that was signed.
* @param signature The signature.
* @return True if they match, false otherwise.
* @throws RuntimeException
*/
override fun verify(data: ByteArray?, signature: ByteArray?): Boolean {
return try {
Util.checkLength(
signature,
SodiumConstants.SIGNATURE_BYTES
)
val sigAndMsg = Util.merge(signature, data)
val buffer = Util.zeros(sigAndMsg.size)
val bufferLen = IntArray(1)
Util.isValid(
Sodium.crypto_sign_ed25519_open(
buffer,
bufferLen,
sigAndMsg,
sigAndMsg.size,
mPublicKey.bytes
), "signature was forged or corrupted"
)
} catch (t: Throwable) {
false
}
}
companion object {
init {
try {
NaCl.sodium()
} catch (t: Throwable) {
// do nothing
}
}
/**
* Creates a new Stellar KeyPair from a strkey encoded Stellar secret seed.
*
* @param seed Char array containing strkey encoded Stellar secret seed.
* @return [KeyPairNativeJNIImpl]
*/
@JvmStatic
fun fromSecretSeed(seed: CharArray): KeyPairNativeJNIImpl {
val decoded = decodeStellarSecretSeed(seed)
val keypair = fromSecretSeed(decoded)
Arrays.fill(decoded, 0.toByte())
return keypair
}
/**
* **Insecure** Creates a new Stellar KeyPair from a strkey encoded Stellar secret seed.
* This method is insecure. Use only if you are aware of security implications.
*
* @param seed The strkey encoded Stellar secret seed.
* @return [KeyPairNativeJNIImpl]
* @see [Using Password-Based Encryption](http://docs.oracle.com/javase/1.5.0/docs/guide/security/jce/JCERefGuide.html.PBEEx)
*/
@JvmStatic
fun fromSecretSeed(seed: String): KeyPairNativeJNIImpl {
val charSeed = seed.toCharArray()
val decoded = decodeStellarSecretSeed(charSeed)
val keypair = fromSecretSeed(decoded)
Arrays.fill(charSeed, ' ')
return keypair
}
/**
* Creates a new Stellar keypair from a raw 32 byte secret seed.
*
* @param seed The 32 byte secret seed.
* @return [KeyPairNativeJNIImpl]
*/
@JvmStatic
fun fromSecretSeed(seed: ByteArray): KeyPairNativeJNIImpl {
val publicKey: ByteArray = Util.zeros(PUBLICKEY_BYTES)
val secretKey: ByteArray = Util.zeros(SECRETKEY_BYTES * 2)
Sodium.crypto_sign_ed25519_seed_keypair(publicKey, secretKey, seed)
return KeyPairNativeJNIImpl(
PublicKey(publicKey),
PrivateKey(secretKey, seed)
)
}
/**
* Creates a new Stellar KeyPair from a strkey encoded Stellar account ID.
*
* @param accountId The strkey encoded Stellar account ID.
* @return [KeyPairNativeJNIImpl]
*/
@JvmStatic
fun fromAccountId(accountId: String): KeyPairNativeJNIImpl =
fromPublicKey(decodeStellarAccountId(accountId))
/**
* Creates a new Stellar keypair from a 32 byte address.
*
* @param publicKey The 32 byte public key.
* @return [KeyPairNativeJNIImpl]
*/
@JvmStatic
fun fromPublicKey(publicKey: ByteArray): KeyPairNativeJNIImpl = KeyPairNativeJNIImpl(
PublicKey(
publicKey
)
)
/**
* Generates a random Stellar keypair.
*
* @return a random Stellar keypair.
*/
@JvmStatic
fun random(): KeyPairNativeJNIImpl {
val publicKey: ByteArray = Util.zeros(PUBLICKEY_BYTES)
val secretKey: ByteArray = Util.zeros(SECRETKEY_BYTES * 2)
Sodium.crypto_box_curve25519xsalsa20poly1305_keypair(publicKey, secretKey)
val seed = ByteArray(SECRETKEY_BYTES)
Sodium.crypto_sign_ed25519_sk_to_seed(seed, secretKey)
val publicKey2: ByteArray = Util.zeros(PUBLICKEY_BYTES)
val secretKey2: ByteArray = Util.zeros(SECRETKEY_BYTES * 2)
Sodium.crypto_sign_ed25519_seed_keypair(publicKey2, secretKey2, seed)
return KeyPairNativeJNIImpl(
PublicKey(publicKey2),
PrivateKey(secretKey2, seed)
)
}
}
data class PublicKey(val bytes: ByteArray) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is PublicKey) return false
if (!bytes.contentEquals(other.bytes)) return false
return true
}
override fun hashCode(): Int {
return bytes.contentHashCode()
}
}
data class PrivateKey(val bytes: ByteArray, val seed: ByteArray) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is PrivateKey) return false
if (!bytes.contentEquals(other.bytes)) return false
if (!seed.contentEquals(other.seed)) return false
return true
}
override fun hashCode(): Int {
var result = bytes.contentHashCode()
result = 31 * result + seed.contentHashCode()
return result
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy