org.pkcs11.jacknji11.C.java Source code

Java tutorial

Introduction

Here is the source code for org.pkcs11.jacknji11.C.java

Source

/*
 * Copyright 2010-2011 Joel Hockey (joel.hockey@gmail.com). All rights reserved.
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package org.pkcs11.jacknji11;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pkcs11.jacknji11.jna.JNA;

/**
 * Low-level java interface that maps to {@link NativeProvider} cryptoki calls.
 *
 * jacknji11 provides 3 interfaces for calling cryptoki functions.
 * <ol>
 * <li>{@link org.pkcs11.jacknji11.NativeProvider} provides the lowest level
 * direct mapping to the <code>'C_*'</code> functions.  There is little
 * reason why you would ever want to invoke it directly, but you can.
 * <li>{@link org.pkcs11.jacknji11.C} provides the exact same functions
 * as {@link org.pkcs11.jacknji11.NativeProvider} by calling through to the
 * corresponding native method.  The <code>'C_'</code> at the start of the
 * function name is removed since the <code>'C.'</code> when you call the
 * static methods of this class looks similar.  In addition to calling
 * the native methods, {@link org.pkcs11.jacknji11.C} provides logging
 * through apache commons logging.  You can use this if you require fine-grain
 * control over something such as checking
 * {@link org.pkcs11.jacknji11.CKR} return codes.
 * <li>{@link org.pkcs11.jacknji11.CE} (<b>C</b>ryptoki
 * with <b>E</b>xceptions) provides the most user-friendly interface
 * and is the preferred interface to use.  It calls
 * related function(s) in {@link org.pkcs11.jacknji11.C},
 * and converts any non-zero return values into a
 * {@link org.pkcs11.jacknji11.CKRException}.  It automatically resizes
 * arrays and other helpful things.
 * </ol>
 *
 * @author Joel Hockey (joel.hockey@gmail.com)
 */
public class C {
    private static final Log log = LogFactory.getLog(C.class);

    public static NativeProvider NATIVE = new JNA();

    private static final NativePointer NULL = new NativePointer(0);

    /**
     * Initialise Cryptoki with null mutexes, and CKF_OS_LOCKING_OK flag set.
     * @see NativeProvider#C_Initialize(CK_C_INITIALIZE_ARGS)
     * @return {@link CKR} return code
     */
    public static long Initialize() {
        CK_C_INITIALIZE_ARGS args = new CK_C_INITIALIZE_ARGS(null, null, null, null,
                CK_C_INITIALIZE_ARGS.CKF_OS_LOCKING_OK);
        return Initialize(args);
    }

