| LEFT | RIGHT | 
|    1 /* |    1 /* | 
|    2  * This file is part of Adblock Plus <https://adblockplus.org/>, |    2  * This file is part of Adblock Plus <https://adblockplus.org/>, | 
|    3  * Copyright (C) 2006-2016 Eyeo GmbH |    3  * Copyright (C) 2006-2016 Eyeo GmbH | 
|    4  * |    4  * | 
|    5  * Adblock Plus is free software: you can redistribute it and/or modify |    5  * Adblock Plus is free software: you can redistribute it and/or modify | 
|    6  * it under the terms of the GNU General Public License version 3 as |    6  * it under the terms of the GNU General Public License version 3 as | 
|    7  * published by the Free Software Foundation. |    7  * published by the Free Software Foundation. | 
|    8  * |    8  * | 
|    9  * Adblock Plus is distributed in the hope that it will be useful, |    9  * Adblock Plus is distributed in the hope that it will be useful, | 
|   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of |   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|   12  * GNU General Public License for more details. |   12  * GNU General Public License for more details. | 
|   13  * |   13  * | 
|   14  * You should have received a copy of the GNU General Public License |   14  * You should have received a copy of the GNU General Public License | 
|   15  * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. |   15  * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 
|   16  */ |   16  */ | 
|   17  |   17  | 
|   18 /* globals atob, console */ |   18 /* global console */ | 
|   19  |   19  | 
|   20 "use strict"; |   20 "use strict"; | 
|   21  |   21  | 
|   22 /** |   22 /** | 
|   23  * This is a specialized RSA library meant only to verify SHA1-based signatures. |   23  * This is a specialized RSA library meant only to verify SHA1-based signatures. | 
|   24  */ |   24  */ | 
|   25  |   25  | 
|   26 let {BigInteger} = require("jsbn"); |   26 const {BigInteger} = require("jsbn"); | 
|   27 let Rusha = require("rusha"); |   27 const Rusha = require("rusha"); | 
|   28  |   28  | 
|   29 let rusha = new Rusha(); |   29 let rusha = new Rusha(); | 
|   30  |   30  | 
|   31 // Define ASN.1 templates for the data structures used |   31 // Define ASN.1 templates for the data structures used | 
|   32 function seq(...args) |   32 function seq(...args) | 
|   33 { |   33 { | 
|   34   return {type: 0x30, children: args}; |   34   return {type: 0x30, children: args}; | 
|   35 } |   35 } | 
|   36 function obj(id) |   36 function obj(id) | 
|   37 { |   37 { | 
| (...skipping 24 matching lines...) Expand all  Loading... | 
|   62 let signatureTemplate = seq( |   62 let signatureTemplate = seq( | 
|   63   seq(obj("\x2B\x0E\x03\x02\x1A"), {}), |   63   seq(obj("\x2B\x0E\x03\x02\x1A"), {}), | 
|   64   octetResult("sha1") |   64   octetResult("sha1") | 
|   65 ); |   65 ); | 
|   66  |   66  | 
|   67 /** |   67 /** | 
|   68  * Reads ASN.1 data matching the template passed in. This will throw an |   68  * Reads ASN.1 data matching the template passed in. This will throw an | 
|   69  * exception if the data format doesn't match the template. On success an |   69  * exception if the data format doesn't match the template. On success an | 
|   70  * object containing result properties is returned. |   70  * object containing result properties is returned. | 
|   71  * @see http://luca.ntop.org/Teaching/Appunti/asn1.html for info on the format. |   71  * @see http://luca.ntop.org/Teaching/Appunti/asn1.html for info on the format. | 
|   72  * @param {String} data |   72  * @param {string} data | 
|   73  * @param {Object} templ |   73  * @param {Object} templ | 
|   74  * @returns {Object} |   74  * @returns {Object} | 
|   75  */ |   75  */ | 
|   76 function readASN1(data, templ) |   76 function readASN1(data, templ) | 
|   77 { |   77 { | 
|   78   let pos = 0; |   78   let pos = 0; | 
|   79   function next() |   79   function next() | 
|   80   { |   80   { | 
|   81     return data.charCodeAt(pos++); |   81     return data.charCodeAt(pos++); | 
|   82   } |   82   } | 
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  136   let out = {}; |  136   let out = {}; | 
|  137   readNode(templ); |  137   readNode(templ); | 
|  138   if (pos != data.length) |  138   if (pos != data.length) | 
|  139     throw "Too much data"; |  139     throw "Too much data"; | 
|  140   return out; |  140   return out; | 
|  141 } |  141 } | 
|  142  |  142  | 
|  143 /** |  143 /** | 
|  144  * Reads a BER-encoded RSA public key. On success returns an object with the |  144  * Reads a BER-encoded RSA public key. On success returns an object with the | 
|  145  * properties n and e (the components of the key), otherwise null. |  145  * properties n and e (the components of the key), otherwise null. | 
|  146  * @param {String} key |  146  * @param {string} key | 
|  147  * @return {Object|null} |  147  * @return {?Object} | 
|  148  */ |  148  */ | 
|  149 function readPublicKey(key) |  149 function readPublicKey(key) | 
|  150 { |  150 { | 
|  151   try |  151   try | 
|  152   { |  152   { | 
|  153     return readASN1(atob(key), publicKeyTemplate); |  153     return readASN1(atob(key), publicKeyTemplate); | 
|  154   } |  154   } | 
|  155   catch (e) |  155   catch (e) | 
|  156   { |  156   { | 
|  157     console.warn("Invalid RSA public key: " + e); |  157     console.warn("Invalid RSA public key: " + e); | 
|  158     return null; |  158     return null; | 
|  159   } |  159   } | 
|  160 } |  160 } | 
|  161  |  161  | 
|  162 /** |  162 /** | 
|  163  * Checks whether the signature is valid for the given public key and data. |  163  * Checks whether the signature is valid for the given public key and data. | 
|  164  * @param {String} key |  164  * @param {string} key | 
|  165  * @param {String} signature |  165  * @param {string} signature | 
|  166  * @param {String} data |  166  * @param {string} data | 
|  167  * @return {Boolean} |  167  * @return {boolean} | 
|  168  */ |  168  */ | 
|  169 function verifySignature(key, signature, data) |  169 function verifySignature(key, signature, data) | 
|  170 { |  170 { | 
|  171   let keyData = readPublicKey(key); |  171   let keyData = readPublicKey(key); | 
|  172   if (!keyData) |  172   if (!keyData) | 
|  173     return false; |  173     return false; | 
|  174  |  174  | 
|  175   // We need the exponent as regular number |  175   // We need the exponent as regular number | 
|  176   keyData.e = parseInt(keyData.e.toString(16), 16); |  176   keyData.e = parseInt(keyData.e.toString(16), 16); | 
|  177  |  177  | 
|  178   // Decrypt signature data using RSA algorithm |  178   // Decrypt signature data using RSA algorithm | 
|  179   let sigInt = new BigInteger(atob(signature), 256); |  179   let sigInt = new BigInteger(atob(signature), 256); | 
|  180   let digest = sigInt.modPowInt(keyData.e, keyData.n).toString(256); |  180   let digest = sigInt.modPowInt(keyData.e, keyData.n).toString(256); | 
|  181  |  181  | 
|  182   try |  182   try | 
|  183   { |  183   { | 
|  184     let pos = 0; |  184     let pos = 0; | 
|  185     let next = () => digest.charCodeAt(pos++); |  185     let next = () => digest.charCodeAt(pos++); | 
|  186  |  186  | 
|  187     // Skip padding, see http://tools.ietf.org/html/rfc3447#section-9.2 step 5 |  187     // Skip padding, see http://tools.ietf.org/html/rfc3447#section-9.2 step 5 | 
|  188     if (next() != 1) |  188     if (next() != 1) | 
|  189       throw "Wrong padding in signature digest"; |  189       throw "Wrong padding in signature digest"; | 
|  190     while (next() == 255) |  190     while (next() == 255) {} | 
|  191     { |  | 
|  192       // Empty |  | 
|  193     } |  | 
|  194     if (digest.charCodeAt(pos - 1) != 0) |  191     if (digest.charCodeAt(pos - 1) != 0) | 
|  195       throw "Wrong padding in signature digest"; |  192       throw "Wrong padding in signature digest"; | 
|  196  |  193  | 
|  197     // Rest is an ASN.1 structure, get the SHA1 hash from it and compare to |  194     // Rest is an ASN.1 structure, get the SHA1 hash from it and compare to | 
|  198     // the real one |  195     // the real one | 
|  199     let {sha1} = readASN1(digest.substr(pos), signatureTemplate); |  196     let {sha1} = readASN1(digest.substr(pos), signatureTemplate); | 
|  200     let expected = new BigInteger(rusha.digest(data), 16); |  197     let expected = new BigInteger(rusha.digest(data), 16); | 
|  201     return (sha1.compareTo(expected) == 0); |  198     return (sha1.compareTo(expected) == 0); | 
|  202   } |  199   } | 
|  203   catch (e) |  200   catch (e) | 
|  204   { |  201   { | 
|  205     console.warn("Invalid encrypted signature: " + e); |  202     console.warn("Invalid encrypted signature: " + e); | 
|  206     return false; |  203     return false; | 
|  207   } |  204   } | 
|  208 } |  205 } | 
|  209 exports.verifySignature = verifySignature; |  206 exports.verifySignature = verifySignature; | 
| LEFT | RIGHT |