1 module jwtlited.phobos;
2 
3 public import jwtlited;
4 
5 alias HS256Handler = HMACImpl!(JWTAlgorithm.HS256);
6 alias HS384Handler = HMACImpl!(JWTAlgorithm.HS384);
7 alias HS512Handler = HMACImpl!(JWTAlgorithm.HS512);
8 
9 /**
10  * Implementation of HS256, HS384 and HS512 signing algorithms.
11  */
12 private struct HMACImpl(JWTAlgorithm implAlg)
13 {
14     import std.digest.hmac : HMAC;
15     import std.digest.sha : SHA256, SHA384, SHA512;
16 
17     static if (implAlg == JWTAlgorithm.HS256) {
18         enum signLen = 32;
19         alias SHA = SHA256;
20     } else static if (implAlg == JWTAlgorithm.HS384) {
21         enum signLen = 48;
22         alias SHA = SHA384;
23     } else static if (implAlg == JWTAlgorithm.HS512) {
24         enum signLen = 64;
25         alias SHA = SHA512;
26     }
27     else static assert(0, "Unsupprted algorithm for HMAC implementation");
28 
29     private const(ubyte)[] key;
30 
31     bool loadKey(K)(K key) if (isToken!K)
32     {
33         if (!key.length) return false;
34         this.key = cast(const(ubyte)[])key;
35         return true;
36     }
37 
38     bool isValidAlg(JWTAlgorithm alg) { return implAlg == alg; }
39 
40     bool isValid(V, S)(V value, S sign) if (isToken!V && isToken!S)
41     {
42         assert(key.length, "Secret key not set");
43         if (!key.length || !value.length || sign.length != signLen) return false;
44 
45         immutable sig = HMAC!SHA(key)
46             .put(cast(const(ubyte)[])value)
47             .finish();
48         assert(sig.length == signLen);
49         return cast(const(ubyte)[])sign == sig[];
50     }
51 
52     JWTAlgorithm signAlg() { return implAlg; }
53 
54     int sign(S, V)(auto ref S sink, auto ref V value) if (isToken!V)
55     {
56         import std.range : put;
57 
58         assert(key.length, "Secret key not set");
59         if (!key.length || !value.length) return false;
60 
61         put(sink, HMAC!SHA(key)
62             .put(cast(const(ubyte)[])value)
63             .finish()[]);
64         return signLen;
65     }
66 }
67 
68 ///
69 @safe unittest
70 {
71     import jwtlited.phobos;
72     import std.stdio;
73 
74     HS256Handler handler;
75     enum payload = `{"foo":42}`;
76     bool ret = handler.loadKey("foo bar baz");
77     assert(ret);
78     char[512] tok;
79     immutable len = handler.encode(tok[], payload);
80     assert(len > 0);
81     writeln("HS256: ", tok[0..len]);
82 
83     assert(handler.validate(tok[0..len]));
84     char[32] hdr, pay;
85     assert(handler.decode(tok[0..len], hdr[], pay[]));
86     assert(pay[0..payload.length] == payload);
87 }
88 
89 @safe unittest
90 {
91     static assert(isValidator!HS256Handler);
92     static assert(isSigner!HS256Handler);
93 }
94 
95 version (unittest) import jwtlited.tests;
96 
97 @("Phobos tests")
98 @safe unittest
99 {
100     static void eval(H)(ref immutable TestCase tc)
101     {
102         H h;
103         assert(h.loadKey(tc.key) == !!(tc.valid & Valid.key));
104         evalTest(h, tc);
105     }
106 
107     import std.algorithm : canFind, filter;
108 
109     with (JWTAlgorithm)
110     {
111         static immutable testAlgs = [HS256, HS384, HS512];
112 
113         foreach (tc; testCases.filter!(a => testAlgs.canFind(a.alg)))
114         {
115             switch (tc.alg)
116             {
117                 case HS256: eval!HS256Handler(tc); break;
118                 case HS384: eval!HS384Handler(tc); break;
119                 case HS512: eval!HS512Handler(tc); break;
120                 default: assert(0);
121             }
122         }
123     }
124 }