/* * An implementation of convertion from OpenSSL to OpenSSH public key format * * Copyright (c) 2008 Mounir IDRASSI . All rights reserved. * * This program 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. */ #include #include #include #include #include #include #include #include #include "xbps_api_impl.h" static unsigned char pSshHeader[11] = { 0x00, 0x00, 0x00, 0x07, 0x73, 0x73, 0x68, 0x2D, 0x72, 0x73, 0x61 }; static int SshEncodeBuffer(unsigned char *pEncoding, int bufferLen, unsigned char *pBuffer) { int adjustedLen = bufferLen, index; if (*pBuffer & 0x80) { adjustedLen++; pEncoding[4] = 0; index = 5; } else { index = 4; } pEncoding[0] = (unsigned char) (adjustedLen >> 24); pEncoding[1] = (unsigned char) (adjustedLen >> 16); pEncoding[2] = (unsigned char) (adjustedLen >> 8); pEncoding[3] = (unsigned char) (adjustedLen ); memcpy(&pEncoding[index], pBuffer, bufferLen); return index + bufferLen; } static char * fp2str(unsigned const char *fp, unsigned int len) { unsigned int i, c = 0; char res[48], cur[4]; for (i = 0; i < len; i++) { if (i > 0) c = i*3; sprintf(cur, "%02x", fp[i]); res[c] = cur[0]; res[c+1] = cur[1]; res[c+2] = ':'; } res[c+2] = '\0'; return strdup(res); } char * xbps_pubkey2fp(struct xbps_handle *xhp, xbps_data_t pubkey) { EVP_MD_CTX *mdctx = NULL; EVP_PKEY *pPubKey = NULL; RSA *pRsa = NULL; BIO *bio = NULL; const void *pubkeydata; unsigned char md_value[EVP_MAX_MD_SIZE]; const BIGNUM *n, *e; unsigned char *nBytes = NULL, *eBytes = NULL, *pEncoding = NULL; unsigned int md_len = 0; char *hexfpstr = NULL; int index = 0, nLen = 0, eLen = 0, encodingLength = 0; ERR_load_crypto_strings(); OpenSSL_add_all_algorithms(); mdctx = EVP_MD_CTX_new(); assert(mdctx); pubkeydata = xbps_data_data_nocopy(pubkey); bio = BIO_new_mem_buf(pubkeydata, xbps_data_size(pubkey)); assert(bio); pPubKey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); if (!pPubKey) { xbps_dbg_printf(xhp, "unable to decode public key from the given file: %s\n", ERR_error_string(ERR_get_error(), NULL)); goto out; } if (EVP_PKEY_base_id(pPubKey) != EVP_PKEY_RSA) { xbps_dbg_printf(xhp, "only RSA public keys are currently supported\n"); goto out; } pRsa = EVP_PKEY_get1_RSA(pPubKey); if (!pRsa) { xbps_dbg_printf(xhp, "failed to get RSA public key : %s\n", ERR_error_string(ERR_get_error(), NULL)); goto out; } RSA_get0_key(pRsa, &n, &e, NULL); // reading the modulus nLen = BN_num_bytes(n); nBytes = (unsigned char*) malloc(nLen); if (nBytes == NULL) goto out; BN_bn2bin(n, nBytes); // reading the public exponent eLen = BN_num_bytes(e); eBytes = (unsigned char*) malloc(eLen); if (eBytes == NULL) goto out; BN_bn2bin(e, eBytes); encodingLength = 11 + 4 + eLen + 4 + nLen; // correct depending on the MSB of e and N if (eBytes[0] & 0x80) encodingLength++; if (nBytes[0] & 0x80) encodingLength++; pEncoding = malloc(encodingLength); assert(pEncoding); memcpy(pEncoding, pSshHeader, 11); index = SshEncodeBuffer(&pEncoding[11], eLen, eBytes); (void)SshEncodeBuffer(&pEncoding[11 + index], nLen, nBytes); /* * Compute the RSA fingerprint (MD5). */ EVP_MD_CTX_init(mdctx); EVP_DigestInit_ex(mdctx, EVP_md5(), NULL); EVP_DigestUpdate(mdctx, pEncoding, encodingLength); if (EVP_DigestFinal_ex(mdctx, md_value, &md_len) == 0) goto out; EVP_MD_CTX_free(mdctx); mdctx = NULL; /* * Convert result to a compatible OpenSSH hex fingerprint. */ hexfpstr = fp2str(md_value, md_len); out: if (mdctx) EVP_MD_CTX_free(mdctx); if (bio) BIO_free_all(bio); if (pRsa) RSA_free(pRsa); if (pPubKey) EVP_PKEY_free(pPubKey); if (nBytes) free(nBytes); if (eBytes) free(eBytes); if (pEncoding) free(pEncoding); return hexfpstr; }