/* * Copyright 2015 Licel Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.licel.jcardsim.framework; import javacard.framework.JCSystem; import javacard.framework.PIN; import javacard.framework.PINException; import javacard.framework.Util; /** * Implementation for <code>OwnerPin</code> * @see OwnerPin */ public class OwnerPINProxy implements PIN { private static final byte VALIDATED = 0; private static final byte NUMFLAGS = 1; private byte tryLimit; private byte maxPINSize; private byte pinValue[]; private byte pinSize; // CHECK !!! Use transient array with size 1 element for store validation state private boolean flags[]; // CHECK !!! Use transient array with size 1 element for store tries counter private byte triesLeft[]; /** * Constructor. Allocates a new <code>PIN</code> instance with validated flag * set to <code>false</code> * @param tryLimit the maximum number of times an incorrect PIN can be presented. <code>tryLimit</code> must be >=1 * @param maxPINSize the maximum allowed PIN size. <code>maxPINSize</code> must be >=1 * @throws PINException with the following reason codes: * <ul> * <li><code>PINException.ILLEGAL_VALUE</code> if <code>tryLimit</code> parameter is less than 1. * <li><code>PINException.ILLEGAL_VALUE</code> if <code>maxPINSize</code> parameter is less than 1. * </ul> */ public OwnerPINProxy(byte tryLimit, byte maxPINSize) throws PINException { if (tryLimit < 1 || maxPINSize < 1) { PINException.throwIt(PINException.ILLEGAL_VALUE); } pinValue = new byte[maxPINSize]; pinSize = maxPINSize; this.maxPINSize = maxPINSize; this.tryLimit = tryLimit; triesLeft = new byte[1]; resetTriesRemaining(); flags = JCSystem.makeTransientBooleanArray((short) 1, JCSystem.CLEAR_ON_RESET); setValidatedFlag(false); } /** * This protected method returns the validated flag. * This method is intended for subclass of this <code>OwnerPIN</code> to access or * override the internal PIN state of the <code>OwnerPIN</code>. * @return the boolean state of the PIN validated flag */ protected boolean getValidatedFlag() { return flags[0]; } /** * This protected method sets the value of the validated flag. * This method is intended for subclass of this <code>OwnerPIN</code> to control or * override the internal PIN state of the <code>OwnerPIN</code>. * @param value the new value for the validated flag */ protected void setValidatedFlag(boolean value) { flags[0] = value; } /** * !!! CHECK * This internal method resets tries counter */ private void resetTriesRemaining() { Util.arrayFillNonAtomic(triesLeft, (short) 0, (short) 1, tryLimit); } /** * !!! CHECK * This internal method decrement tries counter */ private void decrementTriesRemaining() { Util.arrayFillNonAtomic(triesLeft, (short) 0, (short) 1, (byte) (triesLeft[0] - 1)); } /** * Returns the number of times remaining that an incorrect PIN can * be presented before the <code>PIN</code> is blocked. * @return the number of times remaining */ @Override public byte getTriesRemaining() { return triesLeft[0]; } /** * Compares <code>pin</code> against the PIN value. If they match and the * <code>PIN</code> is not blocked, it sets the validated flag * and resets the try counter to its maximum. If it does not match, * it decrements the try counter and, if the counter has reached * zero, blocks the <code>PIN</code>. Even if a transaction is in progress, update of * internal state - the try counter, the validated flag, and the blocking state, * shall not participate in the transaction. * <p> * Note:<ul> * <li><em>If </em><code>NullPointerException</code><em> or </em><code>ArrayIndexOutOfBoundsException</code><em> is * thrown, the validated flag must be set to false, the try counter must be decremented * and, the <code>PIN</code> blocked if the counter reaches zero.</em> * <li><em>If </em><code>offset</code><em> or </em><code>length</code><em> parameter * is negative an </em><code>ArrayIndexOutOfBoundsException</code><em> exception is thrown.</em> * <li><em>If </em><code>offset+length</code><em> is greater than </em><code>pin.length</code><em>, the length * of the </em><code>pin</code><em> array, an </em><code>ArrayIndexOutOfBoundsException</code><em> exception is thrown.</em> * <li><em>If </em><code>pin</code><em> parameter is </em><code>null</code><em> * a </em><code>NullPointerException</code><em> exception is thrown.</em></ul> * @param pin the byte array containing the PIN value being checked * @param offset the starting offset in the <code>pin</code> array * @param length the length of <code>pin</code> * @return <code>true</code> if the PIN value matches; <code>false</code> otherwise * @throws ArrayIndexOutOfBoundsException if the check operation would cause access of data outside array bounds. * @throws NullPointerException if <code>pin</code> is <code>null</code> */ @Override public boolean check(byte pin[], short offset, byte length) throws ArrayIndexOutOfBoundsException, NullPointerException { boolean noMoreTries = false; setValidatedFlag(false); if (getTriesRemaining() == 0) { noMoreTries = true; } else { decrementTriesRemaining(); } if (length > 0) { byte tester = pin[(short) ((offset + length) - 1)]; if (length != pinSize || noMoreTries) { return false; } } if (Util.arrayCompare(pin, offset, pinValue, (short) 0, length) == 0 && length == pinSize) { setValidatedFlag(true); resetTriesRemaining(); return true; } else { return false; } } /** * Returns <code>true</code> if a valid PIN has been presented since the last * card reset or last call to <code>reset()</code>. * @return <code>true</code> if validated; <code>false</code> otherwise */ @Override public boolean isValidated() { return getValidatedFlag(); } /** * If the validated flag is set, this method resets the validated flag and * resets the <code>PIN</code> try counter to the value of the <code>PIN</code> try limit. * Even if a transaction is in progress, update of * internal state - the try counter, the validated flag, and the blocking state, * shall not participate in the transaction. * If the validated flag is not set, this method does nothing. */ @Override public void reset() { if (isValidated()) { resetAndUnblock(); } } /** * This method sets a new value for the PIN and resets the <code>PIN</code> try * counter to the value of the <code>PIN</code> try limit. It also resets the validated flag.<p> * This method copies the input pin parameter into an internal representation. If a transaction is * in progress, the new pin and try counter update must be conditional i.e * the copy operation must use the transaction facility. * @param pin the byte array containing the new PIN value * @param offset the starting offset in the pin array * @param length he length of the new PIN * @throws PINException with the following reason codes: * <ul> * <li><code>PINException.ILLEGAL_VALUE</code> if length is greater than configured maximum PIN size. * </ul> */ public void update(byte pin[], short offset, byte length) throws PINException { if (length > maxPINSize) { PINException.throwIt(PINException.ILLEGAL_VALUE); } Util.arrayCopy(pin, offset, pinValue, (short) 0, length); pinSize = length; triesLeft[0] = tryLimit; setValidatedFlag(false); } /** * This method resets the validated flag and * resets the <code>PIN</code> try counter to the value of the <code>PIN</code> try limit. * Even if a transaction is in progress, update of * internal state - the try counter, the validated flag, and the blocking state, * shall not participate in the transaction. * This method is used by the owner to re-enable the blocked <code>PIN</code>. */ public void resetAndUnblock() { resetTriesRemaining(); setValidatedFlag(false); } }