[CORDA-2084] EdDSA, SPHINCS-256 and RSA PKCS#1 are deterministic, no RNG required. (#4051)

This commit is contained in:
Konstantinos Chalkias 2018-10-10 10:35:18 +01:00 committed by GitHub
parent 9ebeac1ad8
commit 554b1fa371
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 138 additions and 104 deletions

View File

@ -424,15 +424,19 @@ object Crypto {
} }
require(clearData.isNotEmpty()) { "Signing of an empty array is not permitted!" } require(clearData.isNotEmpty()) { "Signing of an empty array is not permitted!" }
val signature = Signature.getInstance(signatureScheme.signatureName, providerMap[signatureScheme.providerName]) val signature = Signature.getInstance(signatureScheme.signatureName, providerMap[signatureScheme.providerName])
// Note that deterministic signature schemes, such as EdDSA, do not require extra randomness, but we have to // Note that deterministic signature schemes, such as EdDSA, original SPHINCS-256 and RSA PKCS#1, do not require
// ensure that non-deterministic algorithms (i.e., ECDSA) use non-blocking SecureRandom implementations (if possible). // extra randomness, but we have to ensure that non-deterministic algorithms (i.e., ECDSA) use non-blocking
// TODO consider updating this when the related BC issue for Sphincs is fixed. // SecureRandom implementation. Also, SPHINCS-256 implementation in BouncyCastle 1.60 fails with
if (signatureScheme != SPHINCS256_SHA256) { // ClassCastException if we invoke initSign with a SecureRandom as an input.
signature.initSign(privateKey, newSecureRandom()) // TODO Although we handle the above issue here, consider updating to BC 1.61+ which provides a fix.
} else { if (signatureScheme == EDDSA_ED25519_SHA512
// Special handling for Sphincs, due to a BC implementation issue. || signatureScheme == SPHINCS256_SHA256
// As Sphincs is deterministic, it does not require RNG input anyway. || signatureScheme == RSA_SHA256) {
signature.initSign(privateKey) signature.initSign(privateKey)
} else {
// The rest of the algorithms will require a SecureRandom input (i.e., ECDSA or any new algorithm for which
// we don't know if it's deterministic).
signature.initSign(privateKey, newSecureRandom())
} }
signature.update(clearData) signature.update(clearData)
return signature.sign() return signature.sign()

View File

