base64.py 19.7 KB
Newer Older
1
#! /usr/bin/env python3
Guido van Rossum's avatar
Guido van Rossum committed
2

3
"""Base16, Base32, Base64 (RFC 3548), Base85 and Ascii85 data encodings"""
4

5 6
# Modified 04-Oct-1995 by Jack Jansen to use binascii module
# Modified 30-Dec-2003 by Barry Warsaw to add full RFC 3548 support
7
# Modified 22-May-2007 by Guido van Rossum to use bytes everywhere
8

9 10
import re
import struct
11
import binascii
12

13 14 15

__all__ = [
    # Legacy interface exports traditional RFC 1521 Base64 encodings
16
    'encode', 'decode', 'encodebytes', 'decodebytes',
17 18 19
    # Generalized interface for other encodings
    'b64encode', 'b64decode', 'b32encode', 'b32decode',
    'b16encode', 'b16decode',
20 21
    # Base85 and Ascii85 encodings
    'b85encode', 'b85decode', 'a85encode', 'a85decode',
22 23 24 25 26 27 28 29 30 31
    # Standard Base64 encoding
    'standard_b64encode', 'standard_b64decode',
    # Some common Base64 alternatives.  As referenced by RFC 3458, see thread
    # starting at:
    #
    # http://zgp.org/pipermail/p2p-hackers/2001-September/000316.html
    'urlsafe_b64encode', 'urlsafe_b64decode',
    ]


32
bytes_types = (bytes, bytearray)  # Types acceptable as binary data
33

34 35 36 37 38 39
def _bytes_from_decode_data(s):
    if isinstance(s, str):
        try:
            return s.encode('ascii')
        except UnicodeEncodeError:
            raise ValueError('string argument should contain only ASCII characters')
40
    if isinstance(s, bytes_types):
41
        return s
42 43 44 45 46
    try:
        return memoryview(s).tobytes()
    except TypeError:
        raise TypeError("argument should be a bytes-like object or ASCII "
                        "string, not %r" % s.__class__.__name__) from None
47

48

49 50 51
# Base64 encoding/decoding uses binascii

def b64encode(s, altchars=None):
52
    """Encode a byte string using Base64.
53

54 55 56 57
    s is the byte string to encode.  Optional altchars must be a byte
    string of length 2 which specifies an alternative alphabet for the
    '+' and '/' characters.  This allows an application to
    e.g. generate url or filesystem safe Base64 strings.
58

59
    The encoded byte string is returned.
60 61 62 63
    """
    # Strip off the trailing newline
    encoded = binascii.b2a_base64(s)[:-1]
    if altchars is not None:
64
        assert len(altchars) == 2, repr(altchars)
65
        return encoded.translate(bytes.maketrans(b'+/', altchars))
66 67 68
    return encoded


69
def b64decode(s, altchars=None, validate=False):
70
    """Decode a Base64 encoded byte string.
71

72 73 74
    s is the byte string to decode.  Optional altchars must be a
    string of length 2 which specifies the alternative alphabet used
    instead of the '+' and '/' characters.
75

76 77 78 79 80 81
    The decoded string is returned.  A binascii.Error is raised if s is
    incorrectly padded.

    If validate is False (the default), non-base64-alphabet characters are
    discarded prior to the padding check.  If validate is True,
    non-base64-alphabet characters in the input result in a binascii.Error.
82
    """
83
    s = _bytes_from_decode_data(s)
84
    if altchars is not None:
85
        altchars = _bytes_from_decode_data(altchars)
86
        assert len(altchars) == 2, repr(altchars)
87
        s = s.translate(bytes.maketrans(altchars, b'+/'))
88 89
    if validate and not re.match(b'^[A-Za-z0-9+/]*={0,2}$', s):
        raise binascii.Error('Non-base64 digit found')
90
    return binascii.a2b_base64(s)
91 92 93


def standard_b64encode(s):
94
    """Encode a byte string using the standard Base64 alphabet.
95

96
    s is the byte string to encode.  The encoded byte string is returned.
97 98 99 100
    """
    return b64encode(s)

def standard_b64decode(s):
101
    """Decode a byte string encoded with the standard Base64 alphabet.
102

103 104 105 106
    s is the byte string to decode.  The decoded byte string is
    returned.  binascii.Error is raised if the input is incorrectly
    padded or if there are non-alphabet characters present in the
    input.
107 108 109
    """
    return b64decode(s)

110 111 112 113

