Skip to content

Commit

Permalink
Add RSA support to DefaultCryptoService
Browse files Browse the repository at this point in the history
  • Loading branch information
n0900 committed Nov 20, 2023
1 parent fdf13f6 commit 108a847
Show file tree
Hide file tree
Showing 21 changed files with 142 additions and 119 deletions.
2 changes: 1 addition & 1 deletion kmp-crypto
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,15 @@ class IssueCredentialProtocolTest : FreeSpec({
oobInvitation.shouldBeInstanceOf<InternalNextMessage.SendAndWrap>()
val invitationMessage = oobInvitation.message

val parsedInvitation = holderProtocol.parseMessage(invitationMessage, issuerCryptoService.toJsonWebKey())
val parsedInvitation = holderProtocol.parseMessage(invitationMessage, issuerCryptoService.jsonWebKey)
parsedInvitation.shouldBeInstanceOf<InternalNextMessage.SendAndWrap>()
val requestCredential = parsedInvitation.message

val parsedRequestCredential = issuerProtocol.parseMessage(requestCredential, holderCryptoService.toJsonWebKey())
val parsedRequestCredential = issuerProtocol.parseMessage(requestCredential, holderCryptoService.jsonWebKey)
parsedRequestCredential.shouldBeInstanceOf<InternalNextMessage.SendAndWrap>()
val issueCredential = parsedRequestCredential.message

val parsedIssueCredential = holderProtocol.parseMessage(issueCredential, issuerCryptoService.toJsonWebKey())
val parsedIssueCredential = holderProtocol.parseMessage(issueCredential, issuerCryptoService.jsonWebKey)
parsedIssueCredential.shouldBeInstanceOf<InternalNextMessage.Finished>()

val issuedCredential = parsedIssueCredential.lastMessage
Expand All @@ -71,11 +71,11 @@ class IssueCredentialProtocolTest : FreeSpec({
requestCredential.shouldBeInstanceOf<InternalNextMessage.SendAndWrap>()

val parsedRequestCredential =
issuerProtocol.parseMessage(requestCredential.message, holderCryptoService.toJsonWebKey())
issuerProtocol.parseMessage(requestCredential.message, holderCryptoService.jsonWebKey)
parsedRequestCredential.shouldBeInstanceOf<InternalNextMessage.SendAndWrap>()
val issueCredential = parsedRequestCredential.message

val parsedIssueCredential = holderProtocol.parseMessage(issueCredential, issuerCryptoService.toJsonWebKey())
val parsedIssueCredential = holderProtocol.parseMessage(issueCredential, issuerCryptoService.jsonWebKey)
parsedIssueCredential.shouldBeInstanceOf<InternalNextMessage.Finished>()

val issuedCredential = parsedIssueCredential.lastMessage
Expand All @@ -89,7 +89,7 @@ class IssueCredentialProtocolTest : FreeSpec({
threadId = uuid4().toString(),
attachment = JwmAttachment(id = uuid4().toString(), "mimeType", JwmAttachmentData())
),
issuerCryptoService.toJsonWebKey()
issuerCryptoService.jsonWebKey
)
parsed.shouldBeInstanceOf<InternalNextMessage.IncorrectState>()
}
Expand All @@ -99,7 +99,7 @@ class IssueCredentialProtocolTest : FreeSpec({
oobInvitation.shouldBeInstanceOf<InternalNextMessage.SendAndWrap>()
val invitationMessage = oobInvitation.message

val parsedInvitation = holderProtocol.parseMessage(invitationMessage, issuerCryptoService.toJsonWebKey())
val parsedInvitation = holderProtocol.parseMessage(invitationMessage, issuerCryptoService.jsonWebKey)
parsedInvitation.shouldBeInstanceOf<InternalNextMessage.SendAndWrap>()
val requestCredential = parsedInvitation.message

Expand All @@ -117,7 +117,7 @@ class IssueCredentialProtocolTest : FreeSpec({
)
)
val parsedRequestCredential =
issuerProtocol.parseMessage(wrongRequestCredential, holderCryptoService.toJsonWebKey())
issuerProtocol.parseMessage(wrongRequestCredential, holderCryptoService.jsonWebKey)
parsedRequestCredential.shouldBeInstanceOf<InternalNextMessage.SendProblemReport>()
val problemReport = parsedRequestCredential.message

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ class PresentProofMessengerTest : FreeSpec() {
verifierCryptoService = DefaultCryptoService()
issuerCryptoService = DefaultCryptoService()
holder = HolderAgent.newDefaultInstance(holderCryptoService)
verifier = VerifierAgent.newDefaultInstance(verifierCryptoService.identifier)
verifier = VerifierAgent.newDefaultInstance(verifierCryptoService.jsonWebKey.identifier)
issuer = IssuerAgent.newDefaultInstance(issuerCryptoService)
verifierChallenge = uuid4().toString()
holderServiceEndpoint = "https://example.com/present-proof?${uuid4()}"
}

"presentProof" {
val credentialSubject = randomCredential(holderCryptoService.identifier)
val credentialSubject = randomCredential(holderCryptoService.jsonWebKey.identifier)
holder.storeCredentials(issuer.issueCredential(credentialSubject).toStoreCredentialInput())
val holderMessenger = PresentProofMessenger.newHolderInstance(
holder = holder,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class PresentProofProtocolTest : FreeSpec({
holderCryptoService = DefaultCryptoService()
verifierCryptoService = DefaultCryptoService()
holder = HolderAgent.newDefaultInstance(holderCryptoService)
verifier = VerifierAgent.newDefaultInstance(verifierCryptoService.identifier)
verifier = VerifierAgent.newDefaultInstance(verifierCryptoService.jsonWebKey.identifier)
holderProtocol = PresentProofProtocol.newHolderInstance(
holder = holder,
serviceEndpoint = "https://example.com/",
Expand All @@ -59,16 +59,16 @@ class PresentProofProtocolTest : FreeSpec({
oobInvitation.shouldBeInstanceOf<InternalNextMessage.SendAndWrap>()
val invitationMessage = oobInvitation.message

val parsedInvitation = verifierProtocol.parseMessage(invitationMessage, holderCryptoService.toJsonWebKey())
val parsedInvitation = verifierProtocol.parseMessage(invitationMessage, holderCryptoService.jsonWebKey)
parsedInvitation.shouldBeInstanceOf<InternalNextMessage.SendAndWrap>()
val requestPresentation = parsedInvitation.message

val parsedRequestPresentation =
holderProtocol.parseMessage(requestPresentation, verifierCryptoService.toJsonWebKey())
holderProtocol.parseMessage(requestPresentation, verifierCryptoService.jsonWebKey)
parsedRequestPresentation.shouldBeInstanceOf<InternalNextMessage.SendAndWrap>()
val presentation = parsedRequestPresentation.message

val parsedPresentation = verifierProtocol.parseMessage(presentation, holderCryptoService.toJsonWebKey())
val parsedPresentation = verifierProtocol.parseMessage(presentation, holderCryptoService.jsonWebKey)
parsedPresentation.shouldBeInstanceOf<InternalNextMessage.Finished>()

val receivedPresentation = parsedPresentation.lastMessage
Expand All @@ -90,11 +90,11 @@ class PresentProofProtocolTest : FreeSpec({
requestPresentation.shouldBeInstanceOf<InternalNextMessage.SendAndWrap>()

val parsedRequestPresentation =
holderProtocol.parseMessage(requestPresentation.message, verifierCryptoService.toJsonWebKey())
holderProtocol.parseMessage(requestPresentation.message, verifierCryptoService.jsonWebKey)
parsedRequestPresentation.shouldBeInstanceOf<InternalNextMessage.SendAndWrap>()
val presentation = parsedRequestPresentation.message

val parsedPresentation = verifierProtocol.parseMessage(presentation, holderCryptoService.toJsonWebKey())
val parsedPresentation = verifierProtocol.parseMessage(presentation, holderCryptoService.jsonWebKey)
parsedPresentation.shouldBeInstanceOf<InternalNextMessage.Finished>()

val receivedPresentation = parsedPresentation.lastMessage
Expand All @@ -108,7 +108,7 @@ class PresentProofProtocolTest : FreeSpec({
parentThreadId = uuid4().toString(),
attachment = JwmAttachment(id = uuid4().toString(), "mimeType", JwmAttachmentData())
),
holderCryptoService.toJsonWebKey()
holderCryptoService.jsonWebKey
)
parsed.shouldBeInstanceOf<InternalNextMessage.IncorrectState>()
}
Expand All @@ -118,12 +118,12 @@ class PresentProofProtocolTest : FreeSpec({
oobInvitation.shouldBeInstanceOf<InternalNextMessage.SendAndWrap>()
val invitationMessage = oobInvitation.message

val parsedInvitation = verifierProtocol.parseMessage(invitationMessage, holderCryptoService.toJsonWebKey())
val parsedInvitation = verifierProtocol.parseMessage(invitationMessage, holderCryptoService.jsonWebKey)
parsedInvitation.shouldBeInstanceOf<InternalNextMessage.SendAndWrap>()
val requestPresentation = parsedInvitation.message

val parsedRequestPresentation =
holderProtocol.parseMessage(requestPresentation, verifierCryptoService.toJsonWebKey())
holderProtocol.parseMessage(requestPresentation, verifierCryptoService.jsonWebKey)
parsedRequestPresentation.shouldBeInstanceOf<InternalNextMessage.SendProblemReport>()
val problemReport = parsedRequestPresentation.message

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class OidcSiopVerifier(
) = OidcSiopVerifier(
verifier = verifier,
relyingPartyUrl = relyingPartyUrl,
agentPublicKey = cryptoService.toPublicKey(),
agentPublicKey = cryptoService.publicKey,
jwsService = jwsService,
verifierJwsService = verifierJwsService,
timeLeewaySeconds = timeLeewaySeconds,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class OidcSiopWallet(
clientId: String = "https://wallet.a-sit.at/"
) = OidcSiopWallet(
holder = holder,
agentPublicKey = cryptoService.toPublicKey(),
agentPublicKey = cryptoService.publicKey,
jwsService = jwsService,
verifierJwsService = verifierJwsService,
clock = clock,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,12 @@ class WalletService(
tokenResponse: TokenResponseParameters,
issuerMetadata: IssuerMetadata
): CredentialRequestParameters {
// TODO Specification is missing a proof type for binding method `cose_key`, so we'll use JWT
// TODO() Specification is missing a proof type for binding method `cose_key`, so we'll use JWT
val proof = CredentialRequestProof(
proofType = OpenIdConstants.ProofTypes.JWT,
jwt = jwsService.createSignedJwsAddingParams(
header = JwsHeader(
algorithm = cryptoService.jwsAlgorithm,
algorithm = cryptoService.algorithm,
type = OpenIdConstants.ProofTypes.JWT_HEADER_TYPE,
),
payload = JsonWebToken(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,15 @@ class OidcSiopIsoProtocolTest : FreeSpec({
relyingPartyUrl = "https://example.com/rp/${uuid4()}"
walletUrl = "https://example.com/wallet/${uuid4()}"
holderAgent = HolderAgent.newDefaultInstance(holderCryptoService)
verifierAgent = VerifierAgent.newDefaultInstance(verifierCryptoService.identifier)
verifierAgent = VerifierAgent.newDefaultInstance(verifierCryptoService.jsonWebKey.identifier)
runBlocking {
holderAgent.storeCredentials(
IssuerAgent.newDefaultInstance(
DefaultCryptoService(),
dataProvider = DummyCredentialDataProvider(),
).issueCredentialWithTypes(
holderAgent.identifier,
subjectPublicKey = holderCryptoService.toPublicKey().toCoseKey().getOrNull(),
subjectPublicKey = holderCryptoService.coseKey,
attributeTypes = listOf(ConstantIndex.MobileDrivingLicence2023.vcType)
).toStoreCredentialInput()
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class OidcSiopProtocolTest : FreeSpec({
relyingPartyUrl = "https://example.com/rp/${uuid4()}"
walletUrl = "https://example.com/wallet/${uuid4()}"
holderAgent = HolderAgent.newDefaultInstance(holderCryptoService)
verifierAgent = VerifierAgent.newDefaultInstance(verifierCryptoService.identifier)
verifierAgent = VerifierAgent.newDefaultInstance(verifierCryptoService.jsonWebKey.identifier)
runBlocking {
holderAgent.storeCredentials(
IssuerAgent.newDefaultInstance(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import at.asitplus.crypto.datatypes.Digest
import at.asitplus.crypto.datatypes.EcCurve
import at.asitplus.crypto.datatypes.JwsAlgorithm
import at.asitplus.crypto.datatypes.cose.CoseAlgorithm
import at.asitplus.crypto.datatypes.cose.CoseKey
import at.asitplus.crypto.datatypes.jws.JsonWebKey
import at.asitplus.crypto.datatypes.jws.JweAlgorithm
import at.asitplus.crypto.datatypes.jws.JweEncryption
import at.asitplus.crypto.datatypes.jws.toJsonWebKey
import kotlin.jvm.Transient

interface CryptoService {

Expand Down Expand Up @@ -44,22 +46,22 @@ interface CryptoService {

fun messageDigest(input: ByteArray, digest: Digest): KmmResult<ByteArray>

fun toPublicKey(): CryptoPublicKey
val publicKey: CryptoPublicKey

fun toJsonWebKey(): JsonWebKey
val jsonWebKey: JsonWebKey

val jwsAlgorithm: JwsAlgorithm
val algorithm: JwsAlgorithm

val coseAlgorithm: CoseAlgorithm
val coseKey: CoseKey

/**
* May be used in [at.asitplus.wallet.lib.cbor.CoseService] to transport the signing key for a COSE structure.
* a `null` value signifies that raw public keys are used and no certificate is present
*/
val certificate: ByteArray?

val identifier: String
get() = toJsonWebKey().identifier
// val identifier: String
// get() = jsonWebKey.identifier

}

Expand Down Expand Up @@ -96,9 +98,9 @@ data class AuthenticatedCiphertext(val ciphertext: ByteArray, val authtag: ByteA
}

interface EphemeralKeyHolder {
fun toPublicJsonWebKey(): JsonWebKey?
val publicJsonWebKey: JsonWebKey?
}

expect class DefaultCryptoService() : CryptoService

expect class DefaultVerifierCryptoService() : VerifierCryptoService
expect class DefaultVerifierCryptoService() : VerifierCryptoService
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class HolderAgent(
subjectCredentialStore = subjectCredentialStore,
jwsService = DefaultJwsService(cryptoService),
coseService = DefaultCoseService(cryptoService),
identifier = cryptoService.identifier,
identifier = cryptoService.jsonWebKey.identifier,
)

/**
Expand All @@ -56,7 +56,7 @@ class HolderAgent(
subjectCredentialStore = subjectCredentialStore,
jwsService = DefaultJwsService(cryptoService),
coseService = DefaultCoseService(cryptoService),
identifier = cryptoService.identifier,
identifier = cryptoService.jsonWebKey.identifier,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class IssuerAgent(
jwsService = DefaultJwsService(cryptoService),
coseService = DefaultCoseService(cryptoService),
dataProvider = dataProvider,
identifier = cryptoService.identifier,
identifier = cryptoService.jsonWebKey.identifier,
timePeriodProvider = timePeriodProvider,
clock = clock,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class VerifierAgent private constructor(
*/
fun newRandomInstance(): VerifierAgent = VerifierAgent(
validator = Validator.newDefaultInstance(),
identifier = DefaultCryptoService().identifier,
identifier = DefaultCryptoService().jsonWebKey.identifier,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package at.asitplus.wallet.lib.cbor

import at.asitplus.KmmResult
import at.asitplus.crypto.datatypes.CryptoPublicKey
import at.asitplus.crypto.datatypes.JwsAlgorithm
import at.asitplus.crypto.datatypes.cose.*
import at.asitplus.crypto.datatypes.jws.JwsExtensions.extractSignatureValues
import at.asitplus.wallet.lib.agent.CryptoService
Expand Down Expand Up @@ -52,9 +53,8 @@ class DefaultCoseService(private val cryptoService: CryptoService) : CoseService
addKeyId: Boolean,
addCertificate: Boolean,
): KmmResult<CoseSigned> {
var copyProtectedHeader = protectedHeader.copy(algorithm = cryptoService.coseAlgorithm)
if (addKeyId)
copyProtectedHeader = copyProtectedHeader.copy(kid = cryptoService.identifier.encodeToByteArray())
var copyProtectedHeader = protectedHeader.copy(algorithm = cryptoService.algorithm.toCoseAlgorithm())
if (addKeyId) copyProtectedHeader = copyProtectedHeader.copy(kid = cryptoService.jsonWebKey.identifier.encodeToByteArray())

val copyUnprotectedHeader = if (addCertificate) {
(unprotectedHeader ?: CoseHeader()).copy(certificateChain = cryptoService.certificate)
Expand All @@ -74,11 +74,16 @@ class DefaultCoseService(private val cryptoService: CryptoService) : CoseService
return KmmResult.failure(it)
}

val rawSignature: ByteArray = when (cryptoService.coseAlgorithm) {
CoseAlgorithm.ES256, CoseAlgorithm.ES384, CoseAlgorithm.ES512 -> signature.extractSignatureValues(
(cryptoService.toPublicKey() as CryptoPublicKey.Ec).curve.signatureLengthBytes / 2u
// val rawSignature: ByteArray = when (cryptoService.algorithm.toCoseAlgorithm()) {
// CoseAlgorithm.ES256, CoseAlgorithm.ES384, CoseAlgorithm.ES512 -> signature.extractSignatureValues(
// (cryptoService.publicKey as CryptoPublicKey.Ec).curve.signatureLengthBytes / 2u)
//
// else -> signature
// }
val rawSignature = when (cryptoService.algorithm) {
JwsAlgorithm.ES256, JwsAlgorithm.ES384, JwsAlgorithm.ES512 -> signature.extractSignatureValues(
(cryptoService.publicKey as CryptoPublicKey.Ec).curve.signatureLengthBytes / 2u
)

else -> signature
}

Expand All @@ -103,8 +108,11 @@ class DefaultVerifierCoseService(
payload = coseSigned.payload,
).serialize()

val algorithm = coseSigned.protectedHeader.value.algorithm
?: return KmmResult.failure(IllegalArgumentException("Algorithm not specified"))
val algorithm = coseSigned.protectedHeader.value.algorithm ?: return KmmResult.failure(
IllegalArgumentException(
"Algorithm not specified"
)
)
val publicKey = signer.toCryptoPublicKey().getOrElse {
return KmmResult.failure<Boolean>(IllegalArgumentException("Signer not convertible"))
.also { Napier.w("Could not convert signer to public key: $signer") }
Expand Down
Loading

0 comments on commit 108a847

Please sign in to comment.