diff options
author | Rutger Broekhoff | 2021-05-22 16:56:57 +0200 |
---|---|---|
committer | Rutger Broekhoff | 2021-05-22 16:56:57 +0200 |
commit | d52705d2587a7cc96e535b9d9730ea8cab5594c8 (patch) | |
tree | 517bdc45c0acd2eb52a6514b0b99181d97f88b96 /src | |
parent | 6bcacd5e07d715930d43dbfb9ac5cb3d723b4df8 (diff) | |
download | zig-nkeys-d52705d2587a7cc96e535b9d9730ea8cab5594c8.tar.gz zig-nkeys-d52705d2587a7cc96e535b9d9730ea8cab5594c8.zip |
Rewrite Base32 decoder
Diffstat (limited to 'src')
-rw-r--r-- | src/base32.zig | 129 | ||||
-rw-r--r-- | src/nkeys.zig | 16 |
2 files changed, 87 insertions, 58 deletions
diff --git a/src/base32.zig b/src/base32.zig index fa58327..559427b 100644 --- a/src/base32.zig +++ b/src/base32.zig | |||
@@ -1,4 +1,5 @@ | |||
1 | const std = @import("std"); | 1 | const std = @import("std"); |
2 | const testing = std.testing; | ||
2 | 3 | ||
3 | pub const Encoder = struct { | 4 | pub const Encoder = struct { |
4 | const Self = @This(); | 5 | const Self = @This(); |
@@ -25,7 +26,7 @@ pub const Encoder = struct { | |||
25 | std.debug.assert(dest.len >= out_len); | 26 | std.debug.assert(dest.len >= out_len); |
26 | 27 | ||
27 | var e = init(source); | 28 | var e = init(source); |
28 | for (dest) |*b| b.* = e.next() orelse unreachable; | 29 | for (dest[0..out_len]) |*b| b.* = e.next() orelse unreachable; |
29 | return dest[0..out_len]; | 30 | return dest[0..out_len]; |
30 | } | 31 | } |
31 | 32 | ||
@@ -94,8 +95,8 @@ pub const Encoder = struct { | |||
94 | } | 95 | } |
95 | 96 | ||
96 | // Returns the next 5-bit integer, read from `self.buffer`. | 97 | // Returns the next 5-bit integer, read from `self.buffer`. |
97 | fn next_u5(self: *Self) ?u5 { | 98 | fn nextU5(self: *Self) ?u5 { |
98 | // `self.buffer` is read 5 bits at a time by `next_u5`. | 99 | // `self.buffer` is read 5 bits at a time by `nextU5`. |
99 | // Because of the elements of `self.buffer` being 8 bits each, we need to | 100 | // Because of the elements of `self.buffer` being 8 bits each, we need to |
100 | // read from 2 bytes from `self.buffer` to return a whole u5. | 101 | // read from 2 bytes from `self.buffer` to return a whole u5. |
101 | // `front_bits` are the bits that come first, read from `self.buffer[self.index]`. | 102 | // `front_bits` are the bits that come first, read from `self.buffer[self.index]`. |
@@ -118,7 +119,7 @@ pub const Encoder = struct { | |||
118 | self.index = null; | 119 | self.index = null; |
119 | } | 120 | } |
120 | } else { | 121 | } else { |
121 | // We need to read from the current byte in the next call to `next_u5` too. | 122 | // We need to read from the current byte in the next call to `nextU5` too. |
122 | self.bit_off += 5; | 123 | self.bit_off += 5; |
123 | } | 124 | } |
124 | 125 | ||
@@ -132,40 +133,43 @@ pub const Encoder = struct { | |||
132 | 133 | ||
133 | // Returns the next byte of the encoded buffer. | 134 | // Returns the next byte of the encoded buffer. |
134 | pub fn next(self: *Self) ?u8 { | 135 | pub fn next(self: *Self) ?u8 { |
135 | const unencoded = self.next_u5() orelse return null; | 136 | const unencoded = self.nextU5() orelse return null; |
136 | return char(unencoded); | 137 | return char(unencoded); |
137 | } | 138 | } |
138 | }; | 139 | }; |
139 | 140 | ||
140 | // TODO(rutgerbrf): simplify the code of the decoder | ||
141 | |||
142 | pub const DecodeError = error{CorruptInputError}; | 141 | pub const DecodeError = error{CorruptInputError}; |
143 | 142 | ||
144 | pub const Decoder = struct { | 143 | pub const Decoder = struct { |
145 | const Self = @This(); | 144 | const Self = @This(); |
146 | 145 | ||
147 | out_off: u4 = 0, | 146 | buffer: []const u8, |
148 | buf: u8 = 0, | 147 | index: ?usize, |
149 | 148 | ||
150 | pub fn read(self: *Self, c: u8) DecodeError!?u8 { | 149 | buf: u8, |
151 | var ret: ?u8 = null; | 150 | buf_len: u4, |
152 | var decoded_c = try decodeChar(c); | 151 | |
153 | var bits_left: u3 = 5; | 152 | pub fn init(buffer: []const u8) Self { |
154 | while (bits_left > 0) { | 153 | return .{ |
155 | var space_avail: u4 = 8 - self.out_off; | 154 | .buffer = buffer, |
156 | var write_bits: u3 = if (bits_left < space_avail) bits_left else @truncate(u3, space_avail); | 155 | .index = 0, |
157 | bits_left -= write_bits; | 156 | .buf_len = 0, |
158 | var mask: u8 = (@as(u8, 0x01) << write_bits) - 1; | 157 | .buf = 0, |
159 | var want: u8 = (decoded_c >> bits_left) & mask; | 158 | }; |
160 | self.buf |= want << @truncate(u3, space_avail - write_bits); | 159 | } |
161 | self.out_off += write_bits; | 160 | |
162 | if (self.out_off == 8) { | 161 | pub fn calcSize(source_len: usize) usize { |
163 | ret = self.buf; | 162 | const source_len_bits = source_len * 5; |
164 | self.out_off = 0; | 163 | return source_len_bits / 8; |
165 | self.buf = 0; | 164 | } |
166 | } | 165 | |
167 | } | 166 | pub fn decode(dest: []u8, source: []const u8) DecodeError![]const u8 { |
168 | return ret; | 167 | const out_len = calcSize(source.len); |
168 | std.debug.assert(dest.len >= out_len); | ||
169 | |||
170 | var d = init(source); | ||
171 | for (dest[0..out_len]) |*b| b.* = (try d.next()) orelse unreachable; | ||
172 | return dest[0..out_len]; | ||
169 | } | 173 | } |
170 | 174 | ||
171 | fn decodeChar(p: u8) DecodeError!u5 { | 175 | fn decodeChar(p: u8) DecodeError!u5 { |
@@ -180,26 +184,57 @@ pub const Decoder = struct { | |||
180 | } | 184 | } |
181 | return value; | 185 | return value; |
182 | } | 186 | } |
183 | }; | ||
184 | 187 | ||
185 | pub fn decodedLen(enc_len: usize) usize { | 188 | fn nextU5(self: *Self) DecodeError!?u5 { |
186 | const enc_len_bits = enc_len * 5; | 189 | const index = self.index orelse return null; |
187 | return enc_len_bits / 8; | 190 | self.index = if (index + 1 < self.buffer.len) index + 1 else null; |
188 | } | 191 | return try decodeChar(self.buffer[index]); |
192 | } | ||
189 | 193 | ||
190 | pub fn decode(ps: []const u8, out: []u8) DecodeError!usize { | 194 | pub fn next(self: *Self) DecodeError!?u8 { |
191 | var d = Decoder{}; | 195 | var read_any = false; |
192 | var i: usize = 0; | 196 | while (true) { |
193 | for (ps) |p| { | 197 | const c = (try self.nextU5()) orelse break; |
194 | if (i >= out.len) break; | 198 | const buf_remaining_len = 8 - self.buf_len; |
195 | if (try d.read(p)) |b| { | 199 | const write_len = if (buf_remaining_len > 5) 5 else buf_remaining_len; |
196 | out[i] = b; | 200 | const c_remaining_len = 5 - write_len; |
197 | i += 1; | 201 | self.buf |= (@as(u8, c) << 3) >> @truncate(u3, self.buf_len); |
202 | self.buf_len += write_len; | ||
203 | read_any = true; | ||
204 | if (self.buf_len == 8) { | ||
205 | const ret = self.buf; | ||
206 | self.buf_len = c_remaining_len; | ||
207 | self.buf = 0; | ||
208 | if (write_len != 5) self.buf = @as(u8, c) << @truncate(u3, write_len + 3); | ||
209 | return ret; | ||
210 | } | ||
198 | } | 211 | } |
212 | if ((self.buf_len == 0 or self.buf == 0) and !read_any) return null; | ||
213 | |||
214 | const ret = self.buf; | ||
215 | self.buf_len = 0; | ||
216 | self.buf = 0; | ||
217 | |||
218 | return ret; | ||
199 | } | 219 | } |
200 | if (d.out_off != 0 and i < out.len) { | 220 | }; |
201 | out[i] = d.buf; | 221 | |
202 | i += 1; | 222 | test { |
203 | } | 223 | const encoded = "ORUGS4ZANFZSAYJAORSXG5A"; |
204 | return i; // amount of bytes processed | 224 | const decoded = "this is a test"; |
225 | |||
226 | var decode_buf: [Decoder.calcSize(encoded.len)]u8 = undefined; | ||
227 | _ = try Decoder.decode(&decode_buf, encoded); | ||
228 | |||
229 | try testing.expectEqualStrings(decoded, &decode_buf); | ||
230 | } | ||
231 | |||
232 | test { | ||
233 | const encoded = "SNAH7EH5X4P5R2M2RGF3LVAL6NRFIXLN2E67O6FNRUQ4JCQBPL64GEBPLY"; | ||
234 | const decoded = [_]u8{ 0x93, 0x40, 0x7f, 0x90, 0xfd, 0xbf, 0x1f, 0xd8, 0xe9, 0x9a, 0x89, 0x8b, 0xb5, 0xd4, 0x0b, 0xf3, 0x62, 0x54, 0x5d, 0x6d, 0xd1, 0x3d, 0xf7, 0x78, 0xad, 0x8d, 0x21, 0xc4, 0x8a, 0x01, 0x7a, 0xfd, 0xc3, 0x10, 0x2f, 0x5e }; | ||
235 | |||
236 | var decode_buf: [Decoder.calcSize(encoded.len)]u8 = undefined; | ||
237 | _ = try Decoder.decode(&decode_buf, encoded); | ||
238 | |||
239 | try testing.expectEqualSlices(u8, &decoded, &decode_buf); | ||
205 | } | 240 | } |
diff --git a/src/nkeys.zig b/src/nkeys.zig index 818fd98..8806a81 100644 --- a/src/nkeys.zig +++ b/src/nkeys.zig | |||
@@ -289,7 +289,7 @@ fn decode( | |||
289 | ) !DecodedNKey(prefix_len, data_len) { | 289 | ) !DecodedNKey(prefix_len, data_len) { |
290 | var raw: [prefix_len + data_len + 2]u8 = undefined; | 290 | var raw: [prefix_len + data_len + 2]u8 = undefined; |
291 | defer wipeBytes(&raw); | 291 | defer wipeBytes(&raw); |
292 | std.debug.assert((try base32.decode(text[0..], &raw)) == raw.len); | 292 | _ = try base32.Decoder.decode(&raw, text[0..]); |
293 | 293 | ||
294 | var checksum = mem.readIntLittle(u16, raw[raw.len - 2 .. raw.len]); | 294 | var checksum = mem.readIntLittle(u16, raw[raw.len - 2 .. raw.len]); |
295 | try crc16.validate(raw[0 .. raw.len - 2], checksum); | 295 | try crc16.validate(raw[0 .. raw.len - 2], checksum); |
@@ -347,25 +347,19 @@ pub fn fromSeed(text: *const text_seed) !SeedKeyPair { | |||
347 | pub fn isValidEncoding(text: []const u8) bool { | 347 | pub fn isValidEncoding(text: []const u8) bool { |
348 | if (text.len < 4) return false; | 348 | if (text.len < 4) return false; |
349 | var made_crc: u16 = 0; | 349 | var made_crc: u16 = 0; |
350 | var dec = base32.Decoder{}; | 350 | var dec = base32.Decoder.init(text); |
351 | var crc_buf: [2]u8 = undefined; | 351 | var crc_buf: [2]u8 = undefined; |
352 | var crc_buf_len: u8 = 0; | 352 | var crc_buf_len: u8 = 0; |
353 | var expect_len: usize = base32.decodedLen(text.len); | 353 | var expect_len: usize = base32.Decoder.calcSize(text.len); |
354 | var wrote_n_total: usize = 0; | 354 | var wrote_n_total: usize = 0; |
355 | for (text) |c, i| { | 355 | while (dec.next() catch return false) |b| { |
356 | var b = (dec.read(c) catch return false) orelse continue; | ||
357 | wrote_n_total += 1; | 356 | wrote_n_total += 1; |
358 | if (crc_buf_len == 2) made_crc = crc16.update(made_crc, &.{crc_buf[0]}); | 357 | if (crc_buf_len == 2) made_crc = crc16.update(made_crc, &.{crc_buf[0]}); |
359 | crc_buf[0] = crc_buf[1]; | 358 | crc_buf[0] = crc_buf[1]; |
360 | crc_buf[1] = b; | 359 | crc_buf[1] = b; |
361 | if (crc_buf_len != 2) crc_buf_len += 1; | 360 | if (crc_buf_len != 2) crc_buf_len += 1; |
362 | } | 361 | } |
363 | if (dec.out_off != 0 and wrote_n_total < expect_len) { | 362 | std.debug.assert(wrote_n_total == expect_len); |
364 | if (crc_buf_len == 2) made_crc = crc16.update(made_crc, &.{crc_buf[0]}); | ||
365 | crc_buf[0] = crc_buf[1]; | ||
366 | crc_buf[1] = dec.buf; | ||
367 | if (crc_buf_len != 2) crc_buf_len += 1; | ||
368 | } | ||
369 | if (crc_buf_len != 2) unreachable; | 363 | if (crc_buf_len != 2) unreachable; |
370 | var got_crc = mem.readIntLittle(u16, &crc_buf); | 364 | var got_crc = mem.readIntLittle(u16, &crc_buf); |
371 | return made_crc == got_crc; | 365 | return made_crc == got_crc; |