diff options
| -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; |