PageRenderTime 47ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/chrome/content/lib/forge/js/pem.js

http://github.com/stesie/geierlein
JavaScript | 285 lines | 193 code | 26 blank | 66 comment | 55 complexity | 5e902d8a6821d58fd41e8bccab6ef95e MD5 | raw file
Possible License(s): AGPL-3.0, GPL-2.0
  1. /**
  2. * Javascript implementation of basic PEM (Privacy Enhanced Mail) algorithms.
  3. *
  4. * See: RFC 1421.
  5. *
  6. * @author Dave Longley
  7. *
  8. * Copyright (c) 2013-2014 Digital Bazaar, Inc.
  9. *
  10. * A Forge PEM object has the following fields:
  11. *
  12. * type: identifies the type of message (eg: "RSA PRIVATE KEY").
  13. *
  14. * procType: identifies the type of processing performed on the message,
  15. * it has two subfields: version and type, eg: 4,ENCRYPTED.
  16. *
  17. * contentDomain: identifies the type of content in the message, typically
  18. * only uses the value: "RFC822".
  19. *
  20. * dekInfo: identifies the message encryption algorithm and mode and includes
  21. * any parameters for the algorithm, it has two subfields: algorithm and
  22. * parameters, eg: DES-CBC,F8143EDE5960C597.
  23. *
  24. * headers: contains all other PEM encapsulated headers -- where order is
  25. * significant (for pairing data like recipient ID + key info).
  26. *
  27. * body: the binary-encoded body.
  28. */
  29. (function() {
  30. /* ########## Begin module implementation ########## */
  31. function initModule(forge) {
  32. // shortcut for pem API
  33. var pem = forge.pem = forge.pem || {};
  34. /**
  35. * Encodes (serializes) the given PEM object.
  36. *
  37. * @param msg the PEM message object to encode.
  38. * @param options the options to use:
  39. * maxline the maximum characters per line for the body, (default: 64).
  40. *
  41. * @return the PEM-formatted string.
  42. */
  43. pem.encode = function(msg, options) {
  44. options = options || {};
  45. var rval = '-----BEGIN ' + msg.type + '-----\r\n';
  46. // encode special headers
  47. var header;
  48. if(msg.procType) {
  49. header = {
  50. name: 'Proc-Type',
  51. values: [String(msg.procType.version), msg.procType.type]
  52. };
  53. rval += foldHeader(header);
  54. }
  55. if(msg.contentDomain) {
  56. header = {name: 'Content-Domain', values: [msg.contentDomain]};
  57. rval += foldHeader(header);
  58. }
  59. if(msg.dekInfo) {
  60. header = {name: 'DEK-Info', values: [msg.dekInfo.algorithm]};
  61. if(msg.dekInfo.parameters) {
  62. header.values.push(msg.dekInfo.parameters);
  63. }
  64. rval += foldHeader(header);
  65. }
  66. if(msg.headers) {
  67. // encode all other headers
  68. for(var i = 0; i < msg.headers.length; ++i) {
  69. rval += foldHeader(msg.headers[i]);
  70. }
  71. }
  72. // terminate header
  73. if(msg.procType) {
  74. rval += '\r\n';
  75. }
  76. // add body
  77. rval += forge.util.encode64(msg.body, options.maxline || 64) + '\r\n';
  78. rval += '-----END ' + msg.type + '-----\r\n';
  79. return rval;
  80. };
  81. /**
  82. * Decodes (deserializes) all PEM messages found in the given string.
  83. *
  84. * @param str the PEM-formatted string to decode.
  85. *
  86. * @return the PEM message objects in an array.
  87. */
  88. pem.decode = function(str) {
  89. var rval = [];
  90. // split string into PEM messages (be lenient w/EOF on BEGIN line)
  91. var rMessage = /\s*-----BEGIN ([A-Z0-9- ]+)-----\r?\n?([\x21-\x7e\s]+?(?:\r?\n\r?\n))?([:A-Za-z0-9+\/=\s]+?)-----END \1-----/g;
  92. var rHeader = /([\x21-\x7e]+):\s*([\x21-\x7e\s^:]+)/;
  93. var rCRLF = /\r?\n/;
  94. var match;
  95. while(true) {
  96. match = rMessage.exec(str);
  97. if(!match) {
  98. break;
  99. }
  100. var msg = {
  101. type: match[1],
  102. procType: null,
  103. contentDomain: null,
  104. dekInfo: null,
  105. headers: [],
  106. body: forge.util.decode64(match[3])
  107. };
  108. rval.push(msg);
  109. // no headers
  110. if(!match[2]) {
  111. continue;
  112. }
  113. // parse headers
  114. var lines = match[2].split(rCRLF);
  115. var li = 0;
  116. while(match && li < lines.length) {
  117. // get line, trim any rhs whitespace
  118. var line = lines[li].replace(/\s+$/, '');
  119. // RFC2822 unfold any following folded lines
  120. for(var nl = li + 1; nl < lines.length; ++nl) {
  121. var next = lines[nl];
  122. if(!/\s/.test(next[0])) {
  123. break;
  124. }
  125. line += next;
  126. li = nl;
  127. }
  128. // parse header
  129. match = line.match(rHeader);
  130. if(match) {
  131. var header = {name: match[1], values: []};
  132. var values = match[2].split(',');
  133. for(var vi = 0; vi < values.length; ++vi) {
  134. header.values.push(ltrim(values[vi]));
  135. }
  136. // Proc-Type must be the first header
  137. if(!msg.procType) {
  138. if(header.name !== 'Proc-Type') {
  139. throw new Error('Invalid PEM formatted message. The first ' +
  140. 'encapsulated header must be "Proc-Type".');
  141. } else if(header.values.length !== 2) {
  142. throw new Error('Invalid PEM formatted message. The "Proc-Type" ' +
  143. 'header must have two subfields.');
  144. }
  145. msg.procType = {version: values[0], type: values[1]};
  146. } else if(!msg.contentDomain && header.name === 'Content-Domain') {
  147. // special-case Content-Domain
  148. msg.contentDomain = values[0] || '';
  149. } else if(!msg.dekInfo && header.name === 'DEK-Info') {
  150. // special-case DEK-Info
  151. if(header.values.length === 0) {
  152. throw new Error('Invalid PEM formatted message. The "DEK-Info" ' +
  153. 'header must have at least one subfield.');
  154. }
  155. msg.dekInfo = {algorithm: values[0], parameters: values[1] || null};
  156. } else {
  157. msg.headers.push(header);
  158. }
  159. }
  160. ++li;
  161. }
  162. if(msg.procType === 'ENCRYPTED' && !msg.dekInfo) {
  163. throw new Error('Invalid PEM formatted message. The "DEK-Info" ' +
  164. 'header must be present if "Proc-Type" is "ENCRYPTED".');
  165. }
  166. }
  167. if(rval.length === 0) {
  168. throw new Error('Invalid PEM formatted message.');
  169. }
  170. return rval;
  171. };
  172. function foldHeader(header) {
  173. var rval = header.name + ': ';
  174. // ensure values with CRLF are folded
  175. var values = [];
  176. var insertSpace = function(match, $1) {
  177. return ' ' + $1;
  178. };
  179. for(var i = 0; i < header.values.length; ++i) {
  180. values.push(header.values[i].replace(/^(\S+\r\n)/, insertSpace));
  181. }
  182. rval += values.join(',') + '\r\n';
  183. // do folding
  184. var length = 0;
  185. var candidate = -1;
  186. for(var i = 0; i < rval.length; ++i, ++length) {
  187. if(length > 65 && candidate !== -1) {
  188. var insert = rval[candidate];
  189. if(insert === ',') {
  190. ++candidate;
  191. rval = rval.substr(0, candidate) + '\r\n ' + rval.substr(candidate);
  192. } else {
  193. rval = rval.substr(0, candidate) +
  194. '\r\n' + insert + rval.substr(candidate + 1);
  195. }
  196. length = (i - candidate - 1);
  197. candidate = -1;
  198. ++i;
  199. } else if(rval[i] === ' ' || rval[i] === '\t' || rval[i] === ',') {
  200. candidate = i;
  201. }
  202. }
  203. return rval;
  204. }
  205. function ltrim(str) {
  206. return str.replace(/^\s+/, '');
  207. }
  208. } // end module implementation
  209. /* ########## Begin module wrapper ########## */
  210. var name = 'pem';
  211. if(typeof define !== 'function') {
  212. // NodeJS -> AMD
  213. if(typeof module === 'object' && module.exports) {
  214. var nodeJS = true;
  215. define = function(ids, factory) {
  216. factory(require, module);
  217. };
  218. } else {
  219. // <script>
  220. if(typeof forge === 'undefined') {
  221. forge = {};
  222. }
  223. return initModule(forge);
  224. }
  225. }
  226. // AMD
  227. var deps;
  228. var defineFunc = function(require, module) {
  229. module.exports = function(forge) {
  230. var mods = deps.map(function(dep) {
  231. return require(dep);
  232. }).concat(initModule);
  233. // handle circular dependencies
  234. forge = forge || {};
  235. forge.defined = forge.defined || {};
  236. if(forge.defined[name]) {
  237. return forge[name];
  238. }
  239. forge.defined[name] = true;
  240. for(var i = 0; i < mods.length; ++i) {
  241. mods[i](forge);
  242. }
  243. return forge[name];
  244. };
  245. };
  246. var tmpDefine = define;
  247. define = function(ids, factory) {
  248. deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
  249. if(nodeJS) {
  250. delete define;
  251. return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
  252. }
  253. define = tmpDefine;
  254. return define.apply(null, Array.prototype.slice.call(arguments, 0));
  255. };
  256. define(['require', 'module', './util'], function() {
  257. defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
  258. });
  259. })();