OP-01-026 Errors in EMSA-PKCS1-v1_5 decoding routine (High) and OP-01-018 Suggested improvement in RSA signature verification (Low). Do RSA signature verification as described in RFC 3447 Section 8.2.2. Remove pkcs1.emsa.decode(). Rewrite pkcs1.emsa.encode(). Hash algorithms: throw exception on error condition.
This commit is contained in:
parent
28e7a80eba
commit
357d49f7e9
|
@ -85,7 +85,7 @@ function mixOneRound(a, b, c, d, e, x, s, roundNumber) {
|
|||
break;
|
||||
|
||||
default:
|
||||
document.write("Bogus round number");
|
||||
throw new Error("Bogus round number");
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ var jsSHA = (function() {
|
|||
if (!isNaN(num)) {
|
||||
bin[i >> 3] |= num << (24 - (4 * (i % 8)));
|
||||
} else {
|
||||
return "INVALID HEX STRING";
|
||||
throw new Error("INVALID HEX STRING");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -870,7 +870,7 @@ var jsSHA = (function() {
|
|||
H[7].highOrder, H[7].lowOrder];
|
||||
default:
|
||||
/* This should never be reached */
|
||||
return [];
|
||||
throw new Error('Unknown SHA variant');
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -896,7 +896,7 @@ var jsSHA = (function() {
|
|||
/* Convert the input string into the correct type */
|
||||
if ("HEX" === inputFormat) {
|
||||
if (0 !== (srcString.length % 2)) {
|
||||
return "TEXT MUST BE IN BYTE INCREMENTS";
|
||||
throw new Error("TEXT MUST BE IN BYTE INCREMENTS");
|
||||
}
|
||||
this.strBinLen = srcString.length * 4;
|
||||
this.strToHash = hex2binb(srcString);
|
||||
|
@ -905,7 +905,7 @@ var jsSHA = (function() {
|
|||
this.strBinLen = srcString.length * charSize;
|
||||
this.strToHash = str2binb(srcString);
|
||||
} else {
|
||||
return "UNKNOWN TEXT INPUT TYPE";
|
||||
throw new Error("UNKNOWN TEXT INPUT TYPE");
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -934,7 +934,7 @@ var jsSHA = (function() {
|
|||
formatFunc = binb2str;
|
||||
break;
|
||||
default:
|
||||
return "FORMAT NOT RECOGNIZED";
|
||||
throw new Error("FORMAT NOT RECOGNIZED");
|
||||
}
|
||||
|
||||
switch (variant) {
|
||||
|
@ -964,7 +964,7 @@ var jsSHA = (function() {
|
|||
}
|
||||
return formatFunc(this.sha512);
|
||||
default:
|
||||
return "HASH NOT RECOGNIZED";
|
||||
throw new Error("HASH NOT RECOGNIZED");
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -998,7 +998,7 @@ var jsSHA = (function() {
|
|||
formatFunc = binb2str;
|
||||
break;
|
||||
default:
|
||||
return "FORMAT NOT RECOGNIZED";
|
||||
throw new Error("FORMAT NOT RECOGNIZED");
|
||||
}
|
||||
|
||||
/* Validate the hash variant selection and set needed variables */
|
||||
|
@ -1024,14 +1024,14 @@ var jsSHA = (function() {
|
|||
hashBitSize = 512;
|
||||
break;
|
||||
default:
|
||||
return "HASH NOT RECOGNIZED";
|
||||
throw new Error("HASH NOT RECOGNIZED");
|
||||
}
|
||||
|
||||
/* Validate input format selection */
|
||||
if ("HEX" === inputFormat) {
|
||||
/* Nibbles must come in pairs */
|
||||
if (0 !== (key.length % 2)) {
|
||||
return "KEY MUST BE IN BYTE INCREMENTS";
|
||||
throw new Error("KEY MUST BE IN BYTE INCREMENTS");
|
||||
}
|
||||
keyToUse = hex2binb(key);
|
||||
keyBinLen = key.length * 4;
|
||||
|
@ -1039,7 +1039,7 @@ var jsSHA = (function() {
|
|||
keyToUse = str2binb(key);
|
||||
keyBinLen = key.length * charSize;
|
||||
} else {
|
||||
return "UNKNOWN KEY INPUT TYPE";
|
||||
throw new Error("UNKNOWN KEY INPUT TYPE");
|
||||
}
|
||||
|
||||
/* These are used multiple times, calculate and store them */
|
||||
|
|
|
@ -91,7 +91,6 @@ module.exports = {
|
|||
result += message;
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* decodes a EME-PKCS1-v1_5 padding (See {@link http://tools.ietf.org/html/rfc4880#section-13.1.2|RFC 4880 13.1.2})
|
||||
* @param {String} message EME-PKCS1 padded message
|
||||
|
@ -110,53 +109,47 @@ module.exports = {
|
|||
},
|
||||
|
||||
emsa: {
|
||||
|
||||
/**
|
||||
* create a EMSA-PKCS1-v1_5 padding (See {@link http://tools.ietf.org/html/rfc4880#section-13.1.3|RFC 4880 13.1.3})
|
||||
* @param {Integer} algo Hash algorithm type used
|
||||
* @param {String} data Data to be hashed
|
||||
* @param {Integer} keylength Key size of the public mpi in bytes
|
||||
* @returns {String} Hashcode with pkcs1padding as string
|
||||
* @param {String} M message to be encoded
|
||||
* @param {Integer} emLen intended length in octets of the encoded message
|
||||
* @returns {String} encoded message
|
||||
*/
|
||||
encode: function(algo, data, keylength) {
|
||||
var data2 = "";
|
||||
data2 += String.fromCharCode(0x00);
|
||||
data2 += String.fromCharCode(0x01);
|
||||
encode: function(algo, M, emLen) {
|
||||
var i;
|
||||
for (i = 0; i < (keylength - hash_headers[algo].length - 3 -
|
||||
hash.getHashByteLength(algo)); i++)
|
||||
|
||||
data2 += String.fromCharCode(0xff);
|
||||
|
||||
data2 += String.fromCharCode(0x00);
|
||||
|
||||
for (i = 0; i < hash_headers[algo].length; i++)
|
||||
data2 += String.fromCharCode(hash_headers[algo][i]);
|
||||
|
||||
data2 += hash.digest(algo, data);
|
||||
return new BigInteger(util.hexstrdump(data2), 16);
|
||||
},
|
||||
|
||||
/**
|
||||
* extract the hash out of an EMSA-PKCS1-v1.5 padding (See {@link http://tools.ietf.org/html/rfc4880#section-13.1.3|RFC 4880 13.1.3})
|
||||
* @param {String} data Hash in pkcs1 encoding
|
||||
* @returns {String} The hash as string
|
||||
*/
|
||||
decode: function(algo, data) {
|
||||
var i = 0;
|
||||
if (data.charCodeAt(0) === 0) i++;
|
||||
else if (data.charCodeAt(0) != 1) return -1;
|
||||
else i++;
|
||||
|
||||
while (data.charCodeAt(i) == 0xFF) i++;
|
||||
if (data.charCodeAt(i++) !== 0) return -1;
|
||||
var j = 0;
|
||||
for (j = 0; j < hash_headers[algo].length && j + i < data.length; j++) {
|
||||
if (data.charCodeAt(j + i) != hash_headers[algo][j]) return -1;
|
||||
// Apply the hash function to the message M to produce a hash value H
|
||||
var H = hash.digest(algo, M);
|
||||
if (H.length !== hash.getHashByteLength(algo)) {
|
||||
throw new Error('Invalid hash length');
|
||||
}
|
||||
i += j;
|
||||
if (data.substring(i).length < hash.getHashByteLength(algo)) return -1;
|
||||
return data.substring(i);
|
||||
// produce an ASN.1 DER value for the hash function used.
|
||||
// Let T be the full hash prefix
|
||||
var T = '';
|
||||
for (i = 0; i < hash_headers[algo].length; i++) {
|
||||
T += String.fromCharCode(hash_headers[algo][i]);
|
||||
}
|
||||
// add hash value to prefix
|
||||
T += H;
|
||||
// and let tLen be the length in octets of T
|
||||
var tLen = T.length;
|
||||
if (emLen < tLen + 11) {
|
||||
throw new Error('Intended encoded message length too short');
|
||||
}
|
||||
// an octet string PS consisting of emLen - tLen - 3 octets with hexadecimal value 0xFF
|
||||
// The length of PS will be at least 8 octets
|
||||
var PS = '';
|
||||
for (i = 0; i < (emLen - tLen - 3); i++) {
|
||||
PS += String.fromCharCode(0xff);
|
||||
}
|
||||
// Concatenate PS, the hash prefix T, and other padding to form the
|
||||
// encoded message EM as EM = 0x00 || 0x01 || PS || 0x00 || T.
|
||||
var EM = String.fromCharCode(0x00) +
|
||||
String.fromCharCode(0x01) +
|
||||
PS +
|
||||
String.fromCharCode(0x00) +
|
||||
T;
|
||||
return new BigInteger(util.hexstrdump(EM), 16);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -19,8 +19,6 @@ module.exports = {
|
|||
* @return {Boolean} true if signature (sig_data was equal to data over hash)
|
||||
*/
|
||||
verify: function(algo, hash_algo, msg_MPIs, publickey_MPIs, data) {
|
||||
var calc_hash = hashModule.digest(hash_algo, data);
|
||||
var dopublic;
|
||||
|
||||
switch (algo) {
|
||||
case 1:
|
||||
|
@ -31,15 +29,12 @@ module.exports = {
|
|||
// RSA Sign-Only [HAC]
|
||||
var rsa = new publicKey.rsa();
|
||||
var n = publickey_MPIs[0].toBigInteger();
|
||||
var k = publickey_MPIs[0].byteLength();
|
||||
var e = publickey_MPIs[1].toBigInteger();
|
||||
var x = msg_MPIs[0].toBigInteger();
|
||||
dopublic = rsa.verify(x, e, n);
|
||||
var hash = pkcs1.emsa.decode(hash_algo, dopublic.toMPI().substring(2));
|
||||
if (hash == -1) {
|
||||
throw new Error('PKCS1 padding in message or key incorrect. Aborting...');
|
||||
}
|
||||
return hash == calc_hash;
|
||||
|
||||
var m = msg_MPIs[0].toBigInteger();
|
||||
var EM = rsa.verify(m, e, n);
|
||||
var EM2 = pkcs1.emsa.encode(hash_algo, data, k);
|
||||
return EM.compareTo(EM2) === 0;
|
||||
case 16:
|
||||
// Elgamal (Encrypt-Only) [ELGAMAL] [HAC]
|
||||
throw new Error("signing with Elgamal is not defined in the OpenPGP standard.");
|
||||
|
@ -53,7 +48,7 @@ module.exports = {
|
|||
var g = publickey_MPIs[2].toBigInteger();
|
||||
var y = publickey_MPIs[3].toBigInteger();
|
||||
var m = data;
|
||||
dopublic = dsa.verify(hash_algo, s1, s2, m, p, q, g, y);
|
||||
var dopublic = dsa.verify(hash_algo, s1, s2, m, p, q, g, y);
|
||||
return dopublic.compareTo(s1) === 0;
|
||||
default:
|
||||
throw new Error('Invalid signature algorithm.');
|
||||
|
|
Loading…
Reference in New Issue
Block a user