_urlsafe_encode_translation = bytes.maketrans(b'+/', b'-_')
_urlsafe_decode_translation = bytes.maketrans(b'-_', b'+/')

114
def urlsafe_b64encode(s):
115
    """Encode a byte string using a url-safe Base64 alphabet.
116

117 118 119
    s is the byte string to encode.  The encoded byte string is
    returned.  The alphabet uses '-' instead of '+' and '_' instead of
    '/'.
120
    """
121
    return b64encode(s).translate(_urlsafe_encode_translation)
122 123

def urlsafe_b64decode(s):
124
    """Decode a byte string encoded with the standard Base64 alphabet.
125

126 127 128 129
    s is the byte string to decode.  The decoded byte string is
    returned.  binascii.Error is raised if the input is incorrectly
    padded or if there are non-alphabet characters present in the
    input.
130 131 132

    The alphabet uses '-' instead of '+' and '_' instead of '/'.
    """
133 134 135
    s = _bytes_from_decode_data(s)
    s = s.translate(_urlsafe_decode_translation)
    return b64decode(s)
136 137


138

139
# Base32 encoding/decoding must be done in Python
140
_b32alphabet = b'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'
141 142
_b32tab2 = None
_b32rev = None
143 144

def b32encode(s):
145
    """Encode a byte string using Base32.
146

147
    s is the byte string to encode.  The encoded byte string is returned.
148
    """
149 150 151 152 153 154 155 156
    global _b32tab2
    # Delay the initialization of the table to not waste memory
    # if the function is never called
    if _b32tab2 is None:
        b32tab = [bytes((i,)) for i in _b32alphabet]
        _b32tab2 = [a + b for a in b32tab for b in b32tab]
        b32tab = None

157
    if not isinstance(s, bytes_types):
158
        s = memoryview(s).tobytes()
159
    leftover = len(s) % 5
160 161
    # Pad the last quantum with zero bits if necessary
    if leftover:
162
        s = s + bytes(5 - leftover)  # Don't use += !
163
    encoded = bytearray()
164 165 166 167 168 169 170 171 172
    from_bytes = int.from_bytes
    b32tab2 = _b32tab2
    for i in range(0, len(s), 5):
        c = from_bytes(s[i: i + 5], 'big')
        encoded += (b32tab2[c >> 30] +           # bits 1 - 10
                    b32tab2[(c >> 20) & 0x3ff] + # bits 11 - 20
                    b32tab2[(c >> 10) & 0x3ff] + # bits 21 - 30
                    b32tab2[c & 0x3ff]           # bits 31 - 40
                   )
173 174
    # Adjust for any leftover partial quanta
    if leftover == 1:
175
        encoded[-6:] = b'======'
176
    elif leftover == 2:
177
        encoded[-4:] = b'===='
178
    elif leftover == 3:
179
        encoded[-3:] = b'==='
180
    elif leftover == 4:
181 182
        encoded[-1:] = b'='
    return bytes(encoded)
183 184

def b32decode(s, casefold=False, map01=None):
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
    """Decode a Base32 encoded byte string.

    s is the byte string to decode.  Optional casefold is a flag
    specifying whether a lowercase alphabet is acceptable as input.
    For security purposes, the default is False.

    RFC 3548 allows for optional mapping of the digit 0 (zero) to the
    letter O (oh), and for optional mapping of the digit 1 (one) to
    either the letter I (eye) or letter L (el).  The optional argument
    map01 when not None, specifies which letter the digit 1 should be
    mapped to (when map01 is not None, the digit 0 is always mapped to
    the letter O).  For security purposes the default is None, so that
    0 and 1 are not allowed in the input.

    The decoded byte string is returned.  binascii.Error is raised if
    the input is incorrectly padded or if there are non-alphabet
    characters present in the input.
202
    """
203 204 205 206 207
    global _b32rev
    # Delay the initialization of the table to not waste memory
    # if the function is never called
    if _b32rev is None:
        _b32rev = {v: k for k, v in enumerate(_b32alphabet)}
208
    s = _bytes_from_decode_data(s)
209
    if len(s) % 8:
210
        raise binascii.Error('Incorrect padding')
211 212 213
    # Handle section 2.4 zero and one mapping.  The flag map01 will be either
    # False, or the character to map the digit 1 (one) to.  It should be
    # either L (el) or I (eye).
214
    if map01 is not None:
215
        map01 = _bytes_from_decode_data(map01)
216
        assert len(map01) == 1, repr(map01)
217
        s = s.translate(bytes.maketrans(b'01', b'O' + map01))
