e_gmp.c revision 160814
154359Sroberto/* crypto/engine/e_gmp.c */ 254359Sroberto/* Written by Geoff Thorpe (geoff@geoffthorpe.net) for the OpenSSL 354359Sroberto * project 2003. 454359Sroberto */ 554359Sroberto/* ==================================================================== 654359Sroberto * Copyright (c) 1999-2001 The OpenSSL Project. All rights reserved. 754359Sroberto * 854359Sroberto * Redistribution and use in source and binary forms, with or without 954359Sroberto * modification, are permitted provided that the following conditions 1054359Sroberto * are met: 1154359Sroberto * 1254359Sroberto * 1. Redistributions of source code must retain the above copyright 1354359Sroberto * notice, this list of conditions and the following disclaimer. 1454359Sroberto * 1554359Sroberto * 2. Redistributions in binary form must reproduce the above copyright 1654359Sroberto * notice, this list of conditions and the following disclaimer in 1754359Sroberto * the documentation and/or other materials provided with the 1854359Sroberto * distribution. 1954359Sroberto * 2054359Sroberto * 3. All advertising materials mentioning features or use of this 2154359Sroberto * software must display the following acknowledgment: 2254359Sroberto * "This product includes software developed by the OpenSSL Project 2354359Sroberto * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" 2454359Sroberto * 2554359Sroberto * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to 2654359Sroberto * endorse or promote products derived from this software without 2754359Sroberto * prior written permission. For written permission, please contact 2854359Sroberto * licensing@OpenSSL.org. 2954359Sroberto * 3054359Sroberto * 5. Products derived from this software may not be called "OpenSSL" 3154359Sroberto * nor may "OpenSSL" appear in their names without prior written 3254359Sroberto * permission of the OpenSSL Project. 3354359Sroberto * 3454359Sroberto * 6. Redistributions of any form whatsoever must retain the following 3554359Sroberto * acknowledgment: 3654359Sroberto * "This product includes software developed by the OpenSSL Project 3754359Sroberto * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" 3854359Sroberto * 3954359Sroberto * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY 4054359Sroberto * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 4154359Sroberto * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 4254359Sroberto * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR 4354359Sroberto * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 4454359Sroberto * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 4554359Sroberto * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 4654359Sroberto * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 4754359Sroberto * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 4854359Sroberto * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 4954359Sroberto * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 5054359Sroberto * OF THE POSSIBILITY OF SUCH DAMAGE. 5154359Sroberto * ==================================================================== 5254359Sroberto * 5354359Sroberto * This product includes cryptographic software written by Eric Young 5454359Sroberto * (eay@cryptsoft.com). This product includes software written by Tim 5554359Sroberto * Hudson (tjh@cryptsoft.com). 5654359Sroberto * 5754359Sroberto */ 5854359Sroberto 5954359Sroberto/* This engine is not (currently) compiled in by default. Do enable it, 6054359Sroberto * reconfigure OpenSSL with "-DOPENSSL_USE_GMP -lgmp". The GMP libraries and 6154359Sroberto * headers must reside in one of the paths searched by the compiler/linker, 6254359Sroberto * otherwise paths must be specified - eg. try configuring with 6354359Sroberto * "-DOPENSSL_USE_GMP -I<includepath> -L<libpath> -lgmp". YMMV. */ 6454359Sroberto 6554359Sroberto/* As for what this does - it's a largely unoptimised implementation of an 6654359Sroberto * ENGINE that uses the GMP library to perform RSA private key operations. To 6754359Sroberto * obtain more information about what "unoptimised" means, see my original mail 6854359Sroberto * on the subject (though ignore the build instructions which have since 6954359Sroberto * changed); 7054359Sroberto * 7154359Sroberto * http://www.mail-archive.com/openssl-dev@openssl.org/msg12227.html 7254359Sroberto * 7354359Sroberto * On my athlon system at least, it appears the builtin OpenSSL code is now 7454359Sroberto * slightly faster, which is to say that the RSA-related MPI performance 7554359Sroberto * between OpenSSL's BIGNUM and GMP's mpz implementations is probably pretty 7654359Sroberto * balanced for this chip, and so the performance degradation in this ENGINE by 7754359Sroberto * having to convert to/from GMP formats (and not being able to cache 7854359Sroberto * montgomery forms) is probably the difference. However, if some unconfirmed 7954359Sroberto * reports from users is anything to go by, the situation on some other 8054359Sroberto * chipsets might be a good deal more favourable to the GMP version (eg. PPC). 8154359Sroberto * Feedback welcome. */ 8254359Sroberto 8354359Sroberto#include <stdio.h> 8454359Sroberto#include <string.h> 8554359Sroberto#include <openssl/crypto.h> 8654359Sroberto#include <openssl/buffer.h> 8754359Sroberto#include <openssl/engine.h> 8854359Sroberto 8954359Sroberto#ifndef OPENSSL_NO_HW 9054359Sroberto#if defined(OPENSSL_USE_GMP) && !defined(OPENSSL_NO_HW_GMP) 9154359Sroberto 9254359Sroberto#include <gmp.h> 9354359Sroberto 9454359Sroberto#define E_GMP_LIB_NAME "gmp engine" 9554359Sroberto#include "e_gmp_err.c" 9654359Sroberto 9754359Srobertostatic int e_gmp_destroy(ENGINE *e); 9854359Srobertostatic int e_gmp_init(ENGINE *e); 9954359Srobertostatic int e_gmp_finish(ENGINE *e); 10054359Srobertostatic int e_gmp_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f)(void)); 10154359Sroberto 10254359Sroberto#ifndef OPENSSL_NO_RSA 10354359Sroberto/* RSA stuff */ 10454359Srobertostatic int e_gmp_rsa_mod_exp(BIGNUM *r, const BIGNUM *I, RSA *rsa, BN_CTX *ctx); 10554359Srobertostatic int e_gmp_rsa_finish(RSA *r); 10654359Sroberto#endif 10754359Sroberto 10854359Sroberto/* The definitions for control commands specific to this engine */ 10954359Sroberto/* #define E_GMP_CMD_SO_PATH ENGINE_CMD_BASE */ 11054359Srobertostatic const ENGINE_CMD_DEFN e_gmp_cmd_defns[] = { 11154359Sroberto#if 0 11254359Sroberto {E_GMP_CMD_SO_PATH, 11354359Sroberto "SO_PATH", 11454359Sroberto "Specifies the path to the 'e_gmp' shared library", 11554359Sroberto ENGINE_CMD_FLAG_STRING}, 11654359Sroberto#endif 11754359Sroberto {0, NULL, NULL, 0} 11854359Sroberto }; 11954359Sroberto 12054359Sroberto#ifndef OPENSSL_NO_RSA 12154359Sroberto/* Our internal RSA_METHOD that we provide pointers to */ 12254359Srobertostatic RSA_METHOD e_gmp_rsa = 12354359Sroberto { 12454359Sroberto "GMP RSA method", 12554359Sroberto NULL, 12654359Sroberto NULL, 12754359Sroberto NULL, 12854359Sroberto NULL, 12954359Sroberto e_gmp_rsa_mod_exp, 13054359Sroberto NULL, 13154359Sroberto NULL, 13254359Sroberto e_gmp_rsa_finish, 13354359Sroberto /* These flags initialise montgomery crud that GMP ignores, however it 13454359Sroberto * makes sure the public key ops (which are done in openssl) don't seem 13554359Sroberto * *slower* than usual :-) */ 13654359Sroberto RSA_FLAG_CACHE_PUBLIC|RSA_FLAG_CACHE_PRIVATE, 13754359Sroberto NULL, 13854359Sroberto NULL, 13954359Sroberto NULL 14054359Sroberto }; 14154359Sroberto#endif 14254359Sroberto 14354359Sroberto/* Constants used when creating the ENGINE */ 14454359Srobertostatic const char *engine_e_gmp_id = "gmp"; 14554359Srobertostatic const char *engine_e_gmp_name = "GMP engine support"; 14654359Sroberto 14754359Sroberto/* This internal function is used by ENGINE_gmp() and possibly by the 14854359Sroberto * "dynamic" ENGINE support too */ 14954359Srobertostatic int bind_helper(ENGINE *e) 15054359Sroberto { 15154359Sroberto#ifndef OPENSSL_NO_RSA 15254359Sroberto const RSA_METHOD *meth1; 15354359Sroberto#endif 15454359Sroberto if(!ENGINE_set_id(e, engine_e_gmp_id) || 15554359Sroberto !ENGINE_set_name(e, engine_e_gmp_name) || 15654359Sroberto#ifndef OPENSSL_NO_RSA 15754359Sroberto !ENGINE_set_RSA(e, &e_gmp_rsa) || 15854359Sroberto#endif 15954359Sroberto !ENGINE_set_destroy_function(e, e_gmp_destroy) || 16054359Sroberto !ENGINE_set_init_function(e, e_gmp_init) || 16154359Sroberto !ENGINE_set_finish_function(e, e_gmp_finish) || 16254359Sroberto !ENGINE_set_ctrl_function(e, e_gmp_ctrl) || 16354359Sroberto !ENGINE_set_cmd_defns(e, e_gmp_cmd_defns)) 16454359Sroberto return 0; 16554359Sroberto 16654359Sroberto#ifndef OPENSSL_NO_RSA 16754359Sroberto meth1 = RSA_PKCS1_SSLeay(); 16854359Sroberto e_gmp_rsa.rsa_pub_enc = meth1->rsa_pub_enc; 16954359Sroberto e_gmp_rsa.rsa_pub_dec = meth1->rsa_pub_dec; 17054359Sroberto e_gmp_rsa.rsa_priv_enc = meth1->rsa_priv_enc; 17154359Sroberto e_gmp_rsa.rsa_priv_dec = meth1->rsa_priv_dec; 17254359Sroberto e_gmp_rsa.bn_mod_exp = meth1->bn_mod_exp; 17354359Sroberto#endif 17454359Sroberto 17554359Sroberto /* Ensure the e_gmp error handling is set up */ 17654359Sroberto ERR_load_GMP_strings(); 17754359Sroberto return 1; 17854359Sroberto } 17954359Sroberto 18054359Srobertostatic ENGINE *engine_gmp(void) 18154359Sroberto { 18254359Sroberto ENGINE *ret = ENGINE_new(); 18354359Sroberto if(!ret) 18454359Sroberto return NULL; 18554359Sroberto if(!bind_helper(ret)) 18654359Sroberto { 18754359Sroberto ENGINE_free(ret); 18854359Sroberto return NULL; 18954359Sroberto } 19054359Sroberto return ret; 19154359Sroberto } 19254359Sroberto 19354359Srobertovoid ENGINE_load_gmp(void) 19454359Sroberto { 19554359Sroberto /* Copied from eng_[openssl|dyn].c */ 19654359Sroberto ENGINE *toadd = engine_gmp(); 19754359Sroberto if(!toadd) return; 19854359Sroberto ENGINE_add(toadd); 19954359Sroberto ENGINE_free(toadd); 20054359Sroberto ERR_clear_error(); 20154359Sroberto } 20254359Sroberto 20354359Sroberto#ifndef OPENSSL_NO_RSA 20454359Sroberto/* Used to attach our own key-data to an RSA structure */ 20554359Srobertostatic int hndidx_rsa = -1; 20654359Sroberto#endif 20754359Sroberto 20854359Srobertostatic int e_gmp_destroy(ENGINE *e) 20954359Sroberto { 21054359Sroberto ERR_unload_GMP_strings(); 21154359Sroberto return 1; 21254359Sroberto } 21354359Sroberto 21454359Sroberto/* (de)initialisation functions. */ 21554359Srobertostatic int e_gmp_init(ENGINE *e) 21654359Sroberto { 21754359Sroberto#ifndef OPENSSL_NO_RSA 21854359Sroberto if (hndidx_rsa == -1) 21954359Sroberto hndidx_rsa = RSA_get_ex_new_index(0, 22054359Sroberto "GMP-based RSA key handle", 22154359Sroberto NULL, NULL, NULL); 22254359Sroberto#endif 22354359Sroberto if (hndidx_rsa == -1) 22454359Sroberto return 0; 22554359Sroberto return 1; 22654359Sroberto } 22754359Sroberto 22854359Srobertostatic int e_gmp_finish(ENGINE *e) 22954359Sroberto { 23054359Sroberto return 1; 23154359Sroberto } 23254359Sroberto 23354359Srobertostatic int e_gmp_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f)(void)) 23454359Sroberto { 23554359Sroberto int to_return = 1; 23654359Sroberto 23754359Sroberto switch(cmd) 23854359Sroberto { 23954359Sroberto#if 0 24054359Sroberto case E_GMP_CMD_SO_PATH: 24154359Sroberto /* ... */ 24254359Sroberto#endif 24354359Sroberto /* The command isn't understood by this engine */ 24454359Sroberto default: 24554359Sroberto GMPerr(GMP_F_E_GMP_CTRL, 24654359Sroberto GMP_R_CTRL_COMMAND_NOT_IMPLEMENTED); 24754359Sroberto to_return = 0; 24854359Sroberto break; 24954359Sroberto } 25054359Sroberto 25154359Sroberto return to_return; 25254359Sroberto } 25354359Sroberto 25454359Sroberto/* HACK - use text I/O functions in openssl and GMP to handle conversions. This 25554359Sroberto * is vile. */ 25654359Srobertostatic int bn2gmp(const BIGNUM *bn, mpz_t g) 25754359Sroberto { 25854359Sroberto int toret; 25954359Sroberto char *tmpchar = BN_bn2hex(bn); 26054359Sroberto if(!tmpchar) return 0; 26154359Sroberto toret = (mpz_set_str(g, tmpchar, 16) == 0 ? 1 : 0); 26254359Sroberto OPENSSL_free(tmpchar); 26354359Sroberto return toret; 26454359Sroberto } 26554359Sroberto 26654359Srobertostatic int gmp2bn(mpz_t g, BIGNUM *bn) 26754359Sroberto { 26854359Sroberto int toret; 26954359Sroberto char *tmpchar = OPENSSL_malloc(mpz_sizeinbase(g, 16) + 10); 27054359Sroberto if(!tmpchar) return 0; 27154359Sroberto mpz_get_str(tmpchar, 16, g); 27254359Sroberto toret = BN_hex2bn(&bn, tmpchar); 27354359Sroberto OPENSSL_free(tmpchar); 27454359Sroberto return toret; 27554359Sroberto } 27654359Sroberto 27754359Sroberto#ifndef OPENSSL_NO_RSA 27854359Srobertotypedef struct st_e_gmp_rsa_ctx 27954359Sroberto { 28054359Sroberto int public_only; 28154359Sroberto mpz_t n; 28254359Sroberto mpz_t d; 28354359Sroberto mpz_t e; 28454359Sroberto mpz_t p; 28554359Sroberto mpz_t q; 28654359Sroberto mpz_t dmp1; 28754359Sroberto mpz_t dmq1; 28854359Sroberto mpz_t iqmp; 28954359Sroberto mpz_t r0, r1, I0, m1; 29054359Sroberto } E_GMP_RSA_CTX; 29154359Sroberto 29254359Srobertostatic E_GMP_RSA_CTX *e_gmp_get_rsa(RSA *rsa) 29354359Sroberto { 29454359Sroberto E_GMP_RSA_CTX *hptr = RSA_get_ex_data(rsa, hndidx_rsa); 29554359Sroberto if(hptr) return hptr; 29654359Sroberto hptr = OPENSSL_malloc(sizeof(E_GMP_RSA_CTX)); 29754359Sroberto if(!hptr) return NULL; 29854359Sroberto /* These inits could probably be replaced by more intelligent 29954359Sroberto * mpz_init2() versions, to reduce malloc-thrashing. */ 30054359Sroberto mpz_init(hptr->n); 30154359Sroberto mpz_init(hptr->d); 30254359Sroberto mpz_init(hptr->e); 30354359Sroberto mpz_init(hptr->p); 30454359Sroberto mpz_init(hptr->q); 30554359Sroberto mpz_init(hptr->dmp1); 30654359Sroberto mpz_init(hptr->dmq1); 30754359Sroberto mpz_init(hptr->iqmp); 30854359Sroberto mpz_init(hptr->r0); 30954359Sroberto mpz_init(hptr->r1); 31054359Sroberto mpz_init(hptr->I0); 31154359Sroberto mpz_init(hptr->m1); 31254359Sroberto if(!bn2gmp(rsa->n, hptr->n) || !bn2gmp(rsa->e, hptr->e)) 31354359Sroberto goto err; 31454359Sroberto if(!rsa->p || !rsa->q || !rsa->d || !rsa->dmp1 || !rsa->dmq1 || !rsa->iqmp) 31554359Sroberto { 31654359Sroberto hptr->public_only = 1; 31754359Sroberto return hptr; 31854359Sroberto } 31954359Sroberto if(!bn2gmp(rsa->d, hptr->d) || !bn2gmp(rsa->p, hptr->p) || 32054359Sroberto !bn2gmp(rsa->q, hptr->q) || !bn2gmp(rsa->dmp1, hptr->dmp1) || 32154359Sroberto !bn2gmp(rsa->dmq1, hptr->dmq1) || !bn2gmp(rsa->iqmp, hptr->iqmp)) 32254359Sroberto goto err; 32354359Sroberto hptr->public_only = 0; 32454359Sroberto RSA_set_ex_data(rsa, hndidx_rsa, hptr); 32554359Sroberto return hptr; 32654359Srobertoerr: 32754359Sroberto mpz_clear(hptr->n); 32854359Sroberto mpz_clear(hptr->d); 32954359Sroberto mpz_clear(hptr->e); 33054359Sroberto mpz_clear(hptr->p); 33154359Sroberto mpz_clear(hptr->q); 33254359Sroberto mpz_clear(hptr->dmp1); 33354359Sroberto mpz_clear(hptr->dmq1); 33454359Sroberto mpz_clear(hptr->iqmp); 33554359Sroberto mpz_clear(hptr->r0); 33654359Sroberto mpz_clear(hptr->r1); 33754359Sroberto mpz_clear(hptr->I0); 33854359Sroberto mpz_clear(hptr->m1); 33954359Sroberto OPENSSL_free(hptr); 34054359Sroberto return NULL; 34154359Sroberto } 34254359Sroberto 34354359Srobertostatic int e_gmp_rsa_finish(RSA *rsa) 34454359Sroberto { 34554359Sroberto E_GMP_RSA_CTX *hptr = RSA_get_ex_data(rsa, hndidx_rsa); 34654359Sroberto if(!hptr) return 0; 34754359Sroberto mpz_clear(hptr->n); 34854359Sroberto mpz_clear(hptr->d); 34954359Sroberto mpz_clear(hptr->e); 35054359Sroberto mpz_clear(hptr->p); 35154359Sroberto mpz_clear(hptr->q); 35254359Sroberto mpz_clear(hptr->dmp1); 35354359Sroberto mpz_clear(hptr->dmq1); 35454359Sroberto mpz_clear(hptr->iqmp); 35554359Sroberto mpz_clear(hptr->r0); 35654359Sroberto mpz_clear(hptr->r1); 35754359Sroberto mpz_clear(hptr->I0); 35854359Sroberto mpz_clear(hptr->m1); 35954359Sroberto OPENSSL_free(hptr); 36054359Sroberto RSA_set_ex_data(rsa, hndidx_rsa, NULL); 36154359Sroberto return 1; 36254359Sroberto } 36354359Sroberto 36454359Srobertostatic int e_gmp_rsa_mod_exp(BIGNUM *r, const BIGNUM *I, RSA *rsa, BN_CTX *ctx) 36554359Sroberto { 36654359Sroberto E_GMP_RSA_CTX *hptr; 36754359Sroberto int to_return = 0; 36854359Sroberto 36954359Sroberto hptr = e_gmp_get_rsa(rsa); 37054359Sroberto if(!hptr) 37154359Sroberto { 37254359Sroberto GMPerr(GMP_F_E_GMP_RSA_MOD_EXP, 37354359Sroberto GMP_R_KEY_CONTEXT_ERROR); 37454359Sroberto return 0; 37554359Sroberto } 37654359Sroberto if(hptr->public_only) 37754359Sroberto { 37854359Sroberto GMPerr(GMP_F_E_GMP_RSA_MOD_EXP, 37954359Sroberto GMP_R_MISSING_KEY_COMPONENTS); 38054359Sroberto return 0; 38154359Sroberto } 38254359Sroberto 38354359Sroberto /* ugh!!! */ 38454359Sroberto if(!bn2gmp(I, hptr->I0)) 38554359Sroberto return 0; 38654359Sroberto 38754359Sroberto /* This is basically the CRT logic in crypto/rsa/rsa_eay.c reworded into 38854359Sroberto * GMP-speak. It may be that GMP's API facilitates cleaner formulations 38954359Sroberto * of this stuff, eg. better handling of negatives, or functions that 39054359Sroberto * combine operations. */ 39154359Sroberto 39254359Sroberto mpz_mod(hptr->r1, hptr->I0, hptr->q); 39354359Sroberto mpz_powm(hptr->m1, hptr->r1, hptr->dmq1, hptr->q); 39454359Sroberto 39554359Sroberto mpz_mod(hptr->r1, hptr->I0, hptr->p); 39654359Sroberto mpz_powm(hptr->r0, hptr->r1, hptr->dmp1, hptr->p); 39754359Sroberto 39854359Sroberto mpz_sub(hptr->r0, hptr->r0, hptr->m1); 39954359Sroberto 40054359Sroberto if(mpz_sgn(hptr->r0) < 0) 40154359Sroberto mpz_add(hptr->r0, hptr->r0, hptr->p); 40254359Sroberto mpz_mul(hptr->r1, hptr->r0, hptr->iqmp); 40354359Sroberto mpz_mod(hptr->r0, hptr->r1, hptr->p); 40454359Sroberto 40554359Sroberto if(mpz_sgn(hptr->r0) < 0) 40654359Sroberto mpz_add(hptr->r0, hptr->r0, hptr->p); 40754359Sroberto mpz_mul(hptr->r1, hptr->r0, hptr->q); 40854359Sroberto mpz_add(hptr->r0, hptr->r1, hptr->m1); 40954359Sroberto 41054359Sroberto /* ugh!!! */ 41154359Sroberto if(gmp2bn(hptr->r0, r)) 41254359Sroberto to_return = 1; 41354359Sroberto 41454359Sroberto return 1; 41554359Sroberto } 41654359Sroberto#endif 41754359Sroberto 41854359Sroberto/* This stuff is needed if this ENGINE is being compiled into a self-contained 41954359Sroberto * shared-library. */ 42054359Sroberto#ifdef ENGINE_DYNAMIC_SUPPORT 42154359Srobertostatic int bind_fn(ENGINE *e, const char *id) 42254359Sroberto { 42354359Sroberto if(id && (strcmp(id, engine_e_gmp_id) != 0)) 42454359Sroberto return 0; 42554359Sroberto if(!bind_helper(e)) 42654359Sroberto return 0; 42754359Sroberto return 1; 42854359Sroberto } 42954359SrobertoIMPLEMENT_DYNAMIC_CHECK_FN() 43054359SrobertoIMPLEMENT_DYNAMIC_BIND_FN(bind_fn) 43154359Sroberto#endif /* ENGINE_DYNAMIC_SUPPORT */ 43254359Sroberto 43354359Sroberto#endif /* !OPENSSL_NO_HW_GMP */ 43454359Sroberto#endif /* !OPENSSL_NO_HW */ 43554359Sroberto 43654359Sroberto