Skip to content

Commit df21605

Browse files
committed
add blake2b
1 parent e553bb7 commit df21605

File tree

4 files changed

+225
-9
lines changed

4 files changed

+225
-9
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ The JWT library have signing methods:
152152
- `HS384`: jwt.SigningMethodHS384
153153
- `HS512`: jwt.SigningMethodHS512
154154

155+
- `BLAKE2B`: jwt.SigningMethodBLAKE2B
156+
155157
- `none`: jwt.SigningMethodNone
156158

157159

build.zig.zon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
.{
22
.name = "zig-jwt",
33
.description = "A JWT (JSON Web Token) library for zig.",
4-
.version = "1.0.13",
4+
.version = "1.0.15",
55
.paths = .{
66
"build.zig",
77
"build.zig.zon",

src/blake2b.zig

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
const std = @import("std");
2+
const fmt = std.fmt;
3+
const testing = std.testing;
4+
const blake2 = std.crypto.hash.blake2;
5+
const Allocator = std.mem.Allocator;
6+
7+
pub const SigningBlake2b = SignBlake2b(blake2.Blake2b256, "BLAKE2B");
8+
9+
pub fn SignBlake2b(comptime Hash: type, comptime name: []const u8) type {
10+
return struct {
11+
alloc: Allocator,
12+
13+
const Self = @This();
14+
15+
pub const digest_length = Hash.digest_length;
16+
17+
pub fn init(alloc: Allocator) Self {
18+
return .{
19+
.alloc = alloc,
20+
};
21+
}
22+
23+
pub fn alg(self: Self) []const u8 {
24+
_ = self;
25+
return name;
26+
}
27+
28+
pub fn signLength(self: Self) isize {
29+
_ = self;
30+
return digest_length;
31+
}
32+
33+
pub fn sign(self: Self, msg: []const u8, key: []const u8) ![]u8 {
34+
if (key.len * 8 < 256) {
35+
return error.JWTKeyTooShort;
36+
}
37+
38+
var h = Hash.init(.{
39+
.key = key,
40+
});
41+
h.update(msg[0..]);
42+
43+
var out: [digest_length]u8 = undefined;
44+
h.final(out[0..]);
45+
46+
const out_string = try self.alloc.alloc(u8, @as(usize, @intCast(self.signLength())));
47+
@memcpy(out_string[0..], out[0..]);
48+
49+
return out_string;
50+
}
51+
52+
pub fn verify(self: Self, msg: []const u8, signature: []u8, key: []const u8) bool {
53+
if (key.len * 8 < 256) {
54+
return false;
55+
}
56+
57+
const sign_length = self.signLength();
58+
if (signature.len != sign_length) {
59+
return false;
60+
}
61+
62+
var h = Hash.init(.{
63+
.key = key,
64+
});
65+
h.update(msg[0..]);
66+
67+
var out: [digest_length]u8 = undefined;
68+
h.final(out[0..]);
69+
70+
if (std.mem.eql(u8, out[0..], signature[0..])) {
71+
return true;
72+
}
73+
74+
return false;
75+
}
76+
};
77+
}
78+
79+
test "SigningBlake2b" {
80+
const alloc = std.heap.page_allocator;
81+
const h = SigningBlake2b.init(alloc);
82+
83+
const alg = h.alg();
84+
const signLength = h.signLength();
85+
try testing.expectEqual(32, signLength);
86+
try testing.expectEqualStrings("BLAKE2B", alg);
87+
88+
const msg = "test-data";
89+
const key = "12345678901234567890as1234567890";
90+
const sign = "d40bb120a0915ab65e0051fca93854775bd1380a1fb012ebd5c5df361159937e";
91+
92+
const signed = try h.sign(msg, key);
93+
94+
var signature2: [32]u8 = undefined;
95+
@memcpy(signature2[0..], signed);
96+
const singed_res = fmt.bytesToHex(signature2, .lower);
97+
98+
try testing.expectEqualStrings(sign, singed_res[0..]);
99+
100+
const veri = h.verify(msg, signed, key);
101+
102+
try testing.expectEqual(true, veri);
103+
104+
}
105+
106+
test "SigningBlake2b key short" {
107+
const alloc = std.heap.page_allocator;
108+
const h = SigningBlake2b.init(alloc);
109+
110+
const alg = h.alg();
111+
const signLength = h.signLength();
112+
try testing.expectEqual(32, signLength);
113+
try testing.expectEqualStrings("BLAKE2B", alg);
114+
115+
const msg = "test-data";
116+
const key = "test-key";
117+
118+
var need_true: bool = false;
119+
_ = h.sign(msg, key) catch |err| {
120+
need_true = true;
121+
try testing.expectEqual(error.JWTKeyTooShort, err);
122+
};
123+
try testing.expectEqual(true, need_true);
124+
125+
}

src/jwt.zig

Lines changed: 97 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub const rsa_pss = @import("rsa_pss.zig");
1111
pub const ecdsa = @import("ecdsa.zig");
1212
pub const eddsa = @import("eddsa.zig");
1313
pub const hmac = @import("hmac.zig");
14+
pub const blake2b = @import("blake2b.zig");
1415
pub const none = @import("none.zig");
1516
pub const utils = @import("utils.zig");
1617
pub const builder = @import("builder.zig");
@@ -39,6 +40,8 @@ pub const SigningMethodHS256 = JWT(hmac.SigningHS256, []const u8, []const u8);
3940
pub const SigningMethodHS384 = JWT(hmac.SigningHS384, []const u8, []const u8);
4041
pub const SigningMethodHS512 = JWT(hmac.SigningHS512, []const u8, []const u8);
4142

43+
pub const SigningMethodBLAKE2B = JWT(blake2b.SigningBlake2b, []const u8, []const u8);
44+
4245
pub const SigningMethodNone = JWT(none.SigningNone, []const u8, []const u8);
4346

4447
pub const Error = error {
@@ -48,8 +51,8 @@ pub const Error = error {
4851
JWTAlgoInvalid
4952
};
5053

51-
pub fn JWT(comptime Signer: type, comptime SecretKeyType: type, comptime PublicKeyType: type) type {
52-
const BuilderType = builder.Builder(Signer, SecretKeyType);
54+
pub fn JWT(comptime Signer: type, comptime SignKeyType: type, comptime VerifyKeyType: type) type {
55+
const BuilderType = builder.Builder(Signer, SignKeyType);
5356

5457
return struct {
5558
signer: Signer,
@@ -73,17 +76,17 @@ pub fn JWT(comptime Signer: type, comptime SecretKeyType: type, comptime PublicK
7376
}
7477

7578
// use SigningMethod to make token
76-
pub fn sign(self: Self, claims: anytype, secret_key: SecretKeyType) ![]const u8 {
79+
pub fn sign(self: Self, claims: anytype, sign_key: SignKeyType) ![]const u8 {
7780
const header = .{
7881
.typ = "JWT",
7982
.alg = self.signer.alg(),
8083
};
8184

82-
return try self.signWithHeader(header, claims, secret_key);
85+
return try self.signWithHeader(header, claims, sign_key);
8386
}
8487

8588
// use SigningMethod with header to make token
86-
pub fn signWithHeader(self: Self, header: anytype, claims: anytype, secret_key: SecretKeyType) ![]const u8 {
89+
pub fn signWithHeader(self: Self, header: anytype, claims: anytype, sign_key: SignKeyType) ![]const u8 {
8790
var t = Token.init(self.alloc);
8891
try t.setHeader(header);
8992
try t.setClaims(claims);
@@ -93,7 +96,7 @@ pub fn JWT(comptime Signer: type, comptime SecretKeyType: type, comptime PublicK
9396
const signing_string = try t.signingString();
9497
defer self.alloc.free(signing_string);
9598

96-
const signature = try self.signer.sign(signing_string, secret_key);
99+
const signature = try self.signer.sign(signing_string, sign_key);
97100
defer self.alloc.free(signature);
98101

99102
t.withSignature(signature);
@@ -103,7 +106,7 @@ pub fn JWT(comptime Signer: type, comptime SecretKeyType: type, comptime PublicK
103106
}
104107

105108
// parse token and verify token signature
106-
pub fn parse(self: Self, token_string: []const u8, public_key: PublicKeyType) !Token {
109+
pub fn parse(self: Self, token_string: []const u8, verify_key: VerifyKeyType) !Token {
107110
var t = Token.init(self.alloc);
108111
try t.parse(token_string);
109112

@@ -126,7 +129,7 @@ pub fn JWT(comptime Signer: type, comptime SecretKeyType: type, comptime PublicK
126129
const signing_string = try t.signingString();
127130
defer self.alloc.free(signing_string);
128131

129-
if (!self.signer.verify(signing_string, token_sign, public_key)) {
132+
if (!self.signer.verify(signing_string, token_sign, verify_key)) {
130133
return Error.JWTVerifyFail;
131134
}
132135

@@ -185,6 +188,10 @@ pub fn getSigningMethod(name: []const u8) !type {
185188
return SigningMethodES384;
186189
}
187190

191+
if (utils.eq(name, "ES256K")) {
192+
return SigningMethodES256K;
193+
}
194+
188195
if (utils.eq(name, "EdDSA")) {
189196
return SigningMethodEdDSA;
190197
}
@@ -199,6 +206,10 @@ pub fn getSigningMethod(name: []const u8) !type {
199206
return SigningMethodHS512;
200207
}
201208

209+
if (utils.eq(name, "BLAKE2B")) {
210+
return SigningMethodBLAKE2B;
211+
}
212+
202213
if (utils.eq(name, "none")) {
203214
return SigningMethodNone;
204215
}
@@ -226,12 +237,16 @@ test "getSigningMethod" {
226237
try testing.expectEqual(SigningMethodES256, try getSigningMethod("ES256"));
227238
try testing.expectEqual(SigningMethodES384, try getSigningMethod("ES384"));
228239

240+
try testing.expectEqual(SigningMethodES256K, try getSigningMethod("ES256K"));
241+
229242
try testing.expectEqual(SigningMethodEdDSA, try getSigningMethod("EdDSA"));
230243

231244
try testing.expectEqual(SigningMethodHS256, try getSigningMethod("HS256"));
232245
try testing.expectEqual(SigningMethodHS384, try getSigningMethod("HS384"));
233246
try testing.expectEqual(SigningMethodHS512, try getSigningMethod("HS512"));
234247

248+
try testing.expectEqual(SigningMethodBLAKE2B, try getSigningMethod("BLAKE2B"));
249+
235250
try testing.expectEqual(SigningMethodNone, try getSigningMethod("none"));
236251

237252
var need_true: bool = false;
@@ -552,6 +567,30 @@ test "SigningMethodHS512" {
552567

553568
}
554569

570+
test "SigningMethodBLAKE2B" {
571+
const alloc = std.heap.page_allocator;
572+
573+
const claims = .{
574+
.aud = "example.com",
575+
.sub = "foo",
576+
};
577+
const key = "12345678901234567890as1234567890";
578+
579+
const s = SigningMethodBLAKE2B.init(alloc);
580+
const token_string = try s.sign(claims, key);
581+
try testing.expectEqual(true, token_string.len > 0);
582+
583+
// ==========
584+
585+
const p = SigningMethodBLAKE2B.init(alloc);
586+
var parsed = try p.parse(token_string, key);
587+
588+
const claims2 = try parsed.getClaims();
589+
try testing.expectEqualStrings(claims.aud, claims2.object.get("aud").?.string);
590+
try testing.expectEqualStrings(claims.sub, claims2.object.get("sub").?.string);
591+
592+
}
593+
555594
test "SigningMethodNone" {
556595
const alloc = std.heap.page_allocator;
557596

@@ -886,6 +925,56 @@ test "SigningMethodHS256 Check fail" {
886925

887926
}
888927

928+
test "SigningMethodBLAKE2B Check" {
929+
const alloc = std.heap.page_allocator;
930+
931+
const key = "0323354b2b0fa5bc837e0665777ba68f5ab328e6f054c928a90f84b2d2502ebfd3fb5a92d20647ef968ab4c377623d223d2e2172052e4f08c0cd9af567d080a3";
932+
const token_str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJCTEFLRTJCIn0.eyJpc3MiOiJqb2UiLCJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.zVtM3_PWCeOBjiV3bJcx1KoxeZCUs7zqfy6DF2mfb9M";
933+
934+
var key_buf: [key.len]u8 = undefined;
935+
const key_bytes = try fmt.hexToBytes(&key_buf, key);
936+
937+
const claims = .{
938+
.iss = "joe",
939+
.exp = 1300819380,
940+
.@"http://example.com/is_root" = true,
941+
};
942+
943+
const s = SigningMethodBLAKE2B.init(alloc);
944+
const token_string = try s.sign(claims, key_bytes);
945+
try testing.expectEqual(true, token_string.len > 0);
946+
try testing.expectEqualStrings(token_str, token_string);
947+
948+
// ==========
949+
950+
const p = SigningMethodBLAKE2B.init(alloc);
951+
var parsed = try p.parse(token_str, key_bytes);
952+
953+
const claims2 = try parsed.getClaims();
954+
try testing.expectEqualStrings(claims.iss, claims2.object.get("iss").?.string);
955+
956+
}
957+
958+
test "SigningMethodBLAKE2B Check fail" {
959+
const alloc = std.heap.page_allocator;
960+
961+
const key = "0323354b2b0fa5bc837e0665777ba68f5ab328e6f054c928a90f84b2d2502ebfd3fb5a92d20647ef968ab4c377623d223d2e2172052e4f08c0cd9af567d080a3";
962+
const token_str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJCTEFLRTJCIn0.eyJpc3MiOiJqb2UiLCJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.zVtM3_PWCeOBjiV3bJcx1KoxeZCUs7zqfy6DF2mfb12";
963+
964+
var key_buf: [key.len]u8 = undefined;
965+
const key_bytes = try fmt.hexToBytes(&key_buf, key);
966+
967+
const p = SigningMethodBLAKE2B.init(alloc);
968+
969+
var need_true: bool = false;
970+
_ = p.parse(token_str, key_bytes) catch |err| {
971+
need_true = true;
972+
try testing.expectEqual(Error.JWTVerifyFail, err);
973+
};
974+
try testing.expectEqual(true, need_true);
975+
976+
}
977+
889978
test "getTokenHeader" {
890979
const alloc = std.heap.page_allocator;
891980

0 commit comments

Comments
 (0)