Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate JCA signing from DER encoding to P1363 encoding #155

Draft
wants to merge 4 commits into
base: development
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
* Change CSR to take an actual `CryptoSignature` instead of a ByteArray
* Introduce shorthand to create CSR from TbsCSR
* Introduce shorthand to create certificate from TbsCertificate
* Add `SignatureAlgorithm.signWithJCA` and `SignatureAlgorithm.verifyWithJCA` helpers.
* Deprecate `SignatureAlgorithm.getJCASignatureInstance`, `CryptoSignature.parseFromJCA` etc as hazmat.


### 3.9.0 (Supreme 0.4.0)
Expand Down
8 changes: 4 additions & 4 deletions docs/docs/indispensable.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,10 @@ The following functions provide interop functionality with platform types.

### JVM/Android

* `SignatureAlgorithm.getJCASignatureInstance()` gets a pre-configured JCA instance for this algorithm
* `SpecializedSignatureAlgorithm.getJCASignatureInstance()` gets a pre-configured JCA instance for this algorithm
* `SignatureAlgorithm.getJCASignatureInstancePreHashed()` gets a pre-configured JCA instance for pre-hashed data for this algorithm
* `SpecializedSignatureAlgorithm.getJCASignatureInstancePreHashed()` gets a pre-configured JCA instance for pre-hashed data for this algorithm
* `SignatureAlgorithm.signWithJCA { initSign(key); update(data); sign() }` allows transparent signing using the JCA
* `SignatureAlgorithm.signWithJCAPreHashed { initSign(key); update(data); sign() }` allows transparent signing of pre-hashed data using the JCA
* `SignatureAlgorithm.signWithJCA { initSign(key); update(data); sign() }` allows transparent signing using the JCA
* `SignatureAlgorithm.signWithJCAPreHashed { initSign(key); update(data); sign() }` allows transparent signing of pre-hashed data using the JCA

* `Digest.jcaPSSParams` returns a sane default `PSSParameterSpec` for computing PSS signatures
* `Digest.jcaName` returns the JCA name of the digest
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package at.asitplus.signum