    /**
     * Initialise Cryptoki with supplied args.
     * @see NativeProvider#C_Initialize(CK_C_INITIALIZE_ARGS)
     * @return {@link CKR} return code
     */
    public static long Initialize(CK_C_INITIALIZE_ARGS pInitArgs) {
        if (log.isDebugEnabled())
            log.debug("> C_Initialize " + pInitArgs);
        long rv = NATIVE.C_Initialize(pInitArgs);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_Initialize rv=0x%08x{%s}", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * Called to indicate that an application is finished with the Cryptoki library.
     * @see NativeProvider#C_Finalize(Pointer)
     * @return {@link CKR} return code
     */
    public static long Finalize() {
        if (log.isDebugEnabled())
            log.debug("> C_Finalize");
        long rv = NATIVE.C_Finalize(NULL);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_Finalize rv=0x%08x{%s}", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * Returns general information about Cryptoki.
     * @param info location that receives information
     * @return {@link CKR} return code
     * @see NativeProvider#C_GetInfo(CK_INFO)
     */
    public static long GetInfo(CK_INFO info) {
        if (log.isDebugEnabled())
            log.debug("> C_GetInfo");
        long rv = NATIVE.C_GetInfo(info);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_GetInfo rv=0x%08x{%s}\n%s", rv, CKR.L2S(rv), info));
        return rv;
    }

    /**
     * Obtains a list of slots in the system.
     * @param tokenPresent only slots with tokens?
     * @param slotList receives array of slot IDs
     * @param count receives the number of slots
     * @return {@link CKR} return code
     * @see NativeProvider#C_GetSlotList(byte, LongArray, LongRef)
     */
    public static long GetSlotList(boolean tokenPresent, long[] slotList, LongRef count) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_GetSlotList tokenPresent=%b count=%d", tokenPresent, count.value()));
        long rv = NATIVE.C_GetSlotList(tokenPresent, slotList, count);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_GetSlotList rv=0x%08x{%s} count=%d\n  %s", rv, CKR.L2S(rv), count.value(),
                    Arrays.toString(slotList)));
        return rv;
    }

    /**
     * Obtains information about a particular slot in the system.
     * @param slotID the ID of the slot
     * @param info receives the slot information
     * @return {@link CKR} return code
     * @see NativeProvider#C_GetSlotInfo(long, CK_SLOT_INFO)
     */
    public static long GetSlotInfo(long slotID, CK_SLOT_INFO info) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_GetSlotInfo slotID=%d", slotID));
        long rv = NATIVE.C_GetSlotInfo(slotID, info);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_GetSlotInfo rv=0x%08x{%s}\n%s", rv, CKR.L2S(rv), info));
        return rv;
    }

    /**
     * Obtains information about a particular token in the system.
     * @param slotID ID of the token's slot
     * @param info receives the token information
     * @return {@link CKR} return code
     * @see NativeProvider#C_GetTokenInfo(long, CK_TOKEN_INFO)
     */
    public static long GetTokenInfo(long slotID, CK_TOKEN_INFO info) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_GetTokenInfo slotID=%d", slotID));
        long rv = NATIVE.C_GetTokenInfo(slotID, info);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_GetTokenInfo rv=0x%08x{%s}\n%s", rv, CKR.L2S(rv), info));
        return rv;
    }

    /**
     * Waits for a slot event (token insertion, removal, etc.) to occur.
     * @param flags blocking/nonblocking flag
     * @param slot location that receives the slot ID
     * @param reserved reserved.  Should be null
     * @return {@link CKR} return code
     * @see NativeProvider#C_WaitForSlotEvent(long, LongRef, Pointer)
     */
    public static long WaitForSlotEvent(long flags, LongRef slot, NativePointer reserved) {
        if (log.isDebugEnabled())
            log.debug("> C_WaitForSlotEvent");
        long rv = NATIVE.C_WaitForSlotEvent(flags, slot, reserved != null ? reserved : NULL);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_WaitForSlotEvent rv=0x%08x{%s} slot=%d", rv, CKR.L2S(rv), slot.value()));
        return rv;
    }

    /**
     * Obtains a list of mechanism types supported by a token.
     * @param slotID ID of token's slot
     * @param mechanismList gets mechanism array
     * @param count gets # of mechanisms
     * @return {@link CKR} return code
     * @see NativeProvider#C_GetMechanismList(long, LongArray, LongRef)
     */
    public static long GetMechanismList(long slotID, long[] mechanismList, LongRef count) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_GetMechanismList slotID=%d count=%d", slotID, count.value()));
        long rv = NATIVE.C_GetMechanismList(slotID, mechanismList, count);
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("< C_GetMechanismList rv=0x%08x{%s} count=%d", rv, CKR.L2S(rv), count.value()));
            if (mechanismList != null) {
                sb.append('\n');
                for (long m : mechanismList) {
                    sb.append(String.format("  0x%08x{%s}\n", m, CKM.L2S(m)));
                }
            }
            log.debug(sb);
        }
        return rv;
    }

    /**
     * Obtains information about a particular mechanism possibly supported by a token.
     * @param slotID ID of the token's slot
     * @param type {@link CKM} type of mechanism
     * @param info receives mechanism info
     * @return {@link CKR} return code
     * @see NativeProvider#C_GetMechanismInfo(long, long, CK_MECHANISM_INFO)
     */
    public static long GetMechanismInfo(long slotID, long type, CK_MECHANISM_INFO info) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_GetMechanismInfo slotID=%d type=0x%08x{%s}", slotID, type, CKM.L2S(type)));
        long rv = NATIVE.C_GetMechanismInfo(slotID, type, info);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_GetMechanismInfo rv=0x%08x{%s}\n%s", rv, CKR.L2S(rv), info));
        return rv;
    }

    /**
     * Initialises a token.  Pad or truncate label if required.
     * @param slotID ID of the token's slot
     * @param pin the SO's intital PIN
     * @param label 32-byte token label (space padded).  If not 32 bytes, then
     * it will be padded or truncated as required
     * @return {@link CKR} return code
     * @see NativeProvider#C_InitToken(long, byte[], long, byte[])
     */
    public static long InitToken(long slotID, byte[] pin, byte[] label) {
        byte[] label32;
        if (label != null && label.length == 32) {
            label32 = label;
        } else {
            label32 = new byte[32];
            Arrays.fill(label32, (byte) 0x20); // space fill
            if (label != null) {
                System.arraycopy(label, 0, label32, 0, Math.min(label32.length, label.length));
            }
        }
        if (log.isDebugEnabled())
            log.debug(String.format("> C_InitToken slotID=%d pin=*** label=%s", slotID, Buf.escstr(label32)));
        long rv = NATIVE.C_InitToken(slotID, pin, baLen(pin), label32);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_InitToken rv=0x%08x{%s}", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * Initialise normal user with PIN.
     * @param session the session's handle
     * @param pin the normal user's PIN
     * @return {@link CKR} return code
     * @see NativeProvider#C_InitPIN(long, byte[], long)
     */
    public static long InitPIN(long session, byte[] pin) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_InitPIN session=0x%08x pin=***", session));
        long rv = NATIVE.C_InitPIN(session, pin, baLen(pin));
        if (log.isDebugEnabled())
            log.debug(String.format("< C_InitPIN rv=0x%08x{%s}", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * Change PIN.
     * @param session the session's handle
     * @param oldPin old PIN
     * @param newPin new PIN
     * @return {@link CKR} return code
     * @see NativeProvider#C_SetPIN(long, byte[], long, byte[], long)
     */
    public static long SetPIN(long session, byte[] oldPin, byte[] newPin) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_SetPIN session=0x%08x oldPin=*** newPin=***", session));
        long rv = NATIVE.C_SetPIN(session, oldPin, baLen(oldPin), newPin, baLen(newPin));
        if (log.isDebugEnabled())
            log.debug(String.format("< C_SetPIN rv=0x%08x{%s}", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * Opens a session between an application and a token.
     * @param slotID the slot's ID
     * @param flags from {@link CK_SESSION_INFO}
     * @param application passed to callback (ok to leave it null)
     * @param notify callback function (ok to leave it null)
     * @param session gets session handle
     * @return {@link CKR} return code
     * @see NativeProvider#C_OpenSession(long, long, Pointer, CK_NOTIFY, LongRef)
     */
    public static long OpenSession(long slotID, long flags, NativePointer application, CK_NOTIFY notify,
            LongRef session) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_OpenSession slotID=%d flags=0x%08x{%s} application=%s notify=%s", slotID,
                    flags, CK_SESSION_INFO.f2s(flags), application, notify));
        long rv = NATIVE.C_OpenSession(slotID, flags, application != null ? application : NULL, notify, session);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_OpenSession rv=0x%08x{%s} session=0x%08x", rv, CKR.L2S(rv),
                    session.value()));
        return rv;
    }

    /**
     * Closes a session between an application and a token.
     * @param session the session's handle
     * @return {@link CKR} return code
     * @see NativeProvider#C_CloseSession(long)
     */
    public static long CloseSession(long session) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_CloseSession session=0x%08x", session));
        long rv = NATIVE.C_CloseSession(session);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_CloseSession rv=0x%08x{%s}", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * Closes all sessions with a token.
     * @param slotID the token's slot
     * @return {@link CKR} return code
     * @see NativeProvider#C_CloseAllSessions(long)
     */
    public static long CloseAllSessions(long slotID) {
        if (log.isDebugEnabled())
            log.debug("> C_CloseAllSessions");
        long rv = NATIVE.C_CloseAllSessions(slotID);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_CloseAllSessions rv=0x%08x{%s}", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * Obtains information about the session.
     * @param session the session's handle
     * @param info receives session info
     * @return {@link CKR} return code
     * @see NativeProvider#C_GetSessionInfo(long, CK_SESSION_INFO)
     */
    public static long GetSessionInfo(long session, CK_SESSION_INFO info) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_GetSessionInfo session=0x%08x", session));
        long rv = NATIVE.C_GetSessionInfo(session, info);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_GetSessionInfo rv=0x%08x{%s}\n%s", rv, CKR.L2S(rv), info));
        return rv;
    }

    /**
     * Obtains the state of the cryptographic operation.
     * @param session the session's handle
     * @param operationState gets state
     * @param operationStateLen gets state length
     * @return {@link CKR} return code
     * @see NativeProvider#C_GetOperationState(long, byte[], LongRef)
     */
    public static long GetOperationState(long session, byte[] operationState, LongRef operationStateLen) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_GetOperationState session=0x%08x operationStateLen=%d", session,
                    operationStateLen.value()));
        long rv = NATIVE.C_GetOperationState(session, operationState, operationStateLen);
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("< C_GetOperationState rv=0x%08x{%s}\n  operationState (len=%d):\n", rv,
                            CKR.L2S(rv), operationStateLen.value()));
            if (operationState != null) {
                Hex.dump(sb, operationState, 0, (int) operationStateLen.value(), "  ", 32, false);
            }
            log.debug(sb);
        }
        return rv;
    }

    /**
     * Restores the state of the cryptographic operation in a session.
     * @param session the session's handle
     * @param operationState holds state
     * @param encryptionKey en/decryption key
     * @param authenticationKey sign/verify key
     * @return {@link CKR} return code
     * @see NativeProvider#C_SetOperationState(long, byte[], long, long, long)
     */
    public static long SetOperationState(long session, byte[] operationState, long encryptionKey,
            long authenticationKey) {

        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(String.format(
                    "> C_SetOperationState session=0x%08x encryptionKey=0x%08x authenticationKey=0x%08x\n  operationState (len=%d):\n",
                    session, encryptionKey, authenticationKey, operationState.length));
            Hex.dump(sb, operationState, 0, operationState.length, "  ", 32, false);
            log.debug(sb);
        }
        long rv = NATIVE.C_SetOperationState(session, operationState, baLen(operationState), encryptionKey,
                authenticationKey);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_SetOperationState rv=0x%08x{%s}", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * Logs a user into a token.
     * @param session the session's handle
     * @param userType the user type from {@link CKU}
     * @param pin the user's PIN
     * @return {@link CKR} return code
     * @see NativeProvider#C_Login(long, long, byte[], long)
     */
    public static long Login(long session, long userType, byte[] pin) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_Login session=0x%08x userType=0x%08x{%s} pin=***", session, userType,
                    CKU.L2S(userType)));
        long rv = NATIVE.C_Login(session, userType, pin, baLen(pin));
        if (log.isDebugEnabled())
            log.debug(String.format("< C_Login rv=0x%08x{%s}", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * Logs a user out from a token.
     * @param session the session's handle
     * @return {@link CKR} return code
     * @see NativeProvider#C_Logout(long)
     */
    public static long Logout(long session) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_Logout session=0x%08x", session));
        long rv = NATIVE.C_Logout(session);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_Logout rv=0x%08x{%s}", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * Creates a new object.
     * @param session the session's handle
     * @param templ the objects template
     * @param object gets new object's handle
     * @return {@link CKR} return code
     * @see NativeProvider#C_CreateObject(long, Template, long, LongRef)
     */
    public static long CreateObject(long session, CKA[] templ, LongRef object) {
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(String.format("> C_CreateObject session=0x%08x\n", session));
            dumpTemplate(sb, templ);
            log.debug(sb);
        }
        long rv = NATIVE.C_CreateObject(session, templ, templLen(templ), object);
        if (log.isDebugEnabled())
            log.debug(
                    String.format("< C_CreateObject rv=0x%08x{%s} object=0x%08x", rv, CKR.L2S(rv), object.value()));
        return rv;
    }

    /**
     * Copies an object, creating a new object for the copy.
     * @param session the session's handle
     * @param object the object's handle
     * @param templ template for new object
     * @param newObject receives handle of copy
     * @return {@link CKR} return code
     * @see NativeProvider#C_CopyObject(long, long, Template, long, LongRef)
     */
    public static long CopyObject(long session, long object, CKA[] templ, LongRef newObject) {
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("> C_CopyObject session=0x%08x object=0x%08x\n", session, object));
            dumpTemplate(sb, templ);
            log.debug(sb);
        }
        long rv = NATIVE.C_CopyObject(session, object, templ, templLen(templ), newObject);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_CopyObject rv=0x%08x{%s} newObject=0x%08x", rv, CKR.L2S(rv),
                    newObject.value()));
        return rv;
    }

    /**
     * Destroys an object.
     * @param session the session's handle
     * @param object the object's handle
     * @return {@link CKR} return code
     * @see NativeProvider#C_DestroyObject(long, long)
     */
    public static long DestroyObject(long session, long object) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_DestroyObject session=0x%08x object=0x%08x", session, object));
        long rv = NATIVE.C_DestroyObject(session, object);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_DestroyObject rv=0x%08x{%s}", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * Gets the size of an object in bytes.
     * @param session the session's handle
     * @param object the object's handle
     * @param size receives the size of object
     * @return {@link CKR} return code
     * @see NativeProvider#C_GetObjectSize(long, long, LongRef)
     */
    public static long GetObjectSize(long session, long object, LongRef size) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_GetObjectSize session=0x%08x object=0x%08x", session, object));
        long rv = NATIVE.C_GetObjectSize(session, object, size);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_GetObjectSize rv=0x%08x{%s} size=%d", rv, CKR.L2S(rv), size.value()));
        return rv;
    }

    /**
     * Obtains the value of one or more object attributes.
     * @param session the session's handle
     * @param object the objects's handle
     * @param templ specifies attributes, gets values
     * @return {@link CKR} return code
     * @see NativeProvider#C_GetAttributeValue(long, long, Template, long)
     */
    public static long GetAttributeValue(long session, long object, CKA[] templ) {
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("> C_GetAttributeValue session=0x%08x object=0x%08x\n", session, object));
            dumpTemplate(sb, templ);
            log.debug(sb);
        }
        long rv = NATIVE.C_GetAttributeValue(session, object, templ, templLen(templ));
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("< C_GetAttributeValue rv=0x%08x{%s}\n", rv, CKR.L2S(rv)));
            dumpTemplate(sb, templ);
            log.debug(sb);
        }
        return rv;
    }

    /**
     * Modifies the values of one or more object attributes.
     * @param session the session's handle
     * @param object the object's handle
     * @param templ specifies attriutes and values
     * @return {@link CKR} return code
     * @see NativeProvider#C_SetAttributeValue(long, long, Template, long)
     */
    public static long SetAttributeValue(long session, long object, CKA[] templ) {
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("> C_SetAttributeValue session=0x%08x object=0x%08x\n", session, object));
            dumpTemplate(sb, templ);
            log.debug(sb);
        }
        long rv = NATIVE.C_SetAttributeValue(session, object, templ, templLen(templ));
        if (log.isDebugEnabled())
            log.debug(String.format("< C_SetAttributeValue rv=0x%08x{%s}", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * Initialises a search for token and sesion objects that match a template.
     * @param session the session's handle
     * @param templ attribute values to match
     * @return {@link CKR} return code
     * @see NativeProvider#C_FindObjectsInit(long, Template, long)
     */
    public static long FindObjectsInit(long session, CKA[] templ) {
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(String.format("> C_FindObjectsInit session=0x%08x\n", session));
            dumpTemplate(sb, templ);
            log.debug(sb);
        }
        long rv = NATIVE.C_FindObjectsInit(session, templ, templLen(templ));
        if (log.isDebugEnabled())
            log.debug(String.format("< C_FindObjectsInit rv=0x%08x{%s}", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * Continues a search for token and session objects that match a template,
     * obtaining additional object handles.
     * @param session the session's handle
     * @param found gets object handles
     * @param objectCount number of object handles returned
     * @return {@link CKR} return code
     * @see NativeProvider#C_FindObjects(long, LongArray, long, LongRef)
     */
    public static long FindObjects(long session, long[] found, LongRef objectCount) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_FindObjects session=0x%08x maxObjectCount=%d", session,
                    found != null ? found.length : 0));
        long rv = NATIVE.C_FindObjects(session, found, found == null ? 0 : found.length, objectCount);
        if (log.isDebugEnabled()) {
            int l = (int) objectCount.value();
            // only debug found[0:l]
            long[] toDisplay = found;
            if (l < found.length) {
                toDisplay = new long[l];
                System.arraycopy(found, 0, toDisplay, 0, l);
            }
            log.debug(String.format("< C_FindObjects rv=0x%08x{%s} objectCount=%d\n  %s", rv, CKR.L2S(rv),
                    objectCount.value(), Arrays.toString(toDisplay)));
        }
        return rv;
    }

    /**
     * Finishes a search for token and session objects.
     * @param session the session's handle
     * @return {@link CKR} return code
     * @see NativeProvider#C_FindObjectsFinal(long)
     */
    public static long FindObjectsFinal(long session) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_FindObjectsFinal session=0x%08x", session));
        long rv = NATIVE.C_FindObjectsFinal(session);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_FindObjectsFinal rv=0x%08x{%s}", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * Initialises an encryption operation.
     * @param session the session's handle
     * @param mechanism the encryption mechanism
     * @param key handle of encryption key
     * @return {@link CKR} return code
     * @see NativeProvider#C_EncryptInit(long, CKM, long)
     */
    public static long EncryptInit(long session, CKM mechanism, long key) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_EncryptInit session=0x%08x key=0x%08x\n  %s", session, key, mechanism));
        long rv = NATIVE.C_EncryptInit(session, mechanism, key);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_EncryptInit rv=0x%08x{%s}", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * Encrypts single-part data.
     * @param session the session's handle
     * @param data the plaintext data
     * @param encryptedData gets ciphertext
     * @param encryptedDataLen gets c-text size
     * @return {@link CKR} return code
     * @see NativeProvider#C_Encrypt(long, byte[], long, byte[], LongRef)
     */
    public static long Encrypt(long session, byte[] data, byte[] encryptedData, LongRef encryptedDataLen) {
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("> C_Encrypt session=0x%08x encryptedDataLen=%d data\n  (len=%d):\n", session,
                            encryptedDataLen.value(), data.length));
            Hex.dump(sb, data, 0, data.length, "  ", 32, false);
            log.debug(sb);
        }
        long rv = NATIVE.C_Encrypt(session, data, baLen(data), encryptedData, encryptedDataLen);
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("< C_Encrypt rv=0x%08x{%s}\n  encryptedData (len=%d):\n", rv, CKR.L2S(rv),
                            encryptedDataLen.value()));
            Hex.dump(sb, encryptedData, 0, (int) encryptedDataLen.value(), "  ", 32, false);
            log.debug(sb);
        }
        return rv;
    }

    /**
     * Continues a multiple-part encryption.
     * @param session the session's handle
     * @param part the plaintext data
     * @param encryptedPart get ciphertext
     * @param encryptedPartLen gets c-text size
     * @return {@link CKR} return code
     * @see NativeProvider#C_EncryptUpdate(long, byte[], long, byte[], LongRef)
     */
    public static long EncryptUpdate(long session, byte[] part, byte[] encryptedPart, LongRef encryptedPartLen) {
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("> C_EncryptUpdate session=0x%08x encryptedPartLen=%d\n  part (len=%d):\n",
                            session, encryptedPartLen.value(), part.length));
            Hex.dump(sb, part, 0, part.length, "  ", 32, false);
            log.debug(sb);
        }
        long rv = NATIVE.C_EncryptUpdate(session, part, baLen(part), encryptedPart, encryptedPartLen);
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("< C_EncryptUpdate rv=0x%08x{%s}\n  encryptedPart (len=%d):\n", rv, CKR.L2S(rv),
                            encryptedPartLen.value()));
            Hex.dump(sb, encryptedPart, 0, (int) encryptedPartLen.value(), "  ", 32, false);
            log.debug(sb);
        }
        return rv;
    }

    /**
     * Finishes a multiple-part encryption.
     * @param session the session's handle
     * @param lastEncryptedPart last c-text
     * @param lastEncryptedPartLen gets last size
     * @return {@link CKR} return code
     * @see NativeProvider#C_EncryptFinal(long, byte[], LongRef)
     */
    public static long EncryptFinal(long session, byte[] lastEncryptedPart, LongRef lastEncryptedPartLen) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_EncryptFinal session=0x%08x lastEncryptedPartLen=%d", session,
                    lastEncryptedPartLen.value()));
        long rv = NATIVE.C_EncryptFinal(session, lastEncryptedPart, lastEncryptedPartLen);
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("< C_EncryptFinal rv=0x%08x{%s}\n  lastEncryptedPart (len=%d):\n", rv,
                            CKR.L2S(rv), lastEncryptedPartLen.value()));
            Hex.dump(sb, lastEncryptedPart, 0, (int) lastEncryptedPartLen.value(), "  ", 32, false);
            log.debug(sb);
        }
        return rv;
    }

    /**
     * Intialises a decryption operation.
     * @param session the session's handle
     * @param mechanism the decryption mechanism
     * @param key handle of decryption key
     * @return {@link CKR} return code
     * @see NativeProvider#C_DecryptInit(long, CKM, long)
     */
    public static long DecryptInit(long session, CKM mechanism, long key) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_DecryptInit session=0x%08x key=0x%08x\n  %s", session, key, mechanism));
        long rv = NATIVE.C_DecryptInit(session, mechanism, key);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_DecryptInit rv=0x%08x{%s}", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * Decrypts encrypted data in a single part.
     * @param session the session's handle
     * @param encryptedData cipertext
     * @param data gets plaintext
     * @param dataLen gets p-text size
     * @return {@link CKR} return code
     * @see NativeProvider#C_Decrypt(long, byte[], long, byte[], LongRef)
     */
    public static long Decrypt(long session, byte[] encryptedData, byte[] data, LongRef dataLen) {
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("> C_Decrypt session=0x%08x dataLen=%d\n encryptedData (len=%d):\n", session,
                            dataLen.value(), encryptedData.length));
            Hex.dump(sb, encryptedData, 0, encryptedData.length, "  ", 32, false);
            log.debug(sb);
        }
        long rv = NATIVE.C_Decrypt(session, encryptedData, baLen(encryptedData), data, dataLen);
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(String.format("< C_Decrypt rv=0x%08x{%s}\n  data (len=%d):\n", rv,
                    CKR.L2S(rv), dataLen.value()));
            Hex.dump(sb, data, 0, (int) dataLen.value(), "  ", 32, false);
            log.debug(sb);
        }
        return rv;
    }

    /**
     * Continues a multiple-part decryption.
     * @param session the session's handle
     * @param encryptedPart encrypted data
     * @param data gets plaintext
     * @param dataLen get p-text size
     * @return {@link CKR} return code
     * @see NativeProvider#C_DecryptUpdate(long, byte[], long, byte[], LongRef)
     */
    public static long DecryptUpdate(long session, byte[] encryptedPart, byte[] data, LongRef dataLen) {
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("> C_DecryptUpdate session=0x%08x dataLen=%d\n  encryptedPart (len=%d):\n",
                            session, dataLen.value(), encryptedPart.length));
            Hex.dump(sb, encryptedPart, 0, encryptedPart.length, "  ", 32, false);
            log.debug(sb);
        }
        long rv = NATIVE.C_DecryptUpdate(session, encryptedPart, baLen(encryptedPart), data, dataLen);
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(String.format(
                    "< C_DecryptUpdate rv=0x%08x{%s}\n  data (len=%d):\n", rv, CKR.L2S(rv), dataLen.value()));
            Hex.dump(sb, data, 0, (int) dataLen.value(), "  ", 32, false);
            log.debug(sb);
        }
        return rv;
    }

    /**
     * Finishes a multiple-part decryption.
     * @param session the session's handle
     * @param lastPart gets plaintext
     * @param lastPartLen p-text size
     * @return {@link CKR} return code
     * @see NativeProvider#C_DecryptFinal(long, byte[], LongRef)
     */
    public static long DecryptFinal(long session, byte[] lastPart, LongRef lastPartLen) {
        if (log.isDebugEnabled())
            log.debug(
                    String.format("> C_DecryptFinal session=0x%08x lastPartLen=%d", session, lastPartLen.value()));
        long rv = NATIVE.C_DecryptFinal(session, lastPart, lastPartLen);
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("< C_DecryptFinal rv=0x%08x{%s}\n  lastPart (len=%d):\n", rv, CKR.L2S(rv),
                            lastPartLen.value()));
            Hex.dump(sb, lastPart, 0, (int) lastPartLen.value(), "  ", 32, false);
            log.debug(sb);
        }
        return rv;
    }

    /**
     * Initialises a message-digesting operation.
     * @param session the session's handle
     * @param mechanism the digesting mechanism
     * @return {@link CKR} return code
     * @see NativeProvider#C_DigestInit(long, CKM)
     */
    public static long DigestInit(long session, CKM mechanism) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_DigestInit session=0x%08x\n  %s", session, mechanism));
        long rv = NATIVE.C_DigestInit(session, mechanism);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_DigestInit rv=0x%08x{%s}", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * Digests data in a single part.
     * @param session the session's handle
     * @param data data to be digested
     * @param digest gets the message digest
     * @param digestLen gets digest length
     * @return {@link CKR} return code
     * @see NativeProvider#C_Digest(long, byte[], long, byte[], LongRef)
     */
    public static long Digest(long session, byte[] data, byte[] digest, LongRef digestLen) {
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("> C_Digest session=0x%08x digestLen=%d\n  data (len=%d):\n", session,
                            digestLen.value(), data.length));
            Hex.dump(sb, data, 0, data.length, "  ", 32, false);
            log.debug(sb);
        }
        long rv = NATIVE.C_Digest(session, data, baLen(data), digest, digestLen);
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(String.format("< C_Digest rv=0x%08x{%s}\n  digest (len=%d):\n", rv,
                    CKR.L2S(rv), digestLen.value()));
            Hex.dump(sb, digest, 0, (int) digestLen.value(), "  ", 32, false);
            log.debug(sb);
        }
        return rv;
    }

    /**
     * Continues a multiple-part message-digesting.
     * @param session the session's handle
     * @param part data to be digested
     * @return {@link CKR} return code
     * @see NativeProvider#C_DigestUpdate(long, byte[], long)
     */
    public static long DigestUpdate(long session, byte[] part) {
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("> C_DigestUpdate session=0x%08x\n  part (len=%d):\n", session, part.length));
            Hex.dump(sb, part, 0, part.length, "  ", 32, false);
            log.debug(sb);
        }
        long rv = NATIVE.C_DigestUpdate(session, part, baLen(part));
        if (log.isDebugEnabled())
            log.debug(String.format("< C_DigestUpdate rv=0x%08x{%s}", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * Continues a multi-part message-digesting operation, by digesting
     * the value of a secret key as part of the data already digested.
     * @param session the session's handle
     * @param key secret key to digest
     * @return {@link CKR} return code
     * @see NativeProvider#C_DigestKey(long, long)
     */
    public static long DigestKey(long session, long key) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_DigestKey session=0x%08x key=0x%08x", session, key));
        long rv = NATIVE.C_DigestKey(session, key);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_DigestKey rv=0x%08x{%s}", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * Finishes a multiple-part message-digesting operation.
     * @param session the session's handle
     * @param digest gets the message digest
     * @param digestLen gets byte count of digest
     * @return {@link CKR} return code
     * @see NativeProvider#C_DigestFinal(long, byte[], LongRef)
     */
    public static long DigestFinal(long session, byte[] digest, LongRef digestLen) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_DigestFial session=0x%08x digestLen=%d", session, digestLen.value()));
        long rv = NATIVE.C_DigestFinal(session, digest, digestLen);
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(String.format(
                    "< C_DigestFinal rv=0x%08x{%s}\n  digest (len=%d):\n", rv, CKR.L2S(rv), digestLen.value()));
            Hex.dump(sb, digest, 0, (int) digestLen.value(), "  ", 32, false);
            log.debug(sb);
        }
        return rv;
    }

    /**
     * Initialises a signature (private key encryption) operation, where
     * the signature is (will be) an appendix to the data, and plaintext
     * cannot be recovered from the signature.
     * @param session the session's handle
     * @param mechanism the signature mechanism
     * @param key handle of signature key
     * @return {@link CKR} return code
     * @see NativeProvider#C_SignInit(long, CKM, long)
     */
    public static long SignInit(long session, CKM mechanism, long key) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_SignInit session=0x%08x key=0x%08x\n  %s", session, key, mechanism));
        long rv = NATIVE.C_SignInit(session, mechanism, key);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_SignInit rv=0x%08x{%s}", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * Signs (encrypts with private key) data in a single part, where the signature is (will be)
     * an appendix to the data, and plaintext canot be recovered from the signature.
     * @param session the session's handle
     * @param data the data to sign
     * @param signature gets the signature
     * @param signatureLen gets signature length
     * @return {@link CKR} return code
     * @see NativeProvider#C_Sign(long, byte[], long, byte[], LongRef)
     */
    public static long Sign(long session, byte[] data, byte[] signature, LongRef signatureLen) {
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("> C_Sign session=0x%08x signatureLen=%d\n  data (len=%d):\n", session,
                            signatureLen.value(), data.length));
            Hex.dump(sb, data, 0, data.length, "  ", 32, false);
            log.debug(sb);
        }
        long rv = NATIVE.C_Sign(session, data, baLen(data), signature, signatureLen);
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(String.format("< C_Sign rv=0x%08x{%s}\n  signature (len=%d):\n",
                    rv, CKR.L2S(rv), signatureLen.value()));
            Hex.dump(sb, signature, 0, (int) signatureLen.value(), "  ", 32, false);
            log.debug(sb);
        }
        return rv;
    }

    /**
     * Continues a multiple-part signature operation where the signature is
     * (will be) an appendix to the data, and plaintext cannot be recovered from
     * the signature.
     * @param session the session's handle
     * @param part data to sign
     * @return {@link CKR} return code
     * @see NativeProvider#C_SignUpdate(long, byte[], long)
     */
    public static long SignUpdate(long session, byte[] part) {
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("> C_SignUpdate session=0x%08x\n  part (len=%d):\n", session, part.length));
            Hex.dump(sb, part, 0, part.length, "  ", 32, false);
            log.debug(sb);
        }
        long rv = NATIVE.C_SignUpdate(session, part, baLen(part));
        if (log.isDebugEnabled())
            log.debug(String.format("< C_SignUpdate rv=0x%08x{%s}", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * Finishes a multiple-part signature operation, returning the signature.
     * @param session the session's handle
     * @param signature gets the signature
     * @param signatureLen gets signature length
     * @return {@link CKR} return code
     * @see NativeProvider#C_SignFinal(long, byte[], LongRef)
     */
    public static long SignFinal(long session, byte[] signature, LongRef signatureLen) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_SignFinal session=0x%08x signatureLen=%d", session, signatureLen.value()));
        long rv = NATIVE.C_SignFinal(session, signature, signatureLen);
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(String.format(
                    "< C_SignFinal rv=0x%08x{%s}\n  signature (len=%d):\n", rv, CKR.L2S(rv), signatureLen.value()));
            Hex.dump(sb, signature, 0, (int) signatureLen.value(), "  ", 32, false);
            log.debug(sb);
        }
        return rv;
    }

    /**
     * Initialises a signature operation, where the data can be recovered from the signature.
     * @param session the session's handle
     * @param mechanism the signature mechanism
     * @param key handle f the signature key
     * @return {@link CKR} return code
     * @see NativeProvider#C_SignRecoverInit(long, CKM, long)
     */
    public static long SignRecoverInit(long session, CKM mechanism, long key) {
        if (log.isDebugEnabled())
            log.debug(
                    String.format("> C_SignRecoverInit session=0x%08x key=0x%08x\n  %s", session, key, mechanism));
        long rv = NATIVE.C_SignRecoverInit(session, mechanism, key);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_SignRecoverInit rv=0x%08x{%s}", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * Signs data in a single operation, where the data can be recovered from the signature.
     * @param session the session's handle
     * @param data the data to sign
     * @param signature gets the signature
     * @param signatureLen gets signature length
     * @return {@link CKR} return code
     * @see NativeProvider#C_SignRecover(long, byte[], long, byte[], LongRef)
     */
    public static long SignRecover(long session, byte[] data, byte[] signature, LongRef signatureLen) {
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("> C_SignRecover session=0x%08x signatureLen=%d\n  data (len=%d):\n", session,
                            signatureLen.value(), data.length));
            Hex.dump(sb, data, 0, data.length, "  ", 32, false);
            log.debug(sb);
        }
        long rv = NATIVE.C_SignRecover(session, data, baLen(data), signature, signatureLen);
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("< C_SignRecover rv=0x%08x{%s}\n  signature (len=%d):\n", rv, CKR.L2S(rv),
                            signatureLen.value()));
            Hex.dump(sb, signature, 0, (int) signatureLen.value(), "  ", 32, false);
            log.debug(sb);
        }
        return rv;
    }

    /**
     * Initialises a verification operation, where the signature is an appendix to the data,
     * and plaintet cannot be recovered from the signature (e.g. DSA).
     * @param session the session's handle
     * @param mechanism the verification mechanism
     * @param key verification key
     * @return {@link CKR} return code
     * @see NativeProvider#C_VerifyInit(long, CKM, long)
     */
    public static long VerifyInit(long session, CKM mechanism, long key) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_VerifyInit session=0x%08x key=0x%08x\n  %s", session, key, mechanism));
        long rv = NATIVE.C_VerifyInit(session, mechanism, key);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_VerifyInit rv=0x%08x{%s}", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * Verifies a signature in a single-part operation, where the signature is an appendix to the data,
     * and plaintext cannot be recovered from the signature.
     * @param session the session's handle
     * @param data signed data
     * @param signature signature
     * @return {@link CKR} return code
     * @see NativeProvider#C_Verify(long, byte[], long, byte[], long)
     */
    public static long Verify(long session, byte[] data, byte[] signature) {
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("> C_Verify session=0x%08x\n  data (len=%d):\n", session, data.length));
            Hex.dump(sb, data, 0, data.length, "  ", 32, false);
            sb.append("\n  signature (len=%d):\n");
            Hex.dump(sb, signature, 0, signature.length, "  ", 32, false);
            log.debug(sb);
        }
        long rv = NATIVE.C_Verify(session, data, baLen(data), signature, baLen(signature));
        log.debug(String.format("< C_Verify rv=0x%08x{%s} ", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * Continues a multiple-part verification operation where the signature is an appendix to the data,
     * and plaintet cannot be recovered from the signature.
     * @param session the session's handle
     * @param part signed data
     * @return {@link CKR} return code
     * @see NativeProvider#C_VerifyUpdate(long, byte[], long)
     */
    public static long VerifyUpdate(long session, byte[] part) {
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("> C_VerifyUpdate session=0x%08x\n  part (len=%d):\n", session, part.length));
            Hex.dump(sb, part, 0, part.length, "  ", 32, false);
            log.debug(sb);
        }
        long rv = NATIVE.C_VerifyUpdate(session, part, baLen(part));
        if (log.isDebugEnabled())
            log.debug(String.format("< C_VerifyUpdate rv=0x%08x{%s}", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * Finishes a multiple-part verification operation, checking the signature.
     * @param session the session's handle
     * @param signature signature to verify
     * @return {@link CKR} return code
     * @see NativeProvider#C_VerifyFinal(long, byte[], long)
     */
    public static long VerifyFinal(long session, byte[] signature) {
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(String
                    .format("> C_VerifyFinal session=0x%08x\n  signature (len=%d):\n", session, signature.length));
            Hex.dump(sb, signature, 0, signature.length, "  ", 32, false);
            log.debug(sb);
        }
        long rv = NATIVE.C_VerifyFinal(session, signature, baLen(signature));
        if (log.isDebugEnabled())
            log.debug(String.format("< C_VerifyFinal rv=0x%08x{%s}", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * Initialises a signature verification operation, where the data is recovered from the signature.
     * @param session the session's handle
     * @param mechanism the verification mechanism
     * @param key verification key
     * @return {@link CKR} return code
     * @see NativeProvider#C_VerifyRecoverInit(long, CKM, long)
     */
    public static long VerifyRecoverInit(long session, CKM mechanism, long key) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_VerifyRecoverInit session=0x%08x key=0x%08x\n  %s", session, key,
                    mechanism));
        long rv = NATIVE.C_VerifyRecoverInit(session, mechanism, key);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_VerifyRecoverInit rv=0x%08x{%s}", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * Verifies a signature in a single-part operation, where the data is recovered from the signature.
     * @param session the session's handle
     * @param signature signature to verify
     * @param data gets signed data
     * @param dataLen gets signed data length
     * @return {@link CKR} return code
     * @see NativeProvider#C_VerifyRecover(long, byte[], long, byte[], LongRef)
     */
    public static long VerifyRecover(long session, byte[] signature, byte[] data, LongRef dataLen) {
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("> C_VerifyRecover session=0x%08x dataLen=%d\n  signature (len=%d):\n", session,
                            dataLen.value(), signature.length));
            Hex.dump(sb, signature, 0, signature.length, "  ", 32, false);
            log.debug(sb);
        }
        long rv = NATIVE.C_VerifyRecover(session, signature, baLen(signature), data, dataLen);
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(String.format(
                    "< C_VerifyRecover rv=0x%08x{%s}\n  data (len=%d):\n", rv, CKR.L2S(rv), dataLen.value()));
            Hex.dump(sb, data, 0, (int) dataLen.value(), "  ", 32, false);
            log.debug(sb);
        }
        return rv;
    }

    /**
     * Continues a multiple-part digesting and encryption operation.
     * @param session the session's handle
     * @param part the plaintext data
     * @param encryptedPart gets ciphertext
     * @param encryptedPartLen get c-text length
     * @return {@link CKR} return code
     * @see NativeProvider#C_DigestEncryptUpdate(long, byte[], long, byte[], long)
     */
    public static long DigestEncryptUpdate(long session, byte[] part, byte[] encryptedPart,
            LongRef encryptedPartLen) {
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("> C_DigestEncryptUpdate session=0x%08x encryptedPartLen=%d\n  part (len=%d):\n",
                            session, encryptedPartLen, part.length));
            Hex.dump(sb, part, 0, part.length, "  ", 32, false);
            log.debug(sb);
        }
        long rv = NATIVE.C_DigestEncryptUpdate(session, part, baLen(part), encryptedPart, encryptedPartLen);
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("< C_DigestEncryptUpdate rv=0x%08x{%s}\n  encryptedPart (len=%d):\n", rv,
                            CKR.L2S(rv), encryptedPartLen));
            Hex.dump(sb, encryptedPart, 0, (int) encryptedPartLen.value, "  ", 32, false);
            log.debug(sb);
        }
        return rv;
    }

    /**
     * Continues a multiple-part decryption and digesting operation.
     * @param session the session's handle
     * @param encryptedPart ciphertext
     * @param part gets plaintext
     * @param partLen gets plaintext length
     * @return {@link CKR} return code
     * @see NativeProvider#C_DecryptDigestUpdate(long, byte[], long, byte[], LongRef)
     */
    public static long DecryptDigestUpdate(long session, byte[] encryptedPart, byte[] part, LongRef partLen) {
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("> C_DecryptDigestUpdate session=0x%08x partLen=%d\n  encryptedPart (len=%d):\n",
                            session, partLen.value(), encryptedPart.length));
            Hex.dump(sb, encryptedPart, 0, encryptedPart.length, "  ", 32, false);
            log.debug(sb);
        }
        long rv = NATIVE.C_DecryptDigestUpdate(session, encryptedPart, baLen(encryptedPart), part, partLen);
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(String.format(
                    "< C_DecryptDigestUpdate rv=0x%08x{%s}\n  part (len=%d):\n", rv, CKR.L2S(rv), partLen.value()));
            Hex.dump(sb, part, 0, (int) partLen.value(), "  ", 32, false);
            log.debug(sb);
        }
        return rv;
    }

    /**
     * Continues a multiple-part signing and encryption operation.
     * @param session the session's handle
     * @param part the plaintext data
     * @param encryptedPart gets ciphertext
     * @param encryptedPartLen gets c-text length
     * @return {@link CKR} return code
     * @see NativeProvider#C_SignEncryptUpdate(long, byte[], long, byte[], LongRef)
     */
    public static long SignEncryptUpdate(long session, byte[] part, byte[] encryptedPart,
            LongRef encryptedPartLen) {
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("> C_SignEncryptUdate session=0x%08x encryptedPartLen=%d\n  part (len=%d):\n",
                            session, encryptedPartLen.value(), part.length));
            Hex.dump(sb, part, 0, part.length, "  ", 32, false);
            log.debug(sb);
        }
        long rv = NATIVE.C_SignEncryptUpdate(session, part, baLen(part), encryptedPart, encryptedPartLen);
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("< C_SignEncryptUpdate rv=0x%08x{%s}\n  encryptedPart (len=%d):\n", rv,
                            CKR.L2S(rv), encryptedPartLen.value()));
            Hex.dump(sb, encryptedPart, 0, (int) encryptedPartLen.value(), "  ", 32, false);
            log.debug(sb);
        }
        return rv;
    }

    /**
     * Continues a multiple-part decryption and verify operation.
     * @param session the session's handle
     * @param encryptedPart ciphertext
     * @param part gets plaintext
     * @param partLen gets p-text length
     * @return {@link CKR} return code
     * @see NativeProvider#C_DecryptVerifyUpdate(long, byte[], long, byte[], LongRef)
     */
    public static long DecryptVerifyUpdate(long session, byte[] encryptedPart, byte[] part, LongRef partLen) {
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("> C_DecryptVerifyUpdate session=0x%08x partLen=%d\n  encryptedPart (len=%d):\n",
                            session, partLen.value(), encryptedPart.length));
            Hex.dump(sb, encryptedPart, 0, encryptedPart.length, "  ", 32, false);
            log.debug(sb);
        }
        long rv = NATIVE.C_DecryptVerifyUpdate(session, encryptedPart, baLen(encryptedPart), part, partLen);
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(String.format(
                    "< C_DecryptVerifyUpdate rv=0x%08x{%s}\n  part (len=%d):\n", rv, CKR.L2S(rv), partLen.value()));
            Hex.dump(sb, part, 0, (int) partLen.value(), "  ", 32, false);
            log.debug(sb);
        }
        return rv;
    }

    /**
     * Generates a secret key, creating a new key.
     * @param session the session's handle
     * @param mechanism key generation mechanism
     * @param templ template for the new key
     * @param key gets handle of new key
     * @return {@link CKR} return code
     * @see NativeProvider#C_GenerateKey(long, CKM, Template, long, LongRef)
     */
    public static long GenerateKey(long session, CKM mechanism, CKA[] templ, LongRef key) {
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("> C_GenerateKey session=0x%08x %s\n", session, mechanism));
            dumpTemplate(sb, templ);
            log.debug(sb);
        }
        long rv = NATIVE.C_GenerateKey(session, mechanism, templ, templLen(templ), key);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_GenerateKey rv=0x%08x{%s} key=0x%08x", rv, CKR.L2S(rv), key.value()));
        return rv;
    }

    /**
     * Generates a public-key / private-key pair, create new key objects.
     * @param session the session's handle
     * @param mechanism key generation mechansim
     * @param publicKeyTemplate template for the new public key
     * @param privateKeyTemplate template for the new private key
     * @param publicKey gets handle of new public key
     * @param privateKey gets handle of new private key
     * @return {@link CKR} return code
     * @see NativeProvider#C_GenerateKeyPair(long, CKM, Template, long, Template, long, LongRef, LongRef)
     */
    public static long GenerateKeyPair(long session, CKM mechanism, CKA[] publicKeyTemplate,
            CKA[] privateKeyTemplate, LongRef publicKey, LongRef privateKey) {
        if (publicKey == null) {
            publicKey = new LongRef();
        }
        if (privateKey == null) {
            privateKey = new LongRef();
        }

        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("> C_GenerateKeyPair session=0x%08x\n  %s", session, mechanism));
            sb.append("\n  publicKeyTemplate:\n");
            dumpTemplate(sb, publicKeyTemplate);
            sb.append("\n  privateKeyTemplate:\n");
            dumpTemplate(sb, privateKeyTemplate);
            log.debug(sb);
        }
        long rv = NATIVE.C_GenerateKeyPair(session, mechanism, publicKeyTemplate, templLen(publicKeyTemplate),
                privateKeyTemplate, templLen(privateKeyTemplate), publicKey, privateKey);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_GenerateKeyPair rv=0x%08x{%s} publicKey=0x%08x privateKey=0x%08x", rv,
                    CKR.L2S(rv), publicKey.value(), privateKey.value()));
        return rv;
    }

    /**
     * Wraps (encrypts) a key.
     * @param session the session's handle
     * @param mechanism the wrapping mechanism
     * @param wrappingKey wrapping key
     * @param key key to be wrapped
     * @param wrappedKey gets wrapped key
     * @param wrappedKeyLen gets wrapped key length
     * @return {@link CKR} return code
     * @see NativeProvider#C_WrapKey(long, CKM, long, long, byte[], LongRef)
     */
    public static long WrapKey(long session, CKM mechanism, long wrappingKey, long key, byte[] wrappedKey,
            LongRef wrappedKeyLen) {

        if (log.isDebugEnabled())
            log.debug(String.format("> C_WrapKey session=0x%08x key=0x%08x\n  %s", session, key, mechanism));
        long rv = NATIVE.C_WrapKey(session, mechanism, wrappingKey, key, wrappedKey, wrappedKeyLen);
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(String.format(
                    "< C_WrapKey rv=0x%08x{%s}\n  wrappedKey (len=%d):\n", rv, CKR.L2S(rv), wrappedKeyLen.value()));
            Hex.dump(sb, wrappedKey, 0, (int) wrappedKeyLen.value(), "  ", 32, false);
            log.debug(sb);
        }
        return rv;
    }

    /**
     * Unwraps (decrypts) a wrapped key, creating a new key object.
     * @param session the session's handle
     * @param mechanism unwrapping mechanism
     * @param unwrappingKey unwrapping key
     * @param wrappedKey the wrapped key
     * @param templ new key template
     * @param key gets new handle
     * @return {@link CKR} return code
     * @see NativeProvider#C_UnwrapKey(long, CKM, long, byte[], long, Template, long, LongRef)
     */
    public static long UnwrapKey(long session, CKM mechanism, long unwrappingKey, byte[] wrappedKey, CKA[] templ,
            LongRef key) {

        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("> C_UnwrapKey session=0x%08x unwrappingKey=0x%08x %s\n  wrappedKey (len=%d):\n",
                            session, unwrappingKey, mechanism, wrappedKey.length));
            Hex.dump(sb, wrappedKey, 0, wrappedKey.length, "  ", 32, false);
            sb.append('\n');
            dumpTemplate(sb, templ);
            log.debug(sb);
        }
        long rv = NATIVE.C_UnwrapKey(session, mechanism, unwrappingKey, wrappedKey, baLen(wrappedKey), templ,
                templLen(templ), key);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_UnwrapKey rv=0x%08x{%s} key=0x%08x", rv, CKR.L2S(rv), key.value()));
        return rv;
    }

    /**
     * Derives a key from a base key, creating a new key object.
     * @param session the session's handle
     * @param mechanism key derivation mechanism
     * @param baseKey base key
     * @param templ new key template
     * @param key ges new handle
     * @return {@link CKR} return code
     * @see NativeProvider#C_DeriveKey(long, CKM, long, Template, long, LongRef)
     */
    public static long DeriveKey(long session, CKM mechanism, long baseKey, CKA[] templ, LongRef key) {
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("> C_DeriveKey session=0x%08x baseKey=0x%08x %s\n", session, baseKey, mechanism));
            dumpTemplate(sb, templ);
            log.debug(sb);
        }
        long rv = NATIVE.C_DeriveKey(session, mechanism, baseKey, templ, templLen(templ), key);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_DeriveKey rv=0x%08x{%s} key=0x%08x", rv, CKR.L2S(rv), key.value()));
        return rv;
    }

    /**
     * Mixes additional seed material into the token's random number generator.
     * @param session the session's handle
     * @param seed the seed material
     * @return {@link CKR} return code
     * @see NativeProvider#C_SeedRandom(long, byte[], long)
     */
    public static long SeedRandom(long session, byte[] seed) {
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("> C_SeedRandom session=0x%08x\n  seed (len=%d):\n", session, seed.length));
            Hex.dump(sb, seed, 0, seed.length, "  ", 32, false);
            log.debug(sb);
        }
        long rv = NATIVE.C_SeedRandom(session, seed, baLen(seed));
        if (log.isDebugEnabled())
            log.debug(String.format("< C_SeedRandom rv=0x%08x{%s}", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * Generates random or pseudo-random data.
     * @param session the session's handle
     * @param randomData receives the random data
     * @return {@link CKR} return code
     * @see NativeProvider#C_GenerateRandom(long, byte[], long)
     */
    public static long GenerateRandom(long session, byte[] randomData) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_GenerateRandom session=0x%08x randomLen=%d", session, randomData.length));
        long rv = NATIVE.C_GenerateRandom(session, randomData, baLen(randomData));
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder(
                    String.format("< C_GenerateRandom rv=0x%08x{%s}\n  randomData (len=%d):\n", rv, CKR.L2S(rv),
                            randomData.length));
            Hex.dump(sb, randomData, 0, randomData.length, "  ", 32, false);
            log.debug(sb);
        }
        return rv;
    }

    /**
     * In previous versions of Cryptoki, C_GetFunctionStatus obtained the status of a function running in parallel
     * with an application. Now, however, C_GetFunctionStatus is a legacy function which should simply return
     * the value CKR_FUNCTION_NOT_PARALLEL.
     * @param session the session's handle
     * @return {@link CKR} return code
     * @see NativeProvider#C_GetFunctionStatus(long)
     */
    public static long GetFunctionStatus(long session) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_GetFunctionStatus session=0x%08x", session));
        long rv = NATIVE.C_GetFunctionStatus(session);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_GetFunctionStatus rv=0x%08x{%s}", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * In previous versions of Cryptoki, C_CancelFunction cancelled a function running in parallel with an application.
     * Now, however, C_CancelFunction is a legacy function which should simply return the value
     * CKR_FUNCTION_NOT_PARALLEL.
     * @param session the session's handle
     * @return {@link CKR} return code
     * @see NativeProvider#C_CancelFunction(long)
     */
    public static long CancelFunction(long session) {
        if (log.isDebugEnabled())
            log.debug(String.format("> C_CancelFunction session=0x%08x", session));
        long rv = NATIVE.C_CancelFunction(session);
        if (log.isDebugEnabled())
            log.debug(String.format("< C_CancelFunction rv=0x%08x{%s}", rv, CKR.L2S(rv)));
        return rv;
    }

    /**
     * Return length of buf (0 if buf is null).
     * @param buf buf
     * @return length of buf (0 if buf is null)
     */
    private static int baLen(byte[] buf) {
        return buf == null ? 0 : buf.length;
    }

    /**
     * Return length of template (0 if template is null).
     * @param templ template
     * @return length of template (0 if template is null)
     */
    private static int templLen(CKA[] templ) {
        return templ == null ? 0 : templ.length;
    }

    /**
     * Dump for debug.
     * @param sb write to
     */
    private static void dumpTemplate(StringBuilder sb, CKA[] template) {
        int templateLen = templLen(template);
        sb.append("  template (size=").append(templateLen).append(')');
        for (int i = 0; i < templateLen; i++) {
            sb.append("\n  ");
            template[i].dump(sb);
        }
    }

    /**
     * Helper method.  Adds all public static final long fields in c to map, mapping field value to name.
     * @param c class
     * @return map of field value:name
     */
    public static Map<Long, String> createL2SMap(Class<?> c) {
        Map<Long, String> map = new HashMap<Long, String>();
        try {
            for (Field f : c.getDeclaredFields()) {
                // only put 'public static final long' in map
                if (f.getType() == long.class && Modifier.isPublic(f.getModifiers())
                        && Modifier.isStatic(f.getModifiers()) && Modifier.isFinal(f.getModifiers())) {
                    map.put(f.getLong(null), f.getName());
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return map;
    }

    /**
     * Helper method, Maps l to constant name, or value 'unknown %s constant 0x08x' % (ckx, l)'.
     * @param map L2S map
     * @param ckx prefix of constant type, e.g. 'CKA'
     * @param l constant value
     * @return constant name, or value 'unknown %s constant 0x08x' % (ckx, l)'.
     */
    public static String l2s(Map<Long, String> map, String ckx, long l) {
        String s = map.get(l);
        if (s != null) {
            return s;
        } else {
            return String.format("unknown %s constant 0x%08x", ckx, l);
        }
    }

    /**
     * Helper method.  String format of flags.
     * @param l2s l2s map
     * @param flags flags
     * @return string format of flags
     */
    public static String f2s(Map<Long, String> l2s, long flags) {
        StringBuilder sb = new StringBuilder();
        String sep = "";
        for (int i = 63; i >= 0; i--) {
            long mask = 1L << i;
            if ((flags & mask) != 0) {
                sb.append(sep);
                sb.append(C.l2s(l2s, "CKF", mask));
                sep = "|";
            }
        }
        return sb.toString();
    }
}