[Cryptech-Commits] [user/sra/pkcs11] 02/05: Refactor PKCS #1.5 code, add C_Verify*() functions. Tidy up and extend debug-by-printf() support, given all the fun we've been having with gdb and threads on the Novena.

git at cryptech.is git at cryptech.is
Sat Jun 27 03:26:55 UTC 2015


This is an automated email from the git hooks/post-receive script.

sra at hactrn.net pushed a commit to branch master
in repository user/sra/pkcs11.

commit e5b169b8fcec4be52ba3957a1154beaca2a14384
Author: Rob Austein <sra at hactrn.net>
Date:   Wed Jun 24 13:39:01 2015 -0400

    Refactor PKCS #1.5 code, add C_Verify*() functions.  Tidy up and
    extend debug-by-printf() support, given all the fun we've been having
    with gdb and threads on the Novena.
---
 pkcs11.c | 643 +++++++++++++++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 524 insertions(+), 119 deletions(-)

diff --git a/pkcs11.c b/pkcs11.c
index f99face..0be7489 100644
--- a/pkcs11.c
+++ b/pkcs11.c
@@ -112,7 +112,7 @@
 #endif
 
 #ifndef DEBUG_PKCS11
-#define DEBUG_PKCS11    1
+#define DEBUG_PKCS11    2
 #endif
 
 /*
@@ -188,10 +188,12 @@ typedef struct p11_session {
   sqlite3_stmt *find_query;             /* FindObject*() query state */
   int find_query_done;                  /* find_query has terminated */
   const hal_hash_descriptor_t
-  	*digest_descriptor,             /* Hash for C_Digest*() */
-    	*sign_digest_descriptor;        /* Hash for C_Sign*() */
-  CK_OBJECT_HANDLE sign_key_handle;     /* Key  for C_Sign*() */
-
+    *digest_descriptor,                 /* Hash for C_Digest*() */
+    *sign_digest_descriptor,            /* Hash for C_Sign*() */
+    *verify_digest_descriptor;          /* Hash for C_Verify*() */
+  CK_OBJECT_HANDLE
+    sign_key_handle,                    /* Key  for C_Sign*() */
+    verify_key_handle;                  /* Key  for C_Verify() */
 } p11_session_t;
 
 /*
@@ -284,6 +286,8 @@ static pid_t initialized_pid;
  * readable without significantly reducing the voodoo factor.
  */
 
+#if DEBUG_PKCS11
+
 #define lose(_ck_rv_code_)                                              \
   do {                                                                  \
     rv = (_ck_rv_code_);                                                \
@@ -291,6 +295,33 @@ static pid_t initialized_pid;
     goto fail;                                                          \
   } while (0)
 
