2020-09-20 19:20:57 +03:00
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* SM2 asymmetric public - key algorithm
* as specified by OSCCA GM / T 0003.1 - 2012 - - 0003.5 - 2012 SM2 and
* described at https : //tools.ietf.org/html/draft-shen-sm2-ecdsa-02
*
* Copyright ( c ) 2020 , Alibaba Group .
* Authors : Tianjia Zhang < tianjia . zhang @ linux . alibaba . com >
*/
# include <linux/module.h>
# include <linux/mpi.h>
# include <crypto/internal/akcipher.h>
# include <crypto/akcipher.h>
# include <crypto/hash.h>
2022-01-07 15:06:57 +03:00
# include <crypto/sm3.h>
2020-09-20 19:20:57 +03:00
# include <crypto/rng.h>
# include <crypto/sm2.h>
# include "sm2signature.asn1.h"
# define MPI_NBYTES(m) ((mpi_get_nbits(m) + 7) / 8)
struct ecc_domain_parms {
const char * desc ; /* Description of the curve. */
unsigned int nbits ; /* Number of bits. */
unsigned int fips : 1 ; /* True if this is a FIPS140-2 approved curve */
/* The model describing this curve. This is mainly used to select
* the group equation .
*/
enum gcry_mpi_ec_models model ;
/* The actual ECC dialect used. This is used for curve specific
* optimizations and to select encodings etc .
*/
enum ecc_dialects dialect ;
const char * p ; /* The prime defining the field. */
const char * a , * b ; /* The coefficients. For Twisted Edwards
* Curves b is used for d . For Montgomery
* Curves ( a , b ) has ( ( A - 2 ) / 4 , B ^ - 1 ) .
*/
const char * n ; /* The order of the base point. */
const char * g_x , * g_y ; /* Base point. */
unsigned int h ; /* Cofactor. */
} ;
static const struct ecc_domain_parms sm2_ecp = {
. desc = " sm2p256v1 " ,
. nbits = 256 ,
. fips = 0 ,
. model = MPI_EC_WEIERSTRASS ,
. dialect = ECC_DIALECT_STANDARD ,
. p = " 0xfffffffeffffffffffffffffffffffffffffffff00000000ffffffffffffffff " ,
. a = " 0xfffffffeffffffffffffffffffffffffffffffff00000000fffffffffffffffc " ,
. b = " 0x28e9fa9e9d9f5e344d5a9e4bcf6509a7f39789f515ab8f92ddbcbd414d940e93 " ,
. n = " 0xfffffffeffffffffffffffffffffffff7203df6b21c6052b53bbf40939d54123 " ,
. g_x = " 0x32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7 " ,
. g_y = " 0xbc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0 " ,
. h = 1
} ;
static int sm2_ec_ctx_init ( struct mpi_ec_ctx * ec )
{
const struct ecc_domain_parms * ecp = & sm2_ecp ;
MPI p , a , b ;
MPI x , y ;
int rc = - EINVAL ;
p = mpi_scanval ( ecp - > p ) ;
a = mpi_scanval ( ecp - > a ) ;
b = mpi_scanval ( ecp - > b ) ;
if ( ! p | | ! a | | ! b )
goto free_p ;
x = mpi_scanval ( ecp - > g_x ) ;
y = mpi_scanval ( ecp - > g_y ) ;
if ( ! x | | ! y )
goto free ;
rc = - ENOMEM ;
2021-06-04 09:30:35 +03:00
ec - > Q = mpi_point_new ( 0 ) ;
if ( ! ec - > Q )
goto free ;
2020-09-20 19:20:57 +03:00
/* mpi_ec_setup_elliptic_curve */
ec - > G = mpi_point_new ( 0 ) ;
2021-06-04 09:30:35 +03:00
if ( ! ec - > G ) {
mpi_point_release ( ec - > Q ) ;
2020-09-20 19:20:57 +03:00
goto free ;
2021-06-04 09:30:35 +03:00
}
2020-09-20 19:20:57 +03:00
mpi_set ( ec - > G - > x , x ) ;
mpi_set ( ec - > G - > y , y ) ;
mpi_set_ui ( ec - > G - > z , 1 ) ;
rc = - EINVAL ;
ec - > n = mpi_scanval ( ecp - > n ) ;
if ( ! ec - > n ) {
2021-06-04 09:30:35 +03:00
mpi_point_release ( ec - > Q ) ;
2020-09-20 19:20:57 +03:00
mpi_point_release ( ec - > G ) ;
goto free ;
}
ec - > h = ecp - > h ;
ec - > name = ecp - > desc ;
mpi_ec_init ( ec , ecp - > model , ecp - > dialect , 0 , p , a , b ) ;
rc = 0 ;
free :
mpi_free ( x ) ;
mpi_free ( y ) ;
free_p :
mpi_free ( p ) ;
mpi_free ( a ) ;
mpi_free ( b ) ;
return rc ;
}
static void sm2_ec_ctx_deinit ( struct mpi_ec_ctx * ec )
{
mpi_ec_deinit ( ec ) ;
memset ( ec , 0 , sizeof ( * ec ) ) ;
}
/* RESULT must have been initialized and is set on success to the
* point given by VALUE .
*/
static int sm2_ecc_os2ec ( MPI_POINT result , MPI value )
{
int rc ;
size_t n ;
2020-10-15 12:24:41 +03:00
unsigned char * buf ;
2020-09-20 19:20:57 +03:00
MPI x , y ;
2020-10-15 12:24:41 +03:00
n = MPI_NBYTES ( value ) ;
buf = kmalloc ( n , GFP_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
2020-09-20 19:20:57 +03:00
2020-10-15 12:24:41 +03:00
rc = mpi_print ( GCRYMPI_FMT_USG , buf , n , & n , value ) ;
if ( rc )
goto err_freebuf ;
rc = - EINVAL ;
if ( n < 1 | | ( ( n - 1 ) % 2 ) )
goto err_freebuf ;
/* No support for point compression */
if ( * buf ! = 0x4 )
goto err_freebuf ;
rc = - ENOMEM ;
n = ( n - 1 ) / 2 ;
2020-09-20 19:20:57 +03:00
x = mpi_read_raw_data ( buf + 1 , n ) ;
2020-10-15 12:24:41 +03:00
if ( ! x )
goto err_freebuf ;
2020-09-20 19:20:57 +03:00
y = mpi_read_raw_data ( buf + 1 + n , n ) ;
2020-10-15 12:24:41 +03:00
if ( ! y )
goto err_freex ;
2020-09-20 19:20:57 +03:00
mpi_normalize ( x ) ;
mpi_normalize ( y ) ;
mpi_set ( result - > x , x ) ;
mpi_set ( result - > y , y ) ;
mpi_set_ui ( result - > z , 1 ) ;
2020-10-15 12:24:41 +03:00
rc = 0 ;
2020-09-20 19:20:57 +03:00
2020-10-15 12:24:41 +03:00
mpi_free ( y ) ;
err_freex :
mpi_free ( x ) ;
err_freebuf :
kfree ( buf ) ;
return rc ;
2020-09-20 19:20:57 +03:00
}
struct sm2_signature_ctx {
MPI sig_r ;
MPI sig_s ;
} ;
int sm2_get_signature_r ( void * context , size_t hdrlen , unsigned char tag ,
const void * value , size_t vlen )
{
struct sm2_signature_ctx * sig = context ;
if ( ! value | | ! vlen )
return - EINVAL ;
sig - > sig_r = mpi_read_raw_data ( value , vlen ) ;
if ( ! sig - > sig_r )
return - ENOMEM ;
return 0 ;
}
int sm2_get_signature_s ( void * context , size_t hdrlen , unsigned char tag ,
const void * value , size_t vlen )
{
struct sm2_signature_ctx * sig = context ;
if ( ! value | | ! vlen )
return - EINVAL ;
sig - > sig_s = mpi_read_raw_data ( value , vlen ) ;
if ( ! sig - > sig_s )
return - ENOMEM ;
return 0 ;
}
2022-01-07 15:06:57 +03:00
static int sm2_z_digest_update ( struct sm3_state * sctx ,
2020-09-20 19:20:57 +03:00
MPI m , unsigned int pbytes )
{
static const unsigned char zero [ 32 ] ;
unsigned char * in ;
unsigned int inlen ;
in = mpi_get_buffer ( m , & inlen , NULL ) ;
if ( ! in )
return - EINVAL ;
if ( inlen < pbytes ) {
/* padding with zero */
2022-01-07 15:06:57 +03:00
sm3_update ( sctx , zero , pbytes - inlen ) ;
sm3_update ( sctx , in , inlen ) ;
2020-09-20 19:20:57 +03:00
} else if ( inlen > pbytes ) {
/* skip the starting zero */
2022-01-07 15:06:57 +03:00
sm3_update ( sctx , in + inlen - pbytes , pbytes ) ;
2020-09-20 19:20:57 +03:00
} else {
2022-01-07 15:06:57 +03:00
sm3_update ( sctx , in , inlen ) ;
2020-09-20 19:20:57 +03:00
}
kfree ( in ) ;
return 0 ;
}
2022-01-07 15:06:57 +03:00
static int sm2_z_digest_update_point ( struct sm3_state * sctx ,
2020-09-20 19:20:57 +03:00
MPI_POINT point , struct mpi_ec_ctx * ec , unsigned int pbytes )
{
MPI x , y ;
int ret = - EINVAL ;
x = mpi_new ( 0 ) ;
y = mpi_new ( 0 ) ;
if ( ! mpi_ec_get_affine ( x , y , point , ec ) & &
2022-01-07 15:06:57 +03:00
! sm2_z_digest_update ( sctx , x , pbytes ) & &
! sm2_z_digest_update ( sctx , y , pbytes ) )
2020-09-20 19:20:57 +03:00
ret = 0 ;
mpi_free ( x ) ;
mpi_free ( y ) ;
return ret ;
}
int sm2_compute_z_digest ( struct crypto_akcipher * tfm ,
const unsigned char * id , size_t id_len ,
unsigned char dgst [ SM3_DIGEST_SIZE ] )
{
struct mpi_ec_ctx * ec = akcipher_tfm_ctx ( tfm ) ;
uint16_t bits_len ;
unsigned char entl [ 2 ] ;
2022-01-07 15:06:57 +03:00
struct sm3_state sctx ;
2020-09-20 19:20:57 +03:00
unsigned int pbytes ;
if ( id_len > ( USHRT_MAX / 8 ) | | ! ec - > Q )
return - EINVAL ;
bits_len = ( uint16_t ) ( id_len * 8 ) ;
entl [ 0 ] = bits_len > > 8 ;
entl [ 1 ] = bits_len & 0xff ;
pbytes = MPI_NBYTES ( ec - > p ) ;
/* ZA = H256(ENTLA | IDA | a | b | xG | yG | xA | yA) */
2022-01-07 15:06:57 +03:00
sm3_init ( & sctx ) ;
sm3_update ( & sctx , entl , 2 ) ;
sm3_update ( & sctx , id , id_len ) ;
if ( sm2_z_digest_update ( & sctx , ec - > a , pbytes ) | |
sm2_z_digest_update ( & sctx , ec - > b , pbytes ) | |
sm2_z_digest_update_point ( & sctx , ec - > G , ec , pbytes ) | |
sm2_z_digest_update_point ( & sctx , ec - > Q , ec , pbytes ) )
2020-09-20 19:20:57 +03:00
return - EINVAL ;
2022-01-07 15:06:57 +03:00
sm3_final ( & sctx , dgst ) ;
2020-09-20 19:20:57 +03:00
return 0 ;
}
EXPORT_SYMBOL ( sm2_compute_z_digest ) ;
static int _sm2_verify ( struct mpi_ec_ctx * ec , MPI hash , MPI sig_r , MPI sig_s )
{
int rc = - EINVAL ;
struct gcry_mpi_point sG , tP ;
MPI t = NULL ;
MPI x1 = NULL , y1 = NULL ;
mpi_point_init ( & sG ) ;
mpi_point_init ( & tP ) ;
x1 = mpi_new ( 0 ) ;
y1 = mpi_new ( 0 ) ;
t = mpi_new ( 0 ) ;
/* r, s in [1, n-1] */
if ( mpi_cmp_ui ( sig_r , 1 ) < 0 | | mpi_cmp ( sig_r , ec - > n ) > 0 | |
mpi_cmp_ui ( sig_s , 1 ) < 0 | | mpi_cmp ( sig_s , ec - > n ) > 0 ) {
goto leave ;
}
/* t = (r + s) % n, t == 0 */
mpi_addm ( t , sig_r , sig_s , ec - > n ) ;
if ( mpi_cmp_ui ( t , 0 ) = = 0 )
goto leave ;
/* sG + tP = (x1, y1) */
rc = - EBADMSG ;
mpi_ec_mul_point ( & sG , sig_s , ec - > G , ec ) ;
mpi_ec_mul_point ( & tP , t , ec - > Q , ec ) ;
mpi_ec_add_points ( & sG , & sG , & tP , ec ) ;
if ( mpi_ec_get_affine ( x1 , y1 , & sG , ec ) )
goto leave ;
/* R = (e + x1) % n */
mpi_addm ( t , hash , x1 , ec - > n ) ;
/* check R == r */
rc = - EKEYREJECTED ;
if ( mpi_cmp ( t , sig_r ) )
goto leave ;
rc = 0 ;
leave :
mpi_point_free_parts ( & sG ) ;
mpi_point_free_parts ( & tP ) ;
mpi_free ( x1 ) ;
mpi_free ( y1 ) ;
mpi_free ( t ) ;
return rc ;
}
static int sm2_verify ( struct akcipher_request * req )
{
struct crypto_akcipher * tfm = crypto_akcipher_reqtfm ( req ) ;
struct mpi_ec_ctx * ec = akcipher_tfm_ctx ( tfm ) ;
unsigned char * buffer ;
struct sm2_signature_ctx sig ;
MPI hash ;
int ret ;
if ( unlikely ( ! ec - > Q ) )
return - EINVAL ;
buffer = kmalloc ( req - > src_len + req - > dst_len , GFP_KERNEL ) ;
if ( ! buffer )
return - ENOMEM ;
sg_pcopy_to_buffer ( req - > src ,
sg_nents_for_len ( req - > src , req - > src_len + req - > dst_len ) ,
buffer , req - > src_len + req - > dst_len , 0 ) ;
sig . sig_r = NULL ;
sig . sig_s = NULL ;
ret = asn1_ber_decoder ( & sm2signature_decoder , & sig ,
buffer , req - > src_len ) ;
if ( ret )
goto error ;
ret = - ENOMEM ;
hash = mpi_read_raw_data ( buffer + req - > src_len , req - > dst_len ) ;
if ( ! hash )
goto error ;
ret = _sm2_verify ( ec , hash , sig . sig_r , sig . sig_s ) ;
mpi_free ( hash ) ;
error :
mpi_free ( sig . sig_r ) ;
mpi_free ( sig . sig_s ) ;
kfree ( buffer ) ;
return ret ;
}
static int sm2_set_pub_key ( struct crypto_akcipher * tfm ,
const void * key , unsigned int keylen )
{
struct mpi_ec_ctx * ec = akcipher_tfm_ctx ( tfm ) ;
MPI a ;
int rc ;
/* include the uncompressed flag '0x04' */
a = mpi_read_raw_data ( key , keylen ) ;
if ( ! a )
2021-06-04 09:30:35 +03:00
return - ENOMEM ;
2020-09-20 19:20:57 +03:00
mpi_normalize ( a ) ;
rc = sm2_ecc_os2ec ( ec - > Q , a ) ;
mpi_free ( a ) ;
return rc ;
}
static unsigned int sm2_max_size ( struct crypto_akcipher * tfm )
{
/* Unlimited max size */
return PAGE_SIZE ;
}
static int sm2_init_tfm ( struct crypto_akcipher * tfm )
{
struct mpi_ec_ctx * ec = akcipher_tfm_ctx ( tfm ) ;
return sm2_ec_ctx_init ( ec ) ;
}
static void sm2_exit_tfm ( struct crypto_akcipher * tfm )
{
struct mpi_ec_ctx * ec = akcipher_tfm_ctx ( tfm ) ;
sm2_ec_ctx_deinit ( ec ) ;
}
static struct akcipher_alg sm2 = {
. verify = sm2_verify ,
. set_pub_key = sm2_set_pub_key ,
. max_size = sm2_max_size ,
. init = sm2_init_tfm ,
. exit = sm2_exit_tfm ,
. base = {
. cra_name = " sm2 " ,
. cra_driver_name = " sm2-generic " ,
. cra_priority = 100 ,
. cra_module = THIS_MODULE ,
. cra_ctxsize = sizeof ( struct mpi_ec_ctx ) ,
} ,
} ;
static int sm2_init ( void )
{
return crypto_register_akcipher ( & sm2 ) ;
}
static void sm2_exit ( void )
{
crypto_unregister_akcipher ( & sm2 ) ;
}
subsys_initcall ( sm2_init ) ;
module_exit ( sm2_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Tianjia Zhang <tianjia.zhang@linux.alibaba.com> " ) ;
MODULE_DESCRIPTION ( " SM2 generic algorithm " ) ;
MODULE_ALIAS_CRYPTO ( " sm2-generic " ) ;