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

WAL-199 - Enhance error handling in waltid library #762

Merged
merged 34 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
cbaa402
feat : added common module for waltid-libraries
SuperBatata Sep 17, 2024
88450ed
fix : removed unnecessary library-common code
SuperBatata Sep 17, 2024
b533069
feat : added CryptoExceptions class
SuperBatata Sep 17, 2024
7cb412f
feat : added dependencies
SuperBatata Sep 17, 2024
45ef960
dep : added library common to crypto
SuperBatata Sep 18, 2024
adc5461
feat : created cryptoExceptions
SuperBatata Sep 18, 2024
3153a57
fix : added custom exceptions to KeyManager.kt
SuperBatata Sep 18, 2024
6348291
feat : create custom exception for crypto
SuperBatata Sep 18, 2024
6c4a5e0
update gradle file
SuperBatata Sep 18, 2024
5940909
fix : switch to multiplatform in library commons
SuperBatata Sep 18, 2024
82936a9
feat : added library common dependencies
SuperBatata Sep 18, 2024
af365da
feat : added support to CryptoExceptions in statuspage
SuperBatata Sep 18, 2024
3716b71
feat : moved to commonMain
SuperBatata Sep 18, 2024
10b301e
fix :added missing library commons dependency in jvmMain
SuperBatata Sep 19, 2024
599df67
ci: added waltid-libraries to dockerfiles
SuperBatata Sep 19, 2024
13ddf97
fix:removed library-commons from jvm
SuperBatata Sep 19, 2024
65ce461
fix: fixed dockerfiles missing library-commons
SuperBatata Sep 19, 2024
0779989
feat: extend CryptoExceptions.kt with more custom exceptions
SuperBatata Sep 22, 2024
08350fa
feat: update KeyManager.kt with custom exceptions
SuperBatata Sep 22, 2024
34357b7
feat: update OCIKeyRestApi.kt with custom exceptions
SuperBatata Sep 22, 2024
1a7349e
feat: update TSE key with custom exceptions
SuperBatata Sep 22, 2024
9469489
style: format code
SuperBatata Sep 22, 2024
77c4bbf
fix: delete unused main file
SuperBatata Sep 22, 2024
f495ab4
Merge branch 'main' into feat/error-handling-library
SuperBatata Sep 22, 2024
0ab5914
Merge branch 'main' into feat/error-handling-library
SuperBatata Sep 23, 2024
db5adf0
style:removed comments
SuperBatata Sep 24, 2024
3e136d4
feat: add TSEObject and enhance custom exceptions
SuperBatata Sep 24, 2024
6a61d65
fix: update throwable with newly modified exceptions
SuperBatata Sep 24, 2024
1b2bed5
refactor: refactoring TSEAuth.kt
SuperBatata Sep 24, 2024
ad506bb
fix: update custom exceptions for the TSE key
SuperBatata Sep 24, 2024
71cb52a
Merge branch 'main' into feat/error-handling-library
SuperBatata Sep 24, 2024
00917ec
fix: updated group in build.gradle.kts
SuperBatata Sep 25, 2024
70eb319
style: removed comments and prints
SuperBatata Sep 25, 2024
957150c
fix: grammar correction in exception
SuperBatata Sep 25, 2024
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
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ val modules = listOf(

"$libraries:waltid-did",
"$libraries:waltid-java-compat",
"$libraries:waltid-library-commons",

// Service commons
"$services:waltid-service-commons",
Expand Down
35 changes: 4 additions & 31 deletions waltid-libraries/crypto/waltid-crypto/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,7 @@ kotlin {
}
}
js(IR) {
moduleName = "crypto"/*browser {
commonWebpackConfig {
cssSupport {
enabled.set(true)
}
}
}*/
moduleName = "crypto"
nodejs {
generateTypeScriptDefinitions()
testTask {
Expand All @@ -87,8 +81,6 @@ kotlin {
}
binaries.library()
}
// androidTarget()

if (enableIosBuild) {
iosArm64()
iosSimulatorArm64()
Expand Down Expand Up @@ -130,8 +122,10 @@ kotlin {
// Logging
implementation("io.github.oshai:kotlin-logging:7.0.0")

// suspend-transform plugin annotations (required in the current version to avoid "compileOnly" warning)
implementation("${SuspendTransPluginConstants.ANNOTATION_GROUP}:${SuspendTransPluginConstants.ANNOTATION_NAME}:${SuspendTransPluginConstants.ANNOTATION_VERSION}")

implementation(project(":waltid-libraries:waltid-library-commons"))

}
}
val commonTest by getting {
Expand All @@ -157,8 +151,6 @@ kotlin {
// JOSE
implementation("com.nimbusds:nimbus-jose-jwt:9.40")

// Multibase
// implementation("com.github.multiformats:java-multibase:v1.1.1")
}
}
val jvmTest by getting {
Expand All @@ -179,26 +171,13 @@ kotlin {
// JOSE
implementation(npm("jose", "5.2.3"))

// Multibase
// implementation(npm("multiformats", "12.1.2"))
}
}
val jsTest by getting {
dependencies {
implementation(kotlin("test-js"))
}
}
// val androidMain by getting {
// dependencies {
// implementation("io.ktor:ktor-client-android:2.3.10")
// }
// }
// val androidUnitTest by getting {
// dependencies {
// implementation(kotlin("test"))
// }
// }

if (enableIosBuild) {
val iosArm64Main by getting
val iosSimulatorArm64Main by getting
Expand Down Expand Up @@ -242,15 +221,9 @@ kotlin {
val secretMavenUsername = envUsername ?: usernameFile.let {
if (it.isFile) it.readLines().first() else ""
}
//println("Deploy username length: ${secretMavenUsername.length}")
val secretMavenPassword = envPassword ?: passwordFile.let {
if (it.isFile) it.readLines().first() else ""
}

//if (secretMavenPassword.isBlank()) {
// println("WARNING: Password is blank!")
//}

credentials {
username = secretMavenUsername
password = secretMavenPassword
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package id.walt.crypto.keys

import id.walt.commons.exceptions.KeyBackendNotSupportedException
import id.walt.commons.exceptions.KeyTypeMissingException
import id.walt.commons.exceptions.KeyTypeNotSupportedException
import id.walt.crypto.keys.jwk.JWKKey
import id.walt.crypto.keys.oci.OCIKeyRestApi
import id.walt.crypto.keys.tse.TSEKey
Expand All @@ -17,7 +20,7 @@ object KeyManager {
val types = HashMap<String, KType>()
val keyTypeGeneration = HashMap<String, suspend (KeyGenerationRequest) -> Key>()

fun getRegisteredKeyType(type: String): KType = types[type] ?: error("Unknown key type: $type")
fun getRegisteredKeyType(type: String): KType = types[type] ?: throw KeyTypeNotSupportedException(type)

init {
register<JWKKey>("jwk") { generateRequest: KeyGenerationRequest -> JWKKey.generate(generateRequest.keyType) }
Expand All @@ -40,14 +43,19 @@ object KeyManager {
keyTypeGeneration[typeId] = createFunction
}

inline fun <reified T : Key> register(typeId: String, noinline createFunction: suspend (KeyGenerationRequest) -> T) {
inline fun <reified T : Key> register(
typeId: String,
noinline createFunction: suspend (KeyGenerationRequest) -> T
) {
keyManagerLogger().trace { "Registering key type \"$typeId\" to ${T::class.simpleName}..." }
val type = typeOf<T>()
registerByType(type, typeId, createFunction)
}

suspend fun createKey(generationRequest: KeyGenerationRequest): Key {
val function = keyTypeGeneration[generationRequest.backend] ?: error("No such key backend registered: ${generationRequest.backend}")
val function = keyTypeGeneration[generationRequest.backend] ?: throw KeyBackendNotSupportedException(
generationRequest.backend
)
log.debug { "Creating key with generation request: $generationRequest" }

return function.invoke(generationRequest)
Expand All @@ -61,7 +69,7 @@ object KeyManager {
val type = getRegisteredKeyType(it)
val fields = json.filterKeys { it != "type" }.mapValues { it.value }
Json.decodeFromJsonElement(serializer(type), JsonObject(fields)) as Key
}?.apply { init() } ?: error("No type in serialized key")
}?.apply { init() } ?: throw KeyTypeMissingException()

fun resolveSerializedKeyBlocking(jsonString: String) =
resolveSerializedKeyBlocking(json = Json.parseToJsonElement(jsonString).jsonObject)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import id.walt.crypto.keys.JwkKeyMeta
import id.walt.crypto.keys.Key
import id.walt.crypto.keys.KeyType
import kotlinx.serialization.KSerializer
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encodeToString
import kotlinx.serialization.encoding.Decoder
Expand Down
SuperBatata marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package id.walt.crypto.keys.oci

import id.walt.commons.exceptions.KeyNotFoundException
import id.walt.commons.exceptions.KeyTypeNotSupportedException
import id.walt.commons.exceptions.SigningException
import id.walt.commons.exceptions.VerificationException
import id.walt.crypto.keys.EccUtils
import id.walt.crypto.keys.Key
import id.walt.crypto.keys.KeyType
Expand Down Expand Up @@ -72,9 +76,9 @@ class OCIKeyRestApi(
private suspend fun retrievePublicKey(): Key {
val keyData = getKeys(vaultKeyId, config.managementEndpoint, config.tenancyOcid, config.signingKeyPem)
val key =
keyData.firstOrNull { it["id"]?.jsonPrimitive?.content == id } ?: throw IllegalArgumentException("Key with id $id not found")
keyData.firstOrNull { it["id"]?.jsonPrimitive?.content == id } ?: throw KeyNotFoundException(id)
val keyVersion = getKeyVersion(id, vaultKeyId, config.managementEndpoint, config.signingKeyPem)
val keyId = key["id"]?.jsonPrimitive?.content ?: ""
val keyId = key["id"]?.jsonPrimitive?.content ?: throw KeyNotFoundException(id)

return getOCIPublicKey(
keyId, vaultKeyId, config.managementEndpoint, keyVersion, config.signingKeyPem
Expand Down Expand Up @@ -119,7 +123,7 @@ class OCIKeyRestApi(
when (keyType) {
KeyType.secp256r1 -> "ECDSA_SHA_256"
KeyType.RSA -> "SHA_256_RSA_PKCS_PSS"
else -> throw NotImplementedError("Keytype not yet implemented: $keyType")
else -> throw KeyTypeNotSupportedException(keyType.name)
}
}

Expand Down Expand Up @@ -154,7 +158,7 @@ class OCIKeyRestApi(
header("x-content-sha256", calculateSHA256(requestBody))
setBody(requestBody)
}.ociJsonDataBody().jsonObject["signature"]?.jsonPrimitive?.content?.decodeFromBase64()
response ?: error("No signature returned from OCI.")
response ?: throw SigningException("No signature returned from OCI.")
}
}

Expand Down Expand Up @@ -217,7 +221,7 @@ class OCIKeyRestApi(
setBody(requestBody)
}.ociJsonDataBody().jsonObject["isSignatureValid"]?.jsonPrimitive?.boolean ?: false
return if (response) Result.success(detachedPlaintext)
else Result.failure(Exception("Signature is not valid"))
else Result.failure(VerificationException("Signature is not valid"))
}

@JvmBlocking
Expand Down Expand Up @@ -273,14 +277,13 @@ class OCIKeyRestApi(
private fun keyTypeToOciKeyMapping(type: KeyType) = when (type) {
KeyType.secp256r1 -> "ECDSA"
KeyType.RSA -> "RSA"
KeyType.secp256k1 -> throw IllegalArgumentException("Not supported: $type")
KeyType.Ed25519 -> throw IllegalArgumentException("Not supported: $type")
else -> throw KeyTypeNotSupportedException(type.name)
}

private fun ociKeyToKeyTypeMapping(type: String) = when (type) {
"ECDSA" -> KeyType.secp256r1
"RSA" -> KeyType.RSA
else -> throw IllegalArgumentException("Not supported: $type")
else -> throw KeyTypeNotSupportedException(type)
}

@JvmBlocking
Expand All @@ -293,10 +296,9 @@ class OCIKeyRestApi(
val vaultKeyId = "${config.tenancyOcid}/${config.userOcid}/${config.fingerprint}"
val host = config.managementEndpoint
val length = when (type) {
KeyType.Ed25519 -> throw IllegalArgumentException("Not supported: $type")
KeyType.secp256r1 -> 32
KeyType.RSA -> 256
KeyType.secp256k1 -> throw IllegalArgumentException("Not supported: $type")
else -> throw KeyTypeNotSupportedException(type.name)
}
val requestBody = JsonObject(
mapOf(
Expand Down Expand Up @@ -388,7 +390,8 @@ class OCIKeyRestApi(
else -> throw IllegalArgumentException("Unsupported HTTP method: $method")
}

val privateOciApiKey = signingKey ?: error("No private key provided for OCI signing. Please provide a private key.")
val privateOciApiKey = signingKey
?: throw KeyNotFoundException("No private key provided for OCI signing. Please provide a private key.")

return Base64.encode(sha256WithRsa(privateOciApiKey, signingString.encodeToByteArray()))
}
Expand Down Expand Up @@ -438,7 +441,8 @@ class OCIKeyRestApi(
header("Host", host)
}

val publicKeyPem = response.body<JsonObject>()["publicKey"]?.jsonPrimitive?.content ?: error("No public key returned from OCI.")
val publicKeyPem = response.body<JsonObject>()["publicKey"]?.jsonPrimitive?.content
?: throw KeyNotFoundException("No public key returned from OCI for key ID: $OCIDKeyID and version: $keyVersion")
val publicKey = JWKKey.importPEM(publicKeyPem).getOrThrow()

return publicKey
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package id.walt.crypto.keys.tse

import id.walt.commons.exceptions.TSEError
import io.github.reactivecircus.cache4k.Cache
import io.ktor.client.*
import io.ktor.client.call.*
Expand Down Expand Up @@ -56,26 +57,42 @@ data class TSEAuth(
}

init {
require(
accessKey != null
|| (roleId != null && secretId != null)
|| (username != null && password != null)
) {
"No valid authentication method passed!"
requireAuthenticationMethod()
}

private fun requireAuthenticationMethod() {
val usingAccessKey = accessKey != null
val usingRoleIdAndSecret = roleId != null || secretId != null
val usingUsernameAndPassword = username != null || password != null
require(usingAccessKey || usingRoleIdAndSecret || usingUsernameAndPassword) {
throw TSEError.InvalidAuthenticationMethod.MissingAuthenticationMethodException()
}
if (usingRoleIdAndSecret) {
require(roleId != null && secretId != null) {
throw TSEError.InvalidAuthenticationMethod.IncompleteRoleAuthenticationMethodException()
}
}
if (usingUsernameAndPassword) {
require(username != null && password != null) {
throw TSEError.InvalidAuthenticationMethod.IncompleteUserAuthenticationMethodException()
}
}
}

private fun String.getServerUpTov1() = substringBefore("/v1/") + "/v1"

private suspend fun HttpResponse.getClientToken() =
body<JsonObject>().let {
if (it.containsKey("errors")) {
error("Errors occurred at TSE login: " + it["errors"]!!.jsonArray.map { it.jsonPrimitive.content }.joinToString())
}
checkNoErrors(it)
it["auth"]?.jsonObject?.get("client_token")?.jsonPrimitive?.contentOrNull
?: error("Did not receive token after login!")
?: throw TSEError.MissingAuthTokenException()
}

private fun checkNoErrors(json: JsonObject) = json["errors"]?.let {
throw TSEError.LoginException(it.jsonArray.map { it.jsonPrimitive.content })
}


private suspend fun loginAppRole(server: String): String =
http.post("$server/auth/approle/login") {
setBody(
Expand Down Expand Up @@ -104,7 +121,7 @@ data class TSEAuth(
accessKey != null -> accessKey
roleId != null -> loginAppRole(server)
username != null -> loginUserPass(server)
else -> throw IllegalArgumentException("No valid authentication method passed!")
else -> throw TSEError.InvalidAuthenticationMethod.MissingAuthenticationMethodException()
}
}

Expand Down
Loading
Loading