PageRenderTime 52ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/src/main/java/com/caucho/quercus/lib/HashModule.java

https://github.com/moriyoshi/quercus-gae
Java | 537 lines | 354 code | 110 blank | 73 comment | 37 complexity | 3b1bad035415fcfa0da775801874ce61 MD5 | raw file
Possible License(s): GPL-2.0
  1. /*
  2. * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
  3. *
  4. * This file is part of Resin(R) Open Source
  5. *
  6. * Each copy or derived work must preserve the copyright notice and this
  7. * notice unmodified.
  8. *
  9. * Resin Open Source is free software; you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation; either version 2 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * Resin Open Source is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
  17. * of NON-INFRINGEMENT. See the GNU General Public License for more
  18. * details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with Resin Open Source; if not, write to the
  22. *
  23. * Free Software Foundation, Inc.
  24. * 59 Temple Place, Suite 330
  25. * Boston, MA 02111-1307 USA
  26. *
  27. * @author Scott Ferguson
  28. */
  29. package com.caucho.quercus.lib;
  30. import com.caucho.quercus.annotation.Optional;
  31. import com.caucho.quercus.env.*;
  32. import com.caucho.quercus.module.*;
  33. import com.caucho.util.*;
  34. import com.caucho.vfs.*;
  35. import java.io.*;
  36. import java.security.*;
  37. import java.util.*;
  38. import java.util.logging.Level;
  39. import java.util.logging.Logger;
  40. import javax.crypto.*;
  41. import javax.crypto.spec.*;
  42. /**
  43. * Hash functions.
  44. *
  45. * This module uses the {@link MessageDigest} class to calculate
  46. * digests. Typical java installations support MD2, MD5, SHA1, SHA256, SHA384,
  47. * and SHA512.
  48. */
  49. public class HashModule extends AbstractQuercusModule {
  50. private static final L10N L = new L10N(HashModule.class);
  51. private static final Logger log
  52. = Logger.getLogger(HashModule.class.getName());
  53. public static final int HASH_HMAC = 1;
  54. private static HashMap<String,String> _algorithmMap
  55. = new HashMap<String,String>();
  56. public HashModule()
  57. {
  58. }
  59. public String []getLoadedExtensions()
  60. {
  61. return new String[] { "hash" };
  62. }
  63. /**
  64. * Hashes a string
  65. */
  66. public Value hash(Env env,
  67. String algorithm,
  68. StringValue string,
  69. @Optional boolean isBinary)
  70. {
  71. try {
  72. MessageDigest digest = MessageDigest.getInstance(algorithm);
  73. int len = string.length();
  74. for (int i = 0; i < len; i++) {
  75. digest.update((byte) string.charAt(i));
  76. }
  77. byte []bytes = digest.digest();
  78. return hashToValue(env, bytes, isBinary);
  79. } catch (NoSuchAlgorithmException e) {
  80. env.error(L.l("'{0}' is an unknown algorithm", algorithm), e);
  81. return BooleanValue.FALSE;
  82. }
  83. }
  84. /**
  85. * Returns the list of known algorithms
  86. */
  87. public static Value hash_algos(Env env)
  88. {
  89. ArrayValue array = new ArrayValueImpl();
  90. for (String name : _algorithmMap.keySet()) {
  91. array.put(env.createStringOld(name));
  92. }
  93. Collection<String> values = _algorithmMap.values();
  94. for (String name : Security.getAlgorithms("MessageDigest")) {
  95. if (! values.contains(name))
  96. array.put(env.createStringOld(name));
  97. }
  98. return array;
  99. }
  100. /**
  101. * Copies a hash instance
  102. */
  103. public HashContext hash_copy(HashContext context)
  104. {
  105. if (context != null)
  106. return context.copy();
  107. else
  108. return null;
  109. }
  110. /**
  111. * Hashes a file
  112. */
  113. public Value hash_file(Env env,
  114. String algorithm,
  115. Path path,
  116. @Optional boolean isBinary)
  117. {
  118. try {
  119. MessageDigest digest = MessageDigest.getInstance(algorithm);
  120. TempBuffer tempBuffer = TempBuffer.allocate();
  121. byte []buffer = tempBuffer.getBuffer();
  122. ReadStream is = path.openRead();
  123. try {
  124. int len;
  125. while ((len = is.read(buffer, 0, buffer.length)) > 0) {
  126. digest.update(buffer, 0, len);
  127. }
  128. byte []bytes = digest.digest();
  129. return hashToValue(env, bytes, isBinary);
  130. } finally {
  131. TempBuffer.free(tempBuffer);
  132. is.close();
  133. }
  134. } catch (NoSuchAlgorithmException e) {
  135. env.error(L.l("'{0}' is an unknown algorithm", algorithm), e);
  136. return BooleanValue.FALSE;
  137. } catch (IOException e) {
  138. env.error(L.l("'{0}' is an unknown file", path), e);
  139. return BooleanValue.FALSE;
  140. }
  141. }
  142. /**
  143. * Returns the final hash value
  144. */
  145. public Value hash_final(Env env,
  146. HashContext context,
  147. @Optional boolean isBinary)
  148. {
  149. if (context == null)
  150. return BooleanValue.FALSE;
  151. return hashToValue(env, context.digest(), isBinary);
  152. }
  153. /**
  154. * Hashes a string with the algorithm.
  155. */
  156. public Value hash_hmac(Env env,
  157. String algorithm,
  158. StringValue data,
  159. StringValue key,
  160. @Optional boolean isBinary)
  161. {
  162. HashContext context = hash_init(env, algorithm, HASH_HMAC, key);
  163. hash_update(env, context, data);
  164. return hash_final(env, context, isBinary);
  165. }
  166. /**
  167. * Hashes a file with the algorithm.
  168. */
  169. public Value hash_hmac_file(Env env,
  170. String algorithm,
  171. Path path,
  172. StringValue key,
  173. @Optional boolean isBinary)
  174. {
  175. HashContext context = hash_init(env, algorithm, HASH_HMAC, key);
  176. hash_update_file(env, context, path);
  177. return hash_final(env, context, isBinary);
  178. }
  179. /**
  180. * Initialize a hash context.
  181. */
  182. public HashContext hash_init(Env env,
  183. String algorithm,
  184. @Optional int options,
  185. @Optional StringValue keyString)
  186. {
  187. try {
  188. if (options == HASH_HMAC) {
  189. algorithm = "Hmac" + algorithm;
  190. Mac mac = Mac.getInstance(algorithm);
  191. int keySize = 64;
  192. // php/530c
  193. if (keyString != null)
  194. keySize = keyString.length();
  195. byte []keyBytes = new byte[keySize];
  196. for (int i = 0; i < keyString.length(); i++) {
  197. keyBytes[i] = (byte) keyString.charAt(i);
  198. }
  199. Key key = new SecretKeySpec(keyBytes, "dsa");
  200. mac.init(key);
  201. return new HashMacContext(mac);
  202. }
  203. else {
  204. MessageDigest md = MessageDigest.getInstance(algorithm);
  205. return new HashDigestContext(md);
  206. }
  207. } catch (Exception e) {
  208. env.error(L.l("hash_init: '{0}' is an unknown algorithm",
  209. algorithm));
  210. return null;
  211. }
  212. }
  213. /**
  214. * Updates the hash with more data
  215. */
  216. public Value hash_update(Env env,
  217. HashContext context,
  218. StringValue value)
  219. {
  220. if (context == null)
  221. return BooleanValue.FALSE;
  222. context.update(value);
  223. return BooleanValue.TRUE;
  224. }
  225. /**
  226. * Updates the hash with more data
  227. */
  228. public Value hash_update_file(Env env,
  229. HashContext context,
  230. Path path)
  231. {
  232. if (context == null)
  233. return BooleanValue.FALSE;
  234. TempBuffer tempBuffer = TempBuffer.allocate();
  235. byte []buffer = tempBuffer.getBuffer();
  236. ReadStream is = null;
  237. try {
  238. is = path.openRead();
  239. int len;
  240. while ((len = is.read(buffer, 0, buffer.length)) > 0) {
  241. context.update(buffer, 0, len);
  242. }
  243. } catch (IOException e) {
  244. log.log(Level.WARNING, e.toString(), e);
  245. } finally {
  246. TempBuffer.free(tempBuffer);
  247. if (is != null)
  248. is.close();
  249. }
  250. return BooleanValue.TRUE;
  251. }
  252. /**
  253. * Updates the hash with more data
  254. */
  255. public int hash_update_stream(Env env,
  256. HashContext context,
  257. InputStream is,
  258. @Optional("-1") int length)
  259. {
  260. if (context == null)
  261. return -1;
  262. if (length < 0)
  263. length = Integer.MAX_VALUE - 1;
  264. TempBuffer tempBuffer = TempBuffer.allocate();
  265. byte []buffer = tempBuffer.getBuffer();
  266. int readLength = 0;
  267. try {
  268. while (length > 0) {
  269. int sublen = buffer.length;
  270. if (length < sublen)
  271. sublen = length;
  272. int len = is.read(buffer, 0, sublen);
  273. if (len < 0)
  274. return readLength;
  275. context.update(buffer, 0, len);
  276. readLength += len;
  277. length -= len;
  278. }
  279. } catch (IOException e) {
  280. log.log(Level.WARNING, e.toString(), e);
  281. } finally {
  282. TempBuffer.free(tempBuffer);
  283. }
  284. return readLength;
  285. }
  286. // XXX: hash_update_file
  287. // XXX: hash_update_stream
  288. // XXX: hash_update
  289. // XXX: hash
  290. private static Value hashToValue(Env env, byte []bytes, boolean isBinary)
  291. {
  292. if (isBinary) {
  293. StringValue v = env.createBinaryBuilder();
  294. v.append(bytes, 0, bytes.length);
  295. return v;
  296. }
  297. else {
  298. StringValue v = env.createUnicodeBuilder();
  299. for (int i = 0; i < bytes.length; i++) {
  300. int ch = bytes[i];
  301. int d1 = (ch >> 4) & 0xf;
  302. int d2 = (ch) & 0xf;
  303. if (d1 < 10)
  304. v.append((char) ('0' + d1));
  305. else
  306. v.append((char) ('a' + d1 - 10));
  307. if (d2 < 10)
  308. v.append((char) ('0' + d2));
  309. else
  310. v.append((char) ('a' + d2 - 10));
  311. }
  312. return v;
  313. }
  314. }
  315. public abstract static class HashContext
  316. {
  317. abstract void update(StringValue value);
  318. abstract void update(byte []buffer, int offset, int length);
  319. abstract byte []digest();
  320. abstract HashContext copy();
  321. }
  322. public static class HashDigestContext extends HashContext
  323. {
  324. private MessageDigest _digest;
  325. HashDigestContext(MessageDigest digest)
  326. {
  327. _digest = digest;
  328. }
  329. MessageDigest getDigest()
  330. {
  331. return _digest;
  332. }
  333. void update(byte value)
  334. {
  335. _digest.update(value);
  336. }
  337. void update(StringValue value)
  338. {
  339. int len = value.length();
  340. MessageDigest digest = _digest;
  341. for (int i = 0; i < len; i++) {
  342. digest.update((byte) value.charAt(i));
  343. }
  344. }
  345. void update(byte []buffer, int offset, int length)
  346. {
  347. _digest.update(buffer, offset, length);
  348. }
  349. byte []digest()
  350. {
  351. return _digest.digest();
  352. }
  353. HashContext copy()
  354. {
  355. try {
  356. return new HashDigestContext((MessageDigest) _digest.clone());
  357. } catch (Exception e) {
  358. log.log(Level.FINE, e.toString(), e);
  359. return null;
  360. }
  361. }
  362. public String toString()
  363. {
  364. return (getClass().getSimpleName() + "[" + _digest + "]");
  365. }
  366. }
  367. public static class HashMacContext extends HashContext
  368. {
  369. private Mac _digest;
  370. HashMacContext(Mac digest)
  371. {
  372. _digest = digest;
  373. }
  374. void update(byte value)
  375. {
  376. _digest.update(value);
  377. }
  378. void update(StringValue value)
  379. {
  380. int len = value.length();
  381. Mac digest = _digest;
  382. TempBuffer tBuf = TempBuffer.allocate();
  383. byte []buffer = tBuf.getBuffer();
  384. int offset = 0;
  385. while (offset < len) {
  386. int sublen = len - offset;
  387. if (buffer.length < sublen)
  388. sublen = buffer.length;
  389. for (int i = 0; i < sublen; i++) {
  390. buffer[i] = (byte) value.charAt(offset + i);
  391. }
  392. digest.update(buffer, 0, sublen);
  393. offset += sublen;
  394. }
  395. TempBuffer.free(tBuf);
  396. }
  397. void update(byte []buffer, int offset, int length)
  398. {
  399. _digest.update(buffer, offset, length);
  400. }
  401. byte []digest()
  402. {
  403. return _digest.doFinal();
  404. }
  405. HashContext copy()
  406. {
  407. try {
  408. return new HashDigestContext((MessageDigest) _digest.clone());
  409. } catch (Exception e) {
  410. log.log(Level.FINE, e.toString(), e);
  411. return null;
  412. }
  413. }
  414. public String toString()
  415. {
  416. return (getClass().getSimpleName() + "[" + _digest + "]");
  417. }
  418. }
  419. static {
  420. _algorithmMap.put("md2", "MD2");
  421. _algorithmMap.put("md5", "MD5");
  422. _algorithmMap.put("sha1", "SHA");
  423. _algorithmMap.put("sha256", "SHA-256");
  424. _algorithmMap.put("sha384", "SHA-384");
  425. _algorithmMap.put("sha512", "SHA-512");
  426. }
  427. }