1 /** 2 * SCAN Name Abstraction 3 * 4 * Copyright: 5 * (C) 2008-2009 Jack Lloyd 6 * (C) 2014-2015 Etienne Cimon 7 * 8 * License: 9 * Botan is released under the Simplified BSD License (see LICENSE.md) 10 */ 11 module botan.algo_base.scan_token; 12 13 import botan.utils.parsing; 14 import botan.utils.exceptn; 15 import std.exception; 16 import std.array : Appender; 17 import botan.utils.types; 18 import std.conv : to; 19 import core.sync.mutex; 20 import memutils.hashmap; 21 import botan.constants; 22 23 private HashMap!(string, SCANToken) m_cache; 24 25 /** 26 * A class encapsulating a SCAN name (similar to JCE conventions) 27 * http://www.users.zetnet.co.uk/hopwood/crypto/scan/ 28 */ 29 struct SCANToken 30 { 31 public: 32 /** 33 * Params: 34 * algo_spec = A SCAN-format name 35 */ 36 this(string algo_spec) 37 { 38 if (auto tok = algo_spec in m_cache) { 39 m_args = tok.m_args; 40 m_mode_info = tok.m_mode_info; 41 m_alg_name = tok.m_alg_name; 42 m_orig_algo_spec = tok.m_orig_algo_spec; 43 return; 44 } 45 scope(exit) m_cache[algo_spec] = this; 46 m_args = Array!string(); 47 m_mode_info = Array!string(); 48 m_orig_algo_spec = algo_spec; 49 50 Vector!( Pair!(size_t, string) ) names; 51 size_t level = 0; 52 Array!ubyte def_buf; 53 def_buf.reserve(8); 54 auto accum = makePair(level, def_buf); 55 56 string decoding_error = "Bad SCAN name '" ~ algo_spec ~ "': "; 57 58 algo_spec = derefAlias(algo_spec); 59 60 foreach (immutable(char) c; algo_spec) 61 { 62 63 if (c == '/' || c == ',' || c == '(' || c == ')') 64 { 65 if (c == '(') 66 ++level; 67 else if (c == ')') 68 { 69 if (level == 0) 70 throw new DecodingError(decoding_error ~ "Mismatched parens"); 71 --level; 72 } 73 74 if (c == '/' && level > 0) { 75 accum.second ~= c; 76 77 } 78 else 79 { 80 if (accum.second.length > 0) { 81 names ~= makePair(accum.first, derefAlias(cast(string)accum.second[])); 82 } 83 Array!ubyte str; 84 str.reserve(8); 85 accum = makePair(level, str); 86 } 87 } 88 else { 89 accum.second ~= c; 90 } 91 } 92 93 if (accum.second.length > 0) { 94 names ~= makePair(accum.first, derefAlias(cast(string)accum.second[])); 95 } 96 if (level != 0) 97 throw new DecodingError(decoding_error ~ "Missing close paren"); 98 99 if (names.length == 0) 100 throw new DecodingError(decoding_error ~ "Empty name"); 101 102 m_alg_name = names[0].second[].idup; 103 104 if (names.length == 1) 105 return; 106 107 bool in_modes; 108 size_t i = 1; 109 foreach (name; names[1 .. $]) 110 { 111 if (name.first == 0) 112 { 113 string val = makeArg(names, i); 114 m_mode_info.pushBack(val); 115 in_modes = true; 116 } 117 else if (name.first == 1 && !in_modes) { 118 string val = makeArg(names, i); 119 m_args.pushBack(val); 120 } 121 i++; 122 } 123 } 124 125 /** 126 * Returns: original input string 127 */ 128 string toString() const { return m_orig_algo_spec; } 129 130 /** 131 * Returns: algorithm name 132 */ 133 @property string algoName() const { return m_alg_name; } 134 135 /** 136 * Returns: algorithm name plus any arguments 137 */ 138 string algoNameAndArgs() const 139 { 140 Appender!string output; 141 142 output ~= algoName; 143 144 if (argCount()) 145 { 146 output ~= '('; 147 foreach (size_t i; 0 .. argCount()) 148 { 149 output ~= arg(i); 150 if (i != argCount() - 1) 151 output ~= ','; 152 } 153 output ~= ')'; 154 155 } 156 157 return output.data; 158 } 159 160 /** 161 * Returns: number of arguments 162 */ 163 size_t argCount() const { return m_args.length; } 164 165 /** 166 * Params: 167 * lower = is the lower bound 168 * upper = is the upper bound 169 * 170 * Returns: true if the number of arguments is between lower and upper 171 */ 172 bool argCountBetween(size_t lower, size_t upper) const 173 { return ((argCount() >= lower) && (argCount() <= upper)); } 174 175 /** 176 * Params: 177 * i = which argument 178 * 179 * Returns: ith argument 180 */ 181 string arg(size_t i) const 182 { 183 if (i >= argCount()) 184 throw new RangeError("SCANToken.argument - i out of range"); 185 return m_args[i]; 186 } 187 188 /** 189 * Params: 190 * i = which argument 191 * def_value = the default value 192 * 193 * Returns: ith argument or the default value 194 */ 195 string arg(size_t i, in string def_value) const 196 { 197 if (i >= argCount()) 198 return def_value; 199 return m_args[i]; 200 } 201 202 /** 203 * Params: 204 * i = which argument 205 * def_value = the default value 206 * 207 * Returns: ith argument as an integer, or the default value 208 */ 209 size_t argAsInteger(size_t i, size_t def_value) const 210 { 211 if (i >= argCount()) 212 return def_value; 213 return to!uint(m_args[i]); 214 } 215 216 /** 217 * Returns: cipher mode (if any) 218 */ 219 string cipherMode() const 220 { return (m_mode_info.length >= 1) ? m_mode_info[0] : ""; } 221 222 /** 223 * Returns: cipher mode padding (if any) 224 */ 225 string cipherModePad() const 226 { return (m_mode_info.length >= 2) ? m_mode_info[1] : ""; } 227 228 static void addAlias(string _alias, in string basename) 229 { 230 if (!s_alias_map.get(_alias, null)) 231 s_alias_map[_alias] = basename; 232 } 233 234 235 static string derefAlias(string input, bool keep = false) 236 { 237 auto name = s_alias_map.get(input, null); 238 if (name) 239 return name; 240 else if (keep) 241 return input; 242 else 243 return input.idup; 244 } 245 246 static void setDefaultAliases() 247 { 248 // common variations worth supporting 249 addAlias("EME-PKCS1-v1_5", "PKCS1v15"); 250 addAlias("3DES", "TripleDES"); 251 addAlias("DES-EDE", "TripleDES"); 252 addAlias("CAST5", "CAST-128"); 253 addAlias("SHA1", "SHA-160"); 254 addAlias("SHA-1", "SHA-160"); 255 addAlias("MARK-4", "RC4(256)"); 256 addAlias("ARC4", "RC4"); 257 addAlias("OMAC", "CMAC"); 258 259 addAlias("EMSA-PSS", "PSSR"); 260 addAlias("PSS-MGF1", "PSSR"); 261 addAlias("EME-OAEP", "OAEP"); 262 263 addAlias("EMSA2", "EMSA_X931"); 264 addAlias("EMSA3", "EMSA_PKCS1"); 265 addAlias("EMSA-PKCS1-v1_5", "EMSA_PKCS1"); 266 267 // should be renamed in sources 268 addAlias("X9.31", "EMSA2"); 269 270 // kept for compatability with old library versions 271 addAlias("EMSA4", "PSSR"); 272 addAlias("EME1", "OAEP"); 273 274 // probably can be removed 275 addAlias("GOST", "GOST-28147-89"); 276 addAlias("GOST-34.11", "GOST-R-34.11-94"); 277 } 278 279 280 private: 281 static HashMapRef!(string, string) s_alias_map; 282 283 string m_orig_algo_spec; 284 string m_alg_name; 285 Array!string m_args; 286 Array!string m_mode_info; 287 } 288 289 private: 290 291 string makeArg(ref Vector!(Pair!(size_t, string)) names, size_t start) 292 { 293 Appender!string output; 294 output ~= names[start].second; 295 size_t level = names[start].first; 296 297 size_t paren_depth = 0; 298 299 foreach (name; names[(start + 1) .. $]) 300 { 301 if (name.first <= names[start].first) 302 break; 303 304 if (name.first > level) 305 { 306 output ~= '(' ~ name.second; 307 ++paren_depth; 308 } 309 else if (name.first < level) 310 { 311 output ~= ")," ~ name.second; 312 --paren_depth; 313 } 314 else 315 { 316 if (output.data[output.data.length - 1] != '(') 317 output ~= ","; 318 output ~= name.second; 319 } 320 321 level = name.first; 322 } 323 324 foreach (size_t i; 0 .. paren_depth) 325 output ~= ')'; 326 327 return output.data; 328 }