diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml
index 012d8f9..9c92902 100644
--- a/.github/workflows/pull-request.yml
+++ b/.github/workflows/pull-request.yml
@@ -11,8 +11,7 @@ concurrency:
   cancel-in-progress: true
 
 env:
-  JAVA_VERSION: 11
-  NODEJS_VERSION: 16.17.0
+  JAVA_VERSION: 17
   ATALA_GITHUB_ACTOR: ${{ secrets.ATALA_GITHUB_ACTOR }}
   ATALA_GITHUB_TOKEN: ${{ secrets.ATALA_GITHUB_TOKEN }}
 
@@ -24,7 +23,7 @@ jobs:
     runs-on: macos-latest
     steps:
       - name: Checkout the repo
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
         with:
           token: ${{ secrets.ATALA_GITHUB_TOKEN }}
           fetch-depth: 0
@@ -33,7 +32,7 @@ jobs:
         uses: gradle/wrapper-validation-action@v1
 
       - name: Cache gradle
-        uses: actions/cache@v3
+        uses: actions/cache@v4
         with:
           path: |
             ~/.gradle/caches
diff --git a/.github/workflows/release-documentation.yml b/.github/workflows/release-documentation.yml
index 1b6068a..f3bc9b4 100644
--- a/.github/workflows/release-documentation.yml
+++ b/.github/workflows/release-documentation.yml
@@ -7,8 +7,7 @@ defaults:
     shell: bash
 
 env:
-  JAVA_VERSION: 11
-  NODEJS_VERSION: 16.17.0
+  JAVA_VERSION: 17
   ATALA_GITHUB_ACTOR: ${{ secrets.ATALA_GITHUB_ACTOR }}
   ATALA_GITHUB_TOKEN: ${{ secrets.ATALA_GITHUB_TOKEN }}
 
@@ -23,13 +22,13 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: "Checkout the repo"
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
 
       - name: "Validate Gradle Wrapper"
-        uses: gradle/wrapper-validation-action@v1
+        uses: gradle/wrapper-validation-action@v2
 
       - name: "Cache gradle"
-        uses: actions/cache@v3
+        uses: actions/cache@v4
         with:
           path: |
             ~/.gradle/caches
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index f3549cd..491f1c6 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -7,7 +7,7 @@ defaults:
     shell: bash
 
 env:
-  JAVA_VERSION: 11
+  JAVA_VERSION: 17
   ATALA_GITHUB_ACTOR: ${{ secrets.ATALA_GITHUB_ACTOR }}
   ATALA_GITHUB_TOKEN: ${{ secrets.ATALA_GITHUB_TOKEN }}
   SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
@@ -22,7 +22,7 @@ jobs:
   build:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
         with:
           token: ${{ secrets.ATALA_GITHUB_TOKEN }}
           fetch-depth: 0
@@ -32,10 +32,10 @@ jobs:
           node-version: '>=20.8.1'
 
       - name: "Validate Gradle Wrapper"
-        uses: gradle/wrapper-validation-action@v1
+        uses: gradle/wrapper-validation-action@v2
 
       - name: "Cache Gradle"
-        uses: actions/cache@v3
+        uses: actions/cache@v4
         with:
           path: |
             ~/.gradle/caches
@@ -50,13 +50,8 @@ jobs:
         with:
           java-version: ${{ env.JAVA_VERSION }}
           distribution: "zulu"
-      
-      - name: Setup Node.js
-        uses: actions/setup-node@v3
-        with:
-          node-version: "lts/*"
 
-      - uses: crazy-max/ghaction-import-gpg@v5
+      - uses: crazy-max/ghaction-import-gpg@v6
         id: import_gpg
         with:
           gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
diff --git a/build.gradle.kts b/build.gradle.kts
index 3ae81fa..edc9f23 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,5 +1,3 @@
-import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension
-import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin
 import java.util.Base64
 
 val publishedMavenId: String = "io.iohk.atala.prism.didcomm"
@@ -8,6 +6,8 @@ plugins {
     id("org.jetbrains.dokka") version "1.9.10"
     id("maven-publish")
     id("signing")
+    kotlin("jvm") version "1.9.22"
+    id("com.android.library") version "8.1.4" apply false
     id("io.github.gradle-nexus.publish-plugin") version "2.0.0-rc-1"
     id("org.jlleitschuh.gradle.ktlint") version "11.6.1"
 }
@@ -20,11 +20,16 @@ buildscript {
     }
     dependencies {
         classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22")
-        classpath("com.android.tools.build:gradle:7.2.2")
         classpath("org.jetbrains.dokka:dokka-base:1.9.10")
     }
 }
 
+java {
+    toolchain {
+        languageVersion.set(JavaLanguageVersion.of(17))
+    }
+}
+
 group = publishedMavenId
 
 allprojects {
@@ -100,32 +105,36 @@ allprojects {
                         url.set("https://github.com/input-output-hk/atala-prism-didcomm-kmm")
                     }
                 }
-                signing {
-                    val base64EncodedAsciiArmoredSigningKey: String =
-                        System.getenv("BASE64_ARMORED_GPG_SIGNING_KEY_MAVEN") ?: ""
-                    val signingKeyPassword: String =
-                        System.getenv("SIGNING_KEY_PASSWORD") ?: ""
-                    useInMemoryPgpKeys(
-                        String(
-                            Base64.getDecoder().decode(
-                                base64EncodedAsciiArmoredSigningKey.toByteArray()
+                if (System.getenv("BASE64_ARMORED_GPG_SIGNING_KEY_MAVEN") != null) {
+                    if (System.getenv("BASE64_ARMORED_GPG_SIGNING_KEY_MAVEN").isNotBlank()) {
+                        signing {
+                            val base64EncodedAsciiArmoredSigningKey: String =
+                                System.getenv("BASE64_ARMORED_GPG_SIGNING_KEY_MAVEN") ?: ""
+                            val signingKeyPassword: String =
+                                System.getenv("SIGNING_KEY_PASSWORD") ?: ""
+                            useInMemoryPgpKeys(
+                                String(
+                                    Base64.getDecoder().decode(
+                                        base64EncodedAsciiArmoredSigningKey.toByteArray()
+                                    )
+                                ),
+                                signingKeyPassword
                             )
-                        ),
-                        signingKeyPassword
-                    )
-                    sign(this@withType)
+                            sign(this@withType)
+                        }
+                    }
                 }
             }
         }
         repositories {
-            maven {
-                this.name = "GitHubPackages"
-                this.url = uri("https://maven.pkg.github.com/input-output-hk/atala-prism-didcomm-kmm")
-                credentials {
-                    this.username = System.getenv("ATALA_GITHUB_ACTOR")
-                    this.password = System.getenv("ATALA_GITHUB_TOKEN")
-                }
-            }
+//            maven {
+//                this.name = "GitHubPackages"
+//                this.url = uri("https://maven.pkg.github.com/input-output-hk/atala-prism-didcomm-kmm")
+//                credentials {
+//                    this.username = System.getenv("ATALA_GITHUB_ACTOR")
+//                    this.password = System.getenv("ATALA_GITHUB_TOKEN")
+//                }
+//            }
         }
     }
 
@@ -135,10 +144,6 @@ allprojects {
     }
 }
 
