diff options
author | Miloslav Trmač <mitr@redhat.com> | 2010-08-27 09:31:07 +0200 |
---|---|---|
committer | Miloslav Trmač <mitr@redhat.com> | 2010-08-27 09:31:07 +0200 |
commit | 3d758323280763fe59b1f61d4f86aa2a29fe3775 (patch) | |
tree | f195a8cb4aa2ff6fbb5a51e0e6c13df1968401b6 | |
parent | 40b5088d61e6f0830302b253ad407d06e46364b6 (diff) | |
parent | dd5317620d70e6b438e40370163257e47ca385a3 (diff) | |
download | kernel-crypto-3d758323280763fe59b1f61d4f86aa2a29fe3775.tar.gz kernel-crypto-3d758323280763fe59b1f61d4f86aa2a29fe3775.tar.xz kernel-crypto-3d758323280763fe59b1f61d4f86aa2a29fe3775.zip |
Merge remote branch 'standalone/master' into standalone-master
Conflicts:
cryptodev_main.c
ncr-int.h
ncr.c
37 files changed, 5066 insertions, 3646 deletions
diff --git a/.gitignore b/.gitignore index 4379d92e956..46c85a4058e 100644 --- a/.gitignore +++ b/.gitignore @@ -7,8 +7,6 @@ Module.symvers *.o *.mod.c modules.order -examples/cipher -examples/hmac examples/ncr examples/pk examples/speed @@ -68,7 +68,7 @@ TOMCRYPT_OBJECTS = libtomcrypt/misc/zeromem.o libtomcrypt/misc/crypt/crypt_argch cryptodev-objs = cryptodev_main.o cryptodev_cipher.o ncr.o \ ncr-key.o ncr-limits.o ncr-pk.o ncr-sessions.o ncr-dh.o \ - ncr-key-wrap.o ncr-key-storage.o $(TOMMATH_OBJECTS) \ + ncr-key-wrap.o ncr-key-storage.o utils.o $(TOMMATH_OBJECTS) \ $(TOMCRYPT_OBJECTS) @@ -80,8 +80,7 @@ build: install: $(MAKE) -C $(KERNEL_DIR) SUBDIRS=`pwd` modules_install - @echo "Installing cryptodev.h in /usr/include/crypto ..." - @install -D cryptodev.h /usr/include/crypto/cryptodev.h + @echo "Installing ncr.h in /usr/include/crypto ..." @install -D ncr.h /usr/include/crypto/ncr.h clean: @@ -1,6 +1,7 @@ -* ioctl_compat() mode for ncr.h API as it is in cryptodev.h * Put limits to sessions * Export private keys to PKCS #8 format (can it be implemented?) * Documentation for functions * Is a writev() like interface needed? * Implement the NIST DSA generation algorithm. +* Make key generation interruptible. +* Allow assymetric keys to wrap other keys. @@ -16,7 +16,7 @@ .\" 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. .\" .\" Author: Miloslav Trmač <mitr@redhat.com> -.TH CRYPTO 4 2010-08-02 Linux "Linux Programmer's Manual" +.TH CRYPTO 4 2010-08-20 Linux "Linux Programmer's Manual" .SH NAME /dev/crypto \- kernel cryptographic module interface @@ -54,18 +54,125 @@ and passes a pointer to the data structure as the third parameter to .BR ioctl (2). On success, output data is available in the same structure. +.SH OPERATION ATTRIBUTES + +In addition to the fixed members of data structures, +a process can pass additional attributes on input of most operations, +and receive additional attributes back from the kernel. +If this is supported by a specific operation, +the fixed data structure members for this operation include +.B input_size +and \fBoutput_size\fR. + +The input attributes immediately follow the fixed data structure as a sequence +of correctly aligned (\fBstruct nlattr\fP, attribute data) pairs, +using the same data layout and formatting rules as +.BR netlink (7) +messages. +The +.B input_size +member must be initialized to the total length of input (including both the +fixed data structure and the attributes). + +There is an (unspecified) upper limit on the total size of all attributes, +which should be large enough to accommodate any reasonable application. +If a larger input is provided nevertheless, +the operation will fail with \fBEOVERFLOW\fP. + +Output attributes, if any, are written by the kernel in the same format +following the fixed data structure (overwriting input attributes, if any). +The +.B output_size +member must be initialized to the total space available for output +(including the fixed data structure and space for attributes), +and is updated by the kernel with the total space actually used for attributes. + +If the space available for output attributes is too small, +the operation will fail with \fBERANGE\fP. + +As a special case, +.B input_size +may be zero to indicate no input attributes are supplied. +Similarly, +.B output_size +may be zero to indicate no space for output attributes is provided; +in such case +.B output_size +is not overwritten by the kernel with the total space actually used +(which is equal to size of the fixed data structure) +and remains zero when the operation finishes. + +The kernel accepts and silently ignores unknown attributes. + +The kernel will not add new output attributes to existing operations +in future releases +unless the new output attribute was explicitly requested by the application. +(Therefore, the application does not have to allocate any extra space for +output attributes it does not expect.) + +The following attributes are used in more than one operation: + +.TP +.B NCR_ATTR_ALGORITHM +A NUL-terminated string specifying an algorithm +(to be used in an operation, or as a property of a key), +using the Linux crypto API algorithm names. + +Three additional algorithm names are recognized: \fBrsa\fP, \fBdsa\fP, \fBdh\fP. + +.TP +.B NCR_ATTR_IV +Unformatted binary data specifying an initialization vector, + +.TP +.B NCR_ATTR_KEY_FLAGS +An 32-bit unsigned integer in native byte order specifying key flags, +a combination of +.B NCR_KEY_FLAG_EXPORTABLE +(the key material can be exported in plaintext to user space) +and +.B NCR_KEY_FLAG_WRAPPABLE +(the key material can be wrapped and the result made available to user space). + +.TP +.B NCR_ATTR_KEY_ID +Arbitrary binary data +which can be used by the application for key identification. + +.TP +.B NCR_ATTR_KEY_TYPE +An 32-bit unsigned integer in native byte order specifying key type, +one of \fBNCR_KEY_TYPE_SECRET\fP, \fBNCR_KEY_TYPE_PUBLIC\fP and +\fBNCR_KEY_TYPE_PRIVATE\fP. + +.TP +.B NCR_ATTR_WRAPPING_ALGORITHM +A NUL-terminated string specifying a key wrapping algorithm. +The values +.B walg-aes-rfc3394 +and +.B walg-aes-rfc5649 +are currently supported. +.RE + +.SH OPERATIONS + The following operations are defined: .TP .B NCRIO_KEY_INIT Allocate a kernel-space key object. -The parameter is not used on input +The third +.BR ioctl (s) +parameter is ignored (key attributes are set later, when the key material is initialized). -On success, an integer descriptor for the key object +Returns an +.B ncr_key_t +descriptor for the key object (valid within the current .I /dev/crypto namespace) -is stored in the provided area. +on success. There is a per-process and per-user (not per-namespace) limit on the number key objects that can be allocated. @@ -73,7 +180,9 @@ on the number key objects that can be allocated. .TP .B NCRIO_KEY_DEINIT Deallocate a kernel-space key object. -The parameter specifies the integer descriptor of the key object. +The parameter points to a +.B ncr_key_t +descriptor of the key object. After all other operations using this key object (if any) terminate, the key material will be cleared and the object will be freed. Note that this may happen both before this operation returns, @@ -84,34 +193,25 @@ and after it returns, depending on other references to this key object. Clear existing key material in the specified key object, and generate new key material. -The parameter points to \fBstruct ncr_key_generate_st\fP, which specifies: +The parameter points to \fBstruct ncr_key_generate\fP, +which specifies the destination key object in its +.B key +member. + +The following input attributes are recognized: .RS -.IP \fBdesc\fP -The key descriptor -.IP \fBparams.algorithm\fP -The crypto algorithm with which the key will be used -.IP \fBparams.keyflags\fP -Key flags, a combination of -.B NCR_KEY_FLAG_EXPORTABLE -(the key material can be exported in plaintext to user space) -and -.B NCR_KEY_FLAG_WRAPPABLE -(the key material can be wrapped and the result made available to user space). -.IP \fBparams.params\fP -Algorithm-specific key parameters: -For symmetric keys, key length in bits. -For RSA keys, the public exponent and modulus length in bits. -For DSA keys, -.I p -and -.I q -length in bits. -For DH keys, the prime and group generator. +.IP \fBNCR_ATTR_ALGORITHM\fP +Mandatory. +.IP \fBNCR_ATTR_KEY_FLAGS\fP +Optional, flags are unchanged if not present. +.IP \fBNCR_ATTR_SECRET_KEY_BITS\fP +Mandatory for symmetric keys. +An 32-bit unsigned integer in native byte order specifying key length in bits. .RE .IP -Currently only symmetric keys can be generated using this operation. +Only symmetric keys can be currently generated using this operation. In addition to generating the key material, the "persistent" key ID is reset to a random value. @@ -121,98 +221,129 @@ the "persistent" key ID is reset to a random value. Similar to \fBNCRIO_KEY_GENERATE\fP, except that a pair of public/private keys is generated. -The parameter points to \fBstruct ncr_key_generate_st\fP as specified above, -with the additional member -.B desc2 -used to specify the key descriptor for the public key. +The parameter points to \fBstruct ncr_key_generate_pair\fP, +which specifies the destination key objects in its +.B private_key +and +.B public_key +members. + +The following input attributes are recognized: +.RS +.IP \fBNCR_ATTR_KEY_FLAGS\fP +Optional, flags are unchanged if not present. +.IP \fBNCR_ATTR_RSA_E\fP +For RSA keys, +the public exponent as a big-endian multiple-precision integer. +Optional, defaults to 65537. +.IP \fBNCR_ATTR_RSA_MODULUS_BITS\fP +Mandatory for RSA keys. +An 32-bit unsigned integer in native byte order +specifying modulus length in bits. +.IP \fBNCR_ATTR_DSA_P_BITS\fP +For DSA keys, length of the "p" key parameter in bits +as an 32-bit unsigned integer in native byte order. +Optional, defaults to 1024. +.IP \fBNCR_ATTR_DSA_Q_BITS\fP +For DSA keys, length of the "q" key parameter in bits +as an 32-bit unsigned integer in native byte order. +Optional, defaults to 160. +.IP \fBNCR_ATTR_DH_BASE\fP +Mandatory for D-H keys. +The prime modulus of a D-H group as a big-endian multiple-precision integer. +.IP \fBNCR_ATTR_DH_PRIME\fP +Mandatory for D-H keys. +The generator of a D-H group as a big-endian multiple-precision integer. +.RE + +.IP The .B NCR_KEY_FLAG_EXPORTABLE and .B NCR_KEY_FLAG_WRAPPABLE flags are automatically set on the public key. +In addition to generating the key material, +the "persistent" key ID of both keys is set to a same value +derived from the public key. + .TP .B NCRIO_KEY_DERIVE Derive a new key using one key and additional data. -The parameter points to \fBstruct ncr_key_derivation_params_st\fP, -which specifies: +The parameter points to \fBstruct ncr_key_derive\fP, +which specifies the source and destination keys in the +.B input_key +and +.B new_key +members. + +The following input attributes are recognized: .RS -.IP \fBderive\fP -The derivation algorithm. -Currently only -.B NCR_DERIVE_DH -is supported. -.IP \fBnewkey\fP -The descriptor of the resulting key -.IP \fBkeyflags\fP -Flags to use for the resulting key -.IP \fBkey\fP -The source key descriptor -.IP \fBparams\fP -Key type-specific parameters. -For \fBNCR_DERIVE_DH\fP, -.B params.params.dh.pub -and -.B params.params.dh.pub_size -specify the peer's public key. +.IP \fBNCR_ATTR_KEY_FLAGS\fP +Optional, flags are unchanged if not present. +.IP \fBNCR_ATTR_DERIVATION_ALGORITHM\fP +Mandatory. +A NUL-terminated string specifying a key derivation algorithm. +Only +.B dh +is currently supported. +.IP \fBNCR_ATTR_DH_PUBLIC\fP +Mandatory for D-H derivation. +The peer's public D-H value as a big-endian multiple-precision integer. .RE .TP .B NCRIO_KEY_EXPORT - Export key material in the specified key object to user space. Only keys with the .B NCR_KEY_FLAG_EXPORTABLE flag can be exported using this operation. -The parameter points to \fBstruct ncr_key_data_st\fP, which specifies: +The parameter points to \fBstruct ncr_key_export\fP, +which specifies the key to export in the +.B key +member, +and a buffer for the exported data in the +.B buffer +and +.B buffer_size +members. -.RS -.IP \fBkey\fP -The key descriptor -.IP \fBidata\fP -Destination buffer -.IP \fBidata_size\fP -Buffer size -.RE +On success, size of the exported key is returned. -.IP Symmetric keys are written directly into the destination buffer. Public and private keys are formatted using ASN.1, except for DH public keys, which are written a raw binary number. -On success, the -.B idata_size -member is set to the size of the exported key. - .TP .B NCRIO_KEY_IMPORT Clear existing key material in the specified key object, and import key material from user space. -The parameter points to \fBstruct ncr_key_data_st\fP, which specifies: +The parameter points to \fBstruct ncr_key_import\fP, +which specifies the destination key in the +.B key +member, +and the input data in the +.B data +and +.B data_size +members. + +The following input attributes are recognized: .RS -.IP \fBkey\fP -The key descriptor -.IP \fBidata\fP -Source data -.IP \fBidata_size\fP -Source data size -.IP \fBkey_id\fP -New "persistent" key ID. -.IP \fBkey_id_size\fP -Size of data in \fBkey_id\fP. -.IP \fBtype\fP -Key type, one of \fBNCR_KEY_TYPE_SECRET\fP, \fBNCR_KEY_TYPE_PUBLIC\fP and -\fBNCR_KEY_TYPE_PRIVATE\fP. -.IP \fBalgorithm\fP -The crypto algorithm with which the key will be used -.IP \fBflags\fP -Key flags +.IP \fBNCR_ATTR_ALGORITHM\fP +Mandatory. +.IP \fBNCR_ATTR_KEY_FLAGS\fP +Optional, flags are unchanged if not present. +.IP \fBNCR_ATTR_KEY_ID\fP +Optional, the "persistent" key ID is unchanged if not present. +.IP \fBNCR_ATTR_KEY_TYPE\fP +Mandatory. .RE .IP @@ -224,19 +355,28 @@ operation. .B NCRIO_KEY_GET_INFO Get metadata of an existing key. -The parameter points to \fBstruct ncr_key_info_st\fP, +The parameter points to \fBstruct ncr_key_get_info\fP, which specifies \fBkey\fP, the key descriptor. -On success, the following members are set: + +The following input attributes are recognized: .RS -.IP \fBflags\fP -Key flags -.IP \fBtype\fP -Key type -.IP \fBalgorithm\fP -Key algorithm +.IP \fBNCR_ATTR_WANTED_ATTRS\fP +An array of unsigned 16-bit integers in native byte order, +specifying the set of output attributes that should be returned. +\fBNCR_ATTR_ALGORITHM\fP, +.B NCR_ATTR_KEY_FLAGS +and +.B NCR_ATTR_KEY_TYPE +are currently supported. +Unsupported attribute requests are silently ignored .RE +.IP +The output attributes explicitly requested in \fBNCR_ATTR_WANTED_ATTRS\fP, +and no other output attributes, +are returned. + .TP .B NCRIO_KEY_WRAP Wrap one key using another, and write the result to user space. @@ -244,62 +384,59 @@ Only keys with the .B NCR_KEY_FLAG_WRAPPABLE flag can be wrapped using this operation. -The parameter points to \fBstruct ncr_key_wrap_st\fP, which specifies: +The parameter points to \fBstruct ncr_key_wrap\fP, +which specifies the key to wrap in the +.B source_key +member, +the wrapping key in the +.B wrapping_key +member, +and a buffer for the wrapped data in the +.B buffer +and +.B buffer_size +members. + +The following input attributes are recognized: .RS -.IP \fBalgorithm\fP -The wrapping algorithm to use, one of -.B NCR_WALG_AES_RFC3394 -and \fBNCR_WALG_AES_RFC5649\fP. -.IP \fBkeytowrap\fP -The descriptor of the key to wrap -.IP \fBkey\fP -The descriptor of the key used for wrapping -.IP \fBparams\fP -Key type-specific parameters. -For the currently supported wrapping algorithms, -.B params.params.cipher.iv -and -.B params.params.cipher.iv_size -specify the IV. -.IP \fBio\fP -Destination buffer -.IP \fBio_size\fP -Size of the destination buffer +.IP \fBNCR_ATTR_IV\fP +Optional, an empty IV is used if not present. +.IP \fBNCR_ATTR_WRAPPING_ALGORITHM\fP +Mandatory. .RE .IP -Currently only secret keys can be wrapped, -using one of the above-mentioned AES-based algorithms. +Only secret keys can be currently wrapped. -On success, the -.B io_size -member is set to the size of the wrapped key. +On success, size of the wrapped key is returned. .TP .B NCRIO_KEY_UNWRAP Unwrap user-space data into a kernel-space key using another key. -The parameter points to \fBstruct ncr_key_wrap_st\fP, which specifies: +The parameter points to \fBstruct ncr_key_unwrap\fP, +which specifies the destination key in the +.B dest_key +member, +the wrapping key in the +.B wrapping_key +member, +and the wrapped data in the +.B data +and +.B data_size +members. + +The following input attributes are recognized: .RS -.IP \fBalgorithm\fP -The wrapping algorithm to use. -.IP \fBkeytowrap\fP -The descriptor of the target key object -.IP \fBkey\fP -The descriptor of the key used for wrapping -.IP \fBparams\fP -Key type-specific parameters. -For the currently supported wrapping algorithms, -.B params.params.cipher.iv -and -.B params.params.cipher.iv_size -specify the IV. -.IP \fBio\fP -Pointer to the wrapped key -.IP \fBio_size\fP -Size of the wrapped key +.IP \fBNCR_ATTR_IV\fP +Optional, an empty IV is used if not present. +.IP \fBNCR_ATTR_KEY_FLAGS\fP +Optional, flags are unchanged if not present. +.IP \fBNCR_ATTR_WRAPPING_ALGORITHM\fP +Mandatory. .RE .IP @@ -319,54 +456,42 @@ Only keys with the .B NCR_KEY_FLAG_WRAPPABLE flag can be wrapped using this operation. -The parameter points to \fBstruct ncr_key_storage_wrap_st\fP, which specifies: - -.RS -.IP \fBkeytowrap\fP -The descriptor of the key to wrap -.IP \fBio\fP -Destination buffer -.IP \fBio_size\fP -Size of the destination buffer -.RE +The parameter points to \fBstruct ncr_key_storage_wrap\fP, +which specifies the key to wrap in the +.B key +member, +and a buffer for the wrapped data in the +.B buffer +and +.B buffer_size +members. -.IP -On success, the -.B io_size -member is set to the size of the wrapped key. +On success, size of the wrapped key is returned. Both symmetric and asymmetric keys can be wrapped using this operation. -The wrapped data includes the following information in addition to the raw key -material: - -.RS -.IP \(bu -Key type -.IP \(bu -Key flags -.IP \(bu -Key algorithm -.IP \(bu -"Persistent" key ID. -.RE +The wrapped data includes data corresponding the +\fBNCR_ATTR_ALGORITHM\fP, \fBNCR_ATTR_KEY_FLAGS\fP, +.B NCR_ATTR_KEY_TYPE +and +.B NCR_ATTR_KEY_ID +attributes +in addition to the raw key material: .TP .B NCRIO_KEY_STORAGE_UNWRAP Unwrap key and associated metadata created using \fBNCRIO_KEY_STORAGE_WRAP\fP, and restore the information into a specified key object. -The parameter points to \fBstruct ncr_key_storage_wrap_st\fP, which specifies: - -.RS -.IP \fBkeytowrap\fP -The target key descriptor -.IP \fBio\fP -Wrapped data -.IP \fBio_size\fP -Size of the wrapped data -.RE +The parameter points to \fBstruct ncr_key_storage_unwrap\fP, +which specifies the destination key in the +.B key +member +and the wrapped data in the +.B data +and +.B data_size +members. -.IP See .B NCRIO_KEY_STORAGE_WRAP above for the list of attributes that will be restored. @@ -375,23 +500,58 @@ above for the list of attributes that will be restored. .B NCRIO_SESSION_INIT Allocate a session for performing crypto operations. -The parameter points to \fBstruct ncr_session_st\fP, which specifies: - -.RS -.IP \fBalgorithm\fP -The crypto algorithm to use. -.IP \fBkey\fP -The key to use for the operation, if required. -.IP \fBparams\fP -Parameters for the operation. -For symmetric ciphers, the IV. -For RSA operations, the format, used hash algorithms and PSS salt length. -for DSA, the signature hash algorithm. -.IP \fBop\fP -The operation to perform, one of \fBNCR_OP_ENCRYPT\fP, \fBNCR_OP_DECRYPT\fP, -\fBNCR_OP_SIGN\fP and \fBNCR_OP_VERIFY\FP. Use +The parameter points to \fBstruct ncr_session_init\fP, +which specifies the operation to perform, +one of \fBNCR_OP_ENCRYPT\fP, \fBNCR_OP_DECRYPT\fP, +.B NCR_OP_SIGN +and \fBNCR_OP_VERIFY\fP, +in the +.B op +member. +Use .B NCR_OP_SIGN for computing an unkeyed hash as well as keyed hashes and signatures. + +The following input attributes are recognized: + +.RS +.IP \fBNCR_ATTR_ALGORITHM\fP +Mandatory unless +.B NCR_ATTR_SESSION_CLONE_FROM +is provided. +.IP \fBNCR_ATTR_IV\fP +Mandatory for some operations and algorithms. +.IP \fBNCR_ATTR_KEY\fP +Mandatory for some operations and algorithms. +An 32-bit unsigned integer in native byte order +specifying the key to use for the operation. +If +.B NCR_ATTR_SESSION_CLONE_FROM +is provided, the key from the original session is used. +.IP \fBNCR_ATTR_RSA_ENCODING_METHOD\fP +Mandatory for RSA. +An 32-bit unsigned integer in native byte order +specifying a RSA encoding method, +one of \fBRSA_PKCS1_V1_5\fP, +.B RSA_PKCS1_OAEP +and \fBRSA_PKCS1_PSS\fP. +.IP \fBNCR_ATTR_RSA_OAEP_HASH_ALGORITHM\fP +Mandatory for RSA with \fBRSA_PKCS1_OAEP\fP. +A NUL-terminated string specifying a hash algorithm used in the +OAEP encoding method. +.IP \fBNCR_ATTR_RSA_PSS_SALT_LENGTH\fP +For RSA with \fBRSA_PKCS1_PSS\fP. +An 32-bit unsigned integer in native byte order +specifying the PSS salt length. +Optional, defaults to 0. +.IP \fBNCR_ATTR_SESSION_CLONE_FROM\fP +Optional, a 32-bit unsigned integer in native byte order +specifying session state to clone. +Only supported for some operations and algorithms. +.IP \fBNCR_ATTR_SIGNATURE_HASH_ALGORITHM\fP +Mandatory for some operations and algorithms. +A NUL-terminated string specifying a hash algorithm underlying a signature, +using the same formats as \fBNCR_ATTR_ALGORITHM\fP. .RE .IP @@ -399,9 +559,7 @@ On success, an integer descriptor for the created session (valid within the current .I /dev/crypto namespace) -is stored into the -.B ses -member. +is returned. .TP .B NCRIO_SESSION_UPDATE @@ -410,31 +568,53 @@ Update an existing crypto session with new data or perform a single operation using the session context (for operations, such as public key encryption, that work on separate units of data). -The parameter points to \fBstruct ncr_session_op_st\fP, which specifies: +The parameter points to \fBstruct ncr_session_update\fP, +which specifies the descriptor of the session in the +.B ses +member. + +The following input attributes are recognized: .RS -.IP \fBses\fP -The integer descriptor of the session. -.IP \fBtype\fP -Type of the data references used for this operation, either -.B NCR_KEY_DATA -or -.B NCR_DIRECT_DATA. -.IP "\fBdata.udata.input\fP, \fBdata.udata.input_size\fP" -If \fBtype == NCR_DIRECT_DATA\fP, input data for the operation. -.IP \fBdata.kdata.input\fP -If \fBtype == NCR_KEY_DATA\fP, integer key descriptor serving as input for -the operation. +.IP \fBNCR_ATTR_IV\fP +Only used for some operations and algorithms. +Optional, +IV from session initialization or left from last update is used if not present. +.IP \fBNCR_ATTR_UPDATE_INPUT_DATA\fP +A +.B struct ncr_session_input_data +specifying input for the operation in its +.B data +and +.B data_size +members. +.IP \fBNCR_ATTR_UPDATE_INPUT_KEY_AS_DATA\fP +An 32-bit unsigned integer in native byte order +specifying the key descriptor serving as input for the operation. This can be currently used only to compute or verify a signature or hash of a symmetric key: the keying material is directly used as input data for the underlying hash. -.IP "\fBdata.udata.output\fP, \fBdata,udata.output_size\fP" -If \fBtype == NCR_DIRECT_DATA\fP, output buffer for the operation. -.IP "\fBdata.kdata.output\fP, \fBdata,kdata.output_size\fP" -If \fBtype == NCR_KEY_DATA\fP, output buffer for the operation. +.IP \fBNCR_ATTR_UPDATE_OUTPUT_BUFFER\fP +Mandatory for some operations and algorithms. +A +.B struct ncr_session_output_buffer +specifying buffer for operation output in its +.B buffer +and +.B buffer_size +members. +On success the size of output is written to the variable pointed to by the +.B result_size_ptr +member. .RE .IP +It is mandatory to include one of the +.B NCR_ATTR_UPDATE_INPUT_DATA +and +.B NCR_ATTR_UPDATE_INPUT_KEY_AS_DATA +attributes. + For the .B NCR_OP_ENCRYPT and @@ -445,10 +625,6 @@ resulting in an output data block of the same size; for operations using public-key cryptography, a single operation is performed on the input data, resulting in output data. -In both cases, -the relevant -.B output_data -member is set to the size of valid output data on success. For the .B NCR_OP_SIGN @@ -462,14 +638,36 @@ no output data is produced. .B NCRIO_SESSION_FINAL Finalize an existing crypto session and deallocate it. -The parameter points to \fBstruct ncr_session_op_st\fP, as described in the -.B NCRIO_SESSION_UPDATE -section above. -If the parameter specifies valid input data, -it is processed as if using \fBNCRIO_SESSION_UPDATE\fP; +The parameter points to \fBstruct ncr_session_final\fP, +which specifies the descriptor of the session in the +.B ses +member. + +If one of the +.B NCR_ATTR_UPDATE_INPUT_DATA +and +.B NCR_ATTR_UPDATE_INPUT_KEY_AS_DATA +attributes is present, +all attributes are first processed as if using \fBNCRIO_SESSION_UPDATE\fP; thus, the last update operation can be performed together with the finalization in one step. +The following input attributes are recognized: + +.RS +.IP \fBNCR_ATTR_FINAL_INPUT_DATA\fP +Mandatory for some operations and algorithms. +A +.B struct ncr_session_input_data +as described above, specifying input for the operation. +.IP \fBNCR_ATTR_FINAL_OUTPUT_BUFFER\fP +Mandatory for some operations and algorithms. +A +.B struct ncr_session_output_buffer +as described above, specifying buffer for operation output. +.RE + +.IP There is no specific finalization operation performed for .B NCR_OP_ENCRYPT and \fBNCR_OP_DECRYPT\fP. @@ -482,23 +680,19 @@ the signature is created and written as output data. For the .B NCR_OP_VERIFY operation, -a signature specified as input using the -.I output data -fields is verified; -the result of this operation -(\fBNCR_SUCCESS\fP or \fBNCR_VERIFICATION_FAILED\fP) -will be stored into the -.B err -member. -(Note that the +a signature specified as input is verified and +the result of this operation is returned: +non-zero for a valid signature, zero for an invalid signature. +Note that the .BR ioctl (2) -operation will indicate success even if the signature verification fails, -as long all inputs were specified correctly.) +operation return value will be non-negative, i.e. "success", +even if the signature verification fails, +as long all inputs were specified correctly. The session will be deallocated even if the .B NCRIO_SESSION_FINAL operation reports an error, -as long as valid session descriptor was specified. +as long as a valid session descriptor was specified. .TP .B NCRIO_SESSION_ONCE @@ -507,18 +701,19 @@ allocating a temporary session, supplying a single instance of data, and finalizing the session in one operation. -The parameter points to \fBstruct ncr_session_once_op_st\fP, -which includes arguments for one +The parameter points to \fBstruct ncr_session_once\fP, +which specifies the operation to perform in the +.B op +member. + +The attributes handled as if by passing to a .B NCRIO_SESSION_INIT -and one +operation followed by a .B NCRIO_SESSION_FINAL -operation. -The -.B ses -member for the +operation, +and the return value of the .B NCRIO_SESSION_FINAL -sub-operation is ignored, -the sub-operation automatically uses the temporary session. +is returned on success. .TP .B NCRIO_MASTER_KEY_SET @@ -530,15 +725,14 @@ Once a master key is set, it can be changed only by rebooting the system and setting a different key. -The parameter points to \fBstruct ncr_master_key_st\fP, which specifies: -.RS -.IP \fBkey\fP -Pointer to the key material in user space. -.IP \fBkey_size\fP -Size of the key material in bytes. -.RE +The parameter points to \fBstruct ncr_master_key_set\fP, +which specifies the key material in user space using the +.B key +and +.B key_size +members. -Currently only an AES key with size 16, 24, or 32 bytes is acceptable. +Only an AES key with size 16, 24, or 32 bytes is currently acceptable. .SH CONFIGURATION The diff --git a/cryptodev.h b/cryptodev.h deleted file mode 100644 index 4a546b88d2d..00000000000 --- a/cryptodev.h +++ /dev/null @@ -1,154 +0,0 @@ -/* This is a source compatible implementation with the original API of - * cryptodev by Angelos D. Keromytis, found at openbsd cryptodev.h. - * Placed under public domain */ - -#ifndef L_CRYPTODEV_H -#define L_CRYPTODEV_H - -#include <linux/types.h> -#ifndef __KERNEL__ -#define __user -#endif - -/* API extensions for linux */ -#define CRYPTO_HMAC_MAX_KEY_LEN 512 -#define CRYPTO_CIPHER_MAX_KEY_LEN 64 - -/* All the supported algorithms - */ -typedef enum { - CRYPTO_DES_CBC=1, - CRYPTO_3DES_CBC=2, - CRYPTO_BLF_CBC=3, - CRYPTO_CAST_CBC=4, - CRYPTO_SKIPJACK_CBC=5, - CRYPTO_MD5_HMAC=6, - CRYPTO_SHA1_HMAC=7, - CRYPTO_RIPEMD160_HMAC=8, - CRYPTO_MD5_KPDK=9, - CRYPTO_SHA1_KPDK=10, - CRYPTO_RIJNDAEL128_CBC=11, - CRYPTO_AES_CBC=CRYPTO_RIJNDAEL128_CBC, - CRYPTO_ARC4=12, - CRYPTO_MD5=13, - CRYPTO_SHA1=14, - CRYPTO_DEFLATE_COMP=15, - CRYPTO_NULL=16, - CRYPTO_LZS_COMP=17, - CRYPTO_SHA2_256_HMAC=18, - CRYPTO_SHA2_384_HMAC=19, - CRYPTO_SHA2_512_HMAC=20, - CRYPTO_AES_CTR=21, - CRYPTO_AES_XTS=22, - - CRYPTO_CAMELLIA_CBC=101, - CRYPTO_RIPEMD160, - CRYPTO_SHA2_256, - CRYPTO_SHA2_384, - CRYPTO_SHA2_512, - CRYPTO_ALGORITHM_ALL, /* Keep updated - see below */ -} cryptodev_crypto_op_t; -#define CRYPTO_ALGORITHM_MAX (CRYPTO_ALGORITHM_ALL - 1) - -/* Values for ciphers */ -#define DES_BLOCK_LEN 8 -#define DES3_BLOCK_LEN 8 -#define RIJNDAEL128_BLOCK_LEN 16 -#define AES_BLOCK_LEN RIJNDAEL128_BLOCK_LEN -#define CAMELLIA_BLOCK_LEN -#define BLOWFISH_BLOCK_LEN 8 -#define SKIPJACK_BLOCK_LEN 8 -#define CAST128_BLOCK_LEN 8 - -/* the maximum of the above */ -#define EALG_MAX_BLOCK_LEN 16 - -/* Values for hashes/MAC */ -#define AALG_MAX_RESULT_LEN 64 - -/* input of CIOCGSESSION */ -struct session_op { - /* Specify either cipher or mac - */ - __u32 cipher; /* cryptodev_crypto_op_t */ - __u32 mac; /* cryptodev_crypto_op_t */ - - __u32 keylen; - __u8 __user *key; - __u32 mackeylen; - __u8 __user *mackey; - - __u32 ses; /* session identifier */ -}; - -#define COP_ENCRYPT 0 -#define COP_DECRYPT 1 - -/* input of CIOCCRYPT */ - struct crypt_op { - __u32 ses; /* session identifier */ - __u16 op; /* COP_ENCRYPT or COP_DECRYPT */ - __u16 flags; /* no usage so far, use 0 */ - __u32 len; /* length of source data */ - __u8 __user *src; /* source data */ - __u8 __user *dst; /* pointer to output data */ - __u8 __user *mac; /* pointer to output data for hash/MAC operations */ - __u8 __user *iv; /* initialization vector for encryption operations */ -}; - -/* Stuff for bignum arithmetic and public key - * cryptography - not supported yet by linux - * cryptodev. - */ - -#define CRYPTO_ALG_FLAG_SUPPORTED 1 -#define CRYPTO_ALG_FLAG_RNG_ENABLE 2 -#define CRYPTO_ALG_FLAG_DSA_SHA 4 - -struct crparam { - __u8* crp_p; - __u32 crp_nbits; -}; - -#define CRK_MAXPARAM 8 - -/* input of CIOCKEY */ -struct crypt_kop { - __u32 crk_op; /* cryptodev_crk_ot_t */ - __u32 crk_status; - __u16 crk_iparams; - __u16 crk_oparams; - __u32 crk_pad1; - struct crparam crk_param[CRK_MAXPARAM]; -}; - -typedef enum { - CRK_MOD_EXP=0, - CRK_MOD_EXP_CRT=1, - CRK_DSA_SIGN=2, - CRK_DSA_VERIFY=3, - CRK_DH_COMPUTE_KEY=4, - CRK_ALGORITHM_ALL -} cryptodev_crk_op_t; - -#define CRK_ALGORITHM_MAX CRK_ALGORITHM_ALL-1 - -/* features to be queried with CIOCASYMFEAT ioctl - */ -#define CRF_MOD_EXP (1 << CRK_MOD_EXP) -#define CRF_MOD_EXP_CRT (1 << CRK_MOD_EXP_CRT) -#define CRF_DSA_SIGN (1 << CRK_DSA_SIGN) -#define CRF_DSA_VERIFY (1 << CRK_DSA_VERIFY) -#define CRF_DH_COMPUTE_KEY (1 << CRK_DH_COMPUTE_KEY) - - -/* ioctl's. Compatible with old linux cryptodev.h - */ -#define CRIOGET _IOWR('c', 101, __u32) -#define CIOCGSESSION _IOWR('c', 102, struct session_op) -#define CIOCFSESSION _IOW('c', 103, __u32) -#define CIOCCRYPT _IOWR('c', 104, struct crypt_op) -#define CIOCKEY _IOWR('c', 105, struct crypt_kop) -#define CIOCASYMFEAT _IOR('c', 106, __u32) - -#endif /* L_CRYPTODEV_H */ diff --git a/cryptodev_cipher.c b/cryptodev_cipher.c index 832202789b6..1fb11473715 100644 --- a/cryptodev_cipher.c +++ b/cryptodev_cipher.c @@ -31,7 +31,6 @@ #include <linux/uaccess.h> #include <crypto/algapi.h> #include <crypto/hash.h> -#include "cryptodev.h" #include "cryptodev_int.h" @@ -217,7 +216,7 @@ ssize_t cryptodev_cipher_decrypt( struct cipher_data* cdata, const struct scatte /* Hash functions */ -int cryptodev_hash_init( struct hash_data* hdata, const char* alg_name, int hmac_mode, void * mackey, size_t mackeylen) +int cryptodev_hash_init(struct hash_data *hdata, const char *alg_name, const void *mackey, size_t mackeylen) { int ret; @@ -229,7 +228,7 @@ int ret; } /* Copy the key from user and set to TFM. */ - if (hmac_mode != 0) { + if (mackey != NULL) { ret = crypto_ahash_setkey(hdata->async.s, mackey, mackeylen); if (unlikely(ret)) { @@ -279,6 +278,48 @@ error: return ret; } +int cryptodev_hash_clone(struct hash_data *hdata, struct hash_data *old_data, + const void *mackey, size_t mackeylen) +{ + const char *algo; + void *state; + int ret; + + /* We want exactly the same driver. */ + algo = crypto_tfm_alg_driver_name(crypto_ahash_tfm(old_data->async.s)); + ret = cryptodev_hash_init(hdata, algo, mackey, mackeylen); + if (unlikely(ret != 0)) + return ret; + + state = kmalloc(crypto_ahash_statesize(hdata->async.s), GFP_KERNEL); + if (unlikely(state == NULL)) { + ret = -ENOMEM; + goto err; + } + + ret = crypto_ahash_export(old_data->async.request, state); + if (unlikely(ret != 0)) { + dprintk(0, KERN_ERR, "error exporting hash state\n"); + goto err; + } + ret = crypto_ahash_import(hdata->async.request, state); + if (unlikely(ret != 0)) { + dprintk(0,KERN_ERR, + "error in crypto_hash_init()\n"); + goto err; + } + + kfree(state); + + hdata->init = 1; + return 0; + +err: + kfree(state); + cryptodev_hash_deinit(hdata); + return ret; +} + void cryptodev_hash_deinit(struct hash_data* hdata) { if (hdata->init) { diff --git a/cryptodev_int.h b/cryptodev_int.h index b4059febbb0..4b140ba9820 100644 --- a/cryptodev_int.h +++ b/cryptodev_int.h @@ -11,7 +11,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/scatterlist.h> -#include <cryptodev.h> +#include <ncr.h> #define PFX "cryptodev: " #define dprintk(level,severity,format,a...) \ @@ -45,7 +45,7 @@ struct cipher_data struct crypto_ablkcipher* s; struct cryptodev_result *result; struct ablkcipher_request *request; - uint8_t iv[EALG_MAX_BLOCK_LEN]; + uint8_t iv[NCR_CIPHER_MAX_BLOCK_LEN]; } async; }; @@ -77,43 +77,8 @@ ssize_t cryptodev_hash_update( struct hash_data* hdata, struct scatterlist *sg, ssize_t _cryptodev_hash_update( struct hash_data* hdata, const void* data, size_t len); int cryptodev_hash_reset( struct hash_data* hdata); void cryptodev_hash_deinit(struct hash_data* hdata); -int cryptodev_hash_init( struct hash_data* hdata, const char* alg_name, int hmac_mode, void* mackey, size_t mackeylen); - -/* compatibility stuff */ -#ifdef CONFIG_COMPAT -#include <linux/compat.h> - -/* input of CIOCGSESSION */ -struct compat_session_op { - /* Specify either cipher or mac - */ - uint32_t cipher; /* cryptodev_crypto_op_t */ - uint32_t mac; /* cryptodev_crypto_op_t */ - - uint32_t keylen; - compat_uptr_t key; /* pointer to key data */ - uint32_t mackeylen; - compat_uptr_t mackey; /* pointer to mac key data */ - - uint32_t ses; /* session identifier */ -}; - -/* input of CIOCCRYPT */ - struct compat_crypt_op { - uint32_t ses; /* session identifier */ - uint16_t op; /* COP_ENCRYPT or COP_DECRYPT */ - uint16_t flags; /* no usage so far, use 0 */ - uint32_t len; /* length of source data */ - compat_uptr_t src; /* source data */ - compat_uptr_t dst; /* pointer to output data */ - compat_uptr_t mac; /* pointer to output data for hash/MAC operations */ - compat_uptr_t iv; /* initialization vector for encryption operations */ -}; - -/* compat ioctls, defined for the above structs */ -#define COMPAT_CIOCGSESSION _IOWR('c', 102, struct compat_session_op) -#define COMPAT_CIOCCRYPT _IOWR('c', 104, struct compat_crypt_op) - -#endif /* CONFIG_COMPAT */ +int cryptodev_hash_init(struct hash_data *hdata, const char *alg_name, const void *mackey, size_t mackeylen); +int cryptodev_hash_clone(struct hash_data *hdata, struct hash_data *old_data, + const void *mackey, size_t mackeylen); #endif /* CRYPTODEV_INT_H */ diff --git a/cryptodev_main.c b/cryptodev_main.c index da75672615a..2a11fdb1258 100644 --- a/cryptodev_main.c +++ b/cryptodev_main.c @@ -39,7 +39,6 @@ #include <linux/syscalls.h> #include <linux/pagemap.h> #include <linux/uaccess.h> -#include "cryptodev.h" #include <linux/scatterlist.h> #include "cryptodev_int.h" #include "ncr-int.h" @@ -50,452 +49,13 @@ MODULE_AUTHOR("Nikos Mavrogiannopoulos <nmav@gnutls.org>"); MODULE_DESCRIPTION("CryptoDev driver"); MODULE_LICENSE("GPL"); -/* ====== Compile-time config ====== */ - -#define CRYPTODEV_STATS - /* ====== Module parameters ====== */ int cryptodev_verbosity = 0; module_param(cryptodev_verbosity, int, 0644); MODULE_PARM_DESC(cryptodev_verbosity, "0: normal, 1: verbose, 2: debug"); -#ifdef CRYPTODEV_STATS -static int enable_stats = 0; -module_param(enable_stats, int, 0644); -MODULE_PARM_DESC(enable_stats, "collect statictics about cryptodev usage"); -#endif - /* ====== CryptoAPI ====== */ -struct fcrypt { - struct list_head list; - struct semaphore sem; -}; - -struct crypt_priv { - void * ncr; - struct fcrypt fcrypt; -}; - -#define FILL_SG(sg,ptr,len) \ - do { \ - (sg)->page = virt_to_page(ptr); \ - (sg)->offset = offset_in_page(ptr); \ - (sg)->length = len; \ - (sg)->dma_address = 0; \ - } while (0) - -struct csession { - struct list_head entry; - struct semaphore sem; - struct cipher_data cdata; - struct hash_data hdata; - uint32_t sid; -#ifdef CRYPTODEV_STATS -#if ! ((COP_ENCRYPT < 2) && (COP_DECRYPT < 2)) -#error Struct csession.stat uses COP_{ENCRYPT,DECRYPT} as indices. Do something! -#endif - unsigned long long stat[2]; - size_t stat_max_size, stat_count; -#endif - int array_size; - struct page **pages; - struct scatterlist *sg; -}; - -/* Prepare session for future use. */ -static int -crypto_create_session(struct fcrypt *fcr, struct session_op *sop) -{ - struct csession *ses_new = NULL, *ses_ptr; - int ret = 0; - const char *alg_name=NULL; - const char *hash_name=NULL; - int hmac_mode = 1; - - /* Does the request make sense? */ - if (unlikely(!sop->cipher && !sop->mac)) { - dprintk(1,KERN_DEBUG,"Both 'cipher' and 'mac' unset.\n"); - return -EINVAL; - } - - switch (sop->cipher) { - case 0: - break; - case CRYPTO_DES_CBC: - alg_name = "cbc(des)"; - break; - case CRYPTO_3DES_CBC: - alg_name = "cbc(des3_ede)"; - break; - case CRYPTO_BLF_CBC: - alg_name = "cbc(blowfish)"; - break; - case CRYPTO_AES_CBC: - alg_name = "cbc(aes)"; - break; - case CRYPTO_CAMELLIA_CBC: - alg_name = "cbc(camelia)"; - break; - case CRYPTO_AES_CTR: - alg_name = "ctr(aes)"; - break; - case CRYPTO_NULL: - alg_name = "ecb(cipher_null)"; - break; - default: - dprintk(1,KERN_DEBUG,"%s: bad cipher: %d\n", __func__, sop->cipher); - return -EINVAL; - } - - switch (sop->mac) { - case 0: - break; - case CRYPTO_MD5_HMAC: - hash_name = "hmac(md5)"; - break; - case CRYPTO_RIPEMD160_HMAC: - hash_name = "hmac(rmd160)"; - break; - case CRYPTO_SHA1_HMAC: - hash_name = "hmac(sha1)"; - break; - case CRYPTO_SHA2_256_HMAC: - hash_name = "hmac(sha256)"; - break; - case CRYPTO_SHA2_384_HMAC: - hash_name = "hmac(sha384)"; - break; - case CRYPTO_SHA2_512_HMAC: - hash_name = "hmac(sha512)"; - break; - - /* non-hmac cases */ - case CRYPTO_MD5: - hash_name = "md5"; - hmac_mode = 0; - break; - case CRYPTO_RIPEMD160: - hash_name = "rmd160"; - hmac_mode = 0; - break; - case CRYPTO_SHA1: - hash_name = "sha1"; - hmac_mode = 0; - break; - case CRYPTO_SHA2_256: - hash_name = "sha256"; - hmac_mode = 0; - break; - case CRYPTO_SHA2_384: - hash_name = "sha384"; - hmac_mode = 0; - break; - case CRYPTO_SHA2_512: - hash_name = "sha512"; - hmac_mode = 0; - break; - - default: - dprintk(1,KERN_DEBUG,"%s: bad mac: %d\n", __func__, sop->mac); - return -EINVAL; - } - - /* Create a session and put it to the list. */ - ses_new = kzalloc(sizeof(*ses_new), GFP_KERNEL); - if(!ses_new) { - return -ENOMEM; - } - - /* Set-up crypto transform. */ - if (alg_name) { - uint8_t keyp[CRYPTO_CIPHER_MAX_KEY_LEN]; - - if (unlikely(sop->keylen > CRYPTO_CIPHER_MAX_KEY_LEN)) { - dprintk(1,KERN_DEBUG,"Setting key failed for %s-%zu.\n", - alg_name, (size_t)sop->keylen*8); - ret = -EINVAL; - goto error_cipher; - } - - if (unlikely(copy_from_user(keyp, sop->key, sop->keylen))) { - ret = -EFAULT; - goto error_cipher; - } - - ret = cryptodev_cipher_init(&ses_new->cdata, alg_name, keyp, sop->keylen); - if (ret < 0) { - dprintk(1,KERN_DEBUG,"%s: Failed to load cipher for %s\n", __func__, - alg_name); - ret = -EINVAL; - goto error_cipher; - } - } - - if (hash_name) { - uint8_t keyp[CRYPTO_HMAC_MAX_KEY_LEN]; - - if (unlikely(sop->mackeylen > CRYPTO_HMAC_MAX_KEY_LEN)) { - dprintk(1,KERN_DEBUG,"Setting key failed for %s-%zu.\n", - alg_name, (size_t)sop->mackeylen*8); - ret = -EINVAL; - goto error_hash; - } - - if (unlikely(copy_from_user(keyp, sop->mackey, - sop->mackeylen))) { - ret = -EFAULT; - goto error_hash; - } - - ret = cryptodev_hash_init(&ses_new->hdata, hash_name, hmac_mode, keyp, sop->mackeylen); - if (ret != 0) { - dprintk(1,KERN_DEBUG,"%s: Failed to load hash for %s\n", __func__, - hash_name); - ret = -EINVAL; - goto error_hash; - } - } - - ses_new->array_size = DEFAULT_PREALLOC_PAGES; - dprintk(2, KERN_DEBUG, "%s: preallocating for %d user pages\n", - __func__, ses_new->array_size); - ses_new->pages = kzalloc(ses_new->array_size * - sizeof(struct page *), GFP_KERNEL); - ses_new->sg = kzalloc(ses_new->array_size * - sizeof(struct scatterlist), GFP_KERNEL); - if (ses_new->sg == NULL || ses_new->pages == NULL) { - dprintk(0,KERN_DEBUG,"Memory error\n"); - ret = -ENOMEM; - goto error_hash; - } - - /* put the new session to the list */ - get_random_bytes(&ses_new->sid, sizeof(ses_new->sid)); - init_MUTEX(&ses_new->sem); - - down(&fcr->sem); -restart: - list_for_each_entry(ses_ptr, &fcr->list, entry) { - /* Check for duplicate SID */ - if (unlikely(ses_new->sid == ses_ptr->sid)) { - get_random_bytes(&ses_new->sid, sizeof(ses_new->sid)); - /* Unless we have a broken RNG this - shouldn't loop forever... ;-) */ - goto restart; - } - } - - list_add(&ses_new->entry, &fcr->list); - up(&fcr->sem); - - /* Fill in some values for the user. */ - sop->ses = ses_new->sid; - - return 0; - -error_hash: - cryptodev_cipher_deinit( &ses_new->cdata); - kfree(ses_new->sg); - kfree(ses_new->pages); -error_cipher: - if (ses_new) kfree(ses_new); - - return ret; - -} - -/* Everything that needs to be done when remowing a session. */ -static inline void -crypto_destroy_session(struct csession *ses_ptr) -{ - if(down_trylock(&ses_ptr->sem)) { - dprintk(2, KERN_DEBUG, "Waiting for semaphore of sid=0x%08X\n", - ses_ptr->sid); - down(&ses_ptr->sem); - } - dprintk(2, KERN_DEBUG, "Removed session 0x%08X\n", ses_ptr->sid); -#if defined(CRYPTODEV_STATS) - if(enable_stats) - dprintk(2, KERN_DEBUG, - "Usage in Bytes: enc=%llu, dec=%llu, max=%zu, avg=%lu, cnt=%zu\n", - ses_ptr->stat[COP_ENCRYPT], ses_ptr->stat[COP_DECRYPT], - ses_ptr->stat_max_size, ses_ptr->stat_count > 0 - ? ((unsigned long)(ses_ptr->stat[COP_ENCRYPT]+ - ses_ptr->stat[COP_DECRYPT]) / - ses_ptr->stat_count) : 0, - ses_ptr->stat_count); -#endif - cryptodev_cipher_deinit(&ses_ptr->cdata); - cryptodev_hash_deinit(&ses_ptr->hdata); - dprintk(2, KERN_DEBUG, "%s: freeing space for %d user pages\n", - __func__, ses_ptr->array_size); - kfree(ses_ptr->pages); - kfree(ses_ptr->sg); - - up(&ses_ptr->sem); - kfree(ses_ptr); -} - -/* Look up a session by ID and remove. */ -static int -crypto_finish_session(struct fcrypt *fcr, uint32_t sid) -{ - struct csession *tmp, *ses_ptr; - struct list_head *head; - int ret = 0; - - down(&fcr->sem); - head = &fcr->list; - list_for_each_entry_safe(ses_ptr, tmp, head, entry) { - if(ses_ptr->sid == sid) { - list_del(&ses_ptr->entry); - crypto_destroy_session(ses_ptr); - break; - } - } - - if (unlikely(!ses_ptr)) { - dprintk(1, KERN_ERR, "Session with sid=0x%08X not found!\n", sid); - ret = -ENOENT; - } - up(&fcr->sem); - - return ret; -} - -/* Remove all sessions when closing the file */ -static int -crypto_finish_all_sessions(struct fcrypt *fcr) -{ - struct csession *tmp, *ses_ptr; - struct list_head *head; - - down(&fcr->sem); - - head = &fcr->list; - list_for_each_entry_safe(ses_ptr, tmp, head, entry) { - list_del(&ses_ptr->entry); - crypto_destroy_session(ses_ptr); - } - up(&fcr->sem); - - return 0; -} - -/* Look up session by session ID. The returned session is locked. */ -static struct csession * -crypto_get_session_by_sid(struct fcrypt *fcr, uint32_t sid) -{ - struct csession *ses_ptr; - - down(&fcr->sem); - list_for_each_entry(ses_ptr, &fcr->list, entry) { - if(ses_ptr->sid == sid) { - down(&ses_ptr->sem); - break; - } - } - up(&fcr->sem); - - return ses_ptr; -} - -static int -hash_n_crypt(struct csession *ses_ptr, struct crypt_op *cop, - struct scatterlist *src_sg, struct scatterlist *dst_sg, uint32_t len) -{ - int ret; - - /* Always hash before encryption and after decryption. Maybe - * we should introduce a flag to switch... TBD later on. - */ - if (cop->op == COP_ENCRYPT) { - if (ses_ptr->hdata.init != 0) { - ret = cryptodev_hash_update(&ses_ptr->hdata, src_sg, len); - if (unlikely(ret)) - goto out_err; - } - if (ses_ptr->cdata.init != 0) { - ret = cryptodev_cipher_encrypt( &ses_ptr->cdata, src_sg, dst_sg, len); - - if (unlikely(ret)) - goto out_err; - } - } else { - if (ses_ptr->cdata.init != 0) { - ret = cryptodev_cipher_decrypt( &ses_ptr->cdata, src_sg, dst_sg, len); - - if (unlikely(ret)) - goto out_err; - } - - if (ses_ptr->hdata.init != 0) { - ret = cryptodev_hash_update(&ses_ptr->hdata, dst_sg, len); - if (unlikely(ret)) - goto out_err; - } - } - return 0; -out_err: - dprintk(0, KERN_ERR, "CryptoAPI failure: %d\n",ret); - return ret; -} - -/* This is the main crypto function - feed it with plaintext - and get a ciphertext (or vice versa :-) */ -static int -__crypto_run_std(struct csession *ses_ptr, struct crypt_op *cop) -{ - char *data; - char __user *src, *dst; - struct scatterlist sg; - size_t nbytes, bufsize; - int ret = 0; - - nbytes = cop->len; - data = (char*)__get_free_page(GFP_KERNEL); - - if (unlikely(!data)) { - return -ENOMEM; - } - bufsize = PAGE_SIZE < nbytes ? PAGE_SIZE : nbytes; - - src = cop->src; - dst = cop->dst; - - while(nbytes > 0) { - size_t current_len = nbytes > bufsize ? bufsize : nbytes; - - if (unlikely(copy_from_user(data, src, current_len))) { - ret = -EFAULT; - break; - } - - sg_init_one(&sg, data, current_len); - - ret = hash_n_crypt(ses_ptr, cop, &sg, &sg, current_len); - - if (unlikely(ret)) - break; - - if (ses_ptr->cdata.init != 0) { - if (unlikely(copy_to_user(dst, data, current_len))) { - ret = -EFAULT; - break; - } - } - - dst += current_len; - nbytes -= current_len; - src += current_len; - } - - free_page((unsigned long)data); - return ret; -} - -#ifndef DISABLE_ZCOPY void release_user_pages(struct page **pg, int pagecount) { @@ -538,415 +98,58 @@ int __get_userbuf(uint8_t __user *addr, uint32_t len, int write, return 0; } -/* make cop->src and cop->dst available in scatterlists */ -static int get_userbuf(struct csession *ses, - struct crypt_op *cop, struct scatterlist **src_sg, - struct scatterlist **dst_sg, int *tot_pages) -{ - int src_pagecount, dst_pagecount = 0, pagecount, write_src = 1; - - if (cop->src == NULL) { - return -EINVAL; - } - - src_pagecount = PAGECOUNT(cop->src, cop->len); - if (!ses->cdata.init) { /* hashing only */ - write_src = 0; - } else if (cop->src != cop->dst) { /* non-in-situ transformation */ - if (cop->dst == NULL) { - return -EINVAL; - } - dst_pagecount = PAGECOUNT(cop->dst, cop->len); - write_src = 0; - } - (*tot_pages) = pagecount = src_pagecount + dst_pagecount; - - if (pagecount > ses->array_size) { - struct scatterlist *sg; - struct page **pages; - int array_size; - - for (array_size = ses->array_size; array_size < pagecount; - array_size *= 2) - ; - - dprintk(2, KERN_DEBUG, "%s: reallocating to %d elements\n", - __func__, array_size); - pages = krealloc(ses->pages, array_size * sizeof(struct page *), - GFP_KERNEL); - if (pages == NULL) - return -ENOMEM; - ses->pages = pages; - sg = krealloc(ses->sg, array_size * sizeof(struct scatterlist), - GFP_KERNEL); - if (sg == NULL) - return -ENOMEM; - ses->sg = sg; - ses->array_size = array_size; - } - - if (__get_userbuf(cop->src, cop->len, write_src, - src_pagecount, ses->pages, ses->sg)) { - dprintk(1, KERN_ERR, "failed to get user pages for data input\n"); - return -EINVAL; - } - (*src_sg) = (*dst_sg) = ses->sg; - - if (dst_pagecount) { - (*dst_sg) = ses->sg + src_pagecount; - - if (__get_userbuf(cop->dst, cop->len, 1, dst_pagecount, - ses->pages + src_pagecount, *dst_sg)) { - dprintk(1, KERN_ERR, "failed to get user pages for data output\n"); - release_user_pages(ses->pages, src_pagecount); - return -EINVAL; - } - } - return 0; -} - -/* This is the main crypto function - zero-copy edition */ -static int -__crypto_run_zc(struct csession *ses_ptr, struct crypt_op *cop) -{ - struct scatterlist *src_sg, *dst_sg; - int ret = 0, pagecount; - - ret = get_userbuf(ses_ptr, cop, &src_sg, &dst_sg, &pagecount); - if (unlikely(ret)) { - dprintk(1, KERN_ERR, "Error getting user pages. Falling back to non zero copy.\n"); - return __crypto_run_std(ses_ptr, cop); - } - - ret = hash_n_crypt(ses_ptr, cop, src_sg, dst_sg, cop->len); - - release_user_pages(ses_ptr->pages, pagecount); - return ret; -} - -#endif /* DISABLE_ZCOPY */ - -static int crypto_run(struct fcrypt *fcr, struct crypt_op *cop) -{ - struct csession *ses_ptr; - uint8_t hash_output[AALG_MAX_RESULT_LEN]; - int ret; - - if (unlikely(cop->op != COP_ENCRYPT && cop->op != COP_DECRYPT)) { - dprintk(1, KERN_DEBUG, "invalid operation op=%u\n", cop->op); - return -EINVAL; - } - - /* this also enters ses_ptr->sem */ - ses_ptr = crypto_get_session_by_sid(fcr, cop->ses); - if (unlikely(!ses_ptr)) { - dprintk(1, KERN_ERR, "invalid session ID=0x%08X\n", cop->ses); - return -EINVAL; - } - - if (ses_ptr->hdata.init != 0) { - ret = cryptodev_hash_reset(&ses_ptr->hdata); - if (unlikely(ret)) { - dprintk(1, KERN_ERR, - "error in cryptodev_hash_reset()\n"); - goto out_unlock; - } - } - - if (ses_ptr->cdata.init != 0) { - int blocksize = ses_ptr->cdata.blocksize; - - if (unlikely(cop->len % blocksize)) { - dprintk(1, KERN_ERR, - "data size (%u) isn't a multiple of block size (%u)\n", - cop->len, blocksize); - ret = -EINVAL; - goto out_unlock; - } - - if (cop->iv) { - uint8_t iv[EALG_MAX_BLOCK_LEN]; - - ret = copy_from_user(iv, cop->iv, min( (int)sizeof(iv), (ses_ptr->cdata.ivsize))); - if (unlikely(ret)) { - dprintk(1, KERN_ERR, "error copying IV (%d bytes)\n", min( (int)sizeof(iv), (ses_ptr->cdata.ivsize))); - ret = -EFAULT; - goto out_unlock; - } - - cryptodev_cipher_set_iv(&ses_ptr->cdata, iv, ses_ptr->cdata.ivsize); - } - } - -#ifdef DISABLE_ZCOPY - ret = __crypto_run_std(ses_ptr, cop); -#else /* normal */ - ret = __crypto_run_zc(ses_ptr, cop); -#endif - if (unlikely(ret)) - goto out_unlock; - - if (ses_ptr->hdata.init != 0) { - ret = cryptodev_hash_final(&ses_ptr->hdata, hash_output); - if (unlikely(ret)) { - dprintk(0, KERN_ERR, "CryptoAPI failure: %d\n",ret); - goto out_unlock; - } - - if (unlikely(copy_to_user(cop->mac, hash_output, ses_ptr->hdata.digestsize))) { - ret = -EFAULT; - goto out_unlock; - } - } - -#if defined(CRYPTODEV_STATS) - if (enable_stats) { - /* this is safe - we check cop->op at the function entry */ - ses_ptr->stat[cop->op] += cop->len; - if (ses_ptr->stat_max_size < cop->len) - ses_ptr->stat_max_size = cop->len; - ses_ptr->stat_count++; - } -#endif - -out_unlock: - up(&ses_ptr->sem); - return ret; -} - - /* ====== /dev/crypto ====== */ static int cryptodev_open(struct inode *inode, struct file *filp) { - struct crypt_priv *pcr; - - pcr = kmalloc(sizeof(*pcr), GFP_KERNEL); - if(!pcr) - return -ENOMEM; + void *ncr; - memset(pcr, 0, sizeof(*pcr)); - init_MUTEX(&pcr->fcrypt.sem); - INIT_LIST_HEAD(&pcr->fcrypt.list); - - pcr->ncr = ncr_init_lists(); - if (pcr->ncr == NULL) { - kfree(pcr); + ncr = ncr_init_lists(); + if (ncr == NULL) { return -ENOMEM; } - filp->private_data = pcr; + filp->private_data = ncr; return 0; } static int cryptodev_release(struct inode *inode, struct file *filp) { - struct crypt_priv *pcr = filp->private_data; + void *ncr = filp->private_data; - if(pcr) { - crypto_finish_all_sessions(&pcr->fcrypt); - ncr_deinit_lists(pcr->ncr); - kfree(pcr); + if (ncr) { + ncr_deinit_lists(ncr); filp->private_data = NULL; } return 0; } -static int -clonefd(struct file *filp) +static long +cryptodev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { - int ret; - ret = get_unused_fd(); - if (ret >= 0) { - get_file(filp); - fd_install(ret, filp); - } + void *ncr = filp->private_data; - return ret; -} - -static int -cryptodev_ioctl(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg_) -{ - void __user *arg = (void __user *)arg_; - int __user *p = arg; - struct session_op sop; - struct crypt_op cop; - struct crypt_priv *pcr = filp->private_data; - struct fcrypt * fcr; - uint32_t ses; - int ret, fd; - - if (unlikely(!pcr)) + if (unlikely(!ncr)) BUG(); - fcr = &pcr->fcrypt; - - switch (cmd) { - case CIOCASYMFEAT: - return put_user(0, p); - case CRIOGET: - fd = clonefd(filp); - ret = put_user(fd, p); - if (unlikely(ret)) { - sys_close(fd); - return ret; - } - return ret; - case CIOCGSESSION: - if (unlikely(copy_from_user(&sop, arg, sizeof(sop)))) - return -EFAULT; - - ret = crypto_create_session(fcr, &sop); - if (unlikely(ret)) - return ret; - ret = copy_to_user(arg, &sop, sizeof(sop)); - if (unlikely(ret)) { - crypto_finish_session(fcr, sop.ses); - return -EFAULT; - } - return ret; - case CIOCFSESSION: - ret = get_user(ses, (uint32_t __user *)arg); - if (unlikely(ret)) - return ret; - ret = crypto_finish_session(fcr, ses); - return ret; - case CIOCCRYPT: - if (unlikely(copy_from_user(&cop, arg, sizeof(cop)))) - return -EFAULT; - - ret = crypto_run(fcr, &cop); - if (unlikely(ret)) - return ret; - if (unlikely(copy_to_user(arg, &cop, sizeof(cop)))) - return -EFAULT; - return 0; - - default: - return ncr_ioctl(pcr->ncr, cmd, arg_); - } + return ncr_ioctl(ncr, cmd, arg); } /* compatibility code for 32bit userlands */ #ifdef CONFIG_COMPAT -static inline void -compat_to_session_op(struct compat_session_op *compat, struct session_op *sop) -{ - sop->cipher = compat->cipher; - sop->mac = compat->mac; - sop->keylen = compat->keylen; - - sop->key = compat_ptr(compat->key); - sop->mackeylen = compat->mackeylen; - sop->mackey = compat_ptr(compat->mackey); - sop->ses = compat->ses; -} - -static inline void -session_op_to_compat(struct session_op *sop, struct compat_session_op *compat) -{ - compat->cipher = sop->cipher; - compat->mac = sop->mac; - compat->keylen = sop->keylen; - - compat->key = ptr_to_compat(sop->key); - compat->mackeylen = sop->mackeylen; - compat->mackey = ptr_to_compat(sop->mackey); - compat->ses = sop->ses; -} - -static inline void -compat_to_crypt_op(struct compat_crypt_op *compat, struct crypt_op *cop) -{ - cop->ses = compat->ses; - cop->op = compat->op; - cop->flags = compat->flags; - cop->len = compat->len; - - cop->src = compat_ptr(compat->src); - cop->dst = compat_ptr(compat->dst); - cop->mac = compat_ptr(compat->mac); - cop->iv = compat_ptr(compat->iv); -} - -static inline void -crypt_op_to_compat(struct crypt_op *cop, struct compat_crypt_op *compat) -{ - compat->ses = cop->ses; - compat->op = cop->op; - compat->flags = cop->flags; - compat->len = cop->len; - - compat->src = ptr_to_compat(cop->src); - compat->dst = ptr_to_compat(cop->dst); - compat->mac = ptr_to_compat(cop->mac); - compat->iv = ptr_to_compat(cop->iv); -} - static long -cryptodev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg_) +cryptodev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - void __user *arg = (void __user *)arg_; - struct fcrypt *fcr = file->private_data; - struct session_op sop; - struct compat_session_op compat_sop; - struct crypt_op cop; - struct compat_crypt_op compat_cop; - int ret; - - if (unlikely(!fcr)) + void *ncr = file->private_data; + + if (unlikely(!ncr)) BUG(); - switch (cmd) { - case CIOCASYMFEAT: - case CRIOGET: - case CIOCFSESSION: - return cryptodev_ioctl(NULL, file, cmd, arg_); - - case COMPAT_CIOCGSESSION: - if (unlikely(copy_from_user(&compat_sop, arg, - sizeof(compat_sop)))) - return -EFAULT; - compat_to_session_op(&compat_sop, &sop); - - ret = crypto_create_session(fcr, &sop); - if (unlikely(ret)) - return ret; - - session_op_to_compat(&sop, &compat_sop); - ret = copy_to_user(arg, &compat_sop, sizeof(compat_sop)); - if (unlikely(ret)) { - crypto_finish_session(fcr, sop.ses); - return -EFAULT; - } - return ret; - - case COMPAT_CIOCCRYPT: - if (unlikely(copy_from_user(&compat_cop, arg, - sizeof(compat_cop)))) - return -EFAULT; - - compat_to_crypt_op(&compat_cop, &cop); - - ret = crypto_run(fcr, &cop); - if (unlikely(ret)) - return ret; - - crypt_op_to_compat(&cop, &compat_cop); - if (unlikely(copy_to_user(arg, &compat_cop, - sizeof(compat_cop)))) - return -EFAULT; - return 0; - - default: - return -EINVAL; - } + return ncr_compat_ioctl(ncr, cmd, arg); } #endif /* CONFIG_COMPAT */ @@ -955,7 +158,7 @@ static const struct file_operations cryptodev_fops = { .owner = THIS_MODULE, .open = cryptodev_open, .release = cryptodev_release, - .ioctl = cryptodev_ioctl, + .unlocked_ioctl = cryptodev_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = cryptodev_compat_ioctl, #endif /* CONFIG_COMPAT */ diff --git a/examples/Makefile b/examples/Makefile index 100cc490600..9911100263e 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -1,19 +1,13 @@ CC = gcc CFLAGS = -Wall -g -O2 -progs := cipher hmac ncr pk speed +progs := ncr pk speed all: $(progs) -cipher: cipher.c - $(CC) $(CFLAGS) $< -o $@ - speed: speed.c $(CC) $(CFLAGS) $< -o $@ -hmac: hmac.c - $(CC) $(CFLAGS) $< -o $@ - ncr: ncr.c $(CC) $(CFLAGS) $< -o $@ @@ -23,9 +17,7 @@ pk: pk.c check: $(progs) ./ncr ./pk - ./cipher - ./hmac ./speed clean: - rm -f *.o *~ hmac cipher ncr pk speed
\ No newline at end of file + rm -f *.o *~ $(progs) diff --git a/examples/cipher.c b/examples/cipher.c deleted file mode 100644 index 52b4996f4a6..00000000000 --- a/examples/cipher.c +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Demo on how to use /dev/crypto device for ciphering. - * - * Placed under public domain. - * - */ -#include <stdint.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <fcntl.h> - -#include <sys/ioctl.h> -#include "../cryptodev.h" - -#define DATA_SIZE 4096 -#define BLOCK_SIZE 16 -#define KEY_SIZE 16 - -static int -test_crypto(int cfd) -{ - uint8_t plaintext[DATA_SIZE]; - uint8_t ciphertext[DATA_SIZE]; - uint8_t iv[BLOCK_SIZE]; - uint8_t key[KEY_SIZE]; - - struct session_op sess; - struct crypt_op cryp; - - memset(&sess, 0, sizeof(sess)); - memset(&cryp, 0, sizeof(cryp)); - - memset(plaintext, 0x15, sizeof(plaintext)); - memset(key, 0x33, sizeof(key)); - memset(iv, 0x03, sizeof(iv)); - - /* Get crypto session for AES128 */ - sess.cipher = CRYPTO_AES_CBC; - sess.keylen = KEY_SIZE; - sess.key = key; - if (ioctl(cfd, CIOCGSESSION, &sess)) { - perror("ioctl(CIOCGSESSION)"); - return 1; - } - - /* Encrypt data.in to data.encrypted */ - cryp.ses = sess.ses; - cryp.len = sizeof(plaintext); - cryp.src = plaintext; - cryp.dst = ciphertext; - cryp.iv = iv; - cryp.op = COP_ENCRYPT; - if (ioctl(cfd, CIOCCRYPT, &cryp)) { - perror("ioctl(CIOCCRYPT)"); - return 1; - } - - if (ioctl(cfd, CIOCGSESSION, &sess)) { - perror("ioctl(CIOCGSESSION)"); - return 1; - } - - /* Decrypt data.encrypted to data.decrypted */ - cryp.ses = sess.ses; - cryp.len = sizeof(plaintext); - cryp.src = ciphertext; - cryp.dst = ciphertext; - cryp.iv = iv; - cryp.op = COP_DECRYPT; - if (ioctl(cfd, CIOCCRYPT, &cryp)) { - perror("ioctl(CIOCCRYPT)"); - return 1; - } - - /* Verify the result */ - if (memcmp(plaintext, ciphertext, sizeof(plaintext)) != 0) { - fprintf(stderr, - "FAIL: Decrypted data are different from the input data.\n"); - return 1; - } else - printf("Test passed\n"); - - /* Finish crypto session */ - if (ioctl(cfd, CIOCFSESSION, &sess.ses)) { - perror("ioctl(CIOCFSESSION)"); - return 1; - } - - return 0; -} - -static int test_aes(int cfd) -{ - uint8_t plaintext1[BLOCK_SIZE]; - uint8_t ciphertext1[BLOCK_SIZE] = { 0xdf, 0x55, 0x6a, 0x33, 0x43, 0x8d, 0xb8, 0x7b, 0xc4, 0x1b, 0x17, 0x52, 0xc5, 0x5e, 0x5e, 0x49 }; - uint8_t iv1[BLOCK_SIZE]; - uint8_t key1[KEY_SIZE] = { 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - uint8_t plaintext2[BLOCK_SIZE] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00 }; - uint8_t ciphertext2[BLOCK_SIZE] = { 0xb7, 0x97, 0x2b, 0x39, 0x41, 0xc4, 0x4b, 0x90, 0xaf, 0xa7, 0xb2, 0x64, 0xbf, 0xba, 0x73, 0x87 }; - uint8_t iv2[BLOCK_SIZE]; - uint8_t key2[KEY_SIZE]; - - struct session_op sess; - struct crypt_op cryp; - - memset(&sess, 0, sizeof(sess)); - memset(&cryp, 0, sizeof(cryp)); - - memset(plaintext1, 0x0, sizeof(plaintext1)); - memset(iv1, 0x0, sizeof(iv1)); - - /* Get crypto session for AES128 */ - sess.cipher = CRYPTO_AES_CBC; - sess.keylen = KEY_SIZE; - sess.key = key1; - if (ioctl(cfd, CIOCGSESSION, &sess)) { - perror("ioctl(CIOCGSESSION)"); - return 1; - } - - /* Encrypt data.in to data.encrypted */ - cryp.ses = sess.ses; - cryp.len = sizeof(plaintext1); - cryp.src = plaintext1; - cryp.dst = plaintext1; - cryp.iv = iv1; - cryp.op = COP_ENCRYPT; - if (ioctl(cfd, CIOCCRYPT, &cryp)) { - perror("ioctl(CIOCCRYPT)"); - return 1; - } - - /* Verify the result */ - if (memcmp(plaintext1, ciphertext1, sizeof(plaintext1)) != 0) { - fprintf(stderr, - "FAIL: Decrypted data are different from the input data.\n"); - return 1; - } - - /* Test 2 */ - - memset(key2, 0x0, sizeof(key2)); - memset(iv2, 0x0, sizeof(iv2)); - - /* Get crypto session for AES128 */ - sess.cipher = CRYPTO_AES_CBC; - sess.keylen = KEY_SIZE; - sess.key = key2; - if (ioctl(cfd, CIOCGSESSION, &sess)) { - perror("ioctl(CIOCGSESSION)"); - return 1; - } - - /* Encrypt data.in to data.encrypted */ - cryp.ses = sess.ses; - cryp.len = sizeof(plaintext2); - cryp.src = plaintext2; - cryp.dst = plaintext2; - cryp.iv = iv2; - cryp.op = COP_ENCRYPT; - if (ioctl(cfd, CIOCCRYPT, &cryp)) { - perror("ioctl(CIOCCRYPT)"); - return 1; - } - - /* Verify the result */ - if (memcmp(plaintext2, ciphertext2, sizeof(plaintext2)) != 0) { - fprintf(stderr, - "FAIL: Decrypted data are different from the input data.\n"); - return 1; - } - - printf("AES Test passed\n"); - - /* Finish crypto session */ - if (ioctl(cfd, CIOCFSESSION, &sess.ses)) { - perror("ioctl(CIOCFSESSION)"); - return 1; - } - - return 0; -} - -int -main() -{ - int fd = -1, cfd = -1; - - /* Open the crypto device */ - fd = open("/dev/crypto", O_RDWR, 0); - if (fd < 0) { - perror("open(/dev/crypto)"); - return 1; - } - - /* Clone file descriptor */ - if (ioctl(fd, CRIOGET, &cfd)) { - perror("ioctl(CRIOGET)"); - return 1; - } - - /* Set close-on-exec (not really neede here) */ - if (fcntl(cfd, F_SETFD, 1) == -1) { - perror("fcntl(F_SETFD)"); - return 1; - } - - /* Run the test itself */ - if (test_aes(cfd)) - return 1; - - if (test_crypto(cfd)) - return 1; - - /* Close cloned descriptor */ - if (close(cfd)) { - perror("close(cfd)"); - return 1; - } - - /* Close the original descriptor */ - if (close(fd)) { - perror("close(fd)"); - return 1; - } - - return 0; -} - diff --git a/examples/hmac.c b/examples/hmac.c deleted file mode 100644 index c54d7419a34..00000000000 --- a/examples/hmac.c +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Demo on how to use /dev/crypto device for HMAC. - * - * Placed under public domain. - * - */ -#include <stdint.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <fcntl.h> - -#include <sys/ioctl.h> -#include "../cryptodev.h" - -#define DATA_SIZE 4096 -#define BLOCK_SIZE 16 -#define KEY_SIZE 16 -#define SHA1_HASH_LEN 20 - -static int -test_crypto(int cfd) -{ - struct { - uint8_t in[DATA_SIZE], - encrypted[DATA_SIZE], - decrypted[DATA_SIZE], - iv[BLOCK_SIZE], - key[KEY_SIZE]; - } data; - struct session_op sess; - struct crypt_op cryp; - uint8_t mac[AALG_MAX_RESULT_LEN]; - uint8_t oldmac[AALG_MAX_RESULT_LEN]; - uint8_t md5_hmac_out[] = "\x75\x0c\x78\x3e\x6a\xb0\xb5\x03\xea\xa8\x6e\x31\x0a\x5d\xb7\x38"; - uint8_t sha1_out[] = "\x8f\x82\x03\x94\xf9\x53\x35\x18\x20\x45\xda\x24\xf3\x4d\xe5\x2b\xf8\xbc\x34\x32"; - int i; - - memset(&sess, 0, sizeof(sess)); - memset(&cryp, 0, sizeof(cryp)); - - /* Use the garbage that is on the stack :-) */ - /* memset(&data, 0, sizeof(data)); */ - - /* SHA1 plain test */ - memset(mac, 0, sizeof(mac)); - - sess.cipher = 0; - sess.mac = CRYPTO_SHA1; - if (ioctl(cfd, CIOCGSESSION, &sess)) { - perror("ioctl(CIOCGSESSION)"); - return 1; - } - - cryp.ses = sess.ses; - cryp.len = sizeof("what do ya want for nothing?")-1; - cryp.src = "what do ya want for nothing?"; - cryp.mac = mac; - cryp.op = COP_ENCRYPT; - if (ioctl(cfd, CIOCCRYPT, &cryp)) { - perror("ioctl(CIOCCRYPT)"); - return 1; - } - - if (memcmp(mac, sha1_out, 20)!=0) { - printf("mac: "); - for (i=0;i<SHA1_HASH_LEN;i++) { - printf("%.2x", (uint8_t)mac[i]); - } - puts("\n"); - fprintf(stderr, "HASH test 1: failed\n"); - } else { - fprintf(stderr, "HASH test 1: passed\n"); - } - - /* MD5-HMAC test */ - memset(mac, 0, sizeof(mac)); - - sess.cipher = 0; - sess.mackey = (uint8_t*)"Jefe"; - sess.mackeylen = 4; - sess.mac = CRYPTO_MD5_HMAC; - if (ioctl(cfd, CIOCGSESSION, &sess)) { - perror("ioctl(CIOCGSESSION)"); - return 1; - } - - cryp.ses = sess.ses; - cryp.len = sizeof("what do ya want for nothing?")-1; - cryp.src = "what do ya want for nothing?"; - cryp.mac = mac; - cryp.op = COP_ENCRYPT; - if (ioctl(cfd, CIOCCRYPT, &cryp)) { - perror("ioctl(CIOCCRYPT)"); - return 1; - } - - if (memcmp(mac, md5_hmac_out, 16)!=0) { - printf("mac: "); - for (i=0;i<SHA1_HASH_LEN;i++) { - printf("%.2x", (uint8_t)mac[i]); - } - puts("\n"); - fprintf(stderr, "HMAC test 1: failed\n"); - } else { - fprintf(stderr, "HMAC test 1: passed\n"); - } - - /* Hash and encryption in one step test */ - sess.cipher = CRYPTO_AES_CBC; - sess.mac = CRYPTO_SHA1_HMAC; - sess.keylen = KEY_SIZE; - sess.key = data.key; - sess.mackeylen = 16; - sess.mackey = (uint8_t*)"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"; - if (ioctl(cfd, CIOCGSESSION, &sess)) { - perror("ioctl(CIOCGSESSION)"); - return 1; - } - - /* Encrypt data.in to data.encrypted */ - cryp.ses = sess.ses; - cryp.len = sizeof(data.in); - cryp.src = data.in; - cryp.dst = data.encrypted; - cryp.iv = data.iv; - cryp.mac = mac; - cryp.op = COP_ENCRYPT; - if (ioctl(cfd, CIOCCRYPT, &cryp)) { - perror("ioctl(CIOCCRYPT)"); - return 1; - } - - memcpy(oldmac, mac, sizeof(mac)); - - /* Decrypt data.encrypted to data.decrypted */ - cryp.src = data.encrypted; - cryp.dst = data.decrypted; - cryp.op = COP_DECRYPT; - if (ioctl(cfd, CIOCCRYPT, &cryp)) { - perror("ioctl(CIOCCRYPT)"); - return 1; - } - - /* Verify the result */ - if (memcmp(data.in, data.decrypted, sizeof(data.in)) != 0) { - fprintf(stderr, - "FAIL: Decrypted data are different from the input data.\n"); - return 1; - } else - printf("Crypt Test: passed\n"); - - if (memcmp(mac, oldmac, 20) != 0) { - fprintf(stderr, - "FAIL: Hash in decrypted data different than in encrypted.\n"); - return 1; - } else - printf("HMAC Test 2: passed\n"); - - /* Finish crypto session */ - if (ioctl(cfd, CIOCFSESSION, &sess.ses)) { - perror("ioctl(CIOCFSESSION)"); - return 1; - } - - return 0; -} - -int -main() -{ - int fd = -1, cfd = -1; - - /* Open the crypto device */ - fd = open("/dev/crypto", O_RDWR, 0); - if (fd < 0) { - perror("open(/dev/crypto)"); - return 1; - } - - /* Clone file descriptor */ - if (ioctl(fd, CRIOGET, &cfd)) { - perror("ioctl(CRIOGET)"); - return 1; - } - - /* Set close-on-exec (not really neede here) */ - if (fcntl(cfd, F_SETFD, 1) == -1) { - perror("fcntl(F_SETFD)"); - return 1; - } - - /* Run the test itself */ - if (test_crypto(cfd)) - return 1; - - /* Close cloned descriptor */ - if (close(cfd)) { - perror("close(cfd)"); - return 1; - } - - /* Close the original descriptor */ - if (close(fd)) { - perror("close(fd)"); - return 1; - } - - return 0; -} diff --git a/examples/ncr.c b/examples/ncr.c index 9a75a996e58..9691fea5dc1 100644 --- a/examples/ncr.c +++ b/examples/ncr.c @@ -13,11 +13,18 @@ #include <sys/ioctl.h> #include <sys/types.h> #include <sys/stat.h> +#include <sys/socket.h> +#include <linux/netlink.h> #include "../ncr.h" #include <stdlib.h> #define DATA_SIZE 4096 +#define ALIGN_NL __attribute__((aligned(NLA_ALIGNTO))) + +#define ALG_AES_CBC "cbc(aes)" +#define ALG_AES_ECB "ecb(aes)" + static void randomize_data(uint8_t * data, size_t data_size) { int i; @@ -33,11 +40,42 @@ int i; static int test_ncr_key(int cfd) { - struct ncr_key_generate_st kgen; + struct __attribute__((packed)) { + struct ncr_key_generate f; + struct nlattr algo_head ALIGN_NL; + char algo[sizeof(ALG_AES_CBC)] ALIGN_NL; + struct nlattr flags_head ALIGN_NL; + uint32_t flags ALIGN_NL; + struct nlattr bits_head ALIGN_NL; + uint32_t bits ALIGN_NL; + } kgen; + struct __attribute__((packed)) { + struct ncr_key_get_info f; + /* This union is only here to stop gcc from complaining about + aliasing. */ + union { + unsigned char __reserve[DATA_SIZE]; + struct nlattr first_header; + } u ALIGN_NL; + } kinfo; + struct nlattr *nla; ncr_key_t key; - struct ncr_key_data_st keydata; + struct __attribute__((packed)) { + struct ncr_key_import f; + struct nlattr id_head ALIGN_NL; + uint8_t id[2] ALIGN_NL; + struct nlattr type_head ALIGN_NL; + uint32_t type ALIGN_NL; + struct nlattr algo_head ALIGN_NL; + char algo[sizeof(ALG_AES_CBC)] ALIGN_NL; + struct nlattr flags_head ALIGN_NL; + uint32_t flags ALIGN_NL; + } kimport; + struct ncr_key_export kexport; uint8_t data[KEY_DATA_SIZE]; uint8_t data_bak[KEY_DATA_SIZE]; + uint16_t *attr_p; + int got_algo, got_flags, got_type; fprintf(stdout, "Tests on Keys:\n"); @@ -51,23 +89,32 @@ test_ncr_key(int cfd) memcpy(data_bak, data, sizeof(data)); /* convert it to key */ - if (ioctl(cfd, NCRIO_KEY_INIT, &key)) { + key = ioctl(cfd, NCRIO_KEY_INIT); + if (key == -1) { perror("ioctl(NCRIO_KEY_INIT)"); return 1; } - keydata.key_id[0] = 'a'; - keydata.key_id[2] = 'b'; - keydata.key_id_size = 2; - keydata.type = NCR_KEY_TYPE_SECRET; - keydata.algorithm = NCR_ALG_AES_CBC; - keydata.flags = NCR_KEY_FLAG_EXPORTABLE; - - keydata.key = key; - keydata.idata = data; - keydata.idata_size = sizeof(data); - - if (ioctl(cfd, NCRIO_KEY_IMPORT, &keydata)) { + memset(&kimport.f, 0, sizeof(kimport.f)); + kimport.f.input_size = sizeof(kimport); + kimport.f.key = key; + kimport.f.data = data; + kimport.f.data_size = sizeof(data); + kimport.id_head.nla_len = NLA_HDRLEN + sizeof(kimport.id); + kimport.id_head.nla_type = NCR_ATTR_KEY_ID; + kimport.id[0] = 'a'; + kimport.id[1] = 'b'; + kimport.type_head.nla_len = NLA_HDRLEN + sizeof(kimport.type); + kimport.type_head.nla_type = NCR_ATTR_KEY_TYPE; + kimport.type = NCR_KEY_TYPE_SECRET; + kimport.algo_head.nla_len = NLA_HDRLEN + sizeof(kimport.algo); + kimport.algo_head.nla_type = NCR_ATTR_ALGORITHM; + strcpy(kimport.algo, ALG_AES_CBC); + kimport.flags_head.nla_len = NLA_HDRLEN + sizeof(kimport.flags); + kimport.flags_head.nla_type = NCR_ATTR_KEY_FLAGS; + kimport.flags = NCR_KEY_FLAG_EXPORTABLE; + + if (ioctl(cfd, NCRIO_KEY_IMPORT, &kimport)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_IMPORT)"); return 1; @@ -76,20 +123,14 @@ test_ncr_key(int cfd) /* now try to read it */ fprintf(stdout, "\tKey export...\n"); - memset(&keydata, 0, sizeof(keydata)); - keydata.key = key; - keydata.idata = data; - keydata.idata_size = sizeof(data); + memset(&kexport, 0, sizeof(kexport)); + kexport.key = key; + kexport.buffer = data; + kexport.buffer_size = sizeof(data); - if (ioctl(cfd, NCRIO_KEY_EXPORT, &keydata)) { + if (ioctl(cfd, NCRIO_KEY_EXPORT, &kexport) != sizeof(data)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); - perror("ioctl(NCRIO_KEY_IMPORT)"); - return 1; - } - - if (keydata.idata_size != sizeof(data)) { - fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); - fprintf(stderr, "data returned but differ!\n"); + perror("ioctl(NCRIO_KEY_EXPORT)"); return 1; } @@ -112,37 +153,46 @@ test_ncr_key(int cfd) fprintf(stdout, "\tKey import...\n"); /* convert it to key */ - if (ioctl(cfd, NCRIO_KEY_INIT, &key)) { + key = ioctl(cfd, NCRIO_KEY_INIT); + if (key == -1) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_INIT)"); return 1; } - kgen.desc = key; - kgen.params.algorithm = NCR_ALG_AES_CBC; - kgen.params.keyflags = NCR_KEY_FLAG_EXPORTABLE; - kgen.params.params.secret.bits = 128; /* 16 bytes */ - + memset(&kgen.f, 0, sizeof(kgen.f)); + kgen.f.input_size = sizeof(kgen); + kgen.f.key = key; + kgen.algo_head.nla_len = NLA_HDRLEN + sizeof(kgen.algo); + kgen.algo_head.nla_type = NCR_ATTR_ALGORITHM; + strcpy(kgen.algo, ALG_AES_CBC); + kgen.flags_head.nla_len = NLA_HDRLEN + sizeof(kgen.flags); + kgen.flags_head.nla_type = NCR_ATTR_KEY_FLAGS; + kgen.flags = NCR_KEY_FLAG_EXPORTABLE; + kgen.bits_head.nla_len = NLA_HDRLEN + sizeof(kgen.bits); + kgen.bits_head.nla_type = NCR_ATTR_SECRET_KEY_BITS; + kgen.bits = 128; /* 16 bytes */ + if (ioctl(cfd, NCRIO_KEY_GENERATE, &kgen)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); - perror("ioctl(NCRIO_KEY_IMPORT)"); + perror("ioctl(NCRIO_KEY_GENERATE)"); return 1; } memset(data, 0, sizeof(data)); - memset(&keydata, 0, sizeof(keydata)); - keydata.key = key; - keydata.idata = data; - keydata.idata_size = sizeof(data); + memset(&kexport, 0, sizeof(kexport)); + kexport.key = key; + kexport.buffer = data; + kexport.buffer_size = sizeof(data); - if (ioctl(cfd, NCRIO_KEY_EXPORT, &keydata)) { + if (ioctl(cfd, NCRIO_KEY_EXPORT, &kexport) != sizeof(data)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); - perror("ioctl(NCRIO_KEY_IMPORT)"); + perror("ioctl(NCRIO_KEY_EXPORT)"); return 1; } - if (keydata.idata_size == 0 || (data[0] == 0 && data[1] == 0 && data[2] == 0 && data[4] == 0)) { + if (data[0] == 0 && data[1] == 0 && data[2] == 0 && data[4] == 0) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); fprintf(stderr, "Generated key: %.2x.%.2x.%.2x.%.2x.%.2x.%.2x.%.2x.%.2x." "%.2x.%.2x.%.2x.%.2x.%.2x.%.2x.%.2x.%.2x\n", data[0], data[1], @@ -152,6 +202,92 @@ test_ncr_key(int cfd) return 1; } + memset(&kinfo.f, 0, sizeof(kinfo.f)); + kinfo.f.output_size = sizeof(kinfo); + kinfo.f.key = key; + nla = &kinfo.u.first_header; + nla->nla_type = NCR_ATTR_WANTED_ATTRS; + attr_p = (uint16_t *)((char *)nla + NLA_HDRLEN); + *attr_p++ = NCR_ATTR_ALGORITHM; + *attr_p++ = NCR_ATTR_KEY_FLAGS; + *attr_p++ = NCR_ATTR_KEY_TYPE; + nla->nla_len = (char *)attr_p - (char *)nla; + kinfo.f.input_size = (char *)attr_p - (char *)&kinfo; + + if (ioctl(cfd, NCRIO_KEY_GET_INFO, &kinfo)) { + fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); + perror("ioctl(NCRIO_KEY_GET_INFO)"); + return 1; + } + + got_algo = got_flags = got_type = 0; + if (kinfo.f.output_size < + (char *)&kinfo.u.first_header - (char *)&kinfo) { + fprintf(stderr, "No nlattr returned\n"); + return 1; + } + nla = &kinfo.u.first_header; + for (;;) { + void *data; + + if (nla->nla_len > + kinfo.f.output_size - ((char *)nla - (char *)&kinfo)) { + fprintf(stderr, "Attributes overflow\n"); + return 1; + } + data = (char *)nla + NLA_HDRLEN; + switch (nla->nla_type) { + case NCR_ATTR_ALGORITHM: + if (nla->nla_len < NLA_HDRLEN + 1) { + fprintf(stderr, "Attribute too small\n"); + return 1; + } + if (((char *)data)[nla->nla_len - NLA_HDRLEN - 1] + != 0) { + fprintf(stderr, "NUL missing\n"); + return 1; + } + if (strcmp(data, ALG_AES_CBC) != 0) { + fprintf(stderr, "Unexpected algorithm\n"); + return 1; + } + got_algo++; + break; + case NCR_ATTR_KEY_FLAGS: + if (nla->nla_len < NLA_HDRLEN + sizeof(uint32_t)) { + fprintf(stderr, "Attribute too small\n"); + return 1; + } + if (*(uint32_t *)data != NCR_KEY_FLAG_EXPORTABLE) { + fprintf(stderr, "Unexpected key flags\n"); + return 1; + } + got_flags++; + break; + case NCR_ATTR_KEY_TYPE: + if (nla->nla_len < NLA_HDRLEN + sizeof(uint32_t)) { + fprintf(stderr, "Attribute too small\n"); + return 1; + } + if (*(uint32_t *)data != NCR_KEY_TYPE_SECRET) { + fprintf(stderr, "Unexpected key type\n"); + return 1; + } + got_type++; + break; + } + + if (NLA_ALIGN(nla->nla_len) + NLA_HDRLEN > + kinfo.f.output_size - ((char *)nla - (char *)&kinfo)) + break; + nla = (struct nlattr *)((char *)nla + NLA_ALIGN(nla->nla_len)); + } + if (got_algo != 1 || got_flags != 1 || got_type != 1) { + fprintf(stderr, "Unexpected attrs - %d, %d, %d\n", got_algo, + got_flags, got_type); + return 1; + } + if (ioctl(cfd, NCRIO_KEY_DEINIT, &key)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_DEINIT)"); @@ -162,32 +298,41 @@ test_ncr_key(int cfd) * try to export it. */ fprintf(stdout, "\tKey protection of non-exportable keys...\n"); - if (ioctl(cfd, NCRIO_KEY_INIT, &key)) { + key = ioctl(cfd, NCRIO_KEY_INIT); + if (key == -1) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_INIT)"); return 1; } - kgen.desc = key; - kgen.params.algorithm = NCR_ALG_AES_CBC; - kgen.params.keyflags = 0; - kgen.params.params.secret.bits = 128; /* 16 bytes */ - + memset(&kgen.f, 0, sizeof(kgen.f)); + kgen.f.input_size = sizeof(kgen); + kgen.f.key = key; + kgen.algo_head.nla_len = NLA_HDRLEN + sizeof(kgen.algo); + kgen.algo_head.nla_type = NCR_ATTR_ALGORITHM; + strcpy(kgen.algo, ALG_AES_CBC); + kgen.flags_head.nla_len = NLA_HDRLEN + sizeof(kgen.flags); + kgen.flags_head.nla_type = NCR_ATTR_KEY_FLAGS; + kgen.flags = 0; + kgen.bits_head.nla_len = NLA_HDRLEN + sizeof(kgen.flags); + kgen.bits_head.nla_type = NCR_ATTR_SECRET_KEY_BITS; + kgen.bits = 128; /* 16 bytes */ + if (ioctl(cfd, NCRIO_KEY_GENERATE, &kgen)) { - perror("ioctl(NCRIO_KEY_IMPORT)"); + perror("ioctl(NCRIO_KEY_GENERATE)"); return 1; } memset(data, 0, sizeof(data)); - memset(&keydata, 0, sizeof(keydata)); - keydata.key = key; - keydata.idata = data; - keydata.idata_size = sizeof(data); + memset(&kexport, 0, sizeof(kexport)); + kexport.key = key; + kexport.buffer = data; + kexport.buffer_size = sizeof(data); /* try to get the output data - should fail */ - if (ioctl(cfd, NCRIO_KEY_EXPORT, &keydata)==0) { + if (ioctl(cfd, NCRIO_KEY_EXPORT, &kexport) >= 0) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); fprintf(stderr, "Data were exported, but shouldn't be!\n"); return 1; @@ -208,10 +353,31 @@ test_ncr_key(int cfd) static int test_ncr_wrap_key(int cfd) { - int i; + int i, ret; ncr_key_t key, key2; - struct ncr_key_data_st keydata; - struct ncr_key_wrap_st kwrap; + struct __attribute__((packed)) { + struct ncr_key_import f; + struct nlattr id_head ALIGN_NL; + uint8_t id[2] ALIGN_NL; + struct nlattr type_head ALIGN_NL; + uint32_t type ALIGN_NL; + struct nlattr algo_head ALIGN_NL; + char algo[sizeof(ALG_AES_CBC)] ALIGN_NL; + struct nlattr flags_head ALIGN_NL; + uint32_t flags ALIGN_NL; + } kimport; + struct __attribute__((packed)) { + struct ncr_key_wrap f; + struct nlattr algo_head ALIGN_NL; + char algo[sizeof(NCR_WALG_AES_RFC3394)] ALIGN_NL; + } kwrap; + struct __attribute__((packed)) { + struct ncr_key_unwrap f; + struct nlattr wrap_algo_head ALIGN_NL; + char wrap_algo[sizeof(NCR_WALG_AES_RFC3394)] ALIGN_NL; + struct nlattr flags_head ALIGN_NL; + uint32_t flags ALIGN_NL; + } kunwrap; uint8_t data[WRAPPED_KEY_DATA_SIZE]; int data_size; @@ -224,83 +390,106 @@ test_ncr_wrap_key(int cfd) fprintf(stdout, "\tKey Wrap test...\n"); /* convert it to key */ - if (ioctl(cfd, NCRIO_KEY_INIT, &key)) { + key = ioctl(cfd, NCRIO_KEY_INIT); + if (key == -1) { perror("ioctl(NCRIO_KEY_INIT)"); return 1; } - keydata.key_id[0] = 'a'; - keydata.key_id[2] = 'b'; - keydata.key_id_size = 2; - keydata.type = NCR_KEY_TYPE_SECRET; - keydata.algorithm = NCR_ALG_AES_CBC; - keydata.flags = NCR_KEY_FLAG_EXPORTABLE|NCR_KEY_FLAG_WRAPPABLE; - - keydata.key = key; - keydata.idata = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"; - keydata.idata_size = 16; - - if (ioctl(cfd, NCRIO_KEY_IMPORT, &keydata)) { + memset(&kimport.f, 0, sizeof(kimport.f)); + kimport.f.input_size = sizeof(kimport); + kimport.f.key = key; + kimport.f.data = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"; + kimport.f.data_size = 16; + kimport.id_head.nla_len = NLA_HDRLEN + sizeof(kimport.id); + kimport.id_head.nla_type = NCR_ATTR_KEY_ID; + kimport.id[0] = 'a'; + kimport.id[1] = 'b'; + kimport.type_head.nla_len = NLA_HDRLEN + sizeof(kimport.type); + kimport.type_head.nla_type = NCR_ATTR_KEY_TYPE; + kimport.type = NCR_KEY_TYPE_SECRET; + kimport.algo_head.nla_len = NLA_HDRLEN + sizeof(kimport.algo); + kimport.algo_head.nla_type = NCR_ATTR_ALGORITHM; + strcpy(kimport.algo, ALG_AES_CBC); + kimport.flags_head.nla_len = NLA_HDRLEN + sizeof(kimport.flags); + kimport.flags_head.nla_type = NCR_ATTR_KEY_FLAGS; + kimport.flags = NCR_KEY_FLAG_EXPORTABLE|NCR_KEY_FLAG_WRAPPING|NCR_KEY_FLAG_UNWRAPPING; + + ret = ioctl(cfd, NCRIO_KEY_IMPORT, &kimport); + if (geteuid() == 0 && ret) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_IMPORT)"); return 1; } + if (geteuid() != 0) { + /* cannot test further */ + fprintf(stdout, "\t(Wrapping test not completed. Run as root)\n"); + return 0; + } /* convert it to key */ - if (ioctl(cfd, NCRIO_KEY_INIT, &key2)) { + key2 = ioctl(cfd, NCRIO_KEY_INIT); + if (key2 == -1) { perror("ioctl(NCRIO_KEY_INIT)"); return 1; } - keydata.key_id[0] = 'b'; - keydata.key_id[2] = 'a'; - keydata.key_id_size = 2; - keydata.type = NCR_KEY_TYPE_SECRET; - keydata.algorithm = NCR_ALG_AES_CBC; - keydata.flags = NCR_KEY_FLAG_EXPORTABLE|NCR_KEY_FLAG_WRAPPABLE; - - keydata.key = key2; + memset(&kimport.f, 0, sizeof(kimport.f)); + kimport.f.input_size = sizeof(kimport); + kimport.f.key = key2; #define DKEY "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF" - keydata.idata = DKEY; - keydata.idata_size = 16; - - if (ioctl(cfd, NCRIO_KEY_IMPORT, &keydata)) { + kimport.f.data = DKEY; + kimport.f.data_size = 16; + kimport.id_head.nla_len = NLA_HDRLEN + sizeof(kimport.id); + kimport.id_head.nla_type = NCR_ATTR_KEY_ID; + kimport.id[0] = 'b'; + kimport.id[1] = 'a'; + kimport.type_head.nla_len = NLA_HDRLEN + sizeof(kimport.type); + kimport.type_head.nla_type = NCR_ATTR_KEY_TYPE; + kimport.type = NCR_KEY_TYPE_SECRET; + kimport.algo_head.nla_len = NLA_HDRLEN + sizeof(kimport.algo); + kimport.algo_head.nla_type = NCR_ATTR_ALGORITHM; + strcpy(kimport.algo, ALG_AES_CBC); + kimport.flags_head.nla_len = NLA_HDRLEN + sizeof(kimport.flags); + kimport.flags_head.nla_type = NCR_ATTR_KEY_FLAGS; + kimport.flags = NCR_KEY_FLAG_EXPORTABLE|NCR_KEY_FLAG_WRAPPABLE; + + if (ioctl(cfd, NCRIO_KEY_IMPORT, &kimport)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_IMPORT)"); return 1; } /* now try wrapping key2 using key */ - memset(&kwrap, 0, sizeof(kwrap)); - kwrap.algorithm = NCR_WALG_AES_RFC3394; - kwrap.keytowrap = key2; - kwrap.key = key; - kwrap.io = data; - kwrap.io_size = sizeof(data); - - if (ioctl(cfd, NCRIO_KEY_WRAP, &kwrap)) { + memset(&kwrap.f, 0, sizeof(kwrap.f)); + kwrap.f.input_size = sizeof(kwrap); + kwrap.f.wrapping_key = key; + kwrap.f.source_key = key2; + kwrap.f.buffer = data; + kwrap.f.buffer_size = sizeof(data); + kwrap.algo_head.nla_len = NLA_HDRLEN + sizeof(kwrap.algo); + kwrap.algo_head.nla_type = NCR_ATTR_WRAPPING_ALGORITHM; + strcpy(kwrap.algo, NCR_WALG_AES_RFC3394); + + data_size = ioctl(cfd, NCRIO_KEY_WRAP, &kwrap); + if (data_size < 0) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_WRAP)"); return 1; } - - data_size = kwrap.io_size; - if (kwrap.io_size != 24 || memcmp(data, + if (data_size != 24 || memcmp(data, "\x1F\xA6\x8B\x0A\x81\x12\xB4\x47\xAE\xF3\x4B\xD8\xFB\x5A\x7B\x82\x9D\x3E\x86\x23\x71\xD2\xCF\xE5", 24) != 0) { fprintf(stderr, "Wrapped data do not match.\n"); - fprintf(stderr, "Data[%d]: ",(int) kwrap.io_size); - for(i=0;i<kwrap.io_size;i++) + fprintf(stderr, "Data[%d]: ",(int) data_size); + for(i=0;i<data_size;i++) fprintf(stderr, "%.2x:", data[i]); fprintf(stderr, "\n"); return 1; } - - - /* test unwrapping */ fprintf(stdout, "\tKey Unwrap test...\n"); @@ -310,19 +499,26 @@ test_ncr_wrap_key(int cfd) return 1; } - if (ioctl(cfd, NCRIO_KEY_INIT, &key2)) { + key2 = ioctl(cfd, NCRIO_KEY_INIT); + if (key2 == -1) { perror("ioctl(NCRIO_KEY_INIT)"); return 1; } - memset(&kwrap, 0, sizeof(kwrap)); - kwrap.algorithm = NCR_WALG_AES_RFC3394; - kwrap.keytowrap = key2; - kwrap.key = key; - kwrap.io = data; - kwrap.io_size = data_size; - - if (ioctl(cfd, NCRIO_KEY_UNWRAP, &kwrap)) { + memset(&kunwrap.f, 0, sizeof(kunwrap.f)); + kunwrap.f.input_size = sizeof(kunwrap); + kunwrap.f.wrapping_key = key; + kunwrap.f.dest_key = key2; + kunwrap.f.data = data; + kunwrap.f.data_size = data_size; + kunwrap.wrap_algo_head.nla_len = NLA_HDRLEN + sizeof(kunwrap.wrap_algo); + kunwrap.wrap_algo_head.nla_type = NCR_ATTR_WRAPPING_ALGORITHM; + strcpy(kunwrap.wrap_algo, NCR_WALG_AES_RFC3394); + kunwrap.flags_head.nla_len = NLA_HDRLEN + sizeof(kunwrap.flags); + kunwrap.flags_head.nla_type = NCR_ATTR_KEY_FLAGS; + kunwrap.flags = NCR_KEY_FLAG_EXPORTABLE|NCR_KEY_FLAG_WRAPPABLE; + + if (ioctl(cfd, NCRIO_KEY_UNWRAP, &kunwrap)) { perror("ioctl(NCRIO_KEY_UNWRAP)"); return 1; } @@ -360,7 +556,129 @@ test_ncr_wrap_key(int cfd) #endif return 0; +} +/* check whether wrapping of long keys is not allowed with + * shorted wrapping keys */ +static int +test_ncr_wrap_key2(int cfd) +{ + int ret; + ncr_key_t key, key2; + struct __attribute__((packed)) { + struct ncr_key_import f; + struct nlattr id_head ALIGN_NL; + uint8_t id[2] ALIGN_NL; + struct nlattr type_head ALIGN_NL; + uint32_t type ALIGN_NL; + struct nlattr algo_head ALIGN_NL; + char algo[sizeof(ALG_AES_CBC)] ALIGN_NL; + struct nlattr flags_head ALIGN_NL; + uint32_t flags ALIGN_NL; + } kimport; + struct __attribute__((packed)) { + struct ncr_key_wrap f; + struct nlattr algo_head ALIGN_NL; + char algo[sizeof(NCR_WALG_AES_RFC3394)] ALIGN_NL; + } kwrap; + uint8_t data[WRAPPED_KEY_DATA_SIZE]; + + /* test 1: generate a key in userspace import it + * to kernel via data and export it. + */ + + fprintf(stdout, "\tKey Wrap test II...\n"); + + if (geteuid() != 0) { + /* cannot test further */ + fprintf(stdout, "\t(Wrapping test not completed. Run as root)\n"); + return 0; + } + + /* convert it to key */ + key = ioctl(cfd, NCRIO_KEY_INIT); + if (key == -1) { + perror("ioctl(NCRIO_KEY_INIT)"); + return 1; + } + + memset(&kimport.f, 0, sizeof(kimport.f)); + kimport.f.input_size = sizeof(kimport); + kimport.f.key = key; + kimport.f.data = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"; + kimport.f.data_size = 16; + kimport.id_head.nla_len = NLA_HDRLEN + sizeof(kimport.id); + kimport.id_head.nla_type = NCR_ATTR_KEY_ID; + kimport.id[0] = 'a'; + kimport.id[1] = 'b'; + kimport.type_head.nla_len = NLA_HDRLEN + sizeof(kimport.type); + kimport.type_head.nla_type = NCR_ATTR_KEY_TYPE; + kimport.type = NCR_KEY_TYPE_SECRET; + kimport.algo_head.nla_len = NLA_HDRLEN + sizeof(kimport.algo); + kimport.algo_head.nla_type = NCR_ATTR_ALGORITHM; + strcpy(kimport.algo, ALG_AES_CBC); + kimport.flags_head.nla_len = NLA_HDRLEN + sizeof(kimport.flags); + kimport.flags_head.nla_type = NCR_ATTR_KEY_FLAGS; + kimport.flags = NCR_KEY_FLAG_EXPORTABLE|NCR_KEY_FLAG_WRAPPING|NCR_KEY_FLAG_UNWRAPPING; + + if (ioctl(cfd, NCRIO_KEY_IMPORT, &kimport)) { + fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); + perror("ioctl(NCRIO_KEY_IMPORT)"); + return 1; + } + + + /* convert it to key */ + key2 = ioctl(cfd, NCRIO_KEY_INIT); + if (key2 == -1) { + perror("ioctl(NCRIO_KEY_INIT)"); + return 1; + } + + memset(&kimport.f, 0, sizeof(kimport.f)); + kimport.f.input_size = sizeof(kimport); + kimport.f.key = key2; + kimport.f.data = "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF"; + kimport.f.data_size = 32; + kimport.id_head.nla_len = NLA_HDRLEN + sizeof(kimport.id); + kimport.id_head.nla_type = NCR_ATTR_KEY_ID; + kimport.id[0] = 'b'; + kimport.id[1] = 'a'; + kimport.type_head.nla_len = NLA_HDRLEN + sizeof(kimport.type); + kimport.type_head.nla_type = NCR_ATTR_KEY_TYPE; + kimport.type = NCR_KEY_TYPE_SECRET; + kimport.algo_head.nla_len = NLA_HDRLEN + sizeof(kimport.algo); + kimport.algo_head.nla_type = NCR_ATTR_ALGORITHM; + strcpy(kimport.algo, ALG_AES_CBC); + kimport.flags_head.nla_len = NLA_HDRLEN + sizeof(kimport.flags); + kimport.flags_head.nla_type = NCR_ATTR_KEY_FLAGS; + kimport.flags = NCR_KEY_FLAG_EXPORTABLE|NCR_KEY_FLAG_WRAPPABLE; + + if (ioctl(cfd, NCRIO_KEY_IMPORT, &kimport)) { + fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); + perror("ioctl(NCRIO_KEY_IMPORT)"); + return 1; + } + + /* now try wrapping key2 using key */ + memset(&kwrap.f, 0, sizeof(kwrap.f)); + kwrap.f.input_size = sizeof(kwrap); + kwrap.f.wrapping_key = key; + kwrap.f.source_key = key2; + kwrap.f.buffer = data; + kwrap.f.buffer_size = sizeof(data); + kwrap.algo_head.nla_len = NLA_HDRLEN + sizeof(kwrap.algo); + kwrap.algo_head.nla_type = NCR_ATTR_WRAPPING_ALGORITHM; + strcpy(kwrap.algo, NCR_WALG_AES_RFC3394); + + ret = ioctl(cfd, NCRIO_KEY_WRAP, &kwrap); + if (ret >= 0) { + fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); + /* wrapping shouldn't have been allowed */ + return 1; + } + + return 0; } static int @@ -368,8 +686,20 @@ test_ncr_store_wrap_key(int cfd) { int i; ncr_key_t key2; - struct ncr_key_data_st keydata; - struct ncr_key_storage_wrap_st kwrap; + struct __attribute__((packed)) { + struct ncr_key_import f; + struct nlattr id_head ALIGN_NL; + uint8_t id[2] ALIGN_NL; + struct nlattr type_head ALIGN_NL; + uint32_t type ALIGN_NL; + struct nlattr algo_head ALIGN_NL; + char algo[sizeof(ALG_AES_CBC)] ALIGN_NL; + struct nlattr flags_head ALIGN_NL; + uint32_t flags ALIGN_NL; + } kimport; + struct ncr_key_export kexport; + struct ncr_key_storage_wrap kwrap; + struct ncr_key_storage_unwrap kunwrap; uint8_t data[DATA_SIZE]; int data_size; @@ -382,24 +712,33 @@ test_ncr_store_wrap_key(int cfd) fprintf(stdout, "\tKey Storage wrap test...\n"); /* convert it to key */ - if (ioctl(cfd, NCRIO_KEY_INIT, &key2)) { + key2 = ioctl(cfd, NCRIO_KEY_INIT); + if (key2 == -1) { perror("ioctl(NCRIO_KEY_INIT)"); return 1; } - keydata.key_id[0] = 'b'; - keydata.key_id[2] = 'a'; - keydata.key_id_size = 2; - keydata.type = NCR_KEY_TYPE_SECRET; - keydata.algorithm = NCR_ALG_AES_CBC; - keydata.flags = NCR_KEY_FLAG_EXPORTABLE|NCR_KEY_FLAG_WRAPPABLE; - - keydata.key = key2; + memset(&kimport.f, 0, sizeof(kimport.f)); + kimport.f.input_size = sizeof(kimport); + kimport.f.key = key2; #define DKEY "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF" - keydata.idata = DKEY; - keydata.idata_size = 16; - - if (ioctl(cfd, NCRIO_KEY_IMPORT, &keydata)) { + kimport.f.data = DKEY; + kimport.f.data_size = 16; + kimport.id_head.nla_len = NLA_HDRLEN + sizeof(kimport.id); + kimport.id_head.nla_type = NCR_ATTR_KEY_ID; + kimport.id[0] = 'b'; + kimport.id[1] = 'a'; + kimport.type_head.nla_len = NLA_HDRLEN + sizeof(kimport.type); + kimport.type_head.nla_type = NCR_ATTR_KEY_TYPE; + kimport.type = NCR_KEY_TYPE_SECRET; + kimport.algo_head.nla_len = NLA_HDRLEN + sizeof(kimport.algo); + kimport.algo_head.nla_type = NCR_ATTR_ALGORITHM; + strcpy(kimport.algo, ALG_AES_CBC); + kimport.flags_head.nla_len = NLA_HDRLEN + sizeof(kimport.flags); + kimport.flags_head.nla_type = NCR_ATTR_KEY_FLAGS; + kimport.flags = NCR_KEY_FLAG_EXPORTABLE|NCR_KEY_FLAG_WRAPPABLE; + + if (ioctl(cfd, NCRIO_KEY_IMPORT, &kimport)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_IMPORT)"); return 1; @@ -407,18 +746,18 @@ test_ncr_store_wrap_key(int cfd) /* now try wrapping key2 using key */ memset(&kwrap, 0, sizeof(kwrap)); - kwrap.keytowrap = key2; - kwrap.io = data; - kwrap.io_size = sizeof(data); + kwrap.key = key2; + kwrap.buffer = data; + kwrap.buffer_size = sizeof(data); - if (ioctl(cfd, NCRIO_KEY_STORAGE_WRAP, &kwrap)) { + data_size = ioctl(cfd, NCRIO_KEY_STORAGE_WRAP, &kwrap); + if (data_size < 0) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_STORAGE_WRAP)"); return 1; } /* test unwrapping */ - data_size = kwrap.io_size; fprintf(stdout, "\tKey Storage Unwrap test...\n"); /* reset key2 */ @@ -428,38 +767,38 @@ test_ncr_store_wrap_key(int cfd) return 1; } - if (ioctl(cfd, NCRIO_KEY_INIT, &key2)) { + key2 = ioctl(cfd, NCRIO_KEY_INIT); + if (key2 == -1) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_INIT)"); return 1; } - memset(&kwrap, 0, sizeof(kwrap)); - kwrap.keytowrap = key2; - kwrap.io = data; - kwrap.io_size = data_size; + memset(&kunwrap, 0, sizeof(kunwrap)); + kunwrap.key = key2; + kunwrap.data = data; + kunwrap.data_size = data_size; - if (ioctl(cfd, NCRIO_KEY_STORAGE_UNWRAP, &kwrap)) { + if (ioctl(cfd, NCRIO_KEY_STORAGE_UNWRAP, &kunwrap)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_STORAGE_UNWRAP)"); return 1; } /* now export the unwrapped */ - memset(&keydata, 0, sizeof(keydata)); - keydata.key = key2; - keydata.idata = data; - keydata.idata_size = sizeof(data); + memset(&kexport, 0, sizeof(kexport)); + kexport.key = key2; + kexport.buffer = data; + kexport.buffer_size = sizeof(data); - if (ioctl(cfd, NCRIO_KEY_EXPORT, &keydata)) { + data_size = ioctl(cfd, NCRIO_KEY_EXPORT, &kexport); + if (data_size != 16) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); - perror("ioctl(NCRIO_KEY_IMPORT)"); + perror("ioctl(NCRIO_KEY_EXPORT)"); return 1; } - - data_size = keydata.idata_size; - if (data_size != 16 || memcmp(data, DKEY, 16) != 0) { + if (memcmp(data, DKEY, 16) != 0) { fprintf(stderr, "Unwrapped data do not match.\n"); fprintf(stderr, "Data[%d]: ", (int) data_size); for(i=0;i<data_size;i++) @@ -509,56 +848,91 @@ static int test_ncr_aes(int cfd) { ncr_key_t key; - struct ncr_key_data_st keydata; + struct __attribute__((packed)) { + struct ncr_key_import f; + struct nlattr id_head ALIGN_NL; + uint8_t id[2] ALIGN_NL; + struct nlattr type_head ALIGN_NL; + uint32_t type ALIGN_NL; + struct nlattr algo_head ALIGN_NL; + char algo[sizeof(ALG_AES_ECB)] ALIGN_NL; + struct nlattr flags_head ALIGN_NL; + uint32_t flags ALIGN_NL; + } kimport; uint8_t data[KEY_DATA_SIZE]; int i, j; - struct ncr_session_once_op_st nop; - int data_size; + struct __attribute__((packed)) { + struct ncr_session_once f; + struct nlattr algo_head ALIGN_NL; + char algo[sizeof(ALG_AES_ECB)] ALIGN_NL; + struct nlattr key_head ALIGN_NL; + uint32_t key ALIGN_NL; + struct nlattr input_head ALIGN_NL; + struct ncr_session_input_data input ALIGN_NL; + struct nlattr output_head ALIGN_NL; + struct ncr_session_output_buffer output ALIGN_NL; + } op; + size_t data_size; /* convert it to key */ - if (ioctl(cfd, NCRIO_KEY_INIT, &key)) { + key = ioctl(cfd, NCRIO_KEY_INIT); + if (key == -1) { perror("ioctl(NCRIO_KEY_INIT)"); return 1; } - keydata.key_id[0] = 'a'; - keydata.key_id[2] = 'b'; - keydata.key_id_size = 2; - keydata.type = NCR_KEY_TYPE_SECRET; - keydata.algorithm = NCR_ALG_AES_CBC; - keydata.flags = NCR_KEY_FLAG_EXPORTABLE; - - fprintf(stdout, "Tests on AES Encryption\n"); for (i=0;i<sizeof(aes_vectors)/sizeof(aes_vectors[0]);i++) { - keydata.key = key; - keydata.idata = (void*)aes_vectors[i].key; - keydata.idata_size = 16; - if (ioctl(cfd, NCRIO_KEY_IMPORT, &keydata)) { + memset(&kimport.f, 0, sizeof(kimport.f)); + kimport.f.input_size = sizeof(kimport); + kimport.f.key = key; + kimport.f.data = aes_vectors[i].key; + kimport.f.data_size = 16; + kimport.id_head.nla_len = NLA_HDRLEN + sizeof(kimport.id); + kimport.id_head.nla_type = NCR_ATTR_KEY_ID; + kimport.id[0] = 'a'; + kimport.id[1] = 'b'; + kimport.type_head.nla_len = NLA_HDRLEN + sizeof(kimport.type); + kimport.type_head.nla_type = NCR_ATTR_KEY_TYPE; + kimport.type = NCR_KEY_TYPE_SECRET; + kimport.algo_head.nla_len = NLA_HDRLEN + sizeof(kimport.algo); + kimport.algo_head.nla_type = NCR_ATTR_ALGORITHM; + strcpy(kimport.algo, ALG_AES_ECB); + kimport.flags_head.nla_len = NLA_HDRLEN + sizeof(kimport.flags); + kimport.flags_head.nla_type = NCR_ATTR_KEY_FLAGS; + kimport.flags = NCR_KEY_FLAG_EXPORTABLE; + if (ioctl(cfd, NCRIO_KEY_IMPORT, &kimport)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_IMPORT)"); return 1; } /* encrypt */ - memset(&nop, 0, sizeof(nop)); - nop.init.algorithm = NCR_ALG_AES_ECB; - nop.init.key = key; - nop.init.op = NCR_OP_ENCRYPT; - nop.op.data.udata.input = (void*)aes_vectors[i].plaintext; - nop.op.data.udata.input_size = 16; - nop.op.data.udata.output = data; - nop.op.data.udata.output_size = sizeof(data); - nop.op.type = NCR_DIRECT_DATA; - - if (ioctl(cfd, NCRIO_SESSION_ONCE, &nop)) { + memset(&op.f, 0, sizeof(op.f)); + op.f.input_size = sizeof(op); + op.f.op = NCR_OP_ENCRYPT; + op.algo_head.nla_len = NLA_HDRLEN + sizeof(op.algo); + op.algo_head.nla_type = NCR_ATTR_ALGORITHM; + strcpy(op.algo, ALG_AES_ECB); + op.key_head.nla_len = NLA_HDRLEN + sizeof(op.key); + op.key_head.nla_type = NCR_ATTR_KEY; + op.key = key; + op.input_head.nla_len = NLA_HDRLEN + sizeof(op.input); + op.input_head.nla_type = NCR_ATTR_UPDATE_INPUT_DATA; + op.input.data = aes_vectors[i].plaintext; + op.input.data_size = 16; + op.output_head.nla_len = NLA_HDRLEN + sizeof(op.output); + op.output_head.nla_type = NCR_ATTR_UPDATE_OUTPUT_BUFFER; + op.output.buffer = data; + op.output.buffer_size = sizeof(data); + op.output.result_size_ptr = &data_size; + + if (ioctl(cfd, NCRIO_SESSION_ONCE, &op)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_SESSION_ONCE)"); return 1; } - - data_size = nop.op.data.udata.output_size; /* verify */ if (data_size != 16 || memcmp(data, aes_vectors[i].ciphertext, 16) != 0) { @@ -580,34 +954,55 @@ test_ncr_aes(int cfd) fprintf(stdout, "Tests on AES Decryption\n"); for (i=0;i<sizeof(aes_vectors)/sizeof(aes_vectors[0]);i++) { - keydata.key = key; - keydata.idata = (void*)aes_vectors[i].key; - keydata.idata_size = 16; - if (ioctl(cfd, NCRIO_KEY_IMPORT, &keydata)) { + memset(&kimport.f, 0, sizeof(kimport.f)); + kimport.f.input_size = sizeof(kimport); + kimport.f.key = key; + kimport.f.data = aes_vectors[i].key; + kimport.f.data_size = 16; + kimport.id_head.nla_len = NLA_HDRLEN + sizeof(kimport.id); + kimport.id_head.nla_type = NCR_ATTR_KEY_ID; + kimport.id[0] = 'a'; + kimport.id[1] = 'b'; + kimport.type_head.nla_len = NLA_HDRLEN + sizeof(kimport.type); + kimport.type_head.nla_type = NCR_ATTR_KEY_TYPE; + kimport.type = NCR_KEY_TYPE_SECRET; + kimport.algo_head.nla_len = NLA_HDRLEN + sizeof(kimport.algo); + kimport.algo_head.nla_type = NCR_ATTR_ALGORITHM; + strcpy(kimport.algo, ALG_AES_CBC); + kimport.flags_head.nla_len = NLA_HDRLEN + sizeof(kimport.flags); + kimport.flags_head.nla_type = NCR_ATTR_KEY_FLAGS; + kimport.flags = NCR_KEY_FLAG_EXPORTABLE; + if (ioctl(cfd, NCRIO_KEY_IMPORT, &kimport)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_IMPORT)"); return 1; } /* decrypt */ - memset(&nop, 0, sizeof(nop)); - nop.init.algorithm = NCR_ALG_AES_ECB; - nop.init.key = key; - nop.init.op = NCR_OP_DECRYPT; - nop.op.data.udata.input = (void*)aes_vectors[i].ciphertext; - nop.op.data.udata.input_size = 16; - nop.op.data.udata.output = data; - nop.op.data.udata.output_size = sizeof(data); - nop.op.type = NCR_DIRECT_DATA; - - if (ioctl(cfd, NCRIO_SESSION_ONCE, &nop)) { + memset(&op.f, 0, sizeof(op.f)); + op.f.input_size = sizeof(op); + op.f.op = NCR_OP_DECRYPT; + op.algo_head.nla_len = NLA_HDRLEN + sizeof(op.algo); + op.algo_head.nla_type = NCR_ATTR_ALGORITHM; + strcpy(op.algo, ALG_AES_ECB); + op.key_head.nla_len = NLA_HDRLEN + sizeof(op.key); + op.key_head.nla_type = NCR_ATTR_KEY; + op.key = key; + op.input_head.nla_len = NLA_HDRLEN + sizeof(op.input); + op.input_head.nla_type = NCR_ATTR_UPDATE_INPUT_DATA; + op.input.data = aes_vectors[i].ciphertext; + op.input.data_size = 16; + op.output_head.nla_len = NLA_HDRLEN + sizeof(op.output); + op.output_head.nla_type = NCR_ATTR_UPDATE_OUTPUT_BUFFER; + op.output.buffer = data; + op.output.buffer_size = sizeof(data); + op.output.result_size_ptr = &data_size; + + if (ioctl(cfd, NCRIO_SESSION_ONCE, &op)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_SESSION_ONCE)"); return 1; } - - data_size = nop.op.data.udata.output_size; - if (data_size != 16 || memcmp(data, aes_vectors[i].plaintext, 16) != 0) { fprintf(stderr, "AES test vector %d failed!\n", i); @@ -633,8 +1028,7 @@ test_ncr_aes(int cfd) } struct hash_vectors_st { - const char* name; - ncr_algorithm_t algorithm; + const char* algorithm; const uint8_t* key; /* if hmac */ int key_size; const uint8_t* plaintext; @@ -644,8 +1038,7 @@ struct hash_vectors_st { ncr_crypto_op_t op; } hash_vectors[] = { { - .name = "SHA1", - .algorithm = NCR_ALG_SHA1, + .algorithm = "sha1", .key = NULL, .plaintext = (uint8_t*)"what do ya want for nothing?", .plaintext_size = sizeof("what do ya want for nothing?")-1, @@ -654,8 +1047,7 @@ struct hash_vectors_st { .op = NCR_OP_SIGN, }, { - .name = "HMAC-MD5", - .algorithm = NCR_ALG_HMAC_MD5, + .algorithm = "hmac(md5)", .key = (uint8_t*)"Jefe", .key_size = 4, .plaintext = (uint8_t*)"what do ya want for nothing?", @@ -666,8 +1058,7 @@ struct hash_vectors_st { }, /* from rfc4231 */ { - .name = "HMAC-SHA224", - .algorithm = NCR_ALG_HMAC_SHA2_224, + .algorithm = "hmac(sha224)", .key = (uint8_t*)"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", .key_size = 20, .plaintext = (uint8_t*)"Hi There", @@ -677,8 +1068,7 @@ struct hash_vectors_st { .op = NCR_OP_SIGN, }, { - .name = "HMAC-SHA256", - .algorithm = NCR_ALG_HMAC_SHA2_256, + .algorithm = "hmac(sha256)", .key = (uint8_t*)"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", .key_size = 20, .plaintext = (uint8_t*)"Hi There", @@ -688,8 +1078,7 @@ struct hash_vectors_st { .op = NCR_OP_SIGN, }, { - .name = "HMAC-SHA384", - .algorithm = NCR_ALG_HMAC_SHA2_384, + .algorithm = "hmac(sha384)", .key = (uint8_t*)"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", .key_size = 20, .plaintext = (uint8_t*)"Hi There", @@ -699,8 +1088,7 @@ struct hash_vectors_st { .op = NCR_OP_SIGN, }, { - .name = "HMAC-SHA512", - .algorithm = NCR_ALG_HMAC_SHA2_512, + .algorithm = "hmac(sha512)", .key = (uint8_t*)"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", .key_size = 20, .plaintext = (uint8_t*)"Hi There", @@ -718,62 +1106,102 @@ static int test_ncr_hash(int cfd) { ncr_key_t key; - struct ncr_key_data_st keydata; + struct __attribute__((packed)) { + struct ncr_key_import f; + struct nlattr id_head ALIGN_NL; + uint8_t id[2] ALIGN_NL; + struct nlattr type_head ALIGN_NL; + uint32_t type ALIGN_NL; + struct nlattr flags_head ALIGN_NL; + uint32_t flags ALIGN_NL; + struct nlattr algo_head ALIGN_NL; + char algo[128] ALIGN_NL; + } kimport; uint8_t data[HASH_DATA_SIZE]; - int i, j, data_size; - struct ncr_session_once_op_st nop; + int i, j; + size_t data_size; + struct __attribute__((packed)) { + struct ncr_session_once f; + struct nlattr key_head ALIGN_NL; + uint32_t key ALIGN_NL; + struct nlattr input_head ALIGN_NL; + struct ncr_session_input_data input ALIGN_NL; + struct nlattr output_head ALIGN_NL; + struct ncr_session_output_buffer output ALIGN_NL; + struct nlattr algo_head ALIGN_NL; + char algo[128] ALIGN_NL; + } op; /* convert it to key */ - if (ioctl(cfd, NCRIO_KEY_INIT, &key)) { + key = ioctl(cfd, NCRIO_KEY_INIT); + if (key == -1) { perror("ioctl(NCRIO_KEY_INIT)"); return 1; } - keydata.key_id[0] = 'a'; - keydata.key_id[2] = 'b'; - keydata.key_id_size = 2; - keydata.type = NCR_KEY_TYPE_SECRET; - keydata.algorithm = NCR_ALG_AES_CBC; - keydata.flags = NCR_KEY_FLAG_EXPORTABLE; - - fprintf(stdout, "Tests on Hashes\n"); for (i=0;i<sizeof(hash_vectors)/sizeof(hash_vectors[0]);i++) { + size_t algo_size; - fprintf(stdout, "\t%s:\n", hash_vectors[i].name); + algo_size = strlen(hash_vectors[i].algorithm) + 1; + fprintf(stdout, "\t%s:\n", hash_vectors[i].algorithm); /* import key */ if (hash_vectors[i].key != NULL) { - keydata.key = key; - keydata.idata = (void*)hash_vectors[i].key; - keydata.idata_size = hash_vectors[i].key_size; - if (ioctl(cfd, NCRIO_KEY_IMPORT, &keydata)) { + memset(&kimport.f, 0, sizeof(kimport.f)); + kimport.f.key = key; + kimport.f.data = hash_vectors[i].key; + kimport.f.data_size = hash_vectors[i].key_size; + kimport.id_head.nla_len + = NLA_HDRLEN + sizeof(kimport.id); + kimport.id_head.nla_type = NCR_ATTR_KEY_ID; + kimport.id[0] = 'a'; + kimport.id[1] = 'b'; + kimport.type_head.nla_len + = NLA_HDRLEN + sizeof(kimport.type); + kimport.type_head.nla_type = NCR_ATTR_KEY_TYPE; + kimport.type = NCR_KEY_TYPE_SECRET; + kimport.flags_head.nla_len + = NLA_HDRLEN + sizeof(kimport.flags); + kimport.flags_head.nla_type = NCR_ATTR_KEY_FLAGS; + kimport.flags = NCR_KEY_FLAG_EXPORTABLE; + kimport.algo_head.nla_len = NLA_HDRLEN + algo_size; + kimport.algo_head.nla_type = NCR_ATTR_ALGORITHM; + memcpy(kimport.algo, hash_vectors[i].algorithm, + algo_size); + kimport.f.input_size + = kimport.algo + algo_size - (char *)&kimport; + if (ioctl(cfd, NCRIO_KEY_IMPORT, &kimport)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_IMPORT)"); return 1; } } - /* encrypt */ - memset(&nop, 0, sizeof(nop)); - nop.init.algorithm = hash_vectors[i].algorithm; - if (hash_vectors[i].key != NULL) - nop.init.key = key; - nop.init.op = hash_vectors[i].op; - nop.op.data.udata.input = (void*)hash_vectors[i].plaintext; - nop.op.data.udata.input_size = hash_vectors[i].plaintext_size; - nop.op.data.udata.output = data; - nop.op.data.udata.output_size = sizeof(data); - nop.op.type = NCR_DIRECT_DATA; - - if (ioctl(cfd, NCRIO_SESSION_ONCE, &nop)) { + memset(&op.f, 0, sizeof(op.f)); + op.f.op = hash_vectors[i].op; + op.key_head.nla_len = NLA_HDRLEN + sizeof(op.key); + op.key_head.nla_type = NCR_ATTR_KEY; + op.key = hash_vectors[i].key != NULL ? key : NCR_KEY_INVALID; + op.input_head.nla_len = NLA_HDRLEN + sizeof(op.input); + op.input_head.nla_type = NCR_ATTR_UPDATE_INPUT_DATA; + op.input.data = hash_vectors[i].plaintext; + op.input.data_size = hash_vectors[i].plaintext_size; + op.output_head.nla_len = NLA_HDRLEN + sizeof(op.output); + op.output_head.nla_type = NCR_ATTR_FINAL_OUTPUT_BUFFER; + op.output.buffer = data; + op.output.buffer_size = sizeof(data); + op.output.result_size_ptr = &data_size; + op.algo_head.nla_len = NLA_HDRLEN + algo_size; + op.algo_head.nla_type = NCR_ATTR_ALGORITHM; + memcpy(op.algo, hash_vectors[i].algorithm, algo_size); + op.f.input_size = op.algo + algo_size - (char *)&op; + + if (ioctl(cfd, NCRIO_SESSION_ONCE, &op)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_SESSION_ONCE)"); return 1; } - - data_size = nop.op.data.udata.output_size; - if (data_size != hash_vectors[i].output_size || memcmp(data, hash_vectors[i].output, hash_vectors[i].output_size) != 0) { @@ -799,94 +1227,353 @@ test_ncr_hash(int cfd) } static int +test_ncr_hash_clone(int cfd) +{ + ncr_key_t key; + struct __attribute__((packed)) { + struct ncr_key_import f; + struct nlattr id_head ALIGN_NL; + uint8_t id[2] ALIGN_NL; + struct nlattr type_head ALIGN_NL; + uint32_t type ALIGN_NL; + struct nlattr flags_head ALIGN_NL; + uint32_t flags ALIGN_NL; + struct nlattr algo_head ALIGN_NL; + char algo[128] ALIGN_NL; + } kimport; + uint8_t data[HASH_DATA_SIZE]; + const struct hash_vectors_st *hv; + int j; + size_t data_size; + struct __attribute__((packed)) { + struct ncr_session_init f; + struct nlattr key_head ALIGN_NL; + uint32_t key ALIGN_NL; + struct nlattr algo_head ALIGN_NL; + char algo[128] ALIGN_NL; + } kinit; + struct __attribute__((packed)) { + struct ncr_session_update f; + struct nlattr input_head ALIGN_NL; + struct ncr_session_input_data input ALIGN_NL; + } kupdate; + struct __attribute__((packed)) { + struct ncr_session_final f; + struct nlattr input_head ALIGN_NL; + struct ncr_session_input_data input ALIGN_NL; + struct nlattr output_head ALIGN_NL; + struct ncr_session_output_buffer output ALIGN_NL; + } kfinal; + struct __attribute__((packed)) { + struct ncr_session_once f; + struct nlattr clone_head ALIGN_NL; + uint32_t clone ALIGN_NL; + struct nlattr input_head ALIGN_NL; + struct ncr_session_input_data input ALIGN_NL; + struct nlattr output_head ALIGN_NL; + struct ncr_session_output_buffer output ALIGN_NL; + } kclone; + ncr_session_t ses; + + /* convert it to key */ + key = ioctl(cfd, NCRIO_KEY_INIT); + if (key == -1) { + perror("ioctl(NCRIO_KEY_INIT)"); + return 1; + } + + fprintf(stdout, "Tests of hash cloning\n"); + for (hv = hash_vectors; + hv < hash_vectors + sizeof(hash_vectors) / sizeof(hash_vectors[0]); + hv++) { + size_t algo_size; + + algo_size = strlen(hv->algorithm) + 1; + fprintf(stdout, "\t%s:\n", hv->algorithm); + /* import key */ + if (hv->key != NULL) { + + memset(&kimport.f, 0, sizeof(kimport.f)); + kimport.f.key = key; + kimport.f.data = hv->key; + kimport.f.data_size = hv->key_size; + kimport.id_head.nla_len + = NLA_HDRLEN + sizeof(kimport.id); + kimport.id_head.nla_type = NCR_ATTR_KEY_ID; + kimport.id[0] = 'a'; + kimport.id[1] = 'b'; + kimport.type_head.nla_len + = NLA_HDRLEN + sizeof(kimport.type); + kimport.type_head.nla_type = NCR_ATTR_KEY_TYPE; + kimport.type = NCR_KEY_TYPE_SECRET; + kimport.flags_head.nla_len + = NLA_HDRLEN + sizeof(kimport.flags); + kimport.flags_head.nla_type = NCR_ATTR_KEY_FLAGS; + kimport.flags = NCR_KEY_FLAG_EXPORTABLE; + kimport.algo_head.nla_len = NLA_HDRLEN + algo_size; + kimport.algo_head.nla_type = NCR_ATTR_ALGORITHM; + memcpy(kimport.algo, hv->algorithm, algo_size); + kimport.f.input_size + = kimport.algo + algo_size - (char *)&kimport; + if (ioctl(cfd, NCRIO_KEY_IMPORT, &kimport)) { + fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); + perror("ioctl(NCRIO_KEY_IMPORT)"); + return 1; + } + } + + /* Initialize a session */ + memset(&kinit.f, 0, sizeof(kinit.f)); + kinit.f.op = hv->op; + kinit.key_head.nla_len = NLA_HDRLEN + sizeof(kinit.key); + kinit.key_head.nla_type = NCR_ATTR_KEY; + kinit.key = hv->key != NULL ? key : NCR_KEY_INVALID; + kinit.algo_head.nla_len = NLA_HDRLEN + algo_size; + kinit.algo_head.nla_type = NCR_ATTR_ALGORITHM; + memcpy(kinit.algo, hv->algorithm, algo_size); + kinit.f.input_size = kinit.algo + algo_size - (char *)&kinit; + + ses = ioctl(cfd, NCRIO_SESSION_INIT, &kinit); + if (ses < 0) { + fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); + perror("ioctl(NCRIO_SESSION_INIT)"); + return 1; + } + + /* Submit half of the data */ + memset(&kupdate.f, 0, sizeof(kupdate.f)); + kupdate.f.input_size = sizeof(kupdate); + kupdate.f.ses = ses; + kupdate.input_head.nla_len = NLA_HDRLEN + sizeof(kupdate.input); + kupdate.input_head.nla_type = NCR_ATTR_UPDATE_INPUT_DATA; + kupdate.input.data = hv->plaintext; + kupdate.input.data_size = hv->plaintext_size / 2; + + if (ioctl(cfd, NCRIO_SESSION_UPDATE, &kupdate)) { + fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); + perror("ioctl(NCRIO_SESSION_UPDATE)"); + return 1; + } + + /* Clone a session, submit the other half, verify. */ + memset(&kclone.f, 0, sizeof(kclone.f)); + kclone.f.input_size = sizeof(kclone); + kclone.f.op = hv->op; + kclone.clone_head.nla_len = NLA_HDRLEN + sizeof(kclone.clone); + kclone.clone_head.nla_type = NCR_ATTR_SESSION_CLONE_FROM; + kclone.clone = ses; + kclone.input_head.nla_len = NLA_HDRLEN + sizeof(kclone.input); + kclone.input_head.nla_type = NCR_ATTR_UPDATE_INPUT_DATA; + kclone.input.data = hv->plaintext + hv->plaintext_size / 2; + kclone.input.data_size + = hv->plaintext_size - hv->plaintext_size / 2; + kclone.output_head.nla_len = NLA_HDRLEN + sizeof(kclone.output); + kclone.output_head.nla_type = NCR_ATTR_FINAL_OUTPUT_BUFFER; + kclone.output.buffer = data; + kclone.output.buffer_size = sizeof(data); + kclone.output.result_size_ptr = &data_size; + + if (ioctl(cfd, NCRIO_SESSION_ONCE, &kclone)) { + fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); + perror("ioctl(NCRIO_SESSION_ONCE)"); + return 1; + } + + if (data_size != hv->output_size + || memcmp(data, hv->output, hv->output_size) != 0) { + fprintf(stderr, "HASH test vector %td failed!\n", + hv - hash_vectors); + + fprintf(stderr, "Output[%zu]: ", data_size); + for(j = 0; j < data_size; j++) + fprintf(stderr, "%.2x:", (int)data[j]); + fprintf(stderr, "\n"); + + fprintf(stderr, "Expected[%d]: ", hv->output_size); + for (j = 0; j < hv->output_size; j++) + fprintf(stderr, "%.2x:", (int)hv->output[j]); + fprintf(stderr, "\n"); + return 1; + } + + /* Submit the other half to the original session, verify. */ + memset(&kfinal.f, 0, sizeof(kfinal.f)); + kfinal.f.input_size = sizeof(kfinal); + kfinal.f.ses = ses; + kfinal.input_head.nla_len = NLA_HDRLEN + sizeof(kfinal.input); + kfinal.input_head.nla_type = NCR_ATTR_UPDATE_INPUT_DATA; + kfinal.input.data = hv->plaintext + hv->plaintext_size / 2; + kfinal.input.data_size + = hv->plaintext_size - hv->plaintext_size / 2; + kfinal.output_head.nla_len = NLA_HDRLEN + sizeof(kfinal.output); + kfinal.output_head.nla_type = NCR_ATTR_FINAL_OUTPUT_BUFFER; + kfinal.output.buffer = data; + kfinal.output.buffer_size = sizeof(data); + kfinal.output.result_size_ptr = &data_size; + + if (ioctl(cfd, NCRIO_SESSION_FINAL, &kfinal)) { + fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); + perror("ioctl(NCRIO_SESSION_FINAL)"); + return 1; + } + + if (data_size != hv->output_size + || memcmp(data, hv->output, hv->output_size) != 0) { + fprintf(stderr, "HASH test vector %td failed!\n", + hv - hash_vectors); + + fprintf(stderr, "Output[%zu]: ", data_size); + for(j = 0; j < data_size; j++) + fprintf(stderr, "%.2x:", (int)data[j]); + fprintf(stderr, "\n"); + + fprintf(stderr, "Expected[%d]: ", hv->output_size); + for (j = 0; j < hv->output_size; j++) + fprintf(stderr, "%.2x:", (int)hv->output[j]); + fprintf(stderr, "\n"); + return 1; + } + } + + fprintf(stdout, "\n"); + + return 0; + +} + +static int test_ncr_hash_key(int cfd) { ncr_key_t key; - struct ncr_key_data_st keydata; + ncr_session_t ses; + struct __attribute__((packed)) { + struct ncr_key_import f; + struct nlattr id_head ALIGN_NL; + uint8_t id[2] ALIGN_NL; + struct nlattr type_head ALIGN_NL; + uint32_t type ALIGN_NL; + struct nlattr flags_head ALIGN_NL; + uint32_t flags ALIGN_NL; + struct nlattr algo_head ALIGN_NL; + char algo[128] ALIGN_NL; + } kimport; uint8_t data[HASH_DATA_SIZE]; - int j, data_size; - struct ncr_session_op_st op; - struct ncr_session_st op_init; + int j; + size_t data_size, algo_size; + struct __attribute__((packed)) { + struct ncr_session_init f; + struct nlattr algo_head ALIGN_NL; + char algo[128] ALIGN_NL; + } op_init; + struct __attribute__((packed)) { + struct ncr_session_update f; + struct nlattr data_head ALIGN_NL; + struct ncr_session_input_data data ALIGN_NL; + } op_up_data; + struct __attribute__((packed)) { + struct ncr_session_update f; + struct nlattr key_head ALIGN_NL; + uint32_t key; + } op_up_key; + struct __attribute__((packed)) { + struct ncr_session_final f; + struct nlattr output_head ALIGN_NL; + struct ncr_session_output_buffer output ALIGN_NL; + } op_final; const uint8_t *output = (void*)"\xe2\xd7\x2c\x2e\x14\xad\x97\xc8\xd2\xdb\xce\xd8\xb3\x52\x9f\x1c\xb3\x2c\x5c\xec"; /* convert it to key */ - if (ioctl(cfd, NCRIO_KEY_INIT, &key)) { + key = ioctl(cfd, NCRIO_KEY_INIT); + if (key == -1) { perror("ioctl(NCRIO_KEY_INIT)"); return 1; } - keydata.key_id[0] = 'a'; - keydata.key_id[2] = 'b'; - keydata.key_id_size = 2; - keydata.type = NCR_KEY_TYPE_SECRET; - keydata.algorithm = NCR_ALG_AES_CBC; - keydata.flags = NCR_KEY_FLAG_EXPORTABLE; - fprintf(stdout, "Tests on Hashes of Keys\n"); - fprintf(stdout, "\t%s:\n", hash_vectors[0].name); + fprintf(stdout, "\t%s:\n", hash_vectors[0].algorithm); + algo_size = strlen(hash_vectors[0].algorithm) + 1; /* import key */ - keydata.key = key; - keydata.idata = (void*)hash_vectors[0].plaintext; - keydata.idata_size = hash_vectors[0].plaintext_size; - if (ioctl(cfd, NCRIO_KEY_IMPORT, &keydata)) { + memset(&kimport.f, 0, sizeof(kimport.f)); + kimport.f.key = key; + kimport.f.data = hash_vectors[0].plaintext; + kimport.f.data_size = hash_vectors[0].plaintext_size; + kimport.id_head.nla_len = NLA_HDRLEN + sizeof(kimport.id); + kimport.id_head.nla_type = NCR_ATTR_KEY_ID; + kimport.id[0] = 'a'; + kimport.id[1] = 'b'; + kimport.type_head.nla_len = NLA_HDRLEN + sizeof(kimport.type); + kimport.type_head.nla_type = NCR_ATTR_KEY_TYPE; + kimport.type = NCR_KEY_TYPE_SECRET; + kimport.flags_head.nla_len = NLA_HDRLEN + sizeof(kimport.flags); + kimport.flags_head.nla_type = NCR_ATTR_KEY_FLAGS; + kimport.flags = NCR_KEY_FLAG_EXPORTABLE; + kimport.algo_head.nla_len = NLA_HDRLEN + algo_size; + kimport.algo_head.nla_type = NCR_ATTR_ALGORITHM; + memcpy(kimport.algo, hash_vectors[0].algorithm, algo_size); + kimport.f.input_size = kimport.algo + algo_size - (char *)&kimport; + if (ioctl(cfd, NCRIO_KEY_IMPORT, &kimport)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_IMPORT)"); return 1; } - /* encrypt */ - memset(&op_init, 0, sizeof(op_init)); - op_init.algorithm = hash_vectors[0].algorithm; - op_init.op = hash_vectors[0].op; + memset(&op_init.f, 0, sizeof(op_init.f)); + op_init.f.op = hash_vectors[0].op; + op_init.algo_head.nla_len = NLA_HDRLEN + algo_size; + op_init.algo_head.nla_type = NCR_ATTR_ALGORITHM; + memcpy(op_init.algo, hash_vectors[0].algorithm, algo_size); + op_init.f.input_size = op_init.algo + algo_size - (char *)&op_init; - if (ioctl(cfd, NCRIO_SESSION_INIT, &op_init)) { + ses = ioctl(cfd, NCRIO_SESSION_INIT, &op_init); + if (ses < 0) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_SESSION_INIT)"); return 1; } - memset(&op, 0, sizeof(op)); - op.ses = op_init.ses; - op.data.udata.input = (void*)hash_vectors[0].plaintext; - op.data.udata.input_size = hash_vectors[0].plaintext_size; - op.data.udata.output = NULL; - op.data.udata.output_size = 0; - op.type = NCR_DIRECT_DATA; + memset(&op_up_data.f, 0, sizeof(op_up_data.f)); + op_up_data.f.input_size = sizeof(op_up_data); + op_up_data.f.ses = ses; + op_up_data.data_head.nla_len = NLA_HDRLEN + sizeof(op_up_data.data); + op_up_data.data_head.nla_type = NCR_ATTR_UPDATE_INPUT_DATA; + op_up_data.data.data = hash_vectors[0].plaintext; + op_up_data.data.data_size = hash_vectors[0].plaintext_size; - if (ioctl(cfd, NCRIO_SESSION_UPDATE, &op)) { + if (ioctl(cfd, NCRIO_SESSION_UPDATE, &op_up_data)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_SESSION_UPDATE)"); return 1; } - memset(&op, 0, sizeof(op)); - op.ses = op_init.ses; - op.data.kdata.input = key; - op.data.kdata.output = NULL; - op.data.kdata.output_size = 0; - op.type = NCR_KEY_DATA; + memset(&op_up_key.f, 0, sizeof(op_up_key.f)); + op_up_key.f.input_size = sizeof(op_up_key); + op_up_key.f.ses = ses; + op_up_key.key_head.nla_len = NLA_HDRLEN + sizeof(op_up_key.key); + op_up_key.key_head.nla_type = NCR_ATTR_UPDATE_INPUT_KEY_AS_DATA; + op_up_key.key = key; - if (ioctl(cfd, NCRIO_SESSION_UPDATE, &op)) { + if (ioctl(cfd, NCRIO_SESSION_UPDATE, &op_up_key)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_SESSION_UPDATE)"); return 1; } - op.data.udata.input = NULL; - op.data.udata.input_size = 0; - op.data.udata.output = data; - op.data.udata.output_size = sizeof(data); - op.type = NCR_DIRECT_DATA; + memset(&op_final.f, 0, sizeof(op_final.f)); + op_final.f.input_size = sizeof(op_final); + op_final.f.ses = ses; + op_final.output_head.nla_len = NLA_HDRLEN + sizeof(op_final.output); + op_final.output_head.nla_type = NCR_ATTR_FINAL_OUTPUT_BUFFER; + op_final.output.buffer = data; + op_final.output.buffer_size = sizeof(data); + op_final.output.result_size_ptr = &data_size; - if (ioctl(cfd, NCRIO_SESSION_FINAL, &op)) { + if (ioctl(cfd, NCRIO_SESSION_FINAL, &op_final)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_SESSION_FINAL)"); return 1; } - data_size = op.data.udata.output_size; - if (data_size != hash_vectors[0].output_size || memcmp(data, output, hash_vectors[0].output_size) != 0) { @@ -933,12 +1620,18 @@ main() if (test_ncr_hash(fd)) return 1; + if (test_ncr_hash_clone(fd)) + return 1; + if (test_ncr_hash_key(fd)) return 1; if (test_ncr_wrap_key(fd)) return 1; + if (test_ncr_wrap_key2(fd)) + return 1; + if (test_ncr_store_wrap_key(fd)) return 1; diff --git a/examples/pk.c b/examples/pk.c index 3102a3b1abd..5ccb73e5205 100644 --- a/examples/pk.c +++ b/examples/pk.c @@ -4,6 +4,7 @@ * Placed under public domain. * */ +#include <assert.h> #include <stdint.h> #include <stdio.h> #include <string.h> @@ -11,8 +12,10 @@ #include <fcntl.h> #include <time.h> #include <sys/ioctl.h> +#include <sys/socket.h> #include <sys/types.h> #include <sys/stat.h> +#include <linux/netlink.h> #include "../ncr.h" #include <stdlib.h> #include <gnutls/gnutls.h> @@ -23,6 +26,16 @@ #define DATA_SIZE 4096 +#define ALIGN_NL __attribute__((aligned(NLA_ALIGNTO))) + +#define SIGNATURE_HASH "sha1" +#define SIGNATURE_HASH_SIZE 20 + +#define ALG_AES_CBC "cbc(aes)" +#define ALG_DH "dh" +#define ALG_DSA "dsa" +#define ALG_RSA "rsa" + static void print_hex_datum (gnutls_datum_t * dat) { @@ -307,16 +320,32 @@ const char dh_params_txt[] = "-----BEGIN DH PARAMETERS-----\n"\ static int test_ncr_dh(int cfd) { -struct ncr_key_generate_st kgen; +struct __attribute__((packed)) { + struct ncr_key_generate_pair f; + struct nlattr algo_head ALIGN_NL; + char algo[sizeof(ALG_DH)] ALIGN_NL; + struct nlattr flags_head ALIGN_NL; + uint32_t flags ALIGN_NL; + unsigned char buffer[DATA_SIZE] ALIGN_NL; +} kgen; +struct nlattr *nla; ncr_key_t private1, public1, public2, private2; ncr_key_t z1, z2; int ret; gnutls_datum g, p, params; gnutls_dh_params_t dhp; unsigned char y1[1024], y2[1024]; -size_t y1_size, y2_size; -struct ncr_key_data_st keydata; -struct ncr_key_derivation_params_st kderive; +ssize_t y1_size, y2_size; +struct ncr_key_export kexport; +struct __attribute__((packed)) { + struct ncr_key_derive f; + struct nlattr algo_head ALIGN_NL; + char algo[sizeof(NCR_DERIVE_DH)] ALIGN_NL; + struct nlattr flags_head ALIGN_NL; + uint32_t flags ALIGN_NL; + struct nlattr public_head ALIGN_NL; + unsigned char public[DATA_SIZE] ALIGN_NL; +} kderive; fprintf(stdout, "Tests on DH key exchange:"); fflush(stdout); @@ -346,157 +375,201 @@ struct ncr_key_derivation_params_st kderive; } /* generate a DH key */ - if (ioctl(cfd, NCRIO_KEY_INIT, &private1)) { + private1 = ioctl(cfd, NCRIO_KEY_INIT); + if (private1 == -1) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_INIT)"); return 1; } - if (ioctl(cfd, NCRIO_KEY_INIT, &public1)) { + public1 = ioctl(cfd, NCRIO_KEY_INIT); + if (public1 == -1) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_INIT)"); return 1; } - memset(&kgen, 0, sizeof(kgen)); - kgen.desc = private1; - kgen.desc2 = public1; - kgen.params.algorithm = NCR_ALG_DH; - kgen.params.keyflags = NCR_KEY_FLAG_EXPORTABLE; - kgen.params.params.dh.p = p.data; - kgen.params.params.dh.p_size = p.size; - kgen.params.params.dh.g = g.data; - kgen.params.params.dh.g_size = g.size; + memset(&kgen.f, 0, sizeof(kgen.f)); + kgen.f.private_key = private1; + kgen.f.public_key = public1; + kgen.algo_head.nla_len = NLA_HDRLEN + sizeof(kgen.algo); + kgen.algo_head.nla_type = NCR_ATTR_ALGORITHM; + strcpy(kgen.algo, ALG_DH); + kgen.flags_head.nla_len = NLA_HDRLEN + sizeof(kgen.flags); + kgen.flags_head.nla_type = NCR_ATTR_KEY_FLAGS; + kgen.flags = NCR_KEY_FLAG_EXPORTABLE; + nla = (struct nlattr *)kgen.buffer; + nla->nla_len = NLA_HDRLEN + p.size; + nla->nla_type = NCR_ATTR_DH_PRIME; + memcpy((char *)nla + NLA_HDRLEN, p.data, p.size); + nla = (struct nlattr *)((char *)nla + NLA_ALIGN(nla->nla_len)); + nla->nla_len = NLA_HDRLEN + g.size; + nla->nla_type = NCR_ATTR_DH_BASE; + memcpy((char *)nla + NLA_HDRLEN, g.data, g.size); + nla = (struct nlattr *)((char *)nla + NLA_ALIGN(nla->nla_len)); + kgen.f.input_size = (char *)nla - (char *)&kgen; + assert(kgen.f.input_size <= sizeof(kgen)); if (ioctl(cfd, NCRIO_KEY_GENERATE_PAIR, &kgen)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); - perror("ioctl(NCRIO_KEY_GENERATE)"); + perror("ioctl(NCRIO_KEY_GENERATE_PAIR)"); return 1; } /* generate another DH key */ - if (ioctl(cfd, NCRIO_KEY_INIT, &private2)) { + private2 = ioctl(cfd, NCRIO_KEY_INIT); + if (private2 == -1) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_INIT)"); return 1; } - if (ioctl(cfd, NCRIO_KEY_INIT, &public2)) { + public2 = ioctl(cfd, NCRIO_KEY_INIT); + if (public2 == -1) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_INIT)"); return 1; } - memset(&kgen, 0, sizeof(kgen)); - kgen.desc = private2; - kgen.desc2 = public2; - kgen.params.algorithm = NCR_ALG_DH; - kgen.params.keyflags = NCR_KEY_FLAG_EXPORTABLE; - kgen.params.params.dh.p = p.data; - kgen.params.params.dh.p_size = p.size; - kgen.params.params.dh.g = g.data; - kgen.params.params.dh.g_size = g.size; + memset(&kgen.f, 0, sizeof(kgen.f)); + kgen.f.private_key = private2; + kgen.f.public_key = public2; + kgen.algo_head.nla_len = NLA_HDRLEN + sizeof(kgen.algo); + kgen.algo_head.nla_type = NCR_ATTR_ALGORITHM; + strcpy(kgen.algo, ALG_DH); + kgen.flags_head.nla_len = NLA_HDRLEN + sizeof(kgen.flags); + kgen.flags_head.nla_type = NCR_ATTR_KEY_FLAGS; + kgen.flags = NCR_KEY_FLAG_EXPORTABLE; + nla = (struct nlattr *)kgen.buffer; + nla->nla_len = NLA_HDRLEN + p.size; + nla->nla_type = NCR_ATTR_DH_PRIME; + memcpy((char *)nla + NLA_HDRLEN, p.data, p.size); + nla = (struct nlattr *)((char *)nla + NLA_ALIGN(nla->nla_len)); + nla->nla_len = NLA_HDRLEN + g.size; + nla->nla_type = NCR_ATTR_DH_BASE; + memcpy((char *)nla + NLA_HDRLEN, g.data, g.size); + nla = (struct nlattr *)((char *)nla + NLA_ALIGN(nla->nla_len)); + kgen.f.input_size = (char *)nla - (char *)&kgen; + assert(kgen.f.input_size <= sizeof(kgen)); if (ioctl(cfd, NCRIO_KEY_GENERATE_PAIR, &kgen)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); - perror("ioctl(NCRIO_KEY_GENERATE)"); + perror("ioctl(NCRIO_KEY_GENERATE_PAIR)"); return 1; } /* export y1=g^x1 */ - memset(&keydata, 0, sizeof(keydata)); - keydata.key = public1; - keydata.idata = y1; - keydata.idata_size = sizeof(y1); + memset(&kexport, 0, sizeof(kexport)); + kexport.key = public1; + kexport.buffer = y1; + kexport.buffer_size = sizeof(y1); - if (ioctl(cfd, NCRIO_KEY_EXPORT, &keydata)) { + y1_size = ioctl(cfd, NCRIO_KEY_EXPORT, &kexport); + if (y1_size < 0) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_EXPORT)"); return 1; } - - y1_size = keydata.idata_size; /* export y2=g^x2 */ - memset(&keydata, 0, sizeof(keydata)); - keydata.key = public2; - keydata.idata = y2; - keydata.idata_size = sizeof(y2); + memset(&kexport, 0, sizeof(kexport)); + kexport.key = public2; + kexport.buffer = y2; + kexport.buffer_size = sizeof(y2); - if (ioctl(cfd, NCRIO_KEY_EXPORT, &keydata)) { + y2_size = ioctl(cfd, NCRIO_KEY_EXPORT, &kexport); + if (y2_size < 0) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_EXPORT)"); return 1; } - y2_size = keydata.idata_size; - /* z1=y1^x2 */ - if (ioctl(cfd, NCRIO_KEY_INIT, &z1)) { + z1 = ioctl(cfd, NCRIO_KEY_INIT); + if (z1 == -1) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_INIT)"); return 1; } - memset(&kderive, 0, sizeof(kderive)); - kderive.derive = NCR_DERIVE_DH; - kderive.newkey = z1; - kderive.keyflags = NCR_KEY_FLAG_EXPORTABLE; - kderive.key = private1; - kderive.params.params.dh.pub = y2; - kderive.params.params.dh.pub_size = y2_size; + memset(&kderive.f, 0, sizeof(kderive.f)); + kderive.f.input_key = private1; + kderive.f.new_key = z1; + kderive.algo_head.nla_len = NLA_HDRLEN + sizeof(kderive.algo); + kderive.algo_head.nla_type = NCR_ATTR_DERIVATION_ALGORITHM; + strcpy(kderive.algo, NCR_DERIVE_DH); + kderive.flags_head.nla_len = NLA_HDRLEN + sizeof(kderive.flags); + kderive.flags_head.nla_type = NCR_ATTR_KEY_FLAGS; + kderive.flags = NCR_KEY_FLAG_EXPORTABLE; + kderive.public_head.nla_len = NLA_HDRLEN + y2_size; + kderive.public_head.nla_type = NCR_ATTR_DH_PUBLIC; + memcpy(kderive.public, y2, y2_size); + nla = (struct nlattr *)((char *)&kderive.public_head + + NLA_ALIGN(kderive.public_head.nla_len)); + kderive.f.input_size = (char *)nla - (char *)&kderive; + assert(kderive.f.input_size <= sizeof(kderive)); if (ioctl(cfd, NCRIO_KEY_DERIVE, &kderive)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); - perror("ioctl(NCRIO_KEY_INIT)"); + perror("ioctl(NCRIO_KEY_DERIVE)"); return 1; } /* z2=y2^x1 */ - if (ioctl(cfd, NCRIO_KEY_INIT, &z2)) { + z2 = ioctl(cfd, NCRIO_KEY_INIT); + if (z2 == -1) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_INIT)"); return 1; } - memset(&kderive, 0, sizeof(kderive)); - kderive.derive = NCR_DERIVE_DH; - kderive.newkey = z2; - kderive.keyflags = NCR_KEY_FLAG_EXPORTABLE; - kderive.key = private2; - kderive.params.params.dh.pub = y1; - kderive.params.params.dh.pub_size = y1_size; + memset(&kderive.f, 0, sizeof(kderive.f)); + kderive.f.input_key = private2; + kderive.f.new_key = z2; + kderive.algo_head.nla_len = NLA_HDRLEN + sizeof(kderive.algo); + kderive.algo_head.nla_type = NCR_ATTR_DERIVATION_ALGORITHM; + strcpy(kderive.algo, NCR_DERIVE_DH); + kderive.flags_head.nla_len = NLA_HDRLEN + sizeof(kderive.flags); + kderive.flags_head.nla_type = NCR_ATTR_KEY_FLAGS; + kderive.flags = NCR_KEY_FLAG_EXPORTABLE; + kderive.public_head.nla_len = NLA_HDRLEN + y2_size; + kderive.public_head.nla_type = NCR_ATTR_DH_PUBLIC; + memcpy(kderive.public, y1, y1_size); + nla = (struct nlattr *)((char *)&kderive.public_head + + NLA_ALIGN(kderive.public_head.nla_len)); + kderive.f.input_size = (char *)nla - (char *)&kderive; + assert(kderive.f.input_size <= sizeof(kderive)); if (ioctl(cfd, NCRIO_KEY_DERIVE, &kderive)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); - perror("ioctl(NCRIO_KEY_INIT)"); + perror("ioctl(NCRIO_KEY_DERIVE)"); return 1; } /* z1==z2 */ - memset(&keydata, 0, sizeof(keydata)); - keydata.key = z1; - keydata.idata = y1; - keydata.idata_size = sizeof(y1); + memset(&kexport, 0, sizeof(kexport)); + kexport.key = z1; + kexport.buffer = y1; + kexport.buffer_size = sizeof(y1); - if (ioctl(cfd, NCRIO_KEY_EXPORT, &keydata)) { + y1_size = ioctl(cfd, NCRIO_KEY_EXPORT, &kexport); + if (y1_size < 0) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_EXPORT)"); return 1; } - y1_size = keydata.idata_size; - memset(&keydata, 0, sizeof(keydata)); - keydata.key = z2; - keydata.idata = y2; - keydata.idata_size = sizeof(y2); + memset(&kexport, 0, sizeof(kexport)); + kexport.key = z2; + kexport.buffer = y2; + kexport.buffer_size = sizeof(y2); - if (ioctl(cfd, NCRIO_KEY_EXPORT, &keydata)) { + y2_size = ioctl(cfd, NCRIO_KEY_EXPORT, &kexport); + if (y2_size < 0) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_EXPORT)"); return 1; } - y2_size = keydata.idata_size; if (y1_size == 0 || y1_size != y2_size || memcmp(y1, y2, y1_size) != 0) { int i; @@ -524,14 +597,232 @@ struct ncr_key_derivation_params_st kderive; return 0; } +/* check whether wrapping of long keys is not allowed with + * shorted wrapping keys */ +static int +test_ncr_wrap_key3(int cfd) +{ + int ret, i; + ncr_key_t key; + size_t data_size; + struct __attribute__((packed)) { + struct ncr_key_import f; + struct nlattr id_head ALIGN_NL; + uint8_t id[2] ALIGN_NL; + struct nlattr type_head ALIGN_NL; + uint32_t type ALIGN_NL; + struct nlattr algo_head ALIGN_NL; + char algo[sizeof(ALG_AES_CBC)] ALIGN_NL; + struct nlattr flags_head ALIGN_NL; + uint32_t flags ALIGN_NL; + } kimport; + struct __attribute__((packed)) { + struct ncr_key_wrap f; + struct nlattr algo_head ALIGN_NL; + char algo[sizeof(NCR_WALG_AES_RFC5649)] ALIGN_NL; + } kwrap; + struct __attribute__((packed)) { + struct ncr_key_unwrap f; + struct nlattr wrap_algo_head ALIGN_NL; + char wrap_algo[sizeof(NCR_WALG_AES_RFC5649)] ALIGN_NL; + struct nlattr flags_head ALIGN_NL; + uint32_t flags ALIGN_NL; + } kunwrap; + struct __attribute__((packed)) { + struct ncr_key_generate_pair f; + struct nlattr algo_head ALIGN_NL; + char algo[sizeof(ALG_RSA)] ALIGN_NL; + struct nlattr flags_head ALIGN_NL; + uint32_t flags ALIGN_NL; + struct nlattr bits_head ALIGN_NL; + uint32_t bits ALIGN_NL; + } kgen; + ncr_key_t pubkey, privkey; + uint8_t data[DATA_SIZE]; + /* only the first two should be allowed to be wrapped. + * the latter shouldn't because it has security level larger + * then 128 bits (the size of the wrapping key). + */ + const int sizes[] = {1024, 3248, 5200}; + + fprintf(stdout, "Tests on key wrapping (might take long): "); + fflush(stdout); + + /* convert it to key */ + privkey = ioctl(cfd, NCRIO_KEY_INIT); + if (privkey == -1) { + fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); + perror("ioctl(NCRIO_KEY_INIT)"); + return 1; + } + + pubkey = ioctl(cfd, NCRIO_KEY_INIT); + if (pubkey == -1) { + fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); + perror("ioctl(NCRIO_KEY_INIT)"); + return 1; + } + + if (geteuid() != 0) { + /* cannot test further */ + fprintf(stdout, "\t(Wrapping test not completed. Run as root)\n"); + return 0; + } + + /* make a wrapping key */ + key = ioctl(cfd, NCRIO_KEY_INIT); + if (key == -1) { + perror("ioctl(NCRIO_KEY_INIT)"); + return 1; + } + + memset(&kimport.f, 0, sizeof(kimport.f)); + kimport.f.input_size = sizeof(kimport); + kimport.f.key = key; + kimport.f.data = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"; + kimport.f.data_size = 16; + kimport.id_head.nla_len = NLA_HDRLEN + sizeof(kimport.id); + kimport.id_head.nla_type = NCR_ATTR_KEY_ID; + kimport.id[0] = 'a'; + kimport.id[1] = 'b'; + kimport.type_head.nla_len = NLA_HDRLEN + sizeof(kimport.type); + kimport.type_head.nla_type = NCR_ATTR_KEY_TYPE; + kimport.type = NCR_KEY_TYPE_SECRET; + kimport.algo_head.nla_len = NLA_HDRLEN + sizeof(kimport.algo); + kimport.algo_head.nla_type = NCR_ATTR_ALGORITHM; + strcpy(kimport.algo, ALG_AES_CBC); + kimport.flags_head.nla_len = NLA_HDRLEN + sizeof(kimport.flags); + kimport.flags_head.nla_type = NCR_ATTR_KEY_FLAGS; + kimport.flags = NCR_KEY_FLAG_EXPORTABLE|NCR_KEY_FLAG_WRAPPING|NCR_KEY_FLAG_UNWRAPPING; + + if (ioctl(cfd, NCRIO_KEY_IMPORT, &kimport)) { + fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); + perror("ioctl(NCRIO_KEY_IMPORT)"); + return 1; + } + + for (i=0;i<sizeof(sizes)/sizeof(sizes[0]);i++) { + + fprintf(stdout, "."); + fflush(stdout); + + memset(&kgen.f, 0, sizeof(kgen.f)); + kgen.f.input_size = sizeof(kgen); + kgen.f.private_key = privkey; + kgen.f.public_key = pubkey; + kgen.algo_head.nla_len = NLA_HDRLEN + sizeof(kgen.algo); + kgen.algo_head.nla_type = NCR_ATTR_ALGORITHM; + strcpy(kgen.algo, ALG_RSA); + kgen.flags_head.nla_len = NLA_HDRLEN + sizeof(kgen.flags); + kgen.flags_head.nla_type = NCR_ATTR_KEY_FLAGS; + kgen.flags = NCR_KEY_FLAG_EXPORTABLE|NCR_KEY_FLAG_WRAPPABLE; + kgen.bits_head.nla_len = NLA_HDRLEN + sizeof(kgen.bits); + kgen.bits_head.nla_type = NCR_ATTR_RSA_MODULUS_BITS; + kgen.bits = sizes[i]; + + if (ioctl(cfd, NCRIO_KEY_GENERATE_PAIR, &kgen)) { + fprintf(stderr, "Error[%d-%d]: %s:%d\n", i, sizes[i], __func__, __LINE__); + perror("ioctl(NCRIO_KEY_GENERATE_PAIR)"); + return 1; + } + + /* now try wrapping key2 using key */ + memset(&kwrap.f, 0, sizeof(kwrap.f)); + kwrap.f.input_size = sizeof(kwrap); + kwrap.f.wrapping_key = key; + kwrap.f.source_key = pubkey; + kwrap.f.buffer = data; + kwrap.f.buffer_size = sizeof(data); + kwrap.algo_head.nla_len = NLA_HDRLEN + sizeof(kwrap.algo); + kwrap.algo_head.nla_type = NCR_ATTR_WRAPPING_ALGORITHM; + strcpy(kwrap.algo, NCR_WALG_AES_RFC5649); + + ret = ioctl(cfd, NCRIO_KEY_WRAP, &kwrap); + if (ret < 0) { + fprintf(stderr, "Error[%d-%d]: %s:%d\n", i, sizes[i], __func__, __LINE__); + /* wrapping of public key should have been allowed! */ + return 1; + } + + /* now try wrapping private using key */ + memset(&kwrap.f, 0, sizeof(kwrap.f)); + kwrap.f.input_size = sizeof(kwrap); + kwrap.f.wrapping_key = key; + kwrap.f.source_key = privkey; + kwrap.f.buffer = data; + kwrap.f.buffer_size = sizeof(data); + kwrap.algo_head.nla_len = NLA_HDRLEN + sizeof(kwrap.algo); + kwrap.algo_head.nla_type = NCR_ATTR_WRAPPING_ALGORITHM; + strcpy(kwrap.algo, NCR_WALG_AES_RFC5649); + + ret = ioctl(cfd, NCRIO_KEY_WRAP, &kwrap); + if (ret < 0 && i != 2) { + fprintf(stderr, "Error[%d-%d]: %s:%d\n", i, sizes[i], __func__, __LINE__); + /* wrapping should have been allowed */ + return 1; + } else if (ret >= 0 && i == 2) { + fprintf(stderr, "Error[%d-%d]: %s:%d\n", i, sizes[i], __func__, __LINE__); + /* wrapping shouldn't have been allowed */ + return 1; + } + + if (ret >= 0) { + data_size = ret; + + /* try unwrapping */ + memset(&kunwrap.f, 0, sizeof(kunwrap.f)); + kunwrap.f.input_size = sizeof(kunwrap); + kunwrap.f.wrapping_key = key; + kunwrap.f.dest_key = privkey; + kunwrap.f.data = data; + kunwrap.f.data_size = data_size; + kunwrap.wrap_algo_head.nla_len + = NLA_HDRLEN + sizeof(kunwrap.wrap_algo); + kunwrap.wrap_algo_head.nla_type + = NCR_ATTR_WRAPPING_ALGORITHM; + strcpy(kunwrap.wrap_algo, NCR_WALG_AES_RFC5649); + kunwrap.flags_head.nla_len + = NLA_HDRLEN + sizeof(kunwrap.flags); + kunwrap.flags_head.nla_type = NCR_ATTR_KEY_FLAGS; + kunwrap.flags = 0; + + ret = ioctl(cfd, NCRIO_KEY_UNWRAP, &kunwrap); + if (ret) { + fprintf(stderr, "Error[%d-%d]: %s:%d\n", i, sizes[i], __func__, __LINE__); + return 1; + } + } + fprintf(stdout, "*"); + fflush(stdout); + + } + + fprintf(stdout, " Success\n"); + return 0; +} + #define RSA_ENCRYPT_SIZE 32 static int rsa_key_encrypt(int cfd, ncr_key_t privkey, ncr_key_t pubkey, int oaep) { - struct ncr_session_once_op_st nop; + struct __attribute__((packed)) { + struct ncr_session_once f; + struct nlattr algo_head ALIGN_NL; + char algo[sizeof(ALG_RSA)] ALIGN_NL; + struct nlattr key_head ALIGN_NL; + uint32_t key ALIGN_NL; + struct nlattr rsa_head ALIGN_NL; + uint32_t rsa ALIGN_NL; + struct nlattr oaep_hash_head ALIGN_NL; + char oaep_hash[sizeof(SIGNATURE_HASH)] ALIGN_NL; + struct nlattr input_head ALIGN_NL; + struct ncr_session_input_data input ALIGN_NL; + struct nlattr output_head ALIGN_NL; + struct ncr_session_output_buffer output ALIGN_NL; + } op; uint8_t data[DATA_SIZE]; uint8_t vdata[RSA_ENCRYPT_SIZE]; - int enc_size; + size_t enc_size, dec_size; fprintf(stdout, "Tests on RSA (%s) key encryption:", (oaep!=0)?"OAEP":"PKCS V1.5"); fflush(stdout); @@ -540,55 +831,79 @@ static int rsa_key_encrypt(int cfd, ncr_key_t privkey, ncr_key_t pubkey, int oae memcpy(vdata, data, sizeof(vdata)); /* do encryption */ - memset(&nop, 0, sizeof(nop)); - nop.init.algorithm = NCR_ALG_RSA; - nop.init.key = pubkey; + memset(&op.f, 0, sizeof(op.f)); + op.f.input_size = sizeof(op); + op.f.op = NCR_OP_ENCRYPT; + op.algo_head.nla_len = NLA_HDRLEN + sizeof(op.algo); + op.algo_head.nla_type = NCR_ATTR_ALGORITHM; + strcpy(op.algo, ALG_RSA); + op.key_head.nla_len = NLA_HDRLEN + sizeof(op.key); + op.key_head.nla_type = NCR_ATTR_KEY; + op.key = pubkey; + op.rsa_head.nla_len = NLA_HDRLEN + sizeof(op.rsa); + op.rsa_head.nla_type = NCR_ATTR_RSA_ENCODING_METHOD; if (oaep) { - nop.init.params.params.rsa.type = RSA_PKCS1_OAEP; - nop.init.params.params.rsa.oaep_hash = NCR_ALG_SHA1; + op.rsa = RSA_PKCS1_OAEP; } else { - nop.init.params.params.rsa.type = RSA_PKCS1_V1_5; + op.rsa = RSA_PKCS1_V1_5; } - nop.init.op = NCR_OP_ENCRYPT; - nop.op.data.udata.input = data; - nop.op.data.udata.input_size = RSA_ENCRYPT_SIZE; - nop.op.data.udata.output = data; - nop.op.data.udata.output_size = sizeof(data); - nop.op.type = NCR_DIRECT_DATA; - - if (ioctl(cfd, NCRIO_SESSION_ONCE, &nop)) { + op.oaep_hash_head.nla_len = NLA_HDRLEN + sizeof(op.oaep_hash); + op.oaep_hash_head.nla_type = NCR_ATTR_RSA_OAEP_HASH_ALGORITHM; + strcpy(op.oaep_hash, SIGNATURE_HASH); /* Ignored if not using OAEP */ + op.input_head.nla_len = NLA_HDRLEN + sizeof(op.input); + op.input_head.nla_type = NCR_ATTR_UPDATE_INPUT_DATA; + op.input.data = data; + op.input.data_size = RSA_ENCRYPT_SIZE; + op.output_head.nla_len = NLA_HDRLEN + sizeof(op.output); + op.output_head.nla_type = NCR_ATTR_UPDATE_OUTPUT_BUFFER; + op.output.buffer = data; + op.output.buffer_size = sizeof(data); + op.output.result_size_ptr = &enc_size; + + if (ioctl(cfd, NCRIO_SESSION_ONCE, &op)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_SESSION_ONCE)"); return 1; } - - enc_size = nop.op.data.udata.output_size; /* decrypt data */ - memset(&nop, 0, sizeof(nop)); - nop.init.algorithm = NCR_ALG_RSA; - nop.init.key = privkey; - nop.init.op = NCR_OP_DECRYPT; + memset(&op.f, 0, sizeof(op.f)); + op.f.input_size = sizeof(op); + op.f.op = NCR_OP_DECRYPT; + op.algo_head.nla_len = NLA_HDRLEN + sizeof(op.algo); + op.algo_head.nla_type = NCR_ATTR_ALGORITHM; + strcpy(op.algo, ALG_RSA); + op.key_head.nla_len = NLA_HDRLEN + sizeof(op.key); + op.key_head.nla_type = NCR_ATTR_KEY; + op.key = privkey; + op.rsa_head.nla_len = NLA_HDRLEN + sizeof(op.rsa); + op.rsa_head.nla_type = NCR_ATTR_RSA_ENCODING_METHOD; if (oaep) { - nop.init.params.params.rsa.type = RSA_PKCS1_OAEP; - nop.init.params.params.rsa.oaep_hash = NCR_ALG_SHA1; + op.rsa = RSA_PKCS1_OAEP; } else { - nop.init.params.params.rsa.type = RSA_PKCS1_V1_5; + op.rsa = RSA_PKCS1_V1_5; } - nop.op.data.udata.input = data; - nop.op.data.udata.input_size = enc_size; - nop.op.data.udata.output = data; - nop.op.data.udata.output_size = sizeof(data); - nop.op.type = NCR_DIRECT_DATA; - - - if (ioctl(cfd, NCRIO_SESSION_ONCE, &nop)) { + op.oaep_hash_head.nla_len = NLA_HDRLEN + sizeof(op.oaep_hash); + op.oaep_hash_head.nla_type = NCR_ATTR_RSA_OAEP_HASH_ALGORITHM; + strcpy(op.oaep_hash, SIGNATURE_HASH); /* Ignored if not using OAEP */ + op.input_head.nla_len = NLA_HDRLEN + sizeof(op.input); + op.input_head.nla_type = NCR_ATTR_UPDATE_INPUT_DATA; + op.input.data = data; + op.input.data_size = enc_size; + op.output_head.nla_len = NLA_HDRLEN + sizeof(op.output); + op.output_head.nla_type = NCR_ATTR_UPDATE_OUTPUT_BUFFER; + op.output.buffer = data; + op.output.buffer_size = sizeof(data); + op.output.result_size_ptr = &dec_size; + + if (ioctl(cfd, NCRIO_SESSION_ONCE, &op)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_SESSION_ONCE)"); return 1; } - if (memcmp(vdata, data, sizeof(vdata)) != 0) { + if (dec_size != sizeof(vdata) + || memcmp(vdata, data, sizeof(vdata)) != 0) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); fprintf(stderr, "Decrypted data do not match!\n"); return 1; @@ -604,61 +919,234 @@ static int rsa_key_encrypt(int cfd, ncr_key_t privkey, ncr_key_t pubkey, int oae static int rsa_key_sign_verify(int cfd, ncr_key_t privkey, ncr_key_t pubkey, int pss) { - struct ncr_session_once_op_st nop; + struct __attribute__((packed)) { + struct ncr_session_once f; + struct nlattr algo_head ALIGN_NL; + char algo[sizeof(ALG_RSA)] ALIGN_NL; + struct nlattr key_head ALIGN_NL; + uint32_t key ALIGN_NL; + struct nlattr rsa_head ALIGN_NL; + uint32_t rsa ALIGN_NL; + struct nlattr sign_hash_head ALIGN_NL; + char sign_hash[sizeof(SIGNATURE_HASH)] ALIGN_NL; + struct nlattr input_head ALIGN_NL; + struct ncr_session_input_data input ALIGN_NL; + struct nlattr signature_head ALIGN_NL; + struct ncr_session_output_buffer signature ALIGN_NL; + } ksign; + struct __attribute__((packed)) { + struct ncr_session_once f; + struct nlattr algo_head ALIGN_NL; + char algo[sizeof(ALG_RSA)] ALIGN_NL; + struct nlattr key_head ALIGN_NL; + uint32_t key ALIGN_NL; + struct nlattr rsa_head ALIGN_NL; + uint32_t rsa ALIGN_NL; + struct nlattr sign_hash_head ALIGN_NL; + char sign_hash[sizeof(SIGNATURE_HASH)] ALIGN_NL; + struct nlattr input_head ALIGN_NL; + struct ncr_session_input_data input ALIGN_NL; + struct nlattr signature_head ALIGN_NL; + struct ncr_session_input_data signature ALIGN_NL; + } kverify; uint8_t data[DATA_SIZE]; uint8_t sig[DATA_SIZE]; - int sig_size; + size_t sig_size; + int ret; fprintf(stdout, "Tests on RSA (%s) key signature:", (pss!=0)?"PSS":"PKCS V1.5"); fflush(stdout); memset(data, 0x3, sizeof(data)); - /* sign datad */ - memset(&nop, 0, sizeof(nop)); - nop.init.algorithm = NCR_ALG_RSA; - nop.init.key = privkey; - nop.init.params.params.rsa.type = (pss!=0)?RSA_PKCS1_PSS:RSA_PKCS1_V1_5; - nop.init.params.params.rsa.sign_hash = NCR_ALG_SHA1; + /* sign data */ + memset(&ksign.f, 0, sizeof(ksign.f)); + ksign.f.input_size = sizeof(ksign); + ksign.f.op = NCR_OP_SIGN; + ksign.algo_head.nla_len = NLA_HDRLEN + sizeof(ksign.algo); + ksign.algo_head.nla_type = NCR_ATTR_ALGORITHM; + strcpy(ksign.algo, ALG_RSA); + ksign.key_head.nla_len = NLA_HDRLEN + sizeof(ksign.key); + ksign.key_head.nla_type = NCR_ATTR_KEY; + ksign.key = privkey; + ksign.rsa_head.nla_len = NLA_HDRLEN + sizeof(ksign.rsa); + ksign.rsa_head.nla_type = NCR_ATTR_RSA_ENCODING_METHOD; + ksign.rsa = (pss != 0) ? RSA_PKCS1_PSS : RSA_PKCS1_V1_5; + ksign.sign_hash_head.nla_len = NLA_HDRLEN + sizeof(ksign.sign_hash); + ksign.sign_hash_head.nla_type = NCR_ATTR_SIGNATURE_HASH_ALGORITHM; + strcpy(ksign.sign_hash, SIGNATURE_HASH); + ksign.input_head.nla_len = NLA_HDRLEN + sizeof(ksign.input); + ksign.input_head.nla_type = NCR_ATTR_UPDATE_INPUT_DATA; + ksign.input.data = data; + ksign.input.data_size = DATA_TO_SIGN; + ksign.signature_head.nla_len = NLA_HDRLEN + sizeof(ksign.signature); + ksign.signature_head.nla_type = NCR_ATTR_FINAL_OUTPUT_BUFFER; + ksign.signature.buffer = sig; + ksign.signature.buffer_size = sizeof(sig); + ksign.signature.result_size_ptr = &sig_size; + + if (ioctl(cfd, NCRIO_SESSION_ONCE, &ksign)) { + fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); + perror("ioctl(NCRIO_SESSION_ONCE)"); + return 1; + } - nop.init.op = NCR_OP_SIGN; - nop.op.data.udata.input = data; - nop.op.data.udata.input_size = DATA_TO_SIGN; - nop.op.data.udata.output = sig; - nop.op.data.udata.output_size = sizeof(sig); - nop.op.type = NCR_DIRECT_DATA; + /* verify signature */ + memset(data, 0x3, sizeof(data)); - if (ioctl(cfd, NCRIO_SESSION_ONCE, &nop)) { + memset(&kverify.f, 0, sizeof(kverify.f)); + kverify.f.input_size = sizeof(kverify); + kverify.f.op = NCR_OP_VERIFY; + kverify.algo_head.nla_len = NLA_HDRLEN + sizeof(kverify.algo); + kverify.algo_head.nla_type = NCR_ATTR_ALGORITHM; + strcpy(kverify.algo, ALG_RSA); + kverify.key_head.nla_len = NLA_HDRLEN + sizeof(kverify.key); + kverify.key_head.nla_type = NCR_ATTR_KEY; + kverify.key = pubkey; + kverify.rsa_head.nla_len = NLA_HDRLEN + sizeof(kverify.rsa); + kverify.rsa_head.nla_type = NCR_ATTR_RSA_ENCODING_METHOD; + kverify.rsa = (pss != 0) ? RSA_PKCS1_PSS : RSA_PKCS1_V1_5; + kverify.sign_hash_head.nla_len = NLA_HDRLEN + sizeof(kverify.sign_hash); + kverify.sign_hash_head.nla_type = NCR_ATTR_SIGNATURE_HASH_ALGORITHM; + strcpy(kverify.sign_hash, SIGNATURE_HASH); + kverify.input_head.nla_len = NLA_HDRLEN + sizeof(kverify.input); + kverify.input_head.nla_type = NCR_ATTR_UPDATE_INPUT_DATA; + kverify.input.data = data; + kverify.input.data_size = DATA_TO_SIGN; + kverify.signature_head.nla_len = NLA_HDRLEN + sizeof(kverify.signature); + kverify.signature_head.nla_type = NCR_ATTR_FINAL_INPUT_DATA; + kverify.signature.data = sig; + kverify.signature.data_size = sig_size; + + ret = ioctl(cfd, NCRIO_SESSION_ONCE, &kverify); + if (ret < 0) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_SESSION_ONCE)"); return 1; } - - sig_size = nop.op.data.udata.output_size; - /* verify signature */ - memset(&nop, 0, sizeof(nop)); - nop.init.algorithm = NCR_ALG_RSA; - nop.init.key = pubkey; - nop.init.params.params.rsa.type = (pss!=0)?RSA_PKCS1_PSS:RSA_PKCS1_V1_5; - nop.init.params.params.rsa.sign_hash = NCR_ALG_SHA1; + if (ret) + fprintf(stdout, " Success\n"); + else { + fprintf(stdout, " Verification Failed!\n"); + return 1; + } + + return 0; + +} + +static int rsa_key_sign_verify_transparent(int cfd, ncr_key_t privkey, + ncr_key_t pubkey, int pss) +{ + struct __attribute__((packed)) { + struct ncr_session_once f; + struct nlattr algo_head ALIGN_NL; + char algo[sizeof(NCR_ALG_RSA_TRANSPARENT_HASH)] ALIGN_NL; + struct nlattr key_head ALIGN_NL; + uint32_t key ALIGN_NL; + struct nlattr rsa_head ALIGN_NL; + uint32_t rsa ALIGN_NL; + struct nlattr sign_hash_head ALIGN_NL; + char sign_hash[sizeof(SIGNATURE_HASH)] ALIGN_NL; + struct nlattr input_head ALIGN_NL; + struct ncr_session_input_data input ALIGN_NL; + struct nlattr signature_head ALIGN_NL; + struct ncr_session_output_buffer signature ALIGN_NL; + } ksign; + struct __attribute__((packed)) { + struct ncr_session_once f; + struct nlattr algo_head ALIGN_NL; + char algo[sizeof(NCR_ALG_RSA_TRANSPARENT_HASH)] ALIGN_NL; + struct nlattr key_head ALIGN_NL; + uint32_t key ALIGN_NL; + struct nlattr rsa_head ALIGN_NL; + uint32_t rsa ALIGN_NL; + struct nlattr sign_hash_head ALIGN_NL; + char sign_hash[sizeof(SIGNATURE_HASH)] ALIGN_NL; + struct nlattr input_head ALIGN_NL; + struct ncr_session_input_data input ALIGN_NL; + struct nlattr signature_head ALIGN_NL; + struct ncr_session_input_data signature ALIGN_NL; + } kverify; + uint8_t data[SIGNATURE_HASH_SIZE]; + uint8_t sig[DATA_SIZE]; + size_t sig_size; + int ret; + + fprintf(stdout, "Tests on transparent RSA (%s) key signature:", + (pss != 0) ? "PSS" : "PKCS V1.5"); + fflush(stdout); memset(data, 0x3, sizeof(data)); - nop.init.op = NCR_OP_VERIFY; - nop.op.data.udata.input = data; - nop.op.data.udata.input_size = DATA_TO_SIGN; - nop.op.data.udata.output = sig; - nop.op.data.udata.output_size = sig_size; - nop.op.type = NCR_DIRECT_DATA; + /* sign data */ + memset(&ksign.f, 0, sizeof(ksign.f)); + ksign.f.input_size = sizeof(ksign); + ksign.f.op = NCR_OP_SIGN; + ksign.algo_head.nla_len = NLA_HDRLEN + sizeof(ksign.algo); + ksign.algo_head.nla_type = NCR_ATTR_ALGORITHM; + strcpy(ksign.algo, NCR_ALG_RSA_TRANSPARENT_HASH); + ksign.key_head.nla_len = NLA_HDRLEN + sizeof(ksign.key); + ksign.key_head.nla_type = NCR_ATTR_KEY; + ksign.key = privkey; + ksign.rsa_head.nla_len = NLA_HDRLEN + sizeof(ksign.rsa); + ksign.rsa_head.nla_type = NCR_ATTR_RSA_ENCODING_METHOD; + ksign.rsa = (pss != 0) ? RSA_PKCS1_PSS : RSA_PKCS1_V1_5; + ksign.sign_hash_head.nla_len = NLA_HDRLEN + sizeof(ksign.sign_hash); + ksign.sign_hash_head.nla_type = NCR_ATTR_SIGNATURE_HASH_ALGORITHM; + strcpy(ksign.sign_hash, SIGNATURE_HASH); + ksign.input_head.nla_len = NLA_HDRLEN + sizeof(ksign.input); + ksign.input_head.nla_type = NCR_ATTR_UPDATE_INPUT_DATA; + ksign.input.data = data; + ksign.input.data_size = SIGNATURE_HASH_SIZE; + ksign.signature_head.nla_len = NLA_HDRLEN + sizeof(ksign.signature); + ksign.signature_head.nla_type = NCR_ATTR_FINAL_OUTPUT_BUFFER; + ksign.signature.buffer = sig; + ksign.signature.buffer_size = sizeof(sig); + ksign.signature.result_size_ptr = &sig_size; + + if (ioctl(cfd, NCRIO_SESSION_ONCE, &ksign)) { + fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); + perror("ioctl(NCRIO_SESSION_ONCE)"); + return 1; + } - if (ioctl(cfd, NCRIO_SESSION_ONCE, &nop)) { + /* verify signature */ + memset(data, 0x3, sizeof(data)); + + memset(&kverify.f, 0, sizeof(kverify.f)); + kverify.f.input_size = sizeof(kverify); + kverify.f.op = NCR_OP_VERIFY; + kverify.algo_head.nla_len = NLA_HDRLEN + sizeof(kverify.algo); + kverify.algo_head.nla_type = NCR_ATTR_ALGORITHM; + strcpy(kverify.algo, NCR_ALG_RSA_TRANSPARENT_HASH); + kverify.key_head.nla_len = NLA_HDRLEN + sizeof(kverify.key); + kverify.key_head.nla_type = NCR_ATTR_KEY; + kverify.key = pubkey; + kverify.rsa_head.nla_len = NLA_HDRLEN + sizeof(kverify.rsa); + kverify.rsa_head.nla_type = NCR_ATTR_RSA_ENCODING_METHOD; + kverify.rsa = (pss != 0) ? RSA_PKCS1_PSS : RSA_PKCS1_V1_5; + kverify.sign_hash_head.nla_len = NLA_HDRLEN + sizeof(kverify.sign_hash); + kverify.sign_hash_head.nla_type = NCR_ATTR_SIGNATURE_HASH_ALGORITHM; + strcpy(kverify.sign_hash, SIGNATURE_HASH); + kverify.input_head.nla_len = NLA_HDRLEN + sizeof(kverify.input); + kverify.input_head.nla_type = NCR_ATTR_UPDATE_INPUT_DATA; + kverify.input.data = data; + kverify.input.data_size = SIGNATURE_HASH_SIZE; + kverify.signature_head.nla_len = NLA_HDRLEN + sizeof(kverify.signature); + kverify.signature_head.nla_type = NCR_ATTR_FINAL_INPUT_DATA; + kverify.signature.data = sig; + kverify.signature.data_size = sig_size; + + ret = ioctl(cfd, NCRIO_SESSION_ONCE, &kverify); + if (ret < 0) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_SESSION_ONCE)"); return 1; } - if (nop.op.err == NCR_SUCCESS) + if (ret) fprintf(stdout, " Success\n"); else { fprintf(stdout, " Verification Failed!\n"); @@ -666,62 +1154,105 @@ static int rsa_key_sign_verify(int cfd, ncr_key_t privkey, ncr_key_t pubkey, int } return 0; - } static int dsa_key_sign_verify(int cfd, ncr_key_t privkey, ncr_key_t pubkey) { - struct ncr_session_once_op_st nop; + struct __attribute__((packed)) { + struct ncr_session_once f; + struct nlattr algo_head ALIGN_NL; + char algo[sizeof(ALG_DSA)] ALIGN_NL; + struct nlattr key_head ALIGN_NL; + uint32_t key ALIGN_NL; + struct nlattr sign_hash_head ALIGN_NL; + char sign_hash[sizeof(SIGNATURE_HASH)] ALIGN_NL; + struct nlattr input_head ALIGN_NL; + struct ncr_session_input_data input ALIGN_NL; + struct nlattr signature_head ALIGN_NL; + struct ncr_session_output_buffer signature ALIGN_NL; + } ksign; + struct __attribute__((packed)) { + struct ncr_session_once f; + struct nlattr algo_head ALIGN_NL; + char algo[sizeof(ALG_DSA)] ALIGN_NL; + struct nlattr key_head ALIGN_NL; + uint32_t key ALIGN_NL; + struct nlattr sign_hash_head ALIGN_NL; + char sign_hash[sizeof(SIGNATURE_HASH)] ALIGN_NL; + struct nlattr input_head ALIGN_NL; + struct ncr_session_input_data input ALIGN_NL; + struct nlattr signature_head ALIGN_NL; + struct ncr_session_input_data signature ALIGN_NL; + } kverify; uint8_t data[DATA_SIZE]; uint8_t sig[DATA_SIZE]; - int sig_size; + size_t sig_size; + int ret; fprintf(stdout, "Tests on DSA key signature:"); fflush(stdout); memset(data, 0x3, sizeof(data)); - /* sign datad */ - memset(&nop, 0, sizeof(nop)); - nop.init.algorithm = NCR_ALG_DSA; - nop.init.key = privkey; - nop.init.params.params.dsa.sign_hash = NCR_ALG_SHA1; - - nop.init.op = NCR_OP_SIGN; - nop.op.data.udata.input = data; - nop.op.data.udata.input_size = DATA_TO_SIGN; - nop.op.data.udata.output = sig; - nop.op.data.udata.output_size = sizeof(sig); - nop.op.type = NCR_DIRECT_DATA; - - if (ioctl(cfd, NCRIO_SESSION_ONCE, &nop)) { + /* sign data */ + memset(&ksign.f, 0, sizeof(ksign.f)); + ksign.f.input_size = sizeof(ksign); + ksign.f.op = NCR_OP_SIGN; + ksign.algo_head.nla_len = NLA_HDRLEN + sizeof(ksign.algo); + ksign.algo_head.nla_type = NCR_ATTR_ALGORITHM; + strcpy(ksign.algo, ALG_DSA); + ksign.key_head.nla_len = NLA_HDRLEN + sizeof(ksign.key); + ksign.key_head.nla_type = NCR_ATTR_KEY; + ksign.key = privkey; + ksign.sign_hash_head.nla_len = NLA_HDRLEN + sizeof(ksign.sign_hash); + ksign.sign_hash_head.nla_type = NCR_ATTR_SIGNATURE_HASH_ALGORITHM; + strcpy(ksign.sign_hash, SIGNATURE_HASH); + ksign.input_head.nla_len = NLA_HDRLEN + sizeof(ksign.input); + ksign.input_head.nla_type = NCR_ATTR_UPDATE_INPUT_DATA; + ksign.input.data = data; + ksign.input.data_size = DATA_TO_SIGN; + ksign.signature_head.nla_len = NLA_HDRLEN + sizeof(ksign.signature); + ksign.signature_head.nla_type = NCR_ATTR_FINAL_OUTPUT_BUFFER; + ksign.signature.buffer = sig; + ksign.signature.buffer_size = sizeof(sig); + ksign.signature.result_size_ptr = &sig_size; + + if (ioctl(cfd, NCRIO_SESSION_ONCE, &ksign)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_SESSION_ONCE)"); return 1; } - - sig_size = nop.op.data.udata.output_size; /* verify signature */ - memset(&nop, 0, sizeof(nop)); - nop.init.algorithm = NCR_ALG_DSA; - nop.init.key = pubkey; - nop.init.params.params.dsa.sign_hash = NCR_ALG_SHA1; - - nop.init.op = NCR_OP_VERIFY; - nop.op.data.udata.input = data; - nop.op.data.udata.input_size = DATA_TO_SIGN; - nop.op.data.udata.output = sig; - nop.op.data.udata.output_size = sizeof(sig); - nop.op.type = NCR_DIRECT_DATA; - - if (ioctl(cfd, NCRIO_SESSION_ONCE, &nop)) { + memset(&kverify.f, 0, sizeof(kverify.f)); + kverify.f.input_size = sizeof(kverify); + kverify.f.op = NCR_OP_VERIFY; + kverify.algo_head.nla_len = NLA_HDRLEN + sizeof(kverify.algo); + kverify.algo_head.nla_type = NCR_ATTR_ALGORITHM; + strcpy(kverify.algo, ALG_DSA); + kverify.key_head.nla_len = NLA_HDRLEN + sizeof(kverify.key); + kverify.key_head.nla_type = NCR_ATTR_KEY; + kverify.key = pubkey; + kverify.sign_hash_head.nla_len = NLA_HDRLEN + sizeof(kverify.sign_hash); + kverify.sign_hash_head.nla_type = NCR_ATTR_SIGNATURE_HASH_ALGORITHM; + strcpy(kverify.sign_hash, SIGNATURE_HASH); + kverify.input_head.nla_len = NLA_HDRLEN + sizeof(kverify.input); + kverify.input_head.nla_type = NCR_ATTR_UPDATE_INPUT_DATA; + kverify.input.data = data; + kverify.input.data_size = DATA_TO_SIGN; + kverify.signature_head.nla_len = NLA_HDRLEN + sizeof(kverify.signature); + kverify.signature_head.nla_type = NCR_ATTR_FINAL_INPUT_DATA; + kverify.signature.data = sig; + kverify.signature.data_size = sizeof(sig); + + ret = ioctl(cfd, NCRIO_SESSION_ONCE, &kverify); + if (ret < 0) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_SESSION_ONCE)"); return 1; } - if (nop.op.err == NCR_SUCCESS) + if (ret) fprintf(stdout, " Success\n"); else { fprintf(stdout, " Verification Failed!\n"); @@ -732,13 +1263,127 @@ static int dsa_key_sign_verify(int cfd, ncr_key_t privkey, ncr_key_t pubkey) } +static int dsa_key_sign_verify_transparent(int cfd, ncr_key_t privkey, + ncr_key_t pubkey) +{ + struct __attribute__((packed)) { + struct ncr_session_once f; + struct nlattr algo_head ALIGN_NL; + char algo[sizeof(NCR_ALG_DSA_TRANSPARENT_HASH)] ALIGN_NL; + struct nlattr key_head ALIGN_NL; + uint32_t key ALIGN_NL; + struct nlattr sign_hash_head ALIGN_NL; + char sign_hash[sizeof(SIGNATURE_HASH)] ALIGN_NL; + struct nlattr input_head ALIGN_NL; + struct ncr_session_input_data input ALIGN_NL; + struct nlattr signature_head ALIGN_NL; + struct ncr_session_output_buffer signature ALIGN_NL; + } ksign; + struct __attribute__((packed)) { + struct ncr_session_once f; + struct nlattr algo_head ALIGN_NL; + char algo[sizeof(NCR_ALG_DSA_TRANSPARENT_HASH)] ALIGN_NL; + struct nlattr key_head ALIGN_NL; + uint32_t key ALIGN_NL; + struct nlattr sign_hash_head ALIGN_NL; + char sign_hash[sizeof(SIGNATURE_HASH)] ALIGN_NL; + struct nlattr input_head ALIGN_NL; + struct ncr_session_input_data input ALIGN_NL; + struct nlattr signature_head ALIGN_NL; + struct ncr_session_input_data signature ALIGN_NL; + } kverify; + uint8_t data[SIGNATURE_HASH_SIZE]; + uint8_t sig[DATA_SIZE]; + size_t sig_size; + int ret; + + fprintf(stdout, "Tests on transparent DSA key signature:"); + fflush(stdout); + + memset(data, 0x3, sizeof(data)); + + /* sign data */ + memset(&ksign.f, 0, sizeof(ksign.f)); + ksign.f.input_size = sizeof(ksign); + ksign.f.op = NCR_OP_SIGN; + ksign.algo_head.nla_len = NLA_HDRLEN + sizeof(ksign.algo); + ksign.algo_head.nla_type = NCR_ATTR_ALGORITHM; + strcpy(ksign.algo, NCR_ALG_DSA_TRANSPARENT_HASH); + ksign.key_head.nla_len = NLA_HDRLEN + sizeof(ksign.key); + ksign.key_head.nla_type = NCR_ATTR_KEY; + ksign.key = privkey; + ksign.sign_hash_head.nla_len = NLA_HDRLEN + sizeof(ksign.sign_hash); + ksign.sign_hash_head.nla_type = NCR_ATTR_SIGNATURE_HASH_ALGORITHM; + strcpy(ksign.sign_hash, SIGNATURE_HASH); + ksign.input_head.nla_len = NLA_HDRLEN + sizeof(ksign.input); + ksign.input_head.nla_type = NCR_ATTR_UPDATE_INPUT_DATA; + ksign.input.data = data; + ksign.input.data_size = SIGNATURE_HASH_SIZE; + ksign.signature_head.nla_len = NLA_HDRLEN + sizeof(ksign.signature); + ksign.signature_head.nla_type = NCR_ATTR_FINAL_OUTPUT_BUFFER; + ksign.signature.buffer = sig; + ksign.signature.buffer_size = sizeof(sig); + ksign.signature.result_size_ptr = &sig_size; + + if (ioctl(cfd, NCRIO_SESSION_ONCE, &ksign)) { + fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); + perror("ioctl(NCRIO_SESSION_ONCE)"); + return 1; + } + + /* verify signature */ + memset(&kverify.f, 0, sizeof(kverify.f)); + kverify.f.input_size = sizeof(kverify); + kverify.f.op = NCR_OP_VERIFY; + kverify.algo_head.nla_len = NLA_HDRLEN + sizeof(kverify.algo); + kverify.algo_head.nla_type = NCR_ATTR_ALGORITHM; + strcpy(kverify.algo, NCR_ALG_DSA_TRANSPARENT_HASH); + kverify.key_head.nla_len = NLA_HDRLEN + sizeof(kverify.key); + kverify.key_head.nla_type = NCR_ATTR_KEY; + kverify.key = pubkey; + kverify.sign_hash_head.nla_len = NLA_HDRLEN + sizeof(kverify.sign_hash); + kverify.sign_hash_head.nla_type = NCR_ATTR_SIGNATURE_HASH_ALGORITHM; + strcpy(kverify.sign_hash, SIGNATURE_HASH); + kverify.input_head.nla_len = NLA_HDRLEN + sizeof(kverify.input); + kverify.input_head.nla_type = NCR_ATTR_UPDATE_INPUT_DATA; + kverify.input.data = data; + kverify.input.data_size = SIGNATURE_HASH_SIZE; + kverify.signature_head.nla_len = NLA_HDRLEN + sizeof(kverify.signature); + kverify.signature_head.nla_type = NCR_ATTR_FINAL_INPUT_DATA; + kverify.signature.data = sig; + kverify.signature.data_size = sizeof(sig); + + ret = ioctl(cfd, NCRIO_SESSION_ONCE, &kverify); + if (ret < 0) { + fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); + perror("ioctl(NCRIO_SESSION_ONCE)"); + return 1; + } + + if (ret) + fprintf(stdout, " Success\n"); + else { + fprintf(stdout, " Verification Failed!\n"); + return 1; + } + + return 0; +} static int test_ncr_rsa(int cfd) { int ret; - struct ncr_key_generate_st kgen; + struct __attribute__((packed)) { + struct ncr_key_generate_pair f; + struct nlattr algo_head ALIGN_NL; + char algo[sizeof(ALG_RSA)] ALIGN_NL; + struct nlattr flags_head ALIGN_NL; + uint32_t flags ALIGN_NL; + struct nlattr bits_head ALIGN_NL; + uint32_t bits ALIGN_NL; + } kgen; ncr_key_t pubkey, privkey; - struct ncr_key_data_st keydata; + struct ncr_key_export kexport; uint8_t data[DATA_SIZE]; int data_size; @@ -746,24 +1391,33 @@ static int test_ncr_rsa(int cfd) fflush(stdout); /* convert it to key */ - if (ioctl(cfd, NCRIO_KEY_INIT, &privkey)) { + privkey = ioctl(cfd, NCRIO_KEY_INIT); + if (privkey == -1) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_INIT)"); return 1; } - if (ioctl(cfd, NCRIO_KEY_INIT, &pubkey)) { + pubkey = ioctl(cfd, NCRIO_KEY_INIT); + if (pubkey == -1) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_INIT)"); return 1; } memset(&kgen, 0, sizeof(kgen)); - kgen.desc = privkey; - kgen.desc2 = pubkey; - kgen.params.algorithm = NCR_ALG_RSA; - kgen.params.keyflags = NCR_KEY_FLAG_EXPORTABLE|NCR_KEY_FLAG_WRAPPABLE; - kgen.params.params.rsa.bits = 1024; + kgen.f.input_size = sizeof(kgen); + kgen.f.private_key = privkey; + kgen.f.public_key = pubkey; + kgen.algo_head.nla_len = NLA_HDRLEN + sizeof(kgen.algo); + kgen.algo_head.nla_type = NCR_ATTR_ALGORITHM; + strcpy(kgen.algo, ALG_RSA); + kgen.flags_head.nla_len = NLA_HDRLEN + sizeof(kgen.flags); + kgen.flags_head.nla_type = NCR_ATTR_KEY_FLAGS; + kgen.flags = NCR_KEY_FLAG_EXPORTABLE|NCR_KEY_FLAG_WRAPPABLE; + kgen.bits_head.nla_len = NLA_HDRLEN + sizeof(kgen.bits); + kgen.bits_head.nla_type = NCR_ATTR_RSA_MODULUS_BITS; + kgen.bits = 1024; if (ioctl(cfd, NCRIO_KEY_GENERATE_PAIR, &kgen)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); @@ -773,18 +1427,17 @@ static int test_ncr_rsa(int cfd) /* export the private key */ memset(data, 0, sizeof(data)); - memset(&keydata, 0, sizeof(keydata)); - keydata.key = privkey; - keydata.idata = data; - keydata.idata_size = sizeof(data); + memset(&kexport, 0, sizeof(kexport)); + kexport.key = privkey; + kexport.buffer = data; + kexport.buffer_size = sizeof(data); - if (ioctl(cfd, NCRIO_KEY_EXPORT, &keydata)) { + data_size = ioctl(cfd, NCRIO_KEY_EXPORT, &kexport); + if (data_size < 0) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_EXPORT)"); return 1; } - - data_size = keydata.idata_size; ret = privkey_info(data, data_size, 0); if (ret != 0) { @@ -795,18 +1448,17 @@ static int test_ncr_rsa(int cfd) /* export the public key */ memset(data, 0, sizeof(data)); - memset(&keydata, 0, sizeof(keydata)); - keydata.key = pubkey; - keydata.idata = data; - keydata.idata_size = sizeof(data); + memset(&kexport, 0, sizeof(kexport)); + kexport.key = pubkey; + kexport.buffer = data; + kexport.buffer_size = sizeof(data); - if (ioctl(cfd, NCRIO_KEY_EXPORT, &keydata)) { + data_size = ioctl(cfd, NCRIO_KEY_EXPORT, &kexport); + if (data_size < 0) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); - perror("ioctl(NCRIO_KEY_IMPORT)"); + perror("ioctl(NCRIO_KEY_EXPORT)"); return 1; } - - data_size = keydata.idata_size; ret = pubkey_info(data, data_size, 0); if (ret != 0) { @@ -828,6 +1480,18 @@ static int test_ncr_rsa(int cfd) return 1; } + ret = rsa_key_sign_verify_transparent(cfd, privkey, pubkey, 1); + if (ret != 0) { + fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); + return 1; + } + + ret = rsa_key_sign_verify_transparent(cfd, privkey, pubkey, 0); + if (ret != 0) { + fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); + return 1; + } + ret = rsa_key_encrypt(cfd, privkey, pubkey, 0); if (ret != 0) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); @@ -847,9 +1511,19 @@ static int test_ncr_rsa(int cfd) static int test_ncr_dsa(int cfd) { int ret; - struct ncr_key_generate_st kgen; + struct __attribute__((packed)) { + struct ncr_key_generate_pair f; + struct nlattr algo_head ALIGN_NL; + char algo[sizeof(ALG_DSA)] ALIGN_NL; + struct nlattr flags_head ALIGN_NL; + uint32_t flags ALIGN_NL; + struct nlattr q_bits_head ALIGN_NL; + uint32_t q_bits ALIGN_NL; + struct nlattr p_bits_head ALIGN_NL; + uint32_t p_bits ALIGN_NL; + } kgen; ncr_key_t pubkey, privkey; - struct ncr_key_data_st keydata; + struct ncr_key_export kexport; uint8_t data[DATA_SIZE]; int data_size; @@ -857,25 +1531,36 @@ static int test_ncr_dsa(int cfd) fflush(stdout); /* convert it to key */ - if (ioctl(cfd, NCRIO_KEY_INIT, &privkey)) { + privkey = ioctl(cfd, NCRIO_KEY_INIT); + if (privkey == -1) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_INIT)"); return 1; } - if (ioctl(cfd, NCRIO_KEY_INIT, &pubkey)) { + pubkey = ioctl(cfd, NCRIO_KEY_INIT); + if (pubkey == -1) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_INIT)"); return 1; } memset(&kgen, 0, sizeof(kgen)); - kgen.desc = privkey; - kgen.desc2 = pubkey; - kgen.params.algorithm = NCR_ALG_DSA; - kgen.params.keyflags = NCR_KEY_FLAG_EXPORTABLE|NCR_KEY_FLAG_WRAPPABLE; - kgen.params.params.dsa.q_bits = 160; - kgen.params.params.dsa.p_bits = 1024; + kgen.f.input_size = sizeof(kgen); + kgen.f.private_key = privkey; + kgen.f.public_key = pubkey; + kgen.algo_head.nla_len = NLA_HDRLEN + sizeof(kgen.algo); + kgen.algo_head.nla_type = NCR_ATTR_ALGORITHM; + strcpy(kgen.algo, ALG_DSA); + kgen.flags_head.nla_len = NLA_HDRLEN + sizeof(kgen.flags); + kgen.flags_head.nla_type = NCR_ATTR_KEY_FLAGS; + kgen.flags = NCR_KEY_FLAG_EXPORTABLE|NCR_KEY_FLAG_WRAPPABLE; + kgen.q_bits_head.nla_len = NLA_HDRLEN + sizeof(kgen.q_bits); + kgen.q_bits_head.nla_type = NCR_ATTR_DSA_Q_BITS; + kgen.q_bits = 160; + kgen.p_bits_head.nla_len = NLA_HDRLEN + sizeof(kgen.p_bits); + kgen.p_bits_head.nla_type = NCR_ATTR_DSA_P_BITS; + kgen.p_bits = 1024; if (ioctl(cfd, NCRIO_KEY_GENERATE_PAIR, &kgen)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); @@ -883,18 +1568,18 @@ static int test_ncr_dsa(int cfd) return 1; } - memset(&keydata, 0, sizeof(keydata)); memset(data, 0, sizeof(data)); - keydata.key = privkey; - keydata.idata = data; - keydata.idata_size = sizeof(data); + memset(&kexport, 0, sizeof(kexport)); + kexport.key = privkey; + kexport.buffer = data; + kexport.buffer_size = sizeof(data); - if (ioctl(cfd, NCRIO_KEY_EXPORT, &keydata)) { + data_size = ioctl(cfd, NCRIO_KEY_EXPORT, &kexport); + if (data_size < 0) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_EXPORT)"); return 1; } - data_size = keydata.idata_size; ret = privkey_info(data, data_size, 0); if (ret != 0) { @@ -905,18 +1590,17 @@ static int test_ncr_dsa(int cfd) /* export the public key */ memset(data, 0, sizeof(data)); - memset(&keydata, 0, sizeof(keydata)); - keydata.key = pubkey; - keydata.idata = data; - keydata.idata_size = sizeof(data); + memset(&kexport, 0, sizeof(kexport)); + kexport.key = pubkey; + kexport.buffer = data; + kexport.buffer_size = sizeof(data); - if (ioctl(cfd, NCRIO_KEY_EXPORT, &keydata)) { + data_size = ioctl(cfd, NCRIO_KEY_EXPORT, &kexport); + if (data_size < 0) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); - perror("ioctl(NCRIO_KEY_IMPORT)"); + perror("ioctl(NCRIO_KEY_EXPORT)"); return 1; } - - data_size = keydata.idata_size; ret = pubkey_info(data, data_size, 0); if (ret != 0) { @@ -932,6 +1616,12 @@ static int test_ncr_dsa(int cfd) return 1; } + ret = dsa_key_sign_verify_transparent(cfd, privkey, pubkey); + if (ret != 0) { + fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); + return 1; + } + return 0; } @@ -961,6 +1651,9 @@ main() if (test_ncr_dsa(fd)) return 1; + + if (test_ncr_wrap_key3(fd)) + return 1; /* Close the original descriptor */ if (close(fd)) { diff --git a/examples/speed.c b/examples/speed.c index 5898aaae76e..70a2ed851c3 100644 --- a/examples/speed.c +++ b/examples/speed.c @@ -17,6 +17,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <fcntl.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -24,11 +25,15 @@ #include <sys/time.h> #include <sys/types.h> #include <sys/stat.h> +#include <sys/socket.h> #include <signal.h> #include <unistd.h> -#include "../cryptodev.h" +#include <linux/netlink.h> #include "../ncr.h" +#define ALIGN_NL __attribute__((aligned(NLA_ALIGNTO))) +#define ALG_AES_CBC "cbc(aes)" + static double udifftimeval(struct timeval start, struct timeval end) { return (double)(end.tv_usec - start.tv_usec) + @@ -68,56 +73,7 @@ static void value2human(double bytes, double time, double* data, double* speed,c } -int encrypt_data(struct session_op *sess, int fdc, int chunksize) -{ - struct crypt_op cop; - char *buffer, iv[32]; - static int val = 23; - struct timeval start, end; - double total = 0; - double secs, ddata, dspeed; - char metric[16]; - - buffer = malloc(chunksize); - memset(iv, 0x23, 32); - - printf("\tEncrypting in chunks of %d bytes: ", chunksize); - fflush(stdout); - - memset(buffer, val++, chunksize); - - must_finish = 0; - alarm(5); - - gettimeofday(&start, NULL); - do { - memset(&cop, 0, sizeof(cop)); - cop.ses = sess->ses; - cop.len = chunksize; - cop.iv = (unsigned char *)iv; - cop.op = COP_ENCRYPT; - cop.flags = 0; - cop.src = cop.dst = (unsigned char *)buffer; - - if (ioctl(fdc, CIOCCRYPT, &cop)) { - perror("ioctl(CIOCCRYPT)"); - return 1; - } - total+=chunksize; - } while(must_finish==0); - gettimeofday(&end, NULL); - - secs = udifftimeval(start, end)/ 1000000.0; - - value2human(total, secs, &ddata, &dspeed, metric); - printf ("done. %.2f %s in %.2f secs: ", ddata, metric, secs); - printf ("%.2f %s/sec\n", dspeed, metric); - - return 0; -} - - -int encrypt_data_ncr_direct(int cfd, int algo, int chunksize) +int encrypt_data_ncr_direct(int cfd, const char *algo, int chunksize) { char *buffer, iv[32]; static int val = 23; @@ -126,23 +82,48 @@ int encrypt_data_ncr_direct(int cfd, int algo, int chunksize) double secs, ddata, dspeed; char metric[16]; ncr_key_t key; - struct ncr_key_generate_st kgen; - struct ncr_session_once_op_st nop; - - if (ioctl(cfd, NCRIO_KEY_INIT, &key)) { + struct __attribute__((packed)) { + struct ncr_key_generate f; + struct nlattr algo_head ALIGN_NL; + char algo[sizeof(ALG_AES_CBC)] ALIGN_NL; + struct nlattr bits_head ALIGN_NL; + uint32_t bits ALIGN_NL; + } kgen; + struct __attribute__((packed)) { + struct ncr_session_once f; + struct nlattr key_head ALIGN_NL; + uint32_t key ALIGN_NL; + struct nlattr input_head ALIGN_NL; + struct ncr_session_input_data input ALIGN_NL; + struct nlattr output_head ALIGN_NL; + struct ncr_session_output_buffer output ALIGN_NL; + struct nlattr iv_head ALIGN_NL; + struct nlattr algo_head ALIGN_NL; + char algo[128] ALIGN_NL; + } op; + size_t algo_size; + + algo_size = strlen(algo) + 1; + key = ioctl(cfd, NCRIO_KEY_INIT); + if (key == -1) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_KEY_INIT)"); return 1; } - kgen.desc = key; - kgen.params.algorithm = NCR_ALG_AES_CBC; - kgen.params.keyflags = NCR_KEY_FLAG_EXPORTABLE; - kgen.params.params.secret.bits = 128; /* 16 bytes */ - + memset(&kgen.f, 0, sizeof(kgen.f)); + kgen.f.input_size = sizeof(kgen); + kgen.f.key = key; + kgen.algo_head.nla_len = NLA_HDRLEN + sizeof(kgen.algo); + kgen.algo_head.nla_type = NCR_ATTR_ALGORITHM; + strcpy(kgen.algo, ALG_AES_CBC); + kgen.bits_head.nla_len = NLA_HDRLEN + sizeof(kgen.bits); + kgen.bits_head.nla_type = NCR_ATTR_SECRET_KEY_BITS; + kgen.bits = 128; /* 16 bytes */ + if (ioctl(cfd, NCRIO_KEY_GENERATE, &kgen)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); - perror("ioctl(NCRIO_KEY_IMPORT)"); + perror("ioctl(NCRIO_KEY_GENERATE)"); return 1; } @@ -160,17 +141,30 @@ int encrypt_data_ncr_direct(int cfd, int algo, int chunksize) gettimeofday(&start, NULL); do { - memset(&nop, 0, sizeof(nop)); - nop.init.algorithm = algo; - nop.init.key = key; - nop.init.op = NCR_OP_ENCRYPT; - nop.op.data.udata.input = buffer; - nop.op.data.udata.input_size = chunksize; - nop.op.data.udata.output = buffer; - nop.op.data.udata.output_size = chunksize; - nop.op.type = NCR_DIRECT_DATA; - - if (ioctl(cfd, NCRIO_SESSION_ONCE, &nop)) { + size_t output_size; + + memset(&op.f, 0, sizeof(op.f)); + op.f.op = NCR_OP_ENCRYPT; + op.key_head.nla_len = NLA_HDRLEN + sizeof(op.key); + op.key_head.nla_type = NCR_ATTR_KEY; + op.key = key; + op.input_head.nla_len = NLA_HDRLEN + sizeof(op.input); + op.input_head.nla_type = NCR_ATTR_UPDATE_INPUT_DATA; + op.input.data = buffer; + op.input.data_size = chunksize; + op.output_head.nla_len = NLA_HDRLEN + sizeof(op.output); + op.output_head.nla_type = NCR_ATTR_UPDATE_OUTPUT_BUFFER; + op.output.buffer = buffer; + op.output.buffer_size = chunksize; + op.output.result_size_ptr = &output_size; + op.iv_head.nla_len = NLA_HDRLEN + 0; + op.iv_head.nla_type = NCR_ATTR_IV; + op.algo_head.nla_len = NLA_HDRLEN + algo_size; + op.algo_head.nla_type = NCR_ATTR_ALGORITHM; + memcpy(op.algo, algo, algo_size); + op.f.input_size = op.algo + algo_size - (char *)&op; + + if (ioctl(cfd, NCRIO_SESSION_ONCE, &op)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); perror("ioctl(NCRIO_SESSION_ONCE)"); return 1; @@ -191,9 +185,7 @@ int encrypt_data_ncr_direct(int cfd, int algo, int chunksize) int main(void) { - int fd, i, fdc = -1; - struct session_op sess; - char keybuf[32]; + int fd, i; signal(SIGALRM, alarm_handler); @@ -201,57 +193,20 @@ int main(void) perror("open()"); return 1; } - if (ioctl(fd, CRIOGET, &fdc)) { - perror("ioctl(CRIOGET)"); - return 1; - } - - fprintf(stderr, "Testing NULL cipher: \n"); - memset(&sess, 0, sizeof(sess)); - sess.cipher = CRYPTO_NULL; - sess.keylen = 0; - sess.key = (unsigned char *)keybuf; - if (ioctl(fdc, CIOCGSESSION, &sess)) { - perror("ioctl(CIOCGSESSION)"); - return 1; - } - - for (i = 256; i <= (64 * 1024); i *= 2) { - if (encrypt_data(&sess, fdc, i)) - break; - } fprintf(stderr, "\nTesting NCR-DIRECT with NULL cipher: \n"); for (i = 256; i <= (64 * 1024); i *= 2) { - if (encrypt_data_ncr_direct(fdc, NCR_ALG_NULL, i)) + if (encrypt_data_ncr_direct(fd, "ecb(cipher_null)", i)) break; } - fprintf(stderr, "\nTesting AES-128-CBC cipher: \n"); - memset(&sess, 0, sizeof(sess)); - sess.cipher = CRYPTO_AES_CBC; - sess.keylen = 16; - memset(keybuf, 0x42, 16); - sess.key = (unsigned char *)keybuf; - if (ioctl(fdc, CIOCGSESSION, &sess)) { - perror("ioctl(CIOCGSESSION)"); - return 1; - } - - for (i = 256; i <= (64 * 1024); i *= 2) { - if (encrypt_data(&sess, fdc, i)) - break; - } - fprintf(stderr, "\nTesting NCR-DIRECT with AES-128-CBC cipher: \n"); for (i = 256; i <= (64 * 1024); i *= 2) { - if (encrypt_data_ncr_direct(fdc, NCR_ALG_AES_CBC, i)) + if (encrypt_data_ncr_direct(fd, "cbc(aes)", i)) break; } - - close(fdc); close(fd); return 0; } diff --git a/extras/openssl-0.9.8k-cryptodev-linux.diff b/extras/openssl-0.9.8k-cryptodev-linux.diff deleted file mode 100644 index 0a43e635ae9..00000000000 --- a/extras/openssl-0.9.8k-cryptodev-linux.diff +++ /dev/null @@ -1,74 +0,0 @@ -diff -ur openssl-0.9.8k/crypto/engine/eng_all.c openssl-0.9.8k.new/crypto/engine/eng_all.c ---- openssl-0.9.8k/crypto/engine/eng_all.c 2008-06-04 21:01:39.000000000 +0300 -+++ openssl-0.9.8k.new/crypto/engine/eng_all.c 2009-11-24 13:41:49.000000000 +0200 -@@ -104,16 +104,15 @@ - #endif - #endif - #ifndef OPENSSL_NO_HW --#if defined(__OpenBSD__) || defined(__FreeBSD__) -+# if (OpenBSD >= 200112) || ((__FreeBSD_version >= 470101 && __FreeBSD_version < 500000) || __FreeBSD_version >= 500041) || defined(__linux__) - ENGINE_load_cryptodev(); --#endif -+# endif - #if defined(OPENSSL_SYS_WIN32) && !defined(OPENSSL_NO_CAPIENG) - ENGINE_load_capi(); - #endif - #endif - } - --#if defined(__OpenBSD__) || defined(__FreeBSD__) - void ENGINE_setup_bsd_cryptodev(void) { - static int bsd_cryptodev_default_loaded = 0; - if (!bsd_cryptodev_default_loaded) { -@@ -122,4 +121,3 @@ - } - bsd_cryptodev_default_loaded=1; - } --#endif -diff -ur openssl-0.9.8k/crypto/engine/eng_cryptodev.c openssl-0.9.8k.new/crypto/engine/eng_cryptodev.c ---- openssl-0.9.8k/crypto/engine/eng_cryptodev.c 2004-06-15 14:45:42.000000000 +0300 -+++ openssl-0.9.8k.new/crypto/engine/eng_cryptodev.c 2009-11-24 13:45:31.000000000 +0200 -@@ -34,14 +34,15 @@ - #if (defined(__unix__) || defined(unix)) && !defined(USG) && \ - (defined(OpenBSD) || defined(__FreeBSD_version)) - #include <sys/param.h> --# if (OpenBSD >= 200112) || ((__FreeBSD_version >= 470101 && __FreeBSD_version < 500000) || __FreeBSD_version >= 500041) --# define HAVE_CRYPTODEV --# endif - # if (OpenBSD >= 200110) - # define HAVE_SYSLOG_R - # endif - #endif - -+#if (OpenBSD >= 200112) || ((__FreeBSD_version >= 470101 && __FreeBSD_version < 500000) || __FreeBSD_version >= 500041) || defined(__linux__) -+# define HAVE_CRYPTODEV -+#endif -+ - #ifndef HAVE_CRYPTODEV - - void -diff -ur openssl-0.9.8k/crypto/engine/engine.h openssl-0.9.8k.new/crypto/engine/engine.h ---- openssl-0.9.8k/crypto/engine/engine.h 2008-06-04 21:01:40.000000000 +0300 -+++ openssl-0.9.8k.new/crypto/engine/engine.h 2009-11-24 13:41:49.000000000 +0200 -@@ -703,9 +703,7 @@ - * values. */ - void *ENGINE_get_static_state(void); - --#if defined(__OpenBSD__) || defined(__FreeBSD__) - void ENGINE_setup_bsd_cryptodev(void); --#endif - - /* BEGIN ERROR CODES */ - /* The following lines are auto generated by the script mkerr.pl. Any changes -diff -ur openssl-0.9.8k/crypto/evp/c_all.c openssl-0.9.8k.new/crypto/evp/c_all.c ---- openssl-0.9.8k/crypto/evp/c_all.c 2004-08-29 19:36:04.000000000 +0300 -+++ openssl-0.9.8k.new/crypto/evp/c_all.c 2009-11-24 13:41:49.000000000 +0200 -@@ -83,8 +83,6 @@ - OpenSSL_add_all_ciphers(); - OpenSSL_add_all_digests(); - #ifndef OPENSSL_NO_ENGINE --# if defined(__OpenBSD__) || defined(__FreeBSD__) - ENGINE_setup_bsd_cryptodev(); --# endif - #endif - } diff --git a/extras/openssl-0.9.8l-cryptodev-aes256.patch b/extras/openssl-0.9.8l-cryptodev-aes256.patch deleted file mode 100644 index cf9bbbc80c6..00000000000 --- a/extras/openssl-0.9.8l-cryptodev-aes256.patch +++ /dev/null @@ -1,112 +0,0 @@ -This is http://people.freebsd.org/~pjd/patches/hw_cryptodev.c.patch adopted for -openssl-0.9.8l. It makes AES192 and AES256 CBC known to the cryptodev engine. - -There's also http://people.freebsd.org/~pjd/patches/eng_cryptodev.c.patch, -which seems more current, also adds SHA digests and does somehting CTX-related -to cryptodev_rsa_nocrt_mod_exp(). But since digests are disabled in -cryptodev_usable_digests() anyway and cryptodev_rsa_nocrt_mod_exp() is used for -RSA only, I didn't bother with it. - ---- openssl-0.9.8l/crypto/engine/eng_cryptodev.caes256 2004-06-15 13:45:42.000000000 +0200 -+++ openssl-0.9.8l/crypto/engine/eng_cryptodev.c 2010-02-16 21:57:15.000000000 +0100 -@@ -133,11 +133,14 @@ - { CRYPTO_DES_CBC, NID_des_cbc, 8, 8, }, - { CRYPTO_3DES_CBC, NID_des_ede3_cbc, 8, 24, }, - { CRYPTO_AES_CBC, NID_aes_128_cbc, 16, 16, }, -+ { CRYPTO_AES_CBC, NID_aes_192_cbc, 16, 24, }, -+ { CRYPTO_AES_CBC, NID_aes_256_cbc, 16, 32, }, - { CRYPTO_BLF_CBC, NID_bf_cbc, 8, 16, }, - { CRYPTO_CAST_CBC, NID_cast5_cbc, 8, 16, }, - { CRYPTO_SKIPJACK_CBC, NID_undef, 0, 0, }, - { 0, NID_undef, 0, 0, }, - }; -+#define NCIPHERS (sizeof(ciphers) / sizeof(ciphers[0])) - - static struct { - int id; -@@ -229,8 +232,8 @@ - int i; - - for (i = 0; ciphers[i].id; i++) -- if (ciphers[i].id == cipher) -- return (ciphers[i].keylen == len); -+ if (ciphers[i].id == cipher && ciphers[i].keylen == len) -+ return (1); - return (0); - } - -@@ -255,7 +258,7 @@ - static int - get_cryptodev_ciphers(const int **cnids) - { -- static int nids[CRYPTO_ALGORITHM_MAX]; -+ static int nids[NCIPHERS]; - struct session_op sess; - int fd, i, count = 0; - -@@ -266,7 +269,7 @@ - memset(&sess, 0, sizeof(sess)); - sess.key = (caddr_t)"123456781234567812345678"; - -- for (i = 0; ciphers[i].id && count < CRYPTO_ALGORITHM_MAX; i++) { -+ for (i = 0; ciphers[i].id && count < NCIPHERS; i++) { - if (ciphers[i].nid == NID_undef) - continue; - sess.cipher = ciphers[i].id; -@@ -550,7 +553,7 @@ - NULL - }; - --const EVP_CIPHER cryptodev_aes_cbc = { -+const EVP_CIPHER cryptodev_aes128_cbc = { - NID_aes_128_cbc, - 16, 16, 16, - EVP_CIPH_CBC_MODE, -@@ -563,6 +566,32 @@ - NULL - }; - -+const EVP_CIPHER cryptodev_aes192_cbc = { -+ NID_aes_192_cbc, -+ 16, 24, 16, -+ EVP_CIPH_CBC_MODE, -+ cryptodev_init_key, -+ cryptodev_cipher, -+ cryptodev_cleanup, -+ sizeof(struct dev_crypto_state), -+ EVP_CIPHER_set_asn1_iv, -+ EVP_CIPHER_get_asn1_iv, -+ NULL -+}; -+ -+const EVP_CIPHER cryptodev_aes256_cbc = { -+ NID_aes_256_cbc, -+ 16, 32, 16, -+ EVP_CIPH_CBC_MODE, -+ cryptodev_init_key, -+ cryptodev_cipher, -+ cryptodev_cleanup, -+ sizeof(struct dev_crypto_state), -+ EVP_CIPHER_set_asn1_iv, -+ EVP_CIPHER_get_asn1_iv, -+ NULL -+}; -+ - /* - * Registered by the ENGINE when used to find out how to deal with - * a particular NID in the ENGINE. this says what we'll do at the -@@ -589,7 +618,13 @@ - *cipher = &cryptodev_cast_cbc; - break; - case NID_aes_128_cbc: -- *cipher = &cryptodev_aes_cbc; -+ *cipher = &cryptodev_aes128_cbc; -+ break; -+ case NID_aes_192_cbc: -+ *cipher = &cryptodev_aes192_cbc; -+ break; -+ case NID_aes_256_cbc: -+ *cipher = &cryptodev_aes256_cbc; - break; - default: - *cipher = NULL; diff --git a/libtomcrypt/hashes/hash_memory.c b/libtomcrypt/hashes/hash_memory.c index a416de9624e..c6f51881245 100644 --- a/libtomcrypt/hashes/hash_memory.c +++ b/libtomcrypt/hashes/hash_memory.c @@ -44,7 +44,7 @@ int hash_memory(const struct algo_properties_st *hash, const unsigned char *in, return CRYPT_BUFFER_OVERFLOW; } - err = cryptodev_hash_init( &hdata, hash->kstr, 0, NULL, 0); + err = cryptodev_hash_init(&hdata, hash->kstr, NULL, 0); if (err < 0) { err = CRYPT_INVALID_HASH; goto LBL_ERR; diff --git a/libtomcrypt/hashes/hash_memory_multi.c b/libtomcrypt/hashes/hash_memory_multi.c index a9149166700..74226767a72 100644 --- a/libtomcrypt/hashes/hash_memory_multi.c +++ b/libtomcrypt/hashes/hash_memory_multi.c @@ -50,7 +50,7 @@ int hash_memory_multi(const struct algo_properties_st *hash, unsigned char *out, return CRYPT_BUFFER_OVERFLOW; } - err = cryptodev_hash_init( &hdata, hash->kstr, 0, NULL, 0); + err = cryptodev_hash_init(&hdata, hash->kstr, NULL, 0); if (err < 0) { err = CRYPT_INVALID_HASH; goto LBL_ERR; diff --git a/libtomcrypt/headers/tomcrypt_argchk.h b/libtomcrypt/headers/tomcrypt_argchk.h index 1b94434ccb3..1ba08c7ffad 100644 --- a/libtomcrypt/headers/tomcrypt_argchk.h +++ b/libtomcrypt/headers/tomcrypt_argchk.h @@ -25,8 +25,8 @@ void crypt_argchk(char *v, char *s, int d); #elif ARGTYPE == 4 -#define LTC_ARGCHK(x) if (!(x)) return CRYPT_INVALID_ARG; -#define LTC_ARGCHKVD(x) if (!(x)) return; +#define LTC_ARGCHK(x) if (!(x)) { printk( "\nwarning: ARGCHK failed at %s:%d\n", __FILE__, __LINE__); return CRYPT_INVALID_ARG; } +#define LTC_ARGCHKVD(x) if (!(x)) { printk( "\nwarning: ARGCHK failed at %s:%d\n", __FILE__, __LINE__); return; } #endif diff --git a/libtomcrypt/headers/tomcrypt_cfg.h b/libtomcrypt/headers/tomcrypt_cfg.h index b750c8be78c..8ad90bce4b0 100644 --- a/libtomcrypt/headers/tomcrypt_cfg.h +++ b/libtomcrypt/headers/tomcrypt_cfg.h @@ -42,9 +42,9 @@ LTC_EXPORT int LTC_CALL XSTRCMP(const char *s1, const char *s2); #endif -/* type of argument checking, 0=default, 1=fatal and 2=error+continue, 3=nothing */ +/* type of argument checking, 0=default, 1=fatal and 2=error+continue, 3=nothing, 4=return error */ #ifndef ARGTYPE - #define ARGTYPE 0 + #define ARGTYPE 4 #endif /* Controls endianess and size of registers. Leave uncommented to get platform neutral [slower] code diff --git a/libtomcrypt/headers/tomcrypt_pk.h b/libtomcrypt/headers/tomcrypt_pk.h index 145165efe51..73348805748 100644 --- a/libtomcrypt/headers/tomcrypt_pk.h +++ b/libtomcrypt/headers/tomcrypt_pk.h @@ -26,7 +26,7 @@ int rand_prime(mp_int *N, long len); /* Min and Max RSA key sizes (in bits) */ #define MIN_RSA_SIZE 1024 -#define MAX_RSA_SIZE 4096 +#define MAX_RSA_SIZE 8192 /** RSA LTC_PKCS style key */ typedef struct Rsa_key { diff --git a/libtomcrypt/pk/rsa/rsa_import.c b/libtomcrypt/pk/rsa/rsa_import.c index 9f3c5bfa642..87cb1030d47 100644 --- a/libtomcrypt/pk/rsa/rsa_import.c +++ b/libtomcrypt/pk/rsa/rsa_import.c @@ -84,7 +84,7 @@ int rsa_import(const unsigned char *in, unsigned long inlen, rsa_key *key) } /* it's a private key */ if ((err = der_decode_sequence_multi(in, inlen, - LTC_ASN1_INTEGER, 1UL, zero, + LTC_ASN1_INTEGER, 1UL, &zero, LTC_ASN1_INTEGER, 1UL, &key->N, LTC_ASN1_INTEGER, 1UL, &key->e, LTC_ASN1_INTEGER, 1UL, &key->d, @@ -27,7 +27,6 @@ #include <linux/slab.h> #include <linux/random.h> #include <linux/uaccess.h> -#include "cryptodev.h" #include <linux/scatterlist.h> #include <ncr.h> #include <ncr-int.h> @@ -281,3 +280,15 @@ fail: return ret; } + +int ncr_pk_get_dh_size( dh_key* key) +{ +int ret; + ret = mp_count_bits(&key->p); + if (ret <= 0) { + err(); + return -EINVAL; + } + + return ret; +} @@ -22,4 +22,6 @@ int dh_import(const uint8_t *in, size_t inlen, dh_key *key); int dh_derive_gxy(struct key_item_st* newkey, dh_key * key, void* pk, size_t pk_size); +int ncr_pk_get_dh_size( dh_key* key); + #endif diff --git a/ncr-int.h b/ncr-int.h index 2689804e29d..87964b08065 100644 --- a/ncr-int.h +++ b/ncr-int.h @@ -1,6 +1,7 @@ #ifndef NCR_INT_H # define NCR_INT_H +#include <linux/compat.h> #include <linux/idr.h> #include <linux/mutex.h> #include "ncr.h" @@ -14,9 +15,33 @@ #define err() printk(KERN_DEBUG"ncr: %s: %s: %d\n", __FILE__, __func__, __LINE__) +struct nlattr; +struct ncr_out; + +// Not all known algorithms - only for quick internal identification. Note +// that more than one struct algo_properties_st may share the same enum value! +enum ncr_algorithm { + NCR_ALG_NONE__, + NCR_ALG_NULL, + + NCR_ALG_3DES_CBC, + + NCR_ALG_MD5, + NCR_ALG_SHA1, + NCR_ALG_SHA2_224, + NCR_ALG_SHA2_256, + NCR_ALG_SHA2_384, + NCR_ALG_SHA2_512, + + NCR_ALG_RSA, + NCR_ALG_DSA, + NCR_ALG_DH, +}; + struct algo_properties_st { - ncr_algorithm_t algo; + enum ncr_algorithm algo; const char *kstr; + size_t kstr_len; unsigned needs_iv:1; unsigned is_hmac:1; unsigned can_sign:1; @@ -25,6 +50,7 @@ struct algo_properties_st { unsigned can_kx:1; /* key exchange */ unsigned is_symmetric:1; unsigned is_pk:1; + unsigned has_transparent_hash:1; int digest_size; /* NCR_KEY_TYPE_SECRET if for a secret key algorithm or MAC, * NCR_KEY_TYPE_PUBLIC for a public key algorithm. @@ -32,32 +58,6 @@ struct algo_properties_st { ncr_key_type_t key_type; }; -struct session_item_st { - const struct algo_properties_st *algorithm; - ncr_crypto_op_t op; - - /* contexts for various options. - * simpler to have them like that than - * in a union. - */ - struct cipher_data cipher; - struct ncr_pk_ctx pk; - struct hash_data hash; - - struct scatterlist *sg; - struct page **pages; - unsigned array_size; - unsigned available_pages; - struct mutex mem_mutex; /* down when the - * values above are changed. - */ - - struct key_item_st* key; - - atomic_t refcnt; - ncr_session_t desc; -}; - struct key_item_st { /* This object is also not protected from concurrent access. */ @@ -104,21 +104,33 @@ struct ncr_lists { void* ncr_init_lists(void); void ncr_deinit_lists(struct ncr_lists *lst); -int ncr_ioctl(struct ncr_lists *lst, unsigned int cmd, unsigned long arg); +long ncr_ioctl(struct ncr_lists *lst, unsigned int cmd, unsigned long arg); +long ncr_compat_ioctl(struct ncr_lists *lst, unsigned int cmd, + unsigned long arg); /* key derivation */ -int ncr_key_derive(struct ncr_lists *lst, void __user* arg); +int ncr_key_derive(struct ncr_lists *lst, const struct ncr_key_derive *data, + struct nlattr *tb[]); + +void ncr_key_clear(struct key_item_st* item); +int ncr_key_update_flags(struct key_item_st *item, const struct nlattr *nla); /* key handling */ -int ncr_key_init(struct ncr_lists *lst, void __user* arg); -int ncr_key_deinit(struct ncr_lists *lst, void __user* arg); -int ncr_key_export(struct ncr_lists *lst, void __user* arg); -int ncr_key_import(struct ncr_lists *lst, void __user* arg); +int ncr_key_init(struct ncr_lists *lst); +int ncr_key_deinit(struct ncr_lists *lst, ncr_key_t desc); +int ncr_key_export(struct ncr_lists *lst, const struct ncr_key_export *data, + struct nlattr *tb[]); +int ncr_key_import(struct ncr_lists *lst, const struct ncr_key_import *data, + struct nlattr *tb[]); void ncr_key_list_deinit(struct ncr_lists *lst); -int ncr_key_generate(struct ncr_lists *lst, void __user* arg); -int ncr_key_info(struct ncr_lists *lst, void __user* arg); - -int ncr_key_generate_pair(struct ncr_lists *lst, void __user* arg); +int ncr_key_generate(struct ncr_lists *lst, const struct ncr_key_generate *gen, + struct nlattr *tb[]); +int ncr_key_get_info(struct ncr_lists *lst, struct ncr_out *out, + const struct ncr_key_get_info *info, struct nlattr *tb[]); + +int ncr_key_generate_pair(struct ncr_lists *lst, + const struct ncr_key_generate_pair *gen, + struct nlattr *tb[]); int ncr_key_get_public(struct ncr_lists *lst, void __user* arg); int ncr_key_item_get_read(struct key_item_st**st, struct ncr_lists *lst, @@ -138,21 +150,32 @@ int ncr_limits_add_and_check(uid_t uid, pid_t pid, limits_type_t type); void ncr_limits_init(void); void ncr_limits_deinit(void); -int ncr_key_wrap(struct ncr_lists *lst, void __user* arg); -int ncr_key_unwrap(struct ncr_lists *lst, void __user* arg); -int ncr_key_storage_wrap(struct ncr_lists *lst, void __user* arg); -int ncr_key_storage_unwrap(struct ncr_lists *lst, void __user* arg); +int ncr_key_wrap(struct ncr_lists *lst, const struct ncr_key_wrap *wrap, + struct nlattr *tb[]); +int ncr_key_unwrap(struct ncr_lists *lst, const struct ncr_key_unwrap *wrap, + struct nlattr *tb[]); +int ncr_key_storage_wrap(struct ncr_lists *lst, + const struct ncr_key_storage_wrap *wrap, + struct nlattr *tb[]); +int ncr_key_storage_unwrap(struct ncr_lists *lst, + const struct ncr_key_storage_unwrap *wrap, + struct nlattr *tb[]); /* sessions */ -struct session_item_st* ncr_session_new(struct ncr_lists *lst); -void _ncr_sessions_item_put( struct session_item_st* item); -struct session_item_st* ncr_sessions_item_get(struct ncr_lists *lst, ncr_session_t desc); void ncr_sessions_list_deinit(struct ncr_lists *lst); -int ncr_session_init(struct ncr_lists* lists, void __user* arg); -int ncr_session_update(struct ncr_lists* lists, void __user* arg); -int ncr_session_final(struct ncr_lists* lists, void __user* arg); -int ncr_session_once(struct ncr_lists* lists, void __user* arg); +int ncr_session_init(struct ncr_lists *lists, + const struct ncr_session_init *session, + struct nlattr *tb[]); +int ncr_session_update(struct ncr_lists *lists, + const struct ncr_session_update *op, struct nlattr *tb[], + int compat); +int ncr_session_final(struct ncr_lists *lists, + const struct ncr_session_final *op, struct nlattr *tb[], + int compat); +int ncr_session_once(struct ncr_lists *lists, + const struct ncr_session_once *once, struct nlattr *tb[], + int compat); /* master key */ extern struct key_item_st master_key; @@ -166,7 +189,32 @@ int key_to_storage_data( uint8_t** data, size_t * data_size, const struct key_it /* misc helper macros */ -const struct algo_properties_st *_ncr_algo_to_properties(ncr_algorithm_t algo); -const struct algo_properties_st *ncr_key_params_get_sign_hash(const struct algo_properties_st *algo, struct ncr_key_params_st * params); +const struct algo_properties_st *_ncr_algo_to_properties(const char *algo); +const struct algo_properties_st *_ncr_nla_to_properties(const struct nlattr *nla); +int _ncr_key_get_sec_level(struct key_item_st* item); + +/* CONFIG_COMPAT handling */ + +#ifdef CONFIG_COMPAT +struct compat_ncr_session_input_data { + compat_uptr_t data; + compat_size_t data_size; +}; + +struct compat_ncr_session_output_buffer { + compat_uptr_t buffer; + compat_size_t buffer_size; + compat_uptr_t result_size_ptr; +}; +#endif + +int ncr_session_input_data_from_nla(struct ncr_session_input_data *dest, + const struct nlattr *nla, int compat); + +int ncr_session_output_buffer_from_nla(struct ncr_session_output_buffer *dest, + const struct nlattr *nla, int compat); + +int ncr_session_output_buffer_set_size(const struct ncr_session_output_buffer *dest, + size_t size, int compat); #endif diff --git a/ncr-key-storage.c b/ncr-key-storage.c index fc6948f5c61..9afa2dc0411 100644 --- a/ncr-key-storage.c +++ b/ncr-key-storage.c @@ -26,16 +26,16 @@ #include <linux/mm.h> #include <linux/slab.h> #include <linux/uaccess.h> -#include "cryptodev.h" #include <linux/scatterlist.h> #include "ncr.h" #include "ncr-int.h" #include "cryptodev_int.h" struct packed_key { + uint32_t version; uint8_t type; uint32_t flags; - uint16_t algorithm; /* valid for public/private keys */ + uint8_t algorithm[32]; /* NUL-terminated */ uint8_t key_id[MAX_KEY_ID_SIZE]; uint8_t key_id_size; @@ -43,6 +43,8 @@ struct packed_key { uint32_t raw_size; } __attribute__((__packed__)); +#define THIS_VERSION 1 + int key_to_storage_data( uint8_t** sdata, size_t * sdata_size, const struct key_item_st *key) { struct packed_key * pkey; @@ -54,9 +56,11 @@ int key_to_storage_data( uint8_t** sdata, size_t * sdata_size, const struct key_ return -ENOMEM; } + pkey->version = THIS_VERSION; pkey->type = key->type; pkey->flags = key->flags; - pkey->algorithm = key->algorithm->algo; + BUG_ON(strlen(key->algorithm->kstr) > sizeof(pkey->algorithm) - 1); + strcpy(pkey->algorithm, key->algorithm->kstr); pkey->key_id_size = key->key_id_size; memcpy(pkey->key_id, key->key_id, key->key_id_size); @@ -91,7 +95,9 @@ int key_from_storage_data(struct key_item_st* key, const void* data, size_t data const struct packed_key * pkey = data; int ret; - if (data_size != sizeof(*pkey) || pkey->key_id_size > MAX_KEY_ID_SIZE) { + if (data_size != sizeof(*pkey) || pkey->version != THIS_VERSION + || memchr(pkey->algorithm, '\0', sizeof(pkey->algorithm)) == NULL + || pkey->key_id_size > MAX_KEY_ID_SIZE) { err(); return -EINVAL; } diff --git a/ncr-key-wrap.c b/ncr-key-wrap.c index 0c56def27f7..7ea70cfc847 100644 --- a/ncr-key-wrap.c +++ b/ncr-key-wrap.c @@ -28,16 +28,22 @@ #include <linux/highmem.h> #include <linux/random.h> #include <linux/uaccess.h> -#include "cryptodev.h" #include <linux/scatterlist.h> +#include <net/netlink.h> #include "ncr.h" #include "ncr-int.h" #include "cryptodev_int.h" +#define KEY_WRAP_VERSION 0 + typedef uint8_t val64_t[8]; static const val64_t initA = "\xA6\xA6\xA6\xA6\xA6\xA6\xA6\xA6"; +static int key_to_packed_data( uint8_t** sdata, size_t * sdata_size, const struct key_item_st *key); +static int key_from_packed_data(struct nlattr *tb[], struct key_item_st* key, + const void* data, size_t data_size); + static void val64_xor( val64_t val, uint32_t x) { @@ -56,7 +62,7 @@ int i,j; if (*output_size < (n+1)*8) { err(); - return -EINVAL; + return -ERANGE; } memcpy(A, iv, 8); @@ -259,29 +265,73 @@ cleanup: static int wrap_aes_rfc5649(struct key_item_st* tobewrapped, struct key_item_st *kek, void* output, size_t* output_size, const void* iv, size_t iv_size) { - if (tobewrapped->type != NCR_KEY_TYPE_SECRET) { +int ret; +uint8_t* sdata = NULL; +size_t sdata_size = 0; + + ret = key_to_packed_data(&sdata, &sdata_size, tobewrapped); + if (ret < 0) { err(); - return -EINVAL; + return ret; } - return _wrap_aes_rfc5649(tobewrapped->key.secret.data, tobewrapped->key.secret.size, + ret = _wrap_aes_rfc5649(sdata, sdata_size, kek, output, output_size, iv, iv_size); + + kfree(sdata); + return ret; } static int unwrap_aes_rfc5649(struct key_item_st* output, struct key_item_st *kek, - void* wrapped, size_t wrapped_size, const void* iv, size_t iv_size) + void *wrapped, size_t wrapped_size, struct nlattr *tb[]) { - output->type = NCR_KEY_TYPE_SECRET; +const struct nlattr *nla; +int ret, iv_size; +void * sdata; +size_t sdata_size = KEY_DATA_MAX_SIZE; +const uint8_t *iv; + + sdata = kmalloc(sdata_size, GFP_KERNEL); + if (sdata == NULL) { + err(); + return -ENOMEM; + } + + nla = tb[NCR_ATTR_IV]; + if (nla != NULL) { + iv = nla_data(nla); + iv_size = nla_len(nla); + } else { + iv = NULL; + iv_size = 0; + } + + ret = _unwrap_aes_rfc5649(sdata, &sdata_size, kek, + wrapped, wrapped_size, iv, iv_size); + if (ret < 0) { + err(); + goto fail; + } + + ret = key_from_packed_data(tb, output, sdata, sdata_size); + if (ret < 0) { + err(); + goto fail; + } + + ret = 0; + +fail: + kfree(sdata); + return ret; - return _unwrap_aes_rfc5649(output->key.secret.data, &output->key.secret.size, kek, - wrapped, wrapped_size, iv, iv_size); } /* Wraps using the RFC3394 way. */ -static int wrap_aes(struct key_item_st* tobewrapped, struct key_item_st *kek, +static int wrap_aes_rfc3394(struct key_item_st* tobewrapped, struct key_item_st *kek, void* output, size_t *output_size, const void* iv, size_t iv_size) { size_t key_size, n; @@ -358,15 +408,25 @@ void print_val64(char* str, val64_t val) } #endif -static int unwrap_aes(struct key_item_st* output, struct key_item_st *kek, - void* wrapped_key, size_t wrapped_key_size, const void* iv, size_t iv_size) +static int unwrap_aes_rfc3394(struct key_item_st* output, struct key_item_st *kek, + void* wrapped_key, size_t wrapped_key_size, + struct nlattr *tb[]) { +const struct nlattr *nla; size_t n; val64_t A; int i, ret; struct cipher_data ctx; val64_t * R = NULL; - +int iv_size; +const uint8_t *iv; + + nla = tb[NCR_ATTR_IV]; + if (nla != NULL) { + iv = nla_data(nla); + iv_size = nla_len(nla); + } else + iv_size = 0; if (iv_size < sizeof(initA)) { iv_size = sizeof(initA); iv = initA; @@ -413,12 +473,17 @@ val64_t * R = NULL; goto cleanup; } + ret = ncr_key_update_flags(output, tb[NCR_ATTR_KEY_FLAGS]); + if (ret != 0) { + err(); + goto cleanup; + } + memset(&output->key, 0, sizeof(output->key)); for (i=0;i<n;i++) { memcpy(&output->key.secret.data[i*8], R[i], sizeof(R[i])); } output->key.secret.size = n*8; - output->flags = NCR_KEY_FLAG_WRAPPABLE; output->type = NCR_KEY_TYPE_SECRET; ret = 0; @@ -430,21 +495,54 @@ cleanup: return ret; } -int ncr_key_wrap(struct ncr_lists *lst, void __user* arg) +/* will check if the kek is of equal or higher security level than + * wkey. To prevent encrypting a 256 bit key with an 128 bit one. + */ +static int check_key_level(struct key_item_st* kek, struct key_item_st* wkey) +{ +int kek_level, wkey_level; + + /* allow wrapping of public keys with any key */ + if (wkey->type == NCR_KEY_TYPE_PUBLIC) + return 0; + + kek_level = _ncr_key_get_sec_level(kek); + if (kek_level < 0) { + err(); + return kek_level; + } + + wkey_level = _ncr_key_get_sec_level(wkey); + if (wkey_level < 0) { + err(); + return wkey_level; + } + + if (wkey_level > kek_level) { + err(); + return -EPERM; + } + + return 0; +} + +int ncr_key_wrap(struct ncr_lists *lst, const struct ncr_key_wrap *wrap, + struct nlattr *tb[]) { -struct ncr_key_wrap_st wrap; +const struct nlattr *nla; struct key_item_st* wkey = NULL; struct key_item_st* key = NULL; void* data = NULL; -size_t data_size; +const void *iv; +size_t data_size, iv_size; int ret; - if (unlikely(copy_from_user(&wrap, arg, sizeof(wrap)))) { + if (wrap->buffer_size < 0) { err(); - return -EFAULT; + return -EINVAL; } - ret = ncr_key_item_get_read( &wkey, lst, wrap.keytowrap); + ret = ncr_key_item_get_read(&wkey, lst, wrap->source_key); if (ret < 0) { err(); return ret; @@ -456,13 +554,25 @@ int ret; goto fail; } - ret = ncr_key_item_get_read( &key, lst, wrap.key); + ret = ncr_key_item_get_read(&key, lst, wrap->wrapping_key); + if (ret < 0) { + err(); + goto fail; + } + + if (!(key->flags & NCR_KEY_FLAG_WRAPPING)) { + err(); + ret = -EPERM; + goto fail; + } + + ret = check_key_level(key, wkey); if (ret < 0) { err(); goto fail; } - data_size = wrap.io_size; + data_size = wrap->buffer_size; data = kmalloc(data_size, GFP_KERNEL); if (data == NULL) { err(); @@ -470,18 +580,30 @@ int ret; goto fail; } - switch(wrap.algorithm) { - case NCR_WALG_AES_RFC3394: - ret = wrap_aes(wkey, key, data, &data_size, - wrap.params.params.cipher.iv, wrap.params.params.cipher.iv_size); - break; - case NCR_WALG_AES_RFC5649: - ret = wrap_aes_rfc5649(wkey, key, data, &data_size, - wrap.params.params.cipher.iv, wrap.params.params.cipher.iv_size); - break; - default: - err(); - ret = -EINVAL; + nla = tb[NCR_ATTR_IV]; + if (nla != NULL) { + iv = nla_data(nla); + iv_size = nla_len(nla); + } else { + iv = NULL; + iv_size = 0; + } + + nla = tb[NCR_ATTR_WRAPPING_ALGORITHM]; + if (nla == NULL) { + err(); + ret = -EINVAL; + goto fail; + } + if (nla_strcmp(nla, NCR_WALG_AES_RFC3394) == 0) + ret = wrap_aes_rfc3394(wkey, key, data, &data_size, iv, + iv_size); + else if (nla_strcmp(nla, NCR_WALG_AES_RFC5649) == 0) + ret = wrap_aes_rfc5649(wkey, key, data, &data_size, iv, + iv_size); + else { + err(); + ret = -EINVAL; } if (ret < 0) { @@ -489,18 +611,13 @@ int ret; goto fail; } - ret = copy_to_user(wrap.io, data, data_size); + ret = copy_to_user(wrap->buffer, data, data_size); if (unlikely(ret)) { ret = -EFAULT; goto fail; } - wrap.io_size = data_size; - - ret = copy_to_user(arg, &wrap, sizeof(wrap)); - if (unlikely(ret)) { - ret = -EFAULT; - } + ret = data_size; fail: if (wkey != NULL) _ncr_key_item_put(wkey); @@ -513,33 +630,35 @@ fail: /* Unwraps keys. All keys unwrapped are not accessible by * userspace. */ -int ncr_key_unwrap(struct ncr_lists *lst, void __user* arg) +int ncr_key_unwrap(struct ncr_lists *lst, const struct ncr_key_unwrap *wrap, + struct nlattr *tb[]) { -struct ncr_key_wrap_st wrap; +const struct nlattr *nla; struct key_item_st* wkey = NULL; struct key_item_st* key = NULL; void* data = NULL; size_t data_size; int ret; - if (unlikely(copy_from_user(&wrap, arg, sizeof(wrap)))) { + ret = ncr_key_item_get_write(&wkey, lst, wrap->dest_key); + if (ret < 0) { err(); - return -EFAULT; + return ret; } - ret = ncr_key_item_get_write( &wkey, lst, wrap.keytowrap); + ret = ncr_key_item_get_read(&key, lst, wrap->wrapping_key); if (ret < 0) { err(); - return ret; + goto fail; } - ret = ncr_key_item_get_read( &key, lst, wrap.key); - if (ret < 0) { + if (!(key->flags & NCR_KEY_FLAG_UNWRAPPING)) { err(); + ret = -EPERM; goto fail; } - data_size = wrap.io_size; + data_size = wrap->data_size; data = kmalloc(data_size, GFP_KERNEL); if (data == NULL) { err(); @@ -547,24 +666,27 @@ int ret; goto fail; } - if (unlikely(copy_from_user(data, wrap.io, data_size))) { + if (unlikely(copy_from_user(data, wrap->data, data_size))) { err(); ret = -EFAULT; goto fail; } + + ncr_key_clear(wkey); - switch(wrap.algorithm) { - case NCR_WALG_AES_RFC3394: - ret = unwrap_aes(wkey, key, data, data_size, - wrap.params.params.cipher.iv, wrap.params.params.cipher.iv_size); - break; - case NCR_WALG_AES_RFC5649: - ret = unwrap_aes_rfc5649(wkey, key, data, data_size, - wrap.params.params.cipher.iv, wrap.params.params.cipher.iv_size); - break; - default: - err(); - ret = -EINVAL; + nla = tb[NCR_ATTR_WRAPPING_ALGORITHM]; + if (nla == NULL) { + err(); + ret = -EINVAL; + goto fail; + } + if (nla_strcmp(nla, NCR_WALG_AES_RFC3394) == 0) + ret = unwrap_aes_rfc3394(wkey, key, data, data_size, tb); + else if (nla_strcmp(nla, NCR_WALG_AES_RFC5649) == 0) + ret = unwrap_aes_rfc5649(wkey, key, data, data_size, tb); + else { + err(); + ret = -EINVAL; } fail: @@ -575,9 +697,10 @@ fail: return ret; } -int ncr_key_storage_wrap(struct ncr_lists *lst, void __user* arg) +int ncr_key_storage_wrap(struct ncr_lists *lst, + const struct ncr_key_storage_wrap *wrap, + struct nlattr *tb[]) { -struct ncr_key_storage_wrap_st wrap; struct key_item_st* wkey = NULL; void* data = NULL; size_t data_size; @@ -590,24 +713,18 @@ int ret; return -ENOKEY; } - if (unlikely(copy_from_user(&wrap, arg, sizeof(wrap)))) { + if (wrap->buffer_size < 0) { err(); - return -EFAULT; + return -EINVAL; } - ret = ncr_key_item_get_read( &wkey, lst, wrap.keytowrap); + ret = ncr_key_item_get_read(&wkey, lst, wrap->key); if (ret < 0) { err(); return ret; } - if (!(wkey->flags & NCR_KEY_FLAG_WRAPPABLE)) { - err(); - ret = -EPERM; - goto fail; - } - - data_size = wrap.io_size; + data_size = wrap->buffer_size; data = kmalloc(data_size, GFP_KERNEL); if (data == NULL) { err(); @@ -627,18 +744,13 @@ int ret; goto fail; } - ret = copy_to_user(wrap.io, data, data_size); + ret = copy_to_user(wrap->buffer, data, data_size); if (unlikely(ret)) { ret = -EFAULT; goto fail; } - wrap.io_size = data_size; - - ret = copy_to_user(arg, &wrap, sizeof(wrap)); - if (unlikely(ret)) { - ret = -EFAULT; - } + ret = data_size; fail: if (wkey != NULL) _ncr_key_item_put(wkey); @@ -648,12 +760,10 @@ fail: return ret; } -/* Unwraps keys. All keys unwrapped are not accessible by - * userspace. - */ -int ncr_key_storage_unwrap(struct ncr_lists *lst, void __user* arg) +int ncr_key_storage_unwrap(struct ncr_lists *lst, + const struct ncr_key_storage_unwrap *wrap, + struct nlattr *tb[]) { -struct ncr_key_storage_wrap_st wrap; struct key_item_st* wkey = NULL; void* data = NULL; uint8_t * sdata = NULL; @@ -665,18 +775,13 @@ int ret; return -ENOKEY; } - if (unlikely(copy_from_user(&wrap, arg, sizeof(wrap)))) { - err(); - return -EFAULT; - } - - ret = ncr_key_item_get_write( &wkey, lst, wrap.keytowrap); + ret = ncr_key_item_get_write(&wkey, lst, wrap->key); if (ret < 0) { err(); return ret; } - data_size = wrap.io_size; + data_size = wrap->data_size; data = kmalloc(data_size, GFP_KERNEL); if (data == NULL) { err(); @@ -684,7 +789,7 @@ int ret; goto fail; } - if (unlikely(copy_from_user(data, wrap.io, data_size))) { + if (unlikely(copy_from_user(data, wrap->data, data_size))) { err(); ret = -EFAULT; goto fail; @@ -698,14 +803,14 @@ int ret; goto fail; } - wkey->flags = NCR_KEY_FLAG_WRAPPABLE; - ret = _unwrap_aes_rfc5649(sdata, &sdata_size, &master_key, data, data_size, NULL, 0); if (ret < 0) { err(); goto fail; } + ncr_key_clear(wkey); + ret = key_from_storage_data(wkey, sdata, sdata_size); if (ret < 0) { err(); @@ -720,3 +825,241 @@ fail: return ret; } + +#define DER_KEY_MAX_SIZE (KEY_DATA_MAX_SIZE+16) + +/* Packed data are DER encoded: + * PackedData ::= SEQUENCE { + * version INTEGER { v1(0) } + * type INTEGER { secret_key(0), rsa_privkey(1), rsa_pubkey(2), dsa_privkey(3), dsa_pubkey(4), + * dh_privkey(5), dh_pubkey(6) }, + * data OCTET STRING + * } + * + * This allows distinguishing types of wrapped keys. + */ +static int key_to_packed_data( uint8_t** sdata, size_t * sdata_size, const struct key_item_st *key) +{ + uint8_t * pkey = NULL; + uint8_t * derkey = NULL; + uint32_t pkey_size; + int ret, err; + unsigned long version = KEY_WRAP_VERSION; + unsigned long type; + unsigned long derlen; + + *sdata_size = KEY_DATA_MAX_SIZE; + pkey = kmalloc(*sdata_size, GFP_KERNEL); + if (pkey == NULL) { + err(); + return -ENOMEM; + } + + derlen = DER_KEY_MAX_SIZE; + derkey = kmalloc(derlen, GFP_KERNEL); + if (derkey == NULL) { + err(); + goto fail; + } + + if (key->type == NCR_KEY_TYPE_SECRET) { + memcpy(pkey, key->key.secret.data, key->key.secret.size); + pkey_size = key->key.secret.size; + + type = 0; + } else if (key->type == NCR_KEY_TYPE_PRIVATE || key->type == NCR_KEY_TYPE_PUBLIC) { + pkey_size = *sdata_size; + ret = ncr_pk_pack( key, pkey, &pkey_size); + if (ret < 0) { + err(); + goto fail; + } + + switch (key->algorithm->algo) { + case NCR_ALG_RSA: + if (key->type == NCR_KEY_TYPE_PUBLIC) + type = 2; + else type = 1; + break; + case NCR_ALG_DSA: + if (key->type == NCR_KEY_TYPE_PUBLIC) + type = 4; + else type = 3; + break; + case NCR_ALG_DH: + if (key->type == NCR_KEY_TYPE_PUBLIC) + type = 6; + else type = 5; + break; + default: + /* unsupported yet */ + ret = -EINVAL; + err(); + goto fail; + } + + } else { + err(); + ret = -EINVAL; + goto fail; + } + + err = der_encode_sequence_multi(derkey, &derlen, + LTC_ASN1_SHORT_INTEGER, 1UL, &version, + LTC_ASN1_SHORT_INTEGER, 1UL, &type, + LTC_ASN1_OCTET_STRING, (unsigned long)pkey_size, pkey, + LTC_ASN1_EOL, 0UL, NULL); + + kfree(pkey); + + if (err != CRYPT_OK) { + err(); + ret = _ncr_tomerr(err); + goto fail; + } + + *sdata = (void*)derkey; + *sdata_size = derlen; + + return 0; +fail: + kfree(pkey); + kfree(derkey); + + return ret; +} + +inline static int packed_type_to_key_type(unsigned long type, struct key_item_st* key) +{ + switch(type) { + case 0: + key->type = NCR_KEY_TYPE_SECRET; + key->algorithm = _ncr_algo_to_properties("cbc(aes)"); + break; + case 1: + key->type = NCR_KEY_TYPE_PRIVATE; + key->algorithm = _ncr_algo_to_properties("rsa"); + break; + case 2: + key->type = NCR_KEY_TYPE_PUBLIC; + key->algorithm = _ncr_algo_to_properties("rsa"); + break; + case 3: + key->type = NCR_KEY_TYPE_PRIVATE; + key->algorithm = _ncr_algo_to_properties("dsa"); + break; + case 4: + key->type = NCR_KEY_TYPE_PUBLIC; + key->algorithm = _ncr_algo_to_properties("dsa"); + break; + case 5: + key->type = NCR_KEY_TYPE_PRIVATE; + key->algorithm = _ncr_algo_to_properties("dh"); + break; + case 6: + key->type = NCR_KEY_TYPE_PUBLIC; + key->algorithm = _ncr_algo_to_properties("dh"); + break; + default: + err(); + return -EINVAL; + } + + if (key->algorithm == NULL) { + err(); + return -EINVAL; + } + + return 0; +} + + + +/* Unpack, or better decode the DER data + */ +static int key_from_packed_data(struct nlattr *tb[], struct key_item_st *key, + const void *data, size_t data_size) +{ + ltc_asn1_list list[6]; + int ret, i = 0, pkey_size, err; + unsigned long version, type; + uint8_t * pkey = NULL; + + if (data_size > DER_KEY_MAX_SIZE) { + err(); + return -EINVAL; + } + + pkey_size = KEY_DATA_MAX_SIZE; + pkey = kmalloc(pkey_size, GFP_KERNEL); + if (pkey == NULL) { + err(); + return -ENOMEM; + } + + list[i].type = LTC_ASN1_SHORT_INTEGER; + list[i].size = 1; + list[i++].data = &version; + + list[i].type = LTC_ASN1_SHORT_INTEGER; + list[i].size = 1; + list[i++].data = &type; + + list[i].type = LTC_ASN1_OCTET_STRING; + list[i].size = pkey_size; + list[i++].data = pkey; + + err = der_decode_sequence(data, data_size, list, i); + if (err != CRYPT_OK) { + err(); + ret = _ncr_tomerr(err); + goto fail; + } + + if (version != KEY_WRAP_VERSION) { + err(); + ret = -EINVAL; + goto fail; + } + + pkey_size = list[2].size; + + ret = packed_type_to_key_type(type, key); + if (ret < 0) { + err(); + goto fail; + } + + ret = ncr_key_update_flags(key, tb[NCR_ATTR_KEY_FLAGS]); + if (ret != 0) { + err(); + return ret; + } + + if (key->type == NCR_KEY_TYPE_SECRET) { + if (data_size > NCR_CIPHER_MAX_KEY_LEN) { + err(); + return -EINVAL; + } + key->key.secret.size = pkey_size; + memcpy(key->key.secret.data, pkey, pkey_size); + } else if (key->type == NCR_KEY_TYPE_PUBLIC + || key->type == NCR_KEY_TYPE_PRIVATE) { + + ret = ncr_pk_unpack( key, pkey, pkey_size); + if (ret < 0) { + err(); + return ret; + } + } else { + err(); + return -EINVAL; + } + + ret = 0; + +fail: + kfree(pkey); + + return ret; +} diff --git a/ncr-key.c b/ncr-key.c index bf438fa22cc..406e03299c3 100644 --- a/ncr-key.c +++ b/ncr-key.c @@ -27,12 +27,11 @@ #include <linux/slab.h> #include <linux/random.h> #include <linux/uaccess.h> -#include "cryptodev.h" #include <linux/scatterlist.h> +#include <net/netlink.h> #include "ncr.h" #include "ncr-int.h" - -static void ncr_key_clear(struct key_item_st* item); +#include "utils.h" static int key_list_deinit_fn(int id, void *item, void *unused) { @@ -151,7 +150,7 @@ static void _ncr_key_remove(struct ncr_lists *lst, ncr_key_t desc) _ncr_key_item_put(item); } -int ncr_key_init(struct ncr_lists *lst, void __user* arg) +int ncr_key_init(struct ncr_lists *lst) { ncr_key_t desc; struct key_item_st* key; @@ -190,49 +189,33 @@ int ncr_key_init(struct ncr_lists *lst, void __user* arg) desc = key->desc; mutex_unlock(&lst->key_idr_mutex); - ret = copy_to_user(arg, &desc, sizeof(desc)); - if (unlikely(ret)) { - _ncr_key_remove(lst, desc); - return -EFAULT; - } - return ret; + return desc; err_limits: ncr_limits_remove(current_euid(), task_pid_nr(current), LIMIT_TYPE_KEY); return ret; } -int ncr_key_deinit(struct ncr_lists *lst, void __user* arg) +int ncr_key_deinit(struct ncr_lists *lst, ncr_key_t desc) { - ncr_key_t desc; - - if (unlikely(copy_from_user(&desc, arg, sizeof(desc)))) { - err(); - return -EFAULT; - } - _ncr_key_remove(lst, desc); - return 0; } -/* "exports" a key to a data item. If the key is not exportable - * to userspace then the data item will also not be. - */ -int ncr_key_export(struct ncr_lists *lst, void __user* arg) +int ncr_key_export(struct ncr_lists *lst, const struct ncr_key_export *data, + struct nlattr *tb[]) { -struct ncr_key_data_st data; struct key_item_st* item = NULL; void* tmp = NULL; uint32_t tmp_size; int ret; - if (unlikely(copy_from_user(&data, arg, sizeof(data)))) { + if (data->buffer_size < 0) { err(); - return -EFAULT; + return -EINVAL; } - ret = ncr_key_item_get_read( &item, lst, data.key); + ret = ncr_key_item_get_read(&item, lst, data->key); if (ret < 0) { err(); return ret; @@ -246,15 +229,15 @@ int ret; switch (item->type) { case NCR_KEY_TYPE_SECRET: - if (item->key.secret.size > data.idata_size) { + if (item->key.secret.size > data->buffer_size) { err(); - ret = -EINVAL; + ret = -ERANGE; goto fail; } /* found */ if (item->key.secret.size > 0) { - ret = copy_to_user(data.idata, item->key.secret.data, item->key.secret.size); + ret = copy_to_user(data->buffer, item->key.secret.data, item->key.secret.size); if (unlikely(ret)) { err(); ret = -EFAULT; @@ -262,11 +245,11 @@ int ret; } } - data.idata_size = item->key.secret.size; + ret = item->key.secret.size; break; case NCR_KEY_TYPE_PUBLIC: case NCR_KEY_TYPE_PRIVATE: - tmp_size = data.idata_size; + tmp_size = data->buffer_size; tmp = kmalloc(tmp_size, GFP_KERNEL); if (tmp == NULL) { @@ -276,20 +259,19 @@ int ret; } ret = ncr_pk_pack(item, tmp, &tmp_size); - data.idata_size = tmp_size; - if (ret < 0) { err(); goto fail; } - ret = copy_to_user(data.idata, tmp, tmp_size); + ret = copy_to_user(data->buffer, tmp, tmp_size); if (unlikely(ret)) { err(); ret = -EFAULT; goto fail; } + ret = tmp_size; break; default: err(); @@ -297,12 +279,6 @@ int ret; goto fail; } - if (unlikely(copy_to_user(arg, &data, sizeof(data)))) { - err(); - ret = -EFAULT; - } else - ret = 0; - fail: kfree(tmp); if (item) @@ -311,23 +287,30 @@ fail: } -/* "imports" a key from a data item. If the key is not exportable - * to userspace then the key item will also not be. - */ -int ncr_key_import(struct ncr_lists *lst, void __user* arg) +int ncr_key_update_flags(struct key_item_st* item, const struct nlattr *nla) +{ + uint32_t flags; + + if (nla == NULL) + return 0; + flags = nla_get_u32(nla); + if (!capable(CAP_SYS_ADMIN) + && (flags & (NCR_KEY_FLAG_WRAPPING | NCR_KEY_FLAG_UNWRAPPING)) != 0) + return -EPERM; + item->flags = flags; + return 0; +} + +int ncr_key_import(struct ncr_lists *lst, const struct ncr_key_import *data, + struct nlattr *tb[]) { -struct ncr_key_data_st data; +const struct nlattr *nla; struct key_item_st* item = NULL; int ret; void* tmp = NULL; size_t tmp_size; - if (unlikely(copy_from_user(&data, arg, sizeof(data)))) { - err(); - return -EFAULT; - } - - ret = ncr_key_item_get_write( &item, lst, data.key); + ret = ncr_key_item_get_write( &item, lst, data->key); if (ret < 0) { err(); return ret; @@ -335,42 +318,55 @@ size_t tmp_size; ncr_key_clear(item); - tmp = kmalloc(data.idata_size, GFP_KERNEL); + tmp = kmalloc(data->data_size, GFP_KERNEL); if (tmp == NULL) { err(); ret = -ENOMEM; goto fail; } - if (unlikely(copy_from_user(tmp, data.idata, data.idata_size))) { + if (unlikely(copy_from_user(tmp, data->data, data->data_size))) { err(); ret = -EFAULT; goto fail; } - tmp_size = data.idata_size; - - item->type = data.type; - item->algorithm = _ncr_algo_to_properties(data.algorithm); - if (item->algorithm == NULL) { + tmp_size = data->data_size; + + nla = tb[NCR_ATTR_KEY_TYPE]; + if (tb == NULL) { err(); ret = -EINVAL; goto fail; } - item->flags = data.flags; + item->type = nla_get_u32(nla); - if (data.key_id_size > MAX_KEY_ID_SIZE) { + item->algorithm = _ncr_nla_to_properties(tb[NCR_ATTR_ALGORITHM]); + if (item->algorithm == NULL) { err(); ret = -EINVAL; goto fail; } - item->key_id_size = data.key_id_size; - if (data.key_id_size > 0) - memcpy(item->key_id, data.key_id, data.key_id_size); + ret = ncr_key_update_flags(item, tb[NCR_ATTR_KEY_FLAGS]); + if (ret < 0) { + err(); + goto fail; + } + + nla = tb[NCR_ATTR_KEY_ID]; + if (nla != NULL) { + if (nla_len(nla) > MAX_KEY_ID_SIZE) { + err(); + ret = -EOVERFLOW; + goto fail; + } + + item->key_id_size = nla_len(nla); + memcpy(item->key_id, nla_data(nla), item->key_id_size); + } switch(item->type) { case NCR_KEY_TYPE_SECRET: - if (tmp_size > NCR_CIPHER_MAX_KEY_LEN) { err(); ret = -EINVAL; @@ -405,7 +401,7 @@ fail: return ret; } -static void ncr_key_clear(struct key_item_st* item) +void ncr_key_clear(struct key_item_st* item) { /* clears any previously allocated parameters */ if (item->type == NCR_KEY_TYPE_PRIVATE || @@ -423,20 +419,16 @@ static void ncr_key_clear(struct key_item_st* item) /* Generate a secret key */ -int ncr_key_generate(struct ncr_lists *lst, void __user* arg) +int ncr_key_generate(struct ncr_lists *lst, const struct ncr_key_generate *gen, + struct nlattr *tb[]) { -struct ncr_key_generate_st gen; +const struct nlattr *nla; struct key_item_st* item = NULL; const struct algo_properties_st *algo; int ret; size_t size; - if (unlikely(copy_from_user(&gen, arg, sizeof(gen)))) { - err(); - return -EFAULT; - } - - ret = ncr_key_item_get_write( &item, lst, gen.desc); + ret = ncr_key_item_get_write(&item, lst, gen->key); if (ret < 0) { err(); return ret; @@ -445,8 +437,13 @@ size_t size; ncr_key_clear(item); /* we generate only secret keys */ - item->flags = gen.params.keyflags; - algo = _ncr_algo_to_properties(gen.params.algorithm); + ret = ncr_key_update_flags(item, tb[NCR_ATTR_KEY_FLAGS]); + if (ret < 0) { + err(); + goto fail; + } + + algo = _ncr_nla_to_properties(tb[NCR_ATTR_ALGORITHM]); if (algo == NULL) { err(); ret = -EINVAL; @@ -454,11 +451,19 @@ size_t size; } item->type = algo->key_type; if (item->type == NCR_KEY_TYPE_SECRET) { + u32 key_bits; + item->algorithm = algo; - size = gen.params.params.secret.bits/8; - if ((gen.params.params.secret.bits % 8 != 0) || - (size > NCR_CIPHER_MAX_KEY_LEN)) { + nla = tb[NCR_ATTR_SECRET_KEY_BITS]; + if (nla == NULL) { + err(); + ret = -EINVAL; + goto fail; + } + key_bits = nla_get_u32(nla); + size = key_bits / 8; + if (key_bits % 8 != 0 || size > NCR_CIPHER_MAX_KEY_LEN) { err(); ret = -EINVAL; goto fail; @@ -486,18 +491,120 @@ fail: return ret; } -int ncr_key_info(struct ncr_lists *lst, void __user* arg) +/* Those values are derived from "ECRYPT II Yearly Report on Algorithms and + * Keysizes (2009-2010)". It maps the strength of public key algorithms to + * symmetric ones. Should be kept up to date. + */ +static const struct { + unsigned int bits; /* sec level */ + unsigned int rsa_bits; + unsigned int dlog_bits; +} ecrypt_vals[] = { + {64, 816, 816}, + {80, 1248, 1248}, + {112, 2432, 2432}, + {128, 3248, 3248}, + {160, 5312, 5312}, + {192, 7936, 7936}, + {256, 15424, 15424}, + {0,0,0} +}; + +static unsigned int rsa_to_bits(unsigned int rsa_bits) { -struct ncr_key_info_st info; -struct key_item_st* item = NULL; -int ret; +int i = 1; - if (unlikely(copy_from_user(&info, arg, sizeof(info)))) { - err(); - return -EFAULT; + if (rsa_bits <= ecrypt_vals[0].rsa_bits) + return ecrypt_vals[0].rsa_bits; + + do { + if (rsa_bits <= ecrypt_vals[i].rsa_bits && + rsa_bits > ecrypt_vals[i-1].rsa_bits) { + + return ecrypt_vals[i].bits; + } + } while(ecrypt_vals[++i].bits != 0); + + /* return the highest found so far */ + return ecrypt_vals[i-1].bits; +} + +static unsigned int dlog_to_bits(unsigned int dlog_bits) +{ +int i = 1; + + if (dlog_bits <= ecrypt_vals[0].dlog_bits) + return ecrypt_vals[0].dlog_bits; + + do { + if (dlog_bits <= ecrypt_vals[i].dlog_bits && + dlog_bits > ecrypt_vals[i-1].dlog_bits) { + + return ecrypt_vals[i].bits; + } + } while(ecrypt_vals[++i].bits != 0); + + /* return the highest found so far */ + return ecrypt_vals[i-1].bits; +} + +/* returns the security level of the key in bits. Private/Public keys + * are mapped to symmetric key bits using the ECRYPT II 2010 recommendation. + */ +int _ncr_key_get_sec_level(struct key_item_st* item) +{ +int bits; + + /* FIXME: should we move everything here into algorithm properties? + */ + if (item->type == NCR_KEY_TYPE_SECRET) { + if (item->algorithm->algo == NCR_ALG_3DES_CBC) + return 112; + + return item->key.secret.size*8; + } else if (item->type == NCR_KEY_TYPE_PRIVATE) { + switch(item->algorithm->algo) { + case NCR_ALG_RSA: + bits = ncr_pk_get_rsa_size(&item->key.pk.rsa); + if (bits < 0) { + err(); + return bits; + } + + return rsa_to_bits(bits); + case NCR_ALG_DSA: + bits = ncr_pk_get_dsa_size(&item->key.pk.dsa); + if (bits < 0) { + err(); + return bits; + } + + return dlog_to_bits(bits); + case NCR_ALG_DH: + bits = ncr_pk_get_dh_size(&item->key.pk.dh); + if (bits < 0) { + err(); + return bits; + } + + return dlog_to_bits(bits); + default: + return -EINVAL; + } + } else { + return -EINVAL; } +} - ret = ncr_key_item_get_read(&item, lst, info.key); +int ncr_key_get_info(struct ncr_lists *lst, struct ncr_out *out, + const struct ncr_key_get_info *info, struct nlattr *tb[]) +{ +const struct nlattr *nla; +const u16 *attr, *attr_end; +struct key_item_st* item = NULL; +int ret; + + ret = ncr_key_item_get_read(&item, lst, info->key); if (ret < 0) { err(); return ret; @@ -509,11 +616,41 @@ int ret; goto fail; } - info.flags = item->flags; - info.type = item->type; - info.algorithm = item->algorithm->algo; - - ret = 0; + nla = tb[NCR_ATTR_WANTED_ATTRS]; + if (nla == NULL || nla_len(nla) % sizeof(u16) != 0) { + err(); + ret = -EINVAL; + goto fail; + } + attr = nla_data(nla); + attr_end = attr + nla_len(nla) / sizeof(u16); + while (attr < attr_end) { + switch (*attr) { + case NCR_ATTR_KEY_FLAGS: + ret = ncr_out_put_u32(out, *attr, item->flags); + break; + case NCR_ATTR_KEY_TYPE: + ret = ncr_out_put_u32(out, *attr, item->type); + break; + case NCR_ATTR_ALGORITHM: + ret = ncr_out_put_string(out, *attr, + item->algorithm->kstr); + break; + default: + break; /* Silently ignore */ + } + if (ret != 0) { + err(); + goto fail; + } + attr++; + } + + ret = ncr_out_finish(out); + if (ret != 0) { + err(); + goto fail; + } fail: _ncr_key_item_put( item); @@ -521,25 +658,21 @@ fail: return ret; } -int ncr_key_generate_pair(struct ncr_lists *lst, void __user* arg) +int ncr_key_generate_pair(struct ncr_lists *lst, + const struct ncr_key_generate_pair *gen, + struct nlattr *tb[]) { -struct ncr_key_generate_st gen; struct key_item_st* private = NULL; struct key_item_st* public = NULL; int ret; - if (unlikely(copy_from_user(&gen, arg, sizeof(gen)))) { - err(); - return -EFAULT; - } - - ret = ncr_key_item_get_write( &private, lst, gen.desc); + ret = ncr_key_item_get_write(&private, lst, gen->private_key); if (ret < 0) { err(); goto fail; } - ret = ncr_key_item_get_write( &public, lst, gen.desc2); + ret = ncr_key_item_get_write(&public, lst, gen->public_key); if (ret < 0) { err(); goto fail; @@ -549,8 +682,8 @@ int ret; ncr_key_clear(private); /* we generate only secret keys */ - private->flags = public->flags = gen.params.keyflags; - private->algorithm = public->algorithm = _ncr_algo_to_properties(gen.params.algorithm); + private->algorithm = public->algorithm + = _ncr_nla_to_properties(tb[NCR_ATTR_ALGORITHM]); if (private->algorithm == NULL) { err(); ret = -EINVAL; @@ -558,10 +691,21 @@ int ret; } public->type = public->algorithm->key_type; private->type = NCR_KEY_TYPE_PRIVATE; + ret = ncr_key_update_flags(private, tb[NCR_ATTR_KEY_FLAGS]); + if (ret < 0) { + err(); + goto fail; + } + ret = ncr_key_update_flags(public, tb[NCR_ATTR_KEY_FLAGS]); + if (ret < 0) { + err(); + goto fail; + } + public->flags |= (NCR_KEY_FLAG_EXPORTABLE|NCR_KEY_FLAG_WRAPPABLE); if (public->type == NCR_KEY_TYPE_PUBLIC) { - ret = ncr_pk_generate(public->algorithm, &gen.params, private, public); + ret = ncr_pk_generate(public->algorithm, tb, private, public); if (ret < 0) { err(); goto fail; @@ -585,28 +729,28 @@ fail: return ret; } -/* "exports" a key to a data item. If the key is not exportable - * to userspace then the data item will also not be. - */ -int ncr_key_derive(struct ncr_lists *lst, void __user* arg) +int ncr_key_derive(struct ncr_lists *lst, const struct ncr_key_derive *data, + struct nlattr *tb[]) { -struct ncr_key_derivation_params_st data; int ret; struct key_item_st* key = NULL; struct key_item_st* newkey = NULL; - if (unlikely(copy_from_user(&data, arg, sizeof(data)))) { - err(); - return -EFAULT; - } - - ret = ncr_key_item_get_read( &key, lst, data.key); + ret = ncr_key_item_get_read(&key, lst, data->input_key); if (ret < 0) { err(); return ret; } + + /* wrapping keys cannot be used for anything except wrapping. + */ + if (key->flags & NCR_KEY_FLAG_WRAPPING || key->flags & NCR_KEY_FLAG_UNWRAPPING) { + err(); + ret = -EINVAL; + goto fail; + } - ret = ncr_key_item_get_write( &newkey, lst, data.newkey); + ret = ncr_key_item_get_write(&newkey, lst, data->new_key); if (ret < 0) { err(); goto fail; @@ -614,12 +758,16 @@ struct key_item_st* newkey = NULL; ncr_key_clear(newkey); - newkey->flags = data.keyflags; + ret = ncr_key_update_flags(newkey, tb[NCR_ATTR_KEY_FLAGS]); + if (ret < 0) { + err(); + goto fail; + } switch (key->type) { case NCR_KEY_TYPE_PUBLIC: case NCR_KEY_TYPE_PRIVATE: - ret = ncr_pk_derive(newkey, key, &data); + ret = ncr_pk_derive(newkey, key, tb); if (ret < 0) { err(); goto fail; @@ -631,12 +779,6 @@ struct key_item_st* newkey = NULL; goto fail; } - if (unlikely(copy_to_user(arg, &data, sizeof(data)))) { - err(); - ret = -EFAULT; - } else - ret = 0; - fail: if (key) _ncr_key_item_put(key); diff --git a/ncr-limits.c b/ncr-limits.c index 34acac708d0..0c12824a211 100644 --- a/ncr-limits.c +++ b/ncr-limits.c @@ -29,7 +29,6 @@ #include <linux/slab.h> #include <linux/highmem.h> #include <linux/random.h> -#include "cryptodev.h" #include <asm/atomic.h> #include <linux/version.h> #include <linux/file.h> @@ -27,8 +27,8 @@ #include <linux/slab.h> #include <linux/random.h> #include <linux/uaccess.h> -#include "cryptodev.h" #include <linux/scatterlist.h> +#include <net/netlink.h> #include "ncr.h" #include "ncr-int.h" #include <tomcrypt.h> @@ -37,7 +37,7 @@ int _ncr_tomerr(int err) { switch (err) { case CRYPT_BUFFER_OVERFLOW: - return -EOVERFLOW; + return -ERANGE; case CRYPT_MEM: return -ENOMEM; default: @@ -123,7 +123,8 @@ static int ncr_pk_make_public_and_id( struct key_item_st * private, struct key_i } key_id_size = MAX_KEY_ID_SIZE; - cret = hash_memory(_ncr_algo_to_properties(NCR_ALG_SHA1), tmp, max_size, private->key_id, &key_id_size); + cret = hash_memory(_ncr_algo_to_properties("sha1"), tmp, max_size, + private->key_id, &key_id_size); if (cret != CRYPT_OK) { err(); ret = _ncr_tomerr(cret); @@ -167,8 +168,9 @@ int ncr_pk_pack( const struct key_item_st * key, uint8_t * packed, uint32_t * pa } break; case NCR_ALG_DH: - ret = dh_export(packed, &max_size, key->key.pk.dsa.type, (void*)&key->key.pk.dsa); + ret = dh_export(packed, &max_size, key->key.pk.dh.type, (void*)&key->key.pk.dh); if (ret < 0) { + *packed_size = max_size; err(); return ret; } @@ -222,75 +224,88 @@ int ncr_pk_unpack( struct key_item_st * key, const void * packed, size_t packed_ return 0; } -struct keygen_st { -}; +static int binary_to_ulong(unsigned long *dest, const struct nlattr *nla) +{ + unsigned long value; + const uint8_t *start, *end, *p; + + value = 0; + start = nla_data(nla); + end = start + nla_len(nla); + for (p = start; p < end; p++) { + if (value > (ULONG_MAX - *p) / 256) + return -EOVERFLOW; + value = value * 256 + *p; + } + *dest = value; + return 0; +} -int ncr_pk_generate(const struct algo_properties_st *algo, - struct ncr_key_generate_params_st * params, +int ncr_pk_generate(const struct algo_properties_st *algo, struct nlattr *tb[], struct key_item_st* private, struct key_item_st* public) { + const struct nlattr *nla; unsigned long e; int cret, ret; - uint8_t * tmp = NULL; private->algorithm = public->algorithm = algo; ret = 0; switch(algo->algo) { case NCR_ALG_RSA: - e = params->params.rsa.e; - - if (e == 0) + nla = tb[NCR_ATTR_RSA_E]; + if (nla != NULL) { + ret = binary_to_ulong(&e, nla); + if (ret != 0) + break; + } else e = 65537; - cret = rsa_make_key(params->params.rsa.bits/8, e, &private->key.pk.rsa); + + nla = tb[NCR_ATTR_RSA_MODULUS_BITS]; + if (nla == NULL) { + ret = -EINVAL; + break; + } + cret = rsa_make_key(nla_get_u32(nla) / 8, e, &private->key.pk.rsa); if (cret != CRYPT_OK) { err(); return _ncr_tomerr(cret); } break; - case NCR_ALG_DSA: - if (params->params.dsa.q_bits==0) - params->params.dsa.q_bits = 160; - if (params->params.dsa.p_bits==0) - params->params.dsa.p_bits = 1024; + case NCR_ALG_DSA: { + u32 q_bits, p_bits; - cret = dsa_make_key(params->params.dsa.q_bits/8, - params->params.dsa.p_bits/8, &private->key.pk.dsa); + nla = tb[NCR_ATTR_DSA_Q_BITS]; + if (nla != NULL) + q_bits = nla_get_u32(nla); + else + q_bits = 160; + nla = tb[NCR_ATTR_DSA_P_BITS]; + if (nla != NULL) + p_bits = nla_get_u32(nla); + else + p_bits = 1024; + cret = dsa_make_key(q_bits / 8, p_bits / 8, + &private->key.pk.dsa); if (cret != CRYPT_OK) { err(); return _ncr_tomerr(cret); } break; + } case NCR_ALG_DH: { - uint8_t * p, *g; - size_t p_size, g_size; - - p_size = params->params.dh.p_size; - g_size = params->params.dh.g_size; - - tmp = kmalloc(g_size+p_size, GFP_KERNEL); - if (tmp == NULL) { - err(); - ret = -ENOMEM; - goto fail; - } - - p = tmp; - g = &tmp[p_size]; - - if (unlikely(copy_from_user(p, params->params.dh.p, p_size))) { - err(); - ret = -EFAULT; - goto fail; - } + const struct nlattr *p, *g; - if (unlikely(copy_from_user(g, params->params.dh.g, g_size))) { - err(); - ret = -EFAULT; + p = tb[NCR_ATTR_DH_PRIME]; + g = tb[NCR_ATTR_DH_BASE]; + if (p == NULL || g == NULL) { + ret = -EINVAL; goto fail; } - - ret = dh_import_params(&private->key.pk.dh, p, p_size, g, g_size); + + ret = dh_import_params(&private->key.pk.dh, nla_data(p), + nla_len(p), nla_data(g), + nla_len(g)); if (ret < 0) { err(); goto fail; @@ -309,8 +324,6 @@ int ncr_pk_generate(const struct algo_properties_st *algo, } fail: - kfree(tmp); - if (ret < 0) { err(); return ret; @@ -325,25 +338,6 @@ fail: return 0; } -const struct algo_properties_st *ncr_key_params_get_sign_hash( - const struct algo_properties_st *algo, - struct ncr_key_params_st * params) -{ - ncr_algorithm_t id; - - switch(algo->algo) { - case NCR_ALG_RSA: - id = params->params.rsa.sign_hash; - break; - case NCR_ALG_DSA: - id = params->params.dsa.sign_hash; - break; - default: - return ERR_PTR(-EINVAL); - } - return _ncr_algo_to_properties(id); -} - /* Encryption/Decryption */ @@ -356,12 +350,17 @@ void ncr_pk_cipher_deinit(struct ncr_pk_ctx* ctx) } int ncr_pk_cipher_init(const struct algo_properties_st *algo, - struct ncr_pk_ctx* ctx, struct ncr_key_params_st* params, - struct key_item_st *key, const struct algo_properties_st *sign_hash) + struct ncr_pk_ctx* ctx, struct nlattr *tb[], + struct key_item_st *key, + const struct algo_properties_st *sign_hash) { + const struct nlattr *nla; + memset(ctx, 0, sizeof(*ctx)); - if (key->algorithm != algo) { + /* Allow using the same key for transparent and non-transparent + hashing. */ + if (key->algorithm->algo != algo->algo) { err(); return -EINVAL; } @@ -369,26 +368,38 @@ int ncr_pk_cipher_init(const struct algo_properties_st *algo, ctx->algorithm = algo; ctx->key = key; ctx->sign_hash = sign_hash; + ctx->salt_len = 0; switch(algo->algo) { case NCR_ALG_RSA: - if (params->params.rsa.type == RSA_PKCS1_V1_5) + nla = tb[NCR_ATTR_RSA_ENCODING_METHOD]; + if (nla == NULL) { + err(); + return -EINVAL; + } + switch (nla_get_u32(nla)) { + case RSA_PKCS1_V1_5: ctx->type = LTC_LTC_PKCS_1_V1_5; - else if (params->params.rsa.type == RSA_PKCS1_OAEP) { + break; + case RSA_PKCS1_OAEP: ctx->type = LTC_LTC_PKCS_1_OAEP; - ctx->oaep_hash = _ncr_algo_to_properties(params->params.rsa.oaep_hash); + nla = tb[NCR_ATTR_RSA_OAEP_HASH_ALGORITHM]; + ctx->oaep_hash = _ncr_nla_to_properties(nla); if (ctx->oaep_hash == NULL) { err(); return -EINVAL; } - } else if (params->params.rsa.type == RSA_PKCS1_PSS) { + break; + case RSA_PKCS1_PSS: ctx->type = LTC_LTC_PKCS_1_PSS; - } else { + nla = tb[NCR_ATTR_RSA_PSS_SALT_LENGTH]; + if (nla != NULL) + ctx->salt_len = nla_get_u32(nla); + break; + default: err(); return -EINVAL; } - - ctx->salt_len = params->params.rsa.pss_salt; break; case NCR_ALG_DSA: break; @@ -531,30 +542,11 @@ fail: return ret; } -int ncr_pk_cipher_sign(const struct ncr_pk_ctx* ctx, - const struct scatterlist* isg, unsigned int isg_cnt, size_t isg_size, - struct scatterlist *osg, unsigned int osg_cnt, size_t* osg_size) +int ncr_pk_cipher_sign(const struct ncr_pk_ctx *ctx, const void *hash, + size_t hash_size, void *sig, size_t *sig_size) { -int cret, ret; -unsigned long osize = *osg_size; -uint8_t* tmp; -void * input, *output; - - tmp = kmalloc(isg_size + *osg_size, GFP_KERNEL); - if (tmp == NULL) { - err(); - return -ENOMEM; - } - - input = tmp; - output = &tmp[isg_size]; - - ret = sg_copy_to_buffer((struct scatterlist*)isg, isg_cnt, input, isg_size); - if (ret != isg_size) { - err(); - ret = -EINVAL; - goto fail; - } + int cret; + unsigned long osize = *sig_size; switch(ctx->algorithm->algo) { case NCR_ALG_RSA: @@ -562,63 +554,35 @@ void * input, *output; err(); return -EINVAL; } - cret = rsa_sign_hash_ex( input, isg_size, output, &osize, + cret = rsa_sign_hash_ex(hash, hash_size, sig, &osize, ctx->type, ctx->sign_hash, ctx->salt_len, &ctx->key->key.pk.rsa); if (cret != CRYPT_OK) { err(); return _ncr_tomerr(cret); } - *osg_size = osize; + *sig_size = osize; break; case NCR_ALG_DSA: - cret = dsa_sign_hash( input, isg_size, output, &osize, + cret = dsa_sign_hash(hash, hash_size, sig, &osize, &ctx->key->key.pk.dsa); if (cret != CRYPT_OK) { err(); return _ncr_tomerr(cret); } - *osg_size = osize; + *sig_size = osize; break; default: err(); - ret = -EINVAL; - goto fail; - } - - ret = sg_copy_from_buffer(osg, osg_cnt, output, *osg_size); - if (ret != *osg_size) { - err(); - ret = -EINVAL; - goto fail; + return -EINVAL; } - ret = 0; -fail: - kfree(tmp); - - return ret; + return 0; } -int ncr_pk_cipher_verify(const struct ncr_pk_ctx* ctx, - const struct scatterlist* sign_sg, unsigned int sign_sg_cnt, size_t sign_sg_size, - const void* hash, size_t hash_size, ncr_error_t* err) +int ncr_pk_cipher_verify(const struct ncr_pk_ctx* ctx, const void *sig, + size_t sig_size, const void *hash, size_t hash_size) { -int cret, ret; -int stat = 0; -uint8_t* sig; - - sig = kmalloc(sign_sg_size, GFP_KERNEL); - if (sig == NULL) { - err(); - return -ENOMEM; - } - - ret = sg_copy_to_buffer((struct scatterlist*)sign_sg, sign_sg_cnt, sig, sign_sg_size); - if (ret != sign_sg_size) { - err(); - ret = -EINVAL; - goto fail; - } + int cret, ret, stat; switch(ctx->algorithm->algo) { case NCR_ALG_RSA: @@ -626,35 +590,28 @@ uint8_t* sig; err(); return -EINVAL; } - cret = rsa_verify_hash_ex( sig, sign_sg_size, - hash, hash_size, ctx->type, ctx->sign_hash, - ctx->salt_len, &stat, &ctx->key->key.pk.rsa); + cret = rsa_verify_hash_ex(sig, sig_size, hash, + hash_size, ctx->type, + ctx->sign_hash, ctx->salt_len, + &stat, &ctx->key->key.pk.rsa); if (cret != CRYPT_OK) { err(); ret = _ncr_tomerr(cret); goto fail; } - if (stat == 1) - *err = 0; - else - *err = NCR_VERIFICATION_FAILED; - + ret = (stat == 1); break; case NCR_ALG_DSA: - cret = dsa_verify_hash( sig, sign_sg_size, - hash, hash_size, &stat, &ctx->key->key.pk.dsa); + cret = dsa_verify_hash(sig, sig_size, hash, hash_size, + &stat, &ctx->key->key.pk.dsa); if (cret != CRYPT_OK) { err(); ret = _ncr_tomerr(cret); goto fail; } - if (stat == 1) - *err = 0; - else - *err = NCR_VERIFICATION_FAILED; - + ret = (stat == 1); break; default: err(); @@ -662,55 +619,67 @@ uint8_t* sig; goto fail; } - ret = 0; fail: - kfree(sig); return ret; } int ncr_pk_derive(struct key_item_st* newkey, struct key_item_st* oldkey, - struct ncr_key_derivation_params_st * params) + struct nlattr *tb[]) { +const struct nlattr *nla; int ret; -void* tmp = NULL; -size_t size; - switch(params->derive) { - case NCR_DERIVE_DH: - if (oldkey->type != NCR_KEY_TYPE_PRIVATE && - oldkey->algorithm->algo != NCR_ALG_DH) { - err(); - return -EINVAL; - } - - size = params->params.params.dh.pub_size; - tmp = kmalloc(size, GFP_KERNEL); - if (tmp == NULL) { - err(); - return -ENOMEM; - } - - if (unlikely(copy_from_user(tmp, params->params.params.dh.pub, - size))) { - err(); - ret = -EFAULT; - goto fail; - } - - ret = dh_derive_gxy(newkey, &oldkey->key.pk.dh, tmp, size); - if (ret < 0) { - err(); - goto fail; - } - - break; - default: + nla = tb[NCR_ATTR_DERIVATION_ALGORITHM]; + if (nla == NULL) { + err(); + return -EINVAL; + } + if (nla_strcmp(nla, NCR_DERIVE_DH) == 0) { + if (oldkey->type != NCR_KEY_TYPE_PRIVATE && + oldkey->algorithm->algo != NCR_ALG_DH) { + err(); + return -EINVAL; + } + + nla = tb[NCR_ATTR_DH_PUBLIC]; + if (nla == NULL) { err(); return -EINVAL; + } + ret = dh_derive_gxy(newkey, &oldkey->key.pk.dh, nla_data(nla), + nla_len(nla)); + if (ret < 0) { + err(); + return ret; + } + } else { + err(); + return -EINVAL; } - ret = 0; -fail: - kfree(tmp); + return 0; +} + +int ncr_pk_get_rsa_size( rsa_key* key) +{ +int ret; + ret = mp_count_bits(&key->N); + if (ret <= 0) { + err(); + return -EINVAL; + } + + return ret; +} + +int ncr_pk_get_dsa_size( dsa_key* key) +{ +int ret; + ret = mp_count_bits(&key->p); + if (ret <= 0) { + err(); + return -EINVAL; + } + return ret; } @@ -3,6 +3,8 @@ #include <tomcrypt.h> +struct nlattr; + struct ncr_pk_ctx { const struct algo_properties_st *algorithm; /* algorithm */ @@ -19,16 +21,16 @@ struct ncr_pk_ctx { /* PK */ void ncr_pk_clear(struct key_item_st* key); -int ncr_pk_generate(const struct algo_properties_st *algo, - struct ncr_key_generate_params_st * params, +int ncr_pk_generate(const struct algo_properties_st *algo, struct nlattr *tb[], struct key_item_st* private, struct key_item_st* public); int ncr_pk_pack( const struct key_item_st * key, uint8_t * packed, uint32_t * packed_size); int ncr_pk_unpack( struct key_item_st * key, const void * packed, size_t packed_size); /* encryption/decryption */ int ncr_pk_cipher_init(const struct algo_properties_st *algo, - struct ncr_pk_ctx* ctx, struct ncr_key_params_st* params, - struct key_item_st *key, const struct algo_properties_st *sign_hash); + struct ncr_pk_ctx* ctx, struct nlattr *tb[], + struct key_item_st *key, + const struct algo_properties_st *sign_hash); void ncr_pk_cipher_deinit(struct ncr_pk_ctx* ctx); int ncr_pk_cipher_encrypt(const struct ncr_pk_ctx* ctx, @@ -39,17 +41,19 @@ int ncr_pk_cipher_decrypt(const struct ncr_pk_ctx* ctx, const struct scatterlist* isg, unsigned int isg_cnt, size_t isg_size, struct scatterlist *osg, unsigned int osg_cnt, size_t* osg_size); -int ncr_pk_cipher_sign(const struct ncr_pk_ctx* ctx, - const struct scatterlist* isg, unsigned int isg_cnt, size_t isg_size, - struct scatterlist *osg, unsigned int osg_cnt, size_t* osg_size); +int ncr_pk_cipher_sign(const struct ncr_pk_ctx *ctx, const void *hash, + size_t hash_size, void *sig, size_t *sig_size); -int ncr_pk_cipher_verify(const struct ncr_pk_ctx* ctx, - const struct scatterlist* sign_sg, unsigned int sign_sg_cnt, size_t sign_sg_size, - const void* hash, size_t hash_size, ncr_error_t* err); +int ncr_pk_cipher_verify(const struct ncr_pk_ctx* ctx, const void *sig, + size_t sig_size, const void *hash, size_t hash_size); int _ncr_tomerr(int err); int ncr_pk_derive(struct key_item_st* newkey, struct key_item_st* oldkey, - struct ncr_key_derivation_params_st * params); + struct nlattr *tb[]); + +int ncr_pk_get_rsa_size( rsa_key* key); +int ncr_pk_get_dsa_size( dsa_key* key); + #endif diff --git a/ncr-sessions.c b/ncr-sessions.c index 2916729b91a..c65db2f751e 100644 --- a/ncr-sessions.c +++ b/ncr-sessions.c @@ -25,14 +25,46 @@ #include <linux/crypto.h> #include <linux/mutex.h> -#include "cryptodev.h" #include "ncr.h" #include "ncr-int.h" #include <linux/mm_types.h> #include <linux/scatterlist.h> +#include <net/netlink.h> + +struct session_item_st { + atomic_t refcnt; + /* Constant values throughout the life of this object follow. */ + ncr_session_t desc; + const struct algo_properties_st *algorithm; + ncr_crypto_op_t op; + struct key_item_st *key; + + /* Variable values, protected usually by mutex, follow. */ + struct mutex mutex; + + /* contexts for various options. + * simpler to have them like that than + * in a union. + */ + struct cipher_data cipher; + struct ncr_pk_ctx pk; + struct hash_data hash; + /* This is a hack, ideally we'd have a hash algorithm that simply + outputs its input as a digest. We'd still need to distinguish + between the hash to identify in the signature and the hash to + actually use, though. */ + void *transparent_hash; -static int _ncr_session_update_key(struct ncr_lists* lists, struct ncr_session_op_st* op); -static void _ncr_session_remove(struct ncr_lists *lst, ncr_session_t desc); + struct scatterlist *sg; + struct page **pages; + unsigned array_size; + unsigned available_pages; +}; + +static void _ncr_sessions_item_put(struct session_item_st *item); +static int _ncr_session_update_key(struct ncr_lists *lists, + struct session_item_st *sess, + struct nlattr *tb[]); static int session_list_deinit_fn(int id, void *item, void *unused) { @@ -52,12 +84,57 @@ void ncr_sessions_list_deinit(struct ncr_lists *lst) mutex_unlock(&lst->session_idr_mutex); } +/* Allocate a descriptor without making a sesssion available to userspace. */ +static ncr_session_t session_alloc_desc(struct ncr_lists *lst) +{ + int ret, desc; + + mutex_lock(&lst->session_idr_mutex); + if (idr_pre_get(&lst->session_idr, GFP_KERNEL) == 0) { + ret = -ENOMEM; + goto end; + } + /* idr_pre_get() should preallocate enough, and, due to + session_idr_mutex, nobody else can use the preallocated data. + Therefore the loop recommended in idr_get_new() documentation is not + necessary. */ + ret = idr_get_new(&lst->session_idr, NULL, &desc); + if (ret != 0) + goto end; + ret = desc; +end: + mutex_unlock(&lst->session_idr_mutex); + return ret; +} + +/* Drop a pre-allocated, unpublished session descriptor */ +static void session_drop_desc(struct ncr_lists *lst, ncr_session_t desc) +{ + mutex_lock(&lst->session_idr_mutex); + idr_remove(&lst->session_idr, desc); + mutex_unlock(&lst->session_idr_mutex); +} + +/* Make a session descriptor visible in user-space, stealing the reference */ +static void session_publish_ref(struct ncr_lists *lst, + struct session_item_st *sess) +{ + void *old; + + mutex_lock(&lst->session_idr_mutex); + old = idr_replace(&lst->session_idr, sess, sess->desc); + mutex_unlock(&lst->session_idr_mutex); + BUG_ON(old != NULL); +} + /* returns the data item corresponding to desc */ -struct session_item_st* ncr_sessions_item_get(struct ncr_lists *lst, ncr_session_t desc) +static struct session_item_st *session_get_ref(struct ncr_lists *lst, + ncr_session_t desc) { struct session_item_st* item; mutex_lock(&lst->session_idr_mutex); + /* item may be NULL for pre-allocated session IDs. */ item = idr_find(&lst->session_idr, desc); if (item != NULL) { atomic_inc(&item->refcnt); @@ -70,12 +147,30 @@ struct session_item_st* item; return NULL; } -void _ncr_sessions_item_put( struct session_item_st* item) +/* Find a session, stealing the reference, but keep the descriptor allocated. */ +static struct session_item_st *session_unpublish_ref(struct ncr_lists *lst, + ncr_session_t desc) +{ + struct session_item_st *sess; + + mutex_lock(&lst->session_idr_mutex); + /* sess may be NULL for pre-allocated session IDs. */ + sess = idr_replace(&lst->session_idr, NULL, desc); + mutex_unlock(&lst->session_idr_mutex); + if (sess != NULL && !IS_ERR(sess)) + return sess; + + err(); + return NULL; +} + +static void _ncr_sessions_item_put(struct session_item_st *item) { if (atomic_dec_and_test(&item->refcnt)) { cryptodev_cipher_deinit(&item->cipher); ncr_pk_cipher_deinit(&item->pk); cryptodev_hash_deinit(&item->hash); + kfree(item->transparent_hash); if (item->key) _ncr_key_item_put(item->key); kfree(item->sg); @@ -84,7 +179,7 @@ void _ncr_sessions_item_put( struct session_item_st* item) } } -struct session_item_st* ncr_session_new(struct ncr_lists *lst) +static struct session_item_st *ncr_session_new(ncr_session_t desc) { struct session_item_st* sess; @@ -103,21 +198,10 @@ struct session_item_st* ncr_session_new(struct ncr_lists *lst) err(); goto err_sess; } - mutex_init(&sess->mem_mutex); - - atomic_set(&sess->refcnt, 2); /* One for lst->list, one for "sess" */ + mutex_init(&sess->mutex); - mutex_lock(&lst->session_idr_mutex); - /* idr_pre_get() should preallocate enough, and, due to - session_idr_mutex, nobody else can use the preallocated data. - Therefore the loop recommended in idr_get_new() documentation is not - necessary. */ - if (idr_pre_get(&lst->session_idr, GFP_KERNEL) == 0 || - idr_get_new(&lst->session_idr, sess, &sess->desc) != 0) { - mutex_unlock(&lst->session_idr_mutex); - goto err_sess; - } - mutex_unlock(&lst->session_idr_mutex); + atomic_set(&sess->refcnt, 1); + sess->desc = desc; return sess; @@ -129,113 +213,210 @@ err_sess: } static const struct algo_properties_st algo_properties[] = { - { .algo = NCR_ALG_NULL, .kstr = "ecb(cipher_null)", +#define KSTR(x) .kstr = x, .kstr_len = sizeof(x) - 1 + { .algo = NCR_ALG_NULL, KSTR("ecb(cipher_null)"), .needs_iv = 0, .is_symmetric=1, .can_encrypt=1, .key_type = NCR_KEY_TYPE_INVALID }, - { .algo = NCR_ALG_3DES_CBC, .kstr = "cbc(des3_ede)", + { .algo = NCR_ALG_3DES_CBC, KSTR("cbc(des3_ede)"), .needs_iv = 1, .is_symmetric=1, .can_encrypt=1, .key_type = NCR_KEY_TYPE_SECRET }, - { .algo = NCR_ALG_AES_CBC, .kstr = "cbc(aes)", + { KSTR("cbc(aes)"), .needs_iv = 1, .is_symmetric=1, .can_encrypt=1, .key_type = NCR_KEY_TYPE_SECRET }, - { .algo = NCR_ALG_CAMELLIA_CBC, .kstr = "cbc(camelia)", + { KSTR("cbc(camelia)"), .needs_iv = 1, .is_symmetric=1, .can_encrypt=1, .key_type = NCR_KEY_TYPE_SECRET }, - { .algo = NCR_ALG_AES_CTR, .kstr = "ctr(aes)", + { KSTR("ctr(aes)"), .needs_iv = 1, .is_symmetric=1, .can_encrypt=1, .key_type = NCR_KEY_TYPE_SECRET }, - { .algo = NCR_ALG_CAMELLIA_CTR, .kstr = "ctr(camelia)", + { KSTR("ctr(camelia)"), .needs_iv = 1, .is_symmetric=1, .can_encrypt=1, .key_type = NCR_KEY_TYPE_SECRET }, - { .algo = NCR_ALG_ARCFOUR, .kstr = NULL, + { KSTR("ecb(aes)"), .needs_iv = 0, .is_symmetric=1, .can_encrypt=1, .key_type = NCR_KEY_TYPE_SECRET }, - { .algo = NCR_ALG_AES_ECB, .kstr = "ecb(aes)", + { KSTR("ecb(camelia)"), .needs_iv = 0, .is_symmetric=1, .can_encrypt=1, .key_type = NCR_KEY_TYPE_SECRET }, - { .algo = NCR_ALG_CAMELLIA_ECB, .kstr = "ecb(camelia)", - .needs_iv = 0, .is_symmetric=1, .can_encrypt=1, - .key_type = NCR_KEY_TYPE_SECRET }, - { .algo = NCR_ALG_SHA1, .kstr = "sha1", + { .algo = NCR_ALG_SHA1, KSTR("sha1"), .digest_size = 20, .can_digest=1, .key_type = NCR_KEY_TYPE_INVALID }, - { .algo = NCR_ALG_MD5, .kstr = "md5", + { .algo = NCR_ALG_MD5, KSTR("md5"), .digest_size = 16, .can_digest=1, .key_type = NCR_KEY_TYPE_INVALID }, - { .algo = NCR_ALG_SHA2_224, .kstr = "sha224", + { .algo = NCR_ALG_SHA2_224, KSTR("sha224"), .digest_size = 28, .can_digest=1, .key_type = NCR_KEY_TYPE_INVALID }, - { .algo = NCR_ALG_SHA2_256, .kstr = "sha256", + { .algo = NCR_ALG_SHA2_256, KSTR("sha256"), .digest_size = 32, .can_digest=1, .key_type = NCR_KEY_TYPE_INVALID }, - { .algo = NCR_ALG_SHA2_384, .kstr = "sha384", + { .algo = NCR_ALG_SHA2_384, KSTR("sha384"), .digest_size = 48, .can_digest=1, .key_type = NCR_KEY_TYPE_INVALID }, - { .algo = NCR_ALG_SHA2_512, .kstr = "sha512", + { .algo = NCR_ALG_SHA2_512, KSTR("sha512"), .digest_size = 64, .can_digest=1, .key_type = NCR_KEY_TYPE_INVALID }, - { .algo = NCR_ALG_HMAC_SHA1, .is_hmac = 1, .kstr = "hmac(sha1)", + { .is_hmac = 1, KSTR("hmac(sha1)"), .digest_size = 20, .can_sign=1, .key_type = NCR_KEY_TYPE_SECRET }, - { .algo = NCR_ALG_HMAC_MD5, .is_hmac = 1, .kstr = "hmac(md5)", + { .is_hmac = 1, KSTR("hmac(md5)"), .digest_size = 16, .can_sign=1, .key_type = NCR_KEY_TYPE_SECRET }, - { .algo = NCR_ALG_HMAC_SHA2_224, .is_hmac = 1, .kstr = "hmac(sha224)", + { .is_hmac = 1, KSTR("hmac(sha224)"), .digest_size = 28, .can_sign=1, .key_type = NCR_KEY_TYPE_SECRET }, - { .algo = NCR_ALG_HMAC_SHA2_256, .is_hmac = 1, .kstr = "hmac(sha256)", + { .is_hmac = 1, KSTR("hmac(sha256)"), .digest_size = 32, .can_sign=1, .key_type = NCR_KEY_TYPE_SECRET }, - { .algo = NCR_ALG_HMAC_SHA2_384, .is_hmac = 1, .kstr = "hmac(sha384)", + { .is_hmac = 1, KSTR("hmac(sha384)"), .digest_size = 48, .can_sign=1, .key_type = NCR_KEY_TYPE_SECRET }, - { .algo = NCR_ALG_HMAC_SHA2_512, .is_hmac = 1, .kstr = "hmac(sha512)", + { .is_hmac = 1, KSTR("hmac(sha512)"), .digest_size = 64, .can_sign=1, .key_type = NCR_KEY_TYPE_SECRET }, - { .algo = NCR_ALG_RSA, .kstr = NULL, .is_pk = 1, + /* NOTE: These algorithm names are not available through the kernel API + (yet). */ + { .algo = NCR_ALG_RSA, KSTR("rsa"), .is_pk = 1, .can_encrypt=1, .can_sign=1, .key_type = NCR_KEY_TYPE_PUBLIC }, - { .algo = NCR_ALG_DSA, .kstr = NULL, .is_pk = 1, + { .algo = NCR_ALG_RSA, KSTR(NCR_ALG_RSA_TRANSPARENT_HASH), .is_pk = 1, + .can_encrypt=1, .can_sign=1, .has_transparent_hash = 1, + .key_type = NCR_KEY_TYPE_PUBLIC }, + { .algo = NCR_ALG_DSA, KSTR("dsa"), .is_pk = 1, .can_sign=1, .key_type = NCR_KEY_TYPE_PUBLIC }, - { .algo = NCR_ALG_DH, .kstr = NULL, .is_pk = 1, + { .algo = NCR_ALG_DSA, KSTR(NCR_ALG_DSA_TRANSPARENT_HASH), .is_pk = 1, + .can_sign=1, .has_transparent_hash = 1, + .key_type = NCR_KEY_TYPE_PUBLIC }, + { .algo = NCR_ALG_DH, KSTR("dh"), .is_pk = 1, .can_kx=1, .key_type = NCR_KEY_TYPE_PUBLIC }, - { .algo = NCR_ALG_NONE } - +#undef KSTR }; -const struct algo_properties_st *_ncr_algo_to_properties(ncr_algorithm_t algo) +/* The lookups by string are inefficient - can we look up all we need from + crypto API? */ +const struct algo_properties_st *_ncr_algo_to_properties(const char *algo) { - ncr_algorithm_t a; - int i = 0; - - for (i = 0; (a = algo_properties[i].algo) != NCR_ALG_NONE; i++) { - if (a == algo) - return &algo_properties[i]; + const struct algo_properties_st *a; + size_t name_len; + + name_len = strlen(algo); + for (a = algo_properties; + a < algo_properties + ARRAY_SIZE(algo_properties); a++) { + if (a->kstr_len == name_len + && memcmp(a->kstr, algo, name_len) == 0) + return a; } return NULL; } -static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* session) +const struct algo_properties_st *_ncr_nla_to_properties(const struct nlattr *nla) +{ + const struct algo_properties_st *a; + size_t name_len; + + if (nla == NULL) + return NULL; + + /* nla_len() >= 1 ensured by validate_nla() case NLA_NUL_STRING */ + name_len = nla_len(nla) - 1; + for (a = algo_properties; + a < algo_properties + ARRAY_SIZE(algo_properties); a++) { + if (a->kstr_len == name_len + && memcmp(a->kstr, nla_data(nla), name_len + 1) == 0) + return a; + } + return NULL; +} + +static int key_item_get_nla_read(struct key_item_st **st, + struct ncr_lists *lists, + const struct nlattr *nla) { - struct session_item_st* ns = NULL; int ret; - const struct algo_properties_st *sign_hash; - ns = ncr_session_new(lists); - if (ns == NULL) { + if (nla == NULL) { err(); - return -ENOMEM; + return -EINVAL; + } + ret = ncr_key_item_get_read(st, lists, nla_get_u32(nla)); + if (ret < 0) { + err(); + return ret; + } + return ret; +} + +/* The caller is responsible for locking of "session". old_session must not be + locked on entry. */ +static int +init_or_clone_hash(struct session_item_st *session, + struct session_item_st *old_session, + const struct algo_properties_st *algo, + const void *mac_key, size_t mac_key_size) +{ + int ret; + + if (old_session == NULL) + return cryptodev_hash_init(&session->hash, algo->kstr, + mac_key, mac_key_size); + + if (mutex_lock_interruptible(&old_session->mutex)) { + err(); + return -ERESTARTSYS; } + ret = cryptodev_hash_clone(&session->hash, &old_session->hash, mac_key, + mac_key_size); + mutex_unlock(&old_session->mutex); + + return ret; +} - ns->op = session->op; - ns->algorithm = _ncr_algo_to_properties(session->algorithm); - if (ns->algorithm == NULL) { +static struct session_item_st *_ncr_session_init(struct ncr_lists *lists, + ncr_session_t desc, + ncr_crypto_op_t op, + struct nlattr *tb[]) +{ + const struct nlattr *nla; + struct session_item_st *ns, *old_session = NULL; + int ret; + const struct algo_properties_st *sign_hash; + + ns = ncr_session_new(desc); + if (ns == NULL) { err(); - ret = -EINVAL; - goto fail; + return ERR_PTR(-ENOMEM); } + /* ns is the only reference throughout this function, so no locking + is necessary. */ + + ns->op = op; + nla = tb[NCR_ATTR_SESSION_CLONE_FROM]; + if (nla != NULL) { + /* "ns" is not visible to userspace, so this is safe. */ + old_session = session_get_ref(lists, nla_get_u32(nla)); + if (old_session == NULL) { + err(); + ret = -EINVAL; + goto fail; + } + if (ns->op != old_session->op) { + err(); + ret = -EINVAL; + goto fail; + } + } + + if (old_session == NULL) { + ns->algorithm = _ncr_nla_to_properties(tb[NCR_ATTR_ALGORITHM]); + if (ns->algorithm == NULL) { + err(); + ret = -EINVAL; + goto fail; + } + } else + ns->algorithm = old_session->algorithm; - switch(session->op) { + switch(op) { case NCR_OP_ENCRYPT: case NCR_OP_DECRYPT: if (!ns->algorithm->can_encrypt) { @@ -244,19 +425,35 @@ static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* ses goto fail; } + if (old_session != NULL) { + err(); + ret = -EOPNOTSUPP; + goto fail; + } + /* read key */ - ret = ncr_key_item_get_read( &ns->key, lists, session->key); + ret = key_item_get_nla_read(&ns->key, lists, + tb[NCR_ATTR_KEY]); if (ret < 0) { err(); goto fail; } + + /* wrapping keys cannot be used for encryption or decryption + */ + if (ns->key->flags & NCR_KEY_FLAG_WRAPPING || ns->key->flags & NCR_KEY_FLAG_UNWRAPPING) { + err(); + ret = -EINVAL; + goto fail; + } + if (ns->key->type == NCR_KEY_TYPE_SECRET) { int keysize = ns->key->key.secret.size; - if (session->algorithm == NCR_ALG_NULL) + if (ns->algorithm->algo == NCR_ALG_NULL) keysize = 0; - if (ns->algorithm->kstr == NULL) { + if (ns->algorithm->is_pk) { err(); ret = -EINVAL; goto fail; @@ -270,16 +467,19 @@ static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* ses } if (ns->algorithm->needs_iv) { - if (session->params.params.cipher.iv_size > sizeof(session->params.params.cipher.iv)) { + nla = tb[NCR_ATTR_IV]; + if (nla == NULL) { err(); ret = -EINVAL; goto fail; } - cryptodev_cipher_set_iv(&ns->cipher, session->params.params.cipher.iv, session->params.params.cipher.iv_size); + cryptodev_cipher_set_iv(&ns->cipher, + nla_data(nla), + nla_len(nla)); } } else if (ns->key->type == NCR_KEY_TYPE_PRIVATE || ns->key->type == NCR_KEY_TYPE_PUBLIC) { ret = ncr_pk_cipher_init(ns->algorithm, &ns->pk, - &session->params, ns->key, NULL); + tb, ns->key, NULL); if (ret < 0) { err(); goto fail; @@ -300,34 +500,51 @@ static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* ses } if (ns->algorithm->can_digest) { - if (ns->algorithm->kstr == NULL) { + if (ns->algorithm->is_pk) { err(); ret = -EINVAL; goto fail; } - ret = cryptodev_hash_init(&ns->hash, ns->algorithm->kstr, 0, NULL, 0); + ret = init_or_clone_hash(ns, old_session, + ns->algorithm, NULL, + 0); if (ret < 0) { err(); goto fail; } } else { - /* read key */ - ret = ncr_key_item_get_read( &ns->key, lists, session->key); - if (ret < 0) { + /* Get key */ + if (old_session == NULL) { + ret = key_item_get_nla_read(&ns->key, + lists, + tb[NCR_ATTR_KEY]); + if (ret < 0) { + err(); + goto fail; + } + } else { + atomic_inc(&old_session->key->refcnt); + ns->key = old_session->key; + } + + /* wrapping keys cannot be used for anything except wrapping. + */ + if (ns->key->flags & NCR_KEY_FLAG_WRAPPING || ns->key->flags & NCR_KEY_FLAG_UNWRAPPING) { err(); + ret = -EINVAL; goto fail; } if (ns->algorithm->is_hmac && ns->key->type == NCR_KEY_TYPE_SECRET) { - if (ns->algorithm->kstr == NULL) { + if (ns->algorithm->is_pk) { err(); ret = -EINVAL; goto fail; } - ret = cryptodev_hash_init(&ns->hash, ns->algorithm->kstr, 1, + ret = init_or_clone_hash(ns, old_session, ns->algorithm, ns->key->key.secret.data, ns->key->key.secret.size); if (ret < 0) { err(); @@ -335,10 +552,17 @@ static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* ses } } else if (ns->algorithm->is_pk && (ns->key->type == NCR_KEY_TYPE_PRIVATE || ns->key->type == NCR_KEY_TYPE_PUBLIC)) { - sign_hash = ncr_key_params_get_sign_hash(ns->key->algorithm, &session->params); - if (IS_ERR(sign_hash)) { + if (old_session != NULL) { + err(); + ret = -EOPNOTSUPP; + goto fail; + } + + nla = tb[NCR_ATTR_SIGNATURE_HASH_ALGORITHM]; + sign_hash = _ncr_nla_to_properties(nla); + if (sign_hash == NULL) { err(); - ret = PTR_ERR(sign_hash); + ret = -EINVAL; goto fail; } @@ -348,24 +572,33 @@ static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* ses goto fail; } - if (sign_hash->kstr == NULL) { + if (sign_hash->is_pk) { err(); ret = -EINVAL; goto fail; } ret = ncr_pk_cipher_init(ns->algorithm, &ns->pk, - &session->params, ns->key, sign_hash); + tb, ns->key, sign_hash); if (ret < 0) { err(); goto fail; } - ret = cryptodev_hash_init(&ns->hash, sign_hash->kstr, 0, NULL, 0); + ret = cryptodev_hash_init(&ns->hash, sign_hash->kstr, NULL, 0); if (ret < 0) { err(); goto fail; } + + if (ns->algorithm->has_transparent_hash) { + ns->transparent_hash = kzalloc(ns->hash.digestsize, GFP_KERNEL); + if (ns->transparent_hash == NULL) { + err(); + ret = -ENOMEM; + goto fail; + } + } } else { err(); ret = -EINVAL; @@ -379,44 +612,46 @@ static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* ses ret = -EINVAL; goto fail; } + + if (old_session != NULL) + _ncr_sessions_item_put(old_session); - ret = 0; - session->ses = ns->desc; + return ns; fail: - if (ret < 0) { - _ncr_session_remove(lists, ns->desc); - } + if (old_session != NULL) + _ncr_sessions_item_put(old_session); _ncr_sessions_item_put(ns); - return ret; + return ERR_PTR(ret); } -int ncr_session_init(struct ncr_lists* lists, void __user* arg) +int ncr_session_init(struct ncr_lists *lists, + const struct ncr_session_init *session, + struct nlattr *tb[]) { - struct ncr_session_st session; - int ret; + ncr_session_t desc; + struct session_item_st *sess; - if (unlikely(copy_from_user(&session, arg, sizeof(session)))) { + desc = session_alloc_desc(lists); + if (desc < 0) { err(); - return -EFAULT; + return desc; } - ret = _ncr_session_init(lists, &session); - if (unlikely(ret)) { + sess = _ncr_session_init(lists, desc, session->op, tb); + if (IS_ERR(sess)) { err(); - return ret; + session_drop_desc(lists, desc); + return PTR_ERR(sess); } - ret = copy_to_user( arg, &session, sizeof(session)); - if (unlikely(ret)) { - err(); - _ncr_session_remove(lists, session.ses); - return -EFAULT; - } - return ret; + session_publish_ref(lists, sess); + + return desc; } +/* The caller is responsible for locking of the session. */ static int _ncr_session_encrypt(struct session_item_st* sess, const struct scatterlist* input, unsigned input_cnt, size_t input_size, void *output, unsigned output_cnt, size_t *output_size) { @@ -445,6 +680,7 @@ int ret; return 0; } +/* The caller is responsible for locking of the session. */ static int _ncr_session_decrypt(struct session_item_st* sess, const struct scatterlist* input, unsigned input_cnt, size_t input_size, struct scatterlist *output, unsigned output_cnt, size_t *output_size) @@ -474,20 +710,7 @@ int ret; return 0; } -static void _ncr_session_remove(struct ncr_lists *lst, ncr_session_t desc) -{ - struct session_item_st * item; - - mutex_lock(&lst->session_idr_mutex); - item = idr_find(&lst->session_idr, desc); - if (item != NULL) - idr_remove(&lst->session_idr, desc); /* Steal the reference */ - mutex_unlock(&lst->session_idr_mutex); - - if (item != NULL) - _ncr_sessions_item_put(item); -} - +/* The caller is responsible for locking of the session. */ static int _ncr_session_grow_pages(struct session_item_st *ses, int pagecount) { struct scatterlist *sg; @@ -518,66 +741,65 @@ static int _ncr_session_grow_pages(struct session_item_st *ses, int pagecount) return 0; } -/* Only the output buffer is given as scatterlist */ -static int get_userbuf1(struct session_item_st* ses, - void __user * udata, size_t udata_size, struct scatterlist **dst_sg, unsigned *dst_cnt) +/* Make NCR_ATTR_UPDATE_INPUT_DATA and NCR_ATTR_UPDATE_OUTPUT_BUFFER available + in scatterlists. + The caller is responsible for locking of the session. */ +static int get_userbuf2(struct session_item_st *ses, struct nlattr *tb[], + struct scatterlist **src_sg, unsigned *src_cnt, + size_t *src_size, struct ncr_session_output_buffer *dst, + struct scatterlist **dst_sg, unsigned *dst_cnt, + int compat) { - int pagecount = 0; + const struct nlattr *src_nla, *dst_nla; + struct ncr_session_input_data src; + int src_pagecount, dst_pagecount = 0, pagecount, write_src = 1, ret; + size_t input_size; - if (unlikely(udata == NULL)) { - err(); - return -EINVAL; - } + src_nla = tb[NCR_ATTR_UPDATE_INPUT_DATA]; + dst_nla = tb[NCR_ATTR_UPDATE_OUTPUT_BUFFER]; - pagecount = PAGECOUNT(udata, udata_size); - _ncr_session_grow_pages(ses, pagecount); - - if (__get_userbuf(udata, udata_size, 1, - pagecount, ses->pages, ses->sg)) { + ret = ncr_session_input_data_from_nla(&src, src_nla, compat); + if (unlikely(ret != 0)) { err(); - return -EINVAL; + return ret; } - (*dst_sg) = ses->sg; - *dst_cnt = pagecount; + *src_size = src.data_size; - ses->available_pages = pagecount; - - return 0; -} - -/* make op->data.udata.input and op->data.udata.output available in scatterlists */ -static int get_userbuf2(struct session_item_st* ses, - struct ncr_session_op_st* op, struct scatterlist **src_sg, - unsigned *src_cnt, struct scatterlist **dst_sg, unsigned *dst_cnt) -{ - int src_pagecount, dst_pagecount = 0, pagecount, write_src = 1; - size_t input_size = op->data.udata.input_size; - - if (unlikely(op->data.udata.input == NULL)) { - err(); - return -EINVAL; + if (dst_nla != NULL) { + ret = ncr_session_output_buffer_from_nla(dst, dst_nla, compat); + if (unlikely(ret != 0)) { + err(); + return ret; + } } - src_pagecount = PAGECOUNT(op->data.udata.input, input_size); + input_size = src.data_size; + src_pagecount = PAGECOUNT(src.data, input_size); - if (op->data.udata.input != op->data.udata.output) { /* non-in-situ transformation */ + if (dst_nla == NULL || src.data != dst->buffer) { /* non-in-situ transformation */ write_src = 0; - if (op->data.udata.output != NULL) { - dst_pagecount = PAGECOUNT(op->data.udata.output, op->data.udata.output_size); + if (dst_nla != NULL) { + dst_pagecount = PAGECOUNT(dst->buffer, + dst->buffer_size); } else { dst_pagecount = 0; } } else { - src_pagecount = max((int)(PAGECOUNT(op->data.udata.output, op->data.udata.output_size)), - src_pagecount); - input_size = max(input_size, (size_t)op->data.udata.output_size); + src_pagecount = max((int)(PAGECOUNT(dst->buffer, + dst->buffer_size)), + src_pagecount); + input_size = max(input_size, dst->buffer_size); } pagecount = src_pagecount + dst_pagecount; - _ncr_session_grow_pages(ses, pagecount); + ret = _ncr_session_grow_pages(ses, pagecount); + if (ret != 0) { + err(); + return ret; + } - if (__get_userbuf(op->data.udata.input, input_size, write_src, - src_pagecount, ses->pages, ses->sg)) { + if (__get_userbuf((void __user *)src.data, input_size, write_src, + src_pagecount, ses->pages, ses->sg)) { err(); printk("write: %d\n", write_src); return -EINVAL; @@ -589,14 +811,15 @@ static int get_userbuf2(struct session_item_st* ses, *dst_cnt = dst_pagecount; (*dst_sg) = ses->sg + src_pagecount; - if (__get_userbuf(op->data.udata.output, op->data.udata.output_size, 1, dst_pagecount, - ses->pages + src_pagecount, *dst_sg)) { + if (__get_userbuf(dst->buffer, dst->buffer_size, 1, + dst_pagecount, ses->pages + src_pagecount, + *dst_sg)) { err(); release_user_pages(ses->pages, src_pagecount); return -EINVAL; } } else { - if (op->data.udata.output != NULL) { + if (dst_nla != NULL) { *dst_cnt = src_pagecount; (*dst_sg) = (*src_sg); } else { @@ -610,35 +833,25 @@ static int get_userbuf2(struct session_item_st* ses, return 0; } -/* Called when userspace buffers are used */ -static int _ncr_session_update(struct ncr_lists* lists, struct ncr_session_op_st* op) +/* Called when userspace buffers are used. + The caller is responsible for locking of the session. */ +static int _ncr_session_update(struct session_item_st *sess, + struct nlattr *tb[], int compat) { + const struct nlattr *nla; int ret; - struct session_item_st* sess; - struct scatterlist *isg; - struct scatterlist *osg; + struct scatterlist *isg = NULL; + struct scatterlist *osg = NULL; unsigned osg_cnt=0, isg_cnt=0; - size_t isg_size, osg_size; - - sess = ncr_sessions_item_get(lists, op->ses); - if (sess == NULL) { - err(); - return -EINVAL; - } - - if (mutex_lock_interruptible(&sess->mem_mutex)) { - err(); - _ncr_sessions_item_put(sess); - return -ERESTARTSYS; - } + size_t isg_size = 0, osg_size; + struct ncr_session_output_buffer out; - ret = get_userbuf2(sess, op, &isg, &isg_cnt, &osg, &osg_cnt); + ret = get_userbuf2(sess, tb, &isg, &isg_cnt, &isg_size, &out, &osg, + &osg_cnt, compat); if (ret < 0) { err(); - goto fail; + return ret; } - isg_size = op->data.udata.input_size; - osg_size = op->data.udata.output_size; switch(sess->op) { case NCR_OP_ENCRYPT: @@ -648,20 +861,35 @@ static int _ncr_session_update(struct ncr_lists* lists, struct ncr_session_op_st goto fail; } + osg_size = out.buffer_size; if (osg_size < isg_size) { err(); ret = -EINVAL; goto fail; } + if (sess->algorithm->is_symmetric + && sess->algorithm->needs_iv) { + nla = tb[NCR_ATTR_IV]; + if (nla != NULL) + cryptodev_cipher_set_iv(&sess->cipher, + nla_data(nla), + nla_len(nla)); + } + ret = _ncr_session_encrypt(sess, isg, isg_cnt, isg_size, osg, osg_cnt, &osg_size); if (ret < 0) { err(); goto fail; } - op->data.udata.output_size = osg_size; - + + ret = ncr_session_output_buffer_set_size(&out, osg_size, + compat); + if (ret != 0) { + err(); + goto fail; + } break; case NCR_OP_DECRYPT: if (osg == NULL) { @@ -670,28 +898,60 @@ static int _ncr_session_update(struct ncr_lists* lists, struct ncr_session_op_st goto fail; } + osg_size = out.buffer_size; if (osg_size < isg_size) { err(); ret = -EINVAL; goto fail; } + if (sess->algorithm->is_symmetric + && sess->algorithm->needs_iv) { + nla = tb[NCR_ATTR_IV]; + if (nla != NULL) + cryptodev_cipher_set_iv(&sess->cipher, + nla_data(nla), + nla_len(nla)); + } + ret = _ncr_session_decrypt(sess, isg, isg_cnt, isg_size, osg, osg_cnt, &osg_size); if (ret < 0) { err(); goto fail; } - op->data.udata.output_size = osg_size; + ret = ncr_session_output_buffer_set_size(&out, osg_size, + compat); + if (ret != 0) { + err(); + goto fail; + } break; case NCR_OP_SIGN: case NCR_OP_VERIFY: - ret = cryptodev_hash_update(&sess->hash, isg, isg_size); - if (ret < 0) { - err(); - goto fail; + if (sess->algorithm->has_transparent_hash) { + if (isg_size != sess->hash.digestsize) { + err(); + ret = -EINVAL; + goto fail; + } + ret = sg_copy_to_buffer(isg, isg_cnt, + sess->transparent_hash, + isg_size); + if (ret != isg_size) { + err(); + ret = -EINVAL; + goto fail; + } + } else { + ret = cryptodev_hash_update(&sess->hash, isg, + isg_size); + if (ret < 0) { + err(); + goto fail; + } } break; default: @@ -707,213 +967,199 @@ fail: release_user_pages(sess->pages, sess->available_pages); sess->available_pages = 0; } - mutex_unlock(&sess->mem_mutex); - _ncr_sessions_item_put(sess); return ret; } -static int try_session_update(struct ncr_lists* lists, struct ncr_session_op_st* op) +/* The caller is responsible for locking of the session. */ +static int try_session_update(struct ncr_lists *lists, + struct session_item_st *sess, struct nlattr *tb[], + int compat) { - if (op->type == NCR_KEY_DATA) { - if (op->data.kdata.input != NCR_KEY_INVALID) - return _ncr_session_update_key(lists, op); - } else if (op->type == NCR_DIRECT_DATA) { - if (op->data.udata.input != NULL) - return _ncr_session_update(lists, op); - } - - return 0; + if (tb[NCR_ATTR_UPDATE_INPUT_KEY_AS_DATA] != NULL) + return _ncr_session_update_key(lists, sess, tb); + else if (tb[NCR_ATTR_UPDATE_INPUT_DATA] != NULL) + return _ncr_session_update(sess, tb, compat); + else + return 0; } -static int _ncr_session_final(struct ncr_lists* lists, struct ncr_session_op_st* op) +/* The caller is responsible for locking of the session. + Note that one or more _ncr_session_update()s may still be blocked on + sess->mutex and will execute after this function! */ +static int _ncr_session_final(struct ncr_lists *lists, + struct session_item_st *sess, struct nlattr *tb[], + int compat) { + const struct nlattr *nla; int ret; - struct session_item_st* sess; int digest_size; uint8_t digest[NCR_HASH_MAX_OUTPUT_SIZE]; - uint8_t vdigest[NCR_HASH_MAX_OUTPUT_SIZE]; - struct scatterlist *osg; - unsigned osg_cnt=0; - size_t osg_size = 0; - size_t orig_osg_size; - void __user * udata = NULL; - size_t *udata_size; - - sess = ncr_sessions_item_get(lists, op->ses); - if (sess == NULL) { - err(); - return -EINVAL; - } + void *buffer = NULL; - ret = try_session_update(lists, op); + ret = try_session_update(lists, sess, tb, compat); if (ret < 0) { err(); - _ncr_sessions_item_put(sess); return ret; } - if (mutex_lock_interruptible(&sess->mem_mutex)) { - err(); - _ncr_sessions_item_put(sess); - return -ERESTARTSYS; - } - - if (op->type == NCR_DIRECT_DATA) { - udata = op->data.udata.output; - udata_size = &op->data.udata.output_size; - } else if (op->type == NCR_KEY_DATA) { - udata = op->data.kdata.output; - udata_size = &op->data.kdata.output_size; - } else { - err(); - ret = -EINVAL; - goto fail; - } - switch(sess->op) { - case NCR_OP_ENCRYPT: - case NCR_OP_DECRYPT: - break; - case NCR_OP_VERIFY: - ret = get_userbuf1(sess, udata, *udata_size, &osg, &osg_cnt); + case NCR_OP_ENCRYPT: + case NCR_OP_DECRYPT: + break; + case NCR_OP_VERIFY: { + struct ncr_session_input_data src; + + nla = tb[NCR_ATTR_FINAL_INPUT_DATA]; + ret = ncr_session_input_data_from_nla(&src, nla, compat); + if (unlikely(ret != 0)) { + err(); + goto fail; + } + + buffer = kmalloc(src.data_size, GFP_KERNEL); + if (buffer == NULL) { + err(); + ret = -ENOMEM; + goto fail; + } + if (unlikely(copy_from_user(buffer, src.data, src.data_size))) { + err(); + ret = -EFAULT; + goto fail; + } + + digest_size = sess->hash.digestsize; + if (digest_size == 0 || sizeof(digest) < digest_size) { + err(); + ret = -EINVAL; + goto fail; + } + if (sess->algorithm->has_transparent_hash) + memcpy(digest, sess->transparent_hash, digest_size); + else { + ret = cryptodev_hash_final(&sess->hash, digest); if (ret < 0) { err(); goto fail; } - orig_osg_size = osg_size = *udata_size; + } - digest_size = sess->hash.digestsize; - if (digest_size == 0 || sizeof(digest) < digest_size) { + if (!sess->algorithm->is_pk) + ret = (digest_size == src.data_size + && memcmp(buffer, digest, digest_size) == 0); + else { + ret = ncr_pk_cipher_verify(&sess->pk, buffer, + src.data_size, digest, + digest_size); + if (ret < 0) { err(); - ret = -EINVAL; goto fail; } + } + break; + } + + case NCR_OP_SIGN: { + struct ncr_session_output_buffer dst; + size_t output_size; + + nla = tb[NCR_ATTR_FINAL_OUTPUT_BUFFER]; + ret = ncr_session_output_buffer_from_nla(&dst, nla, compat); + if (unlikely(ret != 0)) { + err(); + goto fail; + } + + digest_size = sess->hash.digestsize; + if (digest_size == 0) { + err(); + ret = -EINVAL; + goto fail; + } + + if (sess->algorithm->has_transparent_hash) + memcpy(digest, sess->transparent_hash, digest_size); + else { ret = cryptodev_hash_final(&sess->hash, digest); if (ret < 0) { err(); goto fail; } + } - if (sess->algorithm->is_hmac) { - ret = sg_copy_to_buffer(osg, osg_cnt, vdigest, digest_size); - if (ret != digest_size) { - err(); - ret = -EINVAL; - goto fail; - } - - if (digest_size != osg_size || - memcmp(vdigest, digest, digest_size) != 0) { - - op->err = NCR_VERIFICATION_FAILED; - } else { - op->err = NCR_SUCCESS; - } - } else { - /* PK signature */ - ret = ncr_pk_cipher_verify(&sess->pk, osg, osg_cnt, osg_size, - digest, digest_size, &op->err); - if (ret < 0) { - err(); - goto fail; - } - } - break; + cryptodev_hash_deinit(&sess->hash); - case NCR_OP_SIGN: - ret = get_userbuf1(sess, udata, *udata_size, &osg, &osg_cnt); - if (ret < 0) { + if (!sess->algorithm->is_pk) { + if (dst.buffer_size < digest_size) { err(); + ret = -ERANGE; goto fail; } - orig_osg_size = osg_size = *udata_size; - - digest_size = sess->hash.digestsize; - if (digest_size == 0 || osg_size < digest_size) { + if (unlikely(copy_to_user(dst.buffer, digest, + digest_size))) { err(); - ret = -EINVAL; + ret = -EFAULT; goto fail; } - - ret = cryptodev_hash_final(&sess->hash, digest); + output_size = digest_size; + } else { + output_size = dst.buffer_size; + buffer = kmalloc(output_size, GFP_KERNEL); + if (buffer == NULL) { + err(); + ret = -ENOMEM; + goto fail; + } + ret = ncr_pk_cipher_sign(&sess->pk, digest, digest_size, + buffer, &output_size); if (ret < 0) { err(); goto fail; } - - ret = sg_copy_from_buffer(osg, osg_cnt, digest, digest_size); - if (ret != digest_size) { + if (unlikely(copy_to_user(dst.buffer, buffer, + output_size))) { err(); - ret = -EINVAL; + ret = -EFAULT; goto fail; } - osg_size = digest_size; - - cryptodev_hash_deinit(&sess->hash); + } - if (sess->algorithm->is_pk) { - /* PK signature */ - - ret = ncr_pk_cipher_sign(&sess->pk, osg, osg_cnt, osg_size, - osg, osg_cnt, &orig_osg_size); - if (ret < 0) { - err(); - goto fail; - } - osg_size = orig_osg_size; - } - break; - default: + ret = ncr_session_output_buffer_set_size(&dst, output_size, + compat); + if (ret != 0) { err(); - ret = -EINVAL; goto fail; + } + break; } - - if (osg_size > 0) - *udata_size = osg_size; - - ret = 0; - -fail: - if (sess->available_pages) { - release_user_pages(sess->pages, sess->available_pages); - sess->available_pages = 0; - } - mutex_unlock(&sess->mem_mutex); - - cryptodev_hash_deinit(&sess->hash); - if (sess->algorithm->is_symmetric) { - cryptodev_cipher_deinit(&sess->cipher); - } else { - ncr_pk_cipher_deinit(&sess->pk); + default: + err(); + ret = -EINVAL; + goto fail; } - _ncr_sessions_item_put(sess); - _ncr_session_remove(lists, op->ses); +fail: + kfree(buffer); return ret; } -/* Direct with key: Allows to hash a key */ -/* Called when userspace buffers are used */ -static int _ncr_session_update_key(struct ncr_lists* lists, struct ncr_session_op_st* op) +/* Direct with key: Allows to hash a key. + The caller is responsible for locking of the session. */ +static int _ncr_session_update_key(struct ncr_lists *lists, + struct session_item_st* sess, + struct nlattr *tb[]) { int ret; - struct session_item_st* sess; struct key_item_st* key = NULL; - sess = ncr_sessions_item_get(lists, op->ses); - if (sess == NULL) { - err(); - return -EINVAL; - } - /* read key */ - ret = ncr_key_item_get_read( &key, lists, op->data.kdata.input); + ret = key_item_get_nla_read(&key, lists, + tb[NCR_ATTR_UPDATE_INPUT_KEY_AS_DATA]); if (ret < 0) { err(); - goto fail; + return ret; } if (key->type != NCR_KEY_TYPE_SECRET) { @@ -930,6 +1176,11 @@ static int _ncr_session_update_key(struct ncr_lists* lists, struct ncr_session_o goto fail; case NCR_OP_SIGN: case NCR_OP_VERIFY: + if (sess->algorithm->has_transparent_hash) { + err(); + ret = -EINVAL; + goto fail; + } ret = _cryptodev_hash_update(&sess->hash, key->key.secret.data, key->key.secret.size); if (ret < 0) { @@ -946,89 +1197,102 @@ static int _ncr_session_update_key(struct ncr_lists* lists, struct ncr_session_o ret = 0; fail: - if (key) _ncr_key_item_put(key); - _ncr_sessions_item_put(sess); + _ncr_key_item_put(key); return ret; } -int ncr_session_update(struct ncr_lists* lists, void __user* arg) +int ncr_session_update(struct ncr_lists *lists, + const struct ncr_session_update *op, struct nlattr *tb[], + int compat) { - struct ncr_session_op_st op; + struct session_item_st *sess; int ret; - if (unlikely(copy_from_user( &op, arg, sizeof(op)))) { + sess = session_get_ref(lists, op->ses); + if (sess == NULL) { err(); - return -EFAULT; + return -EINVAL; } - if (op.type == NCR_DIRECT_DATA) - ret = _ncr_session_update(lists, &op); - else if (op.type == NCR_KEY_DATA) - ret = _ncr_session_update_key(lists, &op); + /* Note that op->ses may be reallocated from now on, making the audit + information confusing. */ + + if (mutex_lock_interruptible(&sess->mutex)) { + err(); + ret = -ERESTARTSYS; + goto end; + } + if (tb[NCR_ATTR_UPDATE_INPUT_DATA] != NULL) + ret = _ncr_session_update(sess, tb, compat); + else if (tb[NCR_ATTR_UPDATE_INPUT_KEY_AS_DATA] != NULL) + ret = _ncr_session_update_key(lists, sess, tb); else ret = -EINVAL; + mutex_unlock(&sess->mutex); + +end: + _ncr_sessions_item_put(sess); if (unlikely(ret)) { err(); return ret; } - if (unlikely(copy_to_user(arg, &op, sizeof(op)))) { - err(); - return -EFAULT; - } - return 0; } -int ncr_session_final(struct ncr_lists* lists, void __user* arg) +int ncr_session_final(struct ncr_lists *lists, + const struct ncr_session_final *op, struct nlattr *tb[], + int compat) { - struct ncr_session_op_st op; + struct session_item_st *sess; int ret; - if (unlikely(copy_from_user(&op, arg, sizeof(op)))) { + /* Make the session inaccessible atomically to avoid concurrent + session_final() callers, but keep the ID allocated to keep audit + information unambiguous. */ + sess = session_unpublish_ref(lists, op->ses); + if (sess == NULL) { err(); - return -EFAULT; + return -EINVAL; } - ret = _ncr_session_final(lists, &op); - if (unlikely(ret)) { + if (mutex_lock_interruptible(&sess->mutex)) { err(); - return ret; + /* Other threads may observe the session descriptor + disappearing and reappearing - but then they should not be + accessing it anyway if it is being freed. + session_unpublish_ref keeps the ID allocated for us. */ + session_publish_ref(lists, sess); + return -ERESTARTSYS; } + ret = _ncr_session_final(lists, sess, tb, compat); + mutex_unlock(&sess->mutex); - if (unlikely(copy_to_user(arg, &op, sizeof(op)))) { - err(); - return -EFAULT; - } - return 0; + _ncr_sessions_item_put(sess); + session_drop_desc(lists, op->ses); + + return ret; } -int ncr_session_once(struct ncr_lists* lists, void __user* arg) +int ncr_session_once(struct ncr_lists *lists, + const struct ncr_session_once *once, struct nlattr *tb[], + int compat) { - struct ncr_session_once_op_st kop; + struct session_item_st *sess; int ret; - if (unlikely(copy_from_user(&kop, arg, sizeof(kop)))) { + sess = _ncr_session_init(lists, -1, once->op, tb); + if (IS_ERR(sess)) { err(); - return -EFAULT; + return PTR_ERR(sess); } - ret = _ncr_session_init(lists, &kop.init); - if (ret < 0) { - err(); - return ret; - } - kop.op.ses = kop.init.ses; + /* No locking of sess necessary, "sess" is the only reference. */ + ret = _ncr_session_final(lists, sess, tb, compat); - ret = _ncr_session_final(lists, &kop.op); - if (ret < 0) { - err(); - return ret; - } + _ncr_sessions_item_put(sess); - if (unlikely(copy_to_user(arg, &kop, sizeof(kop)))) - return -EFAULT; - return 0; + return ret; } @@ -22,18 +22,20 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include <linux/compat.h> #include <linux/crypto.h> #include <linux/ioctl.h> #include <linux/mm.h> #include <linux/highmem.h> #include <linux/random.h> #include <linux/uaccess.h> -#include "cryptodev.h" #include <linux/scatterlist.h> #include <linux/cred.h> #include <linux/capability.h> +#include <net/netlink.h> #include "ncr.h" #include "ncr-int.h" +#include "utils.h" #include <linux/workqueue.h> /* This is the master wrapping key for storage of keys @@ -75,26 +77,20 @@ void ncr_master_key_reset(void) memset(&master_key, 0, sizeof(master_key)); } -static int ncr_master_key_set(void __user *arg) +static int ncr_master_key_set(const struct ncr_master_key_set *st, + struct nlattr *tb[]) { -struct ncr_master_key_st st; - - if (current_euid() != 0 && !capable(CAP_SYS_ADMIN)) { + if (!capable(CAP_SYS_ADMIN)) { err(); return -EPERM; } - if (unlikely(copy_from_user(&st, arg, sizeof(st)))) { - err(); - return -EFAULT; - } - - if (st.key_size > sizeof(master_key.key.secret.data)) { + if (st->key_size > sizeof(master_key.key.secret.data)) { err(); return -EINVAL; } - if (st.key_size != 16 && st.key_size != 24 && st.key_size != 32) { + if (st->key_size != 16 && st->key_size != 24 && st->key_size != 32) { dprintk(0, KERN_DEBUG, "Master key size must be 16,24 or 32.\n"); return -EINVAL; } @@ -103,7 +99,8 @@ struct ncr_master_key_st st; dprintk(0, KERN_DEBUG, "Master key was previously initialized.\n"); } - if (unlikely(copy_from_user(master_key.key.secret.data, st.key, st.key_size))) { + if (unlikely(copy_from_user(master_key.key.secret.data, st->key, + st->key_size))) { err(); return -EFAULT; } @@ -111,56 +108,370 @@ struct ncr_master_key_st st; dprintk(0, KERN_INFO, "Initializing master key.\n"); master_key.type = NCR_KEY_TYPE_SECRET; - master_key.key.secret.size = st.key_size; + master_key.key.secret.size = st->key_size; return 0; } -int +long ncr_ioctl(struct ncr_lists *lst, unsigned int cmd, unsigned long arg_) { void __user *arg = (void __user *)arg_; + struct nlattr *tb[NCR_ATTR_MAX + 1]; + void *attr_buf; + int ret; + + if (unlikely(!lst)) + BUG(); + + switch (cmd) { +#define CASE_(LABEL, STRUCT, FUNCTION, ARGS) \ + case (LABEL): { \ + struct STRUCT data; \ + \ + attr_buf = NCR_GET_INPUT_ARGS_NO_OUTPUT(&data, tb, arg); \ + if (IS_ERR(attr_buf)) { \ + err(); \ + return PTR_ERR(attr_buf); \ + } \ + ret = (FUNCTION)ARGS; \ + break; \ + } +#define CASE_NO_OUTPUT(LABEL, STRUCT, FUNCTION) \ + CASE_(LABEL, STRUCT, FUNCTION, (lst, &data, tb)) +#define CASE_NO_OUTPUT_COMPAT(LABEL, STRUCT, FUNCTION) \ + CASE_(LABEL, STRUCT, FUNCTION, (lst, &data, tb, 0)) + + case NCRIO_KEY_INIT: + return ncr_key_init(lst); + CASE_NO_OUTPUT(NCRIO_KEY_GENERATE, ncr_key_generate, ncr_key_generate); + CASE_NO_OUTPUT(NCRIO_KEY_GENERATE_PAIR, ncr_key_generate_pair, + ncr_key_generate_pair); + CASE_NO_OUTPUT(NCRIO_KEY_DERIVE, ncr_key_derive, ncr_key_derive); + case NCRIO_KEY_GET_INFO: { + struct ncr_key_get_info data; + struct ncr_out out; + + attr_buf = NCR_GET_INPUT_ARGS(&data, tb, arg); + if (IS_ERR(attr_buf)) { + err(); + return PTR_ERR(attr_buf); + } + ret = NCR_OUT_INIT(&out, &data, arg); + if (ret != 0) { + err(); + break; + } + ret = ncr_key_get_info(lst, &out, &data, tb); + ncr_out_free(&out); + break; + } + CASE_NO_OUTPUT(NCRIO_KEY_EXPORT, ncr_key_export, ncr_key_export); + CASE_NO_OUTPUT(NCRIO_KEY_IMPORT, ncr_key_import, ncr_key_import); + case NCRIO_KEY_DEINIT: { + ncr_key_t key; + + ret = get_user(key, (const ncr_key_t __user *)arg); + if (unlikely(ret)) { + err(); + return ret; + } + return ncr_key_deinit(lst, key); + } + CASE_NO_OUTPUT(NCRIO_KEY_WRAP, ncr_key_wrap, ncr_key_wrap); + CASE_NO_OUTPUT(NCRIO_KEY_UNWRAP, ncr_key_unwrap, ncr_key_unwrap); + CASE_NO_OUTPUT(NCRIO_KEY_STORAGE_WRAP, ncr_key_storage_wrap, + ncr_key_storage_wrap); + CASE_NO_OUTPUT(NCRIO_KEY_STORAGE_UNWRAP, ncr_key_storage_unwrap, + ncr_key_storage_unwrap); + CASE_NO_OUTPUT(NCRIO_SESSION_INIT, ncr_session_init, ncr_session_init); + CASE_NO_OUTPUT_COMPAT(NCRIO_SESSION_UPDATE, ncr_session_update, + ncr_session_update); + CASE_NO_OUTPUT_COMPAT(NCRIO_SESSION_FINAL, ncr_session_final, + ncr_session_final); + CASE_NO_OUTPUT_COMPAT(NCRIO_SESSION_ONCE, ncr_session_once, + ncr_session_once); + CASE_(NCRIO_MASTER_KEY_SET, ncr_master_key_set, ncr_master_key_set, + (&data, tb)); + default: + return -EINVAL; +#undef CASE_ +#undef CASE_NO_OUTPUT +#undef CASE_NO_OUTPUT_COMPAT + } + kfree(attr_buf); + return ret; +} + +#ifdef CONFIG_COMPAT +struct compat_ncr_key_export { + __u32 input_size, output_size; + ncr_key_t key; + compat_uptr_t buffer; + compat_int_t buffer_size; + __NL_ATTRIBUTES; +}; +#define COMPAT_NCRIO_KEY_EXPORT _IOWR('c', 209, struct compat_ncr_key_export) + +static void convert_ncr_key_export(struct ncr_key_export *new, + const struct compat_ncr_key_export *old) +{ + new->key = old->key; + new->buffer = compat_ptr(old->buffer); + new->buffer_size = old->buffer_size; +} + +struct compat_ncr_key_import { + __u32 input_size, output_size; + ncr_key_t key; + compat_uptr_t data; + __u32 data_size; + __NL_ATTRIBUTES; +}; +#define COMPAT_NCRIO_KEY_IMPORT _IOWR('c', 210, struct compat_ncr_key_import) + +static void convert_ncr_key_import(struct ncr_key_import *new, + const struct compat_ncr_key_import *old) +{ + new->key = old->key; + new->data = compat_ptr(old->data); + new->data_size = old->data_size; +} + +struct compat_ncr_key_wrap { + __u32 input_size, output_size; + ncr_key_t wrapping_key; + ncr_key_t source_key; + compat_uptr_t buffer; + compat_int_t buffer_size; + __NL_ATTRIBUTES; +}; +#define COMPAT_NCRIO_KEY_WRAP _IOWR('c', 250, struct compat_ncr_key_wrap) + +static void convert_ncr_key_wrap(struct ncr_key_wrap *new, + const struct compat_ncr_key_wrap *old) +{ + new->wrapping_key = old->wrapping_key; + new->source_key = old->source_key; + new->buffer = compat_ptr(old->buffer); + new->buffer_size = old->buffer_size; +} + +struct compat_ncr_key_unwrap { + __u32 input_size, output_size; + ncr_key_t wrapping_key; + ncr_key_t dest_key; + compat_uptr_t data; + __u32 data_size; + __NL_ATTRIBUTES; +}; +#define COMPAT_NCRIO_KEY_UNWRAP _IOWR('c', 251, struct compat_ncr_key_unwrap) + +static void convert_ncr_key_unwrap(struct ncr_key_unwrap *new, + const struct compat_ncr_key_unwrap *old) +{ + new->wrapping_key = old->wrapping_key; + new->dest_key = old->dest_key; + new->data = compat_ptr(old->data); + new->data_size = old->data_size; +} + +struct compat_ncr_key_storage_wrap { + __u32 input_size, output_size; + ncr_key_t key; + compat_uptr_t buffer; + compat_int_t buffer_size; + __NL_ATTRIBUTES; +}; +#define COMPAT_NCRIO_KEY_STORAGE_WRAP \ + _IOWR('c', 261, struct compat_ncr_key_storage_wrap) + +static void convert_ncr_key_storage_wrap(struct ncr_key_storage_wrap *new, + const struct compat_ncr_key_storage_wrap *old) +{ + new->key = old->key; + new->buffer = compat_ptr(old->buffer); + new->buffer_size = old->buffer_size; +} + +struct compat_ncr_key_storage_unwrap { + __u32 input_size, output_size; + ncr_key_t key; + compat_uptr_t data; + __u32 data_size; + __NL_ATTRIBUTES; +}; +#define COMPAT_NCRIO_KEY_STORAGE_UNWRAP \ + _IOWR('c', 262, struct compat_ncr_key_storage_wrap) + +static void convert_ncr_key_storage_unwrap(struct ncr_key_storage_unwrap *new, + const struct compat_ncr_key_storage_unwrap *old) +{ + new->key = old->key; + new->data = compat_ptr(old->data); + new->data_size = old->data_size; +} + +struct compat_ncr_master_key_set { + __u32 input_size, output_size; + compat_uptr_t key; + __u32 key_size; + __NL_ATTRIBUTES; +}; +#define COMPAT_NCRIO_MASTER_KEY_SET \ + _IOWR('c', 260, struct compat_ncr_master_key_set) + +static void convert_ncr_master_key_set(struct ncr_master_key_set *new, + const struct compat_ncr_master_key_set *old) +{ + new->key = compat_ptr(old->key); + new->key_size = old->key_size; +} + +long +ncr_compat_ioctl(struct ncr_lists *lst, unsigned int cmd, unsigned long arg_) +{ + void __user *arg = (void __user *)arg_; + struct nlattr *tb[NCR_ATTR_MAX + 1]; + void *attr_buf; + int ret; if (unlikely(!lst)) BUG(); switch (cmd) { - case NCRIO_KEY_INIT: - return ncr_key_init(lst, arg); - case NCRIO_KEY_DEINIT: - return ncr_key_deinit(lst, arg); - case NCRIO_KEY_GENERATE: - return ncr_key_generate(lst, arg); - case NCRIO_KEY_EXPORT: - return ncr_key_export(lst, arg); - case NCRIO_KEY_IMPORT: - return ncr_key_import(lst, arg); - case NCRIO_KEY_GET_INFO: - return ncr_key_info(lst, arg); - case NCRIO_KEY_WRAP: - return ncr_key_wrap(lst, arg); - case NCRIO_KEY_UNWRAP: - return ncr_key_unwrap(lst, arg); - case NCRIO_KEY_STORAGE_WRAP: - return ncr_key_storage_wrap(lst, arg); - case NCRIO_KEY_STORAGE_UNWRAP: - return ncr_key_storage_unwrap(lst, arg); - case NCRIO_SESSION_INIT: - return ncr_session_init(lst, arg); - case NCRIO_SESSION_UPDATE: - return ncr_session_update(lst, arg); - case NCRIO_SESSION_FINAL: - return ncr_session_final(lst, arg); - case NCRIO_SESSION_ONCE: - return ncr_session_once(lst, arg); - - case NCRIO_MASTER_KEY_SET: - return ncr_master_key_set(arg); - case NCRIO_KEY_GENERATE_PAIR: - return ncr_key_generate_pair(lst, arg); - case NCRIO_KEY_DERIVE: - return ncr_key_derive(lst, arg); - default: - return -EINVAL; + case NCRIO_KEY_INIT: + case NCRIO_KEY_GENERATE: + case NCRIO_KEY_GENERATE_PAIR: + case NCRIO_KEY_DERIVE: + case NCRIO_KEY_GET_INFO: + case NCRIO_KEY_DEINIT: + case NCRIO_SESSION_INIT: + return ncr_ioctl(lst, cmd, arg_); + +#define CASE_(LABEL, STRUCT, FUNCTION, ARGS) \ + case (LABEL): { \ + struct compat_##STRUCT old; \ + struct STRUCT new; \ + \ + attr_buf = NCR_GET_INPUT_ARGS_NO_OUTPUT(&old, tb, arg); \ + if (IS_ERR(attr_buf)) { \ + err(); \ + return PTR_ERR(attr_buf); \ + } \ + convert_##STRUCT(&new, &old); \ + ret = (FUNCTION)ARGS; \ + break; \ + } +#define CASE_NO_OUTPUT(LABEL, STRUCT, FUNCTION) \ + CASE_(LABEL, STRUCT, FUNCTION, (lst, &new, tb)) + +#define CASE_COMPAT_ONLY(LABEL, STRUCT, FUNCTION) \ + case (LABEL): { \ + struct STRUCT data; \ + \ + attr_buf = NCR_GET_INPUT_ARGS_NO_OUTPUT(&data, tb, arg); \ + if (IS_ERR(attr_buf)) { \ + err(); \ + return PTR_ERR(attr_buf); \ + } \ + ret = (FUNCTION)(lst, &data, tb, 1); \ + break; \ + } + + CASE_NO_OUTPUT(COMPAT_NCRIO_KEY_EXPORT, ncr_key_export, ncr_key_export); + CASE_NO_OUTPUT(COMPAT_NCRIO_KEY_IMPORT, ncr_key_import, ncr_key_import); + CASE_NO_OUTPUT(COMPAT_NCRIO_KEY_WRAP, ncr_key_wrap, ncr_key_wrap); + CASE_NO_OUTPUT(COMPAT_NCRIO_KEY_UNWRAP, ncr_key_unwrap, ncr_key_unwrap); + CASE_NO_OUTPUT(COMPAT_NCRIO_KEY_STORAGE_WRAP, ncr_key_storage_wrap, + ncr_key_storage_wrap); + CASE_NO_OUTPUT(COMPAT_NCRIO_KEY_STORAGE_UNWRAP, ncr_key_storage_unwrap, + ncr_key_storage_unwrap); + CASE_COMPAT_ONLY(NCRIO_SESSION_UPDATE, ncr_session_update, + ncr_session_update); + CASE_COMPAT_ONLY(NCRIO_SESSION_FINAL, ncr_session_final, + ncr_session_final); + CASE_COMPAT_ONLY(NCRIO_SESSION_ONCE, ncr_session_once, + ncr_session_once); + CASE_(COMPAT_NCRIO_MASTER_KEY_SET, ncr_master_key_set, + ncr_master_key_set, (&new, tb)); + default: + return -EINVAL; +#undef CASE_ +#undef CASE_NO_OUTPUT +#undef CASE_COMPAT_ONLY + } + kfree(attr_buf); + return ret; +} +#endif + +int ncr_session_input_data_from_nla(struct ncr_session_input_data *dest, + const struct nlattr *nla, int compat) +{ + if (unlikely(nla == NULL)) + return -EINVAL; +#ifdef CONFIG_COMPAT + if (!compat) { +#endif + if (unlikely(nla_len(nla) < sizeof(dest))) + return -ERANGE; /* nla_validate would return -ERANGE. */ + memcpy(dest, nla_data(nla), sizeof(*dest)); +#ifdef CONFIG_COMPAT + } else { + struct compat_ncr_session_input_data old; + + if (unlikely(nla_len(nla) < sizeof(old))) + return -ERANGE; + memcpy(&old, nla_data(nla), sizeof(old)); + dest->data = compat_ptr(old.data); + dest->data_size = old.data_size; + } +#endif + return 0; +} + +int ncr_session_output_buffer_from_nla(struct ncr_session_output_buffer *dest, + const struct nlattr *nla, int compat) +{ + if (unlikely(nla == NULL)) + return -EINVAL; +#ifdef CONFIG_COMPAT + if (!compat) { +#endif + if (unlikely(nla_len(nla) < sizeof(dest))) + return -ERANGE; /* nla_validate would return -ERANGE. */ + memcpy(dest, nla_data(nla), sizeof(*dest)); +#ifdef CONFIG_COMPAT + } else { + struct compat_ncr_session_output_buffer old; + + if (unlikely(nla_len(nla) < sizeof(old))) + return -ERANGE; + memcpy(&old, nla_data(nla), sizeof(old)); + dest->buffer = compat_ptr(old.buffer); + dest->buffer_size = old.buffer_size; + dest->result_size_ptr = compat_ptr(old.result_size_ptr); + } +#endif + return 0; +} + + +int ncr_session_output_buffer_set_size(const struct ncr_session_output_buffer *dest, + size_t size, int compat) +{ +#ifdef CONFIG_COMPAT + if (!compat) +#endif + return put_user(size, dest->result_size_ptr); +#ifdef CONFIG_COMPAT + else { + compat_size_t old; + + old = size; + return put_user(old, + (compat_size_t __user *)dest->result_size_ptr); } +#endif } @@ -6,46 +6,75 @@ #define __user #endif +/* Serves to make sure the structure is suitably aligned to continue with + a struct nlattr without external padding. + + 4 is NLA_ALIGNTO from <linux/netlink.h>, but if we + included <linux/netlink.h>, the user would have to include <sys/socket.h> + as well for no obvious reason. "4" is fixed by ABI. */ +#define __NL_ATTRIBUTES char __align[] __attribute__((aligned(4))) + +/* In all ioctls, input_size specifies size of the ncr_* structure and the + following attributes. + + output_size specifies space available for returning output, including the + initial ncr_* structure, and is updated by the ioctl() with the space + actually used. + + There are two special cases: input_size 0 means not attributes are supplied, + and is treated equivalent to sizeof(struct ncr_*). output_size 0 means no + space for output attributes is available, and is not updated. */ + +/* FIXME: better names for algorithm parameters? */ +/* FIXME: Split key generation/derivation attributes to decrease the number + of attributes used for the frequent operations? */ +enum { + NCR_ATTR_UNSPEC, /* 0 is special in lib/nlattr.c. */ + NCR_ATTR_ALGORITHM, /* NLA_NUL_STRING */ + NCR_ATTR_DERIVATION_ALGORITHM, /* NLA_NUL_STRING - NCR_DERIVE_* */ + NCR_ATTR_SIGNATURE_HASH_ALGORITHM, /* NLA_NUL_STRING */ + NCR_ATTR_WRAPPING_ALGORITHM, /* NLA_NUL_STRING - NCR_WALG_* */ + NCR_ATTR_UPDATE_INPUT_DATA, /* NLA_BINARY - ncr_session_input_data */ + /* NLA_BINARY - ncr_session_output_buffer */ + NCR_ATTR_UPDATE_OUTPUT_BUFFER, + NCR_ATTR_UPDATE_INPUT_KEY_AS_DATA, /* NLA_U32 - ncr_key_t */ + NCR_ATTR_FINAL_INPUT_DATA, /* NLA_BINARY - ncr_session_input_data */ + /* NLA_BINARY - ncr_session_output_buffer */ + NCR_ATTR_FINAL_OUTPUT_BUFFER, + NCR_ATTR_KEY, /* NLA_U32 - ncr_key_t */ + NCR_ATTR_KEY_FLAGS, /* NLA_U32 - NCR_KEY_FLAG_* */ + NCR_ATTR_KEY_ID, /* NLA_BINARY */ + NCR_ATTR_KEY_TYPE, /* NLA_U32 - ncr_key_type_t */ + NCR_ATTR_IV, /* NLA_BINARY */ + NCR_ATTR_SECRET_KEY_BITS, /* NLA_U32 */ + NCR_ATTR_RSA_MODULUS_BITS, /* NLA_U32 */ + NCR_ATTR_RSA_E, /* NLA_BINARY */ + NCR_ATTR_RSA_ENCODING_METHOD, /* NLA_U32 - ncr_rsa_type_t */ + NCR_ATTR_RSA_OAEP_HASH_ALGORITHM, /* NLA_NUL_STRING */ + NCR_ATTR_RSA_PSS_SALT_LENGTH, /* NLA_U32 */ + NCR_ATTR_DSA_P_BITS, /* NLA_U32 */ + NCR_ATTR_DSA_Q_BITS, /* NLA_U32 */ + NCR_ATTR_DH_PRIME, /* NLA_BINARY */ + NCR_ATTR_DH_BASE, /* NLA_BINARY */ + NCR_ATTR_DH_PUBLIC, /* NLA_BINARY */ + NCR_ATTR_WANTED_ATTRS, /* NLA_BINARY - array of u16 IDs */ + NCR_ATTR_SESSION_CLONE_FROM, /* NLA_U32 - ncr_session_t */ + + /* Add new attributes here */ + + NCR_ATTR_END__, + NCR_ATTR_MAX = NCR_ATTR_END__ - 1 +}; + #define NCR_CIPHER_MAX_BLOCK_LEN 32 #define NCR_HASH_MAX_OUTPUT_SIZE 64 -typedef enum { - NCR_ALG_NONE, - NCR_ALG_NULL, - NCR_ALG_3DES_CBC, - NCR_ALG_AES_CBC, - NCR_ALG_CAMELLIA_CBC, - NCR_ALG_ARCFOUR, - NCR_ALG_AES_ECB, - NCR_ALG_CAMELLIA_ECB, - NCR_ALG_AES_CTR, - NCR_ALG_CAMELLIA_CTR, - - NCR_ALG_SHA1=40, - NCR_ALG_MD5, - NCR_ALG_SHA2_224, - NCR_ALG_SHA2_256, - NCR_ALG_SHA2_384, - NCR_ALG_SHA2_512, - - NCR_ALG_HMAC_SHA1=80, - NCR_ALG_HMAC_MD5, - NCR_ALG_HMAC_SHA2_224, - NCR_ALG_HMAC_SHA2_256, - NCR_ALG_HMAC_SHA2_384, - NCR_ALG_HMAC_SHA2_512, - - NCR_ALG_RSA=140, - NCR_ALG_DSA, - NCR_ALG_DH, /* DH as in PKCS #3 */ -} ncr_algorithm_t; - - +/* Better names wanted */ +#define NCR_ALG_DSA_TRANSPARENT_HASH "__dsa_transparent_hash" +#define NCR_ALG_RSA_TRANSPARENT_HASH "__rsa_transparent_hash" -typedef enum { - NCR_WALG_AES_RFC3394, /* for secret keys only */ - NCR_WALG_AES_RFC5649, /* can wrap arbitrary key */ -} ncr_wrap_algorithm_t; +#define NCR_WALG_AES_RFC3394 "walg-aes-rfc3394" /* for secret keys only */ +#define NCR_WALG_AES_RFC5649 "walg-aes-rfc5649" /* can wrap arbitrary key */ typedef enum { NCR_KEY_TYPE_INVALID, @@ -57,7 +86,7 @@ typedef enum { /* Key handling */ -typedef int ncr_key_t; +typedef __s32 ncr_key_t; #define NCR_KEY_INVALID ((ncr_key_t)-1) @@ -70,46 +99,25 @@ typedef int ncr_key_t; */ #define NCR_KEY_FLAG_DECRYPT (1<<2) #define NCR_KEY_FLAG_SIGN (1<<3) +#define NCR_KEY_FLAG_ENCRYPT (1<<4) +#define NCR_KEY_FLAG_VERIFY (1<<5) +/* These flags can only be set by administrator, to prevent + * adversaries exporting wrappable keys with random ones. + */ +#define NCR_KEY_FLAG_WRAPPING (1<<6) +#define NCR_KEY_FLAG_UNWRAPPING (1<<7) -struct ncr_key_generate_params_st { - ncr_algorithm_t algorithm; /* just a cipher algorithm when - * generating secret keys - */ - - unsigned int keyflags; - union { - struct { - unsigned int bits; - } secret; - struct { - unsigned int bits; - unsigned long e; /* use zero for default */ - } rsa; - struct { - /* For DSS standard allowed values - * are: p:1024 q: 160 - * p:2048 q: 224 - * p:2048 q: 256 - * p:3072 q: 256 - */ - unsigned int p_bits; - unsigned int q_bits; - } dsa; - struct { - __u8 __user *p; /* prime */ - __kernel_size_t p_size; - __u8 __user *g; /* generator */ - __kernel_size_t g_size; - } dh; - } params; +struct ncr_key_generate { + __u32 input_size, output_size; + ncr_key_t key; + __NL_ATTRIBUTES; }; -/* used in generation - */ -struct ncr_key_generate_st { - ncr_key_t desc; - ncr_key_t desc2; /* public key when called with GENERATE_PAIR */ - struct ncr_key_generate_params_st params; +struct ncr_key_generate_pair { + __u32 input_size, output_size; + ncr_key_t private_key; + ncr_key_t public_key; + __NL_ATTRIBUTES; }; typedef enum { @@ -118,127 +126,109 @@ typedef enum { RSA_PKCS1_PSS, /* for signatures only */ } ncr_rsa_type_t; -/* used in derivation/encryption - */ -struct ncr_key_params_st { - /* this structure always corresponds to a key. Hence the - * parameters of the union selected are based on the corresponding - * key */ - union { - struct { - __u8 iv[NCR_CIPHER_MAX_BLOCK_LEN]; - __kernel_size_t iv_size; - } cipher; - struct { - __u8 __user *pub; - __kernel_size_t pub_size; - } dh; - struct { - ncr_rsa_type_t type; - ncr_algorithm_t oaep_hash; /* for OAEP */ - ncr_algorithm_t sign_hash; /* for signatures */ - unsigned int pss_salt; /* PSS signatures */ - } rsa; - struct { - ncr_algorithm_t sign_hash; /* for signatures */ - } dsa; - } params; -}; - -typedef enum { - NCR_DERIVE_DH=1, -} ncr_derive_t; +#define NCR_DERIVE_DH "dh" -struct ncr_key_derivation_params_st { - ncr_derive_t derive; /* the derivation algorithm */ - ncr_key_t newkey; - unsigned int keyflags; /* for new key */ - - ncr_key_t key; - struct ncr_key_params_st params; +struct ncr_key_derive { + __u32 input_size, output_size; + ncr_key_t input_key; + ncr_key_t new_key; + __NL_ATTRIBUTES; }; #define MAX_KEY_ID_SIZE 20 -struct ncr_key_info_st { - ncr_key_t key; /* input */ - - unsigned int flags; - ncr_key_type_t type; - ncr_algorithm_t algorithm; /* valid for public/private keys */ - - __u8 key_id[MAX_KEY_ID_SIZE]; - __kernel_size_t key_id_size; +struct ncr_key_get_info { + __u32 input_size, output_size; + ncr_key_t key; + __NL_ATTRIBUTES; }; -struct ncr_key_data_st { +struct ncr_key_import { + __u32 input_size, output_size; ncr_key_t key; + const void __user *data; + __u32 data_size; + __NL_ATTRIBUTES; +}; - void __user *idata; - __kernel_size_t idata_size; /* rw in get */ - - /* in case of import this will be used as key id */ - __u8 key_id[MAX_KEY_ID_SIZE]; - __kernel_size_t key_id_size; - ncr_key_type_t type; - unsigned int flags; - ncr_algorithm_t algorithm; /* valid for public/private keys */ +struct ncr_key_export { + __u32 input_size, output_size; + ncr_key_t key; + void __user *buffer; + int buffer_size; + __NL_ATTRIBUTES; }; -#define NCRIO_KEY_INIT _IOW ('c', 204, ncr_key_t) +#define NCRIO_KEY_INIT _IO('c', 0xC0) /* generate a secret key */ -#define NCRIO_KEY_GENERATE _IOR ('c', 205, struct ncr_key_generate_st) +#define NCRIO_KEY_GENERATE _IOWR('c', 0xC1, struct ncr_key_generate) /* generate a public key pair */ -#define NCRIO_KEY_GENERATE_PAIR _IOR ('c', 206, struct ncr_key_generate_st) +#define NCRIO_KEY_GENERATE_PAIR _IOWR('c', 0xC2, struct ncr_key_generate_pair) /* derive a new key from an old one */ -#define NCRIO_KEY_DERIVE _IOR ('c', 207, struct ncr_key_derivation_params_st) +#define NCRIO_KEY_DERIVE _IOWR('c', 0xC3, struct ncr_key_derive) /* return information on a key */ -#define NCRIO_KEY_GET_INFO _IOWR('c', 208, struct ncr_key_info_st) +#define NCRIO_KEY_GET_INFO _IOWR('c', 0xC4, struct ncr_key_get_info) /* export a secret key */ -#define NCRIO_KEY_EXPORT _IOWR('c', 209, struct ncr_key_data_st) +#define NCRIO_KEY_EXPORT _IOWR('c', 0xC5, struct ncr_key_export) /* import a secret key */ -#define NCRIO_KEY_IMPORT _IOWR('c', 210, struct ncr_key_data_st) +#define NCRIO_KEY_IMPORT _IOWR('c', 0xC6, struct ncr_key_import) -#define NCRIO_KEY_DEINIT _IOR ('c', 215, ncr_key_t) +#define NCRIO_KEY_DEINIT _IOW('c', 0xC7, ncr_key_t) /* Key wrap ioctls */ -struct ncr_key_wrap_st { - ncr_wrap_algorithm_t algorithm; - ncr_key_t keytowrap; - - ncr_key_t key; - struct ncr_key_params_st params; +struct ncr_key_wrap { + __u32 input_size, output_size; + ncr_key_t wrapping_key; + ncr_key_t source_key; + void __user *buffer; + int buffer_size; + __NL_ATTRIBUTES; +}; - void __user * io; /* encrypted keytowrap */ - /* this will be updated by the actual size on wrap */ - __kernel_size_t io_size; +struct ncr_key_unwrap { + __u32 input_size, output_size; + ncr_key_t wrapping_key; + ncr_key_t dest_key; + const void __user *data; + __u32 data_size; + __NL_ATTRIBUTES; }; -#define NCRIO_KEY_WRAP _IOWR ('c', 250, struct ncr_key_wrap_st) -#define NCRIO_KEY_UNWRAP _IOR ('c', 251, struct ncr_key_wrap_st) +#define NCRIO_KEY_WRAP _IOWR('c', 0xC8, struct ncr_key_wrap) +#define NCRIO_KEY_UNWRAP _IOWR('c', 0xC9, struct ncr_key_unwrap) /* Internal ops */ -struct ncr_master_key_st { - __u8 __user * key; - __u16 key_size; +struct ncr_master_key_set { + __u32 input_size, output_size; + const void __user *key; + __u32 key_size; + __NL_ATTRIBUTES; }; -#define NCRIO_MASTER_KEY_SET _IOR ('c', 260, struct ncr_master_key_st) +#define NCRIO_MASTER_KEY_SET _IOWR('c', 0xCA, struct ncr_master_key_set) /* These are similar to key_wrap and unwrap except that will store some extra * fields to be able to recover a key */ -struct ncr_key_storage_wrap_st { - ncr_key_t keytowrap; +struct ncr_key_storage_wrap { + __u32 input_size, output_size; + ncr_key_t key; + void __user *buffer; + int buffer_size; + __NL_ATTRIBUTES; +}; - void __user * io; /* encrypted keytowrap */ - /* this will be updated by the actual size on wrap */ - __kernel_size_t io_size; +struct ncr_key_storage_unwrap { + __u32 input_size, output_size; + ncr_key_t key; + const void __user *data; + __u32 data_size; + __NL_ATTRIBUTES; }; -#define NCRIO_KEY_STORAGE_WRAP _IOWR ('c', 261, struct ncr_key_storage_wrap_st) -#define NCRIO_KEY_STORAGE_UNWRAP _IOR ('c', 262, struct ncr_key_storage_wrap_st) +#define NCRIO_KEY_STORAGE_WRAP _IOWR('c', 0xCB, struct ncr_key_storage_wrap) +#define NCRIO_KEY_STORAGE_UNWRAP _IOWR('c', 0xCC, struct ncr_key_storage_wrap) /* Crypto Operations ioctls */ @@ -250,68 +240,49 @@ typedef enum { NCR_OP_VERIFY, } ncr_crypto_op_t; -typedef int ncr_session_t; +typedef __s32 ncr_session_t; #define NCR_SESSION_INVALID ((ncr_session_t)-1) -/* input of CIOCGSESSION */ -struct ncr_session_st { - /* input */ - ncr_algorithm_t algorithm; - - ncr_key_t key; - struct ncr_key_params_st params; - ncr_crypto_op_t op; - - /* output */ - ncr_session_t ses; /* session identifier */ +struct ncr_session_input_data { + const void __user *data; + __kernel_size_t data_size; }; -typedef enum { - NCR_SUCCESS = 0, - NCR_ERROR_GENERIC = -1, - NCR_VERIFICATION_FAILED = -2, -} ncr_error_t; +struct ncr_session_output_buffer { + void __user *buffer; + __kernel_size_t buffer_size; + __kernel_size_t __user *result_size_ptr; +}; -typedef enum { - NCR_KEY_DATA, - NCR_DIRECT_DATA, -} ncr_data_type_t; +struct ncr_session_init { + __u32 input_size, output_size; + __u32 op; /* ncr_crypto_op_t */ + __NL_ATTRIBUTES; +}; -struct ncr_session_op_st { - /* input */ +struct ncr_session_update { + __u32 input_size, output_size; ncr_session_t ses; + __NL_ATTRIBUTES; +}; - union { - struct { - ncr_key_t input; - void __user * output; /* when verifying signature this is - * the place of the signature. - */ - __kernel_size_t output_size; - } kdata; /* NCR_KEY_DATA */ - struct { - void __user * input; - __kernel_size_t input_size; - void __user * output; - __kernel_size_t output_size; - } udata; /* NCR_DIRECT_DATA */ - } data; - ncr_data_type_t type; - - /* output of verification */ - ncr_error_t err; +struct ncr_session_final { + __u32 input_size, output_size; + ncr_session_t ses; + __NL_ATTRIBUTES; }; -struct ncr_session_once_op_st { - struct ncr_session_st init; - struct ncr_session_op_st op; +struct ncr_session_once { + __u32 input_size, output_size; + ncr_crypto_op_t op; + __NL_ATTRIBUTES; }; -#define NCRIO_SESSION_INIT _IOR ('c', 300, struct ncr_session_st) -#define NCRIO_SESSION_UPDATE _IOWR ('c', 301, struct ncr_session_op_st) -#define NCRIO_SESSION_FINAL _IOWR ('c', 302, struct ncr_session_op_st) +#define NCRIO_SESSION_INIT _IOWR('c', 0xD0, struct ncr_session_init) +#define NCRIO_SESSION_UPDATE _IOWR('c', 0xD1, struct ncr_session_update) +#define NCRIO_SESSION_FINAL _IOWR('c', 0xD2, struct ncr_session_final) /* everything in one call */ -#define NCRIO_SESSION_ONCE _IOWR ('c', 303, struct ncr_session_once_op_st) +#define NCRIO_SESSION_ONCE _IOWR('c', 0xD3, struct ncr_session_once) #endif diff --git a/userspace/setkey.c b/userspace/setkey.c index b090bd50306..10e22b9cab6 100644 --- a/userspace/setkey.c +++ b/userspace/setkey.c @@ -23,7 +23,7 @@ int main(int argc, char** argv) { int fd = -1; FILE* fp; - struct ncr_master_key_st key; + struct ncr_master_key_set key; int size, ret; struct stat st; uint8_t rawkey[32]; diff --git a/utils.c b/utils.c new file mode 100644 index 00000000000..a427833fdad --- /dev/null +++ b/utils.c @@ -0,0 +1,298 @@ +/* + * New driver for /dev/crypto device (aka CryptoDev) + * + * Copyright (c) 2010 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * Red Hat Author: Miloslav Trmač + * + */ + +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <net/netlink.h> +#include "ncr-int.h" +#include "utils.h" + +#ifdef CONFIG_COMPAT +/* max() is too clever for compile-time constants */ +#define CONST_MAX(A, B) ((A) > (B) ? (A) : (B)) + +#define MAX_SESSION_INPUT_DATA_SIZE \ + (CONST_MAX(sizeof(struct ncr_session_input_data), \ + sizeof(struct compat_ncr_session_input_data))) +#define MAX_SESSION_OUTPUT_BUFFER_SIZE \ + (CONST_MAX(sizeof(struct ncr_session_output_buffer), \ + sizeof(struct compat_ncr_session_output_buffer))) + +#else /* !CONFIG_COMPAT */ + +#define MAX_SESSION_INPUT_DATA_SIZE (sizeof(struct ncr_session_input_data)) +#define MAX_SESSION_OUTPUT_BUFFER_SIZE \ + (sizeof(struct ncr_session_output_buffer)) + +#endif /* !CONFIG_COMPAT */ + +static const struct nla_policy ncr_attr_policy[NCR_ATTR_MAX + 1] = { + [NCR_ATTR_ALGORITHM] = { NLA_NUL_STRING, 0 }, + [NCR_ATTR_DERIVATION_ALGORITHM] = { NLA_NUL_STRING, 0 }, + [NCR_ATTR_SIGNATURE_HASH_ALGORITHM] = { NLA_NUL_STRING, 0 }, + [NCR_ATTR_WRAPPING_ALGORITHM] = { NLA_NUL_STRING, 0 }, + [NCR_ATTR_UPDATE_INPUT_DATA] = { + NLA_BINARY, MAX_SESSION_INPUT_DATA_SIZE + }, + [NCR_ATTR_UPDATE_OUTPUT_BUFFER] = { + NLA_BINARY, MAX_SESSION_OUTPUT_BUFFER_SIZE + }, + [NCR_ATTR_UPDATE_INPUT_KEY_AS_DATA] = { NLA_U32, 0 }, + [NCR_ATTR_FINAL_INPUT_DATA] = { + NLA_BINARY, MAX_SESSION_INPUT_DATA_SIZE + }, + [NCR_ATTR_FINAL_OUTPUT_BUFFER] = { + NLA_BINARY, MAX_SESSION_OUTPUT_BUFFER_SIZE + }, + [NCR_ATTR_KEY] = { NLA_U32, 0 }, + [NCR_ATTR_KEY_FLAGS] = { NLA_U32, 0 }, + [NCR_ATTR_KEY_ID] = { NLA_BINARY, 0 }, + [NCR_ATTR_KEY_TYPE] = { NLA_U32, 0 }, + [NCR_ATTR_IV] = { NLA_BINARY, 0 }, + [NCR_ATTR_SECRET_KEY_BITS] = { NLA_U32, 0 }, + [NCR_ATTR_RSA_MODULUS_BITS] = { NLA_U32, 0 }, + [NCR_ATTR_RSA_E] = { NLA_BINARY, 0 }, + [NCR_ATTR_RSA_ENCODING_METHOD] = { NLA_U32, 0 }, + [NCR_ATTR_RSA_OAEP_HASH_ALGORITHM] = { NLA_NUL_STRING, 0 }, + [NCR_ATTR_RSA_PSS_SALT_LENGTH] = { NLA_U32, 0 }, + [NCR_ATTR_DSA_P_BITS] = { NLA_U32, 0 }, + [NCR_ATTR_DSA_Q_BITS] = { NLA_U32, 0 }, + [NCR_ATTR_DH_PRIME] = { NLA_BINARY, 0 }, + [NCR_ATTR_DH_BASE] = { NLA_BINARY, 0 }, + [NCR_ATTR_DH_PUBLIC] = { NLA_BINARY, 0 }, + [NCR_ATTR_WANTED_ATTRS] = { NLA_BINARY, 0 }, + [NCR_ATTR_SESSION_CLONE_FROM] = { NLA_U32, 0 }, +}; + +void *__ncr_get_input_args(void *fixed, struct nlattr *tb[], size_t fixed_size, + u32 *input_size_ptr, const void __user *arg) +{ + size_t input_size, buf_size; + void *buf; + int ret; + + if (unlikely(copy_from_user(fixed, arg, fixed_size))) { + err(); + return ERR_PTR(-EFAULT); + } + input_size = *input_size_ptr; + + /* NCR_GET_INPUT_ARGS/NCR_GET_INPUT_ARGS_NO_OUTPUT has verified + fixed_size is correctly aligned for a struct nlattr. */ + if (input_size == 0) + input_size = fixed_size; + else if (unlikely(input_size < fixed_size)) { + err(); + return ERR_PTR(-EINVAL); + } + buf_size = input_size - fixed_size; + if (unlikely(buf_size > NCR_MAX_ATTR_SIZE)) { + err(); + return ERR_PTR(-EOVERFLOW); + } + + if (buf_size == 0) + buf = NULL; + else { + const char __user *var_arg; + + buf = kmalloc(buf_size, GFP_KERNEL); + if (unlikely(buf == NULL)) { + err(); + return ERR_PTR(-ENOMEM); + } + var_arg = (const char __user *)arg + fixed_size; + if (unlikely(copy_from_user(buf, var_arg, buf_size))) { + err(); + ret = -EFAULT; + goto err_buf; + } + } + + ret = nla_parse(tb, NCR_ATTR_MAX, buf, buf_size, ncr_attr_policy); + if (ret != 0) { + err(); + goto err_buf; + } + + return buf; + +err_buf: + kfree(buf); + return ERR_PTR(ret); +} + +static int update_output_size(void __user *arg, size_t output_size_offset, + u32 old_value, u32 new_value) +{ + if (old_value != 0 && old_value != new_value) { + u32 __user *dest; + + dest = (u32 __user *)((char __user *)arg + output_size_offset); + return put_user(new_value, dest); + } + return 0; +} + +void *__ncr_get_input_args_no_output(void *fixed, struct nlattr *tb[], + size_t fixed_size, u32 *input_size_ptr, + size_t output_size_offset, + void __user *arg) +{ + void *attr_buf; + u32 output_size; + int ret; + + attr_buf = __ncr_get_input_args(fixed, tb, fixed_size, input_size_ptr, + arg); + if (IS_ERR(attr_buf)) + return attr_buf; + + output_size = *(const u32 *)((const char *)fixed + output_size_offset); + ret = update_output_size(arg, output_size_offset, output_size, + fixed_size); + if (ret != 0) { + kfree(attr_buf); + return ERR_PTR(ret); + } + return attr_buf; +} + +int __ncr_out_init(struct ncr_out *out, const void *fixed, size_t fixed_size, + size_t output_size_offset, void __user *arg) +{ + u32 output_size; + + /* NCR_OUT_INIT has verified fixed_size is correctly aligned for a + struct nlattr. */ + output_size = *(const u32 *)((const char *)fixed + output_size_offset); + if (output_size == 0) + out->left = 0; + else { + /* NCR_OUT_INIT has verified fixed_size is correctly aligned for + a struct nlattr. */ + if (output_size < fixed_size) + return -EINVAL; + out->left = min_t(size_t, output_size - fixed_size, + NCR_MAX_ATTR_SIZE); + } + out->buf = kmalloc(out->left, GFP_KERNEL); + if (out->buf == NULL) + return -ENOMEM; + out->p = out->buf; + out->arg = arg; + out->output_size_offset = output_size_offset; + out->fixed_size = fixed_size; + out->orig_output_size = output_size; + return 0; +} + +int ncr_out_finish(struct ncr_out *out) +{ + size_t buf_size; + + buf_size = (char *)out->p - (char *)out->buf; + if (buf_size != 0) { + if (unlikely(copy_to_user((char __user *)out->arg + + out->fixed_size, + out->buf, buf_size))) + return -EFAULT; + } + + return update_output_size(out->arg, out->output_size_offset, + out->orig_output_size, + out->fixed_size + buf_size); +} + +void ncr_out_free(struct ncr_out *out) +{ + kfree(out->buf); +} + +struct nlattr *ncr_out_reserve(struct ncr_out *out, int attrtype, int attrlen) +{ + size_t needed; + struct nlattr *nla; + + needed = nla_total_size(attrlen); + if (out->left < needed) + ERR_PTR(-ERANGE); + nla = out->p; + out->p = (char *)out->p + needed; + out->left -= needed; + + nla->nla_len = nla_attr_size(attrlen); + nla->nla_type = attrtype; + memset((unsigned char *)nla + nla->nla_len, 0, nla_padlen(attrlen)); + return nla; +} + +int ncr_out_put(struct ncr_out *out, int attrtype, int attrlen, const void *data) +{ + struct nlattr *nla; + + nla = ncr_out_reserve(out, attrtype, attrlen); + if (IS_ERR(nla)) + return PTR_ERR(nla); + memcpy(nla_data(nla), data, attrlen); + return 0; +} + +/** + * Initialize a nlattr with @attrtype as a buffer of maximum possible size in + * @out. The buffer must be finalized using ncr_out_commit_buffer. + */ +struct nlattr *ncr_out_begin_buffer(struct ncr_out *out, int attrtype) +{ + struct nlattr *nla; + + if (out->left < NLA_HDRLEN) + return ERR_PTR(-ERANGE); + nla = out->p; + + /* Ensure the rounding down of out->left does not decrease it below + NLA_HDRLEN. */ + BUILD_BUG_ON(NLA_ALIGN(NLA_HDRLEN) != NLA_HDRLEN); + nla->nla_len = out->left & ~(NLA_ALIGNTO - 1); + nla->nla_type = attrtype; + return nla; +} + +/** + * Set the length of buffer initialied in @out with ncr_out_begin_buffer() to + * @attrlen and allow adding more attributes. + */ +void ncr_out_commit_buffer(struct ncr_out *out, int attrlen) +{ + struct nlattr *nla; + size_t total; + + nla = out->p; + nla->nla_len = nla_attr_size(attrlen); + memset((unsigned char *)nla + nla->nla_len, 0, nla_padlen(attrlen)); + total = nla_total_size(attrlen); + + out->p = (char *)out->p + total; + out->left -= total; +} diff --git a/utils.h b/utils.h new file mode 100644 index 00000000000..2802afa7758 --- /dev/null +++ b/utils.h @@ -0,0 +1,98 @@ +#ifndef NCR_UTILS_H +#define NCR_UTILS_H + +#include <linux/kernel.h> +#include <linux/netlink.h> + +#define NCR_MAX_ATTR_SIZE 4096 + +struct nlattr; + +struct ncr_out { + void *buf, *p; + size_t left; + void __user *arg; + size_t output_size_offset, fixed_size; + u32 orig_output_size; +}; + +#define __NCR_VERIFY_FIXED_SIZE(fixed) \ + (BUILD_BUG_ON(sizeof(*(fixed)) != NLA_ALIGN(sizeof(*(fixed))))) +#define __NCR_VERIFY_TB(tb) (BUILD_BUG_ON(ARRAY_SIZE(tb) != NCR_ATTR_MAX + 1)) + +extern u32 __ncr_u32_type_check; +#define __OUT_SIZE_OFF(fixed) \ + ((void)(&(fixed)->output_size == &__ncr_u32_type_check), \ + (char *)&(fixed)->output_size - (char *)(fixed)) + + +/** + * Load *@fixed and a sequence of netlink-like attributes from @arg. @fixed + * contains "input_size", which is an u32 filled with total input size, + * including the attributes, which are parsed into @tb. + */ +#define NCR_GET_INPUT_ARGS(fixed, tb, arg) \ + (__NCR_VERIFY_FIXED_SIZE(fixed), \ + __NCR_VERIFY_TB(tb), \ + __ncr_get_input_args(fixed, tb, sizeof(*(fixed)), \ + &(fixed)->input_size, arg)) +void *__ncr_get_input_args(void *fixed, struct nlattr *tb[], size_t fixed_size, + u32 *input_size_ptr, const void __user *arg); + +/** + * Load *@fixed and a sequence of netlink-like attributes from @arg. @fixed + * contains "input_size", which is an u32 filled with total input size, + * including the attributes, which are parsed into @tb. In addition, indicate + * to the user through u32 "output_size" that no output attributes will be + * returned. + */ +#define NCR_GET_INPUT_ARGS_NO_OUTPUT(fixed, tb, arg) \ + (__NCR_VERIFY_FIXED_SIZE(fixed), \ + __NCR_VERIFY_TB(tb), \ + __ncr_get_input_args_no_output(fixed, tb, sizeof(*(fixed)), \ + &(fixed)->input_size, \ + __OUT_SIZE_OFF(fixed), arg)) +void *__ncr_get_input_args_no_output(void *fixed, struct nlattr *tb[], + size_t fixed_size, u32 *input_size_ptr, + size_t output_size_offset, + void __user *arg); + +/** + * Return a new output attribute context for attributes of *@fixed. @fixed + * contains "output_size", an u32 containing total output size, including + * @fixed. Store @arg for later ncr_out_finish(). + */ +#define NCR_OUT_INIT(out, fixed, arg) \ + (__NCR_VERIFY_FIXED_SIZE(fixed), \ + __ncr_out_init((out), (fixed), sizeof(*(fixed)), \ + __OUT_SIZE_OFF(fixed), (arg))) +int __ncr_out_init(struct ncr_out *out, const void *fixed, size_t fixed_size, + size_t output_size_offset, void __user *arg); + +/** + * Write attributes from @out to user space and update user-space output_size. + */ +int ncr_out_finish(struct ncr_out *out); + +void ncr_out_free(struct ncr_out *out); + +int ncr_out_put(struct ncr_out *out, int attrtype, int attrlen, + const void *data); + +static inline int ncr_out_put_u32(struct ncr_out *out, int attrtype, u32 value) +{ + return ncr_out_put(out, attrtype, sizeof(value), &value); +} + +static inline int ncr_out_put_string(struct ncr_out *out, int attrtype, + const char *value) +{ + return ncr_out_put(out, attrtype, strlen(value) + 1, value); +} + +struct nlattr *ncr_out_reserve(struct ncr_out *out, int attrtype, int attrlen); + +struct nlattr *ncr_out_begin_buffer(struct ncr_out *out, int attrtype); +void ncr_out_commit_buffer(struct ncr_out *out, int attrlen); + +#endif |