You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kftpgrabber/kftpgrabber/src/misc/libs/ssh/keys.c

380 lines
11 KiB

/* 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 <string.h>
#include <openssl/dsa.h>
#include <openssl/rsa.h>
#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: {
const BIGNUM *bn_r = 0L;
const 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(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;
}