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

feat: KeyRestoration #111

Merged
merged 4 commits into from
Nov 21, 2023
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ import io.iohk.atala.prism.apollo.derivation.MnemonicLengthException
import io.iohk.atala.prism.walletsdk.apollo.helpers.BytesOps
import io.iohk.atala.prism.walletsdk.apollo.utils.Ed25519KeyPair
import io.iohk.atala.prism.walletsdk.apollo.utils.Ed25519PrivateKey
import io.iohk.atala.prism.walletsdk.apollo.utils.Ed25519PublicKey
import io.iohk.atala.prism.walletsdk.apollo.utils.Secp256k1PrivateKey
import io.iohk.atala.prism.walletsdk.apollo.utils.Secp256k1PublicKey
import io.iohk.atala.prism.walletsdk.apollo.utils.X25519KeyPair
import io.iohk.atala.prism.walletsdk.apollo.utils.X25519PrivateKey
import io.iohk.atala.prism.walletsdk.apollo.utils.X25519PublicKey
import io.iohk.atala.prism.walletsdk.domain.buildingblocks.Apollo
import io.iohk.atala.prism.walletsdk.domain.models.ApolloError
import io.iohk.atala.prism.walletsdk.domain.models.Curve
Expand All @@ -19,8 +22,10 @@ import io.iohk.atala.prism.walletsdk.domain.models.keyManagement.DerivationPathK
import io.iohk.atala.prism.walletsdk.domain.models.keyManagement.IndexKey
import io.iohk.atala.prism.walletsdk.domain.models.keyManagement.KeyTypes
import io.iohk.atala.prism.walletsdk.domain.models.keyManagement.PrivateKey
import io.iohk.atala.prism.walletsdk.domain.models.keyManagement.PublicKey
import io.iohk.atala.prism.walletsdk.domain.models.keyManagement.RawKey
import io.iohk.atala.prism.walletsdk.domain.models.keyManagement.SeedKey
import io.iohk.atala.prism.walletsdk.domain.models.keyManagement.StorableKey
import io.iohk.atala.prism.walletsdk.domain.models.keyManagement.TypeKey

