Deterministic Key Generation for ECDSA and EdDSA (#729)

Deterministic Key Derivation for ECDSA R1/K1 and EdDSA

* DKG description and comments

* Removing a (confusing) not-required comma in comments.

* rename deterministic and generate to derive
This commit is contained in:
Konstantinos Chalkias 2017-06-13 21:55:55 +01:00 committed by GitHub
parent 56bad3a9b4
commit ec0e0dd442
3 changed files with 252 additions and 25 deletions

View File

@ -31,6 +31,12 @@ import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPublicKey
import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter
import org.bouncycastle.jce.ECNamedCurveTable
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.bouncycastle.jce.spec.ECParameterSpec
import org.bouncycastle.jce.spec.ECPrivateKeySpec
import org.bouncycastle.jce.spec.ECPublicKeySpec
import org.bouncycastle.math.ec.ECConstants
import org.bouncycastle.math.ec.FixedPointCombMultiplier
import org.bouncycastle.math.ec.WNafUtil
import org.bouncycastle.operator.ContentSigner
import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder
import org.bouncycastle.pkcs.PKCS10CertificationRequest
@ -50,6 +56,8 @@ import java.security.spec.InvalidKeySpecException
import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.X509EncodedKeySpec
import java.util.*
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
/**
* This object controls and provides the available and supported signature schemes for Corda.
@ -245,8 +253,7 @@ object Crypto {
*/
@Throws(IllegalArgumentException::class, InvalidKeySpecException::class)
fun decodePrivateKey(signatureScheme: SignatureScheme, encodedKey: ByteArray): PrivateKey {
if (!isSupportedSignatureScheme(signatureScheme))
throw IllegalArgumentException("Unsupported key/algorithm for schemeCodeName: $signatureScheme.schemeCodeName")
require(isSupportedSignatureScheme(signatureScheme)) { "Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}" }
try {
return KeyFactory.getInstance(signatureScheme.algorithmName, providerMap[signatureScheme.providerName]).generatePrivate(PKCS8EncodedKeySpec(encodedKey))
} catch (ikse: InvalidKeySpecException) {
@ -301,8 +308,7 @@ object Crypto {
*/
@Throws(IllegalArgumentException::class, InvalidKeySpecException::class)
fun decodePublicKey(signatureScheme: SignatureScheme, encodedKey: ByteArray): PublicKey {
if (!isSupportedSignatureScheme(signatureScheme))
throw IllegalArgumentException("Unsupported key/algorithm for schemeCodeName: $signatureScheme.schemeCodeName")
require(isSupportedSignatureScheme(signatureScheme)) { "Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}" }
try {
return KeyFactory.getInstance(signatureScheme.algorithmName, providerMap[signatureScheme.providerName]).generatePublic(X509EncodedKeySpec(encodedKey))
} catch (ikse: InvalidKeySpecException) {
@ -348,8 +354,7 @@ object Crypto {
*/
@Throws(IllegalArgumentException::class, InvalidKeyException::class, SignatureException::class)
fun doSign(signatureScheme: SignatureScheme, privateKey: PrivateKey, clearData: ByteArray): ByteArray {
if (!isSupportedSignatureScheme(signatureScheme))
throw IllegalArgumentException("Unsupported key/algorithm for schemeCodeName: $signatureScheme.schemeCodeName")
require(isSupportedSignatureScheme(signatureScheme)) { "Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}" }
val signature = Signature.getInstance(signatureScheme.signatureName, providerMap[signatureScheme.providerName])
if (clearData.isEmpty()) throw Exception("Signing of an empty array is not permitted!")
signature.initSign(privateKey)
@ -428,8 +433,7 @@ object Crypto {
*/
@Throws(InvalidKeyException::class, SignatureException::class, IllegalArgumentException::class)
fun doVerify(signatureScheme: SignatureScheme, publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray): Boolean {
if (!isSupportedSignatureScheme(signatureScheme))
throw IllegalArgumentException("Unsupported key/algorithm for schemeCodeName: $signatureScheme.schemeCodeName")
require(isSupportedSignatureScheme(signatureScheme)) { "Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}" }
if (signatureData.isEmpty()) throw IllegalArgumentException("Signature data is empty!")
if (clearData.isEmpty()) throw IllegalArgumentException("Clear data is empty, nothing to verify!")
val verificationResult = isValid(signatureScheme, publicKey, signatureData, clearData)
@ -491,8 +495,7 @@ object Crypto {
*/
@Throws(SignatureException::class, IllegalArgumentException::class)
fun isValid(signatureScheme: SignatureScheme, publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray): Boolean {
if (!isSupportedSignatureScheme(signatureScheme))
throw IllegalArgumentException("Unsupported key/algorithm for schemeCodeName: $signatureScheme.schemeCodeName")
require(isSupportedSignatureScheme(signatureScheme)) { "Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}" }
val signature = Signature.getInstance(signatureScheme.signatureName, providerMap[signatureScheme.providerName])
signature.initVerify(publicKey)
signature.update(clearData)
@ -519,8 +522,7 @@ object Crypto {
@Throws(IllegalArgumentException::class)
@JvmOverloads
fun generateKeyPair(signatureScheme: SignatureScheme = DEFAULT_SIGNATURE_SCHEME): KeyPair {
if (!isSupportedSignatureScheme(signatureScheme))
throw IllegalArgumentException("Unsupported key/algorithm for schemeCodeName: $signatureScheme.schemeCodeName")
require(isSupportedSignatureScheme(signatureScheme)) { "Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}" }
val keyPairGenerator = KeyPairGenerator.getInstance(signatureScheme.algorithmName, providerMap[signatureScheme.providerName])
if (signatureScheme.algSpec != null)
keyPairGenerator.initialize(signatureScheme.algSpec, newSecureRandom())
@ -529,6 +531,107 @@ object Crypto {
return keyPairGenerator.generateKeyPair()
}
/**
* Deterministically generate/derive a [KeyPair] using an existing private key and a seed as inputs.
* This operation is currently supported for ECDSA secp256r1 (NIST P-256), ECDSA secp256k1 and EdDSA ed25519.
*
* Similarly to BIP32, the implemented algorithm uses an HMAC function based on SHA512 and it is actually
* a variation of the private-parent-key -> private-child-key hardened key generation of BIP32.
*
* Unlike BIP32, where both private and public keys are extended to prevent deterministically
* generated child keys from depending solely on the key itself, current method uses normal elliptic curve keys
* without a chain-code and the generated key relies solely on the security of the private key.
*
* Although without a chain-code we lose the aforementioned property of not depending solely on the key,
* it should be mentioned that the cryptographic strength of the HMAC depends upon the size of the secret key.
* @see <a href="https://en.wikipedia.org/wiki/Hash-based_message_authentication_code#Security">HMAC Security</a>
* Thus, as long as the master key is kept secret and has enough entropy (~256 bits for EC-schemes), the system
* is considered secure.
*
* It is also a fact that if HMAC is used as PRF and/or MAC but not as checksum function, the function is still
* secure even if the underlying hash function is not collision resistant (e.g. if we used MD5).
* In practice, for our DKG purposes (thus PRF), a collision would not necessarily reveal the master HMAC key,
* because multiple inputs can produce the same hash output.
*
* All in all, this algorithm can be used with a counter as seed, however it is suggested that the output does
* not solely depend on the key, i.e. a secret salt per user or a random nonce per transaction could serve this role.
*
* @param signatureScheme the [SignatureScheme] of the private key input.
* @param privateKey the [PrivateKey] that will be used as key to the HMAC-ed DKG function.
* @param seed an extra seed that will be used as value to the underlying HMAC.
* @return a new deterministically generated [KeyPair].
* @throws IllegalArgumentException if the requested signature scheme is not supported.
* @throws UnsupportedOperationException if deterministic key generation is not supported for this particular scheme.
*/
fun deterministicKeyPair(signatureScheme: SignatureScheme, privateKey: PrivateKey, seed: ByteArray): KeyPair {
require(isSupportedSignatureScheme(signatureScheme)) { "Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}" }
when (signatureScheme) {
ECDSA_SECP256R1_SHA256, ECDSA_SECP256K1_SHA256 -> return deriveKeyPairECDSA(signatureScheme.algSpec as ECParameterSpec, privateKey, seed)
EDDSA_ED25519_SHA512 -> return deriveKeyPairEdDSA(privateKey, seed)
}
throw UnsupportedOperationException("Although supported for signing, deterministic key generation is not currently implemented for ${signatureScheme.schemeCodeName}")
}
/**
* Deterministically generate/derive a [KeyPair] using an existing private key and a seed as inputs.
* Use this method if the [SignatureScheme] of the private key input is not known.
* @param privateKey the [PrivateKey] that will be used as key to the HMAC-ed DKG function.
* @param seed an extra seed that will be used as value to the underlying HMAC.
* @return a new deterministically generated [KeyPair].
* @throws IllegalArgumentException if the requested signature scheme is not supported.
* @throws UnsupportedOperationException if deterministic key generation is not supported for this particular scheme.
*/
fun deterministicKeyPair(privateKey: PrivateKey, seed: ByteArray): KeyPair {
return deterministicKeyPair(findSignatureScheme(privateKey), privateKey, seed)
}
// Given the domain parameters, this routine deterministically generates an ECDSA key pair
// in accordance with X9.62 section 5.2.1 pages 26, 27.
private fun deriveKeyPairECDSA(parameterSpec: ECParameterSpec, privateKey: PrivateKey, seed: ByteArray): KeyPair {
// Compute hmac(privateKey, seed).
val mac = Mac.getInstance("HmacSHA512", providerMap[BouncyCastleProvider.PROVIDER_NAME])
val key = SecretKeySpec((privateKey as BCECPrivateKey).d.toByteArray(), "HmacSHA512")
mac.init(key)
val macBytes = mac.doFinal(seed)
// Calculate value d for private key.
val deterministicD = BigInteger(1, macBytes).mod(parameterSpec.n)
// Key generation checks follow the BC logic found in
// https://github.com/bcgit/bc-java/blob/master/core/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java
if (deterministicD < ECConstants.TWO
|| WNafUtil.getNafWeight(deterministicD) < parameterSpec.n.bitLength().ushr(2)) {
throw InvalidKeyException("Cannot generate key for this seed, please use another seed value")
}
val privateKeySpec = ECPrivateKeySpec(deterministicD, parameterSpec)
val privateKeyD = BCECPrivateKey(privateKey.algorithm, privateKeySpec, BouncyCastleProvider.CONFIGURATION)
// Compute the public key by scalar multiplication.
val pointQ = FixedPointCombMultiplier().multiply(parameterSpec.g, deterministicD)
// This is unlikely to happen, but we should check for point at infinity.
if (pointQ.isInfinity)
throw InvalidKeyException("Cannot generate key for this seed, please use another seed value")
val publicKeySpec = ECPublicKeySpec(pointQ, parameterSpec)
val publicKeyD = BCECPublicKey(privateKey.algorithm, publicKeySpec, BouncyCastleProvider.CONFIGURATION)
return KeyPair(publicKeyD, privateKeyD)
}
// Deterministically generate an EdDSA key.
private fun deriveKeyPairEdDSA(privateKey: PrivateKey, seed: ByteArray): KeyPair {
// Compute HMAC(privateKey, seed).
val mac = Mac.getInstance("HmacSHA512", providerMap[BouncyCastleProvider.PROVIDER_NAME])
val key = SecretKeySpec((privateKey as EdDSAPrivateKey).geta(), "HmacSHA512")
mac.init(key)
val macBytes = mac.doFinal(seed)
// Calculate key pair.
val params = EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec
val bytes = macBytes.copyOf(params.curve.field.getb() / 8) // Need to pad the entropy to the valid seed length.
val privateKeyD = EdDSAPrivateKeySpec(bytes, params)
val publicKeyD = EdDSAPublicKeySpec(privateKeyD.a, params)
return KeyPair(EdDSAPublicKey(publicKeyD), EdDSAPrivateKey(privateKeyD))
}
/**
* Returns a key pair derived from the given [BigInteger] entropy. This is useful for unit tests
* and other cases where you want hard-coded private keys.
@ -538,11 +641,11 @@ object Crypto {
* @return a new [KeyPair] from an entropy input.
* @throws IllegalArgumentException if the requested signature scheme is not supported for KeyPair generation using an entropy input.
*/
fun generateKeyPairFromEntropy(signatureScheme: SignatureScheme, entropy: BigInteger): KeyPair {
fun deriveKeyPairFromEntropy(signatureScheme: SignatureScheme, entropy: BigInteger): KeyPair {
when (signatureScheme) {
EDDSA_ED25519_SHA512 -> return generateEdDSAKeyPairFromEntropy(entropy)
EDDSA_ED25519_SHA512 -> return deriveEdDSAKeyPairFromEntropy(entropy)
}
throw IllegalArgumentException("Unsupported signature scheme for fixed entropy-based key pair generation: $signatureScheme.schemeCodeName")
throw IllegalArgumentException("Unsupported signature scheme for fixed entropy-based key pair generation: ${signatureScheme.schemeCodeName}")
}
/**
@ -550,12 +653,12 @@ object Crypto {
* @param entropy a [BigInteger] value.
* @return a new [KeyPair] from an entropy input.
*/
fun generateKeyPairFromEntropy(entropy: BigInteger): KeyPair = generateKeyPairFromEntropy(DEFAULT_SIGNATURE_SCHEME, entropy)
fun deriveKeyPairFromEntropy(entropy: BigInteger): KeyPair = deriveKeyPairFromEntropy(DEFAULT_SIGNATURE_SCHEME, entropy)
// custom key pair generator from entropy.
private fun generateEdDSAKeyPairFromEntropy(entropy: BigInteger): KeyPair {
private fun deriveEdDSAKeyPairFromEntropy(entropy: BigInteger): KeyPair {
val params = EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec
val bytes = entropy.toByteArray().copyOf(params.curve.field.getb() / 8) // need to pad the entropy to the valid seed length.
val bytes = entropy.toByteArray().copyOf(params.curve.field.getb() / 8) // Need to pad the entropy to the valid seed length.
val priv = EdDSAPrivateKeySpec(bytes, params)
val pub = EdDSAPublicKeySpec(priv.a, params)
return KeyPair(EdDSAPublicKey(pub), EdDSAPrivateKey(priv))
@ -666,8 +769,7 @@ object Crypto {
*/
@Throws(IllegalArgumentException::class)
fun publicKeyOnCurve(signatureScheme: SignatureScheme, publicKey: PublicKey): Boolean {
if (!isSupportedSignatureScheme(signatureScheme))
throw IllegalArgumentException("Unsupported signature scheme: $signatureScheme.schemeCodeName")
require(isSupportedSignatureScheme(signatureScheme)) { "Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}" }
when (publicKey) {
is BCECPublicKey -> return (publicKey.parameters == signatureScheme.algSpec && !publicKey.q.isInfinity && publicKey.q.isValid)
is EdDSAPublicKey -> return (publicKey.params == signatureScheme.algSpec && !isEdDSAPointAtInfinity(publicKey) && publicKey.a.isOnCurve)

View File

@ -144,7 +144,7 @@ fun generateKeyPair(): KeyPair = Crypto.generateKeyPair()
* you want hard-coded private keys.
* This currently works for the default signature scheme EdDSA ed25519 only.
*/
fun entropyToKeyPair(entropy: BigInteger): KeyPair = Crypto.generateKeyPairFromEntropy(entropy)
fun entropyToKeyPair(entropy: BigInteger): KeyPair = Crypto.deriveKeyPairFromEntropy(entropy)
/**
* Helper function for signing.

View File

@ -2,6 +2,7 @@ package net.corda.core.crypto
import com.google.common.collect.Sets
import net.i2p.crypto.eddsa.EdDSAKey
import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey
import net.i2p.crypto.eddsa.math.GroupElement
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec
@ -9,6 +10,7 @@ import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
import org.bouncycastle.jce.ECNamedCurveTable
import org.bouncycastle.jce.interfaces.ECKey
@ -640,9 +642,9 @@ class CryptoUtilsTest {
val keyPairEdDSA = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)
val pubEdDSA = keyPairEdDSA.public
assertTrue(Crypto.publicKeyOnCurve(Crypto.EDDSA_ED25519_SHA512, pubEdDSA))
// use R1 curve for check.
// Use R1 curve for check.
assertFalse(Crypto.publicKeyOnCurve(Crypto.ECDSA_SECP256R1_SHA256, pubEdDSA))
// check for point at infinity.
// Check for point at infinity.
val pubKeySpec = EdDSAPublicKeySpec((Crypto.EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec).curve.getZero(GroupElement.Representation.P3), Crypto.EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec)
assertFalse(Crypto.publicKeyOnCurve(Crypto.EDDSA_ED25519_SHA512, EdDSAPublicKey(pubKeySpec)))
}
@ -652,8 +654,131 @@ class CryptoUtilsTest {
val keyGen = KeyPairGenerator.getInstance("EC") // sun.security.ec.ECPublicKeyImpl
keyGen.initialize(256, newSecureRandom())
val pairSun = keyGen.generateKeyPair()
val pubSun = pairSun.getPublic()
// should fail as pubSun is not a BCECPublicKey.
val pubSun = pairSun.public
// Should fail as pubSun is not a BCECPublicKey.
Crypto.publicKeyOnCurve(Crypto.ECDSA_SECP256R1_SHA256, pubSun)
}
@Test
fun `ECDSA secp256R1 deterministic key generation`() {
val (priv, pub) = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
val (dpriv, dpub) = Crypto.deterministicKeyPair(priv, "seed-1".toByteArray())
// Check scheme.
assertEquals(priv.algorithm, dpriv.algorithm)
assertEquals(pub.algorithm, dpub.algorithm)
assertTrue(dpriv is BCECPrivateKey)
assertTrue(dpub is BCECPublicKey)
assertEquals((dpriv as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256r1"))
assertEquals((dpub as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256r1"))
assertEquals(Crypto.findSignatureScheme(dpriv), Crypto.ECDSA_SECP256R1_SHA256)
assertEquals(Crypto.findSignatureScheme(dpub), Crypto.ECDSA_SECP256R1_SHA256)
// Validate public key.
assertTrue(Crypto.publicKeyOnCurve(Crypto.ECDSA_SECP256R1_SHA256, dpub))
// Try to sign/verify.
val signedData = Crypto.doSign(dpriv, testBytes)
val verification = Crypto.doVerify(dpub, signedData, testBytes)
assertTrue(verification)
// Check it is a new keyPair.
assertNotEquals(priv, dpriv)
assertNotEquals(pub, dpub)
// A new keyPair is always generated per different seed.
val (dpriv2, dpub2) = Crypto.deterministicKeyPair(priv, "seed-2".toByteArray())
assertNotEquals(dpriv, dpriv2)
assertNotEquals(dpub, dpub2)
// Check if the same input always produces the same output (i.e. deterministically generated).
val (dpriv_1, dpub_1) = Crypto.deterministicKeyPair(priv, "seed-1".toByteArray())
assertEquals(dpriv, dpriv_1)
assertEquals(dpub, dpub_1)
val (dpriv_2, dpub_2) = Crypto.deterministicKeyPair(priv, "seed-2".toByteArray())
assertEquals(dpriv2, dpriv_2)
assertEquals(dpub2, dpub_2)
}
@Test
fun `ECDSA secp256K1 deterministic key generation`() {
val (priv, pub) = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
val (dpriv, dpub) = Crypto.deterministicKeyPair(priv, "seed-1".toByteArray())
// Check scheme.
assertEquals(priv.algorithm, dpriv.algorithm)
assertEquals(pub.algorithm, dpub.algorithm)
assertTrue(dpriv is BCECPrivateKey)
assertTrue(dpub is BCECPublicKey)
assertEquals((dpriv as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256k1"))
assertEquals((dpub as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256k1"))
assertEquals(Crypto.findSignatureScheme(dpriv), Crypto.ECDSA_SECP256K1_SHA256)
assertEquals(Crypto.findSignatureScheme(dpub), Crypto.ECDSA_SECP256K1_SHA256)
// Validate public key.
assertTrue(Crypto.publicKeyOnCurve(Crypto.ECDSA_SECP256K1_SHA256, dpub))
// Try to sign/verify.
val signedData = Crypto.doSign(dpriv, testBytes)
val verification = Crypto.doVerify(dpub, signedData, testBytes)
assertTrue(verification)
// check it is a new keyPair.
assertNotEquals(priv, dpriv)
assertNotEquals(pub, dpub)
// A new keyPair is always generated per different seed.
val (dpriv2, dpub2) = Crypto.deterministicKeyPair(priv, "seed-2".toByteArray())
assertNotEquals(dpriv, dpriv2)
assertNotEquals(dpub, dpub2)
// Check if the same input always produces the same output (i.e. deterministically generated).
val (dpriv_1, dpub_1) = Crypto.deterministicKeyPair(priv, "seed-1".toByteArray())
assertEquals(dpriv, dpriv_1)
assertEquals(dpub, dpub_1)
val (dpriv_2, dpub_2) = Crypto.deterministicKeyPair(priv, "seed-2".toByteArray())
assertEquals(dpriv2, dpriv_2)
assertEquals(dpub2, dpub_2)
}
@Test
fun `EdDSA ed25519 deterministic key generation`() {
val (priv, pub) = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)
val (dpriv, dpub) = Crypto.deterministicKeyPair(priv, "seed-1".toByteArray())
// Check scheme.
assertEquals(priv.algorithm, dpriv.algorithm)
assertEquals(pub.algorithm, dpub.algorithm)
assertTrue(dpriv is EdDSAPrivateKey)
assertTrue(dpub is EdDSAPublicKey)
assertEquals((dpriv as EdDSAKey).params, EdDSANamedCurveTable.getByName("ED25519"))
assertEquals((dpub as EdDSAKey).params, EdDSANamedCurveTable.getByName("ED25519"))
assertEquals(Crypto.findSignatureScheme(dpriv), Crypto.EDDSA_ED25519_SHA512)
assertEquals(Crypto.findSignatureScheme(dpub), Crypto.EDDSA_ED25519_SHA512)
// Validate public key.
assertTrue(Crypto.publicKeyOnCurve(Crypto.EDDSA_ED25519_SHA512, dpub))
// Try to sign/verify.
val signedData = Crypto.doSign(dpriv, testBytes)
val verification = Crypto.doVerify(dpub, signedData, testBytes)
assertTrue(verification)
// Check it is a new keyPair.
assertNotEquals(priv, dpriv)
assertNotEquals(pub, dpub)
// A new keyPair is always generated per different seed.
val (dpriv2, dpub2) = Crypto.deterministicKeyPair(priv, "seed-2".toByteArray())
assertNotEquals(dpriv, dpriv2)
assertNotEquals(dpub, dpub2)
// Check if the same input always produces the same output (i.e. deterministically generated).
val (dpriv_1, dpub_1) = Crypto.deterministicKeyPair(priv, "seed-1".toByteArray())
assertEquals(dpriv, dpriv_1)
assertEquals(dpub, dpub_1)
val (dpriv_2, dpub_2) = Crypto.deterministicKeyPair(priv, "seed-2".toByteArray())
assertEquals(dpriv2, dpriv_2)
assertEquals(dpub2, dpub_2)
}
}