1 /**
  2  *  ---------
  3  * |.##> <##.|  Open Smart Card Development Platform (www.openscdp.org)
  4  * |#       #|  
  5  * |#       #|  Copyright (c) 1999-2010 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 Connector implementing a web service interface to a terminal control center as defined in TR-03129
 25  */
 26 
 27 
 28 load("../cvc.js");
 29 
 30 
 31 /**
 32  * Creates a web service connector to access services of a terminal control center as defined in TR-03129
 33  *
 34  * @class Class implementing a terminal control center web service connector
 35  * @constructor
 36  * @param {String} url the web service endpoint
 37  */
 38 function TCCConnection(url) {
 39 	this.url = url;
 40 	this.soapcon = new SOAPConnection();
 41 	this.verbose = true;
 42 	this.lastError = null;
 43 	this.version = "1.1";
 44 }
 45 
 46 
 47 
 48 /**
 49  * Get the last error return code
 50  *
 51  * @returns the last error return code received or null if none defined
 52  * @type String
 53  */
 54 TCCConnection.prototype.getLastError = function() {
 55 	return this.lastError;
 56 }
 57 
 58 
 59 
 60 /**
 61  * Close the connector and release allocated resources
 62  */
 63 TCCConnection.prototype.close = function() {
 64 	this.soapcon.close();
 65 }
 66 
 67 
 68 
 69 /**
 70  * Obtain a list of card verifiable certificates that can be used to provide the MRTD with a chain of certificates
 71  * up to and including the terminal certificate.
 72  *
 73  * @param {PublicKeyReference} keyNameMRTD the certificate holder reference of the trust anchor used by the MRTD
 74  * @returns a lists of card verifiable certificates from root to terminal or null in case of error
 75  * @type CVC[]
 76  */
 77 TCCConnection.prototype.getCertificateChain = function(keyNameMRTD) {
 78 	
 79 	this.lastError = null;
 80 
 81 	var ns = new Namespace("uri:EAC-PKI-TermContr-Protocol/" + this.version);
 82 	var ns1 = new Namespace("uri:eacBT/" + this.version);
 83 
 84 	if (this.version == "1.0") {
 85 		var request =
 86 			<ns:GetCertificateChain xmlns:ns={ns} xmlns:ns1={ns1}>
 87 				<keyNameMRTD>{keyNameMRTD.getBytes().toString(BASE64)}</keyNameMRTD>
 88 			</ns:GetCertificateChain>
 89 	} else {
 90 		var request =
 91 			<ns:GetCertificateChain xmlns:ns={ns} xmlns:ns1={ns1}>
 92 				<keyCAR>{keyNameMRTD.getBytes().toString(BASE64)}</keyCAR>
 93 			</ns:GetCertificateChain>
 94 	}
 95 	
 96 	if (this.verbose) {
 97 		GPSystem.trace(request.toXMLString());
 98 	}
 99 
100 	var response = this.soapcon.call(this.url, request);
101 
102 	if (this.verbose) {
103 		GPSystem.trace(response.toXMLString());
104 	}
105 
106 	var certmap = [];
107 
108 	if (response.Result.ns1::returnCode.toString() == "ok_cert_available") {
109 		if (this.verbose) {
110 			GPSystem.trace("Received certificates from TCC:");
111 		}
112 		for each (var c in response.Result.ns1::certificateSeq.ns1::certificate) {
113 			var cvc = new CVC(new ByteString(c, BASE64));
114 			certmap[cvc.getCAR().toString()] = cvc;
115 			if (this.verbose) {
116 				GPSystem.trace(cvc.getCAR().toString());
117 				GPSystem.trace(cvc);
118 			}
119 		}
120 	} else {
121 		this.lastError = response.Result.ns1::returnCode.toString();
122 		return null;
123 	}
124 
125 	var certlist = [];
126 
127 	var car = keyNameMRTD;
128 	var cvc = certmap[car.toString()];
129 
130 	while (typeof(cvc) != "undefined") {
131 		certlist.push(cvc);
132 		if (this.verbose) {
133 			GPSystem.trace("Added: " + cvc);
134 		}
135 		car = cvc.getCHR()
136 		cvc = certmap[car.toString()]
137 	}
138 
139 	return certlist;
140 }
141 
142 
143 
144 /**
145  * Obtain a signature from the TCC for a hash or a block of data
146  *
147  * @param {PublicKeyReference} keyCHR the key to be used for signing
148  * @param {ByteString} digest the message digest or null if second variant is used
149  * @returns the signature as a concatenation of coordinates on the curve or null in case of error
150  * @type ByteString
151  */
152 TCCConnection.prototype.getTASignature = function(keyCHR, digest) {
153 	
154 	this.lastError = null;
155 
156 	var ns = new Namespace("uri:EAC-PKI-TermContr-Protocol/" + this.version);
157 	var ns1 = new Namespace("uri:eacBT/" + this.version);
158 	
159 	var request =
160 		<ns:GetTASignature xmlns:ns={ns} xmlns:ns1={ns1}>
161 			<hashTBS>
162 			</hashTBS>
163 			<idPICC>
164 			</idPICC>
165 			<challengePICC>
166 			</challengePICC>
167 			<hashPK>
168 			</hashPK>
169 			<auxPCD>
170 			</auxPCD>
171 			<keyCHR>{keyCHR.getBytes().toString(BASE64)}</keyCHR>
172 		</ns:GetTASignature>
173 
174 	request.hashTBS.ns1::binary = <ns1:binary xmlns:ns1={ns1}>{digest.toString(BASE64)}</ns1:binary>;
175 
176 	if (this.verbose) {
177 		GPSystem.trace(request.toXMLString());
178 	}
179 
180 	var response = this.soapcon.call(this.url, request);
181 
182 	if (this.verbose) {
183 		GPSystem.trace(response.toXMLString());
184 	}
185 	
186 	var signature = null;
187 	
188 	if (response.Result.ns1::returnCode.toString() == "ok_signature_available") {
189 		var signatureStr = response.Result.ns1::Signature.toString();
190 		if (this.verbose) {
191 			GPSystem.trace("Received signature from TCC: " + signatureStr);
192 		}
193 		signature = new ByteString(signatureStr, BASE64);
194 		if (this.verbose) {
195 			GPSystem.trace("Received signature from TCC: " + signature);
196 		}
197 	} else {
198 		this.lastError = response.Result.ns1::returnCode.toString();
199 	}
200 
201 	return signature;
202 }
203 
204 
205 
206 /**
207  * Obtain a signature from the TCC for a hash or a block of data
208  *
209  * @param {PublicKeyReference} keyCHR the key to be used for signing
210  * @param {ByteString} digest the message digest or null if second variant is used
211  * @returns the signature as a concatenation of coordinates on the curve or null in case of error
212  * @type ByteString
213  */
214 TCCConnection.prototype.getTASignature2 = function(idPICC, challengePICC, hashPK, keyCHR) {
215 	
216 	this.lastError = null;
217 
218 	var ns = new Namespace("uri:EAC-PKI-TermContr-Protocol/" + this.version);
219 	var ns1 = new Namespace("uri:eacBT/" + this.version);
220 	
221 	var request =
222 		<ns:GetTASignature xmlns:ns={ns} xmlns:ns1={ns1}>
223 			<hashTBS>
224 			</hashTBS>
225 			<idPICC>
226 			</idPICC>
227 			<challengePICC>
228 			</challengePICC>
229 			<hashPK>
230 			</hashPK>
231 			<auxPCD>
232 			</auxPCD>
233 			<keyCHR>{keyCHR.getBytes().toString(BASE64)}</keyCHR>
234 		</ns:GetTASignature>
235 
236 	request.idPICC.ns1::binary = <ns1:binary xmlns:ns1={ns1}>{idPICC.toString(BASE64)}</ns1:binary>;
237 	request.challengePICC.ns1::binary = <ns1:binary xmlns:ns1={ns1}>{challengePICC.toString(BASE64)}</ns1:binary>;
238 	request.hashPK.ns1::binary = <ns1:binary xmlns:ns1={ns1}>{hashPK.toString(BASE64)}</ns1:binary>;
239 
240 	if (this.verbose) {
241 		GPSystem.trace(request.toXMLString());
242 	}
243 
244 	var response = this.soapcon.call(this.url, request);
245 
246 	if (this.verbose) {
247 		GPSystem.trace(response.toXMLString());
248 	}
249 	
250 	var signature = null;
251 	
252 	if (response.Result.ns1::returnCode.toString() == "ok_signature_available") {
253 		var signatureStr = response.Result.ns1::Signature.toString();
254 		if (this.verbose) {
255 			GPSystem.trace("Received signature from TCC: " + signatureStr);
256 		}
257 		signature = new ByteString(signatureStr, BASE64);
258 		if (this.verbose) {
259 			GPSystem.trace("Received signature from TCC: " + signature);
260 		}
261 	} else {
262 		this.lastError = response.Result.ns1::returnCode.toString();
263 	}
264 
265 	return signature;
266 }
267 
268 
269 
270 /**
271  * Perform a simple test
272  */
273 TCCConnection.test = function() {
274 	var c = new TCCConnection("http://localhost:8080/se/tcc");
275 //	var c = new TCCConnection("http://demo.openscdp.org/se/tcc");
276 
277 	var chr = new PublicKeyReference("UTCVCA00001");
278 	var cl = c.getCertificateChain(chr);
279 	
280 	if (cl == null) {
281 		print("GetCertificateChain reports error: " + c.getLastError());
282 	}
283 	
284 	print("Received certificates:");
285 	for (var i = 0; i < cl.length; i++) {
286 		print(cl[i]);
287 	}
288 
289 	// Extract terminal certificate, which is always the last in the list
290 	var tcert = cl[cl.length - 1];
291 	var chr = tcert.getCHR();
292 	
293 	var crypto = new Crypto();
294 	var message = new ByteString("Hello World", ASCII);
295 	
296 	var digest = crypto.digest(Crypto.SHA_256, message);
297 	
298 	var signature = c.getTASignature(chr, digest);
299 	
300 	if (signature == null) {
301 		print("GetTASignature reports error: " + c.getLastError());
302 		return;
303 	}
304 	
305 	print("Signature: " + signature);
306 	var signature = ECCUtils.wrapSignature(signature);
307 
308 	// Important: Test only works for fixed domain parameter !!!
309 	var dp = new Key();
310 	dp.setComponent(Key.ECC_CURVE_OID, new ByteString("brainpoolP256r1", OID));
311 
312 	var puk = tcert.getPublicKey(dp);
313 	var mech = CVC.getSignatureMech(tcert.getPublicKeyOID());
314 
315 	print("Message: " + message);
316 	print("Hash: " + digest);
317 
318 	print("Signature verification: " + crypto.verify(puk, mech, message, signature));
319 }
320 
321