+#else  /* DEBUG_PKCS11 */
+
+#define lose(_ck_rv_code_)                                              \
+  do {                                                                  \
+    rv = (_ck_rv_code_);                                                \
+    goto fail;                                                          \
+  } while (0)
+
+#endif  /* DEBUG_PKCS11 */
+
+/*
+ * More debug-by-printf() support.  One would like to consider this a
+ * relic of the previous millenium, but, sadly, broken debugging
+ * environments are still all too common.
+ */
+
+#if DEBUG_PKCS11 > 1
+
+#define ENTER_PUBLIC_FUNCTION(_name_) \
+  fprintf(stderr, "Call to unsupported function %s\n", #_name_)
+
+#else  /* DEBUG_PKCS11 > 1 */
+
+#define ENTER_PUBLIC_FUNCTION(_name_)
+
+#endif  /* DEBUG_PKCS11 > 1 */
+
 /*
  * Error checking for SQLite calls.
  */
@@ -1723,7 +1754,9 @@ static CK_RV p11_check_keypair_attributes_check_template_2(const p11_session_t *
 
     /* Required attribute missing from template */
     if (!forbidden_by_api && (required_by_api || !in_descriptor) && pos_in_template < 0) {
-      fprintf(stderr, "[Missing attribute 0x%lx]\n", atd->type); /* XXX */
+#if DEBUG_PKCS11
+      fprintf(stderr, "[Missing attribute 0x%lx]\n", atd->type);
+#endif
       lose(CKR_TEMPLATE_INCOMPLETE);
     }
   }
@@ -1980,6 +2013,97 @@ static CK_RV generate_keypair_rsa_pkcs(p11_session_t *session,
 }
 
 /*
+ * Construct a PKCS #1 DigestInfo object.  This requires some (very
+ * basic) ASN.1 encoding, which we perform inline.
+ */
+
+static int pkcs1_construct_digestinfo(const hal_hash_descriptor_t * const desc,
+                                      const uint8_t * const data, const size_t data_len,
+                                      uint8_t *digest_info, const size_t digest_info_len)
+{
+  uint8_t statebuf[desc->hash_state_length];
+  hal_hash_state_t state = { NULL };
+  uint8_t *d = digest_info;
+
+  /*
+   * Make sure size of output buffer is right.  Caller is responsible
+   * for supplying the right length, the check here is just paranoia.
+   *
+   * This encoder will fail if the DigestInfo object is more than
+   * 129 octets long.  Rewrite if and when we need to support
+   * digests or OIDs long enough for that to be an issue.
+   */
+
+  assert(digest_info_len == desc->digest_length + desc->digest_algorithm_id_length + 4);
+  assert(digest_info_len < 130);
+
+  *d++ = 0x30;                /* SEQUENCE */
+  *d++ = (uint8_t) (digest_info_len - 2);
+
+  memcpy(d, desc->digest_algorithm_id, desc->digest_algorithm_id_length);
+  d += desc->digest_algorithm_id_length;
+
+  *d++ = 0x04;                /* OCTET STRING */
+  *d++ = (uint8_t) desc->digest_length;
+
+  assert(digest_info + digest_info_len == d + desc->digest_length);
+
+  const int ok = (hal_check(hal_hash_initialize(desc, &state, statebuf, sizeof(statebuf))) &&
+                  hal_check(hal_hash_update(state, data, data_len))                        &&
+                  hal_check(hal_hash_finalize(state, d, desc->digest_length)));
+
+  memset(statebuf, 0, sizeof(statebuf));
+  if (!ok)
+    memset(digest_info, 0, digest_info_len);
+  return ok;
+}
+
+/*
+ * Pad an octet string with PKCS #1.5 padding for use with RSA.
+ *
+ * For the moment, this only handles type 01 encryption blocks, thus
+ * is only suitable for use with signature and verification.  If and
+ * when we add support for encryption and decryption, this function
+ * should be extended to take an argument specifying the block type
+ * and include support for generating type 02 encryption blocks.
+ * Other than the block type code, the only difference is the padding
+ * value: for type 01 it's constant (0xFF), for type 02 it should be
+ * non-zero random bytes from the CSPRNG.
+ */
+
+static int pkcs1_5_pad(const uint8_t * const data, const size_t data_len,
+                       uint8_t *block, const size_t block_len)
+{
+  assert(data != NULL && block != NULL);
+
+  /*
+   * Congregation will now please turn to RFC 2313 8.1 as we
+   * construct a PKCS #1.5 type 01 encryption block.
+   */
+
+  if (data_len > block_len - 11)
+    return 0;
+
+  block[0] = 0x00;
+  block[1] = 0x01;
+
+  /* This is where we'd use non-zero random bytes if constructing a type 02 block. */
+  memset(block + 2, 0xFF, block_len - 3 - data_len);
+
+  block[block_len - data_len - 1] = 0x00;
+  memcpy(block + block_len - data_len, data, data_len);
+
+#if DEBUG_PKCS11 > 1
+  fprintf(stderr, "[PKCS #1.5 block_len %lu data_len %lu block ", block_len, data_len);
+  for (int i = 0; i < block_len; i++)
+    fprintf(stderr, "%s%02x", i == 0 ? "" : ":", block[i]);
+  fprintf(stderr, "]\n");
+#endif
+
+  return 1;
+}
+
+/*
  * Sign a PKCS #1 DigestInfo using an RSA key and PKCS #1.5 padding.
  *
  * As explained in RFC 3447, the RSASP1 (signature generation)
@@ -1995,27 +2119,9 @@ static CK_RV sign_rsa_pkcs(hal_rsa_key_t key,
 
   assert(digest_info != NULL && signature != NULL);
 
-  /*
-   * Congregation will now please turn to RFC 2313 8.1 as we
-   * construct a PKCS #1.5 type 01 encryption block.
-   */
-
-  if (digest_info_len > signature_len - 11)
+  if (!pkcs1_5_pad(digest_info, digest_info_len, signature, signature_len))
     lose(CKR_DATA_LEN_RANGE);
 
-  signature[0] = 0x00;
-  signature[1] = 0x01;
-  memset(signature + 2, 0xFF, signature_len - 3 - digest_info_len);
-  signature[signature_len - digest_info_len - 1] = 0x00;
-  memcpy(signature + signature_len - digest_info_len, digest_info, digest_info_len);
-
-#if 0
-  fprintf(stderr, "[PKCS #1.5 signature_len %lu digest_info_len %lu block ", signature_len, digest_info_len);
-  for (int i = 0; i < signature_len; i++)
-    fprintf(stderr, "%s%02x", i == 0 ? "" : ":", signature[i]);
-  fprintf(stderr, "]\n");
-#endif
-
   if (!hal_check(hal_rsa_decrypt(key, signature, signature_len, signature, signature_len)))
     lose(CKR_FUNCTION_FAILED);
 
@@ -2026,6 +2132,46 @@ static CK_RV sign_rsa_pkcs(hal_rsa_key_t key,
   return rv;
 }
 
+/*
+ * Verify a PKCS #1.5 padded RSA signature.
+ *
+ * We don't bother decoding the ASN.1, we just generate the type 01
+ * encryption block we expect and compare it with what we got.
+ *
+ * Using constant-time comparision code for this is probably
+ * unnecessary, but it's also harmless.
+ */
+
+static CK_RV verify_rsa_pkcs(hal_rsa_key_t key,
+                             const uint8_t * const digest_info, const size_t digest_info_len,
+                             const uint8_t * const signature, const size_t signature_len)
+{
+  assert(digest_info != NULL && signature != NULL);
+
+  uint8_t expected[signature_len], received[signature_len];
+  unsigned diff = 0;
+  CK_RV rv;
+
+  if (!pkcs1_5_pad(digest_info, digest_info_len, expected, sizeof(expected)))
+    lose(CKR_DATA_LEN_RANGE);
+
+  if (!hal_check(hal_rsa_encrypt(key, signature, signature_len, received, sizeof(received))))
+    lose(CKR_FUNCTION_FAILED);
+
+  for (int i = 0; i < signature_len; i++)
+    diff |= expected[i] ^ received[i];
+
+  if (diff != 0)
+    lose(CKR_SIGNATURE_INVALID);
+
+  rv = CKR_OK;
+
+ fail:
+  memset(expected, 0, sizeof(expected));
+  memset(received, 0, sizeof(received));
+  return rv;
+}
+
 
 

 
@@ -2035,6 +2181,8 @@ static CK_RV sign_rsa_pkcs(hal_rsa_key_t key,
 
 CK_RV C_Initialize(CK_VOID_PTR pInitArgs)
 {
+  ENTER_PUBLIC_FUNCTION(C_Initialize);
+
   CK_C_INITIALIZE_ARGS_PTR a = pInitArgs;
   int initialized_sql = 0;
   CK_RV rv;
@@ -2158,6 +2306,8 @@ CK_RV C_Initialize(CK_VOID_PTR pInitArgs)
 
 CK_RV C_Finalize(CK_VOID_PTR pReserved)
 {
+  ENTER_PUBLIC_FUNCTION(C_Finalize);
+
   CK_RV rv = CKR_OK;
 
   if (pReserved != NULL)
@@ -2195,6 +2345,8 @@ CK_RV C_Finalize(CK_VOID_PTR pReserved)
 
 CK_RV C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList)
 {
+  ENTER_PUBLIC_FUNCTION(C_GetFunctionList);
+
   /*
    * Use pkcs11f.h to build dispatch vector for C_GetFunctionList().
    * This should be const, but that's not what PKCS #11 says, oh well.
@@ -2221,6 +2373,8 @@ CK_RV C_GetSlotList(CK_BBOOL tokenPresent,
                     CK_SLOT_ID_PTR pSlotList,
                     CK_ULONG_PTR pulCount)
 {
+  ENTER_PUBLIC_FUNCTION(C_GetSlotList);
+
   /*
    * We only have one slot, and it's hardwired.
    * No locking required here as long as this holds.
@@ -2243,6 +2397,8 @@ CK_RV C_GetSlotList(CK_BBOOL tokenPresent,
 CK_RV C_GetTokenInfo(CK_SLOT_ID slotID,
                      CK_TOKEN_INFO_PTR pInfo)
 {
+  ENTER_PUBLIC_FUNCTION(C_GetTokenInfo);
+
   /*
    * No locking required here as long as we're just returning constants.
    */
@@ -2338,6 +2494,8 @@ CK_RV C_OpenSession(CK_SLOT_ID slotID,
                     CK_NOTIFY Notify,
                     CK_SESSION_HANDLE_PTR phSession)
 {
+  ENTER_PUBLIC_FUNCTION(C_OpenSession);
+
   const int parallel_session = (flags & CKF_SERIAL_SESSION) == 0;
   const int read_only_session = (flags & CKF_RW_SESSION) == 0;
   p11_session_t *session = NULL;
@@ -2396,6 +2554,8 @@ CK_RV C_OpenSession(CK_SLOT_ID slotID,
 
 CK_RV C_CloseSession(CK_SESSION_HANDLE hSession)
 {
+  ENTER_PUBLIC_FUNCTION(C_CloseSession);
+
   CK_RV rv;
 
   mutex_lock_or_return_failure(p11_global_mutex);
@@ -2407,6 +2567,8 @@ CK_RV C_CloseSession(CK_SESSION_HANDLE hSession)
 
 CK_RV C_CloseAllSessions(CK_SLOT_ID slotID)
 {
+  ENTER_PUBLIC_FUNCTION(C_CloseAllSessions);
+
   if (slotID != P11_ONE_AND_ONLY_SLOT)
     return CKR_SLOT_ID_INVALID;
 
@@ -2422,6 +2584,8 @@ CK_RV C_Login(CK_SESSION_HANDLE hSession,
               CK_UTF8CHAR_PTR pPin,
               CK_ULONG ulPinLen)
 {
+  ENTER_PUBLIC_FUNCTION(C_Login);
+
   static const char pin_query_format[] =
     " SELECT pbkdf2_iterations, %s_pin, %s_pin_salt from global";
 
@@ -2559,6 +2723,8 @@ CK_RV C_Login(CK_SESSION_HANDLE hSession,
 
 CK_RV C_Logout(CK_SESSION_HANDLE hSession)
 {
+  ENTER_PUBLIC_FUNCTION(C_Logout);
+
   p11_session_t *session;
   CK_RV rv = CKR_OK;
 
@@ -2611,6 +2777,8 @@ CK_RV C_Logout(CK_SESSION_HANDLE hSession)
 CK_RV C_DestroyObject(CK_SESSION_HANDLE hSession,
                       CK_OBJECT_HANDLE hObject)
 {
+  ENTER_PUBLIC_FUNCTION(C_DestroyObject);
+
   static const char delete_object[] =
     " DELETE FROM object WHERE object_handle = ?";
 
@@ -2652,6 +2820,8 @@ CK_RV C_GetAttributeValue(CK_SESSION_HANDLE hSession,
                           CK_ATTRIBUTE_PTR pTemplate,
                           CK_ULONG ulCount)
 {
+  ENTER_PUBLIC_FUNCTION(C_GetAttributeValue);
+
   static const char select_format[] =
     " SELECT value FROM %s_attribute NATURAL JOIN object"
     " WHERE object_handle = ?1 AND type = ?2";
@@ -2746,6 +2916,8 @@ CK_RV C_FindObjectsInit(CK_SESSION_HANDLE hSession,
                         CK_ATTRIBUTE_PTR pTemplate,
                         CK_ULONG ulCount)
 {
+  ENTER_PUBLIC_FUNCTION(C_FindObjectsInit);
+
   static const char select_missing[] =
     " WITH"
     "   known AS (SELECT token_object_id FROM object WHERE token_object_id IS NOT NULL)"
@@ -2887,6 +3059,8 @@ CK_RV C_FindObjects(CK_SESSION_HANDLE hSession,
                     CK_ULONG ulMaxObjectCount,
                     CK_ULONG_PTR pulObjectCount)
 {
+  ENTER_PUBLIC_FUNCTION(C_FindObjects);
+
   p11_session_t *session;
   int i, ret = SQLITE_OK;
   CK_RV rv = CKR_OK;
@@ -2937,6 +3111,8 @@ CK_RV C_FindObjects(CK_SESSION_HANDLE hSession,
 
 CK_RV C_FindObjectsFinal(CK_SESSION_HANDLE hSession)
 {
+  ENTER_PUBLIC_FUNCTION(C_FindObjectsFinal);
+
   static const char drop_format[] =
     " DROP TABLE IF EXISTS findobjects_%lu";
 
@@ -2969,6 +3145,8 @@ CK_RV C_FindObjectsFinal(CK_SESSION_HANDLE hSession)
 CK_RV C_DigestInit(CK_SESSION_HANDLE hSession,
                    CK_MECHANISM_PTR pMechanism)
 {
+  ENTER_PUBLIC_FUNCTION(C_DigestInit);
+
   p11_session_t *session;
   CK_RV rv = CKR_OK;
 
@@ -3008,6 +3186,8 @@ CK_RV C_Digest(CK_SESSION_HANDLE hSession,
                CK_BYTE_PTR pDigest,
                CK_ULONG_PTR pulDigestLen)
 {
+  ENTER_PUBLIC_FUNCTION(C_Digest);
+
   p11_session_t *session;
   CK_RV rv = CKR_OK;
 
@@ -3055,6 +3235,8 @@ CK_RV C_SignInit(CK_SESSION_HANDLE hSession,
                  CK_MECHANISM_PTR pMechanism,
                  CK_OBJECT_HANDLE hKey)
 {
+  ENTER_PUBLIC_FUNCTION(C_SignInit);
+
   p11_session_t *session;
   CK_RV rv = CKR_OK;
 
@@ -3104,6 +3286,8 @@ CK_RV C_Sign(CK_SESSION_HANDLE hSession,
              CK_BYTE_PTR pSignature,
              CK_ULONG_PTR pulSignatureLen)
 {
+  ENTER_PUBLIC_FUNCTION(C_Sign);
+
   uint8_t keybuf[hal_rsa_key_t_size];
   hal_rsa_key_t key = { NULL };
   p11_session_t *session;
@@ -3154,57 +3338,157 @@ CK_RV C_Sign(CK_SESSION_HANDLE hSession,
     /*
      * Caller wanted a hash-and-sign operation.  We need to hash the
      * caller's data and construct a DigestInfo SEQUENCE.
-     *
-     * This probably ought to be a library function somewhere.
      */
 
-    const hal_hash_descriptor_t *desc = session->sign_digest_descriptor;
-    uint8_t digest_info[desc->digest_length + desc->digest_algorithm_id_length + 4];
-    uint8_t statebuf[session->digest_descriptor->hash_state_length];
-    hal_hash_state_t state = { NULL };
-    uint8_t *d = digest_info;
+    uint8_t digest_info[session->sign_digest_descriptor->digest_length + 4 +
+                        session->sign_digest_descriptor->digest_algorithm_id_length];
+
+    if (!pkcs1_construct_digestinfo(session->sign_digest_descriptor,
+                                    pData, ulDataLen, digest_info, sizeof(digest_info)))
+      lose(CKR_FUNCTION_FAILED);
 
+    rv = sign_rsa_pkcs(key, digest_info, sizeof(digest_info), pSignature, signature_len);
+    memset(digest_info, 0, sizeof(digest_info));
+    if (rv != CKR_OK)
+      goto fail;
+  }
+
+  else {
     /*
-     * This encoder will fail if the DigestInfo object is more than
-     * 129 octets long.  Rewrite if and when we need to support
-     * digests or OIDs long enough for that to be an issue.
+     * Caller wanted a pure-signature operation.  We assume that the
+     * input is a valid DigestInfo SEQUENCE: since we've never seen
+     * the original plaintext, we can't check the hash, thus there's
+     * little point in checking the ASN.1 structure.
      */
-    assert(sizeof(digest_info) < 130);
 
-    *d++ = 0x30;                /* SEQUENCE */
-    *d++ = (uint8_t) (sizeof(digest_info) - 2);
+    if ((rv = sign_rsa_pkcs(key, pData, ulDataLen, pSignature, signature_len)) != CKR_OK)
+      goto fail;
+  }
+
+  rv = CKR_OK;                  /* Fall through */
+
+ fail:
+
+  if (session != NULL) {
+    session->sign_key_handle = CK_INVALID_HANDLE;
+    session->sign_digest_descriptor = NULL;
+  }
+
+  hal_rsa_key_clear(key);
+
+  mutex_unlock_return_with_rv(rv, p11_global_mutex);
+}
+
+CK_RV C_VerifyInit(CK_SESSION_HANDLE hSession,
+                   CK_MECHANISM_PTR pMechanism,
+                   CK_OBJECT_HANDLE hKey )
+{
+  ENTER_PUBLIC_FUNCTION(C_VerifyInit);
+
+  p11_session_t *session;
+  CK_RV rv = CKR_OK;
+
+  mutex_lock_or_return_failure(p11_global_mutex);
+
+  if ((session = p11_session_find(hSession)) == NULL)
+    lose(CKR_SESSION_HANDLE_INVALID);
 
-    memcpy(d, desc->digest_algorithm_id, desc->digest_algorithm_id_length);
-    d += desc->digest_algorithm_id_length;
+  if (pMechanism == NULL)
+    lose(CKR_ARGUMENTS_BAD);
 
-    *d++ = 0x04;                /* OCTET STRING */
-    *d++ = (uint8_t) desc->digest_length;
+  if (session->verify_key_handle != CK_INVALID_HANDLE || session->verify_digest_descriptor != NULL)
+    lose(CKR_OPERATION_ACTIVE);
 
-    assert(digest_info + sizeof(digest_info) == d + desc->digest_length);
+  if ((rv = p11_object_check_rights(session, hKey, p11_object_access_read)) != CKR_OK)
+    goto fail;
 
-    if (!hal_check(hal_hash_initialize(desc, &state, statebuf, sizeof(statebuf))) ||
-        !hal_check(hal_hash_update(state, pData, ulDataLen))                      ||
-        !hal_check(hal_hash_finalize(state, d, desc->digest_length))) {
-      memset(digest_info, 0, sizeof(digest_info));
+  /*
+   * Will need to check key algorithm type here once we add support
+   * for signature algorithms other than RSA.
+   */
+
+  session->verify_key_handle = hKey;
+
+  switch (pMechanism->mechanism) {
+  case CKM_RSA_PKCS:            session->verify_digest_descriptor = NULL;               break;
+  case CKM_SHA1_RSA_PKCS:       session->verify_digest_descriptor = hal_hash_sha1;      break;
+  case CKM_SHA256_RSA_PKCS:     session->verify_digest_descriptor = hal_hash_sha256;    break;
+  case CKM_SHA384_RSA_PKCS:     session->verify_digest_descriptor = hal_hash_sha384;    break;
+  case CKM_SHA512_RSA_PKCS:     session->verify_digest_descriptor = hal_hash_sha512;	break;
+  default:                      return CKR_MECHANISM_INVALID;
+  }
+
+  return mutex_unlock(p11_global_mutex);
+
+ fail:
+  if (session != NULL) {
+    session->verify_key_handle = CK_INVALID_HANDLE;
+    session->verify_digest_descriptor = NULL;
+  }
+  mutex_unlock_return_with_rv(rv, p11_global_mutex);
+}
+
+CK_RV C_Verify(CK_SESSION_HANDLE hSession,
+               CK_BYTE_PTR pData,
+               CK_ULONG ulDataLen,
+               CK_BYTE_PTR pSignature,
+               CK_ULONG ulSignatureLen)
+{
+  ENTER_PUBLIC_FUNCTION(C_Verify);
+
+  uint8_t keybuf[hal_rsa_key_t_size];
+  hal_rsa_key_t key = { NULL };
+  p11_session_t *session;
+  CK_RV rv;
+
+  mutex_lock_or_return_failure(p11_global_mutex);
+
+  if ((session = p11_session_find(hSession)) == NULL)
+    lose(CKR_SESSION_HANDLE_INVALID);
+
+  if (pData == NULL)
+    lose(CKR_ARGUMENTS_BAD);
+
+  if (session->verify_key_handle == CK_INVALID_HANDLE)
+    lose(CKR_OPERATION_NOT_INITIALIZED);
+
+  /*
+   * From here down this function is RSA-specific, and will need
+   * rewriting when we add support for other algorithms.
+   */
+
+  if (!p11_object_get_rsa_private_key(session->verify_key_handle,
+                                      &key, keybuf, sizeof(keybuf)))
+    lose(CKR_FUNCTION_FAILED);
+
+  if (session->verify_digest_descriptor != NULL) {
+    /*
+     * Caller wanted a hash-and-verify operation.  We need to hash the
+     * caller's data and construct a DigestInfo SEQUENCE.
+     */
+
+    uint8_t digest_info[session->verify_digest_descriptor->digest_length + 4 +
+                        session->verify_digest_descriptor->digest_algorithm_id_length];
+
+    if (!pkcs1_construct_digestinfo(session->verify_digest_descriptor,
+                                    pData, ulDataLen, digest_info, sizeof(digest_info)))
       lose(CKR_FUNCTION_FAILED);
-    }
 
-    rv = sign_rsa_pkcs(key, digest_info, sizeof(digest_info), pSignature, signature_len);
+    rv = verify_rsa_pkcs(key, digest_info, sizeof(digest_info), pSignature, ulSignatureLen);
     memset(digest_info, 0, sizeof(digest_info));
     if (rv != CKR_OK)
       goto fail;
   }
 
   else {
-
     /*
-     * Caller wanted a pure-signature operation.  We assume that input
-     * is a valid DigestInfo SEQUENCE: we've never seen the original
-     * plaintext, thus can't check the hash, so there's not much point
-     * in checking the ASN.1 structure.
+     * Caller wanted a pure-verification operation.  We assume that
+     * the input is a valid DigestInfo SEQUENCE: since we've never
+     * seen the original plaintext, we can't check the hash, thus
+     * there's little point in checking the ASN.1 structure.
      */
 
-    if ((rv = sign_rsa_pkcs(key, pData, ulDataLen, pSignature, signature_len)) != CKR_OK)
+    if ((rv = verify_rsa_pkcs(key, pData, ulDataLen, pSignature, ulSignatureLen)) != CKR_OK)
       goto fail;
   }
 
@@ -3213,8 +3497,8 @@ CK_RV C_Sign(CK_SESSION_HANDLE hSession,
  fail:
 
   if (session != NULL) {
-    session->sign_key_handle = CK_INVALID_HANDLE;
-    session->sign_digest_descriptor = NULL;
+    session->verify_key_handle = CK_INVALID_HANDLE;
+    session->verify_digest_descriptor = NULL;
   }
 
   hal_rsa_key_clear(key);
@@ -3238,6 +3522,8 @@ CK_RV C_GenerateKeyPair(CK_SESSION_HANDLE hSession,
                         CK_OBJECT_HANDLE_PTR phPublicKey,
                         CK_OBJECT_HANDLE_PTR phPrivateKey)
 {
+  ENTER_PUBLIC_FUNCTION(C_GenerateKeyPair);
+
   p11_session_t *session;
   CK_RV rv;
 
@@ -3272,6 +3558,8 @@ CK_RV C_GenerateRandom(CK_SESSION_HANDLE hSession,
                        CK_BYTE_PTR RandomData,
                        CK_ULONG ulRandomLen)
 {
+  ENTER_PUBLIC_FUNCTION(C_GenerateRandom);
+
   p11_session_t *session;
   CK_RV rv = CKR_OK;
 
@@ -3290,12 +3578,6 @@ CK_RV C_GenerateRandom(CK_SESSION_HANDLE hSession,
   mutex_unlock_return_with_rv(rv, p11_global_mutex);
 }
 
-

-
-/*
- * hsmbully wants additional methods, no real surprise.
- */
-
 /*
  * Supply information about a particular mechanism.  We may want a
  * more generic structure for this, for the moment, just answer the
@@ -3312,6 +3594,8 @@ CK_RV C_GetMechanismInfo(CK_SLOT_ID slotID,
                          CK_MECHANISM_TYPE type,
                          CK_MECHANISM_INFO_PTR pInfo)
 {
+  ENTER_PUBLIC_FUNCTION(C_GetMechanismInfo);
+
   /*
    * No locking here, no obvious need for it.
    */
@@ -3343,6 +3627,7 @@ CK_RV C_GetMechanismInfo(CK_SLOT_ID slotID,
   return CKR_OK;
 }
 
+
 

 
 /*
@@ -3363,223 +3648,322 @@ CK_RV C_GenerateKey(CK_SESSION_HANDLE hSession,
                     CK_ATTRIBUTE_PTR pTemplate,
                     CK_ULONG ulCount,
                     CK_OBJECT_HANDLE_PTR phKey)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_GenerateKey);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_GetInfo(CK_INFO_PTR pInfo)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_GetInfo);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_GetSlotInfo(CK_SLOT_ID slotID,
                     CK_SLOT_INFO_PTR pInfo)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_GetSlotInfo);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_GetMechanismList(CK_SLOT_ID slotID,
                          CK_MECHANISM_TYPE_PTR pMechanismList,
                          CK_ULONG_PTR pulCount)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_GetMechanismList);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_InitToken(CK_SLOT_ID slotID,
                   CK_UTF8CHAR_PTR pPin,
                   CK_ULONG ulPinLen,
                   CK_UTF8CHAR_PTR pLabel)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_InitToken);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_InitPIN(CK_SESSION_HANDLE hSession,
                 CK_UTF8CHAR_PTR pPin,
                 CK_ULONG ulPinLen)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_InitPIN);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_SetPIN(CK_SESSION_HANDLE hSession,
                CK_UTF8CHAR_PTR pOldPin,
                CK_ULONG ulOldLen,
                CK_UTF8CHAR_PTR pNewPin,
                CK_ULONG ulNewLen)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_SetPIN);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_GetSessionInfo(CK_SESSION_HANDLE hSession,
                        CK_SESSION_INFO_PTR pInfo)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_GetSessionInfo);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_GetOperationState(CK_SESSION_HANDLE hSession,
                           CK_BYTE_PTR pOperationState,
                           CK_ULONG_PTR pulOperationStateLen)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_GetOperationState);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_SetOperationState(CK_SESSION_HANDLE hSession,
                           CK_BYTE_PTR pOperationState,
                           CK_ULONG ulOperationStateLen,
                           CK_OBJECT_HANDLE hEncryptionKey,
                           CK_OBJECT_HANDLE hAuthenticationKey)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_SetOperationState);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_CreateObject(CK_SESSION_HANDLE hSession,
                      CK_ATTRIBUTE_PTR pTemplate,
                      CK_ULONG ulCount,
                      CK_OBJECT_HANDLE_PTR phObject)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_CreateObject);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_CopyObject(CK_SESSION_HANDLE hSession,
                    CK_OBJECT_HANDLE hObject,
                    CK_ATTRIBUTE_PTR pTemplate,
                    CK_ULONG ulCount,
                    CK_OBJECT_HANDLE_PTR phNewObject)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_CopyObject);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_GetObjectSize(CK_SESSION_HANDLE hSession,
                       CK_OBJECT_HANDLE hObject,
                       CK_ULONG_PTR pulSize)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_GetObjectSize);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_SetAttributeValue(CK_SESSION_HANDLE hSession,
                           CK_OBJECT_HANDLE hObject,
                           CK_ATTRIBUTE_PTR pTemplate,
                           CK_ULONG ulCount)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_SetAttributeValue);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_EncryptInit(CK_SESSION_HANDLE hSession,
                     CK_MECHANISM_PTR pMechanism,
                     CK_OBJECT_HANDLE hKey)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_EncryptInit);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_Encrypt(CK_SESSION_HANDLE hSession,
                 CK_BYTE_PTR pData,
                 CK_ULONG ulDataLen,
                 CK_BYTE_PTR pEncryptedData,
                 CK_ULONG_PTR pulEncryptedDataLen)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_Encrypt);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_EncryptUpdate(CK_SESSION_HANDLE hSession,
                       CK_BYTE_PTR pPart,
                       CK_ULONG ulPartLen,
                       CK_BYTE_PTR pEncryptedPart,
                       CK_ULONG_PTR pulEncryptedPartLen)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_EncryptUpdate);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_EncryptFinal(CK_SESSION_HANDLE hSession,
                      CK_BYTE_PTR pLastEncryptedPart,
                      CK_ULONG_PTR pulLastEncryptedPartLen)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_EncryptFinal);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_DecryptInit(CK_SESSION_HANDLE hSession,
                     CK_MECHANISM_PTR pMechanism,
                     CK_OBJECT_HANDLE hKey)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_DecryptInit);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_Decrypt(CK_SESSION_HANDLE hSession,
                 CK_BYTE_PTR pEncryptedData,
                 CK_ULONG ulEncryptedDataLen,
                 CK_BYTE_PTR pData,
                 CK_ULONG_PTR pulDataLen)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_Decrypt);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_DecryptUpdate(CK_SESSION_HANDLE hSession,
                       CK_BYTE_PTR pEncryptedPart,
                       CK_ULONG ulEncryptedPartLen,
                       CK_BYTE_PTR pPart,
                       CK_ULONG_PTR pulPartLen)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_DecryptUpdate);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_DecryptFinal(CK_SESSION_HANDLE hSession,
                      CK_BYTE_PTR pLastPart,
                      CK_ULONG_PTR pulLastPartLen)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_DecryptFinal);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_DigestUpdate(CK_SESSION_HANDLE hSession,
                      CK_BYTE_PTR pPart,
                      CK_ULONG ulPartLen)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_DigestUpdate);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_DigestKey(CK_SESSION_HANDLE hSession,
                   CK_OBJECT_HANDLE hKey)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_DigestKey);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_DigestFinal(CK_SESSION_HANDLE hSession,
                     CK_BYTE_PTR pDigest,
                     CK_ULONG_PTR pulDigestLen)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_DigestFinal);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_SignUpdate(CK_SESSION_HANDLE hSession,
                    CK_BYTE_PTR pPart,
                    CK_ULONG ulPartLen)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_SignUpdate);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_SignFinal(CK_SESSION_HANDLE hSession,
                   CK_BYTE_PTR pSignature,
                   CK_ULONG_PTR pulSignatureLen)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_SignFinal);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_SignRecoverInit(CK_SESSION_HANDLE hSession,
                         CK_MECHANISM_PTR pMechanism,
                         CK_OBJECT_HANDLE hKey)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_SignRecoverInit);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_SignRecover(CK_SESSION_HANDLE hSession,
                     CK_BYTE_PTR pData,
                     CK_ULONG ulDataLen,
                     CK_BYTE_PTR pSignature,
                     CK_ULONG_PTR pulSignatureLen)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
-
-CK_RV C_VerifyInit(CK_SESSION_HANDLE hSession,
-                   CK_MECHANISM_PTR pMechanism,
-                   CK_OBJECT_HANDLE hKey )
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
-
-CK_RV C_Verify(CK_SESSION_HANDLE hSession,
-               CK_BYTE_PTR pData,
-               CK_ULONG ulDataLen,
-               CK_BYTE_PTR pSignature,
-               CK_ULONG ulSignatureLen)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_SignRecover);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_VerifyUpdate(CK_SESSION_HANDLE hSession,
                      CK_BYTE_PTR pPart,
                      CK_ULONG ulPartLen)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_VerifyUpdate);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_VerifyFinal(CK_SESSION_HANDLE hSession,
                     CK_BYTE_PTR pSignature,
                     CK_ULONG ulSignatureLen)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_VerifyFinal);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_VerifyRecoverInit(CK_SESSION_HANDLE hSession,
                           CK_MECHANISM_PTR pMechanism,
                           CK_OBJECT_HANDLE hKey)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_VerifyRecoverInit);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_VerifyRecover(CK_SESSION_HANDLE hSession,
                       CK_BYTE_PTR pSignature,
                       CK_ULONG ulSignatureLen,
                       CK_BYTE_PTR pData,
                       CK_ULONG_PTR pulDataLen)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_VerifyRecover);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_DigestEncryptUpdate(CK_SESSION_HANDLE hSession,
                             CK_BYTE_PTR pPart,
                             CK_ULONG ulPartLen,
                             CK_BYTE_PTR pEncryptedPart,
                             CK_ULONG_PTR pulEncryptedPartLen)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_DigestEncryptUpdate);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_DecryptDigestUpdate(CK_SESSION_HANDLE hSession,
                             CK_BYTE_PTR pEncryptedPart,
                             CK_ULONG ulEncryptedPartLen,
                             CK_BYTE_PTR pPart,
                             CK_ULONG_PTR pulPartLen)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_DecryptDigestUpdate);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_SignEncryptUpdate(CK_SESSION_HANDLE hSession,
                           CK_BYTE_PTR pPart,
                           CK_ULONG ulPartLen,
                           CK_BYTE_PTR pEncryptedPart,
                           CK_ULONG_PTR pulEncryptedPartLen)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_SignEncryptUpdate);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_DecryptVerifyUpdate(CK_SESSION_HANDLE hSession,
                             CK_BYTE_PTR pEncryptedPart,
                             CK_ULONG ulEncryptedPartLen,
                             CK_BYTE_PTR pPart,
                             CK_ULONG_PTR pulPartLen)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_DecryptVerifyUpdate);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_WrapKey(CK_SESSION_HANDLE hSession,
                 CK_MECHANISM_PTR pMechanism,
@@ -3587,7 +3971,10 @@ CK_RV C_WrapKey(CK_SESSION_HANDLE hSession,
                 CK_OBJECT_HANDLE hKey,
                 CK_BYTE_PTR pWrappedKey,
                 CK_ULONG_PTR pulWrappedKeyLen)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_WrapKey);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_UnwrapKey(CK_SESSION_HANDLE hSession,
                   CK_MECHANISM_PTR pMechanism,
@@ -3597,7 +3984,10 @@ CK_RV C_UnwrapKey(CK_SESSION_HANDLE hSession,
                   CK_ATTRIBUTE_PTR pTemplate,
                   CK_ULONG ulAttributeCount,
                   CK_OBJECT_HANDLE_PTR phKey)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_UnwrapKey);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_DeriveKey(CK_SESSION_HANDLE hSession,
                   CK_MECHANISM_PTR pMechanism,
@@ -3605,23 +3995,38 @@ CK_RV C_DeriveKey(CK_SESSION_HANDLE hSession,
                   CK_ATTRIBUTE_PTR pTemplate,
                   CK_ULONG ulAttributeCount,
                   CK_OBJECT_HANDLE_PTR phKey)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_DeriveKey);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_SeedRandom(CK_SESSION_HANDLE hSession,
                    CK_BYTE_PTR pSeed,
                    CK_ULONG ulSeedLen)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_SeedRandom);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_GetFunctionStatus(CK_SESSION_HANDLE hSession)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_GetFunctionStatus);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_CancelFunction(CK_SESSION_HANDLE hSession)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_CancelFunction);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 CK_RV C_WaitForSlotEvent(CK_FLAGS flags,
                          CK_SLOT_ID_PTR pSlot,
                          CK_VOID_PTR pRserved)
-{ return CKR_FUNCTION_NOT_SUPPORTED; }
+{
+  ENTER_PUBLIC_FUNCTION(C_WaitForSlotEvent);
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
 
 /*
  * "Any programmer who fails to comply with the standard naming, formatting,



More information about the Commits mailing list