/**
Expand Down Expand Up @@ -108,9 +113,7 @@ class ApolloImpl : Apollo {
throw ApolloError.InvalidIndex("Index must be an integer")
}
val derivationPath =
if (properties[DerivationPathKey().property] != null &&
properties[DerivationPathKey().property] !is String
) {
if (properties[DerivationPathKey().property] != null && properties[DerivationPathKey().property] !is String) {
throw ApolloError.InvalidDerivationPath("Derivation path must be a string")
} else {
"m/$index'/0'/0'"
Expand Down Expand Up @@ -143,4 +146,52 @@ class ApolloImpl : Apollo {
}
throw ApolloError.InvalidKeyType(TypeKey().property, KeyTypes.values().map { it.type }.toTypedArray())
}

override fun isPrivateKeyData(identifier: String, data: ByteArray): Boolean {
return identifier.endsWith("priv")
}

override fun isPublicKeyData(identifier: String, data: ByteArray): Boolean {
return identifier.endsWith("pub")
}

override fun restorePrivateKey(key: StorableKey): PrivateKey {
return when (key.restorationIdentifier) {
"secp256k1+priv" -> {
Secp256k1PrivateKey(key.storableData)
}

"x25519+priv" -> {
X25519PrivateKey(key.storableData)
}

"ed25519+priv" -> {
Ed25519PrivateKey(key.storableData)
}

else -> {
throw ApolloError.RestorationFailedNoIdentifierOrInvalid()
}
}
}

override fun restorePublicKey(key: StorableKey): PublicKey {
return when (key.restorationIdentifier) {
"secp256k1+pub" -> {
Secp256k1PublicKey(key.storableData)
}

"x25519+pub" -> {
X25519PublicKey(key.storableData)
}

"ed25519+pub" -> {
Ed25519PublicKey(key.storableData)
}

else -> {
throw ApolloError.RestorationFailedNoIdentifierOrInvalid()
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ package io.iohk.atala.prism.walletsdk.domain.buildingblocks
import io.iohk.atala.prism.apollo.derivation.MnemonicLengthException
import io.iohk.atala.prism.walletsdk.domain.models.Seed
import io.iohk.atala.prism.walletsdk.domain.models.SeedWords
import io.iohk.atala.prism.walletsdk.domain.models.keyManagement.KeyRestoration
import io.iohk.atala.prism.walletsdk.domain.models.keyManagement.PrivateKey
import kotlin.jvm.Throws

/**
* Apollo defines the set of cryptographic operations that are used in the Atala PRISM.
*/
interface Apollo {
interface Apollo : KeyRestoration {

/**
* Creates a random set of mnemonic phrases that can be used as a seed for generating
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ abstract interface Error {
* This object may include an error code, an error message, and possibly an array of underlying errors. If the error
* received does not conform to the [Error] interface, it will be classified as an [UnknownPrismError].
*/
abstract class UnknownPrismError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) : Error, Throwable(message, cause) {
abstract class UnknownPrismError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) : Error,
Throwable(message, cause) {

override val code: Int?
get() = null
Expand All @@ -36,7 +37,8 @@ abstract class UnknownPrismError @JvmOverloads constructor(message: String? = nu
* This object may include an error code and an error message. If the error received conforms to the [KnownPrismError],
* it will be classified as a known error.
*/
abstract class KnownPrismError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) : Error, Throwable(message, cause) {
abstract class KnownPrismError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) : Error,
Throwable(message, cause) {
override val code: Int?
get() = null
override val message: String?
Expand All @@ -54,7 +56,8 @@ abstract class KnownPrismError @JvmOverloads constructor(message: String? = null
* This object may include an error code, an error message, and possibly an array of underlying errors.
* If the error received does not conform to the [KnownPrismError], it will be classified as an [UnknownPrismError].
*/
abstract class UnknownError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) : UnknownPrismError(message, cause) {
abstract class UnknownError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) :
UnknownPrismError(message, cause) {

class SomethingWentWrongError(
message: String? = null,
Expand All @@ -78,7 +81,8 @@ abstract class UnknownError @JvmOverloads constructor(message: String? = null, c
* This object may include an error code and an error message. If the error received conforms to the [KnownPrismError],
* it will be classified as a known error.
*/
sealed class CommonError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) : KnownPrismError(message, cause) {
sealed class CommonError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) :
KnownPrismError(message, cause) {
class InvalidURLError(val url: String) : CommonError() {
override val code: Int
get() = -2
Expand All @@ -103,7 +107,8 @@ sealed class CommonError @JvmOverloads constructor(message: String? = null, caus
* This object may include an error code and an error message. If the error received conforms to the [KnownPrismError],
* it will be classified as a known error.
*/
sealed class ApolloError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) : KnownPrismError(message, cause) {
sealed class ApolloError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) :
KnownPrismError(message, cause) {
class InvalidMnemonicWord(private val invalidWords: Array<String>? = null) : ApolloError() {
override val code: Int
get() = 11
Expand Down Expand Up @@ -180,6 +185,14 @@ sealed class ApolloError @JvmOverloads constructor(message: String? = null, caus
override val message: String
get() = invalidMessage
}

class RestorationFailedNoIdentifierOrInvalid() : ApolloError() {

override val code: Int
get() = 20
override val message: String
get() = "Restoration failed: no identifier or invalid"
}
}

/**
Expand All @@ -189,7 +202,8 @@ sealed class ApolloError @JvmOverloads constructor(message: String? = null, caus
* This object may include an error code and an error message. If the error received conforms to the [KnownPrismError],
* it will be classified as a known error.
*/
sealed class CastorError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) : KnownPrismError(message, cause) {
sealed class CastorError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) :
KnownPrismError(message, cause) {
class KeyCurveNotSupported(val curve: String) : CastorError() {
override val code: Int
get() = 21
Expand Down Expand Up @@ -276,7 +290,8 @@ sealed class CastorError @JvmOverloads constructor(message: String? = null, caus
* This object may include an error code and an error message. If the error received conforms to the [KnownPrismError],
* it will be classified as a known error.
*/
sealed class MercuryError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) : KnownPrismError(message, cause) {
sealed class MercuryError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) :
KnownPrismError(message, cause) {

class NoDIDReceiverSetError : MercuryError() {
override val code: Int
Expand Down Expand Up @@ -349,7 +364,8 @@ sealed class MercuryError @JvmOverloads constructor(message: String? = null, cau
* This object may include an error code and an error message. If the error received conforms to the [KnownPrismError],
* it will be classified as a known error.
*/
sealed class PlutoError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) : KnownPrismError(message, cause) {
sealed class PlutoError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) :
KnownPrismError(message, cause) {
class MissingDataPersistence(val type: String, private val affecting: String) :
PlutoError() {
override val code: Int
Expand Down Expand Up @@ -433,7 +449,8 @@ sealed class PlutoError @JvmOverloads constructor(message: String? = null, cause
* This object may include an error code and an error message. If the error received conforms to the [KnownPrismError],
* it will be classified as a known error.
*/
sealed class PolluxError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) : KnownPrismError(message, cause) {
sealed class PolluxError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) :
KnownPrismError(message, cause) {
class InvalidPrismDID : PolluxError() {
override val code: Int
get() = 53
Expand All @@ -442,7 +459,8 @@ sealed class PolluxError @JvmOverloads constructor(message: String? = null, caus
get() = "To create a JWT presentation a Prism DID is required"
}

class InvalidCredentialError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) : PolluxError(message, cause) {
class InvalidCredentialError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) :
PolluxError(message, cause) {
override val code: Int
get() = 51

Expand Down Expand Up @@ -474,7 +492,10 @@ sealed class PolluxError @JvmOverloads constructor(message: String? = null, caus
get() = "No domain or challenge found as part of the offer json"
}

class InvalidCredentialDefinitionError @JvmOverloads constructor(message: String? = null, cause: Throwable? = null) : PolluxError(message, cause) {
class InvalidCredentialDefinitionError @JvmOverloads constructor(
message: String? = null,
cause: Throwable? = null
) : PolluxError(message, cause) {
override val code: Int
get() = 56

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ data class PEMKey(val keyType: PEMKeyType, val keyData: ByteArray) {
constructor(keyType: PEMKeyType, keyData: String) : this(keyType, keyData.base64UrlDecodedBytes)

fun pemEncoded(): String {
val base64Data = keyData.base64PadEncoded
val base64Data = keyData.base64PadEncoded.chunked(64).joinToString("\n")
val beginMarker = "-----BEGIN $keyType-----"
val endMarker = "-----END $keyType-----"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.iohk.atala.prism.walletsdk.domain.models.keyManagement

interface KeyRestoration {

fun isPrivateKeyData(identifier: String, data: ByteArray): Boolean

fun isPublicKeyData(identifier: String, data: ByteArray): Boolean

fun restorePrivateKey(key: StorableKey): PrivateKey

fun restorePublicKey(key: StorableKey): PublicKey
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ import io.iohk.atala.prism.walletsdk.domain.models.Curve
import io.iohk.atala.prism.walletsdk.domain.models.KeyCurve
import io.iohk.atala.prism.walletsdk.domain.models.Seed
import io.iohk.atala.prism.walletsdk.domain.models.keyManagement.KeyPair
import io.iohk.atala.prism.walletsdk.domain.models.keyManagement.StorableKey
import kotlinx.serialization.json.Json
import org.junit.Assert.assertNotEquals
import org.junit.Before
import org.junit.Test
import kotlin.test.assertEquals
Expand Down Expand Up @@ -177,4 +179,30 @@ class ApolloTests {
signature[0] = 1
assertFalse((keyPair.publicKey as Ed25519PublicKey).verify(message.toByteArray(), signature))
}

@Test
fun testRestorePrivateKey_whenStorableSecp256k1_thenRestoredOk() {
val keyPairSecp256k1 =
Secp256k1KeyPair.generateKeyPair(Seed(Mnemonic.createRandomSeed()), KeyCurve(Curve.SECP256K1))
val keyPairEd25519 = Ed25519KeyPair.generateKeyPair()
val privateKeySecp256k1 = keyPairSecp256k1.privateKey as Secp256k1PrivateKey
val privateKeyEd25519 = keyPairEd25519.privateKey as Ed25519PrivateKey
val storableKey = keyPairSecp256k1.privateKey as StorableKey
val restoredKey = apollo.restorePrivateKey(storableKey)
assertEquals(privateKeySecp256k1.raw, restoredKey.raw)
assertNotEquals(privateKeyEd25519.raw, restoredKey.raw)
}

@Test
fun testRestorePublicKey_whenStorableSecp256k1_thenRestoredOk() {
val keyPairSecp256k1 =
Secp256k1KeyPair.generateKeyPair(Seed(Mnemonic.createRandomSeed()), KeyCurve(Curve.SECP256K1))
val keyPairEd25519 = Ed25519KeyPair.generateKeyPair()
val publicKeySecp256k1 = keyPairSecp256k1.publicKey as Secp256k1PublicKey
val publicKeyEd25519 = keyPairEd25519.publicKey as Ed25519PublicKey
val storableKey = keyPairSecp256k1.publicKey as StorableKey
val restoredKey = apollo.restorePublicKey(storableKey)
assertEquals(publicKeySecp256k1.raw, restoredKey.raw)
assertNotEquals(publicKeyEd25519.raw, restoredKey.raw)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import io.iohk.atala.prism.walletsdk.domain.models.Signature
import io.iohk.atala.prism.walletsdk.domain.models.keyManagement.KeyPair
import io.iohk.atala.prism.walletsdk.domain.models.keyManagement.PrivateKey
import io.iohk.atala.prism.walletsdk.domain.models.keyManagement.PublicKey
import io.iohk.atala.prism.walletsdk.domain.models.keyManagement.StorableKey

class ApolloMock : Apollo {
var createRandomMnemonicsReturn: Array<String> = emptyArray()
Expand Down Expand Up @@ -41,4 +42,20 @@ class ApolloMock : Apollo {
override fun createPrivateKey(properties: Map<String, Any>): PrivateKey {
TODO("Not yet implemented")
}

override fun isPrivateKeyData(identifier: String, data: ByteArray): Boolean {
TODO("Not yet implemented")
}

override fun isPublicKeyData(identifier: String, data: ByteArray): Boolean {
TODO("Not yet implemented")
}

override fun restorePrivateKey(key: StorableKey): PrivateKey {
TODO("Not yet implemented")
}

override fun restorePublicKey(key: StorableKey): PublicKey {
TODO("Not yet implemented")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import io.iohk.atala.prism.walletsdk.domain.models.Seed
import io.iohk.atala.prism.walletsdk.domain.models.SeedWords
import io.iohk.atala.prism.walletsdk.domain.models.keyManagement.KeyPair
import io.iohk.atala.prism.walletsdk.domain.models.keyManagement.PrivateKey
import io.iohk.atala.prism.walletsdk.domain.models.keyManagement.PublicKey
import io.iohk.atala.prism.walletsdk.domain.models.keyManagement.StorableKey

class ApolloMock : Apollo {
var createRandomMnemonicsReturn: Array<String> = emptyArray()
Expand All @@ -33,4 +35,20 @@ class ApolloMock : Apollo {
override fun createPrivateKey(properties: Map<String, Any>): PrivateKey {
return createPrivateKey ?: Secp256k1PrivateKey(ByteArray(0))
}

override fun isPrivateKeyData(identifier: String, data: ByteArray): Boolean {
TODO("Not yet implemented")
}

override fun isPublicKeyData(identifier: String, data: ByteArray): Boolean {
TODO("Not yet implemented")
}

override fun restorePrivateKey(key: StorableKey): PrivateKey {
TODO("Not yet implemented")
}

override fun restorePublicKey(key: StorableKey): PublicKey {
TODO("Not yet implemented")
}
}
Loading