aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Rutger Broekhoff2021-05-22 16:56:57 +0200
committerLibravatar Rutger Broekhoff2021-05-22 16:56:57 +0200
commitd52705d2587a7cc96e535b9d9730ea8cab5594c8 (patch)
tree517bdc45c0acd2eb52a6514b0b99181d97f88b96
parent6bcacd5e07d715930d43dbfb9ac5cb3d723b4df8 (diff)
downloadzig-nkeys-d52705d2587a7cc96e535b9d9730ea8cab5594c8.tar.gz
zig-nkeys-d52705d2587a7cc96e535b9d9730ea8cab5594c8.zip
Rewrite Base32 decoder
-rw-r--r--src/base32.zig129
-rw-r--r--src/nkeys.zig16
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 @@
1const std = @import("std"); 1const std = @import("std");
2const testing = std.testing;
2 3
3pub const Encoder = struct { 4pub 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
142pub const DecodeError = error{CorruptInputError}; 141pub const DecodeError = error{CorruptInputError};
143 142
144pub const Decoder = struct { 143pub 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
185pub 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
190pub 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; 222test {
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
232test {
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 {
347pub fn isValidEncoding(text: []const u8) bool { 347pub 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;