      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 PKCS#10 Decoder
     25  */
     26
     27 var PKIXCommon = require('scsh/x509/PKIXCommon').PKIXCommon;
     28 var X509Name = require('scsh/x509/X509Name').X509Name;
     29
     30
     31 /**
     32  * Class supporting PKCS#10 certificate signing requests
     33  *
     34  * @param {ASN1} req the certificate request
     35  */
     36 function PKCS10(req) {
     37
     38 	if (req instanceof ASN1) {
     39 		this.req = req;
     40 	} else if (req instanceof ByteString) {
     41 		var asn = new ASN1(req);
     42 		this.req = asn;
     43 	} else {
     44 		throw new GPError(module.id, GPError.INVALID_DATA, 0, "Argument must be either type of ByteString or ASN1");
     45 	}
     46
     47 	this.reqInfo = this.req.get(0);
     48 	this.sigAlg = this.req.get(1);
     49 	this.sig = this.req.get(2);
     50 }
     51
     52 exports.PKCS10 = PKCS10;
     53
     54 PKCS10.RSA = new ByteString("rsaEncryption", OID);
     55 PKCS10.RSA_PSS = new ByteString("id-RSASSA-PSS", OID);
     56 PKCS10.EC = new ByteString("id-ecPublicKey", OID);
     57
     58
     59
     60 /**
     61  * Get the encoded request
     62  *
     63  * @return the encoded request
     64  * @type ByteString
     65  */
     66 PKCS10.prototype.getBytes = function() {
     67 	return this.req.getBytes();
     68 }
     69
     70
     71
     72 /**
     73  * Gets the subject name as TLV object
     74  *
     75  * @return the issuer RDNSequence
     76  * @type ASN1
     77  */
     78 PKCS10.prototype.getSubject = function() {
     79 	return this.reqInfo.get(1);
     80 }
     81
     82
     83
     84 /**
     85  * Get the public key
     86  *
     87  * @type key
     88  * @return the public key
     89  */
     90 PKCS10.prototype.getPublicKey = function() {
     91 	var algorithm = this.reqInfo.get(2).get(0).get(0).value;
     92
     93 	var bitString = this.reqInfo.get(2).get(1).value;	// subjectPublicKey
     94 	bitString = PKIXCommon.removeLeadingZeroBytes(bitString);
     95
     96 	var key = new Key();
     97 	key.setType(Key.PUBLIC);
     98
     99 	if (algorithm.equals(PKCS10.RSA) || algorithm.equals(PKCS10.RSA_PSS)) {
    100 		var p = new ASN1(bitString);
    101 		key.setComponent(Key.MODULUS, PKIXCommon.removeLeadingZeroBytes(p.get(0).value));
    102 		key.setComponent(Key.EXPONENT, PKIXCommon.removeLeadingZeroBytes(p.get(1).value));
    103 	} else {
    104 		key.setComponent(Key.ECC_CURVE_OID, this.reqInfo.get(2).get(0).get(1).value);
    105 		if (bitString.byteAt(0) != 4) {
    106 			throw new GPError(module.id, GPError.INVALID_MECH, 0, "Only uncompressed keys supported (04)");
    107 		}
    108 		var l = (bitString.length - 1) >> 1;
    109 		key.setComponent(Key.ECC_QX, bitString.bytes(1, l));
    110 		key.setComponent(Key.ECC_QY, bitString.bytes(1 + l, l));
    111 	}
    112
    113 	return key;
    114 }
    115
    116
    117
    118 /**
    119  * Get the signature algorithm as Crypto.X constant
    120  *
    121  * @type Number
    122  * @return the signature algorithm
    123  */
    124 PKCS10.prototype.getSignatureAlgorithm = function() {
    125 	var algOID = this.sigAlg.get(0).value;
    126 	if (this.sigAlg.elements > 1) {
    127 		var param = this.sigAlg.get(1).getBytes();
    128 	}
    129 	var alg = PKIXCommon.decodeSignatureAlgorithm(algOID, param);
    130
    131 	if (!alg) {
    132 		throw new GPError(module.id, GPError.INVALID_MECH, alg, "Invalid algorithm");
    133 	}
    134 	return alg;
    135 }
    136
    137
    138
    139 /**
    140  * Get the signature value
    141  *
    142  * @type ByteString
    143  * @return the signature
    144  */
    145 PKCS10.prototype.getSignature = function() {
    146 	var signature = this.sig.value;
    147
    148 	if (signature.byteAt(0) == 0x00) {
    149 		signature = signature.bytes(1);
    150 	}
    151 	return signature;
    152 }
    153
    154
    155
    156 /**
    157  * Verify signature
    158  *
    159  * @param {Key} pubKey optional public key to use for verification
    160  * @type boolean
    161  * @return true if signature is valid
    162  */
    163 PKCS10.prototype.verify = function(pubKey) {
    164 	var crypto = new Crypto();
    165 	if (!pubKey) {
    166 		pubKey = this.getPublicKey();
    167 	}
    168 	var alg = this.getSignatureAlgorithm();
    169 	var signature = this.getSignature();
    170
    171 	return crypto.verify(pubKey, alg, this.reqInfo.getBytes(), signature);
    172 }
    173
    174
    175
    176 PKCS10.prototype.toString = function() {
    177 	var s = "PKCS#10 Request:";
    178
    179 	var k = this.getPublicKey();
    180 	if (k.getComponent(Key.MODULUS)) {
    181 		s+= "Type=RSA,";
    182 	} else {
    183 		s+= "Type=EC,";
    184 		var curve = ASN1.nameForObjectIdentifier(k.getComponent(Key.ECC_CURVE_OID));
    185 		if (curve) {
    186 			s += "Curve=" + curve + ",";
    187 		}
    188 	}
    189 	s+= "Size=" + k.getSize();
    190
    191 	var subject = this.getSubject();
    192 	if (subject) {
    193 		var xName = new X509Name(subject)
    194 		s += "," + xName.toString();
    195 	}
    196
    197 	return s;
    198 }
    199