218
    if casefold:
219
        s = s.upper()
220 221 222
    # Strip off pad characters from the right.  We need to count the pad
    # characters because this will tell us how many null bytes to remove from
    # the end of the decoded string.
223 224 225
    l = len(s)
    s = s.rstrip(b'=')
    padchars = l - len(s)
226
    # Now decode the full quanta
227 228 229 230 231 232 233 234 235
    decoded = bytearray()
    b32rev = _b32rev
    for i in range(0, len(s), 8):
        quanta = s[i: i + 8]
        acc = 0
        try:
            for c in quanta:
                acc = (acc << 5) + b32rev[c]
        except KeyError:
236
            raise binascii.Error('Non-base32 digit found') from None
237
        decoded += acc.to_bytes(5, 'big')
238
    # Process the last, partial quanta
239 240 241 242 243 244 245 246 247 248 249 250 251 252
    if padchars:
        acc <<= 5 * padchars
        last = acc.to_bytes(5, 'big')
        if padchars == 1:
            decoded[-5:] = last[:-1]
        elif padchars == 3:
            decoded[-5:] = last[:-2]
        elif padchars == 4:
            decoded[-5:] = last[:-3]
        elif padchars == 6:
            decoded[-5:] = last[:-4]
        else:
            raise binascii.Error('Incorrect padding')
    return bytes(decoded)
253 254


255

256 257 258 259
# RFC 3548, Base 16 Alphabet specifies uppercase, but hexlify() returns
# lowercase.  The RFC also recommends against accepting input case
# insensitively.
def b16encode(s):
260
    """Encode a byte string using Base16.
261

262
    s is the byte string to encode.  The encoded byte string is returned.
263
    """
264
    return binascii.hexlify(s).upper()
265 266 267


def b16decode(s, casefold=False):
268
    """Decode a Base16 encoded byte string.
269

270 271 272
    s is the byte string to decode.  Optional casefold is a flag
    specifying whether a lowercase alphabet is acceptable as input.
    For security purposes, the default is False.
273

274 275 276
    The decoded byte string is returned.  binascii.Error is raised if
    s were incorrectly padded or if there are non-alphabet characters
    present in the string.
277
    """
278
    s = _bytes_from_decode_data(s)
279
    if casefold:
280
        s = s.upper()
281
    if re.search(b'[^0-9A-F]', s):
282
        raise binascii.Error('Non-base16 digit found')
283 284
    return binascii.unhexlify(s)

285 286 287
#
# Ascii85 encoding/decoding
#
288

289 290 291 292 293
_a85chars = None
_a85chars2 = None
_A85START = b"<~"
_A85END = b"~>"

