ruToken fixups
http://www.opensc-project.org/pipermail/opensc-devel/2008-April/011057.html By Aktiv Co. Aleksey Samsonov git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@3478 c6295689-39f2-0310-b995-f0e70906c6a9
This commit is contained in:
parent
e19d6a93ed
commit
8d7bce4de2
@ -13,7 +13,6 @@ all-local:
|
||||
@$(LN_S) $(top_srcdir)/src/libopensc/pkcs15.h pkcs15.h
|
||||
@$(LN_S) $(top_srcdir)/src/libopensc/types.h types.h
|
||||
@$(LN_S) $(top_srcdir)/src/libopensc/ui.h ui.h
|
||||
@$(LN_S) $(top_srcdir)/src/libopensc/rutoken.h rutoken.h
|
||||
@$(LN_S) $(top_srcdir)/src/pkcs11/pkcs11.h pkcs11.h
|
||||
@$(LN_S) $(top_srcdir)/src/pkcs11/pkcs11-opensc.h pkcs11-opensc.h
|
||||
@$(LN_S) $(top_srcdir)/src/pkcs15init/keycache.h keycache.h
|
||||
|
@ -10,8 +10,7 @@ lib_LTLIBRARIES = libopensc.la
|
||||
openscinclude_HEADERS = \
|
||||
opensc.h pkcs15.h emv.h \
|
||||
cardctl.h asn1.h log.h ui.h \
|
||||
errors.h types.h compression.h \
|
||||
rutoken.h
|
||||
errors.h types.h compression.h
|
||||
noinst_HEADERS = cards.h ctbcs.h internal.h esteid.h muscle.h muscle-filesystem.h \
|
||||
internal-winscard.h p15card-helper.h
|
||||
pkgconfig_DATA = libopensc.pc libpkcs15init.pc libscconf.pc
|
||||
@ -43,7 +42,7 @@ libopensc_la_SOURCES = \
|
||||
pkcs15-openpgp.c pkcs15-infocamere.c pkcs15-starcert.c \
|
||||
pkcs15-tcos.c pkcs15-esteid.c pkcs15-postecert.c pkcs15-gemsafeGPK.c \
|
||||
pkcs15-actalis.c pkcs15-atrust-acos.c pkcs15-tccardos.c pkcs15-piv.c \
|
||||
pkcs15-rutoken.c pkcs15-prkey-rutoken.c \
|
||||
pkcs15-rutoken.c \
|
||||
compression.c p15card-helper.c \
|
||||
\
|
||||
libopensc.exports
|
||||
|
@ -5,7 +5,7 @@ TARGET = opensc.dll opensc_a.lib
|
||||
|
||||
HEADERS = \
|
||||
asn1.h cardctl.h cards.h emv.h errors.h \
|
||||
log.h opensc.h pkcs15.h rutoken.h types.h ui.h
|
||||
log.h opensc.h pkcs15.h types.h ui.h
|
||||
|
||||
HEADERSDIR = $(TOPDIR)\src\include\opensc
|
||||
|
||||
@ -32,7 +32,7 @@ OBJECTS = \
|
||||
pkcs15-openpgp.obj pkcs15-infocamere.obj pkcs15-starcert.obj \
|
||||
pkcs15-tcos.obj pkcs15-esteid.obj pkcs15-postecert.obj pkcs15-gemsafeGPK.obj \
|
||||
pkcs15-actalis.obj pkcs15-atrust-acos.obj pkcs15-tccardos.obj pkcs15-piv.obj \
|
||||
pkcs15-rutoken.obj pkcs15-prkey-rutoken.obj \
|
||||
pkcs15-rutoken.obj \
|
||||
compression.obj p15card-helper.obj \
|
||||
versioninfo.res
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -184,8 +184,6 @@ sc_release_context
|
||||
sc_reset
|
||||
sc_reset_retry_counter
|
||||
sc_restore_security_env
|
||||
sc_rutoken_get_prkey_from_bin
|
||||
sc_rutoken_get_bin_from_prkey
|
||||
sc_select_file
|
||||
sc_set_card_driver
|
||||
sc_set_security_env
|
||||
|
@ -1,377 +0,0 @@
|
||||
/*
|
||||
* ruToken specific operation for PKCS #15 private key functions
|
||||
*
|
||||
* Copyright (C) 2007 Pavel Mironchik <rutoken@rutoken.ru>
|
||||
* Copyright (C) 2007 Eugene Hermann <rutoken@rutoken.ru>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <opensc/opensc.h>
|
||||
#include <opensc/pkcs15.h>
|
||||
#include "rutoken.h"
|
||||
#if defined(HAVE_INTTYPES_H)
|
||||
#include <inttypes.h>
|
||||
#elif defined(HAVE_STDINT_H)
|
||||
#include <stdint.h>
|
||||
#elif defined(_MSC_VER)
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
#else
|
||||
#warning no uint32_t type available, please contact opensc-devel@opensc-project.org
|
||||
#endif
|
||||
|
||||
/* BLOB definition */
|
||||
|
||||
typedef struct _RURSAPUBKEY {
|
||||
uint32_t magic;
|
||||
uint32_t bitlen;
|
||||
uint32_t pubexp;
|
||||
} RURSAPUBKEY;
|
||||
|
||||
typedef struct _RUPUBLICKEYSTRUC {
|
||||
u8 bType;
|
||||
u8 bVersion;
|
||||
uint16_t reserved;
|
||||
uint32_t aiKeyAlg;
|
||||
} RUBLOBHEADER;
|
||||
|
||||
typedef struct _RUPRIVATEKEYBLOB {
|
||||
RUBLOBHEADER blobheader;
|
||||
RURSAPUBKEY rsapubkey;
|
||||
u8 *modulus;
|
||||
u8 *prime1;
|
||||
u8 *prime2;
|
||||
u8 *exponent1;
|
||||
u8 *exponent2;
|
||||
u8 *coefficient;
|
||||
u8 *privateExponent;
|
||||
} RUPRIVATEKEYBLOB;
|
||||
|
||||
|
||||
static void ArrayReverse(u8 *buf, size_t size)
|
||||
{
|
||||
size_t i;
|
||||
u8 tmp;
|
||||
|
||||
for (i=0; i < size/2; ++i)
|
||||
{
|
||||
tmp = buf[i];
|
||||
buf[i] = buf[size-1-i];
|
||||
buf[size-1-i] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
static int free_private_blob(RUPRIVATEKEYBLOB *pr_blob)
|
||||
{
|
||||
free(pr_blob->modulus);
|
||||
free(pr_blob->prime1);
|
||||
free(pr_blob->prime2);
|
||||
free(pr_blob->exponent1);
|
||||
free(pr_blob->exponent2);
|
||||
free(pr_blob->coefficient);
|
||||
free(pr_blob->privateExponent);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bin_to_private_blob(RUPRIVATEKEYBLOB *pr_blob, const u8* buf, size_t buf_len)
|
||||
{
|
||||
const u8 *tmp;
|
||||
size_t len = 2 + sizeof(pr_blob->blobheader) + sizeof(pr_blob->rsapubkey);
|
||||
uint32_t bitlen;
|
||||
|
||||
if (buf_len < len)
|
||||
return -1;
|
||||
|
||||
tmp = buf + 2;
|
||||
memcpy(&pr_blob->blobheader, tmp, sizeof(pr_blob->blobheader));
|
||||
tmp += sizeof(pr_blob->blobheader);
|
||||
|
||||
memcpy(&pr_blob->rsapubkey, tmp, sizeof(pr_blob->rsapubkey));
|
||||
tmp += sizeof(pr_blob->rsapubkey);
|
||||
|
||||
bitlen = pr_blob->rsapubkey.bitlen;
|
||||
|
||||
len += bitlen/8 * 2 + bitlen/16 * 5;
|
||||
if (buf_len < len)
|
||||
return -1;
|
||||
|
||||
pr_blob->modulus = malloc(bitlen/8);
|
||||
pr_blob->prime1 = malloc(bitlen/16);
|
||||
pr_blob->prime2 = malloc(bitlen/16);
|
||||
pr_blob->exponent1 = malloc(bitlen/16);
|
||||
pr_blob->exponent2 = malloc(bitlen/16);
|
||||
pr_blob->coefficient = malloc(bitlen/16);
|
||||
pr_blob->privateExponent = malloc(bitlen/8);
|
||||
if (!pr_blob->modulus || !pr_blob->prime1 || !pr_blob->prime2
|
||||
|| !pr_blob->exponent1 || !pr_blob->exponent2
|
||||
|| !pr_blob->coefficient || !pr_blob->privateExponent
|
||||
)
|
||||
{
|
||||
free_private_blob(pr_blob);
|
||||
return -1;
|
||||
}
|
||||
memcpy(pr_blob->modulus, tmp, bitlen/8);
|
||||
tmp += bitlen/8;
|
||||
memcpy(pr_blob->prime1, tmp, bitlen/16);
|
||||
tmp += bitlen/16;
|
||||
memcpy(pr_blob->prime2, tmp, bitlen/16);
|
||||
tmp += bitlen/16;
|
||||
memcpy(pr_blob->exponent1, tmp, bitlen/16);
|
||||
tmp += bitlen/16;
|
||||
memcpy(pr_blob->exponent2, tmp, bitlen/16);
|
||||
tmp += bitlen/16;
|
||||
memcpy(pr_blob->coefficient, tmp, bitlen/16);
|
||||
tmp += bitlen/16;
|
||||
memcpy(pr_blob->privateExponent, tmp, bitlen/8);
|
||||
tmp += bitlen/8;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int create_private_blob(RUPRIVATEKEYBLOB *pr_blob, const struct sc_pkcs15_prkey_rsa *key)
|
||||
{
|
||||
size_t n;
|
||||
const uint32_t bitlen = key->modulus.len*8;
|
||||
|
||||
if ( key->modulus.len != bitlen/8
|
||||
|| key->p.len != bitlen/16
|
||||
|| key->q.len != bitlen/16
|
||||
|| key->dmp1.len != bitlen/16
|
||||
|| key->dmq1.len != bitlen/16
|
||||
|| key->iqmp.len != bitlen/16
|
||||
|| key->d.len != bitlen/8
|
||||
)
|
||||
return -1;
|
||||
|
||||
/* blobheader */
|
||||
/* u8 bType; */
|
||||
pr_blob->blobheader.bType = 0x07;
|
||||
/* u8 bVersion; */
|
||||
pr_blob->blobheader.bVersion = 0x02;
|
||||
/* u16 reserved; */
|
||||
pr_blob->blobheader.reserved = 0;
|
||||
/* u32 aiKeyAlg; */
|
||||
pr_blob->blobheader.aiKeyAlg = 0x0000a400;
|
||||
|
||||
pr_blob->rsapubkey.magic = 0x32415352; /* "RSA2" */
|
||||
pr_blob->rsapubkey.bitlen = bitlen;
|
||||
|
||||
pr_blob->rsapubkey.pubexp = 0;
|
||||
for (n=0; n < key->exponent.len && n < sizeof(pr_blob->rsapubkey.pubexp); ++n)
|
||||
pr_blob->rsapubkey.pubexp +=
|
||||
(uint32_t)key->exponent.data[key->exponent.len - n - 1] << 8*n;
|
||||
|
||||
pr_blob->modulus = malloc(bitlen/8);
|
||||
pr_blob->prime1 = malloc(bitlen/16);
|
||||
pr_blob->prime2 = malloc(bitlen/16);
|
||||
pr_blob->exponent1 = malloc(bitlen/16);
|
||||
pr_blob->exponent2 = malloc(bitlen/16);
|
||||
pr_blob->coefficient = malloc(bitlen/16);
|
||||
pr_blob->privateExponent = malloc(bitlen/8);
|
||||
if (!pr_blob->modulus || !pr_blob->prime1 || !pr_blob->prime2
|
||||
|| !pr_blob->exponent1 || !pr_blob->exponent2
|
||||
|| !pr_blob->coefficient || !pr_blob->privateExponent
|
||||
)
|
||||
{
|
||||
free_private_blob(pr_blob);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(pr_blob->modulus, key->modulus.data, key->modulus.len);
|
||||
ArrayReverse(pr_blob->modulus, key->modulus.len);
|
||||
memcpy(pr_blob->prime1, key->p.data, key->p.len);
|
||||
ArrayReverse(pr_blob->prime1, key->p.len);
|
||||
memcpy(pr_blob->prime2, key->q.data, key->q.len);
|
||||
ArrayReverse(pr_blob->prime2, key->q.len);
|
||||
memcpy(pr_blob->exponent1, key->dmp1.data, key->dmp1.len);
|
||||
ArrayReverse(pr_blob->exponent1, key->dmp1.len);
|
||||
memcpy(pr_blob->exponent2, key->dmq1.data, key->dmq1.len);
|
||||
ArrayReverse(pr_blob->exponent2, key->dmq1.len);
|
||||
memcpy(pr_blob->coefficient, key->iqmp.data, key->iqmp.len);
|
||||
ArrayReverse(pr_blob->coefficient, key->iqmp.len);
|
||||
memcpy(pr_blob->privateExponent, key->d.data, key->d.len);
|
||||
ArrayReverse(pr_blob->privateExponent, key->d.len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_sc_pksc15_prkey_rsa(const RUPRIVATEKEYBLOB *pr_blob, struct sc_pkcs15_prkey_rsa *key)
|
||||
{
|
||||
static const u8 Exp[3] = { 0x01, 0x00, 0x01 }; /* big endian */
|
||||
|
||||
const uint32_t bitlen = pr_blob->rsapubkey.bitlen;
|
||||
|
||||
key->modulus.data = malloc(bitlen/8);
|
||||
key->modulus.len = bitlen/8;
|
||||
key->p.data = malloc(bitlen/16);
|
||||
key->p.len = bitlen/16;
|
||||
key->q.data = malloc(bitlen/16);
|
||||
key->q.len = bitlen/16;
|
||||
key->dmp1.data = malloc(bitlen/16);
|
||||
key->dmp1.len = bitlen/16;
|
||||
key->dmq1.data = malloc(bitlen/16);
|
||||
key->dmq1.len = bitlen/16; /* ?! bitlen/16 - 1; */
|
||||
key->iqmp.data = malloc(bitlen/16);
|
||||
key->iqmp.len = bitlen/16;
|
||||
key->d.data = malloc(bitlen/8);
|
||||
key->d.len = bitlen/8;
|
||||
key->exponent.data = malloc(sizeof(Exp));
|
||||
key->exponent.len = sizeof(Exp);
|
||||
if(!key->modulus.data || !key->p.data || !key->q.data || !key->dmp1.data
|
||||
|| !key->dmq1.data || !key->iqmp.data || !key->d.data
|
||||
|| !key->exponent.data
|
||||
)
|
||||
{
|
||||
free(key->modulus.data);
|
||||
free(key->p.data);
|
||||
free(key->q.data);
|
||||
free(key->dmp1.data);
|
||||
free(key->dmq1.data);
|
||||
free(key->iqmp.data);
|
||||
free(key->d.data);
|
||||
free(key->exponent.data);
|
||||
memset(key, 0, sizeof(*key));
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(key->exponent.data, &Exp, sizeof(Exp));
|
||||
memcpy(key->modulus.data, pr_blob->modulus, key->modulus.len);
|
||||
ArrayReverse(key->modulus.data, key->modulus.len);
|
||||
memcpy(key->p.data, pr_blob->prime1, key->p.len);
|
||||
ArrayReverse(key->p.data, key->p.len);
|
||||
memcpy(key->q.data, pr_blob->prime2, key->q.len);
|
||||
ArrayReverse(key->q.data, key->q.len);
|
||||
memcpy(key->dmp1.data, pr_blob->exponent1, key->dmp1.len);
|
||||
ArrayReverse(key->dmp1.data, key->dmp1.len);
|
||||
memcpy(key->dmq1.data, pr_blob->exponent2, key->dmq1.len);
|
||||
ArrayReverse(key->dmq1.data, key->dmq1.len);
|
||||
memcpy(key->iqmp.data, pr_blob->coefficient, key->iqmp.len);
|
||||
ArrayReverse(key->iqmp.data, key->iqmp.len);
|
||||
memcpy(key->d.data, pr_blob->privateExponent, key->d.len);
|
||||
ArrayReverse(key->d.data, key->d.len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int private_blob_to_bin(const RUPRIVATEKEYBLOB *pr_blob, u8 *buf, size_t *buf_len)
|
||||
{
|
||||
u8 *tmp;
|
||||
size_t len = 2 + sizeof(pr_blob->blobheader) + sizeof(pr_blob->rsapubkey);
|
||||
|
||||
if(*buf_len < len)
|
||||
return -1;
|
||||
|
||||
buf[0] = 2;
|
||||
buf[1] = 1;
|
||||
tmp = buf + 2;
|
||||
memcpy(tmp, &pr_blob->blobheader, sizeof(pr_blob->blobheader));
|
||||
tmp += sizeof(pr_blob->blobheader);
|
||||
|
||||
memcpy(tmp, &pr_blob->rsapubkey, sizeof(pr_blob->rsapubkey));
|
||||
tmp += sizeof(pr_blob->rsapubkey);
|
||||
|
||||
len += pr_blob->rsapubkey.bitlen/8 * 2 + pr_blob->rsapubkey.bitlen/16 * 5;
|
||||
if (*buf_len < len)
|
||||
return -1;
|
||||
|
||||
memcpy(tmp, pr_blob->modulus, pr_blob->rsapubkey.bitlen/8);
|
||||
tmp += pr_blob->rsapubkey.bitlen/8;
|
||||
|
||||
memcpy(tmp, pr_blob->prime1, pr_blob->rsapubkey.bitlen/16);
|
||||
tmp += pr_blob->rsapubkey.bitlen/16;
|
||||
|
||||
memcpy(tmp, pr_blob->prime2, pr_blob->rsapubkey.bitlen/16);
|
||||
tmp += pr_blob->rsapubkey.bitlen/16;
|
||||
|
||||
memcpy(tmp, pr_blob->exponent1, pr_blob->rsapubkey.bitlen/16);
|
||||
tmp += pr_blob->rsapubkey.bitlen/16;
|
||||
|
||||
memcpy(tmp, pr_blob->exponent2, pr_blob->rsapubkey.bitlen/16);
|
||||
tmp += pr_blob->rsapubkey.bitlen/16;
|
||||
|
||||
memcpy(tmp, pr_blob->coefficient, pr_blob->rsapubkey.bitlen/16);
|
||||
tmp += pr_blob->rsapubkey.bitlen/16;
|
||||
|
||||
memcpy(tmp, pr_blob->privateExponent, pr_blob->rsapubkey.bitlen/8);
|
||||
tmp += pr_blob->rsapubkey.bitlen/8;
|
||||
|
||||
*buf_len = len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clean_prkey_private_blob(const RUPRIVATEKEYBLOB* pr_blob)
|
||||
{
|
||||
const uint32_t bitlen = pr_blob->rsapubkey.bitlen;
|
||||
|
||||
memset(pr_blob->modulus, 0, bitlen/8);
|
||||
memset(pr_blob->prime1, 0, bitlen/16);
|
||||
memset(pr_blob->prime2, 0, bitlen/16);
|
||||
memset(pr_blob->exponent1, 0, bitlen/16);
|
||||
memset(pr_blob->exponent2, 0, bitlen/16);
|
||||
memset(pr_blob->coefficient, 0, bitlen/16);
|
||||
memset(pr_blob->privateExponent, 0, bitlen/8);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sc_rutoken_get_prkey_from_bin(const u8 *data, size_t len, struct sc_pkcs15_prkey **key)
|
||||
{
|
||||
int ret = -1;
|
||||
RUPRIVATEKEYBLOB pr_blob;
|
||||
|
||||
if (data && key)
|
||||
{
|
||||
*key = malloc(sizeof(struct sc_pkcs15_prkey));
|
||||
if (*key)
|
||||
{
|
||||
memset(*key, 0, sizeof(**key));
|
||||
ret = bin_to_private_blob(&pr_blob, data, len);
|
||||
if (ret == 0)
|
||||
{
|
||||
ret = get_sc_pksc15_prkey_rsa(&pr_blob, &(*key)->u.rsa);
|
||||
if (ret == 0)
|
||||
(*key)->algorithm = SC_ALGORITHM_RSA;
|
||||
clean_prkey_private_blob(&pr_blob);
|
||||
free_private_blob(&pr_blob);
|
||||
memset(&pr_blob, 0, sizeof(pr_blob));
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sc_rutoken_get_bin_from_prkey(const struct sc_pkcs15_prkey_rsa *rsa, u8 *key, size_t *keysize)
|
||||
{
|
||||
int r = -1;
|
||||
RUPRIVATEKEYBLOB prkeyblob;
|
||||
|
||||
if (rsa && key && keysize)
|
||||
{
|
||||
r = create_private_blob(&prkeyblob, rsa);
|
||||
if (r == 0)
|
||||
{
|
||||
r = private_blob_to_bin(&prkeyblob, key, keysize);
|
||||
clean_prkey_private_blob(&prkeyblob);
|
||||
free_private_blob(&prkeyblob);
|
||||
memset(&prkeyblob, 0, sizeof(prkeyblob));
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* PKCS15 emulation layer for ruToken
|
||||
* PKCS15 emulation layer for Rutoken
|
||||
*
|
||||
* Copyright (C) 2007 Pavel Mironchik <rutoken@rutoken.ru>
|
||||
* Copyright (C) 2007 Eugene Hermann <rutoken@rutoken.ru>
|
||||
@ -28,17 +28,15 @@
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <opensc/opensc.h>
|
||||
#include "cardctl.h"
|
||||
#include <opensc/log.h>
|
||||
#include <opensc/pkcs15.h>
|
||||
|
||||
#define RUT_LABEL "ruToken card"
|
||||
#include "cardctl.h"
|
||||
|
||||
#define PrKDF_path "3F00FF000001"
|
||||
#define PuKDF_path "3F00FF000002"
|
||||
#define CDF_path "3F00FF000003"
|
||||
#define DODF_path "3F00FF000004"
|
||||
#define AODF_path "3F00FF00A0DF"
|
||||
#define AODF_path "3F00FF000000"
|
||||
|
||||
static const struct
|
||||
{
|
||||
@ -82,8 +80,8 @@ static int add_predefined_pin(sc_pkcs15_card_t *p15card, sc_path_t *adf_path)
|
||||
{
|
||||
free(pin_info);
|
||||
free(pin_obj);
|
||||
return SC_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
return SC_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
pin_info->auth_id.len = 1;
|
||||
pin_info->auth_id.value[0] = (u8)pinlist[i].reference;
|
||||
pin_info->reference = pinlist[i].reference;
|
||||
@ -97,8 +95,7 @@ static int add_predefined_pin(sc_pkcs15_card_t *p15card, sc_path_t *adf_path)
|
||||
pin_info->path = *adf_path;
|
||||
|
||||
strncpy(pin_obj->label, pinlist[i].label, SC_PKCS15_MAX_LABEL_SIZE - 1);
|
||||
pin_obj->flags = SC_PKCS15_CO_FLAG_PRIVATE;
|
||||
|
||||
pin_obj->flags = SC_PKCS15_CO_FLAG_PRIVATE;
|
||||
sc_pkcs15emu_add_pin_obj(p15card, pin_obj, pin_info);
|
||||
free(pin_obj);
|
||||
free(pin_info);
|
||||
@ -119,7 +116,7 @@ static int set_card_info(sc_pkcs15_card_t *p15card)
|
||||
sc_serial_number_t serialnr;
|
||||
char serial[30] = {0};
|
||||
u8 info[8];
|
||||
|
||||
|
||||
/* get the card serial number */
|
||||
if (sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serialnr) < 0)
|
||||
{
|
||||
@ -128,21 +125,19 @@ static int set_card_info(sc_pkcs15_card_t *p15card)
|
||||
}
|
||||
sc_bin_to_hex(serialnr.value, serialnr.len , serial, sizeof(serial), 0);
|
||||
set_string(&p15card->serial_number, serial);
|
||||
|
||||
/* get ruToken information */
|
||||
if (sc_card_ctl(card, SC_CARDCTL_RUTOKEN_GET_INFO, info) < 0)
|
||||
{
|
||||
sc_debug(ctx, "Unable to get token information\n");
|
||||
return SC_ERROR_WRONG_CARD;
|
||||
}
|
||||
set_string(&p15card->label, RUT_LABEL);
|
||||
set_string(&p15card->label, card->name);
|
||||
p15card->version = (info[1] >> 4)*10 + (info[1] & 0x0f);
|
||||
sc_bin_to_hex(info + 3, 3 , serial, sizeof(serial), 0);
|
||||
set_string(&p15card->manufacturer_id, serial);
|
||||
|
||||
return SC_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static int sc_pkcs15_rutoken_init_func(sc_pkcs15_card_t *p15card)
|
||||
{
|
||||
sc_context_t *ctx;
|
||||
@ -152,25 +147,23 @@ static int sc_pkcs15_rutoken_init_func(sc_pkcs15_card_t *p15card)
|
||||
size_t i;
|
||||
int r;
|
||||
unsigned int added_pin = 0;
|
||||
|
||||
|
||||
if (!p15card || !p15card->card || !p15card->card->ctx
|
||||
|| !p15card->card->ops
|
||||
|| !p15card->card->ops->select_file
|
||||
)
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
|
||||
card = p15card->card;
|
||||
ctx = card->ctx;
|
||||
|
||||
r = set_card_info(p15card);
|
||||
if (r != SC_SUCCESS)
|
||||
{
|
||||
{
|
||||
sc_error(ctx, "Unable to set card info: %s\n", sc_strerror(r));
|
||||
r = SC_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < sizeof(arr_profile_df)/sizeof(arr_profile_df[0]); ++i)
|
||||
{
|
||||
{
|
||||
df = NULL;
|
||||
sc_format_path(arr_profile_df[i].path, &path);
|
||||
if (card->ops->select_file(card, &path, &df) == SC_ERROR_FILE_NOT_FOUND)
|
||||
@ -181,7 +174,7 @@ static int sc_pkcs15_rutoken_init_func(sc_pkcs15_card_t *p15card)
|
||||
if (r == SC_SUCCESS)
|
||||
r = sc_pkcs15_add_df(p15card, arr_profile_df[i].type, &path, df);
|
||||
if (df)
|
||||
sc_file_free(df);
|
||||
sc_file_free(df);
|
||||
|
||||
if (r != SC_SUCCESS) break;
|
||||
|
||||
@ -199,17 +192,16 @@ static int sc_pkcs15_rutoken_init_func(sc_pkcs15_card_t *p15card)
|
||||
return r;
|
||||
}
|
||||
|
||||
int sc_pkcs15emu_rutoken_init_ex(sc_pkcs15_card_t *p15card,
|
||||
sc_pkcs15emu_opt_t *opts)
|
||||
int sc_pkcs15emu_rutoken_init_ex(sc_pkcs15_card_t *p15card,
|
||||
sc_pkcs15emu_opt_t *opts)
|
||||
{
|
||||
struct sc_card *card = p15card->card;
|
||||
|
||||
|
||||
SC_FUNC_CALLED(card->ctx, 1);
|
||||
|
||||
/* check if we have the correct card OS */
|
||||
if (strcmp(card->name, "rutoken card"))
|
||||
if (strcmp(card->name, "Rutoken card"))
|
||||
return SC_ERROR_WRONG_CARD;
|
||||
|
||||
sc_debug(card->ctx, "%s found", card->name);
|
||||
return sc_pkcs15_rutoken_init_func(p15card);
|
||||
}
|
||||
|
||||
|
@ -1,7 +0,0 @@
|
||||
#ifndef RUTOKEN_H
|
||||
#define RUTOKEN_H
|
||||
|
||||
int sc_rutoken_get_prkey_from_bin(const u8 *data, size_t len, struct sc_pkcs15_prkey **key);
|
||||
int sc_rutoken_get_bin_from_prkey(const struct sc_pkcs15_prkey_rsa *rsa, u8 *key, size_t *keysize);
|
||||
|
||||
#endif
|
@ -24,7 +24,7 @@ dist_pkgdata_DATA = \
|
||||
rutoken.profile \
|
||||
asepcos.profile
|
||||
|
||||
DEFS = -DSC_PKCS15_PROFILE_DIRECTORY=\"$(pkgdatadir)\"
|
||||
AM_CPPFLAGS = -DSC_PKCS15_PROFILE_DIRECTORY=\"$(pkgdatadir)\"
|
||||
AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(LTLIB_CFLAGS)
|
||||
INCLUDES = -I$(top_srcdir)/src/common -I$(top_builddir)/src/include
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* ruToken specific operation for PKCS15 initialization
|
||||
* Rutoken specific operation for PKCS15 initialization
|
||||
*
|
||||
* Copyright (C) 2007 Pavel Mironchik <rutoken@rutoken.ru>
|
||||
* Copyright (C) 2007 Eugene Hermann <rutoken@rutoken.ru>
|
||||
@ -18,10 +18,20 @@
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#if defined(HAVE_INTTYPES_H)
|
||||
#include <inttypes.h>
|
||||
#elif defined(HAVE_STDINT_H)
|
||||
#include <stdint.h>
|
||||
#elif defined(_MSC_VER)
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
#else
|
||||
#warning no uint32_t type available, please contact opensc-devel@opensc-project.org
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -31,11 +41,14 @@
|
||||
#include <opensc/cardctl.h>
|
||||
#include <opensc/log.h>
|
||||
#include <opensc/pkcs15.h>
|
||||
#include <opensc/rutoken.h>
|
||||
#include "pkcs15-init.h"
|
||||
#include "profile.h"
|
||||
|
||||
#define MAX_ID 255
|
||||
#define RUTOKEN_MIN_ID_PRKEY 0x0000
|
||||
#define RUTOKEN_MAX_ID_PRKEY 0x00FF
|
||||
#define RUTOKEN_MIN_ID_OTHER (RUTOKEN_MAX_ID_PRKEY + 1)
|
||||
#define RUTOKEN_MAX_ID_OTHER 0x0FFF
|
||||
#define RUTOKEN_BUFLEN_LISTFILES (2 * (RUTOKEN_MAX_ID_OTHER + 1))
|
||||
|
||||
static const sc_SecAttrV2_t pr_sec_attr = {0x43, 1, 1, 0, 0, 0, 0, 1, 2, 2, 0, 0, 0, 0, 2};
|
||||
static const sc_SecAttrV2_t pb_sec_attr = {0x42, 0, 1, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 2};
|
||||
@ -47,10 +60,10 @@ static const sc_SecAttrV2_t p1_sec_attr = {0x43, 1, 1, 0, 0, 0, 0,-1, 1, 1, 0, 0
|
||||
|
||||
enum DF_IDs
|
||||
{
|
||||
PrKDFid = 0x1001,
|
||||
PuKDFid = 0x1002,
|
||||
CDFid = 0x1003,
|
||||
DODFid = 0x1004,
|
||||
PrKDFid = 0x0000,
|
||||
PuKDFid = 0x0000,
|
||||
CDFid = 0x0000,
|
||||
DODFid = 0x0000,
|
||||
AODFid = 0xFFFF
|
||||
};
|
||||
|
||||
@ -75,40 +88,76 @@ static const struct
|
||||
{ AODF_name, AODFid, SC_PKCS15_AODF }
|
||||
};
|
||||
|
||||
/*
|
||||
* Create/override new EF.
|
||||
*/
|
||||
static int rutoken_create_file(sc_card_t *card, sc_path_t *path, sc_file_t *ef)
|
||||
|
||||
static int rutoken_get_bin_from_prkey(const struct sc_pkcs15_prkey_rsa *rsa,
|
||||
u8 *bufkey, size_t *bufkey_size)
|
||||
{
|
||||
int ret = SC_SUCCESS;
|
||||
sc_path_t path_dir;
|
||||
const uint32_t bitlen = rsa->modulus.len * 8;
|
||||
size_t i, len;
|
||||
|
||||
SC_FUNC_CALLED(card->ctx, 1);
|
||||
if ( rsa->modulus.len != bitlen/8
|
||||
|| rsa->p.len != bitlen/16
|
||||
|| rsa->q.len != bitlen/16
|
||||
|| rsa->dmp1.len != bitlen/16
|
||||
|| rsa->dmq1.len != bitlen/16
|
||||
|| rsa->iqmp.len != bitlen/16
|
||||
|| rsa->d.len != bitlen/8
|
||||
|| rsa->exponent.len > sizeof(uint32_t)
|
||||
)
|
||||
return -1;
|
||||
|
||||
if (path)
|
||||
{
|
||||
sc_ctx_suppress_errors_on(card->ctx);
|
||||
ret = card->ops->select_file(card, path, NULL);
|
||||
sc_ctx_suppress_errors_off(card->ctx);
|
||||
if (ret == SC_SUCCESS)
|
||||
{
|
||||
sc_path_t del_path;
|
||||
del_path.len = 2;
|
||||
del_path.type = SC_PATH_TYPE_FILE_ID;
|
||||
del_path.value[0] = (u8)(ef->id / 256);
|
||||
del_path.value[1] = (u8)(ef->id % 256);
|
||||
if (card->ops->select_file(card, &del_path, NULL) == SC_SUCCESS)
|
||||
ret = card->ops->delete_file(card, &del_path);
|
||||
}
|
||||
path_dir = *path;
|
||||
path_dir.len -= 2;
|
||||
ret = card->ops->select_file(card, &path_dir, NULL);
|
||||
}
|
||||
if (ret == SC_SUCCESS)
|
||||
{
|
||||
ret = card->ops->create_file(card, ef);
|
||||
}
|
||||
return ret;
|
||||
if (*bufkey_size < 14 + sizeof(uint32_t) * 2 + bitlen/8 * 2 + bitlen/16 * 5)
|
||||
return -1;
|
||||
|
||||
bufkey[0] = 2;
|
||||
bufkey[1] = 1;
|
||||
|
||||
/* BLOB header */
|
||||
bufkey[2] = 0x07; /* Type */
|
||||
bufkey[3] = 0x02; /* Version */
|
||||
/* reserve */
|
||||
bufkey[4] = 0;
|
||||
bufkey[5] = 0;
|
||||
/* aiKeyAlg */
|
||||
bufkey[6] = 0;
|
||||
bufkey[7] = 0xA4;
|
||||
bufkey[8] = 0;
|
||||
bufkey[9] = 0;
|
||||
|
||||
/* RSAPUBKEY */
|
||||
/* magic "RSA2" */
|
||||
bufkey[10] = 0x52;
|
||||
bufkey[11] = 0x53;
|
||||
bufkey[12] = 0x41;
|
||||
bufkey[13] = 0x32;
|
||||
len = 14;
|
||||
/* bitlen */
|
||||
for (i = 0; i < sizeof(uint32_t); ++i)
|
||||
bufkey[len++] = (bitlen >> i*8) & 0xff;
|
||||
/* pubexp */
|
||||
for (i = 0; i < sizeof(uint32_t); ++i)
|
||||
if (i < rsa->exponent.len)
|
||||
bufkey[len++] = rsa->exponent.data[rsa->exponent.len - 1 - i];
|
||||
else
|
||||
bufkey[len++] = 0;
|
||||
|
||||
#define MEMCPY_BUF_REVERSE_RSA(NAME) \
|
||||
do { \
|
||||
for (i = 0; i < rsa->NAME.len; ++i) \
|
||||
bufkey[len++] = rsa->NAME.data[rsa->NAME.len - 1 - i]; \
|
||||
} while (0)
|
||||
|
||||
/* PRIVATEKEYBLOB tail */
|
||||
MEMCPY_BUF_REVERSE_RSA(modulus); /* modulus */
|
||||
MEMCPY_BUF_REVERSE_RSA(p); /* prime1 */
|
||||
MEMCPY_BUF_REVERSE_RSA(q); /* prime2 */
|
||||
MEMCPY_BUF_REVERSE_RSA(dmp1); /* exponent1 */
|
||||
MEMCPY_BUF_REVERSE_RSA(dmq1); /* exponent2 */
|
||||
MEMCPY_BUF_REVERSE_RSA(iqmp); /* coefficient */
|
||||
MEMCPY_BUF_REVERSE_RSA(d); /* privateExponent */
|
||||
|
||||
*bufkey_size = len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -177,7 +226,8 @@ rutoken_select_key_reference(sc_profile_t *profile, sc_card_t *card,
|
||||
}
|
||||
sc_append_file_id(&key_info->path, key_info->key_reference);
|
||||
|
||||
return key_info->key_reference >= 0 && key_info->key_reference <= MAX_ID ?
|
||||
return key_info->key_reference >= RUTOKEN_MIN_ID_PRKEY
|
||||
&& key_info->key_reference <= RUTOKEN_MAX_ID_PRKEY ?
|
||||
SC_SUCCESS : SC_ERROR_TOO_MANY_OBJECTS;
|
||||
}
|
||||
|
||||
@ -195,36 +245,6 @@ rutoken_create_key(sc_profile_t *profile, sc_card_t *card,
|
||||
return SC_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create private key files
|
||||
*/
|
||||
static int rutoken_create_prkeyfile(sc_card_t *card,
|
||||
sc_pkcs15_prkey_info_t *key_info, size_t prsize)
|
||||
{
|
||||
sc_path_t path;
|
||||
sc_file_t *file;
|
||||
int ret;
|
||||
|
||||
SC_FUNC_CALLED(card->ctx, 1);
|
||||
|
||||
file = sc_file_new();
|
||||
if (file)
|
||||
{
|
||||
/* create key file */
|
||||
path = key_info->path;
|
||||
file->type = SC_FILE_TYPE_WORKING_EF;
|
||||
file->id = key_info->key_reference;
|
||||
file->size = prsize;
|
||||
sc_file_set_sec_attr(file, (u8*)&pr_sec_attr, SEC_ATTR_SIZE);
|
||||
ret = rutoken_create_file(card, &path, file);
|
||||
if (file)
|
||||
sc_file_free(file);
|
||||
}
|
||||
else
|
||||
ret = SC_ERROR_OUT_OF_MEMORY;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Store a private key object.
|
||||
*/
|
||||
@ -237,6 +257,7 @@ rutoken_store_key(sc_profile_t *profile, sc_card_t *card,
|
||||
const int nKeyBufSize = 2048;
|
||||
u8 *prkeybuf = NULL;
|
||||
size_t prsize;
|
||||
sc_file_t *file;
|
||||
int ret;
|
||||
|
||||
if (!profile || !profile->ops || !card || !card->ctx
|
||||
@ -264,12 +285,21 @@ rutoken_store_key(sc_profile_t *profile, sc_card_t *card,
|
||||
ret = profile->ops->encode_private_key(profile, card, &key->u.rsa, prkeybuf, &prsize, 0);
|
||||
if (ret == 0)
|
||||
{
|
||||
ret = rutoken_create_prkeyfile(card, key_info, prsize);
|
||||
if (ret == 0)
|
||||
file = sc_file_new();
|
||||
if (!file)
|
||||
ret = SC_ERROR_OUT_OF_MEMORY;
|
||||
else
|
||||
{
|
||||
ret = sc_update_binary(card, 0, prkeybuf, prsize, 0);
|
||||
if (ret < 0 || (size_t)ret != prsize)
|
||||
sc_debug(card->ctx, "ret=%i (%u)\n", ret, prsize);
|
||||
/* create (or update) key file */
|
||||
file->path = key_info->path;
|
||||
file->type = SC_FILE_TYPE_WORKING_EF;
|
||||
file->id = key_info->key_reference;
|
||||
file->size = prsize;
|
||||
sc_file_set_sec_attr(file, (u8*)&pr_sec_attr, SEC_ATTR_SIZE);
|
||||
|
||||
ret = sc_pkcs15init_update_file(profile, card,
|
||||
file, prkeybuf, prsize);
|
||||
sc_file_free(file);
|
||||
}
|
||||
memset(prkeybuf, 0, prsize);
|
||||
}
|
||||
@ -291,7 +321,7 @@ rutoken_encode_private_key(sc_profile_t *profile, sc_card_t *card,
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
|
||||
SC_FUNC_CALLED(card->ctx, 1);
|
||||
r = sc_rutoken_get_bin_from_prkey(rsa, key, keysize);
|
||||
r = rutoken_get_bin_from_prkey(rsa, key, keysize);
|
||||
sc_debug(card->ctx, "sc_rutoken_get_bin_from_prkey returned %i\n", r);
|
||||
return r;
|
||||
}
|
||||
@ -299,7 +329,7 @@ rutoken_encode_private_key(sc_profile_t *profile, sc_card_t *card,
|
||||
static int rutoken_id_in(int id, const u8 *buf, int buflen)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i*2 < buflen; i++)
|
||||
for (i = 0; i*2 + 1 < buflen; i++)
|
||||
if (id == (int)buf[i*2] * 0x100 + buf[i*2 + 1]) return 1;
|
||||
return 0;
|
||||
}
|
||||
@ -308,7 +338,7 @@ static int rutoken_find_id(sc_card_t *card, const sc_path_t *path)
|
||||
{
|
||||
int ret = SC_SUCCESS;
|
||||
sc_file_t *file = NULL;
|
||||
u8 *files = malloc(2048);
|
||||
u8 *files = malloc(RUTOKEN_BUFLEN_LISTFILES);
|
||||
if (!files) return SC_ERROR_OUT_OF_MEMORY;
|
||||
if(path)
|
||||
{
|
||||
@ -317,11 +347,11 @@ static int rutoken_find_id(sc_card_t *card, const sc_path_t *path)
|
||||
}
|
||||
if(ret == SC_SUCCESS)
|
||||
{
|
||||
ret = card->ops->list_files(card, files, 2048);
|
||||
ret = card->ops->list_files(card, files, RUTOKEN_BUFLEN_LISTFILES);
|
||||
if(ret >= 0)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < MAX_ID; i++)
|
||||
for (i = RUTOKEN_MIN_ID_OTHER; i <= RUTOKEN_MAX_ID_OTHER; i++)
|
||||
if(!rutoken_id_in(i, files, ret)) {ret = i; break;}
|
||||
}
|
||||
}
|
||||
@ -600,7 +630,13 @@ static int rutoken_init(sc_profile_t *profile, sc_card_t *card)
|
||||
df->id = arr_def_df[i].dir;
|
||||
r = sc_append_file_id(&df->path, df->id);
|
||||
if (r == SC_SUCCESS)
|
||||
{
|
||||
sc_ctx_suppress_errors_on(card->ctx);
|
||||
r = card->ops->create_file(card, df);
|
||||
sc_ctx_suppress_errors_off(card->ctx);
|
||||
if (r == SC_ERROR_FILE_ALREADY_EXISTS)
|
||||
r = SC_SUCCESS;
|
||||
}
|
||||
if (r != SC_SUCCESS)
|
||||
sc_error(card->ctx, "Failed to create df, %s\n",
|
||||
sc_strerror(r));
|
||||
|
@ -13,20 +13,20 @@ pkcs15 {
|
||||
# Put the DF length into the ODF file?
|
||||
encode-df-length = no;
|
||||
# Have a lastUpdate field in the EF(TokenInfo)?
|
||||
do-last-update = yes;
|
||||
do-last-update = no;
|
||||
}
|
||||
|
||||
# Default settings.
|
||||
# This option block will always be processed.
|
||||
option default_32k {
|
||||
macros {
|
||||
macros {
|
||||
odf-size = 0;
|
||||
aodf-size = 0;
|
||||
dodf-size = 2048;
|
||||
cdf-size = 2048;
|
||||
prkdf-size = 2048;
|
||||
pukdf-size = 2048;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# This option is for cards with very little memory.
|
||||
@ -45,45 +45,45 @@ option default {
|
||||
}
|
||||
|
||||
filesystem {
|
||||
DF MF {
|
||||
DF MF {
|
||||
path = 3F00;
|
||||
type = DF;
|
||||
|
||||
type = DF;
|
||||
|
||||
# Here comes the application DF
|
||||
DF PKCS15-AppDF {
|
||||
type = DF;
|
||||
type = DF;
|
||||
file-id = FF00;
|
||||
|
||||
EF PKCS15-ODF {
|
||||
file-id = 00DF;
|
||||
size = $odf-size;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
EF PKCS15-AODF {
|
||||
file-id = A0DF;
|
||||
size = $aodf-size;
|
||||
}
|
||||
|
||||
EF PKCS15-PrKDF {
|
||||
file-id = 0001;
|
||||
file-id = 0001;
|
||||
size = $prkdf-size;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
EF PKCS15-PuKDF {
|
||||
file-id = 0002;
|
||||
file-id = 0002;
|
||||
size = $pukdf-size;
|
||||
}
|
||||
}
|
||||
|
||||
EF PKCS15-CDF {
|
||||
file-id = 0003;
|
||||
size = $cdf-size;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
EF PKCS15-DODF {
|
||||
file-id = 0004;
|
||||
size = $dodf-size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,6 @@
|
||||
#include <opensc/opensc.h>
|
||||
#include <opensc/cardctl.h>
|
||||
#include <opensc/pkcs15.h>
|
||||
#include <opensc/rutoken.h>
|
||||
#include "util.h"
|
||||
|
||||
#define IV_SIZE 8
|
||||
|
Loading…
Reference in New Issue
Block a user