@ -1,6 +1,12 @@
package net.corda.core.crypto package net.corda.core.crypto
import com.google.common.collect.Sets import com.google.common.collect.Sets
import net.corda.core.crypto.Crypto.ECDSA_SECP256K1_SHA256
import net.corda.core.crypto.Crypto.ECDSA_SECP256R1_SHA256
import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512
import net.corda.core.crypto.Crypto.RSA_SHA256
import net.corda.core.crypto.Crypto.SPHINCS256_SHA256
import net.corda.core.utilities.OpaqueBytes
import net.i2p.crypto.eddsa.EdDSAKey import net.i2p.crypto.eddsa.EdDSAKey
import net.i2p.crypto.eddsa.EdDSAPrivateKey import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey import net.i2p.crypto.eddsa.EdDSAPublicKey
@ -30,17 +36,20 @@ import kotlin.test.*
*/ */
class CryptoUtilsTest { class CryptoUtilsTest {
companion object {
private val testBytes = "Hello World".toByteArray() private val testBytes = "Hello World".toByteArray()
private val test100ZeroBytes = ByteArray(100)
}
// key generation test // key generation test
@Test @Test
fun `Generate key pairs`() { fun `Generate key pairs`() {
// testing supported algorithms // testing supported algorithms
val rsaKeyPair = Crypto.generateKeyPair(Crypto.RSA_SHA256) val rsaKeyPair = Crypto.generateKeyPair(RSA_SHA256)
val ecdsaKKeyPair = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256) val ecdsaKKeyPair = Crypto.generateKeyPair(ECDSA_SECP256K1_SHA256)
val ecdsaRKeyPair = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256) val ecdsaRKeyPair = Crypto.generateKeyPair(ECDSA_SECP256R1_SHA256)
val eddsaKeyPair = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512) val eddsaKeyPair = Crypto.generateKeyPair(EDDSA_ED25519_SHA512)
val sphincsKeyPair = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256) val sphincsKeyPair = Crypto.generateKeyPair(SPHINCS256_SHA256)
// not null private keys // not null private keys
assertNotNull(rsaKeyPair.private) assertNotNull(rsaKeyPair.private)
@ -69,7 +78,7 @@ class CryptoUtilsTest {
@Test @Test
fun `RSA full process keygen-sign-verify`() { fun `RSA full process keygen-sign-verify`() {
val keyPair = Crypto.generateKeyPair(Crypto.RSA_SHA256) val keyPair = Crypto.generateKeyPair(RSA_SHA256)
val (privKey, pubKey) = keyPair val (privKey, pubKey) = keyPair
// test for some data // test for some data
val signedData = Crypto.doSign(privKey, testBytes) val signedData = Crypto.doSign(privKey, testBytes)
@ -101,8 +110,8 @@ class CryptoUtilsTest {
} }
// test for zero bytes data // test for zero bytes data
val signedDataZeros = Crypto.doSign(privKey, ByteArray(100)) val signedDataZeros = Crypto.doSign(privKey, test100ZeroBytes)
val verificationZeros = Crypto.doVerify(pubKey, signedDataZeros, ByteArray(100)) val verificationZeros = Crypto.doVerify(pubKey, signedDataZeros, test100ZeroBytes)
assertTrue(verificationZeros) assertTrue(verificationZeros)
// test for 1MB of data (I successfully tested it locally for 1GB as well) // test for 1MB of data (I successfully tested it locally for 1GB as well)
@ -124,7 +133,7 @@ class CryptoUtilsTest {
@Test @Test
fun `ECDSA secp256k1 full process keygen-sign-verify`() { fun `ECDSA secp256k1 full process keygen-sign-verify`() {
val keyPair = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256) val keyPair = Crypto.generateKeyPair(ECDSA_SECP256K1_SHA256)
val (privKey, pubKey) = keyPair val (privKey, pubKey) = keyPair
// test for some data // test for some data
val signedData = Crypto.doSign(privKey, testBytes) val signedData = Crypto.doSign(privKey, testBytes)
@ -156,8 +165,8 @@ class CryptoUtilsTest {
} }
// test for zero bytes data // test for zero bytes data
val signedDataZeros = Crypto.doSign(privKey, ByteArray(100)) val signedDataZeros = Crypto.doSign(privKey, test100ZeroBytes)
val verificationZeros = Crypto.doVerify(pubKey, signedDataZeros, ByteArray(100)) val verificationZeros = Crypto.doVerify(pubKey, signedDataZeros, test100ZeroBytes)
assertTrue(verificationZeros) assertTrue(verificationZeros)
// test for 1MB of data (I successfully tested it locally for 1GB as well) // test for 1MB of data (I successfully tested it locally for 1GB as well)
@ -179,7 +188,7 @@ class CryptoUtilsTest {
@Test @Test
fun `ECDSA secp256r1 full process keygen-sign-verify`() { fun `ECDSA secp256r1 full process keygen-sign-verify`() {
val keyPair = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256) val keyPair = Crypto.generateKeyPair(ECDSA_SECP256R1_SHA256)
val (privKey, pubKey) = keyPair val (privKey, pubKey) = keyPair
// test for some data // test for some data
val signedData = Crypto.doSign(privKey, testBytes) val signedData = Crypto.doSign(privKey, testBytes)
@ -211,8 +220,8 @@ class CryptoUtilsTest {
} }
// test for zero bytes data // test for zero bytes data
val signedDataZeros = Crypto.doSign(privKey, ByteArray(100)) val signedDataZeros = Crypto.doSign(privKey, test100ZeroBytes)
val verificationZeros = Crypto.doVerify(pubKey, signedDataZeros, ByteArray(100)) val verificationZeros = Crypto.doVerify(pubKey, signedDataZeros, test100ZeroBytes)
assertTrue(verificationZeros) assertTrue(verificationZeros)
// test for 1MB of data (I successfully tested it locally for 1GB as well) // test for 1MB of data (I successfully tested it locally for 1GB as well)
@ -234,7 +243,7 @@ class CryptoUtilsTest {
@Test @Test
fun `EDDSA ed25519 full process keygen-sign-verify`() { fun `EDDSA ed25519 full process keygen-sign-verify`() {
val keyPair = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512) val keyPair = Crypto.generateKeyPair(EDDSA_ED25519_SHA512)
val (privKey, pubKey) = keyPair val (privKey, pubKey) = keyPair
// test for some data // test for some data
val signedData = Crypto.doSign(privKey, testBytes) val signedData = Crypto.doSign(privKey, testBytes)
@ -266,8 +275,8 @@ class CryptoUtilsTest {
} }
// test for zero bytes data // test for zero bytes data
val signedDataZeros = Crypto.doSign(privKey, ByteArray(100)) val signedDataZeros = Crypto.doSign(privKey, test100ZeroBytes)
val verificationZeros = Crypto.doVerify(pubKey, signedDataZeros, ByteArray(100)) val verificationZeros = Crypto.doVerify(pubKey, signedDataZeros, test100ZeroBytes)
assertTrue(verificationZeros) assertTrue(verificationZeros)
// test for 1MB of data (I successfully tested it locally for 1GB as well) // test for 1MB of data (I successfully tested it locally for 1GB as well)
@ -289,7 +298,7 @@ class CryptoUtilsTest {
@Test @Test
fun `SPHINCS-256 full process keygen-sign-verify`() { fun `SPHINCS-256 full process keygen-sign-verify`() {
val keyPair = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256) val keyPair = Crypto.generateKeyPair(SPHINCS256_SHA256)
val (privKey, pubKey) = keyPair val (privKey, pubKey) = keyPair
// test for some data // test for some data
val signedData = Crypto.doSign(privKey, testBytes) val signedData = Crypto.doSign(privKey, testBytes)
@ -321,8 +330,8 @@ class CryptoUtilsTest {
} }
// test for zero bytes data // test for zero bytes data
val signedDataZeros = Crypto.doSign(privKey, ByteArray(100)) val signedDataZeros = Crypto.doSign(privKey, test100ZeroBytes)
val verificationZeros = Crypto.doVerify(pubKey, signedDataZeros, ByteArray(100)) val verificationZeros = Crypto.doVerify(pubKey, signedDataZeros, test100ZeroBytes)
assertTrue(verificationZeros) assertTrue(verificationZeros)
// test for 1MB of data (I successfully tested it locally for 1GB as well) // test for 1MB of data (I successfully tested it locally for 1GB as well)
@ -354,7 +363,7 @@ class CryptoUtilsTest {
@Test @Test
fun `RSA encode decode keys - required for serialization`() { fun `RSA encode decode keys - required for serialization`() {
// Generate key pair. // Generate key pair.
val keyPair = Crypto.generateKeyPair(Crypto.RSA_SHA256) val keyPair = Crypto.generateKeyPair(RSA_SHA256)
val (privKey, pubKey) = keyPair val (privKey, pubKey) = keyPair
// Encode and decode private key. // Encode and decode private key.
@ -369,7 +378,7 @@ class CryptoUtilsTest {
@Test @Test
fun `ECDSA secp256k1 encode decode keys - required for serialization`() { fun `ECDSA secp256k1 encode decode keys - required for serialization`() {
// Generate key pair. // Generate key pair.
val keyPair = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256) val keyPair = Crypto.generateKeyPair(ECDSA_SECP256K1_SHA256)
val (privKey, pubKey) = keyPair val (privKey, pubKey) = keyPair
// Encode and decode private key. // Encode and decode private key.
@ -384,7 +393,7 @@ class CryptoUtilsTest {
@Test @Test
fun `ECDSA secp256r1 encode decode keys - required for serialization`() { fun `ECDSA secp256r1 encode decode keys - required for serialization`() {
// Generate key pair. // Generate key pair.
val keyPair = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256) val keyPair = Crypto.generateKeyPair(ECDSA_SECP256R1_SHA256)
val (privKey, pubKey) = keyPair val (privKey, pubKey) = keyPair
// Encode and decode private key. // Encode and decode private key.
@ -399,7 +408,7 @@ class CryptoUtilsTest {
@Test @Test
fun `EdDSA encode decode keys - required for serialization`() { fun `EdDSA encode decode keys - required for serialization`() {
// Generate key pair. // Generate key pair.
val keyPair = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512) val keyPair = Crypto.generateKeyPair(EDDSA_ED25519_SHA512)
val (privKey, pubKey) = keyPair val (privKey, pubKey) = keyPair
// Encode and decode private key. // Encode and decode private key.
@ -414,7 +423,7 @@ class CryptoUtilsTest {
@Test @Test
fun `SPHINCS-256 encode decode keys - required for serialization`() { fun `SPHINCS-256 encode decode keys - required for serialization`() {
// Generate key pair. // Generate key pair.
val keyPair = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256) val keyPair = Crypto.generateKeyPair(SPHINCS256_SHA256)
val privKey: BCSphincs256PrivateKey = keyPair.private as BCSphincs256PrivateKey val privKey: BCSphincs256PrivateKey = keyPair.private as BCSphincs256PrivateKey
val pubKey: BCSphincs256PublicKey = keyPair.public as BCSphincs256PublicKey val pubKey: BCSphincs256PublicKey = keyPair.public as BCSphincs256PublicKey
@ -443,7 +452,7 @@ class CryptoUtilsTest {
@Test @Test
fun `RSA scheme finder by key type`() { fun `RSA scheme finder by key type`() {
val keyPairRSA = Crypto.generateKeyPair(Crypto.RSA_SHA256) val keyPairRSA = Crypto.generateKeyPair(RSA_SHA256)
val (privRSA, pubRSA) = keyPairRSA val (privRSA, pubRSA) = keyPairRSA
assertEquals(privRSA.algorithm, "RSA") assertEquals(privRSA.algorithm, "RSA")
assertEquals(pubRSA.algorithm, "RSA") assertEquals(pubRSA.algorithm, "RSA")
@ -451,7 +460,7 @@ class CryptoUtilsTest {
@Test @Test
fun `ECDSA secp256k1 scheme finder by key type`() { fun `ECDSA secp256k1 scheme finder by key type`() {
val keyPair = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256) val keyPair = Crypto.generateKeyPair(ECDSA_SECP256K1_SHA256)
val (privKey, pubKey) = keyPair val (privKey, pubKey) = keyPair
// Encode and decode private key. // Encode and decode private key.
@ -466,7 +475,7 @@ class CryptoUtilsTest {
@Test @Test
fun `ECDSA secp256r1 scheme finder by key type`() { fun `ECDSA secp256r1 scheme finder by key type`() {
val keyPairR1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256) val keyPairR1 = Crypto.generateKeyPair(ECDSA_SECP256R1_SHA256)
val (privR1, pubR1) = keyPairR1 val (privR1, pubR1) = keyPairR1
assertEquals(privR1.algorithm, "ECDSA") assertEquals(privR1.algorithm, "ECDSA")
assertEquals((privR1 as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256r1")) assertEquals((privR1 as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256r1"))
@ -476,7 +485,7 @@ class CryptoUtilsTest {
@Test @Test
fun `EdDSA scheme finder by key type`() { fun `EdDSA scheme finder by key type`() {
val keyPairEd = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512) val keyPairEd = Crypto.generateKeyPair(EDDSA_ED25519_SHA512)
val (privEd, pubEd) = keyPairEd val (privEd, pubEd) = keyPairEd
assertEquals(privEd.algorithm, "EdDSA") assertEquals(privEd.algorithm, "EdDSA")
@ -487,7 +496,7 @@ class CryptoUtilsTest {
@Test @Test
fun `SPHINCS-256 scheme finder by key type`() { fun `SPHINCS-256 scheme finder by key type`() {
val keyPairSP = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256) val keyPairSP = Crypto.generateKeyPair(SPHINCS256_SHA256)
val (privSP, pubSP) = keyPairSP val (privSP, pubSP) = keyPairSP
assertEquals(privSP.algorithm, "SPHINCS-256") assertEquals(privSP.algorithm, "SPHINCS-256")
assertEquals(pubSP.algorithm, "SPHINCS-256") assertEquals(pubSP.algorithm, "SPHINCS-256")
@ -495,7 +504,7 @@ class CryptoUtilsTest {
@Test @Test
fun `Automatic EdDSA key-type detection and decoding`() { fun `Automatic EdDSA key-type detection and decoding`() {
val keyPairEd = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512) val keyPairEd = Crypto.generateKeyPair(EDDSA_ED25519_SHA512)
val (privEd, pubEd) = keyPairEd val (privEd, pubEd) = keyPairEd
val encodedPrivEd = privEd.encoded val encodedPrivEd = privEd.encoded
val encodedPubEd = pubEd.encoded val encodedPubEd = pubEd.encoded
@ -511,7 +520,7 @@ class CryptoUtilsTest {
@Test @Test
fun `Automatic ECDSA secp256k1 key-type detection and decoding`() { fun `Automatic ECDSA secp256k1 key-type detection and decoding`() {
val keyPairK1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256) val keyPairK1 = Crypto.generateKeyPair(ECDSA_SECP256K1_SHA256)
val (privK1, pubK1) = keyPairK1 val (privK1, pubK1) = keyPairK1
val encodedPrivK1 = privK1.encoded val encodedPrivK1 = privK1.encoded
val encodedPubK1 = pubK1.encoded val encodedPubK1 = pubK1.encoded
@ -527,7 +536,7 @@ class CryptoUtilsTest {
@Test @Test
fun `Automatic ECDSA secp256r1 key-type detection and decoding`() { fun `Automatic ECDSA secp256r1 key-type detection and decoding`() {
val keyPairR1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256) val keyPairR1 = Crypto.generateKeyPair(ECDSA_SECP256R1_SHA256)
val (privR1, pubR1) = keyPairR1 val (privR1, pubR1) = keyPairR1
val encodedPrivR1 = privR1.encoded val encodedPrivR1 = privR1.encoded
val encodedPubR1 = pubR1.encoded val encodedPubR1 = pubR1.encoded
@ -543,7 +552,7 @@ class CryptoUtilsTest {
@Test @Test
fun `Automatic RSA key-type detection and decoding`() { fun `Automatic RSA key-type detection and decoding`() {
val keyPairRSA = Crypto.generateKeyPair(Crypto.RSA_SHA256) val keyPairRSA = Crypto.generateKeyPair(RSA_SHA256)
val (privRSA, pubRSA) = keyPairRSA val (privRSA, pubRSA) = keyPairRSA
val encodedPrivRSA = privRSA.encoded val encodedPrivRSA = privRSA.encoded
val encodedPubRSA = pubRSA.encoded val encodedPubRSA = pubRSA.encoded
@ -559,7 +568,7 @@ class CryptoUtilsTest {
@Test @Test
fun `Automatic SPHINCS-256 key-type detection and decoding`() { fun `Automatic SPHINCS-256 key-type detection and decoding`() {
val keyPairSP = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256) val keyPairSP = Crypto.generateKeyPair(SPHINCS256_SHA256)
val (privSP, pubSP) = keyPairSP val (privSP, pubSP) = keyPairSP
val encodedPrivSP = privSP.encoded val encodedPrivSP = privSP.encoded
val encodedPubSP = pubSP.encoded val encodedPubSP = pubSP.encoded
@ -575,12 +584,12 @@ class CryptoUtilsTest {
@Test @Test
fun `Failure test between K1 and R1 keys`() { fun `Failure test between K1 and R1 keys`() {
val keyPairK1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256) val keyPairK1 = Crypto.generateKeyPair(ECDSA_SECP256K1_SHA256)
val privK1 = keyPairK1.private val privK1 = keyPairK1.private
val encodedPrivK1 = privK1.encoded val encodedPrivK1 = privK1.encoded
val decodedPrivK1 = Crypto.decodePrivateKey(encodedPrivK1) val decodedPrivK1 = Crypto.decodePrivateKey(encodedPrivK1)
val keyPairR1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256) val keyPairR1 = Crypto.generateKeyPair(ECDSA_SECP256R1_SHA256)
val privR1 = keyPairR1.private val privR1 = keyPairR1.private
val encodedPrivR1 = privR1.encoded val encodedPrivR1 = privR1.encoded
val decodedPrivR1 = Crypto.decodePrivateKey(encodedPrivR1) val decodedPrivR1 = Crypto.decodePrivateKey(encodedPrivR1)
@ -590,7 +599,7 @@ class CryptoUtilsTest {
@Test @Test
fun `Decoding Failure on randomdata as key`() { fun `Decoding Failure on randomdata as key`() {
val keyPairK1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256) val keyPairK1 = Crypto.generateKeyPair(ECDSA_SECP256K1_SHA256)
val privK1 = keyPairK1.private val privK1 = keyPairK1.private
val encodedPrivK1 = privK1.encoded val encodedPrivK1 = privK1.encoded
@ -610,7 +619,7 @@ class CryptoUtilsTest {
@Test @Test
fun `Decoding Failure on malformed keys`() { fun `Decoding Failure on malformed keys`() {
val keyPairK1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256) val keyPairK1 = Crypto.generateKeyPair(ECDSA_SECP256K1_SHA256)
val privK1 = keyPairK1.private val privK1 = keyPairK1.private
val encodedPrivK1 = privK1.encoded val encodedPrivK1 = privK1.encoded
@ -630,25 +639,25 @@ class CryptoUtilsTest {
@Test @Test
fun `Check ECDSA public key on curve`() { fun `Check ECDSA public key on curve`() {
val keyPairK1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256) val keyPairK1 = Crypto.generateKeyPair(ECDSA_SECP256K1_SHA256)
val pubK1 = keyPairK1.public as BCECPublicKey val pubK1 = keyPairK1.public as BCECPublicKey
assertTrue(Crypto.publicKeyOnCurve(Crypto.ECDSA_SECP256K1_SHA256, pubK1)) assertTrue(Crypto.publicKeyOnCurve(ECDSA_SECP256K1_SHA256, pubK1))
// use R1 curve for check. // use R1 curve for check.
assertFalse(Crypto.publicKeyOnCurve(Crypto.ECDSA_SECP256R1_SHA256, pubK1)) assertFalse(Crypto.publicKeyOnCurve(ECDSA_SECP256R1_SHA256, pubK1))
// use ed25519 curve for check. // use ed25519 curve for check.
assertFalse(Crypto.publicKeyOnCurve(Crypto.EDDSA_ED25519_SHA512, pubK1)) assertFalse(Crypto.publicKeyOnCurve(EDDSA_ED25519_SHA512, pubK1))
} }
@Test @Test
fun `Check EdDSA public key on curve`() { fun `Check EdDSA public key on curve`() {
val keyPairEdDSA = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512) val keyPairEdDSA = Crypto.generateKeyPair(EDDSA_ED25519_SHA512)
val pubEdDSA = keyPairEdDSA.public val pubEdDSA = keyPairEdDSA.public
assertTrue(Crypto.publicKeyOnCurve(Crypto.EDDSA_ED25519_SHA512, pubEdDSA)) assertTrue(Crypto.publicKeyOnCurve(EDDSA_ED25519_SHA512, pubEdDSA))
// Use R1 curve for check. // Use R1 curve for check.
assertFalse(Crypto.publicKeyOnCurve(Crypto.ECDSA_SECP256R1_SHA256, pubEdDSA)) assertFalse(Crypto.publicKeyOnCurve(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) val pubKeySpec = EdDSAPublicKeySpec((EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec).curve.getZero(GroupElement.Representation.P3), EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec)
assertFalse(Crypto.publicKeyOnCurve(Crypto.EDDSA_ED25519_SHA512, EdDSAPublicKey(pubKeySpec))) assertFalse(Crypto.publicKeyOnCurve(EDDSA_ED25519_SHA512, EdDSAPublicKey(pubKeySpec)))
} }
@Test(expected = IllegalArgumentException::class) @Test(expected = IllegalArgumentException::class)
@ -658,12 +667,12 @@ class CryptoUtilsTest {
val pairSun = keyGen.generateKeyPair() val pairSun = keyGen.generateKeyPair()
val pubSun = pairSun.public val pubSun = pairSun.public
// Should fail as pubSun is not a BCECPublicKey. // Should fail as pubSun is not a BCECPublicKey.
Crypto.publicKeyOnCurve(Crypto.ECDSA_SECP256R1_SHA256, pubSun) Crypto.publicKeyOnCurve(ECDSA_SECP256R1_SHA256, pubSun)
} }
@Test @Test
fun `ECDSA secp256R1 deterministic key generation`() { fun `ECDSA secp256R1 deterministic key generation`() {
val (priv, pub) = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256) val (priv, pub) = Crypto.generateKeyPair(ECDSA_SECP256R1_SHA256)
val (dpriv, dpub) = Crypto.deriveKeyPair(priv, "seed-1".toByteArray()) val (dpriv, dpub) = Crypto.deriveKeyPair(priv, "seed-1".toByteArray())
// Check scheme. // Check scheme.
@ -673,11 +682,11 @@ class CryptoUtilsTest {
assertTrue(dpub is BCECPublicKey) assertTrue(dpub is BCECPublicKey)
assertEquals((dpriv as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256r1")) assertEquals((dpriv as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256r1"))
assertEquals((dpub 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(dpriv), ECDSA_SECP256R1_SHA256)
assertEquals(Crypto.findSignatureScheme(dpub), Crypto.ECDSA_SECP256R1_SHA256) assertEquals(Crypto.findSignatureScheme(dpub), ECDSA_SECP256R1_SHA256)
// Validate public key. // Validate public key.
assertTrue(Crypto.publicKeyOnCurve(Crypto.ECDSA_SECP256R1_SHA256, dpub)) assertTrue(Crypto.publicKeyOnCurve(ECDSA_SECP256R1_SHA256, dpub))
// Try to sign/verify. // Try to sign/verify.
val signedData = Crypto.doSign(dpriv, testBytes) val signedData = Crypto.doSign(dpriv, testBytes)
@ -704,7 +713,7 @@ class CryptoUtilsTest {
@Test @Test
fun `ECDSA secp256K1 deterministic key generation`() { fun `ECDSA secp256K1 deterministic key generation`() {
val (priv, pub) = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256) val (priv, pub) = Crypto.generateKeyPair(ECDSA_SECP256K1_SHA256)
val (dpriv, dpub) = Crypto.deriveKeyPair(priv, "seed-1".toByteArray()) val (dpriv, dpub) = Crypto.deriveKeyPair(priv, "seed-1".toByteArray())
// Check scheme. // Check scheme.
@ -714,11 +723,11 @@ class CryptoUtilsTest {
assertTrue(dpub is BCECPublicKey) assertTrue(dpub is BCECPublicKey)
assertEquals((dpriv as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256k1")) assertEquals((dpriv as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256k1"))
assertEquals((dpub 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(dpriv), ECDSA_SECP256K1_SHA256)
assertEquals(Crypto.findSignatureScheme(dpub), Crypto.ECDSA_SECP256K1_SHA256) assertEquals(Crypto.findSignatureScheme(dpub), ECDSA_SECP256K1_SHA256)
// Validate public key. // Validate public key.
assertTrue(Crypto.publicKeyOnCurve(Crypto.ECDSA_SECP256K1_SHA256, dpub)) assertTrue(Crypto.publicKeyOnCurve(ECDSA_SECP256K1_SHA256, dpub))
// Try to sign/verify. // Try to sign/verify.
val signedData = Crypto.doSign(dpriv, testBytes) val signedData = Crypto.doSign(dpriv, testBytes)
@ -745,7 +754,7 @@ class CryptoUtilsTest {
@Test @Test
fun `EdDSA ed25519 deterministic key generation`() { fun `EdDSA ed25519 deterministic key generation`() {
val (priv, pub) = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512) val (priv, pub) = Crypto.generateKeyPair(EDDSA_ED25519_SHA512)
val (dpriv, dpub) = Crypto.deriveKeyPair(priv, "seed-1".toByteArray()) val (dpriv, dpub) = Crypto.deriveKeyPair(priv, "seed-1".toByteArray())
// Check scheme. // Check scheme.
@ -755,11 +764,11 @@ class CryptoUtilsTest {
assertTrue(dpub is EdDSAPublicKey) assertTrue(dpub is EdDSAPublicKey)
assertEquals((dpriv as EdDSAKey).params, EdDSANamedCurveTable.getByName("ED25519")) assertEquals((dpriv as EdDSAKey).params, EdDSANamedCurveTable.getByName("ED25519"))
assertEquals((dpub 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(dpriv), EDDSA_ED25519_SHA512)
assertEquals(Crypto.findSignatureScheme(dpub), Crypto.EDDSA_ED25519_SHA512) assertEquals(Crypto.findSignatureScheme(dpub), EDDSA_ED25519_SHA512)
// Validate public key. // Validate public key.
assertTrue(Crypto.publicKeyOnCurve(Crypto.EDDSA_ED25519_SHA512, dpub)) assertTrue(Crypto.publicKeyOnCurve(EDDSA_ED25519_SHA512, dpub))
// Try to sign/verify. // Try to sign/verify.
val signedData = Crypto.doSign(dpriv, testBytes) val signedData = Crypto.doSign(dpriv, testBytes)
@ -786,110 +795,131 @@ class CryptoUtilsTest {
@Test @Test
fun `EdDSA ed25519 keyPair from entropy`() { fun `EdDSA ed25519 keyPair from entropy`() {
val keyPairPositive = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("10")) val keyPairPositive = Crypto.deriveKeyPairFromEntropy(EDDSA_ED25519_SHA512, BigInteger("10"))
assertEquals("DLBL3iHCp9uRReWhhCGfCsrxZZpfAm9h9GLbfN8ijqXTq", keyPairPositive.public.toStringShort()) assertEquals("DLBL3iHCp9uRReWhhCGfCsrxZZpfAm9h9GLbfN8ijqXTq", keyPairPositive.public.toStringShort())
val keyPairNegative = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("-10")) val keyPairNegative = Crypto.deriveKeyPairFromEntropy(EDDSA_ED25519_SHA512, BigInteger("-10"))
assertEquals("DLC5HXnYsJAFqmM9hgPj5G8whQ4TpyE9WMBssqCayLBwA2", keyPairNegative.public.toStringShort()) assertEquals("DLC5HXnYsJAFqmM9hgPj5G8whQ4TpyE9WMBssqCayLBwA2", keyPairNegative.public.toStringShort())
val keyPairZero = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("0")) val keyPairZero = Crypto.deriveKeyPairFromEntropy(EDDSA_ED25519_SHA512, BigInteger("0"))
assertEquals("DL4UVhGh4tqu1G86UVoGNaDDNCMsBtNHzE6BSZuNNJN7W2", keyPairZero.public.toStringShort()) assertEquals("DL4UVhGh4tqu1G86UVoGNaDDNCMsBtNHzE6BSZuNNJN7W2", keyPairZero.public.toStringShort())
val keyPairOne = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("1")) val keyPairOne = Crypto.deriveKeyPairFromEntropy(EDDSA_ED25519_SHA512, BigInteger("1"))
assertEquals("DL8EZUdHixovcCynKMQzrMWBnXQAcbVDHi6ArPphqwJVzq", keyPairOne.public.toStringShort()) assertEquals("DL8EZUdHixovcCynKMQzrMWBnXQAcbVDHi6ArPphqwJVzq", keyPairOne.public.toStringShort())
val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(258).minus(BigInteger.TEN)) val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(EDDSA_ED25519_SHA512, BigInteger("2").pow(258).minus(BigInteger.TEN))
assertEquals("DLB9K1UiBrWonn481z6NzkqoWHjMBXpfDeaet3wiwRNWSU", keyPairBiggerThan256bits.public.toStringShort()) assertEquals("DLB9K1UiBrWonn481z6NzkqoWHjMBXpfDeaet3wiwRNWSU", keyPairBiggerThan256bits.public.toStringShort())
// The underlying implementation uses the first 256 bytes of the entropy. Thus, 2^258-10 and 2^258-50 and 2^514-10 have the same impact. // The underlying implementation uses the first 256 bytes of the entropy. Thus, 2^258-10 and 2^258-50 and 2^514-10 have the same impact.
val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(258).minus(BigInteger("50"))) val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(EDDSA_ED25519_SHA512, BigInteger("2").pow(258).minus(BigInteger("50")))
assertEquals("DLB9K1UiBrWonn481z6NzkqoWHjMBXpfDeaet3wiwRNWSU", keyPairBiggerThan256bitsV2.public.toStringShort()) assertEquals("DLB9K1UiBrWonn481z6NzkqoWHjMBXpfDeaet3wiwRNWSU", keyPairBiggerThan256bitsV2.public.toStringShort())
val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(514).minus(BigInteger.TEN)) val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(EDDSA_ED25519_SHA512, BigInteger("2").pow(514).minus(BigInteger.TEN))
assertEquals("DLB9K1UiBrWonn481z6NzkqoWHjMBXpfDeaet3wiwRNWSU", keyPairBiggerThan512bits.public.toStringShort()) assertEquals("DLB9K1UiBrWonn481z6NzkqoWHjMBXpfDeaet3wiwRNWSU", keyPairBiggerThan512bits.public.toStringShort())
// Try another big number. // Try another big number.
val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(259).plus(BigInteger.ONE)) val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(EDDSA_ED25519_SHA512, BigInteger("2").pow(259).plus(BigInteger.ONE))
assertEquals("DL5tEFVMXMGrzwjfCAW34JjkhsRkPfFyJ38iEnmpB6L2Z9", keyPairBiggerThan258bits.public.toStringShort()) assertEquals("DL5tEFVMXMGrzwjfCAW34JjkhsRkPfFyJ38iEnmpB6L2Z9", keyPairBiggerThan258bits.public.toStringShort())
} }
@Test @Test
fun `ECDSA R1 keyPair from entropy`() { fun `ECDSA R1 keyPair from entropy`() {
val keyPairPositive = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("10")) val keyPairPositive = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256R1_SHA256, BigInteger("10"))
assertEquals("DLHDcxuSt9J3cbjd2Dsx4rAgYYA7BAP7A8VLrFiq1tH9yy", keyPairPositive.public.toStringShort()) assertEquals("DLHDcxuSt9J3cbjd2Dsx4rAgYYA7BAP7A8VLrFiq1tH9yy", keyPairPositive.public.toStringShort())
// The underlying implementation uses the hash of entropy if it is out of range 2 < entropy < N, where N the order of the group. // The underlying implementation uses the hash of entropy if it is out of range 2 < entropy < N, where N the order of the group.
val keyPairNegative = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("-10")) val keyPairNegative = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256R1_SHA256, BigInteger("-10"))
assertEquals("DLBASmjiMZuu1g3EtdHJxfSueXE8PRoUWbkdU61Qcnpamt", keyPairNegative.public.toStringShort()) assertEquals("DLBASmjiMZuu1g3EtdHJxfSueXE8PRoUWbkdU61Qcnpamt", keyPairNegative.public.toStringShort())
val keyPairZero = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("0")) val keyPairZero = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256R1_SHA256, BigInteger("0"))
assertEquals("DLH2FEHEnsT3MpCJt2gfyNjpqRqcBxeupK4YRPXvDsVEkb", keyPairZero.public.toStringShort()) assertEquals("DLH2FEHEnsT3MpCJt2gfyNjpqRqcBxeupK4YRPXvDsVEkb", keyPairZero.public.toStringShort())
// BigIntenger.Zero is out or range, so 1 and hash(1.toByteArray) would have the same impact. // BigIntenger.Zero is out or range, so 1 and hash(1.toByteArray) would have the same impact.
val zeroHashed = BigInteger(1, BigInteger("0").toByteArray().sha256().bytes) val zeroHashed = BigInteger(1, BigInteger("0").toByteArray().sha256().bytes)
// Check oneHashed < N (order of the group), otherwise we would need an extra hash. // Check oneHashed < N (order of the group), otherwise we would need an extra hash.
assertEquals(-1, zeroHashed.compareTo((Crypto.ECDSA_SECP256R1_SHA256.algSpec as ECNamedCurveParameterSpec).n)) assertEquals(-1, zeroHashed.compareTo((ECDSA_SECP256R1_SHA256.algSpec as ECNamedCurveParameterSpec).n))
val keyPairZeroHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, zeroHashed) val keyPairZeroHashed = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256R1_SHA256, zeroHashed)
assertEquals("DLH2FEHEnsT3MpCJt2gfyNjpqRqcBxeupK4YRPXvDsVEkb", keyPairZeroHashed.public.toStringShort()) assertEquals("DLH2FEHEnsT3MpCJt2gfyNjpqRqcBxeupK4YRPXvDsVEkb", keyPairZeroHashed.public.toStringShort())
val keyPairOne = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("1")) val keyPairOne = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256R1_SHA256, BigInteger("1"))
assertEquals("DLHrtKwjv6onq9HcrQDJPs8Cgtai5mZU5ZU6sb1ivJjx3z", keyPairOne.public.toStringShort()) assertEquals("DLHrtKwjv6onq9HcrQDJPs8Cgtai5mZU5ZU6sb1ivJjx3z", keyPairOne.public.toStringShort())
// BigIntenger.ONE is out or range, so 1 and hash(1.toByteArray) would have the same impact. // BigIntenger.ONE is out or range, so 1 and hash(1.toByteArray) would have the same impact.
val oneHashed = BigInteger(1, BigInteger("1").toByteArray().sha256().bytes) val oneHashed = BigInteger(1, BigInteger("1").toByteArray().sha256().bytes)
// Check oneHashed < N (order of the group), otherwise we would need an extra hash. // Check oneHashed < N (order of the group), otherwise we would need an extra hash.
assertEquals(-1, oneHashed.compareTo((Crypto.ECDSA_SECP256R1_SHA256.algSpec as ECNamedCurveParameterSpec).n)) assertEquals(-1, oneHashed.compareTo((ECDSA_SECP256R1_SHA256.algSpec as ECNamedCurveParameterSpec).n))
val keyPairOneHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, oneHashed) val keyPairOneHashed = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256R1_SHA256, oneHashed)
assertEquals("DLHrtKwjv6onq9HcrQDJPs8Cgtai5mZU5ZU6sb1ivJjx3z", keyPairOneHashed.public.toStringShort()) assertEquals("DLHrtKwjv6onq9HcrQDJPs8Cgtai5mZU5ZU6sb1ivJjx3z", keyPairOneHashed.public.toStringShort())
// 2 is in the range. // 2 is in the range.
val keyPairTwo = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2")) val keyPairTwo = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256R1_SHA256, BigInteger("2"))
assertEquals("DLFoz6txJ3vHcKNSM1vFxHJUoEQ69PorBwW64dHsAnEoZB", keyPairTwo.public.toStringShort()) assertEquals("DLFoz6txJ3vHcKNSM1vFxHJUoEQ69PorBwW64dHsAnEoZB", keyPairTwo.public.toStringShort())
// Try big numbers that are out of range. // Try big numbers that are out of range.
val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(258).minus(BigInteger.TEN)) val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256R1_SHA256, BigInteger("2").pow(258).minus(BigInteger.TEN))
assertEquals("DLBv6fZqaCTbE4L7sgjbt19biXHMgU9CzR5s8g8XBJjZ11", keyPairBiggerThan256bits.public.toStringShort()) assertEquals("DLBv6fZqaCTbE4L7sgjbt19biXHMgU9CzR5s8g8XBJjZ11", keyPairBiggerThan256bits.public.toStringShort())
val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(258).minus(BigInteger("50"))) val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256R1_SHA256, BigInteger("2").pow(258).minus(BigInteger("50")))
assertEquals("DLANmjhGSVdLyghxcPHrn3KuGatscf6LtvqifUDxw7SGU8", keyPairBiggerThan256bitsV2.public.toStringShort()) assertEquals("DLANmjhGSVdLyghxcPHrn3KuGatscf6LtvqifUDxw7SGU8", keyPairBiggerThan256bitsV2.public.toStringShort())
val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(514).minus(BigInteger.TEN)) val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256R1_SHA256, BigInteger("2").pow(514).minus(BigInteger.TEN))
assertEquals("DL9sKwMExBTD3MnJN6LWGqo496Erkebs9fxZtXLVJUBY9Z", keyPairBiggerThan512bits.public.toStringShort()) assertEquals("DL9sKwMExBTD3MnJN6LWGqo496Erkebs9fxZtXLVJUBY9Z", keyPairBiggerThan512bits.public.toStringShort())
val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(259).plus(BigInteger.ONE)) val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256R1_SHA256, BigInteger("2").pow(259).plus(BigInteger.ONE))
assertEquals("DLBwjWwPJSF9E7b1NWaSbEJ4oK8CF7RDGWd648TiBhZoL1", keyPairBiggerThan258bits.public.toStringShort()) assertEquals("DLBwjWwPJSF9E7b1NWaSbEJ4oK8CF7RDGWd648TiBhZoL1", keyPairBiggerThan258bits.public.toStringShort())
} }
@Test @Test
fun `ECDSA K1 keyPair from entropy`() { fun `ECDSA K1 keyPair from entropy`() {
val keyPairPositive = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("10")) val keyPairPositive = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256K1_SHA256, BigInteger("10"))
assertEquals("DL6pYKUgH17az8MLdonvvUtUPN8TqwpCGcdgLr7vg3skCU", keyPairPositive.public.toStringShort()) assertEquals("DL6pYKUgH17az8MLdonvvUtUPN8TqwpCGcdgLr7vg3skCU", keyPairPositive.public.toStringShort())
// The underlying implementation uses the hash of entropy if it is out of range 2 <= entropy < N, where N the order of the group. // The underlying implementation uses the hash of entropy if it is out of range 2 <= entropy < N, where N the order of the group.
val keyPairNegative = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("-10")) val keyPairNegative = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256K1_SHA256, BigInteger("-10"))
assertEquals("DLnpXhxece69Nyqgm3pPt3yV7ESQYDJKoYxs1hKgfBAEu", keyPairNegative.public.toStringShort()) assertEquals("DLnpXhxece69Nyqgm3pPt3yV7ESQYDJKoYxs1hKgfBAEu", keyPairNegative.public.toStringShort())
val keyPairZero = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("0")) val keyPairZero = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256K1_SHA256, BigInteger("0"))
assertEquals("DLBC28e18T6KsYwjTFfUWJfhvHjvYVapyVf6antnqUkbgd", keyPairZero.public.toStringShort()) assertEquals("DLBC28e18T6KsYwjTFfUWJfhvHjvYVapyVf6antnqUkbgd", keyPairZero.public.toStringShort())
// BigIntenger.Zero is out or range, so 1 and hash(1.toByteArray) would have the same impact. // BigIntenger.Zero is out or range, so 1 and hash(1.toByteArray) would have the same impact.
val zeroHashed = BigInteger(1, BigInteger("0").toByteArray().sha256().bytes) val zeroHashed = BigInteger(1, BigInteger("0").toByteArray().sha256().bytes)
// Check oneHashed < N (order of the group), otherwise we would need an extra hash. // Check oneHashed < N (order of the group), otherwise we would need an extra hash.
assertEquals(-1, zeroHashed.compareTo((Crypto.ECDSA_SECP256K1_SHA256.algSpec as ECNamedCurveParameterSpec).n)) assertEquals(-1, zeroHashed.compareTo((ECDSA_SECP256K1_SHA256.algSpec as ECNamedCurveParameterSpec).n))
val keyPairZeroHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, zeroHashed) val keyPairZeroHashed = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256K1_SHA256, zeroHashed)
assertEquals("DLBC28e18T6KsYwjTFfUWJfhvHjvYVapyVf6antnqUkbgd", keyPairZeroHashed.public.toStringShort()) assertEquals("DLBC28e18T6KsYwjTFfUWJfhvHjvYVapyVf6antnqUkbgd", keyPairZeroHashed.public.toStringShort())
val keyPairOne = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("1")) val keyPairOne = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256K1_SHA256, BigInteger("1"))
assertEquals("DLBimRXdEQhJUTpL6f9ri9woNdsze6mwkRrhsML13Eh7ET", keyPairOne.public.toStringShort()) assertEquals("DLBimRXdEQhJUTpL6f9ri9woNdsze6mwkRrhsML13Eh7ET", keyPairOne.public.toStringShort())
// BigIntenger.ONE is out or range, so 1 and hash(1.toByteArray) would have the same impact. // BigIntenger.ONE is out or range, so 1 and hash(1.toByteArray) would have the same impact.
val oneHashed = BigInteger(1, BigInteger("1").toByteArray().sha256().bytes) val oneHashed = BigInteger(1, BigInteger("1").toByteArray().sha256().bytes)
// Check oneHashed < N (order of the group), otherwise we would need an extra hash. // Check oneHashed < N (order of the group), otherwise we would need an extra hash.
assertEquals(-1, oneHashed.compareTo((Crypto.ECDSA_SECP256K1_SHA256.algSpec as ECNamedCurveParameterSpec).n)) assertEquals(-1, oneHashed.compareTo((ECDSA_SECP256K1_SHA256.algSpec as ECNamedCurveParameterSpec).n))
val keyPairOneHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, oneHashed) val keyPairOneHashed = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256K1_SHA256, oneHashed)
assertEquals("DLBimRXdEQhJUTpL6f9ri9woNdsze6mwkRrhsML13Eh7ET", keyPairOneHashed.public.toStringShort()) assertEquals("DLBimRXdEQhJUTpL6f9ri9woNdsze6mwkRrhsML13Eh7ET", keyPairOneHashed.public.toStringShort())
// 2 is in the range. // 2 is in the range.
val keyPairTwo = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2")) val keyPairTwo = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256K1_SHA256, BigInteger("2"))
assertEquals("DLG32UWaevGw9YY7w1Rf9mmK88biavgpDnJA9bG4GapVPs", keyPairTwo.public.toStringShort()) assertEquals("DLG32UWaevGw9YY7w1Rf9mmK88biavgpDnJA9bG4GapVPs", keyPairTwo.public.toStringShort())
// Try big numbers that are out of range. // Try big numbers that are out of range.
val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(258).minus(BigInteger.TEN)) val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256K1_SHA256, BigInteger("2").pow(258).minus(BigInteger.TEN))
assertEquals("DLGHsdv2xeAuM7n3sBc6mFfiphXe6VSf3YxqvviKDU6Vbd", keyPairBiggerThan256bits.public.toStringShort()) assertEquals("DLGHsdv2xeAuM7n3sBc6mFfiphXe6VSf3YxqvviKDU6Vbd", keyPairBiggerThan256bits.public.toStringShort())
val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(258).minus(BigInteger("50"))) val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256K1_SHA256, BigInteger("2").pow(258).minus(BigInteger("50")))
assertEquals("DL9yJfiNGqteRrKPjGUkRQkeqzuQ4kwcYQWMCi5YKuUHrk", keyPairBiggerThan256bitsV2.public.toStringShort()) assertEquals("DL9yJfiNGqteRrKPjGUkRQkeqzuQ4kwcYQWMCi5YKuUHrk", keyPairBiggerThan256bitsV2.public.toStringShort())
val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(514).minus(BigInteger.TEN)) val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256K1_SHA256, BigInteger("2").pow(514).minus(BigInteger.TEN))
assertEquals("DL3Wr5EQGrMTaKBy5XMvG8rvSfKX1AYZLCRU8kixGbxt1E", keyPairBiggerThan512bits.public.toStringShort()) assertEquals("DL3Wr5EQGrMTaKBy5XMvG8rvSfKX1AYZLCRU8kixGbxt1E", keyPairBiggerThan512bits.public.toStringShort())
val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(259).plus(BigInteger.ONE)) val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256K1_SHA256, BigInteger("2").pow(259).plus(BigInteger.ONE))
assertEquals("DL7NbssqvuuJ4cqFkkaVYu9j1MsVswESGgCfbqBS9ULwuM", keyPairBiggerThan258bits.public.toStringShort()) assertEquals("DL7NbssqvuuJ4cqFkkaVYu9j1MsVswESGgCfbqBS9ULwuM", keyPairBiggerThan258bits.public.toStringShort())
} }
@Test
fun `Ensure deterministic signatures of EdDSA, SPHINCS-256 and RSA PKCS1`() {
listOf(EDDSA_ED25519_SHA512, SPHINCS256_SHA256, RSA_SHA256)
.forEach { testDeterministicSignatures(it) }
}
private fun testDeterministicSignatures(signatureScheme: SignatureScheme) {
val privateKey = Crypto.generateKeyPair(signatureScheme).private
val signedData1stTime = Crypto.doSign(privateKey, testBytes)
val signedData2ndTime = Crypto.doSign(privateKey, testBytes)
assertEquals(OpaqueBytes(signedData1stTime), OpaqueBytes(signedData2ndTime))
// Try for the special case of signing a zero array.
val signedZeroArray1stTime = Crypto.doSign(privateKey, test100ZeroBytes)
val signedZeroArray2ndTime = Crypto.doSign(privateKey, test100ZeroBytes)
assertEquals(OpaqueBytes(signedZeroArray1stTime), OpaqueBytes(signedZeroArray2ndTime))
// Just in case, test that signatures of different messages are not the same.
assertNotEquals(OpaqueBytes(signedData1stTime), OpaqueBytes(signedZeroArray1stTime))
}
} }