FileDocCategorySizeDatePackage
MySecurityService.javaAPI DocJava Card7748Wed Mar 22 21:07:24 GMT 2006com.sun.javacard.samples.SecureRMIDemo

MySecurityService.java

/*
 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

/*
 * @(#)MySecurityService.java	1.9 06/01/03
 */

package com.sun.javacard.samples.SecureRMIDemo;

import javacard.framework.*;
import javacard.framework.service.*;


public class MySecurityService extends BasicService implements SecurityService {
    
    private static final byte[] PRINCIPAL_APP_PROVIDER_ID = {0x12, 0x34};
    private static final byte[] PRINCIPAL_CARDHOLDER_ID = {0x43, 0x21};
    
    static final byte APDU_SM_MASK_TYPE4            = (byte)0x0C;
    
    private OwnerPIN provider_pin, cardholder_pin = null;
    
    public MySecurityService() {
        provider_pin = new OwnerPIN((byte)2,(byte)2);
        cardholder_pin = new OwnerPIN((byte)2,(byte)2);
        provider_pin.update(PRINCIPAL_APP_PROVIDER_ID, (short)0, (byte)2);
        cardholder_pin.update(PRINCIPAL_CARDHOLDER_ID, (short)0, (byte)2);
    }
    
    /** Pre-processes the input data for the command in the <CODE>APDU</CODE> object.
     * When invoked, the APDU object
     * should either be in <CODE>STATE_INITIAL</CODE> with the APDU buffer in the Init format
     * or in <CODE>STATE_FULL_INCOMING</CODE> with the APDU buffer in the Input Ready format
     * defined in <CODE>BasicService</CODE>.
     * <p>The method must return <CODE>true</CODE> if no more
     * pre-processing should be performed, and <CODE>false</CODE> otherwise.
     * In particular, it must return <CODE>false</CODE> if it has not performed any
     * processing on the command.
     * <P>
     * After normal completion, the <CODE>APDU</CODE> object is usually in <CODE>STATE_FULL_INCOMING</CODE>
     * with the APDU buffer in the Input Ready format defined in <CODE>BasicService</CODE>.
     * However, in some cases if the Service processes the command entirely,
     * the <CODE>APDU</CODE> object may be in <CODE>STATE_OUTGOING</CODE>
     * with the APDU buffer in the Output Ready format defined in <CODE>BasicService</CODE>.
     * @param apdu the <CODE>APDU</CODE> object containing the command being processed.
     * @return <code>true</code> if input processing is finished, <CODE>false</CODE> otherwise.
     */
    public boolean processDataIn(APDU apdu) {
        
        
        if(selectingApplet()) {
            commandProperties = 0;
            authenticated = 0;
            return false;   // in case some other service is interested
        }
        else {
            return preprocessCommandAPDU(apdu);
        }
    }
    
    /**
     * Checks whether a secure channel is in use between the card and the host for
     * the ongoing command that guarantees the indicated properties. The result is only
     * correct after pre-processing the command (for instance during the processing of
     * the command). For properties on incoming data, the result is guaranteed to be
     * correct; for outgoing data, the result reflects the expectations of the client
     * software, with no other guarantee.
     * @param properties the required properties.
     * @return true if the required properties are <CODE>true</CODE>, <CODE>false</CODE> othewise
     * @throws ServiceException with the following reason code:<ul>
     * <li><code>ServiceException.ILLEGAL_PARAM</code> if the specified
     * property is unknown.
     * </ul>
     */
    public boolean isCommandSecure(byte properties) throws ServiceException {
        return (commandProperties & properties) != 0;
    }
    
    private byte commandProperties;
    
    
    /**
     * Checks whether or not the specified principal is currently authenticated.
     * The validity timeframe(selection or reset) and authentication method as well
     * as the exact interpretation of the specified principal parameter needs to be
     * detailed by the implementation class.
     * The only generic guarantee is that the authentication has been performed in the
     * current card session.
     * @param principal an identifier of the principal that needs to be authenticated
     * @return true if the expected principal is authenticated
     * @throws ServiceException with the following reason code:<ul>
     * <li><code>ServiceException.ILLEGAL_PARAM</code> if the specified
     * principal is unknown.
     * </ul>
     */
    public boolean isAuthenticated(short principal) throws ServiceException {
        
        return (authenticated == principal);
    }
    
    private byte authenticated;
    
    private boolean preprocessCommandAPDU(APDU apdu) {
        
        receiveInData(apdu);
        
        if(checkAndRemoveChecksum(apdu)) {
            commandProperties |= SecurityService.PROPERTY_INPUT_INTEGRITY;
        }
        else {
            commandProperties &= ~SecurityService.PROPERTY_INPUT_INTEGRITY;
        }
        
        return false;
    }
    
    private boolean checkAndRemoveChecksum(APDU apdu) {
        byte[] buffer = apdu.getBuffer();
        
        if(!apdu.isSecureMessagingCLA()) {
            return false;
        }
        
        // reset secure flag
        apdu.getBuffer()[0] &= ~APDU_SM_MASK_TYPE4;
        
        short Lc = buffer[4];
        
        if(Lc<2) return false;  // must have at least the checksum
        
        short csum1 = 0;
        buffer[4] -= 2;       // decrease Lc
        Lc = buffer[4];
        
        for(short n = 5; n<(short)(Lc+5); ++n) {
            csum1 += buffer[n];
        }
        
        final short csum2 = Util.getShort(buffer, (short)(Lc+5));
        
        
        return (csum1 == csum2);
    }
    
    
    public boolean processCommand(APDU apdu) {
        if(isAuthenticate(apdu)) {
            receiveInData(apdu);
            if(apdu.getBuffer()[4] == 2) {
                authenticated = 0;
                
                //                short id = Util.getShort(apdu.getBuffer(), (short)5);
                
                if(provider_pin.check(apdu.getBuffer(), (short)5, (byte)2)) {
                    authenticated = PRINCIPAL_APP_PROVIDER;
                    setOutputLength(apdu,(short)0);
                    succeed(apdu);
                }
                else if(cardholder_pin.check(apdu.getBuffer(), (short)5, (byte)2)) {
                    authenticated = PRINCIPAL_CARDHOLDER;
                    setOutputLength(apdu,(short)0);
                    succeed(apdu);
                }
                else {
                    fail(apdu, (short)0x6666);
                }
            }
            else {
                fail(apdu, (short) 0x6565);
            }
            return true;
        }
        else {
            return false;
        }
    }
    
    public boolean processDataOut(APDU apdu) {
        
        if(selectingApplet()) return false;
        
        // if not select...
        // compute and append checksum
        byte[] buffer = apdu.getBuffer();
        short Le = (short)(buffer[4] & 0x00FF);
        
        short csum = 0;
        
        for(short n = 5; n<(short)(Le+5); ++n) {
            csum += buffer[n];
        }
        
        javacard.framework.Util.setShort(buffer, (short)(Le+5), csum);
        buffer[4] += 2;
        return false;
        
        
    }
    
    public boolean isChannelSecure(byte prop) {
        return false;
    }
    
    private static final byte INS_SELECT   = (byte)0xA4;
    private static final byte APDU_CMD_MASK = (byte)0xFC;
    
    private static final byte CLA_AUTH = (byte)0x80;
    private static final byte INS_AUTH = (byte)0x39;
    
    private boolean isAuthenticate(APDU command) {
        return
        (getCLA(command) == CLA_AUTH
        &&
        getINS(command) == INS_AUTH);
        
    }
    
    
    
}