RFC 9285
The Base45 Data Encoding
RFC 9285: The Base45 Data Encoding β¨
okay bestie, this one is RFC 9285, called The Base45 Data Encoding π¦ It's an Informational doc (so not a Standards Track spec π), published in August 2022, with ISSN 2070-1721. The authors are P. FΓ€ltstrΓΆm from Netnod, F. Ljunggren from Kirei, and D.W. van Gulik from Webweaving π It's an IETF product that went through public review and got approved by the IESG πΈ
Abstract
Base45 is an encoding scheme that's built on top of Base64, Base32, and Base16 π
Status of This Memo
This doc is not an Internet Standards Track spec. It's just published for informational purposes π
It's an IETF product and represents the consensus of the IETF community. It went through public review and got the okay from the Internet Engineering Steering Group (IESG). Not every IESG-approved doc is a candidate for any level of Internet Standard, so see Section 2 of RFC 7841 for that π
You can find the current status, any errata, and how to send feedback at https://www.rfc-editor.org/info/rfc9285.
Copyright Notice
Copyright (c) 2022 IETF Trust and the persons identified as the document authors. All rights reserved π
This doc is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info), as they were in effect on the publication date. Please actually read them, because they explain your rights and restrictions for this doc π«Ά Any Code Components pulled out of this doc have to include Revised BSD License text, as described in Section 4.e of the Trust Legal Provisions. They also come with no warranty, as described in the Revised BSD License π
Table of Contents
- 1. Introduction
- 2. Conventions Used in This Document
- 3. Interpretation of Encoded Data
- 4. The Base45 Encoding
- 4.1. When to Use and Not Use Base45
- 4.2. The Alphabet Used in Base45
- 4.3. Encoding Examples
- 4.4. Decoding Example
- 5. IANA Considerations
- 6. Security Considerations
- 7. Normative References
- Acknowledgements
- Authors' Addresses
1. Introduction β¨
okay so a QR code is how we store text as a little picture π± There are a bunch of ways to encode the text in a QR code, like Numeric, Alphanumeric, and Byte mode. Even in Byte mode, though, a typical QR reader assumes the bytes are text in UTF-8 or ISO/IEC 8859-1. So QR codes can't just hold arbitrary binary data directly π That data has to be turned into text first, and then that text gets encoded as a QR code.
Base64, Base32, and Base16 already exist and are described in [RFC4648]. Compared to those, Base45 gives you a more compact QR code encoding, which is a whole mood π₯
One big difference between Base45 and those others is the key table. Also, the padding with '=' isn't required π
2. Conventions Used in This Document π
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" are to be read as described in BCP 14 [RFC2119] [RFC8174], but only when they show up in all capitals, like they do here π§
3. Interpretation of Encoded Data π
Encoded data gets interpreted the way [RFC4648] describes, with one exception: a different alphabet is selected π¦
4. The Base45 Encoding πΈ
QR codes can only store so much binary data. In practice, you have to encode binary data as characters using one of the modes that's already defined for QR codes π The easiest mode to use is Alphanumeric mode (see Section 7.3.4 and Table 2 of [ISO18004]. Unfortunately, Alphanumeric mode uses 45 different characters, which means neither Base32 nor Base64 is a super effective encoding here π
Base45 uses a 45-character subset of US-ASCII: the 45 characters you can use in a QR code in Alphanumeric mode (see Section 7.3.4 and Table 2 of [ISO18004]). It encodes 2 bytes in 3 characters, while Base64 encodes 3 bytes in 4 characters π
Let's do the math, bestie π€ For encoding, two bytes [a, b] MUST be read as a number n in base 256. That means an unsigned 16-bit integer, where n = (a * 256) + b.
That number n gets converted to base 45 [c, d, e], so that n = c + (d * 45) + (e * 45 * 45). Notice the order of c, d and e: they're picked so that the left-most [c] is the least significant one π
Then c, d, and e get looked up in Table 1 to make a three-character string. Decoding just reverses the whole process β¨
For encoding a single byte [a], it MUST be read as a base 256 number, which is an unsigned 8-bit integer. That integer MUST be converted to base 45 [c d], so that a = c + (45 * d). Then c and d get looked up in Table 1 to make a two-character string π
A byte string [a b c d ... x y z] with any content and any length MUST be encoded like this: going from left to right, pairs of bytes MUST be encoded the way described above. If the number of bytes is even, the encoded form is a string whose length is evenly divisible by 3. If the number of bytes is odd, the last (rightmost) byte MUST be encoded on two characters, the way described above π
To decode a Base45 string, you do the inverse operations β
4.1. When to Use and Not Use Base45 π¦
If you're storing binary data in a QR code, the suggested move is Alphanumeric mode, which uses 11 bits for 2 characters, as defined in Section 7.3.4 of [ISO18004]. The Extended Channel Interpretation (ECI) mode indicator for this encoding is 0010 π
On the other hand, if the data is going over some other transport, you should use a transport encoding that fits that transport instead of Base45 π«Ά For example, it's not recommended to encode data in Base45 first and then encode that resulting string in Base64 if you're sending it by email. Instead, you should drop the Base45 encoding and encode the data itself in Base64 π
4.2. The Alphabet Used in Base45 π
Alphanumeric mode is defined to use the 45 characters in this alphabet π§
+=====+==========+=====+==========+=====+==========+=====+==========+
|Value| Encoding |Value| Encoding |Value| Encoding |Value| Encoding |
+=====+==========+=====+==========+=====+==========+=====+==========+
| 00| 0 | 12| C | 24| O | 36| Space |
+-----+----------+-----+----------+-----+----------+-----+----------+
| 01| 1 | 13| D | 25| P | 37| $ |
+-----+----------+-----+----------+-----+----------+-----+----------+
| 02| 2 | 14| E | 26| Q | 38| % |
+-----+----------+-----+----------+-----+----------+-----+----------+
| 03| 3 | 15| F | 27| R | 39| * |
+-----+----------+-----+----------+-----+----------+-----+----------+
| 04| 4 | 16| G | 28| S | 40| + |
+-----+----------+-----+----------+-----+----------+-----+----------+
| 05| 5 | 17| H | 29| T | 41| - |
+-----+----------+-----+----------+-----+----------+-----+----------+
| 06| 6 | 18| I | 30| U | 42| . |
+-----+----------+-----+----------+-----+----------+-----+----------+
| 07| 7 | 19| J | 31| V | 43| / |
+-----+----------+-----+----------+-----+----------+-----+----------+
| 08| 8 | 20| K | 32| W | 44| : |
+-----+----------+-----+----------+-----+----------+-----+----------+
| 09| 9 | 21| L | 33| X | | |
+-----+----------+-----+----------+-----+----------+-----+----------+
| 10| A | 22| M | 34| Y | | |
+-----+----------+-----+----------+-----+----------+-----+----------+
| 11| B | 23| N | 35| Z | | |
+-----+----------+-----+----------+-----+----------+-----+----------+
Table 1: The Base45 Alphabet
4.3. Encoding Examples π
Okay bestie, even though all these examples look like normal text, Base45 is really an encoding for binary data, where each octet can be any value from 0-255 π
Encoding example 1:
The string "AB" is the byte sequence [[65 66]]. If we look at all 16 bits, we get 65 * 256 + 66 = 16706. That equals 11 + (11 * 45) + (8 * 45 * 45), so the sequence in base 45 is [11 11 8]. Referring to Table 1, we get the encoded string "BB8" π¦
+-----------+------------------+
| AB | Initial string |
+-----------+------------------+
| [[65 66]] | Decimal value |
+-----------+------------------+
| [16706] | Value in base 16 |
+-----------+------------------+
| [11 11 8] | Value in base 45 |
+-----------+------------------+
| BB8 | Encoded string |
+-----------+------------------+
Table 2: Example 1 in Detail
Encoding example 2:
The string "Hello!!" as ASCII is the byte sequence [[72 101] [108 108] [111 33] [33]]. If we look at this 16 bits at a time, we get [18533 27756 28449 33]. Note the 33 for the last byte. When we look at the values in base 45, we get [[38 6 9] [36 31 13] [9 2 14] [33 0]], where the last byte is represented by two values. The resulting string "%69 VD92EX0" is created by looking up these values in Table 1. Worth noting, it includes a space πΈ
+---------------------------------------+------------------+
| Hello!! | Initial string |
+---------------------------------------+------------------+
| [[72 101] [108 108] [111 33] [33]] | Decimal value |
+---------------------------------------+------------------+
| [18533 27756 28449 33] | Value in base 16 |
+---------------------------------------+------------------+
| [[38 6 9] [36 31 13] [9 2 14] [33 0]] | Value in base 45 |
+---------------------------------------+------------------+
| %69 VD92EX0 | Encoded string |
+---------------------------------------+------------------+
Table 3: Example 2 in Detail
Encoding example 3:
The string "base-45" as ASCII is the byte sequence [[98 97] [115 101] [45 52] [53]]. If we look at this two bytes at a time, we get [25185 29541 11572 53]. Note the 53 for the last byte. When we look at the values in base 45, we get [[30 19 12] [21 26 14] [7 32 5] [8 1]], where the last byte is represented by two values. Referring to Table 1, we get the encoded string "UJCLQE7W581" β¨
+----------------------------------------+------------------+
| base-45 | Initial string |
+----------------------------------------+------------------+
| [[98 97] [115 101] [45 52] [53]] | Decimal value |
+----------------------------------------+------------------+
| [25185 29541 11572 53] | Value in base 16 |
+----------------------------------------+------------------+
| [[30 19 12] [21 26 14] [7 32 5] [8 1]] | Value in base 45 |
+----------------------------------------+------------------+
| UJCLQE7W581 | Encoded string |
+----------------------------------------+------------------+
Table 4: Example 3 in Detail
4.4. Decoding Example π
Decoding example 1:
The string "QED8WEX0" represents, when looked up in Table 1, the values [26 14 13 8 32 14 33 0]. We arrange the numbers in chunks of three, except for the last one which can be two numbers, and get [[26 14 13] [8 32 14] [33 0]]. In base 45, we get [26981 29798 33], where the bytes are [[105 101] [116 102] [33]]. If we look at the ASCII values, we get the string "ietf!" π
+-------------------------------+------------------------+
| QED8WEX0 | Initial string |
+-------------------------------+------------------------+
| [26 14 13 8 32 14 33 0] | Looked up values |
+-------------------------------+------------------------+
| [[26 14 13] [8 32 14] [33 0]] | Groups of three |
+-------------------------------+------------------------+
| [26981 29798 33] | Interpreted as base 45 |
+-------------------------------+------------------------+
| [[105 101] [116 102] [33]] | Values in base 8 |
+-------------------------------+------------------------+
| ietf! | Decoded string |
+-------------------------------+------------------------+
Table 5: Example 4 in Detail
5. IANA Considerations π
Good news, no IANA chores here π This document has no IANA actions.
6. Security Considerations π
Okay, this section is super important, so pay attention π When implementing encoding and decoding, you have to be very careful so that buffer overflow or similar issues do not occur. That of course includes the calculations in base 45 and the lookup in the table of characters (Table 1). A decoder must also be robust regarding input, including proper handling of any octet value 0-255, including the NUL character (ASCII 0).
Base64 and some other encodings pad the string so the encoding starts with an aligned number of characters, while Base45 specifically avoids padding. Because of this, special care has to be taken when an odd number of octets is to be encoded. Similarly, care must be taken if the number of characters to decode is not evenly divisible by 3.
Base encodings use a specific, reduced alphabet to encode binary data. Non-alphabet characters could exist within base-encoded data, caused by data corruption or by design. Non-alphabet characters can be exploited as a "covert channel", where non-protocol data is sent for nefarious purposes. Non-alphabet characters might also be sent in order to exploit implementation errors leading to, for example, buffer overflow attacks.
Implementations MUST reject any input that is not a valid encoding. For example, it MUST reject the input (encoded data) if it contains characters outside the base alphabet (in Table 1) when interpreting base-encoded data.
Even though a Base45-encoded string contains only characters from the alphabet in Table 1, cases like the following have to be considered. The string "FGW" represents 65535 (FFFF in base 16), which is a valid encoding of 16 bits. A slightly different encoded string of the same length, "GGW", would represent 65536 (10000 in base 16), which is represented by more than 16 bits. Implementations MUST also reject the encoded data if it contains a triplet of characters that, when decoded, results in an unsigned integer that is greater than 65535 (FFFF in base 16).
It's worth noting that the resulting string after encoding to Base45 might include non-URL-safe characters. So if the URL including the Base45-encoded data has to be URL-safe, you have to use percent-encoding π
7. Normative References π
- [ISO18004] ISO/IEC, "Information technology - Automatic identification and data capture techniques - QR Code bar code symbology specification", ISO/IEC 18004:2015, February 2015, <https://www.iso.org/standard/62021.html>.
- [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997, <https://www.rfc-editor.org/info/rfc2119>.
- [RFC4648] Josefsson, S., "The Base16, Base32, and Base64 Data Encodings", RFC 4648, DOI 10.17487/RFC4648, October 2006, <https://www.rfc-editor.org/info/rfc4648>.
- [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, May 2017, <https://www.rfc-editor.org/info/rfc8174>.
Acknowledgements π«Ά
The authors thank Mark Adler, Anders Ahl, Alan Barrett, Sam Spens Clason, Alfred Fiedler, Tomas Harreveld, Erik Hellman, Joakim Jardenberg, Michael Joost, Erik Kline, Christian Landgren, Anders Lowinger, Mans Nilsson, Jakob Schlyter, Peter Teufl, and Gaby Whitehead for the feedback. Also, everyone who has been working with Base64 over a long period of years and has proven the implementations are stable π
Authors' Addresses π
- Patrik FΓ€ltstrΓΆm, Netnod, Email: paf@netnod.se
- Fredrik Ljunggren, Kirei, Email: fredrik@kirei.se
- Dirk-Willem van Gulik, Webweaving, Email: dirkx@webweaving.org
π¦ where this page comes from
- ietf: rfc9285 2022-08
- π girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms