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.
 
 
 
 
 
 

351 lines
7.3 KiB

/*
Copyright Jeroen Vreeken (jeroen@vreeken.net), 2015
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <dml/dml_crypto.h>
#include <dml/dml_stream.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/ecdsa.h>
#include <openssl/sha.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
struct dml_crypto_key {
EC_KEY *ec_key;
X509 *cert;
STACK_OF(X509) *chain;
};
X509_STORE *x509_store;
STACK_OF(X509) *certchain;
int dml_crypto_init(char *ca_file, char *ca_dir)
{
/* Make sure we can find everything in the certificates */
OpenSSL_add_all_algorithms();
x509_store = X509_STORE_new();
if (!x509_store)
goto err_new;
if (ca_file || ca_dir) {
if (!X509_STORE_load_locations(x509_store, ca_file, ca_dir)) {
goto err_load;
}
// printf("Loadded store locations\n");
}
return 0;
err_load:
X509_STORE_free(x509_store);
err_new:
x509_store = NULL;
return -1;
}
struct dml_crypto_key *dml_crypto_key_create(void)
{
return calloc(1, sizeof(struct dml_crypto_key));
}
static void free_chain(STACK_OF(X509) *chain)
{
if (!chain)
return;
X509 *c;
while ((c = sk_X509_pop(chain)))
X509_free(c);
sk_X509_free(chain);
}
int dml_crypto_cert_add_verify(void *certdata, size_t size, uint8_t id[DML_ID_SIZE])
{
STACK_OF(X509) *chain;
uint8_t *data = certdata;
char *name;
chain = sk_X509_new_null();
if (!chain)
return -1;
while (size > 2) {
X509 *cert;
int certsize = (data[0] << 8) | data[1];
const unsigned char *cert_data = data + 2;
// printf("Cert: %zd %d\n", size, certsize);
if (certsize > size - 2)
break;
if (!certsize)
break;
cert = d2i_X509(NULL, &cert_data, certsize);
if (!cert)
break;
// printf("cert: %p\n", cert);
sk_X509_push(chain, cert);
data += certsize + 2;
size -= certsize + 2;
}
if (sk_X509_num(chain) < 1)
goto err_stack;
X509 *cert = sk_X509_pop(chain);
// printf("1st cert %p\n", cert);
X509_STORE_CTX *ctx = X509_STORE_CTX_new();
if (!ctx)
goto err_ctx;
if (X509_STORE_CTX_init(ctx, x509_store, cert, chain) != 1) {
X509_STORE_CTX_free(ctx);
goto err_ctx;
}
int rc = X509_verify_cert(ctx);
// int err = X509_STORE_CTX_get_error(ctx);
X509_STORE_CTX_free(ctx);
// fprintf(stderr, "verify cert rc: %d: %d\n", rc, err);
if (rc != 1) {
int x509_err = X509_STORE_CTX_get_error(ctx);
fprintf(stderr, "verify error: %d: %s\n", x509_err, X509_verify_cert_error_string(x509_err));
goto err_verify;
}
struct dml_stream *ds = dml_stream_by_id(id);
if (!ds)
goto err_stream;
name = dml_stream_name_get(ds);
if (!name)
goto err_name;
rc = X509_check_host(cert, name, 0, X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT, NULL);
// fprintf(stderr, "check host rc: %d\n", rc);
struct dml_crypto_key *dk = dml_stream_crypto_get(ds);
if (!dk) {
dk = dml_crypto_key_create();
dml_stream_crypto_set(ds, dk);
} else {
free_chain(dk->chain);
X509_free(dk->cert);
EC_KEY_free(dk->ec_key);
}
dk->chain = chain;
dk->cert = cert;
EVP_PKEY *evp_key = X509_get_pubkey(cert);
if (!evp_key)
goto err_key;
/* only 256 bits EC for now */
if (EVP_PKEY_bits(evp_key) != 256)
goto err_bits;
dk->ec_key = EVP_PKEY_get1_EC_KEY(evp_key);
if (!dk->ec_key)
goto err_key_type;
EVP_PKEY_free(evp_key);
return !(rc == 1);
err_key_type:
err_bits:
EVP_PKEY_free(evp_key);
err_key:
err_name:
err_stream:
err_verify:
X509_free(cert);
err_ctx:
free_chain(chain);
err_stack:
return -1;
}
/* load a pem file */
int dml_crypto_load_cert(char *file)
{
int ret = -1;
X509 *cert;
certchain = sk_X509_new_null();
FILE *fp = fopen(file, "r");
if (!fp)
goto err_fopen;
while ((cert = PEM_read_X509(fp, NULL, NULL, NULL))) {
ret = 0;
sk_X509_push(certchain, cert);
}
fclose(fp);
return ret;
err_fopen:
return -1;
}
int dml_crypto_cert_get(void **bincert, size_t *size)
{
unsigned char *der = NULL;
int bytes, i;
uint8_t *bin = NULL;
size_t binsize = 0;
if (!certchain)
return -1;
for (i = 0; i < sk_X509_num(certchain); i++) {
X509 *cert = sk_X509_value(certchain, i);
bytes = i2d_X509(cert, &der);
if (bytes >= 0) {
bin = realloc(bin, binsize + sizeof(uint16_t) + bytes);
bin[binsize + 0] = (bytes >> 8) & 0xff;
bin[binsize + 1] = (bytes) & 0xff;
memcpy(bin + binsize + 2, der, bytes);
binsize += sizeof(uint16_t) + bytes;
free(der);
}
}
*size = binsize;
*bincert = bin;
return 0;
}
struct dml_crypto_key *dml_crypto_private_load(char *file)
{
struct dml_crypto_key *dk;
EVP_PKEY *evp_key;
FILE *fp;
dk = calloc(1, sizeof(struct dml_crypto_key));
if (!dk)
goto err_malloc;
fp = fopen(file, "r");
if (!fp)
goto err_fopen;
evp_key = PEM_read_PrivateKey(fp, NULL, NULL, NULL);
dk->ec_key = EVP_PKEY_get1_EC_KEY(evp_key);
EVP_PKEY_free(evp_key);
fclose(fp);
return dk;
err_fopen:
free(dk);
err_malloc:
return NULL;
}
struct dml_crypto_key *dml_crypto_public_get(uint8_t id[DML_ID_SIZE])
{
struct dml_stream *ds = dml_stream_by_id(id);
if (!ds)
return NULL;
struct dml_crypto_key *dk = dml_stream_crypto_get(ds);
if (!dk)
return NULL;
return dk;
}
bool dml_crypto_verify(void *data, size_t len, uint8_t sig[DML_SIG_SIZE], struct dml_crypto_key *dk)
{
uint8_t digest[SHA256_DIGEST_LENGTH];
SHA256_CTX sha256;
SHA256_Init(&sha256);
SHA256_Update(&sha256, data, len);
SHA256_Final(digest, &sha256);
ECDSA_SIG *ecsig = ECDSA_SIG_new();
#if (OPENSSL_VERSION_NUMBER < 0x10100000)
BN_bin2bn(sig, 32, ecsig->r);
BN_bin2bn(sig + 32, 32, ecsig->s);
#else
BIGNUM *r, *s;
r = BN_bin2bn(sig, 32, NULL);
s = BN_bin2bn(sig + 32, 32, NULL);
ECDSA_SIG_set0(ecsig, r, s);
#endif
int ret = ECDSA_do_verify(digest, SHA256_DIGEST_LENGTH, ecsig, dk->ec_key);
ECDSA_SIG_free(ecsig);
if (ret != 1) {
unsigned int err = ERR_get_error();
fprintf(stderr, "ret: %d ERR: %d\n", ret, err);
}
return ret == 1;
}
int dml_crypto_sign(uint8_t sig[DML_SIG_SIZE], void *data, size_t len, struct dml_crypto_key *dk)
{
uint8_t digest[SHA256_DIGEST_LENGTH];
SHA256_CTX sha256;
SHA256_Init(&sha256);
SHA256_Update(&sha256, data, len);
SHA256_Final(digest, &sha256);
ECDSA_SIG *ecsig = ECDSA_do_sign(digest, SHA256_DIGEST_LENGTH, dk->ec_key);
memset(sig, 0, 64);
#if (OPENSSL_VERSION_NUMBER < 0x10100000)
int r_off = 32 - BN_num_bytes(ecsig->r);
int s_off = 32 - BN_num_bytes(ecsig->s);
BN_bn2bin(ecsig->r, sig + r_off);
BN_bn2bin(ecsig->s, sig + 32 + s_off);
#else
const BIGNUM *r, *s;
ECDSA_SIG_get0(ecsig, &r, &s);
int r_off = 32 - BN_num_bytes(r);
int s_off = 32 - BN_num_bytes(s);
BN_bn2bin(r, sig + r_off);
BN_bn2bin(s, sig + 32 + s_off);
#endif
ECDSA_SIG_free(ecsig);
return 0;
}
void dml_crypto_key_free(struct dml_crypto_key *dk)
{
if (!dk)
return;
if (dk->ec_key)
EC_KEY_free(dk->ec_key);
free(dk);
}