Skip to content

Commit

Permalink
add support for EdDSA tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
softprops committed Jul 31, 2024
1 parent 82e522a commit 998755f
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 1 deletion.
15 changes: 15 additions & 0 deletions src/decode.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ const Header = @import("root.zig").Header;
/// Key used for decoding JWT tokens
pub const DecodingKey = union(enum) {
secret: []const u8,
edsa: std.crypto.sign.Ed25519.PublicKey,

fn fromEdsaBytes(bytes: [std.crypto.sign.Ed25519.SecretKey]u8) !@This() {
return .{ .edsa = try std.crypto.sign.Ed25519.SecretKey.fromBytes(bytes) };
}
};

fn decodePart(allocator: std.mem.Allocator, comptime T: type, encoded: []const u8) !T {
Expand Down Expand Up @@ -86,6 +91,16 @@ pub fn verify(
return error.InvalidSignature;
}
},
.EdDSA => {
var src: [std.crypto.sign.Ed25519.Signature.encoded_length]u8 = undefined;
@memcpy(&src, sig);
std.crypto.sign.Ed25519.Signature.fromBytes(src).verify(msg, key.edsa) catch {
return error.InvalidSignature;
};
},

//
//
else => return error.TODO,
}

Expand Down
17 changes: 17 additions & 0 deletions src/encode.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,20 @@ const Header = @import("root.zig").Header;
/// Key used for encoding JWT token components
pub const EncodingKey = union(enum) {
secret: []const u8,
edsa: std.crypto.sign.Ed25519.SecretKey,

/// create a new edsa encoding key from edsa secret key bytes
pub fn fromEdsaBytes(bytes: [std.crypto.sign.Ed25519.SecretKey.encoded_length]u8) !@This() {
return .{ .edsa = try std.crypto.sign.Ed25519.SecretKey.fromBytes(bytes) };
}
};

test EncodingKey {
const pair = try std.crypto.sign.Ed25519.KeyPair.create(null);
const key = try EncodingKey.fromEdsaBytes(pair.secret_key.toBytes());
try std.testing.expectEqual(key.edsa.toBytes(), pair.secret_key.toBytes());
}

fn encodePart(
allocator: std.mem.Allocator,
part: anytype,
Expand Down Expand Up @@ -41,6 +53,11 @@ fn sign(
std.crypto.auth.hmac.sha2.HmacSha512.create(&dest, msg, key.secret);
break :blk allocator.dupe(u8, &dest);
},
.EdDSA => blk: {
const pair = try std.crypto.sign.Ed25519.KeyPair.fromSecretKey(key.edsa);
const dest = (try pair.sign(msg, null)).toBytes();
break :blk allocator.dupe(u8, &dest);
},
else => return error.TODO,
};
}
Expand Down
37 changes: 36 additions & 1 deletion src/root.zig
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,42 @@ pub fn JWT(comptime ClaimSet: type) type {
};
}

test "roundtrip" {
test "EdDSA.roundtrip" {
const allocator = std.testing.allocator;
const validation: Validation = .{
.now = struct {
fn func() u64 {
return 1722441274; // Wednesday, July 31, 2024 3:54:34 PM - in seconds
}
}.func,
};

// predicable key generation
var seed: [32]u8 = undefined;
_ = try std.fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
const pair = try std.crypto.sign.Ed25519.KeyPair.create(seed);

const token = try encode(
allocator,
.{ .alg = .EdDSA },
.{ .sub = "test", .exp = validation.now() + 60 },
.{ .edsa = pair.secret_key },
);
defer allocator.free(token);
try std.testing.expectEqualStrings("eyJhbGciOiJFZERTQSJ9.eyJzdWIiOiJ0ZXN0IiwiZXhwIjoxNzIyNDQxMzM0fQ.qV1oOiw9DmKfaxVv3_W6zn878ke6D-G70bzAMTtNB4-3dCk5reLaqrXEMluP-0vjgfdQaJc-J0XANMP2CVymDQ", token);

var jwt = try decode(
allocator,
struct { sub: []const u8 },
token,
.{ .edsa = pair.public_key },
validation,
);
defer jwt.deinit();
try std.testing.expectEqualStrings("test", jwt.claims.sub);
}

test "HS256.roundtrip" {
const allocator = std.testing.allocator;
const validation: Validation = .{
.now = struct {
Expand Down

0 comments on commit 998755f

Please sign in to comment.