2016-04-12 21:54:58 +03:00
/* Crypto operations using stored keys
*
* Copyright ( c ) 2016 , Intel Corporation
*
* 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 .
*/
# include <linux/mpi.h>
# include <linux/slab.h>
# include <linux/uaccess.h>
# include <keys/user-type.h>
# include "internal.h"
/*
* Public key or shared secret generation function [ RFC2631 sec 2.1 .1 ]
*
* ya = g ^ xa mod p ;
* or
* ZZ = yb ^ xa mod p ;
*
* where xa is the local private key , ya is the local public key , g is
* the generator , p is the prime , yb is the remote public key , and ZZ
* is the shared secret .
*
* Both are the same calculation , so g or yb are the " base " and ya or
* ZZ are the " result " .
*/
static int do_dh ( MPI result , MPI base , MPI xa , MPI p )
{
return mpi_powm ( result , base , xa , p ) ;
}
static ssize_t mpi_from_key ( key_serial_t keyid , size_t maxlen , MPI * mpi )
{
struct key * key ;
key_ref_t key_ref ;
long status ;
ssize_t ret ;
key_ref = lookup_user_key ( keyid , 0 , KEY_NEED_READ ) ;
if ( IS_ERR ( key_ref ) ) {
ret = - ENOKEY ;
goto error ;
}
key = key_ref_to_ptr ( key_ref ) ;
ret = - EOPNOTSUPP ;
if ( key - > type = = & key_type_user ) {
down_read ( & key - > sem ) ;
status = key_validate ( key ) ;
if ( status = = 0 ) {
const struct user_key_payload * payload ;
payload = user_key_payload ( key ) ;
if ( maxlen = = 0 ) {
* mpi = NULL ;
ret = payload - > datalen ;
} else if ( payload - > datalen < = maxlen ) {
* mpi = mpi_read_raw_data ( payload - > data ,
payload - > datalen ) ;
if ( * mpi )
ret = payload - > datalen ;
} else {
ret = - EINVAL ;
}
}
up_read ( & key - > sem ) ;
}
key_put ( key ) ;
error :
return ret ;
}
long keyctl_dh_compute ( struct keyctl_dh_params __user * params ,
2016-05-27 00:38:12 +03:00
char __user * buffer , size_t buflen ,
void __user * reserved )
2016-04-12 21:54:58 +03:00
{
long ret ;
MPI base , private , prime , result ;
unsigned nbytes ;
struct keyctl_dh_params pcopy ;
uint8_t * kbuf ;
ssize_t keylen ;
size_t resultlen ;
if ( ! params | | ( ! buffer & & buflen ) ) {
ret = - EINVAL ;
goto out ;
}
if ( copy_from_user ( & pcopy , params , sizeof ( pcopy ) ) ! = 0 ) {
ret = - EFAULT ;
goto out ;
}
2016-05-27 00:38:12 +03:00
if ( reserved ) {
ret = - EINVAL ;
goto out ;
}
2016-04-12 21:54:58 +03:00
keylen = mpi_from_key ( pcopy . prime , buflen , & prime ) ;
if ( keylen < 0 | | ! prime ) {
/* buflen == 0 may be used to query the required buffer size,
* which is the prime key length .
*/
ret = keylen ;
goto out ;
}
/* The result is never longer than the prime */
resultlen = keylen ;
keylen = mpi_from_key ( pcopy . base , SIZE_MAX , & base ) ;
if ( keylen < 0 | | ! base ) {
ret = keylen ;
goto error1 ;
}
keylen = mpi_from_key ( pcopy . private , SIZE_MAX , & private ) ;
if ( keylen < 0 | | ! private ) {
ret = keylen ;
goto error2 ;
}
result = mpi_alloc ( 0 ) ;
if ( ! result ) {
ret = - ENOMEM ;
goto error3 ;
}
kbuf = kmalloc ( resultlen , GFP_KERNEL ) ;
if ( ! kbuf ) {
ret = - ENOMEM ;
goto error4 ;
}
ret = do_dh ( result , base , private , prime ) ;
if ( ret )
goto error5 ;
ret = mpi_read_buffer ( result , kbuf , resultlen , & nbytes , NULL ) ;
if ( ret ! = 0 )
goto error5 ;
ret = nbytes ;
if ( copy_to_user ( buffer , kbuf , nbytes ) ! = 0 )
ret = - EFAULT ;
error5 :
kfree ( kbuf ) ;
error4 :
mpi_free ( result ) ;
error3 :
mpi_free ( private ) ;
error2 :
mpi_free ( base ) ;
error1 :
mpi_free ( prime ) ;
out :
return ret ;
}