/* gck-session.h - the GObject PKCS#11 wrapper library Copyright (C) 2008, Stefan Walter The Gnome Keyring Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The Gnome Keyring Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the Gnome Library; see the file COPYING.LIB. If not, see . Author: Stef Walter */ #include "config.h" #include "gck.h" #include "gck-private.h" #include "gck/gck-marshal.h" #include #include /** * GckSession: * * Represents an open PKCS11 session. * * Before performing any PKCS11 operations, a session must be opened. This is * analogous to an open database handle, or a file handle. */ /** * GckSessionOptions: * @GCK_SESSION_READ_ONLY: Open session as read only * @GCK_SESSION_READ_WRITE: Open sessions as read/write * @GCK_SESSION_LOGIN_USER: Login as user on new sessions * @GCK_SESSION_AUTHENTICATE: Authenticate as necessary * * Options for creating sessions. */ /** * GckMechanism: * @type: The mechanism type * @parameter: Mechanism specific data. * @n_parameter: Length of mechanism specific data. * * Represents a mechanism used with crypto operations. */ enum { DISCARD_HANDLE, LAST_SIGNAL }; enum { PROP_0, PROP_MODULE, PROP_HANDLE, PROP_INTERACTION, PROP_SLOT, PROP_OPTIONS, PROP_OPENING_FLAGS, PROP_APP_DATA }; typedef struct { /* Not modified after construct/init */ GckSlot *slot; CK_SESSION_HANDLE handle; GckSessionOptions options; gulong opening_flags; gpointer app_data; /* Changable data locked by mutex */ GMutex mutex; GTlsInteraction *interaction; gboolean discarded; } GckSessionPrivate; static void gck_session_initable_iface (GInitableIface *iface); static void gck_session_async_initable_iface (GAsyncInitableIface *iface); G_DEFINE_TYPE_WITH_CODE (GckSession, gck_session, G_TYPE_OBJECT, G_ADD_PRIVATE (GckSession); G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gck_session_initable_iface); G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, gck_session_async_initable_iface); ); static guint signals[LAST_SIGNAL] = { 0 }; /* ---------------------------------------------------------------------------- * OBJECT */ static gboolean gck_session_real_discard_handle (GckSession *self, CK_OBJECT_HANDLE handle) { CK_FUNCTION_LIST_PTR funcs; GckModule *module; CK_RV rv; /* The default functionality, close the handle */ module = gck_session_get_module (self); g_return_val_if_fail (module != NULL, FALSE); funcs = gck_module_get_functions (module); g_return_val_if_fail (funcs, FALSE); rv = (funcs->C_CloseSession) (handle); if (rv != CKR_OK) { g_warning ("couldn't close session properly: %s", gck_message_from_rv (rv)); } g_object_unref (module); return TRUE; } static void gck_session_init (GckSession *self) { GckSessionPrivate *priv = gck_session_get_instance_private (self); g_mutex_init (&priv->mutex); } static void gck_session_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { GckSession *self = GCK_SESSION (obj); switch (prop_id) { case PROP_MODULE: g_value_take_object (value, gck_session_get_module (self)); break; case PROP_HANDLE: g_value_set_ulong (value, gck_session_get_handle (self)); break; case PROP_SLOT: g_value_take_object (value, gck_session_get_slot (self)); break; case PROP_OPTIONS: g_value_set_uint (value, gck_session_get_options (self)); break; case PROP_INTERACTION: g_value_take_object (value, gck_session_get_interaction (self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void gck_session_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) { GckSession *self = GCK_SESSION (obj); GckSessionPrivate *priv = gck_session_get_instance_private (self); /* Only valid calls are from constructor */ switch (prop_id) { case PROP_HANDLE: g_return_if_fail (!priv->handle); priv->handle = g_value_get_ulong (value); break; case PROP_INTERACTION: gck_session_set_interaction (self, g_value_get_object (value)); break; case PROP_SLOT: g_return_if_fail (!priv->slot); priv->slot = g_value_dup_object (value); g_return_if_fail (priv->slot); break; case PROP_OPTIONS: g_return_if_fail (!priv->options); priv->options = g_value_get_flags (value); break; case PROP_OPENING_FLAGS: priv->opening_flags = g_value_get_ulong (value); break; case PROP_APP_DATA: priv->app_data = g_value_get_pointer (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void gck_session_constructed (GObject *obj) { GckSession *self = GCK_SESSION (obj); GckSessionPrivate *priv = gck_session_get_instance_private (self); G_OBJECT_CLASS (gck_session_parent_class)->constructed (obj); priv->opening_flags |= CKF_SERIAL_SESSION; if (priv->options & GCK_SESSION_READ_WRITE) priv->opening_flags |= CKF_RW_SESSION; } static void gck_session_dispose (GObject *obj) { GckSession *self = GCK_SESSION (obj); GckSessionPrivate *priv = gck_session_get_instance_private (self); gboolean discard = FALSE; gboolean handled; g_return_if_fail (GCK_IS_SESSION (self)); if (priv->handle != 0) { g_mutex_lock (&priv->mutex); discard = !priv->discarded; priv->discarded = TRUE; g_mutex_unlock (&priv->mutex); } if (discard) { /* * Let the world know that we're discarding the session * handle. This allows any necessary session reuse to work. */ g_signal_emit_by_name (self, "discard-handle", priv->handle, &handled); g_return_if_fail (handled); } G_OBJECT_CLASS (gck_session_parent_class)->dispose (obj); } static void gck_session_finalize (GObject *obj) { GckSession *self = GCK_SESSION (obj); GckSessionPrivate *priv = gck_session_get_instance_private (self); g_assert (priv->handle == 0 || priv->discarded); g_clear_object (&priv->interaction); g_clear_object (&priv->slot); g_mutex_clear (&priv->mutex); G_OBJECT_CLASS (gck_session_parent_class)->finalize (obj); } static void gck_session_class_init (GckSessionClass *klass) { GObjectClass *gobject_class = (GObjectClass*)klass; gck_session_parent_class = g_type_class_peek_parent (klass); gobject_class->constructed = gck_session_constructed; gobject_class->get_property = gck_session_get_property; gobject_class->set_property = gck_session_set_property; gobject_class->dispose = gck_session_dispose; gobject_class->finalize = gck_session_finalize; klass->discard_handle = gck_session_real_discard_handle; /** * GckSession:module: * * The GckModule that this session is opened on. */ g_object_class_install_property (gobject_class, PROP_MODULE, g_param_spec_object ("module", "Module", "PKCS11 Module", GCK_TYPE_MODULE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); /** * GckSession:handle: * * The raw CK_SESSION_HANDLE handle of this session. */ g_object_class_install_property (gobject_class, PROP_HANDLE, g_param_spec_ulong ("handle", "Session Handle", "PKCS11 Session Handle", 0, G_MAXULONG, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); /** * GckSession:slot: * * The GckSlot this session is opened on. */ g_object_class_install_property (gobject_class, PROP_SLOT, g_param_spec_object ("slot", "Slot that this session uses", "PKCS11 Slot", GCK_TYPE_SLOT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); /** * GckSession:options: * * The options this session was opened with. */ g_object_class_install_property (gobject_class, PROP_OPTIONS, g_param_spec_flags ("options", "Session Options", "Session Options", GCK_TYPE_SESSION_OPTIONS, GCK_SESSION_READ_ONLY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); /** * GckSession:interaction: * * Interaction object used to ask the user for pins when opening * sessions. Used if the session_options of the enumerator have * %GCK_SESSION_LOGIN_USER */ g_object_class_install_property (gobject_class, PROP_INTERACTION, g_param_spec_object ("interaction", "Interaction", "Interaction asking for pins", G_TYPE_TLS_INTERACTION, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** * GckSession:opening-flags: * * Raw PKCS#11 flags used to open the PKCS#11 session. */ g_object_class_install_property (gobject_class, PROP_OPENING_FLAGS, g_param_spec_ulong ("opening-flags", "Opening flags", "PKCS#11 open session flags", 0, G_MAXULONG, 0, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); /** * GckSession:app-data: * * Raw PKCS#11 application data used to open the PKCS#11 session. */ g_object_class_install_property (gobject_class, PROP_APP_DATA, g_param_spec_pointer ("app-data", "App data", "PKCS#11 application data", G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); /** * GckSession::discard-handle: * @session: The session. * @handle: The handle being discarded. * * When a GckSession is being disposed of it emits this signal to allow * a session pool to pick up the handle and keep it around. * * If no signal handler claims the handle, then it is closed. * * Returns: Whether or not this handle was claimed. */ signals[DISCARD_HANDLE] = g_signal_new ("discard-handle", GCK_TYPE_SESSION, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GckSessionClass, discard_handle), g_signal_accumulator_true_handled, NULL, _gck_marshal_BOOLEAN__ULONG, G_TYPE_BOOLEAN, 1, G_TYPE_ULONG); } typedef struct OpenSession { GckArguments base; GTlsInteraction *interaction; GckSlot *slot; gulong flags; gpointer app_data; CK_NOTIFY notify; gboolean auto_login; CK_SESSION_HANDLE session; } OpenSession; static void free_open_session (OpenSession *args) { g_clear_object (&args->interaction); g_clear_object (&args->slot); g_free (args); } static CK_RV perform_open_session (OpenSession *args) { CK_RV rv = CKR_OK; /* First step, open session */ if (!args->session) { rv = (args->base.pkcs11->C_OpenSession) (args->base.handle, args->flags, args->app_data, args->notify, &args->session); } if (rv != CKR_OK || !args->auto_login) return rv; rv = _gck_session_authenticate_token (args->base.pkcs11, args->session, args->slot, args->interaction, NULL); return rv; } static gboolean gck_session_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { GckSession *self = GCK_SESSION (initable); GckSessionPrivate *priv = gck_session_get_instance_private (self); OpenSession args = { GCK_ARGUMENTS_INIT, 0, }; GckModule *module = NULL; gboolean ret = FALSE; gboolean want_login; want_login = (priv->options & GCK_SESSION_LOGIN_USER) == GCK_SESSION_LOGIN_USER; /* Already have a session setup? */ if (priv->handle && !want_login) return TRUE; g_object_ref (self); module = gck_session_get_module (self); /* Open a new session */ args.slot = priv->slot; args.app_data = priv->app_data; args.notify = NULL; args.session = priv->handle; args.flags = priv->opening_flags; args.interaction = priv->interaction ? g_object_ref (priv->interaction) : NULL; args.auto_login = want_login; if (_gck_call_sync (priv->slot, perform_open_session, NULL, &args, cancellable, error)) { priv->handle = args.session; ret = TRUE; } g_clear_object (&args.interaction); g_object_unref (module); g_object_unref (self); return ret; } static void gck_session_initable_iface (GInitableIface *iface) { iface->init = gck_session_initable_init; } static void gck_session_initable_init_async (GAsyncInitable *initable, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GckSession *self = GCK_SESSION (initable); GckSessionPrivate *priv = gck_session_get_instance_private (self); OpenSession *args; gboolean want_login; GckCall *call; g_object_ref (self); call = _gck_call_async_prep (priv->slot, perform_open_session, NULL, sizeof (*args), free_open_session); args = _gck_call_get_arguments (call); want_login = (priv->options & GCK_SESSION_LOGIN_USER) == GCK_SESSION_LOGIN_USER; args->session = priv->handle; _gck_call_async_ready (call, self, cancellable, callback, user_data); /* Already have a session setup? */ if (priv->handle && !want_login) { _gck_call_async_short (call, CKR_OK); g_object_unref (self); return; } args->app_data = priv->app_data; args->notify = NULL; args->slot = g_object_ref (priv->slot); args->interaction = priv->interaction ? g_object_ref (priv->interaction) : NULL; args->auto_login = want_login; args->flags = priv->opening_flags; _gck_call_async_go (call); g_object_unref (self); } static gboolean gck_session_initable_init_finish (GAsyncInitable *initable, GAsyncResult *result, GError **error) { GckSession *self = GCK_SESSION (initable); GckSessionPrivate *priv = gck_session_get_instance_private (self); gboolean ret = FALSE; g_object_ref (self); { OpenSession *args; if (_gck_call_basic_finish (result, error)) { args = _gck_call_async_result_arguments (result, OpenSession); priv->handle = args->session; ret = TRUE; } } g_object_unref (self); return ret; } static void gck_session_async_initable_iface (GAsyncInitableIface *iface) { iface->init_async = gck_session_initable_init_async; iface->init_finish = gck_session_initable_init_finish; } /* ---------------------------------------------------------------------------- * PUBLIC */ /** * GckSessionInfo: * @slot_id: The handle of the PKCS11 slot that this session is opened on. * @state: The user login state of the session. * @flags: Various PKCS11 flags. * @device_error: The last device error that occurred from an operation on this session. * * Information about the session. This is analogous to a CK_SESSION_INFO structure. * * When done with this structure, release it using gck_session_info_free(). */ G_DEFINE_BOXED_TYPE (GckSessionInfo, gck_session_info, gck_session_info_copy, gck_session_info_free) /** * gck_session_info_copy: * @session_info: a session info structure * * Make a new copy of a session info structure. * * Returns: (transfer full): a new copy of the session info */ GckSessionInfo * gck_session_info_copy (GckSessionInfo *session_info) { return g_memdup2 (session_info, sizeof (GckSessionInfo)); } /** * gck_session_info_free: * @session_info: Session info to free. * * Free the GckSessionInfo structure and all associated memory. **/ void gck_session_info_free (GckSessionInfo *session_info) { if (!session_info) return; g_free (session_info); } /** * gck_session_from_handle: * @slot: The slot which the session belongs to. * @session_handle: the raw PKCS#11 handle of the session * @options: Session options. Those which are used during opening a session have no effect. * * Initialize a session object from a raw PKCS#11 session handle. * Usually one would use the [method@Slot.open_session] function to * create a session. * * Returns: (transfer full): the new GckSession object **/ GckSession * gck_session_from_handle (GckSlot *slot, gulong session_handle, GckSessionOptions options) { GckSession *session; g_return_val_if_fail (GCK_IS_SLOT (slot), NULL); session = g_object_new (GCK_TYPE_SESSION, "handle", session_handle, "slot", slot, "options", options, NULL); return session; } /** * gck_session_get_handle: * @self: The session object. * * Get the raw PKCS#11 session handle from a session object. * * Return value: The raw session handle. **/ gulong gck_session_get_handle (GckSession *self) { GckSessionPrivate *priv = gck_session_get_instance_private (self); g_return_val_if_fail (GCK_IS_SESSION (self), (CK_SESSION_HANDLE)-1); return priv->handle; } /** * gck_session_get_module: * @self: The session object. * * Get the PKCS#11 module to which this session belongs. * * Returns: (transfer full): the module, which should be unreffed after use **/ GckModule * gck_session_get_module (GckSession *self) { GckSessionPrivate *priv = gck_session_get_instance_private (self); g_return_val_if_fail (GCK_IS_SESSION (self), NULL); return gck_slot_get_module (priv->slot); } /** * gck_session_get_slot: * @self: The session object. * * Get the PKCS#11 slot to which this session belongs. * * Return value: (transfer full): The slot, which should be unreffed after use. **/ GckSlot * gck_session_get_slot (GckSession *self) { GckSessionPrivate *priv = gck_session_get_instance_private (self); g_return_val_if_fail (GCK_IS_SESSION (self), NULL); g_return_val_if_fail (GCK_IS_SLOT (priv->slot), NULL); return g_object_ref (priv->slot); } /** * gck_session_get_info: * @self: The session object. * * Get information about the session. * * Returns: (transfer full): the session info. Use the gck_session_info_free() * to release when done **/ GckSessionInfo* gck_session_get_info (GckSession *self) { GckSessionPrivate *priv = gck_session_get_instance_private (self); GckSessionInfo *sessioninfo; CK_FUNCTION_LIST_PTR funcs; CK_SESSION_INFO info; GckModule *module; CK_RV rv; g_return_val_if_fail (GCK_IS_SESSION (self), NULL); module = gck_session_get_module (self); g_return_val_if_fail (GCK_IS_MODULE (module), NULL); funcs = gck_module_get_functions (module); g_return_val_if_fail (funcs, NULL); memset (&info, 0, sizeof (info)); rv = (funcs->C_GetSessionInfo) (priv->handle, &info); g_object_unref (module); if (rv != CKR_OK) { g_warning ("couldn't get session info: %s", gck_message_from_rv (rv)); return NULL; } sessioninfo = g_new0 (GckSessionInfo, 1); sessioninfo->flags = info.flags; sessioninfo->slot_id = info.slotID; sessioninfo->state = info.state; sessioninfo->device_error = info.ulDeviceError; return sessioninfo; } /** * gck_session_get_state: * @self: the session * * Get the session state. The state is the various PKCS#11 CKS_XXX flags. * * Returns: the session state */ gulong gck_session_get_state (GckSession *self) { GckSessionPrivate *priv = gck_session_get_instance_private (self); CK_FUNCTION_LIST_PTR funcs; CK_SESSION_INFO info; GckModule *module; CK_RV rv; g_return_val_if_fail (GCK_IS_SESSION (self), 0); module = gck_session_get_module (self); g_return_val_if_fail (GCK_IS_MODULE (module), 0); funcs = gck_module_get_functions (module); g_return_val_if_fail (funcs, 0); memset (&info, 0, sizeof (info)); rv = (funcs->C_GetSessionInfo) (priv->handle, &info); g_object_unref (module); if (rv != CKR_OK) { g_warning ("couldn't get session info: %s", gck_message_from_rv (rv)); return 0; } return info.state; } /** * gck_session_get_options: * @self: The session to get options from. * * Get the options this session was opened with. * * Return value: The session options. **/ GckSessionOptions gck_session_get_options (GckSession *self) { GckSessionPrivate *priv = gck_session_get_instance_private (self); g_return_val_if_fail (GCK_IS_SESSION (self), 0); return priv->options; } /** * gck_session_get_interaction: * @self: the session * * Get the interaction object set on this session, which is used to prompt * for pins and the like. * * Returns: (transfer full) (nullable): the interaction object, or %NULL */ GTlsInteraction * gck_session_get_interaction (GckSession *self) { GckSessionPrivate *priv = gck_session_get_instance_private (self); g_return_val_if_fail (GCK_IS_SESSION (self), NULL); if (priv->interaction) return g_object_ref (priv->interaction); return NULL; } /** * gck_session_set_interaction: * @self: the session * @interaction: (nullable): the interaction or %NULL * * Set the interaction object on this session, which is used to prompt for * pins and the like. */ void gck_session_set_interaction (GckSession *self, GTlsInteraction *interaction) { GckSessionPrivate *priv = gck_session_get_instance_private (self); g_return_if_fail (GCK_IS_SESSION (self)); g_return_if_fail (interaction == NULL || G_IS_TLS_INTERACTION (interaction)); g_mutex_lock (&priv->mutex); g_set_object (&priv->interaction, interaction); g_mutex_unlock (&priv->mutex); } /** * gck_session_open: * @slot: the slot to open session on * @options: session options * @interaction: (nullable): optional interaction for logins or object authentication * @cancellable: (nullable): optional cancellation object * @error: location to place error or %NULL * * Open a session on the slot. This call may block for an indefinite period. * * Returns: (transfer full): the new session */ GckSession * gck_session_open (GckSlot *slot, GckSessionOptions options, GTlsInteraction *interaction, GCancellable *cancellable, GError **error) { return g_initable_new (GCK_TYPE_SESSION, cancellable, error, "slot", slot, "interaction", interaction, "options", options, NULL); } /** * gck_session_open_async: * @slot: the slot to open session on * @options: session options * @interaction: (nullable): optional interaction for logins or object authentication * @cancellable: optional cancellation object * @callback: called when the operation completes * @user_data: data to pass to callback * * Open a session on the slot. This call will return immediately and complete * asynchronously. */ void gck_session_open_async (GckSlot *slot, GckSessionOptions options, GTlsInteraction *interaction, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_async_initable_new_async (GCK_TYPE_SESSION, G_PRIORITY_DEFAULT, cancellable, callback, user_data, "slot", slot, "interaction", interaction, "options", options, NULL); } /** * gck_session_open_finish: * @result: the result passed to the callback * @error: location to return an error or %NULL * * Get the result of an open session operation. * * Returns: (transfer full): the new session */ GckSession * gck_session_open_finish (GAsyncResult *result, GError **error) { GObject *ret; GObject *source; source = g_async_result_get_source_object (result); ret = g_async_initable_new_finish (G_ASYNC_INITABLE (source), result, error); g_object_unref (source); return ret ? GCK_SESSION (ret) : NULL; } /* --------------------------------------------------------------------------------------------- * INIT PIN */ typedef struct _InitPin { GckArguments base; guchar *pin; gsize n_pin; } InitPin; static void free_init_pin (InitPin *args) { g_free (args->pin); g_free (args); } static CK_RV perform_init_pin (InitPin *args) { return (args->base.pkcs11->C_InitPIN) (args->base.handle, (CK_BYTE_PTR)args->pin, args->n_pin); } /** * gck_session_init_pin: * @self: Initialize PIN for this session's slot. * @pin: (nullable) (array length=n_pin): the user's PIN, or %NULL for * protected authentication path * @n_pin: the length of the PIN * @cancellable: (nullable): Optional cancellation object, or %NULL. * @error: A location to return an error. * * Initialize the user's pin on this slot that this session is opened on. * According to the PKCS#11 standards, the session must be logged in with * the CKU_SO user type. * * This call may block for an indefinite period. * * Return value: Whether successful or not. **/ gboolean gck_session_init_pin (GckSession *self, const guchar *pin, gsize n_pin, GCancellable *cancellable, GError **error) { InitPin args = { GCK_ARGUMENTS_INIT, (guchar*)pin, n_pin }; return _gck_call_sync (self, perform_init_pin, NULL, &args, cancellable, error); } /** * gck_session_init_pin_async: * @self: Initialize PIN for this session's slot. * @pin: (nullable) (array length=n_pin): the user's PIN, or %NULL for protected authentication path * @n_pin: the length of the PIN * @cancellable: (nullable): Optional cancellation object, or %NULL. * @callback: Called when the operation completes. * @user_data: Data to pass to the callback. * * Initialize the user's pin on this slot that this session is opened on. * According to the PKCS#11 standards, the session must be logged in with * the `CKU_SO` user type. * * This call will return immediately and completes asynchronously. **/ void gck_session_init_pin_async (GckSession *self, const guchar *pin, gsize n_pin, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GckCall *call; InitPin* args; call = _gck_call_async_prep (self, perform_init_pin, NULL, sizeof (*args), free_init_pin); args = _gck_call_get_arguments (call); args->pin = pin && n_pin ? g_memdup2 (pin, n_pin) : NULL; args->n_pin = n_pin; _gck_call_async_ready_go (call, self, cancellable, callback, user_data); } /** * gck_session_init_pin_finish: * @self: The session. * @result: The result passed to the callback. * @error: A location to return an error. * * Get the result of initializing a user's PIN. * * Return value: Whether the operation was successful or not. **/ gboolean gck_session_init_pin_finish (GckSession *self, GAsyncResult *result, GError **error) { return _gck_call_basic_finish (result, error); } /* --------------------------------------------------------------------------------------------- * SET PIN */ typedef struct _SetPin { GckArguments base; guchar *old_pin; gsize n_old_pin; guchar *new_pin; gsize n_new_pin; } SetPin; static void free_set_pin (SetPin *args) { g_free (args->old_pin); g_free (args->new_pin); g_free (args); } static CK_RV perform_set_pin (SetPin *args) { return (args->base.pkcs11->C_SetPIN) (args->base.handle, (CK_BYTE_PTR)args->old_pin, args->n_old_pin, args->new_pin, args->n_new_pin); } /** * gck_session_set_pin: * @self: Change the PIN for this session's slot. * @old_pin: (nullable) (array length=n_old_pin): the user's old PIN, or %NULL * for protected authentication path. * @n_old_pin: The length of the PIN. * @new_pin: (nullable) (array length=n_new_pin): the user's new PIN, or %NULL * for protected authentication path * @n_new_pin: The length of the PIN. * @cancellable: (nullable): Optional cancellation object, or %NULL. * @error: A location to return an error. * * Change the user's pin on this slot that this session is opened on. * * This call may block for an indefinite period. * * Return value: Whether successful or not. **/ gboolean gck_session_set_pin (GckSession *self, const guchar *old_pin, gsize n_old_pin, const guchar *new_pin, gsize n_new_pin, GCancellable *cancellable, GError **error) { SetPin args = { GCK_ARGUMENTS_INIT, (guchar*)old_pin, n_old_pin, (guchar*)new_pin, n_new_pin }; return _gck_call_sync (self, perform_set_pin, NULL, &args, cancellable, error); } /** * gck_session_set_pin_async: * @self: Change the PIN for this session's slot. * @old_pin: (nullable) (array length=n_new_pin): the user's old PIN, or %NULL * for protected authentication path * @n_old_pin: the length of the old PIN * @new_pin: (nullable) (array length=n_new_pin): the user's new PIN, or %NULL * for protected authentication path * @n_new_pin: the length of the new PIN * @cancellable: (nullable): Optional cancellation object, or %NULL. * @callback: Called when the operation completes. * @user_data: Data to pass to the callback. * * Change the user's pin on this slot that this session is opened on. * * This call will return immediately and completes asynchronously. **/ void gck_session_set_pin_async (GckSession *self, const guchar *old_pin, gsize n_old_pin, const guchar *new_pin, gsize n_new_pin, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { SetPin* args; GckCall *call; call = _gck_call_async_prep (self, perform_set_pin, NULL, sizeof (*args), free_set_pin); args = _gck_call_get_arguments (call); args->old_pin = old_pin && n_old_pin ? g_memdup2 (old_pin, n_old_pin) : NULL; args->n_old_pin = n_old_pin; args->new_pin = new_pin && n_new_pin ? g_memdup2 (new_pin, n_new_pin) : NULL; args->n_new_pin = n_new_pin; _gck_call_async_ready_go (call, self, cancellable, callback, user_data); } /** * gck_session_set_pin_finish: * @self: The session. * @result: The result passed to the callback. * @error: A location to return an error. * * Get the result of changing a user's PIN. * * Return value: Whether the operation was successful or not. **/ gboolean gck_session_set_pin_finish (GckSession *self, GAsyncResult *result, GError **error) { return _gck_call_basic_finish (result, error); } /* --------------------------------------------------------------------------------------------- * LOGIN */ typedef struct _Login { GckArguments base; gulong user_type; guchar *pin; gsize n_pin; } Login; static void free_login (Login *args) { g_free (args->pin); g_free (args); } static CK_RV perform_login (Login *args) { return (args->base.pkcs11->C_Login) (args->base.handle, args->user_type, (CK_BYTE_PTR)args->pin, args->n_pin); } /** * gck_session_login: * @self: Log in to this session. * @user_type: The type of login user. * @pin: (nullable) (array length=n_pin): the user's PIN, or %NULL for * protected authentication path * @n_pin: The length of the PIN. * @cancellable: (nullable): Optional cancellation object, or %NULL. * @error: A location to return an error. * * Login the user on the session. This call may block for * an indefinite period. * * Return value: Whether successful or not. **/ gboolean gck_session_login (GckSession *self, gulong user_type, const guchar *pin, gsize n_pin, GCancellable *cancellable, GError **error) { Login args = { GCK_ARGUMENTS_INIT, user_type, (guchar*)pin, n_pin }; return _gck_call_sync (self, perform_login, NULL, &args, cancellable, error); } /** * gck_session_login_async: * @self: Log in to this session. * @user_type: The type of login user. * @pin: (nullable) (array length=n_pin): the user's PIN, or %NULL for * protected authentication path * @n_pin: The length of the PIN. * @cancellable: (nullable): Optional cancellation object, or %NULL. * @callback: Called when the operation completes. * @user_data: Data to pass to the callback. * * Login the user on the session. This call will return * immediately and completes asynchronously. **/ void gck_session_login_async (GckSession *self, gulong user_type, const guchar *pin, gsize n_pin, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { Login* args; GckCall *call; call = _gck_call_async_prep (self, perform_login, NULL, sizeof (*args), free_login); args = _gck_call_get_arguments (call); args->user_type = user_type; args->pin = pin && n_pin ? g_memdup2 (pin, n_pin) : NULL; args->n_pin = n_pin; _gck_call_async_ready_go (call, self, cancellable, callback, user_data); } /** * gck_session_login_finish: * @self: The session logged into. * @result: The result passed to the callback. * @error: A location to return an error. * * Get the result of a login operation. * * Return value: Whether the operation was successful or not. **/ gboolean gck_session_login_finish (GckSession *self, GAsyncResult *result, GError **error) { return _gck_call_basic_finish (result, error); } typedef struct _Interactive { GckArguments base; GTlsInteraction *interaction; GCancellable *cancellable; GckSlot *token; } Interactive; static void free_interactive (Interactive *args) { g_clear_object (&args->token); g_clear_object (&args->cancellable); g_clear_object (&args->interaction); g_free (args); } static CK_RV perform_interactive (Interactive *args) { return _gck_session_authenticate_token (args->base.pkcs11, args->base.handle, args->token, args->interaction, args->cancellable); } /** * gck_session_login_interactive: * @self: session to use for login * @user_type: the type of login user * @interaction: (nullable): interaction to request PIN when necessary * @cancellable: (nullable): optional cancellation object, or %NULL * @error: location to return an error * * Login the user on the session requesting the password interactively * when necessary. This call may block for an indefinite period. * * Return value: Whether successful or not. */ gboolean gck_session_login_interactive (GckSession *self, gulong user_type, GTlsInteraction *interaction, GCancellable *cancellable, GError **error) { GckSessionPrivate *priv = gck_session_get_instance_private (self); Interactive args = { GCK_ARGUMENTS_INIT, interaction, cancellable, NULL, }; g_return_val_if_fail (GCK_IS_SESSION (self), FALSE); g_return_val_if_fail (interaction == NULL || G_IS_TLS_INTERACTION (interaction), FALSE); g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); /* TODO: For now this is all we support */ g_return_val_if_fail (user_type == CKU_USER, FALSE); args.token = priv->slot; return _gck_call_sync (self, perform_interactive, NULL, &args, cancellable, error); } /** * gck_session_login_interactive_async: * @self: session to use for login * @user_type: the type of login user * @interaction: (nullable): interaction to request PIN when necessary * @cancellable: (nullable): optional cancellation object, or %NULL * @callback: called when the operation completes * @user_data: data to pass to the callback * * Login the user on the session prompting for passwords interactively when * necessary. This call will return immediately and completes asynchronously. **/ void gck_session_login_interactive_async (GckSession *self, gulong user_type, GTlsInteraction *interaction, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GckSessionPrivate *priv = gck_session_get_instance_private (self); Interactive* args; GckCall *call; call = _gck_call_async_prep (self, perform_interactive, NULL, sizeof (*args), free_interactive); args = _gck_call_get_arguments (call); g_return_if_fail (GCK_IS_SESSION (self)); g_return_if_fail (interaction == NULL || G_IS_TLS_INTERACTION (interaction)); g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); /* TODO: For now this is all we support */ g_return_if_fail (user_type == CKU_USER); args->cancellable = cancellable ? g_object_ref (cancellable) : NULL; args->interaction = interaction ? g_object_ref (interaction) : NULL; args->token = g_object_ref (priv->slot); _gck_call_async_ready_go (call, self, cancellable, callback, user_data); } /** * gck_session_login_interactive_finish: * @self: the session logged into * @result: the result passed to the callback * @error: location to return an error * * Get the result of a login operation. * * Return value: Whether the operation was successful or not. **/ gboolean gck_session_login_interactive_finish (GckSession *self, GAsyncResult *result, GError **error) { g_return_val_if_fail (GCK_IS_SESSION (self), FALSE); return _gck_call_basic_finish (result, error); } /* LOGOUT */ static CK_RV perform_logout (GckArguments *args) { return (args->pkcs11->C_Logout) (args->handle); } /** * gck_session_logout: * @self: Logout of this session. * @cancellable: (nullable): Optional cancellation object, or %NULL. * @error: A location to return an error. * * Log out of the session. This call may block for an indefinite period. * * Return value: Whether the logout was successful or not. **/ gboolean gck_session_logout (GckSession *self, GCancellable *cancellable, GError **error) { GckArguments args = GCK_ARGUMENTS_INIT; return _gck_call_sync (self, perform_logout, NULL, &args, cancellable, error); } /** * gck_session_logout_async: * @self: Logout of this session. * @cancellable: Optional cancellation object, or %NULL. * @callback: Called when the operation completes. * @user_data: Data to pass to the callback. * * Log out of the session. This call returns immediately and completes * asynchronously. **/ void gck_session_logout_async (GckSession *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GckCall *call; call = _gck_call_async_prep (self, perform_logout, NULL, 0, NULL); _gck_call_async_ready_go (call, self, cancellable, callback, user_data); } /** * gck_session_logout_finish: * @self: Logout of this session. * @result: The result passed to the callback. * @error: A location to return an error. * * Get the result of logging out of a session. * * Return value: Whether the logout was successful or not. **/ gboolean gck_session_logout_finish (GckSession *self, GAsyncResult *result, GError **error) { return _gck_call_basic_finish (result, error); } /* CREATE OBJECT */ typedef struct _CreateObject { GckArguments base; GckAttributes *attrs; CK_OBJECT_HANDLE object; } CreateObject; static void free_create_object (CreateObject *args) { gck_attributes_unref (args->attrs); g_free (args); } static CK_RV perform_create_object (CreateObject *args) { CK_ATTRIBUTE_PTR attrs; CK_ULONG n_attrs; CK_RV rv; attrs = _gck_attributes_commit_out (args->attrs, &n_attrs); rv = (args->base.pkcs11->C_CreateObject) (args->base.handle, attrs, n_attrs, &args->object); gchar *string = gck_attributes_to_string (args->attrs); if (rv == CKR_OK) g_debug ("created object: %s", string); else g_debug ("failed %s to create object: %s", _gck_stringize_rv (rv), string); g_free (string); return rv; } /** * gck_session_create_object: * @self: The session to create the object on. * @attrs: The attributes to create the object with. * @cancellable: (nullable): Optional cancellation object, or %NULL. * @error: A location to return an error, or %NULL. * * Create a new PKCS#11 object. This call may block for an * indefinite period. * * Returns: (transfer full): the newly created object or %NULL if an error occurred **/ GckObject * gck_session_create_object (GckSession *self, GckAttributes *attrs, GCancellable *cancellable, GError **error) { CreateObject args = { GCK_ARGUMENTS_INIT, attrs, 0 }; gboolean ret; g_return_val_if_fail (GCK_IS_SESSION (self), NULL); g_return_val_if_fail (attrs != NULL, NULL); ret = _gck_call_sync (self, perform_create_object, NULL, &args, cancellable, error); if (!ret) return NULL; return gck_object_from_handle (self, args.object); } /** * gck_session_create_object_async: * @self: The session to create the object on. * @attrs: The attributes to create the object with. * @cancellable: (nullable): Optional cancellation object or %NULL. * @callback: Called when the operation completes. * @user_data: Data to pass to the callback. * * Create a new PKCS#11 object. This call will return immediately * and complete asynchronously. * * If @attrs is a floating reference, it is consumed. **/ void gck_session_create_object_async (GckSession *self, GckAttributes *attrs, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GckCall *call; CreateObject *args; call = _gck_call_async_prep (self, perform_create_object, NULL, sizeof (*args), free_create_object); args = _gck_call_get_arguments (call); g_return_if_fail (attrs); args->attrs = gck_attributes_ref (attrs); _gck_call_async_ready_go (call, self, cancellable, callback, user_data); } /** * gck_session_create_object_finish: * @self: The session to create the object on. * @result: The result passed to the callback. * @error: A location to return an error, or %NULL. * * Get the result of creating a new PKCS#11 object. * * Return value: (transfer full): the newly created object or %NULL if an error occurred **/ GckObject * gck_session_create_object_finish (GckSession *self, GAsyncResult *result, GError **error) { CreateObject *args; args = _gck_call_async_result_arguments (result, CreateObject); if (!_gck_call_basic_finish (result, error)) return NULL; return gck_object_from_handle (self, args->object); } /* FIND OBJECTS */ typedef struct _FindObjects { GckArguments base; GckAttributes *attrs; CK_OBJECT_HANDLE_PTR objects; CK_ULONG n_objects; } FindObjects; static void free_find_objects (FindObjects *args) { gck_attributes_unref (args->attrs); g_free (args->objects); g_free (args); } static CK_RV perform_find_objects (FindObjects *args) { CK_OBJECT_HANDLE_PTR batch; CK_ULONG n_batch, n_found; CK_ATTRIBUTE_PTR attrs; CK_ULONG n_attrs; GArray *array; CK_RV rv; gchar *string = gck_attributes_to_string (args->attrs); g_debug ("matching: %s", string); g_free (string); attrs = _gck_attributes_commit_out (args->attrs, &n_attrs); rv = (args->base.pkcs11->C_FindObjectsInit) (args->base.handle, attrs, n_attrs); if (rv != CKR_OK) return rv; batch = NULL; n_found = n_batch = 4; array = g_array_new (0, 1, sizeof (CK_OBJECT_HANDLE)); do { /* * Reallocate and double in size: * - First time. * - Each time we found as many as batch */ if (n_found == n_batch) { n_batch *= 2; batch = g_realloc (batch, sizeof (CK_OBJECT_HANDLE) * n_batch); } rv = (args->base.pkcs11->C_FindObjects) (args->base.handle, batch, n_batch, &n_found); if (rv != CKR_OK) break; g_array_append_vals (array, batch, n_found); } while (n_found > 0); g_free (batch); if (rv == CKR_OK) { args->n_objects = array->len; args->objects = (CK_OBJECT_HANDLE_PTR)g_array_free (array, FALSE); rv = (args->base.pkcs11->C_FindObjectsFinal) (args->base.handle); } else { args->objects = NULL; args->n_objects = 0; g_array_free (array, TRUE); } return rv; } /** * gck_session_find_handles: * @self: the session to find objects on * @match: the attributes to match against objects * @cancellable: (nullable): optional cancellation object or %NULL * @n_handles: location to return number of handles * @error: a location to return an error or %NULL * * Find the objects matching the passed attributes. This call may * block for an indefinite period. * * Returns: (transfer full) (array length=n_handles) (nullable): a list of * the matching objects, which may be empty **/ gulong * gck_session_find_handles (GckSession *self, GckAttributes *match, GCancellable *cancellable, gulong *n_handles, GError **error) { FindObjects args = { GCK_ARGUMENTS_INIT, match, NULL, 0 }; gulong *results = NULL; g_return_val_if_fail (GCK_IS_SESSION (self), NULL); g_return_val_if_fail (match != NULL, NULL); g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); g_return_val_if_fail (n_handles != NULL, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); if (_gck_call_sync (self, perform_find_objects, NULL, &args, cancellable, error)) { results = args.objects; *n_handles = args.n_objects; args.objects = NULL; } g_free (args.objects); return results; } /** * gck_session_find_handles_async: * @self: the session to find objects on * @match: the attributes to match against the objects * @cancellable: (nullable): optional cancellation object or %NULL * @callback: called when the operation completes * @user_data: data to pass to the callback * * Find the objects matching the passed attributes. This call will * return immediately and complete asynchronously. * * If @match is a floating reference, it is consumed. **/ void gck_session_find_handles_async (GckSession *self, GckAttributes *match, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GckCall *call; FindObjects *args; g_return_if_fail (GCK_IS_SESSION (self)); g_return_if_fail (match != NULL); g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); call = _gck_call_async_prep (self, perform_find_objects, NULL, sizeof (*args), free_find_objects); args = _gck_call_get_arguments (call); args->attrs = gck_attributes_ref (match); _gck_call_async_ready_go (call, self, cancellable, callback, user_data); } /** * gck_session_find_handles_finish: * @self: the session * @result: the asynchronous result * @n_handles: location to store number of handles returned * @error: a location to return an error on failure * * Get the result of a find handles operation. * * Returns: (transfer full) (array length=n_handles) (nullable): an array of * handles that matched, which may be empty, or %NULL on failure **/ gulong * gck_session_find_handles_finish (GckSession *self, GAsyncResult *result, gulong *n_handles, GError **error) { gulong *results = NULL; FindObjects *args; g_return_val_if_fail (GCK_IS_SESSION (self), NULL); g_return_val_if_fail (n_handles != NULL, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); args = _gck_call_async_result_arguments (result, FindObjects); if (!_gck_call_basic_finish (result, error)) return NULL; *n_handles = args->n_objects; results = args->objects; args->objects = NULL; return results; } /** * gck_session_find_objects: * @self: The session to find objects on. * @match: the attributes to match * @cancellable: (nullable): Optional cancellation object or %NULL. * @error: A location to return an error or %NULL. * * Find the objects matching the passed attributes. This call may * block for an indefinite period. * * If @match is a floating reference, it is consumed. * * Returns: (transfer full) (element-type Gck.Object): a list of the matching * objects, which may be empty **/ GList * gck_session_find_objects (GckSession *self, GckAttributes *match, GCancellable *cancellable, GError **error) { GList *results = NULL; gulong *handles; gulong n_handles; g_return_val_if_fail (GCK_IS_SESSION (self), NULL); g_return_val_if_fail (match != NULL, NULL); g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); handles = gck_session_find_handles (self, match, cancellable, &n_handles, error); if (handles == NULL) return NULL; results = gck_objects_from_handle_array (self, handles, n_handles); g_free (handles); return results; } /** * gck_session_find_objects_async: * @self: The session to find objects on. * @match: The attributes to match. * @cancellable: (nullable): Optional cancellation object or %NULL. * @callback: Called when the operation completes. * @user_data: Data to pass to the callback. * * Find the objects matching the passed attributes. This call will * return immediately and complete asynchronously. * * If the @match #GckAttributes is floating, it is consumed. **/ void gck_session_find_objects_async (GckSession *self, GckAttributes *match, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (GCK_IS_SESSION (self)); g_return_if_fail (match != NULL); g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); gck_session_find_handles_async (self, match, cancellable, callback, user_data); } /** * gck_session_find_objects_finish: * @self: The session to find objects on. * @result: The attributes to match. * @error: A location to return an error. * * Get the result of a find operation. * * Returns: (transfer full) (element-type Gck.Object): a list of the matching * objects, which may be empty **/ GList * gck_session_find_objects_finish (GckSession *self, GAsyncResult *result, GError **error) { GList *results = NULL; gulong *handles; gulong n_handles; g_return_val_if_fail (GCK_IS_SESSION (self), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); handles = gck_session_find_handles_finish (self, result, &n_handles, error); if (handles == NULL) return NULL; results = gck_objects_from_handle_array (self, handles, n_handles); g_free (handles); return results; } /** * gck_session_enumerate_objects: * @self: session to enumerate objects on * @match: attributes that the objects must match, or empty for all objects * * Setup an enumerator for listing matching objects available via this session. * * This call will not block but will return an enumerator immediately. * * Returns: (transfer full): a new enumerator **/ GckEnumerator * gck_session_enumerate_objects (GckSession *session, GckAttributes *match) { GckUriData *uri_data; g_return_val_if_fail (match != NULL, NULL); uri_data = gck_uri_data_new (); uri_data->attributes = gck_attributes_ref (match); return _gck_enumerator_new_for_session (session, uri_data); } /* ----------------------------------------------------------------------------- * KEY PAIR GENERATION */ typedef struct _GenerateKeyPair { GckArguments base; GckMechanism mechanism; GckAttributes *public_attrs; GckAttributes *private_attrs; CK_OBJECT_HANDLE public_key; CK_OBJECT_HANDLE private_key; } GenerateKeyPair; static void free_generate_key_pair (GenerateKeyPair *args) { g_clear_pointer (&args->public_attrs, gck_attributes_unref); g_clear_pointer (&args->private_attrs, gck_attributes_unref); g_free (args); } static CK_RV perform_generate_key_pair (GenerateKeyPair *args) { CK_ATTRIBUTE_PTR pub_attrs, priv_attrs; CK_ULONG n_pub_attrs, n_priv_attrs; g_assert (sizeof (CK_MECHANISM) == sizeof (GckMechanism)); pub_attrs = _gck_attributes_commit_out (args->public_attrs, &n_pub_attrs); priv_attrs = _gck_attributes_commit_out (args->private_attrs, &n_priv_attrs); return (args->base.pkcs11->C_GenerateKeyPair) (args->base.handle, (CK_MECHANISM_PTR)&(args->mechanism), pub_attrs, n_pub_attrs, priv_attrs, n_priv_attrs, &args->public_key, &args->private_key); } /** * gck_session_generate_key_pair: * @self: The session to use. * @mech_type: The mechanism type to use for key generation. * @public_attrs: Additional attributes for the generated public key. * @private_attrs: Additional attributes for the generated private key. * @public_key: (optional) (out): location to return the resulting public key * @private_key: (optional) (out): location to return the resulting private key. * @cancellable: (nullable): Optional cancellation object, or %NULL. * @error: A location to return an error, or %NULL. * * Generate a new key pair of public and private keys. This call may block for * an indefinite period. * * If @public_attrs and/or @private_attrs is a floating reference, it is * consumed. * * Return value: %TRUE if the operation succeeded. **/ gboolean gck_session_generate_key_pair (GckSession *self, gulong mech_type, GckAttributes *public_attrs, GckAttributes *private_attrs, GckObject **public_key, GckObject **private_key, GCancellable *cancellable, GError **error) { GckMechanism mech = { mech_type, NULL, 0 }; return gck_session_generate_key_pair_full (self, &mech, public_attrs, private_attrs, public_key, private_key, cancellable, error); } /** * gck_session_generate_key_pair_full: * @self: The session to use. * @mechanism: The mechanism to use for key generation. * @public_attrs: Additional attributes for the generated public key. * @private_attrs: Additional attributes for the generated private key. * @public_key: (optional) (out): a location to return the resulting public key * @private_key: (optional) (out): a location to return the resulting private key * @cancellable: (nullable): Optional cancellation object, or %NULL. * @error: A location to return an error, or %NULL. * * Generate a new key pair of public and private keys. This call may block for an * indefinite period. * * Return value: %TRUE if the operation succeeded. **/ gboolean gck_session_generate_key_pair_full (GckSession *self, GckMechanism *mechanism, GckAttributes *public_attrs, GckAttributes *private_attrs, GckObject **public_key, GckObject **private_key, GCancellable *cancellable, GError **error) { GenerateKeyPair args = { GCK_ARGUMENTS_INIT, GCK_MECHANISM_EMPTY, public_attrs, private_attrs, 0, 0 }; gboolean ret; g_return_val_if_fail (GCK_IS_SESSION (self), FALSE); g_return_val_if_fail (mechanism, FALSE); g_return_val_if_fail (public_attrs, FALSE); g_return_val_if_fail (private_attrs, FALSE); /* Shallow copy of the mechanism structure */ memcpy (&args.mechanism, mechanism, sizeof (args.mechanism)); ret = _gck_call_sync (self, perform_generate_key_pair, NULL, &args, cancellable, error); if (!ret) return FALSE; if (public_key) *public_key = gck_object_from_handle (self, args.public_key); if (private_key) *private_key = gck_object_from_handle (self, args.private_key); return TRUE; } /** * gck_session_generate_key_pair_async: * @self: The session to use. * @mechanism: The mechanism to use for key generation. * @public_attrs: Additional attributes for the generated public key. * @private_attrs: Additional attributes for the generated private key. * @cancellable: (nullable): Optional cancellation object or %NULL. * @callback: Called when the operation completes. * @user_data: Data to pass to the callback. * * Generate a new key pair of public and private keys. This call will * return immediately and complete asynchronously. * * If @public_attrs and/or @private_attrs is a floating reference, it is * consumed. **/ void gck_session_generate_key_pair_async (GckSession *self, GckMechanism *mechanism, GckAttributes *public_attrs, GckAttributes *private_attrs, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GckCall *call; GenerateKeyPair *args; call = _gck_call_async_prep (self, perform_generate_key_pair, NULL, sizeof (*args), free_generate_key_pair); args = _gck_call_get_arguments (call); g_return_if_fail (GCK_IS_SESSION (self)); g_return_if_fail (mechanism); g_return_if_fail (public_attrs); g_return_if_fail (private_attrs); /* Shallow copy of the mechanism structure */ memcpy (&args->mechanism, mechanism, sizeof (args->mechanism)); args->public_attrs = gck_attributes_ref (public_attrs); args->private_attrs = gck_attributes_ref (private_attrs); _gck_call_async_ready_go (call, self, cancellable, callback, user_data); } /** * gck_session_generate_key_pair_finish: * @self: The session to use. * @result: The async result passed to the callback. * @public_key: (optional) (out): a location to return the resulting public key * @private_key: (optional) (out): a location to return the resulting private key * @error: A location to return an error. * * Get the result of a generate key pair operation. * * Return value: %TRUE if the operation succeeded. **/ gboolean gck_session_generate_key_pair_finish (GckSession *self, GAsyncResult *result, GckObject **public_key, GckObject **private_key, GError **error) { GenerateKeyPair *args; g_return_val_if_fail (GCK_IS_SESSION (self), FALSE); g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); args = _gck_call_async_result_arguments (result, GenerateKeyPair); if (!_gck_call_basic_finish (result, error)) return FALSE; if (public_key) *public_key = gck_object_from_handle (self, args->public_key); if (private_key) *private_key = gck_object_from_handle (self, args->private_key); return TRUE; } /* ----------------------------------------------------------------------------- * KEY WRAPPING */ typedef struct _WrapKey { GckArguments base; GckMechanism mechanism; CK_OBJECT_HANDLE wrapper; CK_OBJECT_HANDLE wrapped; gpointer result; gulong n_result; } WrapKey; static void free_wrap_key (WrapKey *args) { g_clear_pointer (&args->result, g_free); g_free (args); } static CK_RV perform_wrap_key (WrapKey *args) { CK_RV rv; g_assert (sizeof (CK_MECHANISM) == sizeof (GckMechanism)); /* Get the length of the result */ rv = (args->base.pkcs11->C_WrapKey) (args->base.handle, (CK_MECHANISM_PTR)&(args->mechanism), args->wrapper, args->wrapped, NULL, &args->n_result); if (rv != CKR_OK) return rv; /* And try again with a real buffer */ args->result = g_malloc0 (args->n_result); return (args->base.pkcs11->C_WrapKey) (args->base.handle, (CK_MECHANISM_PTR)&(args->mechanism), args->wrapper, args->wrapped, args->result, &args->n_result); } /** * gck_session_wrap_key: * @self: The session to use. * @wrapper: The key to use for wrapping. * @mech_type: The mechanism type to use for wrapping. * @wrapped: The key to wrap. * @n_result: A location in which to return the length of the wrapped data. * @cancellable: (nullable): A #GCancellable or %NULL * @error: A location to return an error, or %NULL. * * Wrap a key into a byte stream. This call may block for an * indefinite period. * * Returns: (transfer full) (array length=n_result): the wrapped data or %NULL * if the operation failed **/ guchar * gck_session_wrap_key (GckSession *self, GckObject *key, gulong mech_type, GckObject *wrapped, gsize *n_result, GCancellable *cancellable, GError **error) { GckMechanism mech = { mech_type, NULL, 0 }; return gck_session_wrap_key_full (self, key, &mech, wrapped, n_result, cancellable, error); } /** * gck_session_wrap_key_full: * @self: The session to use. * @wrapper: The key to use for wrapping. * @mechanism: The mechanism to use for wrapping. * @wrapped: The key to wrap. * @n_result: A location in which to return the length of the wrapped data. * @cancellable: (nullable): Optional cancellation object, or %NULL. * @error: A location to return an error, or %NULL. * * Wrap a key into a byte stream. This call may block for an * indefinite period. * * Returns: (transfer full) (array length=n_result): the wrapped data or %NULL * if the operation failed **/ guchar * gck_session_wrap_key_full (GckSession *self, GckObject *wrapper, GckMechanism *mechanism, GckObject *wrapped, gsize *n_result, GCancellable *cancellable, GError **error) { WrapKey args = { GCK_ARGUMENTS_INIT, GCK_MECHANISM_EMPTY, 0, 0, NULL, 0 }; gboolean ret; g_return_val_if_fail (GCK_IS_SESSION (self), FALSE); g_return_val_if_fail (mechanism, FALSE); g_return_val_if_fail (GCK_IS_OBJECT (wrapped), FALSE); g_return_val_if_fail (GCK_IS_OBJECT (wrapper), FALSE); g_return_val_if_fail (n_result, FALSE); /* Shallow copy of the mechanism structure */ memcpy (&args.mechanism, mechanism, sizeof (args.mechanism)); g_object_get (wrapper, "handle", &args.wrapper, NULL); g_return_val_if_fail (args.wrapper != 0, NULL); g_object_get (wrapped, "handle", &args.wrapped, NULL); g_return_val_if_fail (args.wrapped != 0, NULL); ret = _gck_call_sync (self, perform_wrap_key, NULL, &args, cancellable, error); if (!ret) return FALSE; *n_result = args.n_result; return args.result; } /** * gck_session_wrap_key_async: * @self: The session to use. * @wrapper: The key to use for wrapping. * @mechanism: The mechanism to use for wrapping. * @wrapped: The key to wrap. * @cancellable: (nullable): Optional cancellation object or %NULL. * @callback: Called when the operation completes. * @user_data: Data to pass to the callback. * * Wrap a key into a byte stream. This call will * return immediately and complete asynchronously. **/ void gck_session_wrap_key_async (GckSession *self, GckObject *key, GckMechanism *mechanism, GckObject *wrapped, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GckCall *call; WrapKey *args; call = _gck_call_async_prep (self, perform_wrap_key, NULL, sizeof (*args), free_wrap_key); args = _gck_call_get_arguments (call); g_return_if_fail (GCK_IS_SESSION (self)); g_return_if_fail (mechanism); g_return_if_fail (GCK_IS_OBJECT (wrapped)); g_return_if_fail (GCK_IS_OBJECT (key)); /* Shallow copy of the mechanism structure */ memcpy (&args->mechanism, mechanism, sizeof (args->mechanism)); g_object_get (key, "handle", &args->wrapper, NULL); g_return_if_fail (args->wrapper != 0); g_object_get (wrapped, "handle", &args->wrapped, NULL); g_return_if_fail (args->wrapped != 0); _gck_call_async_ready_go (call, self, cancellable, callback, user_data); } /** * gck_session_wrap_key_finish: * @self: The session to use. * @result: The async result passed to the callback. * @n_result: A location in which to return the length of the wrapped data. * @error: A location to return an error. * * Get the result of a wrap key operation. * * Returns: (transfer full) (array length=n_result): the wrapped data or %NULL * if the operation failed **/ guchar * gck_session_wrap_key_finish (GckSession *self, GAsyncResult *result, gsize *n_result, GError **error) { WrapKey *args; gpointer ret; g_return_val_if_fail (GCK_IS_SESSION (self), NULL); g_return_val_if_fail (n_result, NULL); args = _gck_call_async_result_arguments (result, WrapKey); if (!_gck_call_basic_finish (result, error)) return NULL; *n_result = args->n_result; args->n_result = 0; ret = args->result; args->result = NULL; return ret; } /* ----------------------------------------------------------------------------- * KEY UNWRAPPING */ typedef struct _UnwrapKey { GckArguments base; GckMechanism mechanism; GckAttributes *attrs; CK_OBJECT_HANDLE wrapper; gconstpointer input; gulong n_input; CK_OBJECT_HANDLE unwrapped; } UnwrapKey; static void free_unwrap_key (UnwrapKey *args) { g_clear_pointer (&args->attrs, gck_attributes_unref); g_free (args); } static CK_RV perform_unwrap_key (UnwrapKey *args) { CK_ATTRIBUTE_PTR attrs; CK_ULONG n_attrs; g_assert (sizeof (CK_MECHANISM) == sizeof (GckMechanism)); attrs = _gck_attributes_commit_out (args->attrs, &n_attrs); return (args->base.pkcs11->C_UnwrapKey) (args->base.handle, (CK_MECHANISM_PTR)&(args->mechanism), args->wrapper, (CK_BYTE_PTR)args->input, args->n_input, attrs, n_attrs, &args->unwrapped); } /** * gck_session_unwrap_key: * @self: The session to use. * @wrapper: The key to use for unwrapping. * @mech_type: The mechanism to use for unwrapping. * @input: (array length=n_input): the wrapped data as a byte stream * @n_input: The length of the wrapped data. * @attrs: Additional attributes for the unwrapped key. * @cancellable: (nullable): Optional cancellation object, or %NULL. * @error: A location to return an error, or %NULL. * * Unwrap a key from a byte stream. This call may block for an * indefinite period. * * Returns: (transfer full): the new unwrapped key or %NULL if the * operation failed **/ GckObject * gck_session_unwrap_key (GckSession *self, GckObject *wrapper, gulong mech_type, const guchar *input, gsize n_input, GckAttributes *attrs, GCancellable *cancellable, GError **error) { GckMechanism mech = { mech_type, NULL, 0 }; return gck_session_unwrap_key_full (self, wrapper, &mech, input, n_input, attrs, cancellable, error); } /** * gck_session_unwrap_key_full: * @self: The session to use. * @wrapper: The key to use for unwrapping. * @mechanism: The mechanism to use for unwrapping. * @input: (array length=n_input): the wrapped data as a byte stream * @n_input: The length of the wrapped data. * @attrs: Additional attributes for the unwrapped key. * @cancellable: (nullable): Optional cancellation object, or %NULL. * @error: A location to return an error, or %NULL. * * Unwrap a key from a byte stream. This call may block for an * indefinite period. * * Returns: (transfer full): the new unwrapped key or %NULL if the operation * failed **/ GckObject * gck_session_unwrap_key_full (GckSession *self, GckObject *wrapper, GckMechanism *mechanism, const guchar *input, gsize n_input, GckAttributes *attrs, GCancellable *cancellable, GError **error) { UnwrapKey args = { GCK_ARGUMENTS_INIT, GCK_MECHANISM_EMPTY, attrs, 0, input, n_input, 0 }; gboolean ret; g_return_val_if_fail (GCK_IS_SESSION (self), FALSE); g_return_val_if_fail (GCK_IS_OBJECT (wrapper), FALSE); g_return_val_if_fail (mechanism, FALSE); g_return_val_if_fail (attrs, FALSE); /* Shallow copy of the mechanism structure */ memcpy (&args.mechanism, mechanism, sizeof (args.mechanism)); g_object_get (wrapper, "handle", &args.wrapper, NULL); g_return_val_if_fail (args.wrapper != 0, NULL); ret = _gck_call_sync (self, perform_unwrap_key, NULL, &args, cancellable, error); if (!ret) return NULL; return gck_object_from_handle (self, args.unwrapped); } /** * gck_session_unwrap_key_async: * @self: The session to use. * @wrapper: The key to use for unwrapping. * @mechanism: The mechanism to use for unwrapping. * @input: (array length=n_input): the wrapped data as a byte stream * @n_input: The length of the wrapped data. * @attrs: Additional attributes for the unwrapped key. * @cancellable: (nullable): Optional cancellation object or %NULL. * @callback: Called when the operation completes. * @user_data: Data to pass to the callback. * * Unwrap a key from a byte stream. This call will * return immediately and complete asynchronously. **/ void gck_session_unwrap_key_async (GckSession *self, GckObject *wrapper, GckMechanism *mechanism, const guchar *input, gsize n_input, GckAttributes *attrs, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GckCall *call; UnwrapKey *args; call = _gck_call_async_prep (self, perform_unwrap_key, NULL, sizeof (*args), free_unwrap_key); args = _gck_call_get_arguments (call); g_return_if_fail (GCK_IS_SESSION (self)); g_return_if_fail (GCK_IS_OBJECT (wrapper)); g_return_if_fail (attrs); g_object_get (wrapper, "handle", &args->wrapper, NULL); g_return_if_fail (args->wrapper != 0); /* Shallow copy of the mechanism structure */ memcpy (&args->mechanism, mechanism, sizeof (args->mechanism)); args->attrs = gck_attributes_ref (attrs); args->input = input; args->n_input = n_input; _gck_call_async_ready_go (call, self, cancellable, callback, user_data); } /** * gck_session_unwrap_key_finish: * @self: The session to use. * @result: The async result passed to the callback. * @error: A location to return an error. * * Get the result of a unwrap key operation. * * Returns: (transfer full): the new unwrapped key or %NULL if the operation * failed. **/ GckObject * gck_session_unwrap_key_finish (GckSession *self, GAsyncResult *result, GError **error) { UnwrapKey *args; g_return_val_if_fail (GCK_IS_SESSION (self), NULL); args = _gck_call_async_result_arguments (result, UnwrapKey); if (!_gck_call_basic_finish (result, error)) return NULL; return gck_object_from_handle (self, args->unwrapped); } /* ----------------------------------------------------------------------------- * KEY DERIVATION */ typedef struct _DeriveKey { GckArguments base; GckMechanism mechanism; GckAttributes *attrs; CK_OBJECT_HANDLE key; CK_OBJECT_HANDLE derived; } DeriveKey; static void free_derive_key (DeriveKey *args) { g_clear_pointer (&args->attrs, gck_attributes_unref); g_free (args); } static CK_RV perform_derive_key (DeriveKey *args) { CK_ATTRIBUTE_PTR attrs; CK_ULONG n_attrs; g_assert (sizeof (CK_MECHANISM) == sizeof (GckMechanism)); attrs = _gck_attributes_commit_out (args->attrs, &n_attrs); return (args->base.pkcs11->C_DeriveKey) (args->base.handle, (CK_MECHANISM_PTR)&(args->mechanism), args->key, attrs, n_attrs, &args->derived); } /** * gck_session_derive_key: * @self: The session to use. * @base: The key to derive from. * @mech_type: The mechanism to use for derivation. * @attrs: Additional attributes for the derived key. * @cancellable: Optional cancellation object, or %NULL. * @error: A location to return an error, or %NULL. * * Derive a key from another key. This call may block for an * indefinite period. * * If the @attrs #GckAttributes is floating, it is consumed. * * Returns: (transfer full): the new derived key or %NULL if the operation * failed **/ GckObject * gck_session_derive_key (GckSession *self, GckObject *base, gulong mech_type, GckAttributes *attrs, GCancellable *cancellable, GError **error) { GckMechanism mech = { mech_type, NULL, 0 }; return gck_session_derive_key_full (self, base, &mech, attrs, cancellable, error); } /** * gck_session_derive_key_full: * @self: The session to use. * @base: The key to derive from. * @mechanism: The mechanism to use for derivation. * @attrs: Additional attributes for the derived key. * @cancellable: Optional cancellation object, or %NULL. * @error: A location to return an error, or %NULL. * * Derive a key from another key. This call may block for an * indefinite period. * * Returns: (transfer full): the new derived key or %NULL if the operation * failed **/ GckObject* gck_session_derive_key_full (GckSession *self, GckObject *base, GckMechanism *mechanism, GckAttributes *attrs, GCancellable *cancellable, GError **error) { DeriveKey args = { GCK_ARGUMENTS_INIT, GCK_MECHANISM_EMPTY, attrs, 0, 0 }; gboolean ret; g_return_val_if_fail (GCK_IS_SESSION (self), FALSE); g_return_val_if_fail (GCK_IS_OBJECT (base), FALSE); g_return_val_if_fail (mechanism, FALSE); g_return_val_if_fail (attrs, FALSE); /* Shallow copy of the mechanism structure */ memcpy (&args.mechanism, mechanism, sizeof (args.mechanism)); g_object_get (base, "handle", &args.key, NULL); g_return_val_if_fail (args.key != 0, NULL); ret = _gck_call_sync (self, perform_derive_key, NULL, &args, cancellable, error); if (!ret) return NULL; return gck_object_from_handle (self, args.derived); } /** * gck_session_derive_key_async: * @self: The session to use. * @base: The key to derive from. * @mechanism: The mechanism to use for derivation. * @attrs: Additional attributes for the derived key. * @cancellable: Optional cancellation object or %NULL. * @callback: Called when the operation completes. * @user_data: Data to pass to the callback. * * Derive a key from another key. This call will * return immediately and complete asynchronously. **/ void gck_session_derive_key_async (GckSession *self, GckObject *base, GckMechanism *mechanism, GckAttributes *attrs, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GckCall *call; DeriveKey *args; call = _gck_call_async_prep (self, perform_derive_key, NULL, sizeof (*args), free_derive_key); args = _gck_call_get_arguments (call); g_return_if_fail (GCK_IS_SESSION (self)); g_return_if_fail (GCK_IS_OBJECT (base)); g_return_if_fail (attrs); g_object_get (base, "handle", &args->key, NULL); g_return_if_fail (args->key != 0); /* Shallow copy of the mechanism structure */ memcpy (&args->mechanism, mechanism, sizeof (args->mechanism)); args->attrs = gck_attributes_ref (attrs); _gck_call_async_ready_go (call, self, cancellable, callback, user_data); } /** * gck_session_derive_key_finish: * @self: The session to use. * @result: The async result passed to the callback. * @error: A location to return an error. * * Get the result of a derive key operation. * * Returns: (transfer full): the new derived key or %NULL if the operation * failed **/ GckObject * gck_session_derive_key_finish (GckSession *self, GAsyncResult *result, GError **error) { DeriveKey *args; g_return_val_if_fail (GCK_IS_SESSION (self), NULL); args = _gck_call_async_result_arguments (result, DeriveKey); if (!_gck_call_basic_finish (result, error)) return NULL; return gck_object_from_handle (self, args->derived); } /* -------------------------------------------------------------------------------------------------- * COMMON CRYPTO ROUTINES */ typedef struct _Crypt { GckArguments base; /* Functions to call */ CK_C_EncryptInit init_func; CK_C_Encrypt complete_func; /* Interaction */ GckObject *key_object; GTlsInteraction *interaction; /* Input */ CK_OBJECT_HANDLE key; GckMechanism mechanism; guchar *input; CK_ULONG n_input; /* Output */ guchar *result; CK_ULONG n_result; } Crypt; static CK_RV perform_crypt (Crypt *args) { CK_RV rv; g_assert (args); g_assert (args->init_func); g_assert (args->complete_func); g_assert (!args->result); g_assert (!args->n_result); /* Initialize the crypt operation */ rv = (args->init_func) (args->base.handle, (CK_MECHANISM_PTR)&(args->mechanism), args->key); if (rv != CKR_OK) return rv; rv = _gck_session_authenticate_key (args->base.pkcs11, args->base.handle, args->key_object, args->interaction, NULL); if (rv != CKR_OK) return rv; /* Get the length of the result */ rv = (args->complete_func) (args->base.handle, args->input, args->n_input, NULL, &args->n_result); if (rv != CKR_OK) return rv; /* And try again with a real buffer */ args->result = g_malloc0 (args->n_result); return (args->complete_func) (args->base.handle, args->input, args->n_input, args->result, &args->n_result); } static void free_crypt (Crypt *args) { g_clear_object (&args->interaction); g_clear_object (&args->key_object); g_clear_pointer (&args->input, g_free); g_clear_pointer (&args->result, g_free); g_free (args); } static guchar* crypt_sync (GckSession *self, GckObject *key, GckMechanism *mechanism, const guchar *input, gsize n_input, gsize *n_result, GCancellable *cancellable, GError **error, CK_C_EncryptInit init_func, CK_C_Encrypt complete_func) { Crypt args; g_return_val_if_fail (GCK_IS_OBJECT (key), NULL); g_return_val_if_fail (mechanism, NULL); g_return_val_if_fail (init_func, NULL); g_return_val_if_fail (complete_func, NULL); memset (&args, 0, sizeof (args)); g_object_get (key, "handle", &args.key, NULL); g_return_val_if_fail (args.key != 0, NULL); /* Shallow copy of the mechanism structure */ memcpy (&args.mechanism, mechanism, sizeof (args.mechanism)); /* No need to copy in this case */ args.input = (guchar*)input; args.n_input = n_input; args.init_func = init_func; args.complete_func = complete_func; args.key_object = key; args.interaction = gck_session_get_interaction (self); if (!_gck_call_sync (self, perform_crypt, NULL, &args, cancellable, error)) { g_free (args.result); args.result = NULL; } else { *n_result = args.n_result; } g_clear_object (&args.interaction); return args.result; } static void crypt_async (GckSession *self, GckObject *key, GckMechanism *mechanism, const guchar *input, gsize n_input, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data, CK_C_EncryptInit init_func, CK_C_Encrypt complete_func) { GckCall *call; Crypt *args; call = _gck_call_async_prep (self, perform_crypt, NULL, sizeof (*args), free_crypt); args = _gck_call_get_arguments (call); g_return_if_fail (GCK_IS_OBJECT (key)); g_return_if_fail (mechanism); g_return_if_fail (init_func); g_return_if_fail (complete_func); g_object_get (key, "handle", &args->key, NULL); g_return_if_fail (args->key != 0); /* Shallow copy of the mechanism structure */ memcpy (&args->mechanism, mechanism, sizeof (args->mechanism)); args->input = input && n_input ? g_memdup2 (input, n_input) : NULL; args->n_input = n_input; args->init_func = init_func; args->complete_func = complete_func; args->key_object = g_object_ref (key); args->interaction = gck_session_get_interaction (self); _gck_call_async_ready_go (call, self, cancellable, callback, user_data); } static guchar* crypt_finish (GckSession *self, GAsyncResult *result, gsize *n_result, GError **error) { Crypt *args; guchar *res; if (!_gck_call_basic_finish (result, error)) return NULL; args = _gck_call_async_result_arguments (result, Crypt); /* Steal the values from the results */ res = args->result; args->result = NULL; *n_result = args->n_result; args->n_result = 0; return res; } /* -------------------------------------------------------------------------------------------------- * ENCRYPT */ /** * gck_session_encrypt: * @self: The session. * @key: The key to encrypt with. * @mech_type: The mechanism type to use for encryption. * @input: (array length=n_input): the data to encrypt * @n_input: the length of the data to encrypt * @n_result: location to store the length of the result data * @cancellable: Optional cancellation object, or %NULL * @error: A location to place error information. * * Encrypt data in a mechanism specific manner. This call may * block for an indefinite period. * * Returns: (transfer full) (array length=n_result): the data that was encrypted, * or %NULL if an error occured. */ guchar * gck_session_encrypt (GckSession *self, GckObject *key, gulong mech_type, const guchar *input, gsize n_input, gsize *n_result, GCancellable *cancellable, GError **error) { GckMechanism mechanism = { mech_type, NULL, 0 }; return gck_session_encrypt_full (self, key, &mechanism, input, n_input, n_result, cancellable, error); } /** * gck_session_encrypt_full: * @self: The session. * @key: The key to encrypt with. * @mechanism: The mechanism type and parameters to use for encryption. * @input: (array length=n_input): the data to encrypt * @n_input: the length of the data to encrypt * @n_result: location to store the length of the result data * @cancellable: A GCancellable which can be used to cancel the operation. * @error: A location to place error information. * * Encrypt data in a mechanism specific manner. This call may * block for an indefinite period. * * Returns: (transfer full) (array length=n_result): the data that was encrypted, * or %NULL if an error occured */ guchar * gck_session_encrypt_full (GckSession *self, GckObject *key, GckMechanism *mechanism, const guchar *input, gsize n_input, gsize *n_result, GCancellable *cancellable, GError **error) { GckModule *module = NULL; CK_FUNCTION_LIST_PTR funcs; guchar *ret; g_object_get (self, "module", &module, NULL); g_return_val_if_fail (module != NULL, NULL); funcs = gck_module_get_functions (module); g_return_val_if_fail (module != NULL, NULL); ret = crypt_sync (self, key, mechanism, input, n_input, n_result, cancellable, error, funcs->C_EncryptInit, funcs->C_Encrypt); g_object_unref (module); return ret; } /** * gck_session_encrypt_async: * @self: The session. * @key: The key to encrypt with. * @mechanism: The mechanism type and parameters to use for encryption. * @input: (array length=n_input): the data to encrypt * @n_input: length of the data to encrypt * @cancellable: A GCancellable which can be used to cancel the operation. * @callback: Called when the operation completes. * @user_data: A pointer to pass to the callback. * * Encrypt data in a mechanism specific manner. This call will * return immediately and complete asynchronously. **/ void gck_session_encrypt_async (GckSession *self, GckObject *key, GckMechanism *mechanism, const guchar *input, gsize n_input, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GckModule *module = NULL; CK_FUNCTION_LIST_PTR funcs; g_object_get (self, "module", &module, NULL); g_return_if_fail (module != NULL); funcs = gck_module_get_functions (module); g_return_if_fail (module != NULL); crypt_async (self, key, mechanism, input, n_input, cancellable, callback, user_data, funcs->C_EncryptInit, funcs->C_Encrypt); g_object_unref (module); } /** * gck_session_encrypt_finish: * @self: The session. * @result: The result object passed to the callback. * @n_result: A location to store the length of the result data. * @error: A location to place error information. * * Get the result of an encryption operation. * * Returns: (transfer full) (array length=n_result): the data that was encrypted, * or %NULL if an error occurred. */ guchar* gck_session_encrypt_finish (GckSession *self, GAsyncResult *result, gsize *n_result, GError **error) { return crypt_finish (self, result, n_result, error); } /* -------------------------------------------------------------------------------------------------- * DECRYPT */ /** * gck_session_decrypt: * @self: The session. * @key: The key to decrypt with. * @mech_type: The mechanism type to use for decryption. * @input: (array length=n_input): data to decrypt * @n_input: length of the data to decrypt * @n_result: location to store the length of the result data * @cancellable: Optional cancellation object, or %NULL * @error: A location to place an error. * * Decrypt data in a mechanism specific manner. This call may * block for an indefinite period. * * Returns: (transfer full) (array length=n_result): the data that was decrypted, * or %NULL if an error occured */ guchar * gck_session_decrypt (GckSession *self, GckObject *key, gulong mech_type, const guchar *input, gsize n_input, gsize *n_result, GCancellable *cancellable, GError **error) { GckMechanism mechanism = { mech_type, NULL, 0 }; return gck_session_decrypt_full (self, key, &mechanism, input, n_input, n_result, cancellable, error); } /** * gck_session_decrypt_full: * @self: The session. * @key: The key to decrypt with. * @mechanism: The mechanism type and parameters to use for decryption. * @input: (array length=n_input): data to decrypt * @n_input: length of the data to decrypt * @n_result: location to store the length of the result data * @cancellable: A GCancellable which can be used to cancel the operation. * @error: A location to place error information. * * Decrypt data in a mechanism specific manner. This call may * block for an indefinite period. * * Returns: (transfer full) (array length=n_result): the data that was decrypted, * or %NULL if an error occured */ guchar * gck_session_decrypt_full (GckSession *self, GckObject *key, GckMechanism *mechanism, const guchar *input, gsize n_input, gsize *n_result, GCancellable *cancellable, GError **error) { GckModule *module = NULL; CK_FUNCTION_LIST_PTR funcs; guchar *ret; g_object_get (self, "module", &module, NULL); g_return_val_if_fail (module != NULL, NULL); funcs = gck_module_get_functions (module); g_return_val_if_fail (module != NULL, NULL); ret = crypt_sync (self, key, mechanism, input, n_input, n_result, cancellable, error, funcs->C_DecryptInit, funcs->C_Decrypt); g_object_unref (module); return ret; } /** * gck_session_decrypt_async: * @self: The session. * @key: The key to decrypt with. * @mechanism: The mechanism type and parameters to use for decryption. * @input: (array length=n_input): data to decrypt * @n_input: length of the data to decrypt * @cancellable: A GCancellable which can be used to cancel the operation. * @callback: Called when the operation completes. * @user_data: A pointer to pass to the callback. * * Decrypt data in a mechanism specific manner. This call will * return immediately and complete asynchronously. */ void gck_session_decrypt_async (GckSession *self, GckObject *key, GckMechanism *mechanism, const guchar *input, gsize n_input, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GckModule *module = NULL; CK_FUNCTION_LIST_PTR funcs; g_object_get (self, "module", &module, NULL); g_return_if_fail (module != NULL); funcs = gck_module_get_functions (module); g_return_if_fail (module != NULL); crypt_async (self, key, mechanism, input, n_input, cancellable, callback, user_data, funcs->C_DecryptInit, funcs->C_Decrypt); g_object_unref (module); } /** * gck_session_decrypt_finish: * @self: The session. * @result: The result object passed to the callback. * @n_result: A location to store the length of the result data. * @error: A location to place error information. * * Get the result of an decryption operation. * * Returns: (transfer full) (array length=n_result): the data that was decrypted, * or %NULL if an error occurred */ guchar* gck_session_decrypt_finish (GckSession *self, GAsyncResult *result, gsize *n_result, GError **error) { return crypt_finish (self, result, n_result, error); } /* -------------------------------------------------------------------------------------------------- * SIGN */ /** * gck_session_sign: * @self: The session. * @key: The key to sign with. * @mech_type: The mechanism type to use for signing. * @input: (array length=n_input): data to sign * @n_input: length of the data to sign * @n_result: location to store the length of the result data * @cancellable: Optional cancellation object, or %NULL * @error: A location to place an error. * * Sign data in a mechanism specific manner. This call may * block for an indefinite period. * * Returns: (transfer full) (array length=n_result): the data that was signed, * or %NULL if an error occured */ guchar * gck_session_sign (GckSession *self, GckObject *key, gulong mech_type, const guchar *input, gsize n_input, gsize *n_result, GCancellable *cancellable, GError **error) { GckMechanism mechanism = { mech_type, NULL, 0 }; return gck_session_sign_full (self, key, &mechanism, input, n_input, n_result, NULL, error); } /** * gck_session_sign_full: * @self: The session. * @key: The key to sign with. * @mechanism: The mechanism type and parameters to use for signing. * @input: (array length=n_input): data to sign * @n_input: length of the data to sign * @n_result: location to store the length of the result data * @cancellable: A GCancellable which can be used to cancel the operation. * @error: A location to place error information. * * Sign data in a mechanism specific manner. This call may * block for an indefinite period. * * Returns: The data that was signed, or %NULL if an error occured. */ guchar* gck_session_sign_full (GckSession *self, GckObject *key, GckMechanism *mechanism, const guchar *input, gsize n_input, gsize *n_result, GCancellable *cancellable, GError **error) { GckModule *module = NULL; CK_FUNCTION_LIST_PTR funcs; guchar *ret; g_object_get (self, "module", &module, NULL); g_return_val_if_fail (module != NULL, NULL); funcs = gck_module_get_functions (module); g_return_val_if_fail (module != NULL, NULL); ret = crypt_sync (self, key, mechanism, input, n_input, n_result, cancellable, error, funcs->C_SignInit, funcs->C_Sign); g_object_unref (module); return ret; } /** * gck_session_sign_async: * @self: The session. * @key: The key to sign with. * @mechanism: The mechanism type and parameters to use for signing. * @input: (array length=n_input): data to sign * @n_input: length of the data to sign * @cancellable: A GCancellable which can be used to cancel the operation. * @callback: Called when the operation completes. * @user_data: A pointer to pass to the callback. * * Sign data in a mechanism specific manner. This call will * return immediately and complete asynchronously. */ void gck_session_sign_async (GckSession *self, GckObject *key, GckMechanism *mechanism, const guchar *input, gsize n_input, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GckModule *module = NULL; CK_FUNCTION_LIST_PTR funcs; g_object_get (self, "module", &module, NULL); g_return_if_fail (module != NULL); funcs = gck_module_get_functions (module); g_return_if_fail (module != NULL); crypt_async (self, key, mechanism, input, n_input, cancellable, callback, user_data, funcs->C_SignInit, funcs->C_Sign); g_object_unref (module); } /** * gck_session_sign_finish: * @self: The session. * @result: The result object passed to the callback. * @n_result: A location to store the length of the result data. * @error: A location to place error information. * * Get the result of an signing operation. * * Returns: (transfer full) (array length=n_result): the data that was signed, * or %NULL if an error occurred */ guchar * gck_session_sign_finish (GckSession *self, GAsyncResult *result, gsize *n_result, GError **error) { return crypt_finish (self, result, n_result, error); } /* -------------------------------------------------------------------------------------------------- * VERIFY */ typedef struct _Verify { GckArguments base; /* Interaction */ GckObject *key_object; GTlsInteraction *interaction; /* Input */ CK_OBJECT_HANDLE key; GckMechanism mechanism; guchar *input; CK_ULONG n_input; guchar *signature; CK_ULONG n_signature; } Verify; static CK_RV perform_verify (Verify *args) { CK_RV rv; /* Initialize the crypt operation */ rv = (args->base.pkcs11->C_VerifyInit) (args->base.handle, (CK_MECHANISM_PTR)&(args->mechanism), args->key); if (rv != CKR_OK) return rv; rv = _gck_session_authenticate_key (args->base.pkcs11, args->base.handle, args->key_object, args->interaction, NULL); if (rv != CKR_OK) return rv; /* Do the actual verify */ return (args->base.pkcs11->C_Verify) (args->base.handle, args->input, args->n_input, args->signature, args->n_signature); } static void free_verify (Verify *args) { g_clear_object (&args->interaction); g_clear_object (&args->key_object); g_clear_pointer (&args->input, g_free); g_clear_pointer (&args->signature, g_free); g_free (args); } /** * gck_session_verify: * @self: The session. * @key: The key to verify with. * @mech_type: The mechanism type to use for verifying. * @input: (array length=n_input): data to verify * @n_input: length of the data to verify * @signature: (array length=n_signature): the signature * @n_signature: length of the signature * @cancellable: Optional cancellation object, or %NULL * @error: A location to place an error. * * Verify data in a mechanism specific manner. This call may * block for an indefinite period. * * Returns: %TRUE if the data verified correctly, otherwise a failure or error occurred. */ gboolean gck_session_verify (GckSession *self, GckObject *key, gulong mech_type, const guchar *input, gsize n_input, const guchar *signature, gsize n_signature, GCancellable *cancellable, GError **error) { GckMechanism mechanism = { mech_type, NULL, 0 }; return gck_session_verify_full (self, key, &mechanism, input, n_input, signature, n_signature, NULL, error); } /** * gck_session_verify_full: * @self: The session. * @key: The key to verify with. * @mechanism: The mechanism type and parameters to use for signing. * @input: (array length=n_input): data to verify * @n_input: the length of the data to verify * @signature: (array length=n_signature): the signature * @n_signature: length of the signature * @cancellable: A GCancellable which can be used to cancel the operation. * @error: A location to place an error. * * Verify data in a mechanism specific manner. This call may * block for an indefinite period. * * Returns: %TRUE if the data verified correctly, otherwise a failure or error occurred. */ gboolean gck_session_verify_full (GckSession *self, GckObject *key, GckMechanism *mechanism, const guchar *input, gsize n_input, const guchar *signature, gsize n_signature, GCancellable *cancellable, GError **error) { Verify args; gboolean ret; g_return_val_if_fail (GCK_IS_OBJECT (key), FALSE); g_return_val_if_fail (mechanism, FALSE); memset (&args, 0, sizeof (args)); g_object_get (key, "handle", &args.key, NULL); g_return_val_if_fail (args.key != 0, FALSE); /* Shallow copy of the mechanism structure */ memcpy (&args.mechanism, mechanism, sizeof (args.mechanism)); /* No need to copy in this case */ args.input = (guchar*)input; args.n_input = n_input; args.signature = (guchar*)signature; args.n_signature = n_signature; args.key_object = key; args.interaction = gck_session_get_interaction (self); ret = _gck_call_sync (self, perform_verify, NULL, &args, cancellable, error); g_clear_object (&args.interaction); return ret; } /** * gck_session_verify_async: * @self: The session. * @key: The key to verify with. * @mechanism: The mechanism type and parameters to use for signing. * @input: (array length=n_input): data to verify * @n_input: the length of the data to verify * @signature: (array length=n_signature): the signature * @n_signature: the length of the signature * @cancellable: A GCancellable which can be used to cancel the operation. * @callback: Called when the operation completes. * @user_data: A pointer to pass to the callback. * * Verify data in a mechanism specific manner. This call returns * immediately and completes asynchronously. */ void gck_session_verify_async (GckSession *self, GckObject *key, GckMechanism *mechanism, const guchar *input, gsize n_input, const guchar *signature, gsize n_signature, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GckCall *call; Verify *args; call = _gck_call_async_prep (self, perform_verify, NULL, sizeof (*args), free_verify); args = _gck_call_get_arguments (call); g_return_if_fail (GCK_IS_OBJECT (key)); g_return_if_fail (mechanism); g_object_get (key, "handle", &args->key, NULL); g_return_if_fail (args->key != 0); /* Shallow copy of the mechanism structure */ memcpy (&args->mechanism, mechanism, sizeof (args->mechanism)); args->input = input && n_input ? g_memdup2 (input, n_input) : NULL; args->n_input = n_input; args->signature = signature && n_signature ? g_memdup2 (signature, n_signature) : NULL; args->n_signature = n_signature; args->key_object = g_object_ref (key); args->interaction = gck_session_get_interaction (self); _gck_call_async_ready_go (call, self, cancellable, callback, user_data); } /** * gck_session_verify_finish: * @self: The session. * @result: The result object passed to the callback. * @error: A location to place error information. * * Get the result of an verify operation. * * Returns: %TRUE if the data verified correctly, otherwise a failure or error occurred. */ gboolean gck_session_verify_finish (GckSession *self, GAsyncResult *result, GError **error) { return _gck_call_basic_finish (result, error); } static void update_password_for_token (GTlsPassword *password, CK_TOKEN_INFO *token_info, gboolean request_retry) { GTlsPasswordFlags flags; gchar *label; label = gck_string_from_chars (token_info->label, sizeof (token_info->label)); g_tls_password_set_description (password, label); g_free (label); flags = 0; if (request_retry) flags |= G_TLS_PASSWORD_RETRY; if (token_info && token_info->flags & CKF_USER_PIN_COUNT_LOW) flags |= G_TLS_PASSWORD_MANY_TRIES; if (token_info && token_info->flags & CKF_USER_PIN_FINAL_TRY) flags |= G_TLS_PASSWORD_FINAL_TRY; g_tls_password_set_flags (password, flags); } CK_RV _gck_session_authenticate_token (CK_FUNCTION_LIST_PTR funcs, CK_SESSION_HANDLE session, GckSlot *token, GTlsInteraction *interaction, GCancellable *cancellable) { CK_SESSION_INFO session_info; GTlsPassword *password = NULL; CK_TOKEN_INFO token_info; GTlsInteractionResult res; gboolean request_retry; CK_SLOT_ID slot_id; CK_BYTE_PTR pin; gsize n_pin; CK_RV rv = CKR_OK; GError *error = NULL; g_assert (funcs != NULL); g_assert (GCK_IS_SLOT (token)); slot_id = gck_slot_get_handle (token); request_retry = FALSE; do { if (g_cancellable_is_cancelled (cancellable)) { rv = CKR_FUNCTION_CANCELED; break; } rv = (funcs->C_GetTokenInfo) (slot_id, &token_info); if (rv != CKR_OK) { g_warning ("couldn't get token info when logging in: %s", gck_message_from_rv (rv)); break; } /* No login necessary? */ if ((token_info.flags & CKF_LOGIN_REQUIRED) == 0) { g_debug ("no login required for token, skipping login"); rv = CKR_OK; break; } /* Next check if session is logged in? */ rv = (funcs->C_GetSessionInfo) (session, &session_info); if (rv != CKR_OK) { g_warning ("couldn't get session info when logging in: %s", gck_message_from_rv (rv)); break; } /* Already logged in? */ if (session_info.state == CKS_RW_USER_FUNCTIONS || session_info.state == CKS_RO_USER_FUNCTIONS || session_info.state == CKS_RW_SO_FUNCTIONS) { g_debug ("already logged in, skipping login"); rv = CKR_OK; break; } if (token_info.flags & CKF_PROTECTED_AUTHENTICATION_PATH) { g_debug ("trying to log into session: protected authentication path, no password"); /* No password passed for PAP */ pin = NULL; n_pin = 0; /* Not protected auth path */ } else { g_debug ("trying to log into session: want password %s", request_retry ? "login was incorrect" : ""); if (password == NULL) password = g_object_new (GCK_TYPE_PASSWORD, "token", token, NULL); update_password_for_token (password, &token_info, request_retry); if (interaction == NULL) res = G_TLS_INTERACTION_UNHANDLED; else res = g_tls_interaction_invoke_ask_password (interaction, G_TLS_PASSWORD (password), NULL, &error); if (res == G_TLS_INTERACTION_FAILED) { g_message ("interaction couldn't ask password: %s", error->message); rv = _gck_rv_from_error (error, CKR_USER_NOT_LOGGED_IN); g_clear_error (&error); break; } else if (res == G_TLS_INTERACTION_UNHANDLED) { g_message ("couldn't authenticate: no interaction handler"); rv = CKR_USER_NOT_LOGGED_IN; break; } pin = (CK_BYTE_PTR)g_tls_password_get_value (password, &n_pin); } /* Try to log in */ rv = (funcs->C_Login) (session, CKU_USER, (CK_BYTE_PTR)pin, (CK_ULONG)n_pin); /* Only one C_Login call if protected auth path */ if (token_info.flags & CKF_PROTECTED_AUTHENTICATION_PATH) break; request_retry = TRUE; } while (rv == CKR_PIN_INCORRECT); g_clear_object (&password); return rv; } static void update_password_for_key (GTlsPassword *password, CK_TOKEN_INFO *token_info, gboolean request_retry) { GTlsPasswordFlags flags; flags = 0; if (request_retry) flags |= G_TLS_PASSWORD_RETRY; if (token_info && token_info->flags & CKF_USER_PIN_COUNT_LOW) flags |= G_TLS_PASSWORD_MANY_TRIES; if (token_info && token_info->flags & CKF_USER_PIN_FINAL_TRY) flags |= G_TLS_PASSWORD_FINAL_TRY; g_tls_password_set_flags (password, flags); } CK_RV _gck_session_authenticate_key (CK_FUNCTION_LIST_PTR funcs, CK_SESSION_HANDLE session, GckObject *key, GTlsInteraction *interaction, GCancellable *cancellable) { CK_ATTRIBUTE attrs[2]; CK_SESSION_INFO session_info; CK_TOKEN_INFO token_info; GTlsPassword *password = NULL; CK_OBJECT_HANDLE handle; GTlsInteractionResult res; gboolean request_retry; GError *error = NULL; CK_BYTE_PTR pin; gsize pin_len; CK_BBOOL bvalue; gboolean got_label; CK_RV rv; g_assert (funcs != NULL); handle = gck_object_get_handle (key); attrs[0].type = CKA_LABEL; attrs[0].pValue = NULL; attrs[0].ulValueLen = 0; attrs[1].type = CKA_ALWAYS_AUTHENTICATE; attrs[1].pValue = &bvalue; attrs[1].ulValueLen = sizeof (bvalue); rv = (funcs->C_GetAttributeValue) (session, handle, attrs, 2); if (rv == CKR_ATTRIBUTE_TYPE_INVALID) { bvalue = CK_FALSE; } else if (rv != CKR_OK) { g_message ("couldn't check whether key requires authentication, assuming it doesn't: %s", gck_message_from_rv (rv)); return CKR_OK; } /* No authentication needed, on this object */ if (bvalue != CK_TRUE) { g_debug ("key does not require authentication"); return CKR_OK; } got_label = FALSE; request_retry = FALSE; do { if (g_cancellable_is_cancelled (cancellable)) { rv = CKR_FUNCTION_CANCELED; break; } rv = (funcs->C_GetSessionInfo) (session, &session_info); if (rv != CKR_OK) { g_warning ("couldn't get session info when authenticating key: %s", gck_message_from_rv (rv)); return rv; } rv = (funcs->C_GetTokenInfo) (session_info.slotID, &token_info); if (rv != CKR_OK) { g_warning ("couldn't get token info when authenticating key: %s", gck_message_from_rv (rv)); return rv; } /* Protected authentication path, just use NULL passwords */ if (token_info.flags & CKF_PROTECTED_AUTHENTICATION_PATH) { password = NULL; pin = NULL; pin_len = 0; /* Need to prompt for a password */ } else { g_debug ("trying to log into session: want password %s", request_retry ? "login was incorrect" : ""); if (password == NULL) password = g_object_new (GCK_TYPE_PASSWORD, "key", key, NULL); /* Set the password */ update_password_for_key (password, &token_info, request_retry); /* Set the label properly */ if (!got_label) { if (attrs[0].ulValueLen && attrs[0].ulValueLen != GCK_INVALID) { attrs[0].pValue = g_malloc0 (attrs[0].ulValueLen + 1); rv = (funcs->C_GetAttributeValue) (session, handle, attrs, 1); if (rv == CKR_OK) { ((gchar *)attrs[0].pValue)[attrs[0].ulValueLen] = 0; g_tls_password_set_description (password, attrs[0].pValue); } g_free (attrs[0].pValue); attrs[0].pValue = NULL; } got_label = TRUE; } if (interaction == NULL) res = G_TLS_INTERACTION_UNHANDLED; else res = g_tls_interaction_invoke_ask_password (interaction, G_TLS_PASSWORD (password), NULL, &error); if (res == G_TLS_INTERACTION_FAILED) { g_message ("interaction couldn't ask password: %s", error->message); rv = _gck_rv_from_error (error, CKR_USER_NOT_LOGGED_IN); g_clear_error (&error); break; } else if (res == G_TLS_INTERACTION_UNHANDLED) { g_message ("couldn't authenticate: no interaction handler"); rv = CKR_USER_NOT_LOGGED_IN; break; } pin = (CK_BYTE_PTR)g_tls_password_get_value (G_TLS_PASSWORD (password), &pin_len); } /* Try to log in */ rv = (funcs->C_Login) (session, CKU_CONTEXT_SPECIFIC, pin, pin_len); /* Only one C_Login call if protected auth path */ if (token_info.flags & CKF_PROTECTED_AUTHENTICATION_PATH) break; request_retry = TRUE; } while (rv == CKR_PIN_INCORRECT); g_clear_object (&password); return rv; }