1 module itsdangerous.dsigner; 2 3 import std.stdio; 4 import std.format; 5 import std.string: representation; 6 import std.digest.sha; 7 import std.digest.hmac; 8 import std.algorithm.searching; 9 import std.array; 10 11 import itsdangerous.encoding; 12 import itsdangerous.exc; 13 14 bool constantTimeCompare(string val1, string val2){ // not sure if we need this in D 15 /*Return ``True`` if the two strings are equal, ``False`` 16 otherwise. 17 */ 18 const len_eq = val1.length == val2.length; 19 int result; 20 string left; 21 if (len_eq){ 22 result = 0; 23 left = val1; 24 } else { 25 result = 1; 26 left = val2; 27 } 28 import std.range: zip; 29 foreach (x, y; zip(cast(ubyte[])left, cast(ubyte[])val2)) 30 result |= x ^ y; 31 return result == 0; 32 } 33 34 interface SigningAlgorithm { 35 /+Subclasses must implement :meth:`getSignature` to provide 36 signature generation functionality. 37 +/ 38 string getSignature(ubyte[] key, string value); 39 bool verifySignature(ubyte[] key, string value, string sig); 40 } 41 42 class NoneAlgorithm: SigningAlgorithm { 43 string getSignature(ubyte[] key, string value){ 44 return ""; 45 } 46 47 bool verifySignature(ubyte[] key, string value, string sig){ 48 return true; 49 } 50 } 51 52 class HMACAlgorithm(DigestMethod): SigningAlgorithm { 53 54 string getSignature(ubyte[] key, string value){ 55 auto hmac = HMAC!DigestMethod(key); 56 ubyte[] hash = hmac.put(value.representation).finish.dup; 57 return cast(string)hash; 58 } 59 60 bool verifySignature(ubyte[] key, string value, string sig){ 61 /+Verifies the given signature matches the expected 62 signature. 63 +/ 64 //return sig == getSignature(key, value) ; 65 return constantTimeCompare(sig, getSignature(key, value)); 66 } 67 } 68 69 class Signer(DigestMethod, AlgDigestMethod) { 70 71 this(string secretKey, 72 string salt = null, 73 char sep = '.', 74 string keyDerivation = "django-concat" 75 ){ 76 77 if(salt is null) 78 this.salt = "itsdangerous.Signer"; 79 else 80 this.salt = salt; 81 82 if(canFind(BASE64_ALPHABET, sep)) 83 throw new Exception("The given separator cannot be used because it may be\n 84 contained in the signature itself. Alphanumeric\n 85 characters and `-_=` must not be used."); 86 this.secretKey = secretKey; 87 this.sep = sep; 88 this.keyDerivation = keyDerivation; 89 90 digester = new WrapperDigest!DigestMethod(); 91 algorithm = new HMACAlgorithm!AlgDigestMethod(); 92 } 93 94 char sep; 95 96 private { 97 string secretKey; 98 SigningAlgorithm algorithm; 99 string salt; 100 string keyDerivation; 101 WrapperDigest!DigestMethod digester; 102 103 } 104 105 void setAlgorithm(SigningAlgorithm alg){ 106 this.algorithm = alg; 107 } 108 109 SigningAlgorithm getAlgorithm(){ 110 return this.algorithm; 111 } 112 113 final ubyte[] deriveKey(){ 114 /+This method is called to derive the key. The default key 115 derivation choices can be overridden here. Key derivation is not 116 intended to be used as a security method to make a complex key 117 out of a short password. Instead you should use large random 118 secret keys. 119 +/ 120 121 if(keyDerivation == "concat"){ 122 digester.put(salt.representation); 123 digester.put(secretKey.representation); 124 return digester.finish(); 125 } 126 else if (keyDerivation == "django-concat"){ 127 128 digester.put(salt.representation); 129 digester.put("signer".representation); 130 digester.put(secretKey.representation); 131 132 //digester.put(salt.representation ~ "signer".representation ~ secretKey.representation); 133 return digester.finish(); 134 } 135 else if (keyDerivation == "hmac") 136 return salt.representation.hmac!DigestMethod(secretKey.representation).dup; 137 else if (keyDerivation == "none") 138 return secretKey.representation.dup; 139 else 140 throw new Exception("Unknown key derivation method"); 141 142 } 143 144 final string getSignature(string value){ 145 /+Returns the signature for the given value.+/ 146 ubyte[] key = deriveKey(); 147 string sig = algorithm.getSignature(key, value); 148 return base64Encode(sig); 149 } 150 151 string sign(string value){ 152 /+Signs the given string.+/ 153 return value ~ sep ~ getSignature(value); 154 } 155 156 final bool verifySignature(string value, string sig){ 157 /+Verifies the signature for the given value.+/ 158 ubyte[] key = deriveKey(); 159 string decoded; 160 try{ 161 decoded = base64Decode(sig); 162 } catch (Exception exc) { 163 return false; 164 } 165 return algorithm.verifySignature(key, value, decoded); 166 } 167 168 string unsign(string signedValue, int maxAge = 0, int* tstamp = null){ 169 /+ Unsigns the given string. +/ 170 if(!canFind(signedValue, sep)) 171 throw new BadSignature(format("No %c found in value", sep)); 172 immutable arr = signedValue.rsplit(sep); 173 auto value = arr[0]; 174 auto sig = arr[1]; 175 if (verifySignature(value, sig)) 176 return value; 177 throw new BadSignature(format("Signature %s does not match", sig), value); 178 } 179 180 bool validate(string signedValue){ 181 /+Only validates the given signed value. Returns ``True`` if 182 the signature exists and is valid. 183 +/ 184 try{ 185 unsign(signedValue); 186 return true; 187 } catch (BadSignature ex) { 188 return false; 189 } 190 } 191 }