aboutsummaryrefslogtreecommitdiff
path: root/libssh/keys.c
diff options
context:
space:
mode:
authorAris Adamantiadis <aris@0xbadc0de.be>2005-07-05 01:21:44 +0000
committerAris Adamantiadis <aris@0xbadc0de.be>2005-07-05 01:21:44 +0000
commitc65f56aefa50a2e2a78a0e45564526ecc921d74f (patch)
tree11bd53cf92869ccbab30f29253ce30f7078a4a26 /libssh/keys.c
downloadlibssh-c65f56aefa50a2e2a78a0e45564526ecc921d74f.tar.gz
libssh-c65f56aefa50a2e2a78a0e45564526ecc921d74f.tar.xz
libssh-c65f56aefa50a2e2a78a0e45564526ecc921d74f.zip
first import
git-svn-id: svn+ssh://svn.berlios.de/svnroot/repos/libssh/trunk@1 7dcaeef0-15fb-0310-b436-a5af3365683c
Diffstat (limited to 'libssh/keys.c')
-rw-r--r--libssh/keys.c370
1 files changed, 370 insertions, 0 deletions
diff --git a/libssh/keys.c b/libssh/keys.c
new file mode 100644
index 00000000..16a58db0
--- /dev/null
+++ b/libssh/keys.c
@@ -0,0 +1,370 @@
+/* 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., 59 Temple Place - Suite 330, Boston,
+MA 02111-1307, USA. */
+#include <string.h>
+#include <openssl/dsa.h>
+#include <openssl/rsa.h>
+#include "libssh/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();
+ 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);
+ free(p);
+ free(q);
+ free(g);
+ free(pubkey);
+ return key;
+}
+
+PUBLIC_KEY *publickey_make_rsa(BUFFER *buffer, char *type){
+ STRING *e,*n;
+ PUBLIC_KEY *key=malloc(sizeof(PUBLIC_KEY));
+ if(!strcmp(type,"ssh-rsa"))
+ key->type=TYPE_RSA;
+ else
+ key->type=TYPE_RSA1;
+ key->type_c=type;
+ 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();
+ key->rsa_pub->e=make_string_bn(e);
+ key->rsa_pub->n=make_string_bn(n);
+#ifdef DEBUG_CRYPTO
+ ssh_print_bignum("e",key->rsa_pub->e);
+ ssh_print_bignum("n",key->rsa_pub->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-rsa");
+ }
+ if(!strcmp(type,"ssh-rsa1")){
+ free(type);
+ return publickey_make_rsa(tmpbuf,"ssh-rsa1");
+ }
+ 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:
+ r=make_bignum_string(sign->dsa_sign->r);
+ s=make_bignum_string(sign->dsa_sign->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();
+ sig->r=make_string_bn(r); /* is that really portable ? Openssh's hack isn't better */
+ sig->s=make_string_bn(s);
+#ifdef DEBUG_CRYPTO
+ ssh_print_bignum("r",sig->r);
+ ssh_print_bignum("s",sig->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(len<rsalen)
+ ssh_say(0,"Len %d < %d\n",len,rsalen);
+ sign->type=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;
+}
+
+STRING *ssh_encrypt_rsa1(SSH_SESSION *session, STRING *data, PUBLIC_KEY *key){
+ int len=string_len(data);
+ int flen=RSA_size(key->rsa_pub);
+ STRING *ret=string_new(flen);
+ RSA_public_encrypt(len,data->string,ret->string,key->rsa_pub,
+ RSA_PKCS1_PADDING);
+ return ret;
+}
+