      1 /**
      2  *  ---------
      3  * |.##> <##.|  Open Smart Card Development Platform (www.openscdp.org)
      4  * |#       #|
      5  * |#       #|  Copyright (c) 1999-2015 CardContact Software & System Consulting
      6  * |'##> <##'|  Andreas Schwier, 32429 Minden, Germany (www.cardcontact.de)
      7  *  ---------
      8  *
      9  *  This file is part of OpenSCDP.
     10  *
     11  *  OpenSCDP is free software; you can redistribute it and/or modify
     12  *  it under the terms of the GNU General Public License version 2 as
     13  *  published by the Free Software Foundation.
     14  *
     15  *  OpenSCDP is distributed in the hope that it will be useful,
     16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     18  *  GNU General Public License for more details.
     19  *
     20  *  You should have received a copy of the GNU General Public License
     21  *  along with OpenSCDP; if not, write to the Free Software
     22  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     23  *
     24  * @fileoverview Basic helper functions to encode and decode PKCS#1 padding
     25  */
     26
     27
     28
     29 /**
     30  * Empty constructor
     31  */
     32 function PKCS1() {
     33 }
     34
     35 if (typeof(exports) != "undefined")
     36 	exports.PKCS1 = PKCS1;
     37
     38
     39 /**
     40  * Mask Generation Function 1
     41  *
     42  * @param {Crypto} crypto instance providing hash
     43  * @param {Number} hashalg Hash algorithm (one of Crypto.SHA_xxx)
     44  * @param {ByteString} mgfSeed Seed byte string
     45  * @param {Number} maskLen length of requested masked
     46  * @type ByteString
     47  * @return the generated mask
     48  */
     49 PKCS1.MGF1 = function(crypto, hashalg, mgfSeed, maskLen) {
     50 	if ((maskLen < 0) || (maskLen > 256)) {
     51 		throw new Error("maskLen out of range");
     52 	}
     53 	var t = new ByteString("", HEX);
     54
     55 	var c = 0;
     56 	while (t.length < maskLen) {
     57 		t = t.concat(crypto.digest(hashalg, mgfSeed.concat(ByteString.valueOf(c, 4))));
     58 		c++;
     59 	}
     60 	return t.left(maskLen);
     61 }
     62
     63
     64
     65 /**
     66  * EME-OAEP decoder as defined in PKCS#1 V2.1 chapter 7.1.2
     67  *
     68  * @param {Crypto} crypto instance providing hash function
     69  * @param {ByteString} label additional label for encryption
     70  * @param {Number} hashalg Hash algorithm (one of Crypto.SHA_xxx)
     71  * @param {ByteString} em encoded messages after RSA decrypt
     72  * @type ByteString
     73  * @return null if padding error or recovered message
     74  */
     75 PKCS1.decode_EME_OAEP = function(crypto, label, hashalg, em) {
     76 	if (!label) {
     77 		label = new ByteString("", HEX);
     78 	}
     79
     80 	var lHash = crypto.digest(hashalg, label);
     81 	var hLen = lHash.length;
     82 	var y = em.byteAt(0);
     83 	var maskedSeed = em.bytes(1, hLen);
     84 	var maskedDB = em.bytes(1 + hLen);
     85
     86 	var seedMask = PKCS1.MGF1(crypto, hashalg, maskedDB, hLen);
     87 	var seed = maskedSeed.xor(seedMask);
     88 	var dbMask = PKCS1.MGF1(crypto, hashalg, seed, em.length - hLen - 1);
     89 	var DB = maskedDB.xor(dbMask);
     90
     91 	var c1 = y == 0;
     92 	var c2 = DB.left(hLen).equals(lHash);
     93 	var cnt = 0;
     94 	var i = hLen;
     95 	for (; (i < DB.length) && (DB.byteAt(i) != 0x01); i++) {
     96 		cnt += (DB.byteAt(i) == 0x00) ? 1 : 0;
     97 	}
     98
     99 	var c3 = i < DB.length;
    100 	var c4 = cnt == (i - hLen);
    101
    102 	if (!c1 || !c2 || !c3 || !c4) {
    103 		return null;
    104 	}
    105 	return DB.bytes(i + 1);
    106 }
    107
    108
    109
    110 /**
    111  * EME-PKCS1-v1_5 decoder as defined in PKCS#1 V2.1 chapter 7.2.2
    112  *
    113  * @param {ByteString} em encoded messages after RSA decrypt
    114  * @type ByteString
    115  * @return null if padding error or recovered message
    116  */
    117 PKCS1.decode_EME_V15 = function(em) {
    118 	var c1 = em.byteAt(0) == 0;
    119 	var c2 = em.byteAt(1) == 2;
    120
    121 	for (i = 2; (i < em.length) && (em.byteAt(i) != 0x00); i++);
    122
    123 	var c3 = i < em.length;
    124
    125 	if (!c1 || !c2 || !c3) {
    126 		return null;
    127 	}
    128 	return em.bytes(i + 1);
    129 }
    130
    131
    132
    133 PKCS1.ALLFF = new ByteString(" ", HEX);
    134
    135 /**
    136  * EMSA-PKCS-v1_5 encoder as defined in PKCS#1 V2.1 chapter 9.2
    137  *
    138  * This method uses the length of the hash to determine the hash algorithm, i.e. MD5(16), SHA-1(20), SHA-224(28), SHA-256(32), SHA-384(48) or SHA-512(64)
    139  *
    140  * @param {Number} keysize the key size in bits
    141  * @param {ByteString} hash the hash value
    142  * @type ByteString
    143  * @return the encoded message
    144  */
    145 PKCS1.encode_EMSA_V15 = function(keysize, hash) {
    146 	switch(hash.length) {
    147 		case 16:
    148 			var oid = new ByteString("id-md5", OID);
    149 			break;
    150 		case 20:
    151 			var oid = new ByteString("id-sha1", OID);
    152 			break;
    153 		case 28:
    154 			var oid = new ByteString("id-sha224", OID);
    155 			break;
    156 		case 32:
    157 			var oid = new ByteString("id-sha256", OID);
    158 			break;
    159 		case 48:
    160 			var oid = new ByteString("id-sha384", OID);
    161 			break;
    162 		case 64:
    163 			var oid = new ByteString("id-sha512", OID);
    164 			break;
    165 		default:
    166 			throw new GPError(module.id, GPError.INVALID_DATA, 0, "Invalid hash length");
    167 	}
    168 	var di = new ASN1(ASN1.SEQUENCE,
    169 			new ASN1(ASN1.SEQUENCE,
    170 				new ASN1(ASN1.OBJECT_IDENTIFIER, oid),
    171 				new ASN1(ASN1.NULL, new ByteString("", HEX))
    172 			    ),
    173 			new ASN1(ASN1.OCTET_STRING, hash)
    174 		     );
    175
    176 	var t = di.getBytes();
    177 	var em = new ByteBuffer("0001", HEX);
    178 	var pslen = (keysize >> 3) - t.length - 3;
    179 	while (pslen > 0) {
    180 		var l = pslen > 256 ? 256 : pslen;
    181 		em.append(PKCS1.ALLFF.left(l));
    182 		pslen -= l;
    183 	}
    184 	em.append(0x00);
    185 	em.append(t);
    186 	return em.toByteString();
    187 }
    188