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