OLD | NEW |
---|---|
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 part of crypto; | 1 part of crypto; |
6 | 2 |
7 abstract class _CryptoUtils { | 3 const Base64Codec Base64 = const Base64Codec(); |
Lasse Reichstein Nielsen
2015/05/29 06:48:41
Name it either "BASE64" or "base64" (style guide f
Alexander Ivanov
2015/05/29 13:12:06
Done.
| |
8 static String bytesToHex(List<int> bytes) { | |
9 var result = new StringBuffer(); | |
10 for (var part in bytes) { | |
11 result.write('${part < 16 ? '0' : ''}${part.toRadixString(16)}'); | |
12 } | |
13 return result.toString(); | |
14 } | |
15 | 4 |
16 static const int PAD = 61; // '=' | 5 const List<int> _decodeTable = |
17 static const int CR = 13; // '\r' | |
18 static const int LF = 10; // '\n' | |
19 static const int LINE_LENGTH = 76; | |
20 | |
21 static const String _encodeTable = | |
22 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | |
23 | |
24 static const String _encodeTableUrlSafe = | |
25 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; | |
26 | |
27 // Lookup table used for finding Base 64 alphabet index of a given byte. | |
28 // -2 : Outside Base 64 alphabet. | |
29 // -1 : '\r' or '\n' | |
30 // 0 : = (Padding character). | |
31 // >0 : Base 64 alphabet index of given byte. | |
32 static const List<int> _decodeTable = | |
33 const [ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -2, -2, -1, -2, -2, | 6 const [ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -2, -2, -1, -2, -2, |
34 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, | 7 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, |
35 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, 62, -2, 63, | 8 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, 62, -2, 63, |
36 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, 0, -2, -2, | 9 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, 0, -2, -2, |
37 -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, | 10 -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, |
38 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2, 63, | 11 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2, 63, |
39 -2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, | 12 -2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, |
40 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2, | 13 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2, |
41 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, | 14 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, |
42 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, | 15 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, |
43 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, | 16 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, |
44 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, | 17 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, |
45 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, | 18 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, |
46 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, | 19 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, |
47 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, | 20 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, |
48 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 ]; | 21 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 ]; |
49 | 22 |
50 static String bytesToBase64(List<int> bytes, | 23 const String _encodeTableUrlSafe = |
51 [bool urlSafe = false, | 24 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; |
52 bool addLineSeparator = false]) { | 25 |
26 const String _encodeTable = | |
27 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | |
28 | |
29 const int LINE_LENGTH = 76; | |
Lasse Reichstein Nielsen
2015/05/29 06:48:42
These should probably be private.
Alexander Ivanov
2015/05/29 13:12:05
Done.
| |
30 const int PAD = 61; // '=' | |
31 const int CR = 13; // '\r' | |
32 const int LF = 10; // '\n' | |
33 | |
34 class Base64Codec extends Codec<List<int>, String> { | |
35 | |
36 final bool _urlSafe; | |
37 final bool _addLineSeparator; | |
38 | |
39 /** | |
40 * Instantiates a new [Base64Codec]. | |
Lasse Reichstein Nielsen
2015/05/29 06:48:42
Document the parameters, say what they do and what
Alexander Ivanov
2015/05/29 13:12:04
Done.
| |
41 * | |
42 */ | |
43 | |
Lasse Reichstein Nielsen
2015/05/29 06:48:42
No empty line here.
Alexander Ivanov
2015/05/29 13:12:05
Done.
| |
44 const Base64Codec({ bool urlSafe: false, bool addLineSeparator: false }) | |
Lasse Reichstein Nielsen
2015/05/29 06:48:41
No space between "{" and "bool" (or before "}").
Alexander Ivanov
2015/05/29 13:12:05
Done.
| |
45 : _urlSafe = urlSafe, | |
Lasse Reichstein Nielsen
2015/05/29 06:48:42
indent four spaces before :, then one space after.
Alexander Ivanov
2015/05/29 13:12:05
Done.
| |
46 _addLineSeparator = addLineSeparator; | |
47 | |
48 String get name => "base64"; | |
49 | |
50 String encode(List<int> bytes, | |
51 { bool urlSafe, | |
Lasse Reichstein Nielsen
2015/05/29 06:48:42
Again no space after '{' or before '}' (becaue sty
Alexander Ivanov
2015/05/29 13:12:05
Done.
| |
52 bool addLineSeparator }) { | |
53 if (urlSafe == null) urlSafe = _urlSafe; | |
54 if (addLineSeparator == null) addLineSeparator = _addLineSeparator; | |
55 return new Base64Encoder(urlSafe: urlSafe, addLineSeparator: addLineSeparato r) | |
Lasse Reichstein Nielsen
2015/05/29 06:48:42
Long line, break it somewhere.
Alexander Ivanov
2015/05/29 13:12:05
Done.
| |
56 .convert(bytes); | |
57 } | |
58 | |
59 Base64Encoder get encoder => new Base64Encoder(urlSafe: _urlSafe, addLineSepar ator: _addLineSeparator); | |
Lasse Reichstein Nielsen
2015/05/29 06:48:42
Long line.
Alexander Ivanov
2015/05/29 13:12:04
Done.
| |
60 Base64Decoder get decoder => new Base64Decoder(); | |
61 | |
62 } | |
63 | |
64 | |
65 class Base64Encoder extends Converter<List<int>, String> { | |
Lasse Reichstein Nielsen
2015/05/29 07:15:15
Could generally use more comments, but ok for now.
Alexander Ivanov
2015/05/29 13:12:06
Done.
| |
66 final bool _urlSafe; | |
67 final bool _addLineSeparator; | |
68 | |
69 const Base64Encoder({ bool urlSafe: false, bool addLineSeparator: false}) | |
70 : _urlSafe = urlSafe, | |
71 _addLineSeparator = addLineSeparator; | |
72 | |
73 String convert(List<int> bytes) { | |
53 int len = bytes.length; | 74 int len = bytes.length; |
54 if (len == 0) { | 75 if (len == 0) { |
55 return ""; | 76 return ""; |
56 } | 77 } |
57 final String lookup = urlSafe ? _encodeTableUrlSafe : _encodeTable; | 78 final String lookup = _urlSafe ? _encodeTableUrlSafe : _encodeTable; |
58 // Size of 24 bit chunks. | 79 // Size of 24 bit chunks. |
59 final int remainderLength = len.remainder(3); | 80 final int remainderLength = len.remainder(3); |
60 final int chunkLength = len - remainderLength; | 81 final int chunkLength = len - remainderLength; |
61 // Size of base output. | 82 // Size of base output. |
62 int outputLen = ((len ~/ 3) * 4) + ((remainderLength > 0) ? 4 : 0); | 83 int outputLen = ((len ~/ 3) * 4) + ((remainderLength > 0) ? 4 : 0); |
Lasse Reichstein Nielsen
2015/05/29 06:48:41
This seems too complex. Let's look at that later :
Alexander Ivanov
2015/05/29 13:12:05
Done.
| |
63 // Add extra for line separators. | 84 // Add extra for line separators. |
64 if (addLineSeparator) { | 85 if (_addLineSeparator) { |
65 outputLen += ((outputLen - 1) ~/ LINE_LENGTH) << 1; | 86 outputLen += ((outputLen - 1) ~/ LINE_LENGTH) << 1; |
66 } | 87 } |
67 List<int> out = new List<int>(outputLen); | 88 List<int> out = new List<int>(outputLen); |
68 | 89 |
69 // Encode 24 bit chunks. | 90 // Encode 24 bit chunks. |
70 int j = 0, i = 0, c = 0; | 91 int j = 0, i = 0, c = 0; |
71 while (i < chunkLength) { | 92 while (i < chunkLength) { |
72 int x = ((bytes[i++] << 16) & 0xFFFFFF) | | 93 int x = ((bytes[i++] << 16) & 0xFFFFFF) | |
73 ((bytes[i++] << 8) & 0xFFFFFF) | | 94 ((bytes[i++] << 8) & 0xFFFFFF) | |
74 bytes[i++]; | 95 bytes[i++]; |
75 out[j++] = lookup.codeUnitAt(x >> 18); | 96 out[j++] = lookup.codeUnitAt(x >> 18); |
76 out[j++] = lookup.codeUnitAt((x >> 12) & 0x3F); | 97 out[j++] = lookup.codeUnitAt((x >> 12) & 0x3F); |
77 out[j++] = lookup.codeUnitAt((x >> 6) & 0x3F); | 98 out[j++] = lookup.codeUnitAt((x >> 6) & 0x3F); |
78 out[j++] = lookup.codeUnitAt(x & 0x3f); | 99 out[j++] = lookup.codeUnitAt(x & 0x3f); |
79 // Add optional line separator for each 76 char output. | 100 // Add optional line separator for each 76 char output. |
80 if (addLineSeparator && ++c == 19 && j < outputLen - 2) { | 101 if (_addLineSeparator && ++c == 19 && j < outputLen - 2) { |
81 out[j++] = CR; | 102 out[j++] = CR; |
82 out[j++] = LF; | 103 out[j++] = LF; |
83 c = 0; | 104 c = 0; |
84 } | 105 } |
85 } | 106 } |
86 | 107 |
87 // If input length if not a multiple of 3, encode remaining bytes and | 108 // If input length if not a multiple of 3, encode remaining bytes and |
88 // add padding. | 109 // add padding. |
89 if (remainderLength == 1) { | 110 if (remainderLength == 1) { |
90 int x = bytes[i]; | 111 int x = bytes[i]; |
91 out[j++] = lookup.codeUnitAt(x >> 2); | 112 out[j++] = lookup.codeUnitAt(x >> 2); |
92 out[j++] = lookup.codeUnitAt((x << 4) & 0x3F); | 113 out[j++] = lookup.codeUnitAt((x << 4) & 0x3F); |
93 out[j++] = PAD; | 114 out[j++] = PAD; |
94 out[j++] = PAD; | 115 out[j++] = PAD; |
95 } else if (remainderLength == 2) { | 116 } else if (remainderLength == 2) { |
96 int x = bytes[i]; | 117 int x = bytes[i]; |
97 int y = bytes[i + 1]; | 118 int y = bytes[i + 1]; |
98 out[j++] = lookup.codeUnitAt(x >> 2); | 119 out[j++] = lookup.codeUnitAt(x >> 2); |
99 out[j++] = lookup.codeUnitAt(((x << 4) | (y >> 4)) & 0x3F); | 120 out[j++] = lookup.codeUnitAt(((x << 4) | (y >> 4)) & 0x3F); |
100 out[j++] = lookup.codeUnitAt((y << 2) & 0x3F); | 121 out[j++] = lookup.codeUnitAt((y << 2) & 0x3F); |
101 out[j++] = PAD; | 122 out[j++] = PAD; |
102 } | 123 } |
103 | 124 |
104 return new String.fromCharCodes(out); | 125 return new String.fromCharCodes(out); |
105 } | 126 } |
106 | 127 |
107 static List<int> base64StringToBytes(String input) { | 128 _Base64EncoderSink startChunkedConversion(Sink<String> sink) { |
129 StringConversionSink stringSink; | |
130 if (sink is StringConversionSink) { | |
131 stringSink = sink; | |
132 } else { | |
133 stringSink = new StringConversionSink.from(sink); | |
134 } | |
135 return new _Base64EncoderSink(stringSink, _urlSafe, _addLineSeparator); | |
136 } | |
137 } | |
138 | |
139 class _Base64EncoderSink extends ChunkedConversionSink<List<int>> { | |
140 | |
141 final Base64Encoder _encoder; | |
142 final ChunkedConversionSink<String> _outSink; | |
143 final List<int> _buffer = new List<int>(3); | |
144 int _buffer_count = 0; | |
Lasse Reichstein Nielsen
2015/05/29 07:15:16
_bufferCount.
Alexander Ivanov
2015/05/29 13:12:05
Done.
| |
145 | |
146 _Base64EncoderSink(this._outSink, urlSafe, addLineSeparator) | |
147 : _encoder = new Base64Encoder(urlSafe: urlSafe, addLineSeparator: addLineSe parator); | |
Lasse Reichstein Nielsen
2015/05/29 06:48:42
long line, indent : by four.
Alexander Ivanov
2015/05/29 13:12:05
Done.
| |
148 | |
149 void add(List<int> chunk) { | |
150 | |
Lasse Reichstein Nielsen
2015/05/29 06:48:42
We usually don't have an empty line at the beginni
Alexander Ivanov
2015/05/29 13:12:05
Done.
| |
151 var next_buffer_length = (chunk.length + _buffer_count) % 3; | |
152 | |
153 if(chunk.length + _buffer_count >= 3) { | |
Lasse Reichstein Nielsen
2015/05/29 06:48:42
space between "if" and "(". More cases below.
Alexander Ivanov
2015/05/29 13:12:05
Done.
| |
154 int remainder = chunk.length - next_buffer_length; | |
155 var decodable = new List.from(_buffer.sublist(0, _buffer_count))..addAll(c hunk.sublist(0, remainder)); | |
Lasse Reichstein Nielsen
2015/05/29 06:48:42
long line
Alexander Ivanov
2015/05/29 13:12:05
Done.
| |
156 _buffer.setRange(0, next_buffer_length, chunk.sublist(remainder)); | |
Lasse Reichstein Nielsen
2015/05/29 07:15:16
Use the fourth argument to setRange instead of cre
Alexander Ivanov
2015/05/29 13:12:05
Done.
| |
157 | |
158 _outSink.add(_encoder.convert(decodable)); | |
Lasse Reichstein Nielsen
2015/05/29 07:15:16
The current thing works, which is great, so this i
Alexander Ivanov
2015/05/29 13:12:05
Done.
| |
159 } | |
160 else { | |
Lasse Reichstein Nielsen
2015/05/29 06:48:42
Else goes on the same line as '}'.
Alexander Ivanov
2015/05/29 13:12:05
Done.
| |
161 _buffer.setRange(_buffer_count, _buffer_count + chunk.length, chunk); | |
Lasse Reichstein Nielsen
2015/05/29 07:15:16
_buffer_cunt + chunk.length => nextBufferLength
Alexander Ivanov
2015/05/29 13:12:04
Done.
| |
162 } | |
163 _buffer_count = next_buffer_length; | |
164 } | |
165 | |
166 void close() { | |
167 if(_buffer_count > 0) { | |
168 _outSink.add(_encoder.convert(_buffer.sublist(0, _buffer_count))); | |
169 } | |
170 _outSink.close(); | |
171 } | |
172 } | |
173 | |
174 class Base64Decoder extends Converter<String, List<int>> { | |
175 const Base64Decoder(); | |
176 | |
177 List<int> convert(String input, {bool alwaysPadding: false}) { | |
108 int len = input.length; | 178 int len = input.length; |
109 if (len == 0) { | 179 if (len == 0) { |
110 return new List<int>(0); | 180 return new List<int>(0); |
111 } | 181 } |
112 | 182 |
113 // Count '\r', '\n' and illegal characters, For illegal characters, | 183 // Count '\r', '\n' and illegal characters, For illegal characters, |
114 // throw an exception. | 184 // throw an exception. |
115 int extrasLen = 0; | 185 int extrasLen = 0; |
116 for (int i = 0; i < len; i++) { | 186 for (int i = 0; i < len; i++) { |
117 int c = _decodeTable[input.codeUnitAt(i)]; | 187 int c = _decodeTable[input.codeUnitAt(i)]; |
(...skipping 29 matching lines...) Expand all Loading... | |
147 x = ((x << 6) & 0xFFFFFF) | c; | 217 x = ((x << 6) & 0xFFFFFF) | c; |
148 j--; | 218 j--; |
149 } | 219 } |
150 } | 220 } |
151 out[o++] = x >> 16; | 221 out[o++] = x >> 16; |
152 if (o < outputLen) { | 222 if (o < outputLen) { |
153 out[o++] = (x >> 8) & 0xFF; | 223 out[o++] = (x >> 8) & 0xFF; |
154 if (o < outputLen) out[o++] = x & 0xFF; | 224 if (o < outputLen) out[o++] = x & 0xFF; |
155 } | 225 } |
156 } | 226 } |
227 | |
157 return out; | 228 return out; |
158 } | 229 } |
159 | 230 |
231 _Base64DecoderSink startChunkedConversion(Sink<List<int>> sink) { | |
232 if(sink is! ByteConversionSink) { | |
Lasse Reichstein Nielsen
2015/05/29 06:48:41
Indent by 2 only.
Alexander Ivanov
2015/05/29 13:12:05
Done.
| |
233 sink = new ByteConversionSink.from(sink); | |
234 } | |
235 return new _Base64DecoderSink(sink); | |
236 } | |
160 } | 237 } |
238 | |
239 | |
240 class _Base64DecoderSink extends ChunkedConversionSink<String> { | |
241 | |
242 final Base64Decoder _decoder = new Base64Decoder(); | |
243 final ChunkedConversionSink<List<int>> _outSink; | |
244 String _buffer = ""; | |
245 | |
246 _Base64DecoderSink(this._outSink); | |
247 | |
248 void add(String chunk) { | |
249 int next_buffer_length = (chunk.length + _buffer.length) % 4; | |
Lasse Reichstein Nielsen
2015/05/29 06:48:42
We use camelCase for variables, so "nextBufferLeng
Alexander Ivanov
2015/05/29 13:12:05
Done.
| |
250 | |
251 if(chunk.length + _buffer.length >= 4) { | |
252 int remainder = chunk.length - next_buffer_length; | |
253 String decodable = _buffer + chunk.substring(0, remainder); | |
254 _buffer = chunk.substring(remainder); | |
255 | |
256 _outSink.add(_decoder.convert(decodable)); | |
257 } | |
258 else { | |
259 _buffer += chunk; | |
260 } | |
261 } | |
262 | |
263 void close() { | |
264 if (!_buffer.isEmpty) { | |
265 throw new FormatException("Size of Base 64 input must be a multiple of 4 : ${_buffer.toString()}"); | |
Lasse Reichstein Nielsen
2015/05/29 06:48:41
Use other arguments to FormatException (source and
Alexander Ivanov
2015/05/29 13:12:05
Done.
| |
266 } | |
267 _outSink.close(); | |
268 } | |
269 } | |
270 | |
OLD | NEW |