1 module jwtd.jwt_botan;
2 
3 import jwtd.jwt;
4 
5 version (UseBotan) {
6 
7 	import botan.rng.auto_rng;
8 	import botan.mac.hmac;
9 	import botan.hash.hash;
10 	import botan.hash.sha2_32 : SHA256;
11 	import botan.hash.sha2_64 : SHA384, SHA512;
12 	import memutils.unique;
13 
14 	string sign(string msg, string key, JWTAlgorithm algo = JWTAlgorithm.HS256) {
15 		ubyte[] sign;
16 
17 		void sign_hs(HashFunction hashFun) {
18 			Unique!HMAC hmac = new HMAC(hashFun);
19 
20 			hmac.setKey(cast(const(ubyte)*)key.ptr, key.length);
21 			hmac.update(msg);
22 			sign = hmac.finished()[].dup;
23 		}
24 
25 		void sign_rs(string emsaName) {
26 			import botan.filters.data_src;
27 			import botan.pubkey.algo.rsa;
28 
29 			Unique!AutoSeededRNG rng = new AutoSeededRNG;
30 			auto privKey = loadKey(cast(DataSource)DataSourceMemory(key), *rng);
31 			auto signer = PKSigner(privKey, emsaName);
32 			sign = signer.signMessage(cast(const(ubyte)*)msg.ptr, msg.length, *rng)[].dup;
33 		}
34 
35 		void sign_es(string emsaName) {
36 			import botan.filters.data_src;
37 			import botan.pubkey.algo.ecdsa;
38 
39 			Unique!AutoSeededRNG rng = new AutoSeededRNG;
40 			auto privKey = loadKey(cast(DataSource)DataSourceMemory(key), *rng);
41 			auto signer = PKSigner(privKey, emsaName, DER_SEQUENCE);
42 			sign = signer.signMessage(cast(const(ubyte)*)msg.ptr, msg.length, *rng)[].dup;
43 		}
44 
45 		switch(algo) {
46 			case JWTAlgorithm.NONE: {
47 				break;
48 			}
49 			case JWTAlgorithm.HS256: {
50 				Unique!SHA256 hash = new SHA256();
51 				sign_hs(*hash);
52 				break;
53 			}
54 			case JWTAlgorithm.HS384: {
55 				Unique!SHA384 hash = new SHA384();
56 				sign_hs(*hash);
57 				break;
58 			}
59 			case JWTAlgorithm.HS512: {
60 				Unique!SHA512 hash = new SHA512();
61 				sign_hs(*hash);
62 				break;
63 			}
64 			case JWTAlgorithm.RS256:
65 				sign_rs("EMSA3(SHA-256)");
66 				break;
67 			case JWTAlgorithm.RS384:
68 				sign_rs("EMSA3(SHA-384)");
69 				break;
70 			case JWTAlgorithm.RS512:
71 				sign_rs("EMSA3(SHA-512)");
72 				break;
73 			case JWTAlgorithm.ES256:
74 				sign_es("EMSA1(SHA-256)");
75 				break;
76 			case JWTAlgorithm.ES384:
77 				sign_es("EMSA1(SHA-384)");
78 				break;
79 			case JWTAlgorithm.ES512:
80 				sign_es("EMSA1(SHA-512)");
81 				break;
82 			default:
83 				throw new SignException("Wrong algorithm.");
84 		}
85 
86 		return cast(string)sign;
87 	}
88 
89 	bool verifySignature(string signature, string signing_input, string key, JWTAlgorithm algo = JWTAlgorithm.HS256) {
90 
91 		bool verify_rs(string emsaName) {
92 			import x509 = botan.pubkey.x509_key;
93 			import botan.pubkey.algo.rsa;
94 			import botan.filters.data_src;
95 
96 			auto pubKey = x509.loadKey(cast(DataSource)DataSourceMemory(key));
97 			auto verifier = PKVerifier(pubKey, emsaName);
98 			return verifier.verifyMessage(
99 				cast(const(ubyte)*)signing_input.ptr, signing_input.length,
100 				cast(const(ubyte)*)signature.ptr, signature.length);
101 		}
102 
103 		bool verify_es(string emsaName) {
104 			import x509 = botan.pubkey.x509_key;
105 			import botan.pubkey.algo.ecdsa;
106 			import botan.filters.data_src;
107 
108 			auto pubKey = x509.loadKey(cast(DataSource)DataSourceMemory(key));
109 			auto verifier = PKVerifier(pubKey, emsaName, DER_SEQUENCE);
110 			return verifier.verifyMessage(
111 				cast(const(ubyte)*)signing_input.ptr, signing_input.length,
112 				cast(const(ubyte)*)signature.ptr, signature.length);
113 		}
114 
115 		switch(algo) {
116 			case JWTAlgorithm.NONE:
117 				return key.length == 0;
118 			case JWTAlgorithm.HS256:
119 			case JWTAlgorithm.HS384:
120 			case JWTAlgorithm.HS512:
121 				return signature == sign(signing_input, key, algo);
122 			case JWTAlgorithm.RS256:
123 				return verify_rs("EMSA3(SHA-256)");
124 			case JWTAlgorithm.RS384:
125 				return verify_rs("EMSA3(SHA-384)");
126 			case JWTAlgorithm.RS512:
127 				return verify_rs("EMSA3(SHA-512)");
128 			case JWTAlgorithm.ES256:
129 				return verify_es("EMSA1(SHA-256)");
130 			case JWTAlgorithm.ES384:
131 				return verify_es("EMSA1(SHA-384)");
132 			case JWTAlgorithm.ES512:
133 				return verify_es("EMSA1(SHA-512)");
134 			default:
135 				throw new VerifyException("Wrong algorithm.");
136 		}
137 	}
138 }