There is a bug in Base.decode64. I'm not yet sure what it is, but here is a piece of base64 encoded text that can reproduce the error:
UmVhY3Q/CiogV2hhdCBpcyBSZWR1eD8KKiBXaGF0IHdpbGwgYmUgY292ZXJl
ZD8KKiBXaGF0IHdpbGwgKm5vdCogYmUgY292ZXJlZAoqIFdoYXQgaXMgdGhl
IGlkZWFsIGFwcCBmb3IgdGhpcyBzdGFjaz8KKiBXaGF0IGFyZSBzb21lIGFs
dGVybmF0aXZlcz8KKiBHZW5lcmFsIGZvcm1hdCBvZiB0aGUgc2VyaWVzCiog
Q29udGFjdAoKQXMgYW55IGV4cGVyaWVuY2UgZGV2ZWxvcGVyIGtub3dzLCBz
Y2FsaW5nIGlzIG5vdCBhIHRyaXZpYWwgcHJvYmxlbS4gSW4gZmFjdCwgYnVp
bGRpbmcgYW4gYXBwIGlzIG9mdGVuIHRoZSBlYXNpZXN0IHBhcnQgb2YgbGF1
bmNoaW5nIGEgbmV3IHNvZnR3YXJlIHByb2R1Y3QuCgpTY2FsaW5nIHByb2Js
ZW1zIGFyZSBtdWNoIGhhcmRlciB0byBwcmVkaWN0LCB0cmFjayBkb3duLCBh
bmQgcHJldmVudCBpZiB5b3UgZG8gbm90IGNvbnNpZGVyIGl0IGZyb20gdGhl
IG91dHNldCBvZiB5b3VyIHByb2plY3QuIFlvdSBuZWVkIHRvIGJlIGF3YXJl
IG9mIGV2ZXJ5IGluZWZmaWNpZW5jeSwgZW5zdXJlIGFjdGlvbnMgZnJvbSBk
aWZmZXJlbnQgY3VzdG9tZXJzIGRvIG5vdCBjb25mbGljdCwgZ3JvdyB5b3Vy
IGRhdGFiYXNlLCBoYW5kbGUgZXJyb3JzLCB0b2xlcmF0ZSBzZXJ2ZXIgZmFp
bHVyZXMsIGFtb25nIGEgbGl0YW55IG9mIG90aGVyIGlzc3VlcyB0aGF0IGEg
c21hbGxlciBhcHAgc2ltcGx5IGRvZXMgbm90IG5lZWQgdG8gY29uc2lkZXIu
CgpEZXZlbG9wZXJzIHdobyBoYXZlIHRvIGRlYWwgd2l0aCBsZWdhY3kgY29k
ZSBvbiBhIHJlZ3VsYXIgYmFzaXMga25vdyB0aGUgZmVlbGluZyBvZiBzdGFy
aW5nIGF0IGEgY29kZWJhc2UgdGhhdCBsb29rcyBsaWtlIGEgRnJhbmtlbnN0
ZWluIG1vbnN0ZXIgb2Ygb3V0ZGF0ZWQgdGVjaG5vbG9naWVzIGFuZCBhbiBh
c3NvcnRtZW50IG9mIGhhY2tzIHRoYXQgY291bGQgZmFsbCBhcGFydCBhdCBh
bnkgbW9tZW50LiBSZWZhY3RvcmluZyB3b3VsZCB0YWtlIHRvbyBtdWNoIHRp
bWUsIHNvIHlvdSBqdXN0IGNvbnRpbnVlIHRvIHBpbGUgb250byB0aGUgbW9u
c3RlciBhbmQgcHJldGVuZCB0aGF0IGV2ZXJ5dGhpbmcgaXMgZmluZS4KCltp
bWFnZV0oaHR0cDovL3ZpZ25ldHRlMy53aWtpYS5ub2Nvb2tpZS5uZXQvY2Fy
ZGZpZ2h0L2ltYWdlcy9lL2UzL1Bvc3QtNjQyMzEtdGhpcy1pcy1maW5lLWRv
Zy1maXJlLWNvbWljLUltLU43bXAucG5nL3JldmlzaW9uL2xhdGVzdD9jYj0y
MDE1MTAwMzIwMDAxMCkKClNvIHdoeSBub3QganVzdCBidWlsZCB5b3VyIGFw
cCBpbiBhIGZyYW1ld29yayB0aGF0IGlzIGRlc2lnbmVkIGZyb20gdGhlIGdy
b3VuZCB1cCB0byBlYXNpbHkgaGFuZGxlIHlvdXIgc2NhbGluZyBjb25jZXJu
cz8gRW50ZXIgUGhvZW5peC4KCiMjIFdoYXQgaXMgUGhvZW5peD8KClBob2Vu
aXggaXMgYSB3ZWIgZnJhbWV3b3JrIGJ1aWx0IHdpdGggW0VsaXhpcl0obGlu
ay5jb20pIHRoYXQgaXMgYWJsZSB0byBsZXZlcmFnZSB0aGUgW2luc2FuZWx5
IHBvd2VyZnVsIEVybGFuZyBWTV0od2hhdHNhcHAgbGluaykgZ2l2ZXMgeW91
IHRoZSBhYmlsaXR5IHRvIGhhbmRsZSBtaWxsaW9ucyBvZiBjb25jdXJyZW50
IGNvbm5lY3Rpb25zLCB3aGlsZSBhbHNvIHJlbWFpbmluZyBmYXVsdC10b2xl
cmFudCBhbmQgZWFzeSB0byBpbXBsZW1lbnQuCgpUbyBnaXZlIHlvdSBhIHJl
YWwgY29tcGFyaXNvbiwgaGVyZSBpcyBob3cgUGhvZW5peCBwZXJmb3JtcyBj
b21wYXJlZCB0byBvdGhlciBwb3B1bGFyIGZyYW1ld29ya3M6CgpbaW1hZ2Vd
KGkgbmVlZCB0byBtYWtlIHRoaXMgb25lLCBpIHRoaW5rKQpBU1AuTkVUCk1F
QU4KUnVieQpEamFuZ28KTWV0ZW9yCkxhcmF2ZWwKU3ByaW5nCgpUaGVzZSBu
dW1iZXJzIGRvIG5vdCByZXByZXNlbnQgYSBtYXJnaW5hbCBwZXJmb3JtYW5j
ZSBpbXByb3ZlbWVudCBvdmVyIG90aGVyIGZyYW1ld29ya3PigJRpdCBibG93
cyB0aGVtIG91dCBvZiB0aGUgd2F0ZXIuCgpQaG9lbml4IGlzIGJ1aWx0IHdp
dGggdGhlIHByb2dyYW1taW5nIGxhbmd1YWdlIFtFbGl4aXJdKGxpbmspLCB3
aGljaCBpcyBhIFtmdW5jdGlvbmFsIHByb2dyYW1taW5nIGxhbmd1YWdlXShs
aW5rKS4gVGhlIHN5bnRheCBpcyB2ZXJ5IHNpbWlsYXIgdG8gW1J1YnldKGxp
bmspIGFuZCBtb3N0IHByb2dyYW1tZXJzIGZpbmQgaXQgZWFzeSB0byB1bmRl
cnN0YW5kLiBUaGUgaGFyZGVzdCBwYXJ0IGlzIG5vdCB0aGUgbGFuZ3VhZ2Ug
aXRzZWxmLCBidXQgd3JhcHBpbmcgb25lJ3MgaGVhZCBhcm91bmQgdGhlIFtj
b25jZXB0cyBvZiBmdW5jdGlvbmFsIHByb2dyYW1taW5nXShsaW5rKS4KClRo
ZSBiYXNpYyBzeW50YXggZm9yIGVsaXhpciBsb29rcyBsaWtlIHRoaXM6Cgpg
YGBlbGl4aXIKZGVmbW9kdWxlIEZpYiBkbwogIGRlZiBmaWIoMCkgZG8gMCBl
bmQKICBkZWYgZmliKDEpIGRvIDEgZW5kCiAgZGVmIGZpYihuKSBkbyBmaWIo
bi0xKSArIGZpYihuLTIpIGVuZAplbmQKYGBgCgojIyBXaGF0IGlzIFJlYWN0
CgpSZWFjdCBpcyB0aGUgIlYiIGluICJNVkMiIChNb2RlbCBWaWV3IENvbnRy
b2xsZXIpLiBJdCBpcyBhbGwgSmF2YVNjcmlwdCBhbmQKClJhdGhlciB0aGFu
IHVzaW5nIGEgbWFya3VwIGxhbmd1YWdlIGxpa2UgSFRNTCB0byByZW5kZXIg
dGhlIERPTSwgUmVhY3QgY3JlYXRlcyBlYWNoIGVsZW1lbnQgaW4gdGhlIERP
TSB3aXRoIGEgZnVuY3Rpb24gYW5kIGNyZWF0ZXMgYSAidmlydHVhbCBET00i
IHdoaWNoIGl0IGNhbiB1c2UgdG8gY29tcGFyZSBhZ2FpbnN0IHRoZSBjdXJy
ZW50IHZlcnNpb24gb2YgdGhlIERPTSBhbmQgcmVuZGVyIG9ubHkgdGhlIHNt
YWxsZXN0IG5lY2Vzc2FyeSBhbW91bnQgb2YgaW5mb3JtYXRpb24uCgpUaGlz
IHJvdW5kYWJvdXQgYXBwcm9hY2ggbWlnaHQgc2VlbSBsaWtlIG92ZXJraWxs
LCBidXQgaXQgbGVhZHMgdG8gbW9yZSBlZmZpY2llbnQgcmVuZGVyaW5nLCBi
ZXR0ZXIgY29kZSBtYWludGFpbmFiaWxpdHksIGFuZCB0aGUgYWR2YW50YWdl
IG9mIGJlaW5nIGFibGUgdG8gd3JpdGUgZXZlcnl0aGluZyBpbiBhIG1vcmUg
Y29oZXJlbnQgc3ludGF4OiBKYXZhU2NyaXB0LgoKSXQncyBhbHNvIGltbWVu
c2VseSBwb3B1bGFyLCBzdXBwb3J0ZWQgYnkgRmFjZWJvb2ssIGFuZCBpcyBh
bHJlYWR5IHVzZWQgaW4gcHJvZHVjdGlvbiBpbiBjb3VudGxlc3MgbWFqb3Ig
d2Vic2l0ZXMgYW5kIGFwcHMuCgpUaGUgYmFzaWMgc3ludGF4IGZvciBKYXZh
U2NyaXB0IGlzOgoKYGBganMKZnVuY3Rpb24gZmlib25hY2NpKG4pIHsKICAg
aWYgKG4gPCAyKSB7CiAgICAgcmV0dXJuIG47CiAgIH0gZWxzZSB7CiAgICAg
cmV0dXJuIGZpYm9uYWNjaShuIC0gMikgKyBmaWJvbmFjY2kobiAtIDEpOwog
ICB9Cn0KYGBgCgojIyBXaGF0IGlzIFJlZHV4CgojIyBXaGF0IHdpbGwgYmUg
Y292ZXJlZD8KClRoaXMgc2V0IG9mIGNvdXJzZXMgaXMgaW50ZW5kZWQgZm9y
IHBlb3BsZSB3aXRoIGF0IGxlYXN0IHNvbWUgZXhwZXJpZW5jZSBwcm9ncmFt
bWluZy4gSWYgeW91J3JlIGEgUnVieSBvbiBSYWlscyBkZXZlbG9wZXIsIG9y
IGlmIHlvdSBoYXZlIGV4cGVyaWVuY2Ugd2l0aCBhbm90aGVyIGZyYW1ld29y
aywgeW91IHNob3VsZCBoYXZlIG5vIHByb2JsZW0gcGlja2luZyB0aGlzIHVw
LgoKSXQgd291bGQgYmUgaWRlYWwgZm9yIHNvbWVvbmUgd2l0aCBleHBlcmll
bmNlIGluIGZ1bmN0aW9uYWwgcHJvZ3JhbW1pbmcsIGJ1dCB0aGlzIGlzIG5v
dCBuZWNlc3Nhcnk7IHdlIHdpbGwgZ28gdGhyb3VnaCBzb21lIG9mIHRoZSBm
dW5kYW1lbnRhbCBjb25jZXB0cyBvZiBmdW5jdGlvbmFsIHByb2dyYW1taW5n
LgoKV2Ugd2lsbCBjb3ZlciBzb21lIG9mIHRoZSBiYXNpY3Mgb2YgRWxpeGly
IGFuZCB3ZSB3aWxsIHRvdWNoIG9uIEVybGFuZyB3aGVuIG5lY2Vzc2FyeS4g
Rm9yIG1vcmUgZGV0YWlsZWQgdHV0b3JpYWxzIG9uIEVsaXhpciwgSSByZWNv
bW1lbmQgW0VsaXhpclNpcHMuY29tXShlbGl4aXJzaXBzLmNvbSkuCgpPbiB0
aGUgZnJvbnRlbmQsIHdlIHdpbGwgY292ZXIgdGhlIGJhc2ljcyBvZiBSZWFj
dCBhbmQgUmVkdXgsIGJ1dCBhIGRlZXBlciBrbm93bGVkZ2Ugb2YgUmVhY3Qg
d2lsbCBiZSB2ZXJ5IHVzZWZ1bC4gSSByZWNvbW1lbmQgdGhlIFtlZ2doZWFk
LmlvXShlZ2doZWFkLmlvKSB0dXRvcmlhbHMgb24gUmVhY3QsIFJlZHV4LCBh
bmQgUmVhY3QgTmF0aXZlLgoKV2Ugd2lsbCBjb3ZlciBzb21lIG9mIHRoZSBi
YXNpY3Mgb2Ygc3R5bGluZyBhbmQgd2Ugd2lsbCB1c2UgYmFzaWMgQ1NTIHdp
dGhvdXQgeW91ciB0eXBpY2FsIHByZXByb2Nlc3NvciAoc3VjaCBhcyBMRVNT
IG9yIFNhc3MpLiBUaGlzIGlzIGJlY2F1c2Ugd2Ugd2lsbCBiZSB1c2luZyBb
Q1NTIE1vZHVsZXNdKGNzcy1tb2R1bGVzKSB0byBtYWtlIG91ciBDU1MgbW9y
ZSBtYWludGFpbmFibGUgYW5kIG1vZHVsYXIuCgpXZSB3aWxsIGFsc28gY292
ZXIgc29tZSB2ZXJ5IGJhc2ljIHdpcmVmcmFtaW5nIHdpdGggU2tldGNoIDMu
IFdoZW4gZG9pbmcgY2xpZW50IHdvcmssIHdpcmVmcmFtZXMgYXJlIGEgZ29k
c2VuZCwgYXMgdGhleSBib3RoIGVuc3VyZSB0aGF0IHlvdSBhbmQgeW91ciBj
bGllbnQgaGF2ZSB0aGUgc2FtZSBleHBlY3RhdGlvbnMsIGFuZCB0aGV5IHBy
ZXZlbnQgW3Njb3BlIGNyZWVwXShsaW5rKS4gRm9yIGEgbW9yZSBkZXRhaWxl
ZCB0dXRvcmlhbCBvbiBTa2V0Y2gsIEkgc3VnZ2VzdCBbc29tZXRoaW5nXShs
aW5rKS4KCiMjIFdoYXQgd2lsbCAqbm90KiBiZSBjb3ZlcmVkPwoKKipUaGlz
IGlzIG5vdCBhbiBpbnRyby1sZXZlbCB0dXRvcmlhbC4qKiBXZSB3aWxsIG5v
dCBjb3ZlciB0aGUgYmFzaWNzIG9mIHByb2dyYW1taW5nLiBZb3UgZG8gbm90
IG5lZWQgdG8gYmUgYW4gZXhwZXJ0LCBidXQgeW91IHNob3VsZCBhdCBsZWFz
dCBrbm93IHdoYXQgYSBgZnVuY3Rpb25gIGlzIGFuZCBnZW5lcmFsbHkgaG93
IHRvIHVzZSB0aGVtIGluIGF0IGxlYXN0IG9uZSBsYW5ndWFnZS4gWW91IHNo
b3VsZCBhbHNvIGtub3cgd2hhdCBhIGBsaWJyYXJ5YCAoW0xvZGFzaF0obGlu
ayksIFtCb290c3RyYXBdKGxpbmspLCBldGMpIGlzICBhbmQgaGF2ZSBhdCBs
ZWFzdCBzb21lIGlkZWEgb2YgaG93IHRvIHVzZSBvbmUuCgojIyBXaGF0IGlz
IHRoZSBpZGVhbCB0eXBlIG9mIGFwcCBmb3IgdGhpcyBmcmFtZXdvcms/CgpQ
aG9lbml4IGlzIGlkZWFsIGZvciBoaWdobHkgY29uY3VycmVudCBhcHBzLCBt
ZWFuaW5nLCBhcHBzIHRoYXQgaGF2ZSB0aGF0IGhhdmUgYSBsb3Qgb2YgdGhp
bmdzIGhhcHBlbmluZyBhdCB0aGUgc2FtZSB0aW1lLiBGb3IgZXhhbXBsZSwg
YSBwb3B1bGFyIG1lc3NhZ2luZyBhcHAgbWlnaHQgaGF2ZSBhIGZldyBtaWxs
aW9uIHBlb3BsZSB1c2luZyBpdCBhdCBhbnkgZ2l2ZW4gdGltZSwgYW5kIGVh
Y2ggb2YgdGhvc2UgdXNlcnMgd2lsbCBleHBlY3QgdG8gcmVjZWl2ZSByZWFs
LXRpbWUgdXBkYXRlcyB3aGVuIGEgbWVzc2FnZSBpcyBzZW50IG9yIHJlY2Vp
dmVkLgoKVGhpcyBpcyBub3QgdG8gc2F5IHRoYXQgUGhvZW5peCBkb2VzIG5v
dCBwZXJmb3JtIHdlbGwgZm9yIGFwcHMgdGhhdCBkbyBub3QgZGVtYW5kIGNv
bmN1cnJlbmN5LiBJdHMgZmF1bHQtdG9sZXJhbmNlIGFsb25lIG1ha2VzIGl0
IGlkZWFsIGZvciBtb3N0IGVudmlyb25tZW50cywgYnV0IGZvciBhIHNtYWxs
IGhhY2thdGhvbi10eXBlIGFwcCwgUGhvZW5peCBpcyBwcm9iYWJseSBvdmVy
a2lsbC4KClRoZXJlIGFyZSBhbHNvIGxhbmd1YWdlcyBvdGhlciB0aGFuIEVs
aXhpci9FcmxhbmcgdGhhdCBhcmUgYmV0dGVyIHN1aXRlZCBmb3IgbGFyZ2Ut
c2NhbGUgY29tcHV0YXRpb24uIFNvIGlmIHlvdSBmb3Jlc2VlIGEgbG90IG9m
IG51bWJlciBjcnVuY2hpbmcgaW4geW91ciBmdXR1cmUsIHlvdSBtaWdodCB3
YW50IHRvIGNvbnNpZGVyIEphdmEgb3IgKFB5dGhvbj8pLgoKIyMgV2hhdCBh
cmUgc29tZSBhbHRlcm5hdGl2ZXM/CgpZb3UgY2FuIHVzZSBQaG9lbml4IGZv
ciBib3RoIHRoZSBmcm9udGVuZCBhbmQgdGhlIGJhY2tlbmQgdXNpbmcgRWxp
eGlyJ3MgYnVpbHRpbiBtYXJrdXAgbGFuZ3VhZ2UuIElmIHlvdSBjaG9vc2Ug
dG8gZ28gdGhhdCByb3V0ZSwgSSByZWNvbW1lbmQgdGhlIGJvb2sgW1Byb2dy
YW1taW5nIFBob2VuaXhdKGxpbmspLgoKSWYgeW91J3JlIGxvb2tpbmcgdG8g
bWFrZSBhIHF1aWNrIHByb3RvdHlwZSBhbmQgeW91J3JlIHByb2ZpY2llbnQg
aW4gSmF2YVNjcmlwdCwgaXQncyBoYXJkIHRvIGJlYXQgTWV0ZW9yLmpzLCBh
bmQgSSB3b3VsZCByZWNvbW1lbmQgdGhlIGJvb2sgW0Rpc2NvdmVyIE1ldGVv
cl0oZGlzY292ZXJtZXRlb3IuY29tKS4KClJ1Ynkgb24gUmFpbHMgaXMgYWxz
byBhIHNhZmUgYmV0IGFuZCBpcyBwcm9iYWJseSB0aGUgZGVmYXVsdCBjaG9p
Y2UgZm9yIHRoZSBtYWpvcml0eSBvZiBzdGFydHVwcyBhcyBvZiAyMDE2LgoK
QW5ndWxhcj8gTUVBTj8KCkdvPyBKYXZhPyBQeXRob24/IFI/IE90aGVyLi4u
PwoKIyMgR2VuZXJhbCBmb3JtYXR0aW5nCgpUaGUgY291cnNlcyBhcmUgZGl2
aWRlZCBpbnRvIHRocmVlIHRyYWNrcy4gSWYgeW91IHdhbnQgdG8gdGFrZSB0
aGUgZmFzdGVzdCBwb3NzaWJsZSBwYXRoIHRvIGxlYXJuaW5nIFBob2VuaXgs
IHlvdSBjYW4gZm9sbG93IHRoZSBjZW50ZXIgdHJhY2sgKHRoZSAiQXBwIHRy
YWNrIikgYWxsIHRoZSB3YXkgZG93biBhbmQgeW91IHdpbGwgaGF2ZSBhIGZ1
bGx5LWZ1bmN0aW9uYWwgYXBwIGF0IHRoZSBlbmQgb2YgaXQuCgpbaW1hZ2Ug
b2YgYXBwIHRyYWNrXShpbWcpCgpUbyB0aGUgbGVmdCBhbmQgdGhlIHJpZ2h0
IG9mIHRoZSAiQXBwIHRyYWNrIiBhcmUgdGhlICJUZXN0aW5nIHRyYWNrIiBh
bmQgdGhlICJCb251cyB0cmFjayIuIEJlY2F1c2UgUGhvZW5peCBwdXRzIHN1
Y2ggYW4gZW1waGFzaXMgb24gZmF1bHQtdG9sZXJhbmNlLCB0ZXN0aW5nIHNo
b3VsZCBiZSBhIHByaW9yaXR5IGZvciB5b3Ugd2hpbGUgYnVpbGRpbmcgeW91
ciBhcHAuLi4gYnV0IGl0IGlzIG5vdCBzdHJpY3RseSBuZWNlc3NhcnkuCgpb
aW1hZ2Ugb2YgdGVzdGluZyB0cmFja10oaW1nKQoKVG8gdGhlIHJpZ2h0IGlz
IHRoZSAiQm9udXMgdHJhY2siLCB3aGljaCB3aWxsIGluY2x1ZGUgZXh0cmEg
ZmVhdHVyZXMgYW5kIG9wdGlvbmFsaXR5IHRoYXQgbWF5IG9yIG1heSBub3Qg
YmUgcG9zc2libGUgdG8gbWVyZ2UgYmFjayBpbnRvIHRoZSBtYWluIGFwcC4g
Rm9yIGV4YW1wbGUsIHdlIG1heSBzaG93IHlvdSBob3cgdG8gY29ubmVjdCB5
b3VyIFBob2VuaXggYmFja2VuZCB0byBhIFJldGhpbmtEQiBkYXRhYmFzZSwg
YnV0IHNpbmNlIHdlIGFyZSB1c2luZyBQb3N0Z3JlcyBmb3IgdGhlIG1haW4g
YXBwLCBpdCB3aWxsIG5vdCBiZSBwb3NzaWJsZSB0byBtZXJnZSB0aGlzIGJy
YW5jaCBiYWNrIGludG8gdGhlIGFwcC4KCltpbWFnZSBvZiBib251cyB0cmFj
a10oaW1nKQoKVGhlIGNvbmNlcHQgb2YgbWVyZ2luZyBjb21lcyBmcm9tIGBn
aXRgLCB3aGljaCB5b3Ugc2hvdWxkIGhhdmUgc29tZSBmYW1pbGlhcml0eSB3
aXRoLiBJZiB5b3UgZG8gbm90LCBJIHN1Z2dlc3QgW3RoaXNdKHR1dG9yaWFs
KSB0byBmYW1pbGlhcml6ZSB5b3Vyc2VsZi4gSWYgYSBmZWF0dXJlIGNhbiBi
ZSBtZXJnZWQgYmFjayBpbnRvIHRoZSBhcHAsIGl0IHdpbGwgY29udGFpbiBh
cnJvdyBwb2ludGluZyBiYWNrIHRvIHRoZSBtYWluIGJyYW5jaCwgbGlrZSB0
aGUgaW1hZ2UgYmVsb3cgb24gdGhlIGxlZnQuIElmIHRoZSBjaGFuZ2UgY2Fu
bm90IGJlIG1lcmdlZCwgaXQgd2lsbCBub3QgY29udGFpbiBhbiBhcnJvdyBw
b2ludGluZyBiYWNrIHRvIHRoZSBhcHAsIGxpa2UgdGhlIGV4YW1wbGUgYmVs
b3cgdG8gdGhlIHJpZ2h0LgoKW2ltYWdlIG9mIG1lcmdlIGFuZCBub3QgbWVy
Z2VdKGltZykKCkV2ZXJ5IGNoYW5nZSB0byB0aGlzIGFwcCB3aWxsIGhhdmUg
YSBHaXRIdWIgY29tbWl0IHRoYXQgeW91IGNhbiByZWZlcmVuY2UgaWYgeW91
IGdldCBzdHVjay4gVGhvc2UgY29tbWl0cyB3aWxsIGxvb2sgbGlrZSB0aGUg
ZXhhbXBsZSBiZWxvdzoKCltob3cgdG8gZm9ybWF0IGxpbms/IHdpdGggYm9y
ZGVyP10oZ2l0aHViLmNvbSkKCkluIHRoZSBldmVudCB5b3UgaGF2ZSBhIHF1
ZXN0aW9uLCBwbGVhc2UgcG9zdCBhbiBpc3N1ZSBpbiB0aGUgR2l0aHViIHJl
cG9zaXRvcnksIG9yIGlmIHRoZSBxdWVzdGlvbiBpcyBtb3JlIGdlbmVyYWxs
eSBhYm91dCBQaG9lbml4LCBSZWFjdCwgRWxpeGlyLCBldCBhbCwgcGxlYXNl
IHBvc3QgdGhlIHF1ZXN0aW9uIG9uIFtTdGFja092ZXJmbG93XShzdGFja292
ZXJmbG93LmNvbSkKCkNvZGUgd3JpdHRlbiBpbiBjb2RlYmxvY2tzLCBzdWNo
IGFzIHRoZSBjb2RlIGJlbG93LCBzaG93cyB5b3UgdGhlIGNvZGUgYmVpbmcg
YWRkZWQsIHJlbW92ZWQsIG9yIGNoYW5nZWQuIEl0IHdpbGwgYWxzbyByZWZl
cmVuY2UgdGhlIGZpbGVuYW1lIGF0IHRoZSBib3R0b20gb2YgdGhlIGNvZGVi
bG9jay4KCmBgYGVsaXhpcgpkZWZtb2R1bGUKCiMgZmlsZW5hbWUvc29tZXRo
aW5nL3NvbWV0aGlnbmVsc2UuZXgKYGBgCgpDb21tYW5kIGxpbmUgaW5wdXRz
IHdpbGwgbG9vayBsaWtlIHRoZSBleGFtcGxlIGNvZGUgYmVsb3cuIFRoZSBj
b21tYW5kIGxpbmUgaW5wdXRzIGFzc3VtZSB5b3UgYXJlIHVzaW5nIGEgTWFj
LiBJZiB5b3UncmUgb24gYSBMaW51eCBtYWNoaW5lLCB5b3UgcHJvYmFibHkg
YWxyZWFkeSBrbm93IHdoYXQgeW91J3JlIGRvaW5nIGFuZCBpdCB3aWxsIHBy
b2JhYmx5IGJlIHNpbWlsYXIuCgpgYGBiYXNoCiQgY2Qgfi9kaXJlY3Rvcnkv
bmFtZQpgYGAKCiMjIENvbnRhY3QKCkZlZWwgZnJlZSB0byBjb250YWN0IHVz
IGF0IGFueSB0aW1lIHdpdGggY29tbWVudHMgb3IgcXVlc3Rpb25zIGF0IGlu
Zm9AbGVhcm5waG9lbml4LmlvLgo=
That piece of content gets parsed appropriately by erlang's :base64.decode(str) but if I run Base.decode64(str) on it, it will just return :error.
Have you tried stripping new lines before?
I stumbled upon this recently and most likely it's newlines (btw @knewter, I guess you stumbled upon it for the same reason I did, Matasano :smiley:). I did :binary.replace(encoded, "\n", "") but I wasn't sure if this is something we may want (maybe as an option to Base.decode64/1).
do we not delegate to :base64.decode? Should we emulate that behaviour? Or should we document that new lines aren't supported. Though I'd say ignoring any whitespace including new lines would be a sane thing to do, while providing a strict option that fails
Other implementations seem to ignore newline and space in the decode function, as the default behaviour. If adding the option to ignore blank chars, we might as well consider optional padding chars.
Chaging https://github.com/elixir-lang/elixir/blob/master/lib/elixir/lib/base.ex#L547 to
@ignore_chars [?\s, ?\r, ?\n]
for <<c::8 <- main>>, not c in @ignore_chars, into: <<>>, do: <<dec64(c)::6>>
solves part of the problem. The remaining bytes and padding chars are handled with a case statement, so it's not this direct.
Maybe it would be easier if we said which character is wrong but it is hard to add that now without being backwards incompatible as we would change the result to {:error, _}. :(
@josevalim so there's no plan to add Base.decode64/2 that accepts options? I agree {:error, _} would probably be the best solution but I can't think of a way to make that backwards compatible.
Based on this section of RFC4648, the recommendation is not to be "liberal in what you accept". This section also mentions that ignoring non-alphabet chars in base 64 decoding is specific to MIME's implementation of Base64 (see https://tools.ietf.org/html/rfc2045#section-6.8).
That said, the decode16 and decode32 functions already expand from RFC4648 by taking an extra option to set the case to lower, upper or mixed). See 2nd paragraph of the security considerations: http://tools.ietf.org/html/rfc4648#section-12.
So adding options to decode64 and accepting something like ignore_non_alphabet: true or ignore_chars: [?\t, ?\s, ?\n, ?\r] won't be backwards incompatible and still solve the problem of decoding "MIME Base64 Content-Transfer-Encoding" as defined in RFC2045.
Thanks @pma! We could either introduce ignore: :whitespace or ignore: :ascii_whitespace or introduce Base.mime_decode64.
If the only difference between MIME and not is whitespace, I'd rather go with the ignore: :ascii_whitespace option so that we only keep a single function (and there are other encoding/decoding functions that take options in Base, so it would not feel weird). If the differences with MIME are more, then I'd go with Base.mime_decode64.
I've search a bit in the RFC, and other languages, the difference is basically
In other languages, script languages tend to implement the lenient behavior, while others tend to implement the strict behavior.
The current implementation has url_decode64 and decode64, with their ! equivalent,
so making the lenient version another function would make something like 8 functions
just for base64 decoding, which I do not think we want.
We could either implement something like lenient: true, which, when passed,
would ignore non alphabet char instead of raising an error.
Or if we also want to be able to ignore only whitespaces, we could allow
ignore: :whitespaces, and ignore: :non_alphabet or something like this.
I think we can find a better name for the latter though.
By the way, I will be glad to implement this one if nobody started yet.
For reference, here is the memo of what I have searched.
Ignore chars not in alphabet.
All line breaks or other characters
not found in Table 1 must be ignored by decoding software.
In base64 data, characters other than those in Table 1, line breaks, and other
white space probably indicate a transmission error, about which a
warning message or even a message rejection might be appropriate
under some circumstances.
Error on chars not in alphabet, or explicitly state otherwise.
Implementations MUST reject the encoded data if it contains
characters outside the base alphabet when interpreting base-encoded
data, unless the specification referring to this document explicitly
states otherwise. Such specifications may instead state, as MIME
does, that characters outside the base encoding alphabet should
simply be ignored when interpreting data ("be liberal in what you accept").
No strict mode. Ignores both whitespaces and other chars.
>>> import base64
>>> base64.b64decode(b'Zm9vYmFy')
b'foobar'
>>> base64.b64decode(b'\nZ(m\t9vYmFy')
b'foobar'
Normal function ignores both whitespaces and other chars.
Strict function emits an error on both.
[1] pry(main)> require 'base64'
=> true
[2] pry(main)> Base64.decode64("\nZ(m\t9vYmFy")
=> "foobar"
[3] pry(main)> Base64.strict_decode64("\nZ(m\t9vYmFy")
ArgumentError: invalid base64
[2] pry(main)> Base64.strict_decode64("\nZm\t9vYmFy")
ArgumentError: invalid base64
No strict mode. Ignores both whitespaces and other chars.
> new Buffer("\nZ(m\t9vYmFy", "base64").toString()
'foobar'
Ignores whitespaces with normal decode.
Ignores both whitespaces and other chars with mime mode.
iex(1)> :base64.decode("\nZ(m\t9vYmFy")
** (ArgumentError) argument error: 40
(stdlib) base64.erl:192: :base64.decode_binary/2
iex(1)> :base64.decode("\nZm\t9vYmFy")
"foobar"
iex(2)> :base64.mime_decode("\nZ(m\t9vYmFy")
"foobar"
No lenient mode, errors on both whitespaces an other chars.
scala> java.util.Base64.getDecoder().decode("Zm9vYmFy")
res3: Array[Byte] = Array(102, 111, 111, 98, 97, 114)
scala> java.util.Base64.getDecoder().decode("\nZm\t9vYmFy")
java.lang.IllegalArgumentException: Illegal base64 character a
No lenient mode, errors on both whitespaces an other chars.
gore> :import encoding/base64
gore> base64.URLEncoding.DecodeString("Zm9vYmFy")
[]byte{0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72}
<nil>
gore> base64.URLEncoding.DecodeString("\nZm\t9vYmFy")
[]byte{}
3
Default function errors on both whitespaces an other chars.
Lenient function authorizes both.
Prelude> import Data.ByteString.Base64
Prelude Data.ByteString.Base64> :set -XOverloadedStrings
Prelude Data.ByteString.Base64> decode "Zm9vYmFy"
Right "foobar"
Prelude Data.ByteString.Base64> decode "Zm\n9vYmFy"
Left "invalid padding"
Prelude Data.ByteString.Base64> decodeLenient "Zm\n9vYmFy"
"foobar"
Prelude Data.ByteString.Base64> decodeLenient "Zm\n(9vYmFy"
"foobar"
Great wrap up, thank you!
@josevalim If it seems fine, I will implement ignore: :whitespaces and ignore: :non_alphabet and send a PR.
@tuvistavie thank you! let's go with only ignore: :whitespace for now because i haven't seen a need for :non_alphabet yet.
@josevalim Sure! I'll send a PR when I am done.
Most helpful comment
I've search a bit in the RFC, and other languages, the difference is basically
In other languages, script languages tend to implement the lenient behavior, while others tend to implement the strict behavior.
The current implementation has
url_decode64anddecode64, with their!equivalent,so making the lenient version another function would make something like 8 functions
just for base64 decoding, which I do not think we want.
We could either implement something like
lenient: true, which, when passed,would ignore non alphabet char instead of raising an error.
Or if we also want to be able to ignore only whitespaces, we could allow
ignore: :whitespaces, andignore: :non_alphabetor something like this.I think we can find a better name for the latter though.
By the way, I will be glad to implement this one if nobody started yet.
For reference, here is the memo of what I have searched.
Base64 in RFC
RFC 1512 - MIME, 5.2
Ignore chars not in alphabet.
RFC 4648 (and previously 3548) - The Base16, Base32, and Base64 Data Encodings, 3.3
Error on chars not in alphabet, or explicitly state otherwise.
Base64 in other languages
Python
No strict mode. Ignores both whitespaces and other chars.
Ruby
Normal function ignores both whitespaces and other chars.
Strict function emits an error on both.
Node
No strict mode. Ignores both whitespaces and other chars.
Erlang
Ignores whitespaces with normal decode.
Ignores both whitespaces and other chars with mime mode.
Java
No lenient mode, errors on both whitespaces an other chars.
Go
No lenient mode, errors on both whitespaces an other chars.
Haskell
Default function errors on both whitespaces an other chars.
Lenient function authorizes both.