@RequiresOptIn(message = "Access to potentially hazardous platform-specific internals requires explicit opt-in. Specify @OptIn(HazardousMaterials::class). These accessors are unstable and may change without warning.")
/** This is an internal property. It is exposed if you know what you are doing. You very likely don't actually need it. */
annotation class HazardousMaterials
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class ObjectIdentifier @Throws(Asn1Exception::class) constructor(@Transient vara
if (nodes[0] * 40u > UByte.MAX_VALUE.toUInt()) throw Asn1Exception("first node too lage!")
//TODO more sanity checks

if (nodes.first() > 2u) throw Asn1Exception("OID must start with either 1 or 2")
if (nodes.first() > 2u) throw Asn1Exception("OID must start with 0, 1 or 2")
if (nodes[1] > 39u) throw Asn1Exception("Second segment must be <40")
}

Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class ECMathTest : FreeSpec({
// if new curves are ever added that violate this assumption,
// the algorithms in ECMath must be revisited!
// the current algorithm only works on this particular class of curve
curve.a == curve.coordinateCreator.fromInt(-3)
curve.a shouldBe curve.coordinateCreator.fromInt(-3)
}
}
"Addition: group axioms" - {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package at.asitplus.signum.indispensable

import at.asitplus.signum.HazardousMaterials
import at.asitplus.signum.indispensable.asn1.*
import at.asitplus.signum.indispensable.asn1.encoding.encodeToAsn1Primitive
import at.asitplus.signum.indispensable.io.ensureSize
Expand All @@ -26,10 +27,11 @@ import java.security.KeyPairGenerator
import java.security.PrivateKey
import java.security.interfaces.ECPublicKey

internal fun X509SignatureAlgorithm.getContentSigner(key: PrivateKey) =
getJCASignatureInstance().getOrThrow().algorithm.let {
JcaContentSignerBuilder(it).build(key)
}
@OptIn(HazardousMaterials::class)
internal fun X509SignatureAlgorithm.getContentSigner(key: PrivateKey) : ContentSigner {
val algorithm = getJCASignatureInstance(provider = null, forSigning = false).getOrThrow().algorithm
return JcaContentSignerBuilder(algorithm).build(key)
}

@OptIn(ExperimentalStdlibApi::class)
class Pkcs10CertificationRequestJvmTest : FreeSpec({
Expand Down Expand Up @@ -72,11 +74,12 @@ class Pkcs10CertificationRequestJvmTest : FreeSpec({
),
publicKey = cryptoPublicKey
)
val signed = signatureAlgorithm.getJCASignatureInstance().getOrThrow().apply {
val signed = signatureAlgorithm.signWithJCA {
initSign(keyPair.private)
update(tbsCsr.encodeToDer())
}.sign()
val csr = Pkcs10CertificationRequest(tbsCsr, signatureAlgorithm, CryptoSignature.parseFromJca(signed,signatureAlgorithm))
sign()
}.getOrThrow()
val csr = Pkcs10CertificationRequest(tbsCsr, signatureAlgorithm, signed)

val kotlinEncoded = csr.encodeToDer()

Expand Down Expand Up @@ -131,11 +134,12 @@ class Pkcs10CertificationRequestJvmTest : FreeSpec({
)
)
)
val signed = signatureAlgorithm.getJCASignatureInstance().getOrThrow().apply {
val signed = signatureAlgorithm.signWithJCA {
initSign(keyPair.private)
update(tbsCsr.encodeToTlv().derEncoded)
}.sign()
val csr = Pkcs10CertificationRequest(tbsCsr, signatureAlgorithm, CryptoSignature.parseFromJca(signed,signatureAlgorithm))
sign()
}.getOrThrow()
val csr = Pkcs10CertificationRequest(tbsCsr, signatureAlgorithm, signed)

val kotlinEncoded = csr.encodeToTlv().derEncoded
val jvmEncoded = bcCsr.encoded
Expand Down Expand Up @@ -197,11 +201,12 @@ class Pkcs10CertificationRequestJvmTest : FreeSpec({
)
)
)
val signed = signatureAlgorithm.getJCASignatureInstance().getOrThrow().apply {
val signed = signatureAlgorithm.signWithJCA {
initSign(keyPair.private)
update(tbsCsr.encodeToTlv().derEncoded)
}.sign()
val csr = Pkcs10CertificationRequest(tbsCsr, signatureAlgorithm, CryptoSignature.parseFromJca(signed,signatureAlgorithm))
sign()
}.getOrThrow()
val csr = Pkcs10CertificationRequest(tbsCsr, signatureAlgorithm, signed)

val kotlinEncoded = csr.encodeToTlv().derEncoded
val jvmEncoded = bcCsr.encoded
Expand Down Expand Up @@ -314,27 +319,31 @@ class Pkcs10CertificationRequestJvmTest : FreeSpec({
val signatureAlgorithm1 = X509SignatureAlgorithm.ES256
val signatureAlgorithm2 = X509SignatureAlgorithm.ES512

val signed = signatureAlgorithm1.getJCASignatureInstance().getOrThrow().apply {
val signed = signatureAlgorithm1.signWithJCA {
initSign(keyPair.private)
update(tbsCsr1.encodeToDer())
}.sign()
val signed1 = signatureAlgorithm1.getJCASignatureInstance().getOrThrow().apply {
sign()
}.getOrThrow()
val signed1 = signatureAlgorithm1.signWithJCA {
initSign(keyPair.private)
update(tbsCsr1.encodeToDer())
}.sign()
val signed11 = signatureAlgorithm2.getJCASignatureInstance().getOrThrow().apply {
sign()
}.getOrThrow()
val signed11 = signatureAlgorithm2.signWithJCA {
initSign(keyPair.private)
update(tbsCsr1.encodeToDer())
}.sign()
val signed2 = signatureAlgorithm1.getJCASignatureInstance().getOrThrow().apply {
sign()
}.getOrThrow()
val signed2 = signatureAlgorithm1.signWithJCA {
initSign(keyPair1.private)
update(tbsCsr2.encodeToDer())
}.sign()
sign()
}.getOrThrow()

val csr = Pkcs10CertificationRequest(tbsCsr1, signatureAlgorithm1, CryptoSignature.parseFromJca(signed,signatureAlgorithm1))
val csr1 = Pkcs10CertificationRequest(tbsCsr1, signatureAlgorithm1, CryptoSignature.parseFromJca(signed1,signatureAlgorithm1))
val csr11 = Pkcs10CertificationRequest(tbsCsr1, signatureAlgorithm2, CryptoSignature.parseFromJca(signed11,signatureAlgorithm2))
val csr2 = Pkcs10CertificationRequest(tbsCsr2, signatureAlgorithm1, CryptoSignature.parseFromJca(signed2,signatureAlgorithm1))
val csr = Pkcs10CertificationRequest(tbsCsr1, signatureAlgorithm1, signed)
val csr1 = Pkcs10CertificationRequest(tbsCsr1, signatureAlgorithm1, signed1)
val csr11 = Pkcs10CertificationRequest(tbsCsr1, signatureAlgorithm2, signed11)
val csr2 = Pkcs10CertificationRequest(tbsCsr2, signatureAlgorithm1, signed2)

csr shouldNotBe csr1
csr1 shouldBe csr1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,11 @@ class X509CertificateJvmTest : FreeSpec({
subjectName = listOf(RelativeDistinguishedName(AttributeTypeAndValue.CommonName(Asn1String.UTF8(commonName)))),
publicKey = cryptoPublicKey
)
val signed = signatureAlgorithm.getJCASignatureInstance().getOrThrow().apply {
val test = signatureAlgorithm.signWithJCA {
initSign(keyPair.private)
update(tbsCertificate.encodeToTlv().derEncoded)
}.sign()
val test = CryptoSignature.decodeFromDer(signed)
sign()
}.getOrThrow()
val x509Certificate = X509Certificate(tbsCertificate, signatureAlgorithm, test)

val kotlinEncoded = x509Certificate.encodeToDer()
Expand Down Expand Up @@ -123,11 +123,11 @@ class X509CertificateJvmTest : FreeSpec({
subjectName = listOf(RelativeDistinguishedName(AttributeTypeAndValue.CommonName(Asn1String.UTF8(commonName)))),
publicKey = cryptoPublicKey
)
val signed = signatureAlgorithm.getJCASignatureInstance().getOrThrow().apply {
val test = signatureAlgorithm.signWithJCA {
initSign(keyPair.private)
update(tbsCertificate.encodeToTlv().derEncoded)
}.sign()
val test = CryptoSignature.decodeFromDer(signed)
sign()
}.getOrThrow()
val x509Certificate = X509Certificate(tbsCertificate, signatureAlgorithm, test)

repeat(500) {
Expand Down Expand Up @@ -275,24 +275,21 @@ class X509CertificateJvmTest : FreeSpec({
X509Certificate
*/

val signed1 = signatureAlgorithm256.getJCASignatureInstance().getOrThrow().apply {
val signature1 = signatureAlgorithm256.signWithJCA {
initSign(keyPair.private)
update(tbsCertificate1.encodeToTlv().derEncoded)
}.sign()
val signed2 = signatureAlgorithm256.getJCASignatureInstance().getOrThrow().apply {
sign()
}.getOrThrow()
val signature2 = signatureAlgorithm256.signWithJCA {
initSign(keyPair.private)
update(tbsCertificate2.encodeToTlv().derEncoded)
}.sign()
val signed3 = signatureAlgorithm512.getJCASignatureInstance().getOrThrow().apply {
sign()
}.getOrThrow()
val signature3 = signatureAlgorithm512.signWithJCA {
initSign(keyPair.private)
update(tbsCertificate3.encodeToTlv().derEncoded)
}.sign()
val signature1 =
(CryptoSignature.decodeFromDer(signed1) as CryptoSignature.EC.IndefiniteLength).withCurve(ECCurve.SECP_256_R_1)
val signature2 =
(CryptoSignature.decodeFromDer(signed2) as CryptoSignature.EC.IndefiniteLength).withCurve(ECCurve.SECP_256_R_1)
val signature3 =
(CryptoSignature.decodeFromDer(signed3) as CryptoSignature.EC.IndefiniteLength).withCurve(ECCurve.SECP_521_R_1)
sign()
}.getOrThrow()
val x509Certificate1 = X509Certificate(tbsCertificate1, signatureAlgorithm256, signature1)
val x509Certificate2 = X509Certificate(tbsCertificate2, signatureAlgorithm256, signature2)
val x509Certificate3 = X509Certificate(tbsCertificate3, signatureAlgorithm512, signature3)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package at.asitplus.signum.supreme.hazmat

import at.asitplus.signum.indispensable.getJCASignatureInstance
import at.asitplus.signum.supreme.HazardousMaterials
import at.asitplus.signum.HazardousMaterials
import at.asitplus.signum.supreme.os.AndroidKeystoreSigner
import at.asitplus.signum.supreme.sign.AndroidEphemeralSigner
import at.asitplus.signum.supreme.sign.EphemeralKey
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import at.asitplus.KmmResult
import at.asitplus.catching
import at.asitplus.signum.HazardousMaterials
import at.asitplus.signum.indispensable.*
import at.asitplus.signum.indispensable.asn1.Asn1StructuralException
import at.asitplus.signum.indispensable.pki.X509Certificate
Expand Down Expand Up @@ -352,8 +353,9 @@ sealed class AndroidKeystoreSigner private constructor(
}
}

@OptIn(HazardousMaterials::class)
internal suspend fun getJCASignature(signingConfig: AndroidSignerSigningConfiguration): Signature =
signatureAlgorithm.getJCASignatureInstance().getOrThrow().also {
signatureAlgorithm.getJCASignatureInstance(provider = null, forSigning = true).getOrThrow().also {
if (needsAuthenticationForEveryUse) {
it.initSign(jcaPrivateKey)
attemptBiometry(DSL.ConfigStack(signingConfig.unlockPrompt.v, config.unlockPrompt.v), CryptoObject(it))
Expand All @@ -373,6 +375,7 @@ sealed class AndroidKeystoreSigner private constructor(
}
}

@OptIn(HazardousMaterials::class)
final override suspend fun sign(
data: SignatureInput,
configure: DSLConfigureFn<AndroidSignerSigningConfiguration>
Expand All @@ -382,7 +385,7 @@ sealed class AndroidKeystoreSigner private constructor(
.let { data.data.forEach(it::update); it.sign() }

return@signCatching when (this@AndroidKeystoreSigner) {
is ECDSA -> CryptoSignature.EC.parseFromJca(jcaSig).withCurve(publicKey.curve)
is ECDSA -> CryptoSignature.EC.parseFromJca(jcaSig)
is RSA -> CryptoSignature.RSAorHMAC.parseFromJca(jcaSig)
}
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@ package at.asitplus.signum.supreme.sign

import android.security.keystore.KeyProperties
import at.asitplus.signum.indispensable.CryptoPublicKey
import at.asitplus.signum.indispensable.CryptoSignature
import at.asitplus.signum.indispensable.SignatureAlgorithm
import at.asitplus.signum.indispensable.fromJcaPublicKey
import at.asitplus.signum.indispensable.getJCASignatureInstancePreHashed
import at.asitplus.signum.indispensable.jcaName
import at.asitplus.signum.indispensable.parseFromJca
import at.asitplus.signum.supreme.signCatching
import at.asitplus.signum.indispensable.signWithJCAPreHashed
import at.asitplus.signum.supreme.asSignatureResult
import com.ionspin.kotlin.bignum.integer.base63.toJavaBigInteger
import java.security.KeyPairGenerator
import java.security.PrivateKey
Expand All @@ -20,30 +18,22 @@ actual class EphemeralSignerConfiguration internal actual constructor(): Ephemer

sealed class AndroidEphemeralSigner (internal val privateKey: PrivateKey) : Signer {
override val mayRequireUserUnlock = false
override suspend fun sign(data: SignatureInput) = signCatching {
val inputData = data.convertTo(signatureAlgorithm.preHashedSignatureFormat).getOrThrow()
signatureAlgorithm.getJCASignatureInstancePreHashed(provider = null).getOrThrow().run {
initSign(privateKey)
inputData.data.forEach { update(it) }
sign().let(::parseFromJca)
}
}

protected abstract fun parseFromJca(bytes: ByteArray): CryptoSignature.RawByteEncodable
override suspend fun sign(data: SignatureInput) =
data.convertTo(signatureAlgorithm.preHashedSignatureFormat).transform { inputData ->
signatureAlgorithm.signWithJCAPreHashed(provider = null) {
initSign(privateKey)
inputData.data.forEach { update(it) }
sign()
}
}.asSignatureResult()

class EC (config: EphemeralSignerConfiguration, privateKey: PrivateKey,
override val publicKey: CryptoPublicKey.EC, override val signatureAlgorithm: SignatureAlgorithm.ECDSA)
: AndroidEphemeralSigner(privateKey), Signer.ECDSA {

override fun parseFromJca(bytes: ByteArray) = CryptoSignature.EC.parseFromJca(bytes).withCurve(publicKey.curve)
}
: AndroidEphemeralSigner(privateKey), Signer.ECDSA

class RSA (config: EphemeralSignerConfiguration, privateKey: PrivateKey,
override val publicKey: CryptoPublicKey.RSA, override val signatureAlgorithm: SignatureAlgorithm.RSA)
: AndroidEphemeralSigner(privateKey), Signer.RSA {

override fun parseFromJca(bytes: ByteArray) = CryptoSignature.RSAorHMAC.parseFromJca(bytes)
}
: AndroidEphemeralSigner(privateKey), Signer.RSA
}

internal actual fun makeEphemeralKey(configuration: EphemeralSigningKeyConfiguration) : EphemeralKey =
Expand Down
Loading
Loading