## Copyright (c) 2018 Status Research & Development GmbH ## Licensed under either of ## * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) ## * MIT license ([LICENSE-MIT](LICENSE-MIT)) ## at your option. ## This file may not be copied, modified, or distributed except according to ## those terms. ## This module implements BASE64 encoding and decoding procedures. type Base64Status* {.pure.} = enum Error, Success, Incorrect, Overrun Base64Alphabet* = object decode*: array[128, int8] encode*: array[64, uint8] Base64* = object ## Type to use RFC4648 alphabet without padding Base64Pad* = object ## Type to use RFC4648 alphabet with padding Base64Url* = object ## Type to use RFC4648 URL alphabet without padding Base64UrlPad* = object ## Type to use RFC4648 URL alphabet with padding Base64PadTypes* = Base64Pad | Base64UrlPad ## All types with padding support Base64NoPadTypes* = Base64 | Base64Url ## All types without padding support Base64Types* = Base64 | Base64Pad | Base64Url | Base64UrlPad ## All types Base64Error* = object of CatchableError ## Base64 specific exception type proc newAlphabet64*(s: string): Base64Alphabet = doAssert(len(s) == 64) for i in 0.. len(outbytes): outlen = length return Base64Status.Overrun var inlen = len(instr) when (btype is Base64PadTypes): for i in countdown(inlen - 1, 0): if instr[i] != T('='): break dec(inlen) let reminder = inlen mod 4 let limit = inlen - reminder var buffer: array[4, byte] var i, k: int while i < limit: for j in 0..<4: if (cast[byte](instr[i + j]) and 0x80'u8) != 0: outlen = 0 zeroMem(addr outbytes[0], i + 3) return Base64Status.Incorrect let ch = alphabet.decode[cast[int8](instr[i + j])] if ch == -1: outlen = 0 zeroMem(addr outbytes[0], i + 3) return Base64Status.Incorrect buffer[j] = cast[byte](ch) outbytes[k] = cast[byte]((buffer[0] shl 2) or (buffer[1] shr 4)) inc(k) outbytes[k] = cast[byte]((buffer[1] shl 4) or (buffer[2] shr 2)) inc(k) outbytes[k] = cast[byte]((buffer[2] shl 6) or buffer[3]) inc(k) i += 4 if reminder > 0: if reminder == 1: outlen = 0 return Base64Status.Incorrect for j in 0.. 1: outbytes[k] = cast[byte]((buffer[0] shl 2) or (buffer[1] shr 4)) inc(k) if reminder > 2: outbytes[k] = cast[byte]((buffer[1] shl 4) or (buffer[2] shr 2)) inc(k) outlen = k result = Base64Status.Success proc decode*[T: byte|char](btype: typedesc[Base64Types], instr: openArray[T]): seq[byte] = ## Decode BASE64 string ``instr`` and return sequence of bytes as result. if len(instr) == 0: result = newSeq[byte]() else: var length = 0 result = newSeq[byte](btype.decodedLength(len(instr))) if btype.decode(instr, result, length) == Base64Status.Success: result.setLen(length) else: raise newException(Base64Error, "Incorrect base64 string")