X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=lib%2Fsilcssh%2Fsilcssh_pkcs.c;h=d2b2a239c3bfa9c6418a2815aa207f51ebe89bfd;hp=1821709e4c18d5e6a2c426a00ff8de2646228a58;hb=e7b6c157b80152bf9fb9266e6bdd93f9fb0db776;hpb=da3be98515423903e3dde1e3c287498f90c1147f diff --git a/lib/silcssh/silcssh_pkcs.c b/lib/silcssh/silcssh_pkcs.c index 1821709e..d2b2a239 100644 --- a/lib/silcssh/silcssh_pkcs.c +++ b/lib/silcssh/silcssh_pkcs.c @@ -706,15 +706,15 @@ SILC_PKCS_EXPORT_PRIVATE_KEY_FILE(silc_pkcs_ssh_export_private_key_file) silc_hash_final(md5, enc + 16); /* Pad */ - pad_len = 8 - (key_len % 8); + pad_len = (-key_len) % 8; if (pad_len) { keyenc = silc_smalloc(stack, (key_len + pad_len) * sizeof(*keyenc)); - if (!key) + if (!keyenc) goto err; memset(keyenc + key_len, 'F', pad_len); memcpy(keyenc, key, key_len); } else { - keyenc = silc_memdup(key, key_len); + keyenc = silc_smemdup(stack, key, key_len); if (!keyenc) goto err; } @@ -843,21 +843,134 @@ SILC_PKCS_DECRYPT(silc_pkcs_ssh_decrypt) src, src_len, decrypt_cb, context); } +/* Sign context */ +typedef struct { + SilcStack stack; + SilcSshPrivateKey privkey; + SilcPKCSSignCb sign_cb; + void *context; +} *SilcSshSign; + +/* Sign callback. This formats the signature into SSH2 protocool compliant + format. */ + +static void silc_pkcs_ssh_sign_cb(SilcBool success, + const unsigned char *signature, + SilcUInt32 signature_len, + void *context) +{ + SilcSshSign sign = context; + SilcStack stack = sign->stack; + unsigned char rbuf[20], sbuf[20]; + SilcBufferStruct sig; + SilcAsn1 asn1; + SilcMPInt r, s; + + memset(&sig, 0, sizeof(sig)); + + /* Format the signature. RSA is easy because PKCS#1 is already in + correct format. For DSA the returned signature is in PKIX compliant + format and we have to reformat it for SSH2. */ + if (!strcmp(sign->privkey->pkcs->name, "dsa")) { + asn1 = silc_asn1_alloc(stack); + if (!asn1) { + sign->sign_cb(FALSE, NULL, 0, sign->context); + silc_sfree(stack, sign); + silc_stack_free(stack); + return; + } + + /* Decode the signature */ + silc_buffer_set(&sig, (unsigned char *)signature, signature_len); + if (!silc_asn1_decode(asn1, &sig, + SILC_ASN1_SEQUENCE, + SILC_ASN1_INT(&r), + SILC_ASN1_INT(&s), + SILC_ASN1_END, SILC_ASN1_END)) { + sign->sign_cb(FALSE, NULL, 0, sign->context); + silc_asn1_free(asn1); + silc_sfree(stack, sign); + silc_stack_free(stack); + return; + } + + /* Encode the integers */ + memset(rbuf, 0, sizeof(rbuf)); + memset(sbuf, 0, sizeof(sbuf)); + silc_mp_mp2bin_noalloc(&r, rbuf, sizeof(rbuf)); + silc_mp_mp2bin_noalloc(&s, sbuf, sizeof(sbuf)); + + silc_asn1_free(asn1); + + /* Encode SSH2 DSS signature */ + if (silc_buffer_sformat(stack, &sig, + SILC_STR_UI_INT(7), + SILC_STR_UI32_STRING("ssh-dss"), + SILC_STR_UI_INT(sizeof(rbuf) + sizeof(sbuf)), + SILC_STR_DATA(rbuf, sizeof(rbuf)), + SILC_STR_DATA(sbuf, sizeof(sbuf)), + SILC_STR_END) < 0) { + sign->sign_cb(FALSE, NULL, 0, sign->context); + silc_sfree(stack, sign); + silc_stack_free(stack); + return; + } + } else { + /* Encode SSH2 RSA signature */ + if (silc_buffer_sformat(stack, &sig, + SILC_STR_UI_INT(7), + SILC_STR_UI32_STRING("ssh-rsa"), + SILC_STR_UI_INT(signature_len), + SILC_STR_DATA(signature, signature_len), + SILC_STR_END) < 0) { + sign->sign_cb(FALSE, NULL, 0, sign->context); + silc_sfree(stack, sign); + silc_stack_free(stack); + return; + } + } + + /* Deliver result */ + sign->sign_cb(TRUE, silc_buffer_data(&sig), silc_buffer_len(&sig), + sign->context); + + silc_buffer_spurge(stack, &sig); + silc_sfree(stack, sign); + silc_stack_free(stack); +} + /* Sign */ SILC_PKCS_SIGN(silc_pkcs_ssh_sign) { SilcSshPrivateKey privkey = private_key; + SilcStack stack; + SilcSshSign sign; if (!privkey->pkcs->sign) { sign_cb(FALSE, NULL, 0, context); return NULL; } + stack = silc_stack_alloc(0, silc_crypto_stack()); + + sign = silc_scalloc(stack, 1, sizeof(*sign)); + if (!sign) { + sign_cb(FALSE, NULL, 0, context); + silc_stack_free(stack); + return NULL; + } + + sign->stack = stack; + sign->privkey = privkey; + sign->sign_cb = sign_cb; + sign->context = context; + + /* Sign. The callback will format it to SSH2 compliant format. */ return privkey->pkcs->sign(privkey->pkcs, privkey->private_key, src, src_len, compute_hash, hash, rng, - sign_cb, context); + silc_pkcs_ssh_sign_cb, sign); } /* Verify */ @@ -865,16 +978,81 @@ SILC_PKCS_SIGN(silc_pkcs_ssh_sign) SILC_PKCS_VERIFY(silc_pkcs_ssh_verify) { SilcSshPublicKey pubkey = public_key; + SilcAsyncOperation op; + SilcBufferStruct sig, r, s; + unsigned char *signame; + SilcStack stack = NULL; + SilcAsn1 asn1; if (!pubkey->pkcs->verify) { verify_cb(FALSE, context); return NULL; } - return pubkey->pkcs->verify(pubkey->pkcs, pubkey->public_key, - signature, signature_len, - data, data_len, hash, rng, - verify_cb, context); + /* Decode the SSH2 protocol style signature encoding. */ + silc_buffer_set(&sig, signature, signature_len); + if (silc_buffer_unformat(&sig, + SILC_STR_UI32_STRING_ALLOC(&signame), + SILC_STR_UI32_NSTRING(&signature, &signature_len), + SILC_STR_END) < 0) { + verify_cb(FALSE, context); + return NULL; + } + memset(&sig, 0, sizeof(sig)); + + /* DSS signature must be formatted to PKIX compliant format since our + implementation expects that. */ + if (!strcmp(signame, "ssh-dss")) { + /* The integers must be 160 bits each */ + if (signature_len != 40) { + verify_cb(FALSE, context); + silc_free(signame); + return NULL; + } + + silc_buffer_set(&r, signature, 20); + silc_buffer_set(&s, signature + 20, 20); + + stack = silc_stack_alloc(0, silc_crypto_stack()); + + asn1 = silc_asn1_alloc(stack); + if (!asn1) { + verify_cb(FALSE, context); + silc_free(signame); + silc_stack_free(stack); + return NULL; + } + + /* Encode signature to PKIX compliant format. */ + if (!silc_asn1_encode(asn1, &sig, + SILC_ASN1_OPTS(SILC_ASN1_ALLOC), + SILC_ASN1_SEQUENCE, + SILC_ASN1_ANY_PRIMITIVE(SILC_ASN1_TAG_INTEGER, &r), + SILC_ASN1_ANY_PRIMITIVE(SILC_ASN1_TAG_INTEGER, &s), + SILC_ASN1_END, SILC_ASN1_END)) { + verify_cb(FALSE, context); + silc_free(signame); + silc_asn1_free(asn1); + silc_stack_free(stack); + return NULL; + } + + signature = silc_buffer_steal(&sig, &signature_len); + + silc_asn1_free(asn1); + } + + /* Verify */ + op = pubkey->pkcs->verify(pubkey->pkcs, pubkey->public_key, + signature, signature_len, + data, data_len, hash, rng, + verify_cb, context); + + silc_free(signame); + silc_buffer_spurge(stack, &sig); + silc_stack_free(stack); + + return op; } /************************** SSH2 PKCS RSA Alg API ***************************/