Line data Source code
1 : import 'dart:typed_data'; 2 : import 'package:cwtch/third_party/base32/encoding.dart'; 3 : 4 : // ignore: camel_case_types 5 : class base32 { 6 : /// Takes in a [byteList] converts it to a Uint8List so that I can run 7 : /// bit operations on it, then outputs a [String] representation of the 8 : /// base32. 9 0 : static String encode(Uint8List bytesList, {Encoding encoding = Encoding.standardRFC4648}) { 10 0 : var base32Chars = EncodingUtils.getChars(encoding); 11 : var i = 0; 12 0 : var count = (bytesList.length ~/ 5) * 5; 13 : var base32str = ''; 14 0 : while (i < count) { 15 0 : var v1 = bytesList[i++]; 16 0 : var v2 = bytesList[i++]; 17 0 : var v3 = bytesList[i++]; 18 0 : var v4 = bytesList[i++]; 19 0 : var v5 = bytesList[i++]; 20 : 21 0 : base32str += base32Chars[v1 >> 3] + 22 0 : base32Chars[(v1 << 2 | v2 >> 6) & 31] + 23 0 : base32Chars[(v2 >> 1) & 31] + 24 0 : base32Chars[(v2 << 4 | v3 >> 4) & 31] + 25 0 : base32Chars[(v3 << 1 | v4 >> 7) & 31] + 26 0 : base32Chars[(v4 >> 2) & 31] + 27 0 : base32Chars[(v4 << 3 | v5 >> 5) & 31] + 28 0 : base32Chars[v5 & 31]; 29 : } 30 : 31 0 : var remain = bytesList.length - count; 32 0 : if (remain == 1) { 33 0 : var v1 = bytesList[i]; 34 0 : base32str += base32Chars[v1 >> 3] + base32Chars[(v1 << 2) & 31]; 35 0 : if (EncodingUtils.getPadded(encoding)) { 36 0 : base32str += '======'; 37 : } 38 0 : } else if (remain == 2) { 39 0 : var v1 = bytesList[i++]; 40 0 : var v2 = bytesList[i]; 41 0 : base32str += base32Chars[v1 >> 3] + base32Chars[(v1 << 2 | v2 >> 6) & 31] + base32Chars[(v2 >> 1) & 31] + base32Chars[(v2 << 4) & 31]; 42 0 : if (EncodingUtils.getPadded(encoding)) { 43 0 : base32str += '===='; 44 : } 45 0 : } else if (remain == 3) { 46 0 : var v1 = bytesList[i++]; 47 0 : var v2 = bytesList[i++]; 48 0 : var v3 = bytesList[i]; 49 0 : base32str += base32Chars[v1 >> 3] + base32Chars[(v1 << 2 | v2 >> 6) & 31] + base32Chars[(v2 >> 1) & 31] + base32Chars[(v2 << 4 | v3 >> 4) & 31] + base32Chars[(v3 << 1) & 31]; 50 0 : if (EncodingUtils.getPadded(encoding)) { 51 0 : base32str += '==='; 52 : } 53 0 : } else if (remain == 4) { 54 0 : var v1 = bytesList[i++]; 55 0 : var v2 = bytesList[i++]; 56 0 : var v3 = bytesList[i++]; 57 0 : var v4 = bytesList[i]; 58 0 : base32str += base32Chars[v1 >> 3] + 59 0 : base32Chars[(v1 << 2 | v2 >> 6) & 31] + 60 0 : base32Chars[(v2 >> 1) & 31] + 61 0 : base32Chars[(v2 << 4 | v3 >> 4) & 31] + 62 0 : base32Chars[(v3 << 1 | v4 >> 7) & 31] + 63 0 : base32Chars[(v4 >> 2) & 31] + 64 0 : base32Chars[(v4 << 3) & 31]; 65 0 : if (EncodingUtils.getPadded(encoding)) { 66 0 : base32str += '='; 67 : } 68 : } 69 : return base32str; 70 : } 71 : 72 0 : static Uint8List _hexDecode(final String input) => Uint8List.fromList([ 73 0 : for (int i = 0; i < input.length; i += 2) int.parse(input.substring(i, i + 2), radix: 16), 74 : ]); 75 : 76 0 : static String _hexEncode(final Uint8List input) => [for (int i = 0; i < input.length; i++) input[i].toRadixString(16).padLeft(2, '0')].join(); 77 : 78 : /// Takes in a [hex] string, converts the string to a byte list 79 : /// and runs a normal encode() on it. Returning a [String] representation 80 : /// of the base32. 81 0 : static String encodeHexString(String b32hex, {Encoding encoding = Encoding.standardRFC4648}) { 82 0 : return encode(_hexDecode(b32hex), encoding: encoding); 83 : } 84 : 85 : /// Takes in a [utf8string], converts the string to a byte list 86 : /// and runs a normal encode() on it. Returning a [String] representation 87 : /// of the base32. 88 0 : static String encodeString(String utf8string, {Encoding encoding = Encoding.standardRFC4648}) { 89 0 : return encode(Uint8List.fromList(utf8string.codeUnits), encoding: encoding); 90 : } 91 : 92 : /// Takes in a [base32] string and decodes it back to a [String] in hex format. 93 0 : static String decodeAsHexString(String base32, {Encoding encoding = Encoding.standardRFC4648}) { 94 0 : return _hexEncode(decode(base32, encoding: encoding)); 95 : } 96 : 97 : /// Takes in a [base32] string and decodes it back to a [String]. 98 0 : static String decodeAsString(String base32, {Encoding encoding = Encoding.standardRFC4648}) { 99 0 : return decode(base32, encoding: encoding).toList().map((charCode) => String.fromCharCode(charCode)).join(); 100 : } 101 : 102 : /// Takes in a [base32] string and decodes it back to a [Uint8List] that can be 103 : /// converted to a hex string using hexEncode 104 0 : static Uint8List decode(String base32, {Encoding encoding = Encoding.standardRFC4648}) { 105 0 : if (base32.isEmpty) { 106 0 : return Uint8List(0); 107 : } 108 : 109 0 : base32 = _pad(base32, encoding: encoding); 110 : 111 0 : if (!_isValid(base32, encoding: encoding)) { 112 0 : throw FormatException('Invalid Base32 characters'); 113 : } 114 : 115 0 : if (encoding == Encoding.crockford) { 116 0 : base32 = base32.replaceAll('-', ''); 117 : } // Handle crockford dashes. 118 : 119 0 : var base32Decode = EncodingUtils.getDecodeMap(encoding); 120 0 : var length = base32.indexOf('='); 121 0 : if (length == -1) { 122 0 : length = base32.length; 123 : } 124 : 125 : var i = 0; 126 0 : var count = length >> 3 << 3; 127 0 : var bytes = <int>[]; 128 0 : while (i < count) { 129 0 : var v1 = base32Decode[base32[i++]] ?? 0; 130 0 : var v2 = base32Decode[base32[i++]] ?? 0; 131 0 : var v3 = base32Decode[base32[i++]] ?? 0; 132 0 : var v4 = base32Decode[base32[i++]] ?? 0; 133 0 : var v5 = base32Decode[base32[i++]] ?? 0; 134 0 : var v6 = base32Decode[base32[i++]] ?? 0; 135 0 : var v7 = base32Decode[base32[i++]] ?? 0; 136 0 : var v8 = base32Decode[base32[i++]] ?? 0; 137 0 : bytes.add((v1 << 3 | v2 >> 2) & 255); 138 0 : bytes.add((v2 << 6 | v3 << 1 | v4 >> 4) & 255); 139 0 : bytes.add((v4 << 4 | v5 >> 1) & 255); 140 0 : bytes.add((v5 << 7 | v6 << 2 | v7 >> 3) & 255); 141 0 : bytes.add((v7 << 5 | v8) & 255); 142 : } 143 : 144 0 : var remain = length - count; 145 0 : if (remain == 2) { 146 0 : var v1 = base32Decode[base32[i++]] ?? 0; 147 0 : var v2 = base32Decode[base32[i++]] ?? 0; 148 0 : bytes.add((v1 << 3 | v2 >> 2) & 255); 149 0 : } else if (remain == 4) { 150 0 : var v1 = base32Decode[base32[i++]] ?? 0; 151 0 : var v2 = base32Decode[base32[i++]] ?? 0; 152 0 : var v3 = base32Decode[base32[i++]] ?? 0; 153 0 : var v4 = base32Decode[base32[i++]] ?? 0; 154 0 : bytes.add((v1 << 3 | v2 >> 2) & 255); 155 0 : bytes.add((v2 << 6 | v3 << 1 | v4 >> 4) & 255); 156 0 : } else if (remain == 5) { 157 0 : var v1 = base32Decode[base32[i++]] ?? 0; 158 0 : var v2 = base32Decode[base32[i++]] ?? 0; 159 0 : var v3 = base32Decode[base32[i++]] ?? 0; 160 0 : var v4 = base32Decode[base32[i++]] ?? 0; 161 0 : var v5 = base32Decode[base32[i++]] ?? 0; 162 0 : bytes.add((v1 << 3 | v2 >> 2) & 255); 163 0 : bytes.add((v2 << 6 | v3 << 1 | v4 >> 4) & 255); 164 0 : bytes.add((v4 << 4 | v5 >> 1) & 255); 165 0 : } else if (remain == 7) { 166 0 : var v1 = base32Decode[base32[i++]] ?? 0; 167 0 : var v2 = base32Decode[base32[i++]] ?? 0; 168 0 : var v3 = base32Decode[base32[i++]] ?? 0; 169 0 : var v4 = base32Decode[base32[i++]] ?? 0; 170 0 : var v5 = base32Decode[base32[i++]] ?? 0; 171 0 : var v6 = base32Decode[base32[i++]] ?? 0; 172 0 : var v7 = base32Decode[base32[i++]] ?? 0; 173 0 : bytes.add((v1 << 3 | v2 >> 2) & 255); 174 0 : bytes.add((v2 << 6 | v3 << 1 | v4 >> 4) & 255); 175 0 : bytes.add((v4 << 4 | v5 >> 1) & 255); 176 0 : bytes.add((v5 << 7 | v6 << 2 | v7 >> 3) & 255); 177 : } 178 0 : return Uint8List.fromList(bytes); 179 : } 180 : 181 0 : static bool _isValid(String b32str, {Encoding encoding = Encoding.standardRFC4648}) { 182 0 : var regex = EncodingUtils.getRegex(encoding); 183 0 : if (b32str.length % 2 != 0 || !regex.hasMatch(b32str)) { 184 : return false; 185 : } 186 : return true; 187 : } 188 : 189 0 : static String _pad(String base32, {Encoding encoding = Encoding.standardRFC4648}) { 190 0 : if (EncodingUtils.getPadded(encoding)) { 191 0 : int neededPadding = (8 - base32.length % 8) % 8; 192 0 : return base32.padRight(base32.length + neededPadding, '='); 193 : } 194 : return base32; 195 : } 196 : }