294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
def _85encode(b, chars, chars2, pad=False, foldnuls=False, foldspaces=False):
    # Helper function for a85encode and b85encode
    if not isinstance(b, bytes_types):
        b = memoryview(b).tobytes()

    padding = (-len(b)) % 4
    if padding:
        b = b + b'\0' * padding
    words = struct.Struct('!%dI' % (len(b) // 4)).unpack(b)

    chunks = [b'z' if foldnuls and not word else
              b'y' if foldspaces and word == 0x20202020 else
              (chars2[word // 614125] +
               chars2[word // 85 % 7225] +
               chars[word % 85])
              for word in words]

    if padding and not pad:
        if chunks[-1] == b'z':
            chunks[-1] = chars[0] * 5
        chunks[-1] = chunks[-1][:-padding]

    return b''.join(chunks)

def a85encode(b, *, foldspaces=False, wrapcol=0, pad=False, adobe=False):
    """Encode a byte string using Ascii85.

    b is the byte string to encode. The encoded byte string is returned.

    foldspaces is an optional flag that uses the special short sequence 'y'
    instead of 4 consecutive spaces (ASCII 0x20) as supported by 'btoa'. This
    feature is not supported by the "standard" Adobe encoding.

327
    wrapcol controls whether the output should have newline ('\\n') characters
328 329 330 331 332 333 334 335 336
    added to it. If this is non-zero, each output line will be at most this
    many characters long.

    pad controls whether the input string is padded to a multiple of 4 before
    encoding. Note that the btoa implementation always pads.

    adobe controls whether the encoded byte sequence is framed with <~ and ~>,
    which is used by the Adobe implementation.
    """
337 338 339 340 341 342 343
    global _a85chars, _a85chars2
    # Delay the initialization of tables to not waste memory
    # if the function is never called
    if _a85chars is None:
        _a85chars = [bytes((i,)) for i in range(33, 118)]
        _a85chars2 = [(a + b) for a in _a85chars for b in _a85chars]

344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
    result = _85encode(b, _a85chars, _a85chars2, pad, True, foldspaces)

    if adobe:
        result = _A85START + result
    if wrapcol:
        wrapcol = max(2 if adobe else 1, wrapcol)
        chunks = [result[i: i + wrapcol]
                  for i in range(0, len(result), wrapcol)]
        if adobe:
            if len(chunks[-1]) + 2 > wrapcol:
                chunks.append(b'')
        result = b'\n'.join(chunks)
    if adobe:
        result += _A85END

    return result

def a85decode(b, *, foldspaces=False, adobe=False, ignorechars=b' \t\n\r\v'):
    """Decode an Ascii85 encoded byte string.

    s is the byte string to decode.

    foldspaces is a flag that specifies whether the 'y' short sequence should be
    accepted as shorthand for 4 consecutive spaces (ASCII 0x20). This feature is
    not supported by the "standard" Adobe encoding.

    adobe controls whether the input sequence is in Adobe Ascii85 format (i.e.
    is framed with <~ and ~>).

    ignorechars should be a byte string containing characters to ignore from the
    input. This should only contain whitespace characters, and by default
    contains all whitespace characters in ASCII.
    """
    b = _bytes_from_decode_data(b)
    if adobe:
        if not (b.startswith(_A85START) and b.endswith(_A85END)):
            raise ValueError("Ascii85 encoded byte sequences must be bracketed "
381
                             "by {!r} and {!r}".format(_A85START, _A85END))
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
        b = b[2:-2] # Strip off start/end markers
    #
    # We have to go through this stepwise, so as to ignore spaces and handle
    # special short sequences
    #
    packI = struct.Struct('!I').pack
    decoded = []
    decoded_append = decoded.append
    curr = []
    curr_append = curr.append
    curr_clear = curr.clear
    for x in b + b'u' * 4:
        if b'!'[0] <= x <= b'u'[0]:
            curr_append(x)
            if len(curr) == 5:
                acc = 0
                for x in curr:
                    acc = 85 * acc + (x - 33)
                try:
                    decoded_append(packI(acc))
                except struct.error:
                    raise ValueError('Ascii85 overflow') from None
                curr_clear()
        elif x == b'z'[0]:
            if curr:
                raise ValueError('z inside Ascii85 5-tuple')
            decoded_append(b'\0\0\0\0')
        elif foldspaces and x == b'y'[0]:
            if curr:
                raise ValueError('y inside Ascii85 5-tuple')
            decoded_append(b'\x20\x20\x20\x20')
        elif x in ignorechars:
            # Skip whitespace
            continue
        else:
            raise ValueError('Non-Ascii85 digit found: %c' % x)

    result = b''.join(decoded)
    padding = 4 - len(curr)
    if padding:
        # Throw away the extra padding
        result = result[:-padding]
    return result

# The following code is originally taken (with permission) from Mercurial

428 429 430 431
_b85alphabet = (b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                b"abcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~")
_b85chars = None
_b85chars2 = None
432 433 434 435 436
_b85dec = None

def b85encode(b, pad=False):
    """Encode an ASCII-encoded byte array in base85 format.

437
    If pad is true, the input is padded with "\\0" so its length is a multiple of
438 439
    4 characters before encoding.
    """
440 441 442 443 444 445
    global _b85chars, _b85chars2
    # Delay the initialization of tables to not waste memory
    # if the function is never called
    if _b85chars is None:
        _b85chars = [bytes((i,)) for i in _b85alphabet]
        _b85chars2 = [(a + b) for a in _b85chars for b in _b85chars]
446 447 448 449 450
    return _85encode(b, _b85chars, _b85chars2, pad)

def b85decode(b):
    """Decode base85-encoded byte array"""
    global _b85dec
451 452
    # Delay the initialization of tables to not waste memory
    # if the function is never called
453 454
    if _b85dec is None:
        _b85dec = [None] * 256
455 456
        for i, c in enumerate(_b85alphabet):
            _b85dec[c] = i
457

458
    b = _bytes_from_decode_data(b)
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
    padding = (-len(b)) % 5
    b = b + b'~' * padding
    out = []
    packI = struct.Struct('!I').pack
    for i in range(0, len(b), 5):
        chunk = b[i:i + 5]
        acc = 0
        try:
            for c in chunk:
                acc = acc * 85 + _b85dec[c]
        except TypeError:
            for j, c in enumerate(chunk):
                if _b85dec[c] is None:
                    raise ValueError('bad base85 character at position %d'
                                    % (i + j)) from None
            raise
        try:
            out.append(packI(acc))
        except struct.error:
            raise ValueError('base85 overflow in hunk starting at byte %d'
                             % i) from None

    result = b''.join(out)
    if padding:
        result = result[:-padding]
    return result
485

486 487
# Legacy interface.  This code could be cleaned up since I don't believe
# binascii has any line length limitations.  It just doesn't seem worth it
488
# though.  The files should be opened in binary mode.
489

490
MAXLINESIZE = 76 # Excluding the CRLF
491
MAXBINSIZE = (MAXLINESIZE//4)*3
492 493

def encode(input, output):
494
    """Encode a file; input and output are binary files."""
495
    while True:
496
        s = input.read(MAXBINSIZE)
497 498
        if not s:
            break
499 500
        while len(s) < MAXBINSIZE:
            ns = input.read(MAXBINSIZE-len(s))
501 502 503
            if not ns:
                break
            s += ns
504 505
        line = binascii.b2a_base64(s)
        output.write(line)
506

507

508
def decode(input, output):
509
    """Decode a file; input and output are binary files."""
510
    while True:
511
        line = input.readline()
512 513
        if not line:
            break
514 515
        s = binascii.a2b_base64(line)
        output.write(s)
516

517 518 519 520 521 522 523 524 525 526 527 528 529 530 531
def _input_type_check(s):
    try:
        m = memoryview(s)
    except TypeError as err:
        msg = "expected bytes-like object, not %s" % s.__class__.__name__
        raise TypeError(msg) from err
    if m.format not in ('c', 'b', 'B'):
        msg = ("expected single byte elements, not %r from %s" %
                                          (m.format, s.__class__.__name__))
        raise TypeError(msg)
    if m.ndim != 1:
        msg = ("expected 1-D data, not %d-D data from %s" %
                                          (m.ndim, s.__class__.__name__))
        raise TypeError(msg)

532

533 534 535
def encodebytes(s):
    """Encode a bytestring into a bytestring containing multiple lines
    of base-64 data."""
536
    _input_type_check(s)
537 538 539 540
    pieces = []
    for i in range(0, len(s), MAXBINSIZE):
        chunk = s[i : i + MAXBINSIZE]
        pieces.append(binascii.b2a_base64(chunk))
541
    return b"".join(pieces)
542

543 544 545 546 547 548
def encodestring(s):
    """Legacy alias of encodebytes()."""
    import warnings
    warnings.warn("encodestring() is a deprecated alias, use encodebytes()",
                  DeprecationWarning, 2)
    return encodebytes(s)
549

550

551 552
def decodebytes(s):
    """Decode a bytestring of base-64 data into a bytestring."""
553
    _input_type_check(s)
554
    return binascii.a2b_base64(s)
555

556 557 558 559 560 561
def decodestring(s):
    """Legacy alias of decodebytes()."""
    import warnings
    warnings.warn("decodestring() is a deprecated alias, use decodebytes()",
                  DeprecationWarning, 2)
    return decodebytes(s)
562

563

564 565 566
# Usable as a script...
def main():
    """Small main program"""
567 568 569
    import sys, getopt
    try:
        opts, args = getopt.getopt(sys.argv[1:], 'deut')
570
    except getopt.error as msg:
571
        sys.stdout = sys.stderr
572 573
        print(msg)
        print("""usage: %s [-d|-e|-u|-t] [file|-]
574 575
        -d, -u: decode
        -e: encode (default)
576
        -t: encode and decode string 'Aladdin:open sesame'"""%sys.argv[0])
577 578 579 580 581 582
        sys.exit(2)
    func = encode
    for o, a in opts:
        if o == '-e': func = encode
        if o == '-d': func = decode
        if o == '-u': func = decode
583
        if o == '-t': test(); return
584
    if args and args[0] != '-':
585 586
        with open(args[0], 'rb') as f:
            func(f, sys.stdout.buffer)
587
    else:
588
        func(sys.stdin.buffer, sys.stdout.buffer)
589

590

591 592 593
def test():
    s0 = b"Aladdin:open sesame"
    print(repr(s0))
594
    s1 = encodebytes(s0)
595
    print(repr(s1))
596
    s2 = decodebytes(s1)
597 598
    print(repr(s2))
    assert s0 == s2
599

600

601
if __name__ == '__main__':
602
    main()