-rootProject.plugins.withType(NodeJsRootPlugin::class.java) {
-    rootProject.extensions.getByType(NodeJsRootExtension::class.java).nodeVersion = "16.17.0"
-}
-
 nexusPublishing {
     repositories {
         sonatype {
diff --git a/didpeer/build.gradle.kts b/didpeer/build.gradle.kts
index 0b361b5..19785ad 100644
--- a/didpeer/build.gradle.kts
+++ b/didpeer/build.gradle.kts
@@ -95,12 +95,9 @@ kotlin {
     sourceSets {
         val commonMain by getting {
             dependencies {
-                implementation("io.iohk.atala.prism.apollo:multibase:1.0.2")
-                implementation("io.iohk.atala.prism.apollo:varint:1.0.2")
-                implementation("io.iohk.atala.prism.apollo:base64:1.0.2")
-                implementation("io.iohk.atala.prism.apollo:base58:1.0.2")
                 implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
-                implementation("com.squareup.okio:okio:3.6.0")
+                implementation("com.squareup.okio:okio:3.7.0")
+                implementation("com.ionspin.kotlin:bignum:0.3.9")
             }
         }
         val commonTest by getting {
@@ -144,11 +141,10 @@ android {
     sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
     defaultConfig {
         minSdk = 21
-        targetSdk = 34
     }
     compileOptions {
-        sourceCompatibility = JavaVersion.VERSION_11
-        targetCompatibility = JavaVersion.VERSION_11
+        sourceCompatibility = JavaVersion.VERSION_17
+        targetCompatibility = JavaVersion.VERSION_17
     }
     /**
      * Because Software Components will not be created automatically for Maven publishing from
@@ -175,8 +171,8 @@ tasks.withType<DokkaTask>().configureEach {
     }
     dokkaSourceSets {
         configureEach {
-            jdkVersion.set(11)
-            languageVersion.set("1.8.20")
+            jdkVersion.set(17)
+            languageVersion.set("1.9.22")
             apiVersion.set("2.0")
             includes.from(
                 "docs/DIDPeer.md"
@@ -210,14 +206,4 @@ afterEvaluate {
     tasks.withType<PublishToMavenRepository> {
         dependsOn(tasks.withType<Sign>())
     }
-    if (tasks.findByName("lintAnalyzeDebug") != null) {
-        tasks.named("lintAnalyzeDebug") {
-            this.enabled = false
-        }
-    }
-    if (tasks.findByName("lintAnalyzeRelease") != null) {
-        tasks.named("lintAnalyzeRelease") {
-            this.enabled = false
-        }
-    }
 }
diff --git a/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base16/Base16.kt b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base16/Base16.kt
new file mode 100644
index 0000000..d1d347e
--- /dev/null
+++ b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base16/Base16.kt
@@ -0,0 +1,71 @@
+package io.iohk.atala.prism.didcomm.didpeer.base16
+
+import com.ionspin.kotlin.bignum.integer.BigInteger
+import com.ionspin.kotlin.bignum.integer.Sign
+
+/**
+ * Base16 implementation
+ */
+internal object Base16 {
+    private val base: BigInteger = BigInteger.parseString("16")
+
+    /**
+     * Encode string to Base16
+     */
+    fun encode(input: ByteArray, encoding: Encoding = Encoding.Standard): String {
+        if (input.isEmpty()) {
+            return ""
+        }
+        var bi = BigInteger.fromByteArray(input, Sign.POSITIVE)
+        val sb = StringBuilder()
+        while (bi >= base) {
+            val mod = bi.mod(base)
+            sb.insert(0, encoding.alphabet[mod.intValue()])
+            bi = bi.subtract(mod).divide(base)
+        }
+        sb.insert(0, encoding.alphabet[bi.intValue()])
+        // convert leading zeros.
+        for (b in input) {
+            if (b.compareTo(0) == 0) {
+                sb.insert(0, encoding.alphabet[0])
+            } else {
+                break
+            }
+        }
+        return sb.toString()
+    }
+
+    /**
+     * Decode string to Base16
+     */
+    fun decode(input: String, encoding: Encoding = Encoding.Standard): ByteArray {
+        val bytes = decodeToBigInteger(encoding.alphabet, base, input).toByteArray()
+        val stripSignByte = bytes.size > 1 && bytes[0].compareTo(0) == 0 && bytes[1] < 0
+        var leadingZeros = 0
+        var i = 0
+        while (input[i] == encoding.alphabet[0]) {
+            leadingZeros++
+            i++
+        }
+        val tmp = ByteArray(bytes.size - (if (stripSignByte) 1 else 0) + leadingZeros)
+        bytes.copyInto(
+            tmp, // dest
+            0, // dest offset
+            if (stripSignByte) 1 else 0,
+            tmp.size - leadingZeros // can be added -1 not sure
+        )
+        return tmp
+    }
+
+    private fun decodeToBigInteger(alphabet: String, base: BigInteger, input: String): BigInteger {
+        var bi = BigInteger(0)
+        for (i in input.length - 1 downTo 0) {
+            val alphaIndex = alphabet.indexOf(input[i])
+            if (alphaIndex == -1) {
+                throw IllegalStateException("Illegal character " + input[i] + " at " + i)
+            }
+            bi = bi.add(BigInteger(alphaIndex.toLong()).multiply(base.pow(input.length - 1 - i)))
+        }
+        return bi
+    }
+}
diff --git a/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base16/ByteArrayExt.kt b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base16/ByteArrayExt.kt
new file mode 100644
index 0000000..8e2efe3
--- /dev/null
+++ b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base16/ByteArrayExt.kt
@@ -0,0 +1,37 @@
+package io.iohk.atala.prism.didcomm.didpeer.base16
+
+/**
+ * Convert [ByteArray] to [CharArray]
+ * @return [CharArray]
+ */
+fun ByteArray.asCharArray(): CharArray {
+    val chars = CharArray(size)
+    for (i in chars.indices) {
+        chars[i] = get(i).toInt().toChar()
+    }
+    return chars
+}
+
+/**
+ * Encode a [ByteArray] to Base16 [String] standard
+ */
+val ByteArray.base16Encoded: String
+    get() = Base16.encode(this)
+
+/**
+ * Decode a [ByteArray] Base16 standard encoded to [String]
+ */
+val ByteArray.base16Decoded: String
+    get() = asCharArray().concatToString().base16Encoded
+
+/**
+ * Encode a [ByteArray] to Base16 [String] Upper
+ */
+val ByteArray.base16UpperEncoded: String
+    get() = Base16.encode(this, Encoding.Upper)
+
+/**
+ * Decode a [ByteArray] Base16 Upper encoded to [String]
+ */
+val ByteArray.base16UpperDecoded: String
+    get() = asCharArray().concatToString().base16UpperDecoded
diff --git a/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base16/Encoding.kt b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base16/Encoding.kt
new file mode 100644
index 0000000..cde813b
--- /dev/null
+++ b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base16/Encoding.kt
@@ -0,0 +1,22 @@
+package io.iohk.atala.prism.didcomm.didpeer.base16
+
+/**
+ * Base16 encoding scheme
+ */
+sealed interface Encoding {
+    val alphabet: String
+
+    /**
+     * Base16 Standard
+     */
+    data object Standard : Encoding {
+        override val alphabet: String = "0123456789abcdef"
+    }
+
+    /**
+     * Base16 Upper
+     */
+    data object Upper : Encoding {
+        override val alphabet: String = "0123456789ABCDEF"
+    }
+}
diff --git a/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base16/StringExt.kt b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base16/StringExt.kt
new file mode 100644
index 0000000..8e1c73c
--- /dev/null
+++ b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base16/StringExt.kt
@@ -0,0 +1,37 @@
+package io.iohk.atala.prism.didcomm.didpeer.base16
+
+/**
+ * Encode a [String] to Base16 [String]
+ */
+val String.base16Encoded: String
+    get() = Base16.encode(this.encodeToByteArray())
+
+/**
+ * Decode a Base16 [String] to [ByteArray].
+ */
+val String.base16DecodedBytes: ByteArray
+    get() = Base16.decode(this)
+
+/**
+ * Decode a Base16 [String] to [String].
+ */
+val String.base16Decoded: String
+    get() = this.base16DecodedBytes.decodeToString()
+
+/**
+ * Encode a [String] to Base16 [String] Upper
+ */
+val String.base16UpperEncoded: String
+    get() = Base16.encode(this.encodeToByteArray(), Encoding.Upper)
+
+/**
+ * Decode a Base16 [String] Upper to [ByteArray].
+ */
+val String.base16UpperDecodedBytes: ByteArray
+    get() = Base16.decode(this, Encoding.Upper)
+
+/**
+ * Decode a Base16 [String] Upper to [String].
+ */
+val String.base16UpperDecoded: String
+    get() = this.base16UpperDecodedBytes.decodeToString()
diff --git a/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base32/Base32.kt b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base32/Base32.kt
new file mode 100644
index 0000000..9a0bf38
--- /dev/null
+++ b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base32/Base32.kt
@@ -0,0 +1,93 @@
+package io.iohk.atala.prism.didcomm.didpeer.base32
+
+import com.ionspin.kotlin.bignum.integer.BigInteger
+
+/**
+ * Base32 implementation
+ */
+internal final object Base32 {
+    private val base: BigInteger = BigInteger.parseString("32")
+
+    /**
+     * Encode string to Base32
+     */
+    fun encode(input: ByteArray, encoding: Encoding = Encoding.Standard, paddingEnabled: Boolean = true): String {
+        if (input.contentEquals("".encodeToByteArray())) {
+            return ""
+        }
+        val output = StringBuilder()
+        var buffer = 0
+        var bits = 0
+
+        for (byte in input) {
+            buffer = (buffer shl 8) or (byte.toInt() and 0xFF)
+            bits += 8
+
+            while (bits >= 5) {
+                val index = (buffer shr (bits - 5)) and 0x1F
+                output.append(encoding.alphabet[index])
+                bits -= 5
+            }
+        }
+
+        if (bits > 0) {
+            buffer = (buffer shl (5 - bits))
+            val index = (buffer and 0x1F)
+            output.append(encoding.alphabet[index])
+        }
+
+        val padding = (8 - output.length % 8) % 8
+        repeat(padding) {
+            output.append('=')
+        }
+
+        return output.toString()
+    }
+
+    /**
+     * Decode string to Base32
+     */
+    fun decode(input: String, encoding: Encoding = Encoding.Standard): ByteArray {
+        val bytes = decodeToBigInteger(encoding.alphabet, base, input).toByteArray().dropLastWhile {
+            it.toInt() == 0
+        }.toByteArray()
+
+        // Determine if the first byte is a sign byte (0x00) and needs to be stripped
+        val stripSignByte = bytes.size > 1 && bytes[0].compareTo(0) == 0 && bytes[1] < 0
+
+        // Count the number of leading zeros in the input string
+        var leadingZeros = 0
+        var i = 0
+        while (input[i] == encoding.alphabet[0]) {
+            leadingZeros++
+            i++
+        }
+
+        // Allocate a temporary byte array with the correct size, accounting for the sign byte and leading zeros
+        val tmp = ByteArray(bytes.size - (if (stripSignByte) 1 else 0) + leadingZeros)
+
+        // Copy the decoded bytes into the temporary array, skipping the sign byte if necessary
+        bytes.copyInto(
+            tmp, // destination array
+            0, // destination offset
+            if (stripSignByte) 1 else 0, // source offset
+            tmp.size - leadingZeros // number of bytes to copy
+        )
+
+        return tmp
+    }
+
+    private fun decodeToBigInteger(alphabet: String, base: BigInteger, input: String): BigInteger {
+        var bi = BigInteger(0)
+        for (i in input.length - 1 downTo 0) {
+            val alphaIndex = alphabet.indexOf(input[i])
+            if (alphaIndex == -1 && input[i] != '=') {
+                throw IllegalStateException("Illegal character " + input[i] + " at " + i)
+            }
+            if (input[i] != '=') {
+                bi = bi.add(BigInteger(alphaIndex.toLong()).multiply(base.pow(input.length - 1 - i)))
+            }
+        }
+        return bi
+    }
+}
diff --git a/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base32/ByteArrayExt.kt b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base32/ByteArrayExt.kt
new file mode 100644
index 0000000..d81071b
--- /dev/null
+++ b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base32/ByteArrayExt.kt
@@ -0,0 +1,125 @@
+package io.iohk.atala.prism.didcomm.didpeer.base32
+
+/**
+ * Convert [ByteArray] to [CharArray]
+ * @return [CharArray]
+ */
+fun ByteArray.asCharArray(): CharArray {
+    val chars = CharArray(size)
+    for (i in chars.indices) {
+        chars[i] = get(i).toInt().toChar()
+    }
+    return chars
+}
+
+// Standard
+
+/**
+ * Encode a [ByteArray] to Base32 [String] standard
+ */
+val ByteArray.base32Encoded: String
+    get() = Base32.encode(this, paddingEnabled = false)
+
+/**
+ * Decode a [ByteArray] Base32 standard encoded to [String]
+ */
+val ByteArray.base32Decoded: String
+    get() = asCharArray().concatToString().base32Encoded
+
+// Standard with padding
+
+/**
+ * Encode a [ByteArray] to Base32 [String] standard with padding
+ */
+val ByteArray.base32PadEncoded: String
+    get() = Base32.encode(this, Encoding.Standard)
+
+/**
+ * Decode a [ByteArray] Base32 standard with padding encoded to [String]
+ */
+val ByteArray.base32PadDecoded: String
+    get() = asCharArray().concatToString().base32PadEncoded
+
+// Upper
+
+/**
+ * Encode a [ByteArray] to Base32 [String] upper
+ */
+val ByteArray.base32UpperEncoded: String
+    get() = Base32.encode(this, Encoding.Upper, paddingEnabled = false)
+
+/**
+ * Decode a [ByteArray] Base32 Upper encoded to [String]
+ */
+val ByteArray.base32UpperDecoded: String
+    get() = asCharArray().concatToString().base32UpperEncoded
+
+// Upper with padding
+
+/**
+ * Encode a [ByteArray] to Base32 [String] Upper with padding
+ */
+val ByteArray.base32UpperPadEncoded: String
+    get() = Base32.encode(this, Encoding.Upper)
+
+/**
+ * Decode a [ByteArray] Base32 Upper with padding encoded to [String]
+ */
+val ByteArray.base32UpperPadDecoded: String
+    get() = asCharArray().concatToString().base32UpperPadEncoded
+
+// Hex
+
+/**
+ * Encode a [ByteArray] to Base32 [String] hex
+ */
+val ByteArray.base32HexEncoded: String
+    get() = Base32.encode(this, Encoding.Hex, paddingEnabled = false)
+
+/**
+ * Decode a [ByteArray] Base32 Hex encoded to [String]
+ */
+val ByteArray.base32HexDecoded: String
+    get() = asCharArray().concatToString().base32HexEncoded
+
+// Hex with padding
+
+/**
+ * Encode a [ByteArray] to Base32 [String] Hex with padding
+ */
+val ByteArray.base32HexPadEncoded: String
+    get() = Base32.encode(this, Encoding.Hex)
+
+/**
+ * Decode a [ByteArray] Base32 Hex with padding encoded to [String]
+ */
+val ByteArray.base32HexPadDecoded: String
+    get() = asCharArray().concatToString().base32HexPadEncoded
+
+// Hex Upper
+
+/**
+ * Encode a [ByteArray] to Base32 [String] Hex Upper
+ */
+val ByteArray.base32HexUpperEncoded: String
+    get() = Base32.encode(this, Encoding.HexUpper)
+
+/**
+ * Decode a [ByteArray] Base32 Hex Upper encoded to [String]
+ */
+val ByteArray.base32HexUpperDecoded: String
+    get() = asCharArray().concatToString().base32HexUpperEncoded
+
+// Hex Upper with padding
+
+/**
+ * Encode a [ByteArray] to Base32 [String] Hex Upper with padding
+ */
+val ByteArray.base32HexUpperPadEncoded: String
+    get() = Base32.encode(this, Encoding.HexUpper)
+
+/**
+ * Decode a [ByteArray] Base32 HexUpper with padding encoded to [String]
+ */
+val ByteArray.base32HexUpperPadDecoded: String
+    get() = asCharArray().concatToString().base32HexUpperPadEncoded
diff --git a/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base32/Encoding.kt b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base32/Encoding.kt
new file mode 100644
index 0000000..153cd46
--- /dev/null
+++ b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base32/Encoding.kt
@@ -0,0 +1,38 @@
+package io.iohk.atala.prism.didcomm.didpeer.base32
+
+/**
+ * Base32 encoding scheme
+ *
+ * TODO: Figure out a way to put both type with padded version in on scheme
+ */
+sealed interface Encoding {
+    val alphabet: String
+
+    /**
+     * Base32 Standard
+     */
+    data object Standard : Encoding {
+        override val alphabet: String = "abcdefghijklmnopqrstuvwxyz234567"
+    }
+
+    /**
+     * Base32 Upper
+     */
+    data object Upper : Encoding {
+        override val alphabet: String = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
+    }
+
+    /**
+     * Base32 Hex
+     */
+    data object Hex : Encoding {
+        override val alphabet: String = "0123456789abcdefghijklmnopqrstuvw"
+    }
+
+    /**
+     * Base32 Hex Upper
+     */
+    data object HexUpper : Encoding {
+        override val alphabet: String = "0123456789ABCDEFGHIJKLMNOPQRSTUVW"
+    }
+}
diff --git a/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base32/StringExt.kt b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base32/StringExt.kt
new file mode 100644
index 0000000..bbf6050
--- /dev/null
+++ b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base32/StringExt.kt
@@ -0,0 +1,153 @@
+package io.iohk.atala.prism.didcomm.didpeer.base32
+
+// Standard
+/**
+ * Encode a [String] to Base32 [String]
+ */
+val String.base32Encoded: String
+    get() = Base32.encode(this.encodeToByteArray(), paddingEnabled = false)
+
+/**
+ * Decode a Base32 [String] to [ByteArray].
+ */
+val String.base32DecodedBytes: ByteArray
+    get() = Base32.decode(this)
+
+/**
+ * Decode a Base32 [String] to [String].
+ */
+val String.base32Decoded: String
+    get() = this.base32DecodedBytes.decodeToString()
+
+// Standard With Padding
+/**
+ * Encode a [String] to Base32 [String]
+ */
+val String.base32PadEncoded: String
+    get() = Base32.encode(this.encodeToByteArray(), Encoding.Standard)
+
+/**
+ * Decode a Base32 [String] to [ByteArray].
+ */
+val String.base32PadDecodedBytes: ByteArray
+    get() = Base32.decode(this, Encoding.Standard)
+
+/**
+ * Decode a Base32 [String] to [String].
+ */
+val String.base32PadDecoded: String
+    get() = this.base32PadDecodedBytes.decodeToString()
+
+// Upper
+/**
+ * Encode a [String] to Base32 [String]
+ */
+val String.base32UpperEncoded: String
+    get() = Base32.encode(this.encodeToByteArray(), Encoding.Upper, paddingEnabled = false)
+
+/**
+ * Decode a Base32 [String] to [ByteArray].
+ */
+val String.base32UpperDecodedBytes: ByteArray
+    get() = Base32.decode(this, Encoding.Upper)
+
+/**
+ * Decode a Base32 [String] to [String].
+ */
+val String.base32UpperDecoded: String
+    get() = this.base32UpperDecodedBytes.decodeToString()
+
+// Upper With Padding
+/**
+ * Encode a [String] to Base32 [String]
+ */
+val String.base32UpperPadEncoded: String
+    get() = Base32.encode(this.encodeToByteArray(), Encoding.Upper)
+
+/**
+ * Decode a Base32 [String] to [ByteArray].
+ */
+val String.base32UpperPadDecodedBytes: ByteArray
+    get() = Base32.decode(this, Encoding.Upper)
+
+/**
+ * Decode a Base32 [String] to [String].
+ */
+val String.base32UpperPadDecoded: String
+    get() = this.base32UpperPadDecodedBytes.decodeToString()
+
+// Hex
+/**
+ * Encode a [String] to Base32 [String]
+ */
+val String.base32HexEncoded: String
+    get() = Base32.encode(this.encodeToByteArray(), Encoding.Hex, paddingEnabled = false)
+
+/**
+ * Decode a Base32 [String] to [ByteArray].
+ */
+val String.base32HexDecodedBytes: ByteArray
+    get() = Base32.decode(this, Encoding.Hex)
+
+/**
+ * Decode a Base32 [String] to [String].
+ */
+val String.base32HexDecoded: String
+    get() = this.base32HexDecodedBytes.decodeToString()
+
+// Hex with padding
+/**
+ * Encode a [String] to Base32 [String]
+ */
+val String.base32HexPadEncoded: String
+    get() = Base32.encode(this.encodeToByteArray(), Encoding.Hex)
+
+/**
+ * Decode a Base32 [String] to [ByteArray].
+ */
+val String.base32HexPadDecodedBytes: ByteArray
+    get() = Base32.decode(this, Encoding.Hex)
+
+/**
+ * Decode a Base32 [String] to [String].
+ */
+val String.base32HexPadDecoded: String
+    get() = this.base32HexPadDecodedBytes.decodeToString()
+
+// Hex Upper
+/**
+ * Encode a [String] to Base32 [String]
+ */
+val String.base32HexUpperEncoded: String
+    get() = Base32.encode(this.encodeToByteArray(), Encoding.HexUpper, paddingEnabled = false)
+
+/**
+ * Decode a Base32 [String] to [ByteArray].
+ */
+val String.base32HexUpperDecodedBytes: ByteArray
+    get() = Base32.decode(this, Encoding.HexUpper)
+
+/**
+ * Decode a Base32 [String] to [String].
+ */
+val String.base32HexUpperDecoded: String
+    get() = this.base32HexUpperDecodedBytes.decodeToString()
+
+// Hex Upper with padding
+/**
+ * Encode a [String] to Base32 [String]
+ */
+val String.base32HexUpperPadEncoded: String
+    get() = Base32.encode(this.encodeToByteArray(), Encoding.HexUpper)
+
+/**
+ * Decode a Base32 [String] to [ByteArray].
+ */
+val String.base32HexUpperPadDecodedBytes: ByteArray
+    get() = Base32.decode(this, Encoding.HexUpper)
+
+/**
+ * Decode a Base32 [String] to [String].
+ */
+val String.base32HexUpperPadDecoded: String
+    get() = this.base32HexUpperPadDecodedBytes.decodeToString()
diff --git a/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base58/Base58.kt b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base58/Base58.kt
new file mode 100644
index 0000000..31ff38e
--- /dev/null
+++ b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base58/Base58.kt
@@ -0,0 +1,71 @@
+package io.iohk.atala.prism.didcomm.didpeer.base58
+
+import com.ionspin.kotlin.bignum.integer.BigInteger
+import com.ionspin.kotlin.bignum.integer.Sign
+
+/**
+ * Base58 implementation
+ */
+internal object Base58 {
+    private val base: BigInteger = BigInteger.parseString("58")
+
+    /**
+     * Encode string to Base58
+     */
+    fun encode(input: ByteArray, encoding: Encoding = Encoding.BTC): String {
+        if (input.contentEquals("".encodeToByteArray())) {
+            return ""
+        }
+        var bi = BigInteger.fromByteArray(input, Sign.POSITIVE)
+        val sb = StringBuilder()
+        while (bi >= base) {
+            val mod = bi.mod(base)
+            sb.insert(0, encoding.alphabet[mod.intValue()])
+            bi = bi.subtract(mod).divide(base)
+        }
+        sb.insert(0, encoding.alphabet[bi.intValue()])
+        // convert leading zeros.
+        for (b in input) {
+            if (b.compareTo(0) == 0) {
+                sb.insert(0, encoding.alphabet[0])
+            } else {
+                break
+            }
+        }
+        return sb.toString()
+    }
+
+    /**
+     * Decode string to Base58
+     */
+    fun decode(input: String, encoding: Encoding = Encoding.BTC): ByteArray {
+        val bytes = decodeToBigInteger(encoding.alphabet, base, input).toByteArray()
+        val stripSignByte = bytes.size > 1 && bytes[0].compareTo(0) == 0 && bytes[1] < 0
+        var leadingZeros = 0
+        var i = 0
+        while (input[i] == encoding.alphabet[0]) {
+            leadingZeros++
+            i++
+        }
+        val tmp = ByteArray(bytes.size - (if (stripSignByte) 1 else 0) + leadingZeros)
+        bytes.copyInto(
+            tmp, // dest
+            0, // dest offset
+            if (stripSignByte) 1 else 0,
+            tmp.size - leadingZeros // can be added -1 not sure
+        )
+        return tmp
+    }
+
+    private fun decodeToBigInteger(alphabet: String, base: BigInteger, input: String): BigInteger {
+        var bi = BigInteger(0)
+        for (i in input.length - 1 downTo 0) {
+            val alphaIndex = alphabet.indexOf(input[i])
+            if (alphaIndex == -1) {
+                throw IllegalStateException("Illegal character " + input[i] + " at " + i)
+            }
+            bi = bi.add(BigInteger(alphaIndex.toLong()).multiply(base.pow(input.length - 1 - i)))
+        }
+        return bi
+    }
+}
diff --git a/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base58/ByteArrayExt.kt b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base58/ByteArrayExt.kt
new file mode 100644
index 0000000..35abf98
--- /dev/null
+++ b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base58/ByteArrayExt.kt
@@ -0,0 +1,41 @@
+package io.iohk.atala.prism.didcomm.didpeer.base58
+
+/**
+ * Convert [ByteArray] to [CharArray]
+ * @return [CharArray]
+ */
+fun ByteArray.asCharArray(): CharArray {
+    val chars = CharArray(size)
+    for (i in chars.indices) {
+        chars[i] = get(i).toInt().toChar()
+    }
+    return chars
+}
+
+// BTC
+
+/**
+ * Encode a [ByteArray] to Base58 [String] standard
+ */
+val ByteArray.base58BtcEncoded: String
+    get() = Base58.encode(this)
+
+/**
+ * Decode a [ByteArray] Base58 BTC encoded to [String]
+ */
+val ByteArray.base58BtcDecoded: String
+    get() = asCharArray().concatToString().base58BtcEncoded
+
+// Flickr
+
+/**
+ * Encode a [ByteArray] to Base58 [String] standard
+ */
+val ByteArray.base58FlickrEncoded: String
+    get() = Base58.encode(this, Encoding.Flickr)
+
+/**
+ * Decode a [ByteArray] Base58 BTC encoded to [String]
+ */
+val ByteArray.base58FlickrDecoded: String
+    get() = asCharArray().concatToString().base58FlickrEncoded
diff --git a/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base58/Encoding.kt b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base58/Encoding.kt
new file mode 100644
index 0000000..e772529
--- /dev/null
+++ b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base58/Encoding.kt
@@ -0,0 +1,22 @@
+package io.iohk.atala.prism.didcomm.didpeer.base58
+
+/**
+ * Base58 encoding scheme
+ */
+sealed interface Encoding {
+    val alphabet: String
+
+    /**
+     * Base58 BTC => Standard
+     */
+    data object BTC : Encoding {
+        override val alphabet: String = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
+    }
+
+    /**
+     * Base58 Flickr
+     */
+    data object Flickr : Encoding {
+        override val alphabet: String = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"
+    }
+}
diff --git a/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base58/StringExt.kt b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base58/StringExt.kt
new file mode 100644
index 0000000..c57ff17
--- /dev/null
+++ b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base58/StringExt.kt
@@ -0,0 +1,39 @@
+package io.iohk.atala.prism.didcomm.didpeer.base58
+
+// BTC
+/**
+ * Encode a [String] to Base58 [String]
+ */
+val String.base58BtcEncoded: String
+    get() = Base58.encode(this.encodeToByteArray())
+
+/**
+ * Decode a Base58 [String] to [ByteArray].
+ */
+val String.base58BtcDecodedBytes: ByteArray
+    get() = Base58.decode(this)
+
+/**
+ * Decode a Base58 [String] to [String].
+ */
+val String.base58BtcDecoded: String
+    get() = this.base58BtcDecodedBytes.decodeToString()
+
+// Flickr
+/**
+ * Encode a [String] to Base58 [String]
+ */
+val String.base58FlickrEncoded: String
+    get() = Base58.encode(this.encodeToByteArray(), Encoding.Flickr)
+
+/**
+ * Decode a Base58 [String] to [ByteArray].
+ */
+val String.base58FlickrDecodedBytes: ByteArray
+    get() = Base58.decode(this, Encoding.Flickr)
+
+/**
+ * Decode a Base58 [String] to [String].
+ */
+val String.base58FlickrDecoded: String
+    get() = this.base58FlickrDecodedBytes.decodeToString()
diff --git a/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base64/Base64.kt b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base64/Base64.kt
new file mode 100644
index 0000000..99d7f65
--- /dev/null
+++ b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base64/Base64.kt
@@ -0,0 +1,533 @@
+package io.iohk.atala.prism.didcomm.didpeer.base64
+
+import kotlin.math.min
+
+internal object Base64 {
+
+    /**
+     * Encode ByteArray to Base64 String
+     */
+    fun encodeToString(input: ByteArray, encoding: Encoding = Encoding.Standard): String {
+        return when (encoding) {
+            Encoding.Standard -> getEncoder().withoutPadding().encodeToString(input)
+            Encoding.StandardPad -> getEncoder().encodeToString(input)
+            Encoding.UrlSafe -> getUrlEncoder().withoutPadding().encodeToString(input)
+            Encoding.UrlSafePad -> getUrlEncoder().encodeToString(input)
+        }
+    }
+
+    /**
+     * Encode ByteArray to Base64 ByteArray
+     */
+    fun encode(input: ByteArray, encoding: Encoding = Encoding.Standard): ByteArray {
+        return when (encoding) {
+            Encoding.Standard -> getEncoder().withoutPadding().encode(input)
+            Encoding.StandardPad -> getEncoder().encode(input)
+            Encoding.UrlSafe -> getUrlEncoder().withoutPadding().encode(input)
+            Encoding.UrlSafePad -> getUrlEncoder().encode(input)
+        }
+    }
+
+    /**
+     * Decode string to Base64
+     */
+    fun decode(input: String, encoding: Encoding = Encoding.Standard): ByteArray {
+        return when (encoding) {
+            Encoding.Standard, Encoding.StandardPad -> getDecoder().decode(input)
+            Encoding.UrlSafe, Encoding.UrlSafePad -> getUrlDecoder().decode(input)
+        }
+    }
+
+    private fun getEncoder(): Encoder {
+        return Encoder.RFC4648
+    }
+
+    private fun getDecoder(): Decoder {
+        return Decoder.RFC4648
+    }
+
+    private fun getUrlEncoder(): Encoder {
+        return Encoder.RFC4648_URLSAFE
+    }
+
+    private fun getUrlDecoder(): Decoder {
+        return Decoder.RFC4648_URLSAFE
+    }
+
+    /**
+     * This class implements a decoder for decoding byte data using the
+     * Base64 encoding scheme as specified in RFC 4648 and RFC 2045.
+     *
+     *
+     *  The Base64 padding character `'='` is accepted and
+     * interpreted as the end of the encoded byte data, but is not
+     * required. So if the final unit of the encoded byte data only has
+     * two or three Base64 characters (without the corresponding padding
+     * character(s) padded), they are decoded as if followed by padding
+     * character(s). If there is a padding character present in the
+     * final unit, the correct number of padding character(s) must be
+     * present, otherwise `IllegalArgumentException` (
+     * `IOException` when reading from a Base64 stream) is thrown
+     * during decoding.
+     *
+     *
+     *  Instances of [Decoder] class are safe for use by
+     * multiple concurrent threads.
+     *
+     *
+     *  Unless otherwise noted, passing a `null` argument to
+     * a method of this class will cause a
+     * [NullPointerException][NullPointerException] to
+     * be thrown.
+     *
+     * @see Encoder
+     *
+     * @since 1.8
+     */
+    private class Decoder private constructor(private val isURL: Boolean, private val isMIME: Boolean) {
+        /**
+         * Decodes all bytes from the input byte array using the [Base64]
+         * encoding scheme, writing the results into a newly-allocated output
+         * byte array. The returned byte array is of the length of the resulting
+         * bytes.
+         *
+         * @param src
+         * the byte array to decode
+         *
+         * @return A newly-allocated byte array containing the decoded bytes.
+         *
+         * @throws IllegalArgumentException
+         * if `src` is not in valid Base64 scheme
+         */
+        fun decode(src: ByteArray): ByteArray {
+            val dst = ByteArray(outLength(src, 0, src.size))
+            val ret = decode0(src, 0, src.size, dst)
+            if (ret != dst.size) {
+                dst.copyInto(ByteArray(ret))
+                // dst = java.util.Arrays.copyOf(dst, ret)
+            }
+            return dst
+        }
+
+        /**
+         * Decodes a Base64 encoded String into a newly-allocated byte array
+         * using the [Base64] encoding scheme.
+         *
+         *
+         *  An invocation of this method has exactly the same effect as invoking
+         * `decode(src.getBytes(StandardCharsets.ISO_8859_1))`
+         *
+         * @param src
+         * the string to decode
+         *
+         * @return A newly-allocated byte array containing the decoded bytes.
+         *
+         * @throws IllegalArgumentException
+         * if `src` is not in valid Base64 scheme
+         */
+        fun decode(src: String): ByteArray {
+            return decode(src.encodeToByteArray())
+            // return decode(src.toByteArray(java.nio.charset.StandardCharsets.ISO_8859_1))
+        }
+
+        /**
+         * Decodes all bytes from the input byte array using the [Base64]
+         * encoding scheme, writing the results into the given output byte array,
+         * starting at offset 0.
+         *
+         *
+         *  It is the responsibility of the invoker of this method to make
+         * sure the output byte array `dst` has enough space for decoding
+         * all bytes from the input byte array. No bytes will be be written to
+         * the output byte array if the output byte array is not big enough.
+         *
+         *
+         *  If the input byte array is not in valid Base64 encoding scheme
+         * then some bytes may have been written to the output byte array before
+         * [IllegalArgumentException] is thrown.
+         *
+         * @param src
+         * the byte array to decode
+         * @param dst
+         * the output byte array
+         *
+         * @return The number of bytes written to the output byte array
+         *
+         * @throws IllegalArgumentException
+         * if `src` is not in valid Base64 scheme, or `dst`
+         * does not have enough space for decoding all input bytes.
+         */
+        @Throws(IllegalArgumentException::class)
+        fun decode(src: ByteArray, dst: ByteArray): Int {
+            val len = outLength(src, 0, src.size)
+            if (dst.size < len) {
+                throw IllegalArgumentException("Output byte array is too small for decoding all input bytes")
+            }
+            return decode0(src, 0, src.size, dst)
+        }
+
+        @Suppress("NAME_SHADOWING")
+        @Throws(IllegalArgumentException::class)
+        private fun outLength(src: ByteArray, sp: Int, sl: Int): Int {
+            var sp = sp
+            val base64 = if (isURL) {
+                fromBase64URL
+            } else {
+                fromBase64
+            }
+            var paddings = 0
+            var len = sl - sp
+            if (len == 0) {
+                return 0
+            }
+            if (len < 2) {
+                if (isMIME && base64[0] == -1) {
+                    return 0
+                }
+                throw IllegalArgumentException("Input byte[] should at least have 2 bytes for base64 bytes")
+            }
+            if (isMIME) {
+                // scan all bytes to fill out all non-alphabet. a performance
+                // trade-off of pre-scan or Arrays.copyOf
+                var n = 0
+                while (sp < sl) {
+                    var b = src[sp++].toInt() and 0xff
+                    if (b == '='.code) {
+                        len -= sl - sp + 1
+                        break
+                    }
+                    if (base64[b].also { b = it } == -1) {
+                        n++
+                    }
+                }
+                len -= n
+            } else {
+                if (src[sl - 1] == '='.code.toByte()) {
+                    paddings++
+                    if (src[sl - 2] == '='.code.toByte()) {
+                        paddings++
+                    }
+                }
+            }
+            if (paddings == 0 && len and 0x3 != 0) paddings = 4 - (len and 0x3)
+            return 3 * ((len + 3) / 4) - paddings
+        }
+
+        @Suppress("NAME_SHADOWING")
+        @Throws(IllegalArgumentException::class)
+        private fun decode0(src: ByteArray, sp: Int, sl: Int, dst: ByteArray): Int {
+            var sp = sp
+            val base64 = if (isURL) fromBase64URL else fromBase64
+            var dp = 0
+            var bits = 0
+            var shiftto = 18 // pos of first byte of 4-byte atom
+            while (sp < sl) {
+                var b = src[sp++].toInt() and 0xff
+                if (base64[b].also { b = it } < 0) {
+                    if (b == -2) { // padding byte '='
+                        // =     shiftto==18 unnecessary padding
+                        // x=    shiftto==12 a dangling single x
+                        // x     to be handled together with non-padding case
+                        // xx=   shiftto==6&&sp==sl missing last =
+                        // xx=y  shiftto==6 last is not =
+                        if (shiftto == 6 && (sp == sl || src[sp++] != '='.code.toByte()) ||
+                            shiftto == 18
+                        ) {
+                            throw IllegalArgumentException("Input byte array has wrong 4-byte ending unit")
+                        }
+                        break
+                    }
+                    if (isMIME) { // skip if for rfc2045
+                        continue
+                    } else {
+                        throw IllegalArgumentException("Illegal base64 character ${src[sp - 1].toInt().toString(16)}")
+                    }
+                }
+                bits = bits or (b shl shiftto)
+                shiftto -= 6
+                if (shiftto < 0) {
+                    dst[dp++] = (bits shr 16).toByte()
+                    dst[dp++] = (bits shr 8).toByte()
+                    dst[dp++] = bits.toByte()
+                    shiftto = 18
+                    bits = 0
+                }
+            }
+            // reached end of byte array or hit padding '=' characters.
+            when (shiftto) {
+                6 -> {
+                    dst[dp++] = (bits shr 16).toByte()
+                }
+                0 -> {
+                    dst[dp++] = (bits shr 16).toByte()
+                    dst[dp++] = (bits shr 8).toByte()
+                }
+                12 -> {
+                    // dangling single "x", incorrectly encoded.
+                    throw IllegalArgumentException("Last unit does not have enough valid bits")
+                }
+            }
+            // anything left is invalid, if is not MIME.
+            // if MIME, ignore all non-base64 character
+            while (sp < sl) {
+                if (isMIME && base64[src[sp++].toInt()] < 0) {
+                    continue
+                }
+                throw IllegalArgumentException("Input byte array has incorrect ending byte at $sp")
+            }
+            return dp
+        }
+
+        companion object {
+            /**
+             * Lookup table for decoding unicode characters drawn from the
+             * "Base64 Alphabet" (as specified in Table 1 of RFC 2045) into
+             * their 6-bit positive integer equivalents.  Characters that
+             * are not in the Base64 alphabet but fall within the bounds of
+             * the array are encoded to -1.
+             *
+             */
+            private val fromBase64 = IntArray(256)
+
+            init {
+                fromBase64.fill(-1)
+                // java.util.Arrays.fill(fromBase64, -1)
+                for (i in Encoder.toBase64.indices) fromBase64[Encoder.toBase64.get(i).code] =
+                    i
+                fromBase64['='.code] = -2
+            }
+
+            /**
+             * Lookup table for decoding "URL and Filename safe Base64 Alphabet"
+             * as specified in Table2 of the RFC 4648.
+             */
+            private val fromBase64URL = IntArray(256)
+
+            init {
+                fromBase64URL.fill(-1)
+                // java.util.Arrays.fill(fromBase64URL, -1)
+                for (i in Encoder.toBase64URL.indices) fromBase64URL[Encoder.toBase64URL[i].code] = i
+                fromBase64URL['='.code] = -2
+            }
+
+            val RFC4648 = Decoder(false, false)
+            val RFC4648_URLSAFE = Decoder(true, false)
+            val RFC2045 = Decoder(false, true)
+        }
+    }
+
+    /**
+     * This class implements an encoder for encoding byte data using
+     * the Base64 encoding scheme as specified in RFC 4648 and RFC 2045.
+     *
+     *
+     *  Instances of [Encoder] class are safe for use by
+     * multiple concurrent threads.
+     *
+     *
+     *  Unless otherwise noted, passing a `null` argument to
+     * a method of this class will cause a
+     * [NullPointerException][java.lang.NullPointerException] to
+     * be thrown.
+     *
+     * @see Decoder
+     *
+     * @since 1.8
+     */
+    private class Encoder private constructor(
+        private val isURL: Boolean,
+        private val newline: ByteArray?,
+        private val linemax: Int,
+        private val doPadding: Boolean
+    ) {
+        private fun outLength(srclen: Int): Int {
+            var len = if (doPadding) {
+                4 * ((srclen + 2) / 3)
+            } else {
+                val n = srclen % 3
+                4 * (srclen / 3) + if (n == 0) 0 else n + 1
+            }
+            if (linemax > 0) { // line separators
+                len += (len - 1) / linemax * newline!!.size
+            }
+            return len
+        }
+
+        /**
+         * Encodes all bytes from the specified byte array into a newly-allocated
+         * byte array using the [Base64] encoding scheme. The returned byte
+         * array is of the length of the resulting bytes.
+         *
+         * @param src
+         * the byte array to encode
+         * @return A newly-allocated byte array containing the resulting
+         * encoded bytes.
+         */
+        fun encode(src: ByteArray): ByteArray {
+            val len = outLength(src.size) // dst array size
+            val dst = ByteArray(len)
+            val ret = encode0(src, 0, src.size, dst)
+            return if (ret != dst.size) {
+                dst.copyInto(ByteArray(ret))
+                // java.util.Arrays.copyOf(dst, ret)
+            } else {
+                dst
+            }
+        }
+
+        /**
+         * Encodes all bytes from the specified byte array using the
+         * [Base64] encoding scheme, writing the resulting bytes to the
+         * given output byte array, starting at offset 0.
+         *
+         *
+         *  It is the responsibility of the invoker of this method to make
+         * sure the output byte array `dst` has enough space for encoding
+         * all bytes from the input byte array. No bytes will be written to the
+         * output byte array if the output byte array is not big enough.
+         *
+         * @param src
+         * the byte array to encode
+         * @param dst
+         * the output byte array
+         * @return The number of bytes written to the output byte array
+         *
+         * @throws IllegalArgumentException if `dst` does not have enough
+         * space for encoding all input bytes.
+         */
+        @Throws(IllegalArgumentException::class)
+        fun encode(src: ByteArray, dst: ByteArray): Int {
+            val len = outLength(src.size) // dst array size
+            if (dst.size < len) {
+                throw IllegalArgumentException("Output byte array is too small for encoding all input bytes")
+            }
+            return encode0(src, 0, src.size, dst)
+        }
+
+        /**
+         * Encodes the specified byte array into a String using the [Base64]
+         * encoding scheme.
+         *
+         *
+         *  This method first encodes all input bytes into a base64 encoded
+         * byte array and then constructs a new String by using the encoded byte
+         * array and the [ ISO-8859-1][java.nio.charset.StandardCharsets.ISO_8859_1] charset.
+         *
+         *
+         *  In other words, an invocation of this method has exactly the same
+         * effect as invoking
+         * `new String(encode(src), StandardCharsets.ISO_8859_1)`.
+         *
+         * @param src
+         * the byte array to encode
+         * @return A String containing the resulting Base64 encoded characters
+         */
+        fun encodeToString(src: ByteArray): String {
+            val encoded: ByteArray = encode(src)
+            return encoded.decodeToString()
+            // return String(encoded, 0, 0, encoded.size)
+        }
+
+        /**
+         * Returns an encoder instance that encodes equivalently to this one,
+         * but without adding any padding character at the end of the encoded
+         * byte data.
+         *
+         *
+         *  The encoding scheme of this encoder instance is unaffected by
+         * this invocation. The returned encoder instance should be used for
+         * non-padding encoding operation.
+         *
+         * @return an equivalent encoder that encodes without adding any
+         * padding character at the end
+         */
+        fun withoutPadding(): Encoder {
+            return if (!doPadding) this else Encoder(isURL, newline, linemax, false)
+        }
+
+        @Suppress("UNUSED_CHANGED_VALUE")
+        private fun encode0(src: ByteArray, off: Int, end: Int, dst: ByteArray): Int {
+            val base64 = if (isURL) toBase64URL else toBase64
+            var sp = off
+            var slen = (end - off) / 3 * 3
+            val sl = off + slen
+            if (linemax > 0 && slen > linemax / 4 * 3) slen = linemax / 4 * 3
+            var dp = 0
+            while (sp < sl) {
+                val sl0: Int = min(sp + slen, sl)
+                var sp0 = sp
+                var dp0 = dp
+                while (sp0 < sl0) {
+                    val bits = src[sp0++].toInt() and 0xff shl 16 or (
+                            src[sp0++].toInt() and 0xff shl 8
+                            ) or
+                            (src[sp0++].toInt() and 0xff)
+                    dst[dp0++] = base64[bits ushr 18 and 0x3f].code.toByte()
+                    dst[dp0++] = base64[bits ushr 12 and 0x3f].code.toByte()
+                    dst[dp0++] = base64[bits ushr 6 and 0x3f].code.toByte()
+                    dst[dp0++] = base64[bits and 0x3f].code.toByte()
+                }
+                val dlen = (sl0 - sp) / 3 * 4
+                dp += dlen
+                sp = sl0
+                if (dlen == linemax && sp < end) {
+                    for (b in newline!!) {
+                        dst[dp++] = b
+                    }
+                }
+            }
+            if (sp < end) { // 1 or 2 leftover bytes
+                val b0 = src[sp++].toInt() and 0xff
+                dst[dp++] = base64[b0 shr 2].code.toByte()
+                if (sp == end) {
+                    dst[dp++] = base64[b0 shl 4 and 0x3f].code.toByte()
+                    if (doPadding) {
+                        dst[dp++] = '='.code.toByte()
+                        dst[dp++] = '='.code.toByte()
+                    }
+                } else {
+                    val b1 = src[sp++].toInt() and 0xff
+                    dst[dp++] = base64[b0 shl 4 and 0x3f or (b1 shr 4)].code.toByte()
+                    dst[dp++] = base64[b1 shl 2 and 0x3f].code.toByte()
+                    if (doPadding) {
+                        dst[dp++] = '='.code.toByte()
+                    }
+                }
+            }
+            return dp
+        }
+
+        companion object {
+            /**
+             * This array is a lookup table that translates 6-bit positive integer
+             * index values into their "Base64 Alphabet" equivalents as specified
+             * in "Table 1: The Base64 Alphabet" of RFC 2045 (and RFC 4648).
+             */
+            val toBase64 = charArrayOf(
+                'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
+                'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+                'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+                'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+                '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
+            )
+
+            /**
+             * It's the lookup table for "URL and Filename safe Base64" as specified
+             * in Table 2 of the RFC 4648, with the '+' and '/' changed to '-' and
+             * '_'. This table is used when BASE64_URL is specified.
+             */
+            val toBase64URL = charArrayOf(
+                'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
+                'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+                'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+                'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+                '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
+            )
+            private const val MIMELINEMAX = 76
+            private val CRLF = byteArrayOf('\r'.code.toByte(), '\n'.code.toByte())
+            val RFC4648 = Encoder(false, null, -1, true)
+            val RFC4648_URLSAFE = Encoder(true, null, -1, true)
+            val RFC2045 = Encoder(false, CRLF, MIMELINEMAX, true)
+        }
+    }
+}
diff --git a/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base64/ByteArrayExt.kt b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base64/ByteArrayExt.kt
new file mode 100644
index 0000000..cc0a1e8
--- /dev/null
+++ b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base64/ByteArrayExt.kt
@@ -0,0 +1,77 @@
+package io.iohk.atala.prism.didcomm.didpeer.base64
+
+/**
+ * Convert [ByteArray] to [CharArray]
+ * @return [CharArray]
+ */
+fun ByteArray.asCharArray(): CharArray {
+    val chars = CharArray(size)
+    for (i in chars.indices) {
+        chars[i] = get(i).toInt().toChar()
+    }
+    return chars
+}
+
+// Base64Standard
+
+/**
+ * Encode a [ByteArray] to Base64 [String] standard encoding
+ * RFC 4648 Section 4
+ */
+val ByteArray.base64Encoded: String
+    get() = Base64.encodeToString(this)
+
+/**
+ * Decode a [ByteArray] Base64 standard encoded to [String]
+ * RFC 4648 Section 4
+ */
+val ByteArray.base64Decoded: String
+    get() = Base64.decode(this.decodeToString()).decodeToString()
+
+// Base64Standard with padding
+
+/**
+ * Encode a [ByteArray] to Base64 [String] standard encoding
+ * RFC 4648 Section 4
+ */
+val ByteArray.base64PadEncoded: String
+    get() = Base64.encodeToString(this, Encoding.StandardPad)
+
+/**
+ * Decode a [ByteArray] Base64 standard encoded to [String]
+ * RFC 4648 Section 4
+ */
+val ByteArray.base64PadDecoded: String
+    get() = Base64.decode(this.decodeToString(), Encoding.StandardPad).decodeToString()
+
+// Base64URL
+
+/**
+ * Decode a [ByteArray] Base64 URL-safe encoded to [String].
+ * RFC 4648 Section 5
+ */
+val ByteArray.base64UrlDecoded: String
+    get() = Base64.decode(this.decodeToString(), Encoding.UrlSafe).decodeToString()
+
+/**
+ * Encode a [ByteArray] to Base64 URL-safe encoded [String].
+ * RFC 4648 Section 5
+ */
+val ByteArray.base64UrlEncoded: String
+    get() = Base64.encodeToString(this, Encoding.UrlSafe)
+
+// Base64URL with padding
+
+/**
+ * Decode a [ByteArray] Base64 URL-safe encoded to [String].
+ * RFC 4648 Section 5
+ */
+val ByteArray.base64UrlPadDecoded: String
+    get() = Base64.encodeToString(this, Encoding.UrlSafePad)
+
+/**
+ * Encode a [ByteArray] to Base64 URL-safe encoded [String].
+ * RFC 4648 Section 5
+ */
+val ByteArray.base64UrlPadEncoded: String
+    get() = Base64.encodeToString(this, Encoding.UrlSafePad)
diff --git a/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base64/Encoding.kt b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base64/Encoding.kt
new file mode 100644
index 0000000..355a38f
--- /dev/null
+++ b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base64/Encoding.kt
@@ -0,0 +1,36 @@
+package io.iohk.atala.prism.didcomm.didpeer.base64
+
+/**
+ * Base64 encoding scheme
+ */
+sealed interface Encoding {
+    val alphabet: String
+
+    /**
+     * Base64 Standard
+     */
+    data object Standard : Encoding {
+        override val alphabet: String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+    }
+
+    /**
+     * Base64 Standard
+     */
+    data object StandardPad : Encoding {
+        override val alphabet: String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
+    }
+
+    /**
+     * Base64 URL
+     */
+    data object UrlSafe : Encoding {
+        override val alphabet: String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
+    }
+
+    /**
+     * Base64 URL
+     */
+    data object UrlSafePad : Encoding {
+        override val alphabet: String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_="
+    }
+}
diff --git a/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base64/StringExt.kt b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base64/StringExt.kt
new file mode 100644
index 0000000..f303299
--- /dev/null
+++ b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/base64/StringExt.kt
@@ -0,0 +1,91 @@
+package io.iohk.atala.prism.didcomm.didpeer.base64
+
+// Base64Standard
+/**
+ * Encode a [String] to Base64 [String] standard encoding
+ * RFC 4648 Section 4
+ */
+val String.base64Encoded: String
+    get() = Base64.encodeToString(this.encodeToByteArray(), Encoding.Standard)
+
+/**
+ * Decode a Base64 [String] standard encoded to [String].
+ * RFC 4648 Section 4
+ */
+val String.base64Decoded: String
+    get() = Base64.decode(this, Encoding.Standard).decodeToString()
+
+/**
+ * Decode a Base64 [String] standard encoded to [ByteArray].
+ * RFC 4648 Section 4
+ */
+val String.base64DecodedBytes: ByteArray
+    get() = Base64.decode(this, Encoding.Standard)
+
+// Standard with Padding
+/**
+ * Encode a [String] to Base64 [String] standard encoding
+ * RFC 4648 Section 4
+ */
+val String.base64PadEncoded: String
+    get() = Base64.encodeToString(this.encodeToByteArray(), Encoding.StandardPad)
+
+/**
+ * Decode a Base64 [String] standard encoded to [String].
+ * RFC 4648 Section 4
+ */
+val String.base64PadDecoded: String
+    get() = base64PadDecodedBytes.decodeToString()
+
+/**
+ * Decode a Base64 [String] standard encoded to [ByteArray].
+ * RFC 4648 Section 4
+ */
+val String.base64PadDecodedBytes: ByteArray
+    get() = Base64.decode(this, Encoding.StandardPad)
+
+// Base64URL
+/**
+ * Encode a [String] to Base64 URL-safe encoded [String].
+ * RFC 4648 Section 5
+ * See [RFC 4648 §5](https://datatracker.ietf.org/doc/html/rfc4648#section-5)
+ */
+val String.base64UrlEncoded: String
+    get() = Base64.encodeToString(this.encodeToByteArray(), Encoding.UrlSafe)
+
+/**
+ * Decode a Base64 URL-safe encoded [String] to [String].
+ * RFC 4648 Section 5
+ */
+val String.base64UrlDecoded: String
+    get() = base64UrlDecodedBytes.decodeToString()
+
+/**
+ * Decode a Base64 URL-safe encoded [String] to [ByteArray].
+ * RFC 4648 Section 5
+ */
+val String.base64UrlDecodedBytes: ByteArray
+    get() = Base64.decode(this, Encoding.UrlSafe)
+
+// Base64URL with padding
+/**
+ * Encode a [String] to Base64 URL-safe encoded [String].
+ * RFC 4648 Section 5
+ * See [RFC 4648 §5](https://datatracker.ietf.org/doc/html/rfc4648#section-5)
+ */
+val String.base64UrlPadEncoded: String
+    get() = Base64.encodeToString(this.encodeToByteArray(), Encoding.UrlSafePad)
+
+/**
+ * Decode a Base64 URL-safe encoded [String] to [String].
+ * RFC 4648 Section 5
+ */
+val String.base64UrlPadDecoded: String
+    get() = base64UrlPadDecodedBytes.decodeToString()
+
+/**
+ * Decode a Base64 URL-safe encoded [String] to [ByteArray].
+ * RFC 4648 Section 5
+ */
+val String.base64UrlPadDecodedBytes: ByteArray
+    get() = Base64.decode(this, Encoding.UrlSafePad)
diff --git a/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/core/JWKOKP.kt b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/core/JWKOKP.kt
index bbfe2c6..8edbfe7 100644
--- a/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/core/JWKOKP.kt
+++ b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/core/JWKOKP.kt
@@ -1,7 +1,7 @@
 package io.iohk.atala.prism.didcomm.didpeer.core
 
-import io.iohk.atala.prism.apollo.base64.base64UrlDecodedBytes
-import io.iohk.atala.prism.apollo.base64.base64UrlEncoded
+import io.iohk.atala.prism.didcomm.didpeer.base64.base64UrlDecodedBytes
+import io.iohk.atala.prism.didcomm.didpeer.base64.base64UrlEncoded
 import io.iohk.atala.prism.didcomm.didpeer.VerificationMaterialPeerDID
 import io.iohk.atala.prism.didcomm.didpeer.VerificationMethodTypeAgreement
 import io.iohk.atala.prism.didcomm.didpeer.VerificationMethodTypeAuthentication
diff --git a/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/core/Multibase.kt b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/core/Multibase.kt
index c6564f4..d14e338 100644
--- a/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/core/Multibase.kt
+++ b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/core/Multibase.kt
@@ -1,8 +1,8 @@
 package io.iohk.atala.prism.didcomm.didpeer.core
 
-import io.iohk.atala.prism.apollo.base58.base58BtcDecodedBytes
-import io.iohk.atala.prism.apollo.base58.base58BtcEncoded
-import io.iohk.atala.prism.apollo.multibase.MultiBase
+import io.iohk.atala.prism.didcomm.didpeer.base58.base58BtcDecodedBytes
+import io.iohk.atala.prism.didcomm.didpeer.base58.base58BtcEncoded
+import io.iohk.atala.prism.didcomm.didpeer.multibase.MultiBase
 
 /**
  * Converts a byte array to a base58 multibase encoding.
diff --git a/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/core/Multicodec.kt b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/core/Multicodec.kt
index 0a0aab1..104150f 100644
--- a/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/core/Multicodec.kt
+++ b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/core/Multicodec.kt
@@ -1,6 +1,6 @@
 package io.iohk.atala.prism.didcomm.didpeer.core
 
-import io.iohk.atala.prism.apollo.varint.VarInt
+import io.iohk.atala.prism.didcomm.didpeer.varint.VarInt
 import io.iohk.atala.prism.didcomm.didpeer.VerificationMethodTypeAgreement
 import io.iohk.atala.prism.didcomm.didpeer.VerificationMethodTypeAuthentication
 import io.iohk.atala.prism.didcomm.didpeer.VerificationMethodTypePeerDID
diff --git a/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/core/PeerDIDHelper.kt b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/core/PeerDIDHelper.kt
index 5303061..8f2b5f8 100644
--- a/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/core/PeerDIDHelper.kt
+++ b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/core/PeerDIDHelper.kt
@@ -2,8 +2,8 @@
 
 package io.iohk.atala.prism.didcomm.didpeer.core
 
-import io.iohk.atala.prism.apollo.base64.base64PadDecoded
-import io.iohk.atala.prism.apollo.base64.base64UrlEncoded
+import io.iohk.atala.prism.didcomm.didpeer.base64.base64PadDecoded
+import io.iohk.atala.prism.didcomm.didpeer.base64.base64UrlEncoded
 import io.iohk.atala.prism.didcomm.didpeer.JSON
 import io.iohk.atala.prism.didcomm.didpeer.OtherService
 import io.iohk.atala.prism.didcomm.didpeer.PeerDID
diff --git a/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/multibase/MultiBase.kt b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/multibase/MultiBase.kt
new file mode 100644
index 0000000..acddeb9
--- /dev/null
+++ b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/multibase/MultiBase.kt
@@ -0,0 +1,133 @@
+package io.iohk.atala.prism.didcomm.didpeer.multibase
+
+import io.iohk.atala.prism.didcomm.didpeer.base16.base16DecodedBytes
+import io.iohk.atala.prism.didcomm.didpeer.base16.base16Encoded
+import io.iohk.atala.prism.didcomm.didpeer.base16.base16UpperDecodedBytes
+import io.iohk.atala.prism.didcomm.didpeer.base16.base16UpperEncoded
+import io.iohk.atala.prism.didcomm.didpeer.base32.base32DecodedBytes
+import io.iohk.atala.prism.didcomm.didpeer.base32.base32Encoded
+import io.iohk.atala.prism.didcomm.didpeer.base32.base32HexDecodedBytes
+import io.iohk.atala.prism.didcomm.didpeer.base32.base32HexEncoded
+import io.iohk.atala.prism.didcomm.didpeer.base32.base32HexPadDecodedBytes
+import io.iohk.atala.prism.didcomm.didpeer.base32.base32HexPadEncoded
+import io.iohk.atala.prism.didcomm.didpeer.base32.base32HexUpperDecodedBytes
+import io.iohk.atala.prism.didcomm.didpeer.base32.base32HexUpperEncoded
+import io.iohk.atala.prism.didcomm.didpeer.base32.base32HexUpperPadDecodedBytes
+import io.iohk.atala.prism.didcomm.didpeer.base32.base32HexUpperPadEncoded
+import io.iohk.atala.prism.didcomm.didpeer.base32.base32PadDecodedBytes
+import io.iohk.atala.prism.didcomm.didpeer.base32.base32PadEncoded
+import io.iohk.atala.prism.didcomm.didpeer.base32.base32UpperDecodedBytes
+import io.iohk.atala.prism.didcomm.didpeer.base32.base32UpperEncoded
+import io.iohk.atala.prism.didcomm.didpeer.base32.base32UpperPadDecodedBytes
+import io.iohk.atala.prism.didcomm.didpeer.base32.base32UpperPadEncoded
+import io.iohk.atala.prism.didcomm.didpeer.base58.base58BtcDecodedBytes
+import io.iohk.atala.prism.didcomm.didpeer.base58.base58BtcEncoded
+import io.iohk.atala.prism.didcomm.didpeer.base58.base58FlickrDecodedBytes
+import io.iohk.atala.prism.didcomm.didpeer.base58.base58FlickrEncoded
+import io.iohk.atala.prism.didcomm.didpeer.base64.base64DecodedBytes
+import io.iohk.atala.prism.didcomm.didpeer.base64.base64Encoded
+import io.iohk.atala.prism.didcomm.didpeer.base64.base64PadDecodedBytes
+import io.iohk.atala.prism.didcomm.didpeer.base64.base64PadEncoded
+import io.iohk.atala.prism.didcomm.didpeer.base64.base64UrlDecodedBytes
+import io.iohk.atala.prism.didcomm.didpeer.base64.base64UrlEncoded
+import io.iohk.atala.prism.didcomm.didpeer.base64.base64UrlPadDecodedBytes
+import io.iohk.atala.prism.didcomm.didpeer.base64.base64UrlPadEncoded
+
+/**
+ * MultiBase Implementation of [multibase](https://github.com/multiformats/multibase) -self identifying base encodings- in KMM
+ */
+final object MultiBase {
+
+    fun encode(base: Base, data: String): String {
+        return encode(base, data.encodeToByteArray())
+    }
+
+    fun encode(base: Base, data: ByteArray): String {
+        return when (base) {
+            Base.BASE16 -> "${base.prefix}${data.base16Encoded}"
+            Base.BASE16_UPPER -> "${base.prefix}${data.base16UpperEncoded}"
+
+            Base.BASE32 -> "${base.prefix}${data.base32Encoded}"
+            Base.BASE32_UPPER -> "${base.prefix}${data.base32UpperEncoded}"
+            Base.BASE32_PAD -> "${base.prefix}${data.base32PadEncoded}"
+            Base.BASE32_UPPER_PAD -> "${base.prefix}${data.base32UpperPadEncoded}"
+            Base.BASE32_HEX -> "${base.prefix}${data.base32HexEncoded}"
+            Base.BASE32_HEX_UPPER -> "${base.prefix}${data.base32HexUpperEncoded}"
+            Base.BASE32_HEX_PAD -> "${base.prefix}${data.base32HexPadEncoded}"
+            Base.BASE32_HEX_UPPER_PAD -> "${base.prefix}${data.base32HexUpperPadEncoded}"
+
+            Base.BASE58_FLICKR -> "${base.prefix}${data.base58FlickrEncoded}"
+            Base.BASE58_BTC -> "${base.prefix}${data.base58BtcEncoded}"
+
+            Base.BASE64 -> "${base.prefix}${data.base64Encoded}"
+            Base.BASE64_URL -> "${base.prefix}${data.base64UrlEncoded}"
+            Base.BASE64_PAD -> "${base.prefix}${data.base64PadEncoded}"
+            Base.BASE64_URL_PAD -> "${base.prefix}${data.base64UrlPadEncoded}"
+        }
+    }
+
+    @Throws(IllegalStateException::class)
+    fun decode(data: String): ByteArray {
+        val prefix = data[0]
+        val rest = data.substring(1)
+        return when (Base.lookup(prefix)) {
+            Base.BASE16 -> rest.base16DecodedBytes
+            Base.BASE16_UPPER -> rest.base16UpperDecodedBytes
+
+            Base.BASE32 -> rest.base32DecodedBytes
+            Base.BASE32_UPPER -> rest.base32UpperDecodedBytes
+            Base.BASE32_PAD -> rest.base32PadDecodedBytes
+            Base.BASE32_UPPER_PAD -> rest.base32UpperPadDecodedBytes
+            Base.BASE32_HEX -> rest.base32HexDecodedBytes
+            Base.BASE32_HEX_UPPER -> rest.base32HexUpperDecodedBytes
+            Base.BASE32_HEX_PAD -> rest.base32HexPadDecodedBytes
+            Base.BASE32_HEX_UPPER_PAD -> rest.base32HexUpperPadDecodedBytes
+
+            Base.BASE58_FLICKR -> rest.base58FlickrDecodedBytes
+            Base.BASE58_BTC -> rest.base58BtcDecodedBytes
+
+            Base.BASE64 -> rest.base64DecodedBytes
+            Base.BASE64_URL -> rest.base64UrlDecodedBytes
+            Base.BASE64_PAD -> rest.base64PadDecodedBytes
+            Base.BASE64_URL_PAD -> rest.base64UrlPadDecodedBytes
+        }
+    }
+
+    enum class Base(val prefix: Char) {
+        BASE16('f'),
+        BASE16_UPPER('F'),
+
+        BASE32('b'),
+        BASE32_UPPER('B'),
+        BASE32_PAD('c'),
+        BASE32_UPPER_PAD('C'),
+        BASE32_HEX('v'),
+        BASE32_HEX_UPPER('V'),
+        BASE32_HEX_PAD('t'),
+        BASE32_HEX_UPPER_PAD('T'),
+
+        BASE58_FLICKR('Z'),
+        BASE58_BTC('z'),
+
+        BASE64('m'),
+        BASE64_URL('u'),
+        BASE64_PAD('M'),
+        BASE64_URL_PAD('U');
+
+        companion object {
+            private val baseMap: MutableMap<Char, Base> = mutableMapOf()
+
+            init {
+                for (base in values()) {
+                    baseMap[base.prefix] = base
+                }
+            }
+
+            @Throws(IllegalStateException::class)
+            fun lookup(prefix: Char): Base {
+                return baseMap[prefix]
+                    ?: throw IllegalStateException("Unknown Multibase type: $prefix")
+            }
+        }
+    }
+}
diff --git a/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/varint/VarInt.kt b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/varint/VarInt.kt
new file mode 100644
index 0000000..5f578ad
--- /dev/null
+++ b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/varint/VarInt.kt
@@ -0,0 +1,29 @@
+package io.iohk.atala.prism.didcomm.didpeer.varint
+
+import okio.Buffer
+
+object VarInt : VarIntInterface {
+    override fun write(value: Int, byteBuffer: Buffer) {
+        var value = value
+        while ((value and -0x80).toLong() != 0L) {
+            byteBuffer.writeByte(value and 0x7F or 0x80)
+            value = value ushr 7
+        }
+        byteBuffer.writeByte(value and 0x7F)
+    }
+
+    override fun read(byteBuffer: Buffer): Int {
+        var value = 0
+        var i = 0
+        var b = 0
+        while (byteBuffer.size > 0 && byteBuffer.readByte().toInt().also { b = it } and 0x80 != 0) {
+            value = value or (b and 0x7F shl i)
+            i += 7
+            if (i > 35) {
+                throw IllegalArgumentException("Variable length quantity is too long")
+            }
+        }
+        value = value or (b shl i)
+        return value
+    }
+}
diff --git a/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/varint/VarIntInterface.kt b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/varint/VarIntInterface.kt
new file mode 100644
index 0000000..15b2d0a
--- /dev/null
+++ b/didpeer/src/commonMain/kotlin/io/iohk/atala/prism/didcomm/didpeer/varint/VarIntInterface.kt
@@ -0,0 +1,8 @@
+package io.iohk.atala.prism.didcomm.didpeer.varint
+
+import okio.Buffer
+
+interface VarIntInterface {
+    fun write(value: Int, byteBuffer: Buffer)
+    fun read(byteBuffer: Buffer): Int
+}
diff --git a/didpeer/src/commonTest/kotlin/io/iohk/atala/prism/didcomm/didpeer/base16/Base16Tests.kt b/didpeer/src/commonTest/kotlin/io/iohk/atala/prism/didcomm/didpeer/base16/Base16Tests.kt
new file mode 100644
index 0000000..39a04c5
--- /dev/null
+++ b/didpeer/src/commonTest/kotlin/io/iohk/atala/prism/didcomm/didpeer/base16/Base16Tests.kt
@@ -0,0 +1,62 @@
+package io.iohk.atala.prism.didcomm.didpeer.base16
+
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class Base16Tests {
+
+    @Test
+    fun testEncodeBase16() {
+        assertEquals("57656C636F6D6520746F20494F4721".lowercase(), "Welcome to IOG!".base16Encoded)
+    }
+
+    @Test
+    fun testDecodeBase16() {
+        assertEquals("Welcome to IOG!", "57656C636F6D6520746F20494F4721".lowercase().base16Decoded)
+    }
+
+    @Test
+    fun testEncodeBase16Upper() {
+        assertEquals("57656C636F6D6520746F20494F4721", "Welcome to IOG!".base16UpperEncoded)
+    }
+
+    @Test
+    fun testDecodeBase16Upper() {
+        assertEquals("Welcome to IOG!", "57656C636F6D6520746F20494F4721".base16UpperDecoded)
+    }
+
+    @Test
+    fun testEncodeBase16RFC_4648() {
+        assertEquals("", "".base16Encoded)
+    }
+
+    @Test
+    fun testEncodeBase16RFC_4648_1() {
+        assertEquals("66", "f".base16Encoded)
+    }
+
+    @Test
+    fun testEncodeBase16RFC_4648_2() {
+        assertEquals("666F", "fo".base16Encoded.uppercase())
+    }
+
+    @Test
+    fun testEncodeBase16RFC_4648_3() {
+        assertEquals("666F6F", "foo".base16Encoded.uppercase())
+    }
+
+    @Test
+    fun testEncodeBase16RFC_4648_4() {
+        assertEquals("666F6F62", "foob".base16Encoded.uppercase())
+    }
+
+    @Test
+    fun testEncodeBase16RFC_4648_5() {
+        assertEquals("666F6F6261", "fooba".base16Encoded.uppercase())
+    }
+
+    @Test
+    fun testEncodeBase16RFC_4648_6() {
+        assertEquals("666F6F626172", "foobar".base16Encoded.uppercase())
+    }
+}
diff --git a/didpeer/src/commonTest/kotlin/io/iohk/atala/prism/didcomm/didpeer/base32/Base32Tests.kt b/didpeer/src/commonTest/kotlin/io/iohk/atala/prism/didcomm/didpeer/base32/Base32Tests.kt
new file mode 100644
index 0000000..9d45678
--- /dev/null
+++ b/didpeer/src/commonTest/kotlin/io/iohk/atala/prism/didcomm/didpeer/base32/Base32Tests.kt
@@ -0,0 +1,127 @@
+package io.iohk.atala.prism.didcomm.didpeer.base32
+
+import kotlin.test.Test
+import kotlin.test.assertContentEquals
+import kotlin.test.assertEquals
+
+class Base32Tests {
+    @Test
+    fun testEncodeBase32() {
+        assertEquals("K5SWYY3PNVSSA5DPEBEU6RZB".lowercase(), "Welcome to IOG!".base32Encoded)
+    }
+
+    @Test
+    fun testDecodeBase32() {
+        assertEquals("Welcome to IOG!", "K5SWYY3PNVSSA5DPEBEU6RZB".lowercase().base32Decoded)
+    }
+
+    @Test
+    fun testEncodeBase32WithPadding() {
+        assertEquals("K5SWYY3PNVSSA5DPEBEU6RZB".lowercase(), "Welcome to IOG!".base32PadEncoded)
+    }
+
+    @Test
+    fun testDecodeBase32WithPadding() {
+        assertEquals("Welcome to IOG!", "K5SWYY3PNVSSA5DPEBEU6RZB".lowercase().base32PadDecoded)
+    }
+
+    @Test
+    fun testEncodeBase32Upper() {
+        assertEquals("K5SWYY3PNVSSA5DPEBEU6RZB", "Welcome to IOG!".base32UpperEncoded)
+    }
+
+    @Test
+    fun testDecodeBase32Upper() {
+        assertEquals("Welcome to IOG!", "K5SWYY3PNVSSA5DPEBEU6RZB".base32UpperDecoded)
+    }
+
+    @Test
+    fun testEncodeBase32UpperWithPadding() {
+        "".encodeToByteArray()
+        assertEquals("K5SWYY3PNVSSA5DPEBEU6RZB", "Welcome to IOG!".base32UpperPadEncoded)
+    }
+
+    @Test
+    fun testDecodeBase32UpperWithPadding() {
+        assertEquals("Welcome to IOG!", "K5SWYY3PNVSSA5DPEBEU6RZB".base32UpperPadDecoded)
+    }
+
+    @Test
+    fun testDecodeBase32HexUpper() {
+        // Hex value of "Welcome to IOG!" in hex
+        val expected = byteArrayOf(1, 17, -107, -115, -107, -71, -47, -55, -123, -79, -91, -23, -108, -127, -107, -39, -107, -55, -27, -47, -95, -91, -71, -100, -124, -124, -124)
+        assertContentEquals(expected, "8him6pbeehp62r39f9ii0pbmclp7it38d5n6e89144".uppercase().base32HexUpperDecodedBytes)
+    }
+
+    @Test
+    fun testDecodeBase32HexUpperWithPadding() {
+        // Hex value of "Welcome to IOG!" in hex
+        val expected = byteArrayOf(1, 17, -107, -115, -107, -71, -47, -55, -123, -79, -91, -23, -108, -127, -107, -39, -107, -55, -27, -47, -95, -91, -71, -100, -124, -124, -124)
+        assertContentEquals(expected, "8him6pbeehp62r39f9ii0pbmclp7it38d5n6e89144".uppercase().base32HexUpperPadDecodedBytes)
+    }
+
+    @Test
+    fun testEncodeBase32_RFC_4648_1() {
+        assertEquals("MZXW6===", "foo".base32UpperPadEncoded)
+        assertEquals("foo", "MZXW6===".base32UpperPadDecoded)
+    }
+
+    @Test
+    fun testEncodeBase32_RFC_4648_2() {
+        assertEquals("MZXW6YQ=", "foob".base32UpperPadEncoded)
+        assertEquals("foob", "MZXW6YQ=".base32UpperPadDecoded)
+    }
+
+    @Test
+    fun testEncodeBase32_RFC_4648_3() {
+        assertEquals("MZXW6YTB", "fooba".base32UpperPadEncoded)
+        assertEquals("fooba", "MZXW6YTB".base32UpperPadDecoded)
+    }
+
+    @Test
+    fun testEncodeBase32_RFC_4648_4() {
+        assertEquals("MZXW6YTBOI======", "foobar".base32UpperPadEncoded)
+        assertEquals("foobar", "MZXW6YTBOI======".base32UpperPadDecoded)
+    }
+
+    @Test
+    fun testEncodeBase32_RFC_4648_5() {
+        assertEquals("", "".base32Encoded)
+    }
+
+    @Test
+    fun testEncodeBase32_RFC_4648_6() {
+        assertEquals("CO======", "f".base32HexUpperPadEncoded)
+        assertEquals("f", "CO======".base32HexUpperPadDecoded)
+    }
+
+    @Test
+    fun testEncodeBase32_RFC_4648_7() {
+        assertEquals("CPNG====", "fo".base32HexUpperPadEncoded)
+        assertEquals("fo", "CPNG====".base32HexUpperPadDecoded)
+    }
+
+    @Test
+    fun testEncodeBase32_RFC_4648_8() {
+        assertEquals("CPNMU===", "foo".base32HexUpperPadEncoded)
+        assertEquals("foo", "CPNMU===".base32HexUpperPadDecoded)
+    }
+
+    @Test
+    fun testEncodeBase32_RFC_4648_9() {
+        assertEquals("CPNMUOG=", "foob".base32HexUpperPadEncoded)
+        assertEquals("foob", "CPNMUOG=".base32HexUpperPadDecoded)
+    }
+
+    @Test
+    fun testEncodeBase32_RFC_4648_10() {
+        assertEquals("CPNMUOJ1", "fooba".base32HexUpperPadEncoded)
+        assertEquals("fooba", "CPNMUOJ1".base32HexUpperPadDecoded)
+    }
+
+    @Test
+    fun testEncodeBase32_RFC_4648_11() {
+        assertEquals("CPNMUOJ1E8======", "foobar".base32HexUpperPadEncoded)
+        assertEquals("foobar", "CPNMUOJ1E8======".base32HexUpperPadDecoded)
+    }
+}
diff --git a/didpeer/src/commonTest/kotlin/io/iohk/atala/prism/didcomm/didpeer/base58/Base58Tests.kt b/didpeer/src/commonTest/kotlin/io/iohk/atala/prism/didcomm/didpeer/base58/Base58Tests.kt
new file mode 100644
index 0000000..b05eb79
--- /dev/null
+++ b/didpeer/src/commonTest/kotlin/io/iohk/atala/prism/didcomm/didpeer/base58/Base58Tests.kt
@@ -0,0 +1,26 @@
+package io.iohk.atala.prism.didcomm.didpeer.base58
+
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class Base58Tests {
+    @Test
+    fun testEncodeBase58BTC() {
+        assertEquals("Z8XgFYKj8dyvwswt51Y", "Welcome to IOG".base58BtcEncoded)
+    }
+
+    @Test
+    fun testDecodeBase58BTC() {
+        assertEquals("Welcome to IOG", "Z8XgFYKj8dyvwswt51Y".base58BtcDecoded)
+    }
+
+    @Test
+    fun testEncodeBase58Flickr() {
+        assertEquals("y8wFfxjJ8CYVWSWT51x", "Welcome to IOG".base58FlickrEncoded)
+    }
+
+    @Test
+    fun testDecodeBase58Flickr() {
+        assertEquals("Welcome to IOG", "y8wFfxjJ8CYVWSWT51x".base58FlickrDecoded)
+    }
+}
diff --git a/didpeer/src/commonTest/kotlin/io/iohk/atala/prism/didcomm/didpeer/base64/Base64Tests.kt b/didpeer/src/commonTest/kotlin/io/iohk/atala/prism/didcomm/didpeer/base64/Base64Tests.kt
new file mode 100644
index 0000000..f2089f5
--- /dev/null
+++ b/didpeer/src/commonTest/kotlin/io/iohk/atala/prism/didcomm/didpeer/base64/Base64Tests.kt
@@ -0,0 +1,194 @@
+package io.iohk.atala.prism.didcomm.didpeer.base64
+
+import kotlin.test.Test
+import kotlin.test.assertContentEquals
+import kotlin.test.assertEquals
+
+class Base64Tests {
+    // Extension
+    @Test
+    fun byteArray_asCharArray() {
+        assertContentEquals(
+            charArrayOf('"', 'F', '	', 'ᅣ', '_', '\'', '!', Char.MIN_VALUE, '�', 'E', 'ホ', '￘', 'モ', 'ᆰ', '￶', '│'),
+            byteArrayOf(34, 70, 9, -60, 95, 39, 33, 0, -3, 69, -114, -40, -109, -86, -10, -24).asCharArray()
+        )
+    }
+
+    // Base64Standard
+    @Test
+    fun byteArray_base64Decoded() {
+        assertEquals("Hello, world!", "SGVsbG8sIHdvcmxkIQ==".base64PadDecoded)
+        assertContentEquals(
+            byteArrayOf(
+                -94, 124, -26, -112, -72, -84, 16, 11, 67, -45, 107, 38, -99, 79, 62, -49, 83, 26, -85, -70, -122, 53,
+                67, 42, -94, -87, 61, -74, 66, 0, 80, -125, -17, -11, -125, 63, 109, -15, 56, -95, -33, 18, 110, 47,
+                47, -20, -72, -34, 53, -69, 49, -45, 54, 53, -21, 43, 9, -84, -125, 72, -61, 76, 31, -46
+            ),
+            "onzmkLisEAtD02smnU8+z1Maq7qGNUMqoqk9tkIAUIPv9YM/bfE4od8Sbi8v7LjeNbsx0zY16ysJrINIw0wf0g==".base64PadDecodedBytes
+        )
+    }
+
+    @Test
+    fun byteArray_base64Encoded() {
+        assertEquals(
+            "xvrp9DBWlei2mG0ov9MN+A==", // value1
+            byteArrayOf(-58, -6, -23, -12, 48, 86, -107, -24, -74, -104, 109, 40, -65, -45, 13, -8).base64PadEncoded
+        )
+        assertEquals(
+            "IkYJxF8nIQD9RY7Yk6r26A==", // value222
+            byteArrayOf(34, 70, 9, -60, 95, 39, 33, 0, -3, 69, -114, -40, -109, -86, -10, -24).base64PadEncoded
+        )
+        assertEquals(
+            "U0GeVBi2dNcdL2IO0nJo5Q==", // value555
+            byteArrayOf(83, 65, -98, 84, 24, -74, 116, -41, 29, 47, 98, 14, -46, 114, 104, -27).base64PadEncoded
+        )
+    }
+
+    @Test
+    fun string_base64Decoded() {
+        assertEquals("word", "d29yZA==".base64PadDecoded)
+        assertEquals("Word", "V29yZA==".base64PadDecoded)
+        assertEquals("Hello", "SGVsbG8=".base64PadDecoded)
+        assertEquals("World!", "V29ybGQh".base64PadDecoded)
+        assertEquals("Hello, world!", "SGVsbG8sIHdvcmxkIQ==".base64PadDecoded)
+        assertEquals(
+            Encoding.Standard.alphabet,
+            "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3ODkrLw==".base64PadDecoded
+        )
+        assertEquals("abcd", "YWJjZA==".base64PadDecoded)
+        assertEquals("saschpe", "c2FzY2hwZQ==".base64PadDecoded)
+        assertEquals(
+            "1234567890-=!@#\$%^&*()_+qwertyuiop[];'\\,./?><|\":}{P`~",
+            "MTIzNDU2Nzg5MC09IUAjJCVeJiooKV8rcXdlcnR5dWlvcFtdOydcLC4vPz48fCI6fXtQYH4=".base64PadDecoded
+        )
+    }
+
+    @Test
+    fun string_base64Encoded() {
+        assertEquals("d29yZA==", "word".base64PadEncoded)
+        assertEquals("V29yZA==", "Word".base64PadEncoded)
+        assertEquals("SGVsbG8=", "Hello".base64PadEncoded)
+        assertEquals("V29ybGQh", "World!".base64PadEncoded)
+        assertEquals("SGVsbG8sIHdvcmxkIQ==", "Hello, world!".base64PadEncoded)
+        assertEquals("SGVsbG8sIHdvcmxkIQ==", "Hello, world!".encodeToByteArray().base64PadEncoded)
+        assertEquals(
+            "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3ODkrLw==",
+            Encoding.Standard.alphabet.base64PadEncoded
+        )
+        assertEquals("YWJjZA==", "abcd".base64PadEncoded)
+        assertEquals("c2FzY2hwZQ==", "saschpe".base64PadEncoded)
+        assertEquals(
+            "MTIzNDU2Nzg5MC09IUAjJCVeJiooKV8rcXdlcnR5dWlvcFtdOydcLC4vPz48fCI6fXtQYH4=",
+            "1234567890-=!@#\$%^&*()_+qwertyuiop[];'\\,./?><|\":}{P`~".base64PadEncoded
+        )
+    }
+
+    // Base64Url
+    @Test
+    fun byteArray_base64UrlDecoded() {
+        assertEquals("Hello, world!", "SGVsbG8sIHdvcmxkIQ==".base64UrlPadDecoded)
+        assertContentEquals(
+            byteArrayOf(
+                -94, 124, -26, -112, -72, -84, 16, 11, 67, -45, 107, 38, -99, 79, 62, -49, 83, 26, -85, -70, -122, 53,
+                67, 42, -94, -87, 61, -74, 66, 0, 80, -125, -17, -11, -125, 63, 109, -15, 56, -95, -33, 18, 110, 47,
+                47, -20, -72, -34, 53, -69, 49, -45, 54, 53, -21, 43, 9, -84, -125, 72, -61, 76, 31, -46
+            ),
+            "onzmkLisEAtD02smnU8-z1Maq7qGNUMqoqk9tkIAUIPv9YM_bfE4od8Sbi8v7LjeNbsx0zY16ysJrINIw0wf0g==".base64UrlPadDecodedBytes
+        )
+    }
+
+    @Test
+    fun byteArray_base64UrlEncoded() {
+        assertEquals(
+            "xvrp9DBWlei2mG0ov9MN-A", // value1
+            byteArrayOf(-58, -6, -23, -12, 48, 86, -107, -24, -74, -104, 109, 40, -65, -45, 13, -8).base64UrlEncoded
+        )
+        assertEquals(
+            "IkYJxF8nIQD9RY7Yk6r26A", // value222
+            byteArrayOf(34, 70, 9, -60, 95, 39, 33, 0, -3, 69, -114, -40, -109, -86, -10, -24).base64UrlEncoded
+        )
+        assertEquals(
+            "U0GeVBi2dNcdL2IO0nJo5Q", // value555
+            byteArrayOf(83, 65, -98, 84, 24, -74, 116, -41, 29, 47, 98, 14, -46, 114, 104, -27).base64UrlEncoded
+        )
+    }
+
+    @Test
+    fun string_base64UrlDecoded() {
+        assertEquals("word", "d29yZA==".base64UrlDecoded)
+        assertEquals("Word", "V29yZA==".base64UrlDecoded)
+        assertEquals("Hello", "SGVsbG8=".base64UrlDecoded)
+        assertEquals("World!", "V29ybGQh".base64UrlDecoded)
+        assertEquals("Hello, world!", "SGVsbG8sIHdvcmxkIQ".base64UrlDecoded)
+        assertEquals("Hello, world!", "SGVsbG8sIHdvcmxkIQ==".base64UrlPadDecoded)
+        assertEquals(
+            Encoding.Standard.alphabet,
+            "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3ODkrLw==".base64UrlPadDecoded
+        )
+        assertEquals("Salt", "U2FsdA==".base64UrlPadDecoded)
+        assertEquals("Pepper", "UGVwcGVy".base64UrlPadDecoded)
+        assertEquals("abcd", "YWJjZA".base64UrlPadDecoded)
+        assertEquals(
+            "1234567890-=!@#\$%^&*()_+qwertyuiop[];'\\,./?><|\":}{P`~",
+            "MTIzNDU2Nzg5MC09IUAjJCVeJiooKV8rcXdlcnR5dWlvcFtdOydcLC4vPz48fCI6fXtQYH4".base64UrlDecoded
+        )
+        assertEquals("saschpe", "c2FzY2hwZQ".base64UrlDecoded)
+    }
+
+    @Test
+    fun string_base64UrlEncoded() {
+        assertEquals("d29yZA", "word".base64UrlEncoded)
+        assertEquals("V29yZA", "Word".base64UrlEncoded)
+        assertEquals("SGVsbG8", "Hello".base64UrlEncoded)
+        assertEquals("V29ybGQh", "World!".base64UrlEncoded)
+        assertEquals("SGVsbG8sIHdvcmxkIQ", "Hello, world!".base64UrlEncoded)
+        assertEquals("SGVsbG8sIHdvcmxkIQ", "Hello, world!".encodeToByteArray().base64UrlEncoded)
+        assertEquals(
+            "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3ODkrLw",
+            Encoding.Standard.alphabet.base64UrlEncoded
+        )
+        assertEquals("U2FsdA", "Salt".base64UrlEncoded)
+        assertEquals("UGVwcGVy", "Pepper".base64UrlEncoded)
+        assertEquals("YWJjZA", "abcd".base64UrlEncoded)
+        assertEquals(
+            "MTIzNDU2Nzg5MC09IUAjJCVeJiooKV8rcXdlcnR5dWlvcFtdOydcLC4vPz48fCI6fXtQYH4",
+            "1234567890-=!@#\$%^&*()_+qwertyuiop[];'\\,./?><|\":}{P`~".base64UrlEncoded
+        )
+        assertEquals("c2FzY2hwZQ", "saschpe".base64UrlEncoded)
+    }
+
+    @Test
+    fun testEncodeBase16RFC_4648() {
+        assertEquals("", "".base64PadEncoded)
+    }
+
+    @Test
+    fun testEncodeBase16RFC_4648_1() {
+        assertEquals("Zg==", "f".base64PadEncoded)
+    }
+
+    @Test
+    fun testEncodeBase16RFC_4648_2() {
+        assertEquals("Zm8=", "fo".base64PadEncoded)
+    }
+
+    @Test
+    fun testEncodeBase16RFC_4648_3() {
+        assertEquals("Zm9v", "foo".base64PadEncoded)
+    }
+
+    @Test
+    fun testEncodeBase16RFC_4648_4() {
+        assertEquals("Zm9vYg==", "foob".base64PadEncoded)
+    }
+
+    @Test
+    fun testEncodeBase16RFC_4648_5() {
+        assertEquals("Zm9vYmE=", "fooba".base64PadEncoded)
+    }
+
+    @Test
+    fun testEncodeBase16RFC_4648_6() {
+        assertEquals("Zm9vYmFy", "foobar".base64PadEncoded)
+    }
+}
diff --git a/didpeer/src/commonTest/kotlin/io/iohk/atala/prism/didcomm/didpeer/multibase/MultiBaseTests.kt b/didpeer/src/commonTest/kotlin/io/iohk/atala/prism/didcomm/didpeer/multibase/MultiBaseTests.kt
new file mode 100644
index 0000000..9328ab2
--- /dev/null
+++ b/didpeer/src/commonTest/kotlin/io/iohk/atala/prism/didcomm/didpeer/multibase/MultiBaseTests.kt
@@ -0,0 +1,16 @@
+package io.iohk.atala.prism.didcomm.didpeer.multibase
+
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+// TODO: add the rest of unit test
+class MultiBaseTests {
+    @Test
+    fun testMultibaseBase16() {
+        assertEquals("f57656c636f6d6520746f20494f4721", MultiBase.encode(MultiBase.Base.BASE16, "Welcome to IOG!"))
+        assertEquals("Welcome to IOG!", MultiBase.decode("f57656c636f6d6520746f20494f4721").decodeToString())
+
+        assertEquals("F57656C636F6D6520746F20494F4721", MultiBase.encode(MultiBase.Base.BASE16_UPPER, "Welcome to IOG!"))
+        assertEquals("Welcome to IOG!", MultiBase.decode("F57656C636F6D6520746F20494F4721").decodeToString())
+    }
+}
diff --git a/didpeer/src/commonTest/kotlin/io/iohk/atala/prism/didcomm/didpeer/varint/VarIntTests.kt b/didpeer/src/commonTest/kotlin/io/iohk/atala/prism/didcomm/didpeer/varint/VarIntTests.kt
new file mode 100644
index 0000000..4af64c7
--- /dev/null
+++ b/didpeer/src/commonTest/kotlin/io/iohk/atala/prism/didcomm/didpeer/varint/VarIntTests.kt
@@ -0,0 +1,16 @@
+package io.iohk.atala.prism.didcomm.didpeer.varint
+
+import okio.Buffer
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class VarIntTests {
+    @Test
+    fun testVarInt() {
+        val origin = 1234774
+        val byteBuffer = Buffer()
+        VarInt.write(origin, byteBuffer)
+        val value: Int = VarInt.read(byteBuffer)
+        assertEquals(origin, value)
+    }
+}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 41dfb87..e411586 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists