/* keys handle the public key related functions */ /* decoding a public key (both rsa and dsa), decoding a signature (rsa and dsa), veryfying them */ /* Copyright 2003,04 Aris Adamantiadis This file is part of the SSH Library The SSH 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. The SSH 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 the SSH Library; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "priv.h" /* Public key decoding functions */ char *ssh_type_to_char(int type){ switch(type){ case TYPE_DSS: return "ssh-dss"; case TYPE_RSA: case TYPE_RSA1: return "ssh-rsa"; default: return NULL; } } PUBLIC_KEY *publickey_make_dss(BUFFER *buffer){ STRING *p,*q,*g,*pubkey; PUBLIC_KEY *key=malloc(sizeof(PUBLIC_KEY)); key->type=TYPE_DSS; key->type_c="ssh-dss"; p=buffer_get_ssh_string(buffer); q=buffer_get_ssh_string(buffer); g=buffer_get_ssh_string(buffer); pubkey=buffer_get_ssh_string(buffer); buffer_free(buffer); /* we don't need it anymore */ if(!p || !q || !g || !pubkey){ ssh_set_error(NULL,SSH_FATAL,"Invalid DSA public key"); if(p) free(p); if(q) free(q); if(g) free(g); if(pubkey) free(pubkey); free(key); return NULL; } key->dsa_pub=DSA_new(); #if OPENSSL_VERSION_NUMBER < 0x10100000L key->dsa_pub->p=make_string_bn(p); key->dsa_pub->q=make_string_bn(q); key->dsa_pub->g=make_string_bn(g); key->dsa_pub->pub_key=make_string_bn(pubkey); #else DSA_set0_pqg(key->dsa_pub, make_string_bn(p), make_string_bn(q), make_string_bn(g)); DSA_set0_key(key->dsa_pub, make_string_bn(pubkey), NULL); #endif free(p); free(q); free(g); free(pubkey); return key; } PUBLIC_KEY *publickey_make_rsa(BUFFER *buffer){ STRING *e,*n; PUBLIC_KEY *key=malloc(sizeof(PUBLIC_KEY)); key->type=TYPE_RSA; key->type_c="ssh-rsa"; e=buffer_get_ssh_string(buffer); n=buffer_get_ssh_string(buffer); buffer_free(buffer); /* we don't need it anymore */ if(!e || !n){ ssh_set_error(NULL,SSH_FATAL,"Invalid RSA public key"); if(e) free(e); if(n) free(n); free(key); return NULL; } key->rsa_pub=RSA_new(); BIGNUM *bn_e = make_string_bn(e); BIGNUM *bn_n = make_string_bn(n); #if (OPENSSL_VERSION_NUMBER < 0x10100000L) key->rsa_pub->e = bn_e; key->rsa_pub->n = bn_n; #else RSA_set0_key(key->rsa_pub, bn_n, bn_e, NULL); #endif #ifdef DEBUG_CRYPTO ssh_print_bignum("e", bn_e); ssh_print_bignum("n", bn_n); #endif free(e); free(n); return key; } void publickey_free(PUBLIC_KEY *key){ if(!key) return; switch(key->type){ case TYPE_DSS: DSA_free(key->dsa_pub); break; case TYPE_RSA: case TYPE_RSA1: RSA_free(key->rsa_pub); break; default: break; } free(key); } PUBLIC_KEY *publickey_from_string(STRING *pubkey_s){ BUFFER *tmpbuf=buffer_new(); STRING *type_s; char *type; buffer_add_data(tmpbuf,pubkey_s->string,string_len(pubkey_s)); type_s=buffer_get_ssh_string(tmpbuf); if(!type_s){ buffer_free(tmpbuf); ssh_set_error(NULL,SSH_FATAL,"Invalid public key format"); return NULL; } type=string_to_char(type_s); free(type_s); if(!strcmp(type,"ssh-dss")){ free(type); return publickey_make_dss(tmpbuf); } if(!strcmp(type,"ssh-rsa")){ free(type); return publickey_make_rsa(tmpbuf); } ssh_set_error(NULL,SSH_FATAL,"unknown public key protocol %s",type); buffer_free(tmpbuf); free(type); return NULL; } /* Signature decoding functions */ STRING *signature_to_string(SIGNATURE *sign){ STRING *str; STRING *rs,*r,*s; unsigned char buffer[40]; BUFFER *tmpbuf=buffer_new(); STRING *tmp; tmp=string_from_char(ssh_type_to_char(sign->type)); buffer_add_ssh_string(tmpbuf,tmp); free(tmp); switch(sign->type){ case TYPE_DSS: { BIGNUM *bn_r = 0L; BIGNUM *bn_s = 0L; #if OPENSSL_VERSION_NUMBER < 0x10100000L bn_r = sign->dsa_sign->r; bn_s = sign->dsa_sign->s; #else DSA_SIG_get0(sign->dsa_sign, &bn_r, &bn_s); #endif r = make_bignum_string(bn_r); s = make_bignum_string(bn_s); rs=string_new(40); memset(buffer,0,40); memcpy(buffer,r->string+string_len(r)-20,20); memcpy(buffer+ 20, s->string + string_len(s) - 20, 20); string_fill(rs,buffer,40); free(r); free(s); buffer_add_ssh_string(tmpbuf,rs); free(rs); break; } case TYPE_RSA: case TYPE_RSA1: buffer_add_ssh_string(tmpbuf,sign->rsa_sign); break; } str=string_new(buffer_get_len(tmpbuf)); string_fill(str,buffer_get(tmpbuf),buffer_get_len(tmpbuf)); buffer_free(tmpbuf); return str; } /* TODO : split this function in two so it becomes smaller */ SIGNATURE *signature_from_string(STRING *signature,PUBLIC_KEY *pubkey,int needed_type){ DSA_SIG *sig; SIGNATURE *sign=malloc(sizeof(SIGNATURE)); BUFFER *tmpbuf=buffer_new(); STRING *rs; STRING *r,*s,*type_s,*e; int len,rsalen; char *type; buffer_add_data(tmpbuf,signature->string,string_len(signature)); type_s=buffer_get_ssh_string(tmpbuf); if(!type_s){ ssh_set_error(NULL,SSH_FATAL,"Invalid signature packet"); buffer_free(tmpbuf); return NULL; } type=string_to_char(type_s); free(type_s); switch(needed_type){ case TYPE_DSS: if(strcmp(type,"ssh-dss")){ ssh_set_error(NULL,SSH_FATAL,"Invalid signature type : %s",type); buffer_free(tmpbuf); free(type); return NULL; } break; case TYPE_RSA: if(strcmp(type,"ssh-rsa")){ ssh_set_error(NULL,SSH_FATAL,"Invalid signature type : %s",type); buffer_free(tmpbuf); free(type); return NULL; } break; default: ssh_set_error(NULL,SSH_FATAL,"Invalid signature type : %s",type); free(type); buffer_free(tmpbuf); return NULL; } free(type); switch(needed_type){ case TYPE_DSS: rs=buffer_get_ssh_string(tmpbuf); buffer_free(tmpbuf); if(!rs || string_len(rs)!=40){ /* 40 is the dual signature blob len. */ if(rs) free(rs); return NULL; } /* we make use of strings because we have all-made functions to convert them to bignums */ r=string_new(20); s=string_new(20); string_fill(r,rs->string,20); string_fill(s,rs->string+20,20); free(rs); sig=DSA_SIG_new(); BIGNUM *bn_r = make_string_bn(r); BIGNUM *bn_s = make_string_bn(s); #if OPENSSL_VERSION_NUMBER < 0x10100000L sig->r = bn_r; /* is that really portable ? Openssh's hack isn't better */ sig->s = bn_s; #else DSA_SIG_set0(sig, bn_r, bn_s); #endif #ifdef DEBUG_CRYPTO ssh_print_bignum("r", bn_r); ssh_print_bignum("s", bn_s); #endif free(r); free(s); sign->type=TYPE_DSS; sign->dsa_sign=sig; return sign; case TYPE_RSA: e=buffer_get_ssh_string(tmpbuf); buffer_free(tmpbuf); if(!e){ free(e); return NULL; } len=string_len(e); rsalen=RSA_size(pubkey->rsa_pub); if(len>rsalen){ free(e); free(sign); ssh_set_error(NULL,SSH_FATAL,"signature too big ! %d instead of %d",len,rsalen); return NULL; } if(lentype=TYPE_RSA; sign->rsa_sign=e; #ifdef DEBUG_CRYPTO ssh_say(0,"Len : %d\n",len); ssh_print_hexa("rsa signature",e->string,len); #endif return sign; default: return NULL; } } void signature_free(SIGNATURE *sign){ if(!sign) return; switch(sign->type){ case TYPE_DSS: DSA_SIG_free(sign->dsa_sign); break; case TYPE_RSA: case TYPE_RSA1: free(sign->rsa_sign); break; default: ssh_say(1,"freeing a signature with no type !\n"); } free(sign); } /* maybe the missing function from libcrypto */ /* i think now, maybe it's a bad idea to name it has it should have be named in libcrypto */ static STRING *RSA_do_sign(void *payload,int len,RSA *privkey){ STRING *sign; void *buffer=malloc(RSA_size(privkey)); unsigned int size; int err; err=RSA_sign(NID_sha1,payload,len,buffer,&size,privkey); if(!err){ free(buffer); return NULL; } sign=string_new(size); string_fill(sign,buffer,size); free(buffer); return sign; } STRING *ssh_do_sign(SSH_SESSION *session,BUFFER *sigbuf, PRIVATE_KEY *privatekey){ SHACTX *ctx; STRING *session_str=string_new(SHA_DIGEST_LEN); char hash[SHA_DIGEST_LEN]; SIGNATURE *sign; STRING *signature; string_fill(session_str,session->current_crypto->session_id,SHA_DIGEST_LENGTH); ctx=sha1_init(); sha1_update(ctx,session_str,string_len(session_str)+4); sha1_update(ctx,buffer_get(sigbuf),buffer_get_len(sigbuf)); sha1_final(hash,ctx); free(session_str); sign=malloc(sizeof(SIGNATURE)); switch(privatekey->type){ case TYPE_DSS: sign->dsa_sign=DSA_do_sign(hash,SHA_DIGEST_LENGTH,privatekey->dsa_priv); sign->rsa_sign=NULL; break; case TYPE_RSA: sign->rsa_sign=RSA_do_sign(hash,SHA_DIGEST_LENGTH,privatekey->rsa_priv); sign->dsa_sign=NULL; break; } sign->type=privatekey->type; if(!sign->dsa_sign && !sign->rsa_sign){ ssh_set_error(session,SSH_FATAL,"Signing : openssl error"); signature_free(sign); return NULL; } signature=signature_to_string(sign); signature_free(sign); return signature; }