1 /** 2 * --------- 3 * |.##> <##.| Open Smart Card Development Platform (www.openscdp.org) 4 * |# #| 5 * |# #| Copyright (c) 1999-2009 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 AuthenticationObject - Password, PIN or key container for external authentication 25 */ 26 27 28 /** 29 * Create an authentication object 30 * 31 * @class Class implementing authentication objects like PINs, PACE passwords or keys 32 * 33 * @param {String} name the human readable name of the object 34 * @param {String} type one of AuthenticationObject.TYPE_PACE or AuthenticationObject.TYPE_PIN 35 * @param {Number} id the password or key id 36 * @param {ByteString} value the reference value 37 */ 38 function AuthenticationObject(name, type, id, value) { 39 FileSystemIdObject.call(this, name, id); 40 this.type = type; 41 this.value = value; 42 this.retrycounter = 3; 43 this.initialretrycounter = 3; 44 this.usecounter = -1; 45 this.resetcounter = -1; 46 this.minLength = 4; 47 this.isActive = true; // State after using ACTIVATE / DEACTIVATE 48 this.isEnabled = true; // State after using ENABLE / DISABLE VERIFICATION REQUIREMENT 49 this.isTransport = false; // State before first change PIN 50 this.isTerminated = false; // State after TERMINATE 51 this.allowActivate = false; 52 this.allowDeactivate = false; 53 this.allowEnable = false; 54 this.allowDisable = false; 55 this.allowResetRetryCounter = false; 56 this.allowResetValue = false; 57 this.allowTerminate = false; 58 this.unsuspendAuthenticationObject = null; 59 this.unblockAuthenticationObject = null; 60 } 61 62 AuthenticationObject.prototype = new FileSystemIdObject(); 63 AuthenticationObject.prototype.constructor = AuthenticationObject; 64 65 66 AuthenticationObject.TYPE_PACE = "pace"; 67 AuthenticationObject.TYPE_PIN = "pin"; 68 69 70 71 /** 72 * Override from base class 73 */ 74 AuthenticationObject.prototype.getType = function() { 75 return this.type; 76 } 77 78 79 80 AuthenticationObject.prototype.isBlocked = function() { 81 return ((this.initialretrycounter != 0) && (this.retrycounter == 0)); 82 } 83 84 85 86 AuthenticationObject.prototype.isSuspended = function() { 87 return ((this.initialretrycounter != 0) && (this.retrycounter == 1)); 88 } 89 90 91 92 /** 93 * Activate authentication object 94 */ 95 AuthenticationObject.prototype.activate = function() { 96 if (!this.allowActivate) { 97 throw new GPError("AuthenticationObject", GPError.INVALID_DATA, APDU.SW_CONDOFUSENOTSAT, "Activate not allowed for authentication object"); 98 } 99 this.isActive = true; 100 } 101 102 103 104 /** 105 * Deactivate authentication object 106 */ 107 AuthenticationObject.prototype.deactivate = function() { 108 if (!this.allowDeactivate) { 109 throw new GPError("AuthenticationObject", GPError.INVALID_DATA, APDU.SW_CONDOFUSENOTSAT, "Deactivate not allowed for authentication object"); 110 } 111 this.isActive = false; 112 } 113 114 115 116 /** 117 * Reset retry counter and optionally set new reference value 118 * 119 * @param {ByteString} newValue new reference value 120 */ 121 AuthenticationObject.prototype.resetRetryCounter = function(newValue) { 122 if (!this.allowResetRetryCounter) { 123 throw new GPError("AuthenticationObject", GPError.INVALID_DATA, APDU.SW_CONDOFUSENOTSAT, "Reset retry counter not allowed for authentication object"); 124 } 125 if (newValue && !this.allowResetValue) { 126 throw new GPError("AuthenticationObject", GPError.INVALID_DATA, APDU.SW_CONDOFUSENOTSAT, "Reset retry counter not allowed with new value for authentication object"); 127 } 128 if (this.resetcounter != -1) { 129 if (this.resetcounter == 0) { 130 throw new GPError("AuthenticationObject", GPError.INVALID_DATA, APDU.SW_CONDOFUSENOTSAT, "Reset retry counter is 0"); 131 } 132 this.resetcounter--; 133 } 134 if (newValue && (newValue.length < this.minLength)) { 135 throw new GPError("AuthenticationObject", GPError.INVALID_DATA, APDU.SW_WRONGLENGTH, "New reference data too short"); 136 } 137 this.retrycounter = this.initialretrycounter; 138 this.isActive = true; 139 140 if (this.initialretrycounter) { 141 this.retrycounter = this.initialretrycounter; 142 } 143 if (newValue) { 144 this.isTransport = false; 145 this.value = newValue; 146 } 147 } 148 149 150 151 /** 152 * Change reference data, optionally verifying the old value before 153 * 154 * @param {Number} qualifier command qualifier, 00 = oldPIN||newPIN, 01 = newPIN 155 * @param {ByteString} value new reference value 156 */ 157 AuthenticationObject.prototype.changeReferenceData = function(qualifier, value) { 158 if (!this.allowChangeReferenceData) { 159 throw new GPError("AuthenticationObject", GPError.INVALID_DATA, APDU.SW_CONDOFUSENOTSAT, "Change reference data not allowed for authentication object"); 160 } 161 if (qualifier == 0x01) { 162 if (!this.isTerminated) { 163 throw new GPError("AuthenticationObject", GPError.INVALID_DATA, APDU.SW_CONDOFUSENOTSAT, "Change reference data with P1=01 not allowed non terminated authentication object"); 164 } 165 if (this.associatedKey && !this.associatedKey.isTerminated) { 166 throw new GPError("AuthenticationObject", GPError.INVALID_DATA, APDU.SW_REFDATANOTUSABLE, "Associated key is not terminated"); 167 } 168 } 169 if ((qualifier == 0x00) && (value.length <= this.value.length)) { 170 throw new GPError("AuthenticationObject", GPError.INVALID_DATA, APDU.SW_INVDATA, "Command data does not contain a new PIN value for P1=00"); 171 } 172 if (qualifier == 0x00) { 173 this.verify(value.left(this.value.length)); 174 value = value.bytes(this.value.length); 175 } 176 177 if (value.length < this.minLength) { 178 throw new GPError("AuthenticationObject", GPError.INVALID_DATA, APDU.SW_WRONGLENGTH, "New reference data too short"); 179 } 180 181 this.value = value; 182 this.isTerminated = false; 183 } 184 185 186 187 /** 188 * Verify PIN value 189 * 190 * @param {ByteString} value reference value 191 */ 192 AuthenticationObject.prototype.verify = function(value) { 193 if (this.isBlocked()) { 194 throw new GPError("AuthenticationObject", GPError.INVALID_DATA, APDU.SW_AUTHMETHLOCKED, "Authentication method blocked"); 195 } 196 if (this.isTerminated) { 197 throw new GPError("AuthenticationObject", GPError.INVALID_DATA, APDU.SW_REFDATANOTUSABLE, "Authentication method terminated"); 198 } 199 this.decreaseRetryCounter(); 200 if (!this.value.equals(value)) { 201 var sw = APDU.SW_WARNINGCOUNT | this.retrycounter; 202 throw new GPError("AuthenticationObject", GPError.INVALID_DATA, sw, "Authentication failed"); 203 } 204 this.restoreRetryCounter(); 205 } 206 207 208 209 /** 210 * Deactivate authentication object 211 */ 212 AuthenticationObject.prototype.decreaseRetryCounter = function() { 213 if (this.initialretrycounter) { 214 this.retrycounter--; 215 } 216 } 217 218 219 220 /** 221 * Deactivate authentication object 222 */ 223 AuthenticationObject.prototype.restoreRetryCounter = function() { 224 if (this.initialretrycounter) { 225 this.retrycounter = this.initialretrycounter; 226 } 227 } 228 229 230 231 /** 232 * Terminate authentication object 233 */ 234 AuthenticationObject.prototype.terminate = function() { 235 if (!this.allowTerminate) { 236 throw new GPError("AuthenticationObject", GPError.INVALID_DATA, APDU.SW_CONDOFUSENOTSAT, "Terminate not allowed for authentication object"); 237 } 238 this.isTerminated = true; 239 } 240 241 242 243 /** 244 * Convert object to a human readable string 245 */ 246 AuthenticationObject.prototype.toString = function() { 247 var state = ""; 248 if (this.isBlocked()) { 249 state += "blocked "; 250 } else if (this.isTerminated) { 251 state += "terminated "; 252 } else { 253 if (this.isActive) { 254 state += "active "; 255 } 256 if (this.isActive) { 257 state += "enabled "; 258 } else { 259 state += "disabled "; 260 } 261 if (this.isTransport) { 262 state += "transport "; 263 } 264 } 265 var str = this.type + ":" + this.name + "(" + this.id + ") is " + state; 266 if (this.initialretrycounter) { 267 str += " RC=" + this.retrycounter; 268 } 269 return str; 270 } 271 272 273