Skip to content

Commit 9f0ffe6

Browse files
committed
Add support for unpadded Base64 inputs
1 parent 0bab197 commit 9f0ffe6

File tree

2 files changed

+30
-10
lines changed
  • runtime/runtime-core/common

2 files changed

+30
-10
lines changed

runtime/runtime-core/common/src/aws/smithy/kotlin/runtime/text/encoding/Base64.kt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,18 @@ public fun String.decodeBase64Bytes(): ByteArray = encodeToByteArray().decodeBas
9999
* Decode [ByteArray] from base64 format
100100
*/
101101
public fun ByteArray.decodeBase64(): ByteArray {
102-
val encoded = this
102+
103+
// Calculate the padding needed to make the length a multiple of 4
104+
val remainder = size % 4
105+
val encoded: ByteArray = if (remainder == 0) {
106+
this
107+
} else {
108+
this + ByteArray(4 - remainder) { BASE64_PAD.code.toByte() }
109+
}
110+
103111
val decodedLen = base64DecodedLen(encoded)
104112
val decoded = ByteArray(decodedLen)
105-
val blockCnt = size / 4
113+
val blockCnt = encoded.size / 4
106114
var readIdx = 0
107115
var writeIdx = 0
108116

runtime/runtime-core/common/test/aws/smithy/kotlin/runtime/text/encoding/Base64Test.kt

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,6 @@ class Base64Test {
7676
ex.message!!.shouldContain("decode base64: invalid input byte: 45")
7777
}
7878

79-
@Test
80-
fun decodeNonMultipleOf4() {
81-
val ex = assertFails {
82-
"Zm9vY=".decodeBase64()
83-
}
84-
ex.message!!.shouldContain("invalid base64 string of length 6; not a multiple of 4")
85-
}
86-
8779
@Test
8880
fun decodeInvalidPadding() {
8981
val ex = assertFails {
@@ -116,4 +108,24 @@ class Base64Test {
116108
assertEquals(encoded, decoded.encodeBase64())
117109
assertEquals(decoded, encoded.decodeBase64())
118110
}
111+
112+
@Test
113+
fun testUnpaddedInputs() {
114+
// from https://github.com/smithy-lang/smithy/pull/2502
115+
val input = "v2hkZWZhdWx0c79tZGVmYXVsdFN0cmluZ2JoaW5kZWZhdWx0Qm9vbGVhbvVrZGVmYXVsdExpc3Sf/3BkZWZhdWx0VGltZXN0YW1wwQBrZGVmYXVsdEJsb2JDYWJja2RlZmF1bHRCeXRlAWxkZWZhdWx0U2hvcnQBbmRlZmF1bHRJbnRlZ2VyCmtkZWZhdWx0TG9uZxhkbGRlZmF1bHRGbG9hdPo/gAAAbWRlZmF1bHREb3VibGX6P4AAAGpkZWZhdWx0TWFwv/9rZGVmYXVsdEVudW1jRk9PbmRlZmF1bHRJbnRFbnVtAWtlbXB0eVN0cmluZ2BsZmFsc2VCb29sZWFu9GllbXB0eUJsb2JAaHplcm9CeXRlAGl6ZXJvU2hvcnQAa3plcm9JbnRlZ2VyAGh6ZXJvTG9uZwBpemVyb0Zsb2F0+gAAAABqemVyb0RvdWJsZfoAAAAA//8"
116+
input.decodeBase64()
117+
118+
val inputs = mapOf<String, String>(
119+
"YQ" to "a",
120+
"Yg" to "b",
121+
"YWI" to "ab",
122+
"YWJj" to "abc",
123+
"SGVsbG8gd29ybGQ" to "Hello world",
124+
)
125+
126+
inputs.forEach { (encoded, expected) ->
127+
val actual = encoded.decodeBase64()
128+
assertEquals(expected, actual)
129+
}
130+
}
119131
}

0 commit comments

Comments
 (0)