Skip to content

Commit

Permalink
fix: Hide API members that should've been internal (#60)
Browse files Browse the repository at this point in the history
  • Loading branch information
gnarea authored Sep 18, 2023
1 parent e04f15c commit 944de30
Show file tree
Hide file tree
Showing 11 changed files with 74 additions and 200 deletions.
1 change: 0 additions & 1 deletion src/integrationTest/kotlin/MainTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import tech.relaycorp.veraid.dns.DnssecChain
import tech.relaycorp.veraid.pki.MemberCertificate
import tech.relaycorp.veraid.pki.MemberIdBundle
import tech.relaycorp.veraid.pki.OrgCertificate
import tech.relaycorp.veraid.pki.generateRSAKeyPair
import java.time.ZonedDateTime

class MainTest {
Expand Down
3 changes: 0 additions & 3 deletions src/integrationTest/kotlin/TestStubs.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import tech.relaycorp.veraid.pki.deserialiseRSAKeyPair
import tech.relaycorp.veraid.pki.generateRSAKeyPair

object TestStubs {
const val ORG_NAME = "lib-testing.veraid.net"

Expand Down
40 changes: 40 additions & 0 deletions src/integrationTest/kotlin/TestUtils.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,45 @@
import kotlinx.coroutines.delay
import org.bouncycastle.jce.provider.BouncyCastleProvider
import tech.relaycorp.veraid.dns.DnssecChain
import tech.relaycorp.veraid.pki.PkiException
import java.security.KeyFactory
import java.security.KeyPair
import java.security.KeyPairGenerator
import java.security.PrivateKey
import java.security.interfaces.RSAPrivateCrtKey
import java.security.spec.InvalidKeySpecException
import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.RSAPublicKeySpec

private val BC_PROVIDER = BouncyCastleProvider()

fun generateRSAKeyPair(): KeyPair {
val keyGen = KeyPairGenerator.getInstance("RSA", BC_PROVIDER)
keyGen.initialize(2048)
return keyGen.generateKeyPair()
}

private fun ByteArray.deserialisePrivateKey(algorithm: String): PrivateKey {
val privateKeySpec = PKCS8EncodedKeySpec(this)
val keyFactory = KeyFactory.getInstance(algorithm, BC_PROVIDER)
return try {
keyFactory.generatePrivate(privateKeySpec)
} catch (exc: InvalidKeySpecException) {
throw PkiException("Value is not a valid $algorithm private key", exc)
}
}

/**
* Deserialise the RSA key pair from a private key serialization.
*/
@Throws(PkiException::class)
fun ByteArray.deserialiseRSAKeyPair(): KeyPair {
val privateKey = this.deserialisePrivateKey("RSA") as RSAPrivateCrtKey
val keyFactory = KeyFactory.getInstance("RSA", BC_PROVIDER)
val publicKeySpec = RSAPublicKeySpec(privateKey.modulus, privateKey.publicExponent)
val publicKey = keyFactory.generatePublic(publicKeySpec)
return KeyPair(publicKey, privateKey)
}

suspend fun retrieveVeraidDnssecChain(orgName: String, maxRetries: Int): DnssecChain {
var result: DnssecChain? = null
Expand Down
101 changes: 15 additions & 86 deletions src/main/kotlin/tech/relaycorp/veraid/pki/Keys.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,110 +4,39 @@ package tech.relaycorp.veraid.pki

import tech.relaycorp.veraid.KeyAlgorithm
import tech.relaycorp.veraid.OrganisationKeySpec
import tech.relaycorp.veraid.utils.BC_PROVIDER
import tech.relaycorp.veraid.utils.Hash
import tech.relaycorp.veraid.utils.hash
import java.security.KeyFactory
import java.security.KeyPair
import java.security.KeyPairGenerator
import java.security.PrivateKey
import java.security.PublicKey
import java.security.interfaces.RSAPrivateCrtKey
import java.security.interfaces.RSAPublicKey
import java.security.spec.InvalidKeySpecException
import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.RSAPublicKeySpec
import java.security.spec.X509EncodedKeySpec
import java.util.Base64

/**
* RSA modulus.
*/
public enum class RsaModulus(internal val modulus: Int) {
RSA_2048(2048),
RSA_3072(3072),
RSA_4096(4096),
;
private val SUPPORTED_RSA_MODULI = setOf(2048, 3072, 4096)

internal companion object {
private val valueByModulus = RsaModulus.values().associateBy(RsaModulus::modulus)
operator fun get(modulus: Int): RsaModulus? = valueByModulus[modulus]
}
}

private val rsaModulusKeyAlgorithmMap: Map<RsaModulus, KeyAlgorithm> = mapOf(
RsaModulus.RSA_2048 to KeyAlgorithm.RSA_2048,
RsaModulus.RSA_3072 to KeyAlgorithm.RSA_3072,
RsaModulus.RSA_4096 to KeyAlgorithm.RSA_4096,
private val rsaModulusKeyAlgorithmMap: Map<Int, KeyAlgorithm> = mapOf(
2048 to KeyAlgorithm.RSA_2048,
3072 to KeyAlgorithm.RSA_3072,
4096 to KeyAlgorithm.RSA_4096,
)

private val rsaModulusHashMap: Map<RsaModulus, Hash> = mapOf(
RsaModulus.RSA_2048 to Hash.SHA_256,
RsaModulus.RSA_3072 to Hash.SHA_384,
RsaModulus.RSA_4096 to Hash.SHA_512,
private val rsaModulusHashMap: Map<Int, Hash> = mapOf(
2048 to Hash.SHA_256,
3072 to Hash.SHA_384,
4096 to Hash.SHA_512,
)

/**
* Generate an RSA key pair.
*
* @param modulus The modulus
*/
public fun generateRSAKeyPair(modulus: RsaModulus = RsaModulus.RSA_2048): KeyPair {
val keyGen = KeyPairGenerator.getInstance("RSA", BC_PROVIDER)
keyGen.initialize(modulus.modulus)
return keyGen.generateKeyPair()
}

internal val PublicKey.orgKeySpec: OrganisationKeySpec
get() {
if (this !is RSAPublicKey) {
throw PkiException("Key type (${this.algorithm}) is unsupported")
}
val modulusRaw = this.modulus.bitLength()
val modulusSanitised =
RsaModulus[modulusRaw] ?: throw PkiException("RSA modulus $modulusRaw is unsupported")
val modulus = this.modulus.bitLength()
if (modulus !in SUPPORTED_RSA_MODULI) {
throw PkiException("RSA modulus $modulus is unsupported")
}

val keyAlgorithm = rsaModulusKeyAlgorithmMap[modulusSanitised]!!
val hash = rsaModulusHashMap[modulusSanitised]!!
val keyAlgorithm = rsaModulusKeyAlgorithmMap[modulus]!!
val hash = rsaModulusHashMap[modulus]!!
val digest = this.encoded.hash(hash)
val digestHex = Base64.getEncoder().encodeToString(digest)
return OrganisationKeySpec(keyAlgorithm, digestHex)
}

/**
* Deserialise the RSA key pair from a private key serialization.
*/
@Throws(PkiException::class)
public fun ByteArray.deserialiseRSAKeyPair(): KeyPair {
val privateKey = this.deserialisePrivateKey("RSA") as RSAPrivateCrtKey
val keyFactory = KeyFactory.getInstance("RSA", BC_PROVIDER)
val publicKeySpec = RSAPublicKeySpec(privateKey.modulus, privateKey.publicExponent)
val publicKey = keyFactory.generatePublic(publicKeySpec)
return KeyPair(publicKey, privateKey)
}

private fun ByteArray.deserialisePrivateKey(algorithm: String): PrivateKey {
val privateKeySpec = PKCS8EncodedKeySpec(this)
val keyFactory = KeyFactory.getInstance(algorithm, BC_PROVIDER)
return try {
keyFactory.generatePrivate(privateKeySpec)
} catch (exc: InvalidKeySpecException) {
throw PkiException("Value is not a valid $algorithm private key", exc)
}
}

/**
* Deserialise the RSA public key from a public key serialisation.
*/
public fun ByteArray.deserialiseRSAPublicKey(): RSAPublicKey =
deserialisePublicKey("RSA") as RSAPublicKey

private fun ByteArray.deserialisePublicKey(algorithm: String): PublicKey {
val spec = X509EncodedKeySpec(this)
val factory = KeyFactory.getInstance(algorithm, BC_PROVIDER)
return try {
factory.generatePublic(spec)
} catch (exc: InvalidKeySpecException) {
throw PkiException("Value is not a valid $algorithm public key", exc)
}
}
2 changes: 1 addition & 1 deletion src/test/kotlin/tech/relaycorp/veraid/VeraStubs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package tech.relaycorp.veraid
import org.bouncycastle.asn1.ASN1ObjectIdentifier
import tech.relaycorp.veraid.pki.MemberCertificate
import tech.relaycorp.veraid.pki.OrgCertificate
import tech.relaycorp.veraid.pki.generateRSAKeyPair
import tech.relaycorp.veraid.testing.generateRSAKeyPair
import java.time.ZonedDateTime

const val ORG_NAME = "example.com"
Expand Down
109 changes: 3 additions & 106 deletions src/test/kotlin/tech/relaycorp/veraid/pki/KeysTest.kt
Original file line number Diff line number Diff line change
@@ -1,121 +1,18 @@
package tech.relaycorp.veraid.pki

import io.kotest.assertions.throwables.shouldThrow
import io.kotest.matchers.should
import io.kotest.matchers.shouldBe
import io.kotest.matchers.types.beInstanceOf
import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPrivateKey
import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPublicKey
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import tech.relaycorp.veraid.KeyAlgorithm
import tech.relaycorp.veraid.testing.generateRSAKeyPair
import tech.relaycorp.veraid.utils.BC_PROVIDER
import tech.relaycorp.veraid.utils.Hash
import tech.relaycorp.veraid.utils.hash
import java.security.KeyPairGenerator
import java.security.interfaces.RSAPrivateKey
import java.security.interfaces.RSAPublicKey
import java.security.spec.InvalidKeySpecException
import java.util.Base64

class KeysTest {
@Nested
inner class GenerateRSAKeyPair {
@Test
fun `Key pair should be returned when a valid modulus is passed`() {
val keyPair = generateRSAKeyPair(RsaModulus.RSA_4096)

keyPair.private should beInstanceOf<RSAPrivateKey>()
(keyPair.private as RSAPrivateKey).modulus.bitLength() shouldBe 4096

keyPair.public should beInstanceOf<RSAPublicKey>()
(keyPair.public as RSAPublicKey).modulus.bitLength() shouldBe 4096
}

@Test
fun `Modulus should be 2048 by default`() {
val keyPair = generateRSAKeyPair()

(keyPair.private as RSAPrivateKey).modulus.bitLength() shouldBe 2048

(keyPair.public as RSAPublicKey).modulus.bitLength() shouldBe 2048
}

@Test
fun `BouncyCastle provider should be used`() {
val keyPair = generateRSAKeyPair()

keyPair.public should beInstanceOf<BCRSAPublicKey>()
keyPair.private should beInstanceOf<BCRSAPrivateKey>()
}
}

@Nested
inner class DeserialiseRSAKeyPair {
@Test
fun `Deserialise invalid key`() {
val exception =
shouldThrow<PkiException> { "s".toByteArray().deserialiseRSAKeyPair() }

exception.message shouldBe "Value is not a valid RSA private key"
exception.cause should beInstanceOf<InvalidKeySpecException>()
}

@Test
fun `Deserialise valid private key`() {
val keyPair = generateRSAKeyPair()
val privateKeySerialised = keyPair.private.encoded

val keyPairDeserialised = privateKeySerialised.deserialiseRSAKeyPair()

keyPairDeserialised.private shouldBe keyPair.private
keyPairDeserialised.public shouldBe keyPair.public
}

@Test
fun `BouncyCastle provider should be used`() {
val keyPair = generateRSAKeyPair()
val privateKeySerialised = keyPair.private.encoded

val keyPairDeserialised = privateKeySerialised.deserialiseRSAKeyPair()

keyPairDeserialised.public should beInstanceOf<BCRSAPublicKey>()
keyPairDeserialised.private should beInstanceOf<BCRSAPrivateKey>()
}
}

@Nested
inner class DeserialiseRSAPublicKey {
@Test
fun `Deserialise invalid key`() {
val exception =
shouldThrow<PkiException> { "s".toByteArray().deserialiseRSAPublicKey() }

exception.message shouldBe "Value is not a valid RSA public key"
exception.cause should beInstanceOf<InvalidKeySpecException>()
}

@Test
fun `Deserialise valid key`() {
val keyPair = generateRSAKeyPair()
val publicKeySerialised = keyPair.public.encoded

val publicKeyDeserialised = publicKeySerialised.deserialiseRSAPublicKey()

publicKeyDeserialised shouldBe keyPair.public
}

@Test
fun `BouncyCastle provider should be used`() {
val keyPair = generateRSAKeyPair()
val publicKeySerialised = keyPair.public.encoded

val publicKeyDeserialised = publicKeySerialised.deserialiseRSAPublicKey()

publicKeyDeserialised should beInstanceOf<BCRSAPublicKey>()
}
}

@Nested
inner class OrgKeySpec {
@Test
Expand All @@ -130,7 +27,7 @@ class KeysTest {

@Test
fun `RSA-3072 should use SHA-384`() {
val publicKey = generateRSAKeyPair(RsaModulus.RSA_3072).public
val publicKey = generateRSAKeyPair(3072).public

val spec = publicKey.orgKeySpec

Expand All @@ -140,7 +37,7 @@ class KeysTest {

@Test
fun `RSA-4096 should use SHA-512`() {
val publicKey = generateRSAKeyPair(RsaModulus.RSA_4096).public
val publicKey = generateRSAKeyPair(4096).public

val spec = publicKey.orgKeySpec

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import tech.relaycorp.veraid.dns.DnssecChain
import tech.relaycorp.veraid.dns.InvalidChainException
import tech.relaycorp.veraid.dns.RECORD
import tech.relaycorp.veraid.dns.makeResponse
import tech.relaycorp.veraid.testing.generateRSAKeyPair
import tech.relaycorp.veraid.utils.asn1.ASN1Exception
import tech.relaycorp.veraid.utils.asn1.ASN1Utils
import tech.relaycorp.veraid.utils.x509.Certificate
Expand Down
11 changes: 11 additions & 0 deletions src/test/kotlin/tech/relaycorp/veraid/testing/Keys.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package tech.relaycorp.veraid.testing

import tech.relaycorp.veraid.utils.BC_PROVIDER
import java.security.KeyPair
import java.security.KeyPairGenerator

fun generateRSAKeyPair(modulus: Int = 2048): KeyPair {
val keyGen = KeyPairGenerator.getInstance("RSA", BC_PROVIDER)
keyGen.initialize(modulus)
return keyGen.generateKeyPair()
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.EnumSource
import tech.relaycorp.veraid.pki.generateRSAKeyPair
import tech.relaycorp.veraid.testing.generateRSAKeyPair
import tech.relaycorp.veraid.utils.Hash
import tech.relaycorp.veraid.utils.asn1.toDlTaggedObject
import tech.relaycorp.veraid.utils.generateWithDetachedPlaintext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder
import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import tech.relaycorp.veraid.pki.generateRSAKeyPair
import tech.relaycorp.veraid.testing.generateRSAKeyPair
import tech.relaycorp.veraid.utils.BC_PROVIDER
import tech.relaycorp.veraid.utils.Hash
import tech.relaycorp.veraid.utils.asn1.toDlTaggedObject
Expand Down
2 changes: 1 addition & 1 deletion unitTest.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
jacoco {
toolVersion = "0.8.8"
toolVersion = "0.8.10"
}

tasks.jacocoTestReport {
Expand Down

0 comments on commit 944de30

Please sign in to comment.