Skip to content

Commit

Permalink
Add support for compressed certificates in cmd
Browse files Browse the repository at this point in the history
  • Loading branch information
aveenismail committed Sep 18, 2024
1 parent 898f03d commit f0c6d5a
Show file tree
Hide file tree
Showing 6 changed files with 207 additions and 7 deletions.
7 changes: 7 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ project (yubihsm-shell)

option(BUILD_ONLY_LIB "Library only build" OFF)
option(SUPRESS_MSVC_WARNINGS "Suppresses a lot of the warnings when compiling with MSVC" ON)
option(ENABLE_CERT_COMPRESS "Enable/disable compression of certificate" OFF)

if (ENABLE_CERT_COMPRESS)
if(NOT MSVC)
add_definitions(-DUSE_CERT_COMPRESS="1")
endif()
endif()

include(${CMAKE_SOURCE_DIR}/cmake/SecurityFlags.cmake)

Expand Down
100 changes: 100 additions & 0 deletions common/x509_compress.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright 2024 Yubico AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifndef _WIN32_BCRYPT

#include "x509_compress.h"

#include <openssl/x509.h>
#include <zlib.h>

int compress_cert(X509 *cert, uint8_t *compressed_data) {

unsigned char uncompressed_certdata[4096] = {0};
unsigned char *uncompressed_certptr = uncompressed_certdata;
int cert_len = i2d_X509(cert, &uncompressed_certptr);

if( cert_len < 0) {
fprintf(stderr, "Failed to encode X509 certificate before compression\n");
return 0;
}

z_stream zs;
zs.zalloc = Z_NULL;
zs.zfree = Z_NULL;
zs.opaque = Z_NULL;
zs.avail_in = (uInt)cert_len;
zs.next_in = (Bytef *)uncompressed_certdata;
zs.avail_out = (uInt) sizeof(uncompressed_certdata);
zs.next_out = (Bytef *)compressed_data;

if(deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS | 16, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
fprintf(stderr, "Failed to compress certificate: deflateInit2()\n");
return 0;
}
if(deflate(&zs, Z_FINISH) != Z_STREAM_END) {
fprintf(stderr, "Failed to compress certificate: deflate()\n");
return 0;
}
if(deflateEnd(&zs) != Z_OK) {
fprintf(stderr, "Failed to compress certificate: deflateEnd()\n");
return 0;
}

return zs.total_out;
}


X509* uncompress_cert(uint8_t *data, size_t data_len) {
uint8_t *dataptr = data;
uint8_t certdata[4096] = {0};
size_t certdata_len = sizeof(certdata);

z_stream zs;
zs.zalloc = Z_NULL;
zs.zfree = Z_NULL;
zs.opaque = Z_NULL;
zs.avail_in = (uInt) data_len;
zs.next_in = (Bytef *) dataptr;
zs.avail_out = (uInt) certdata_len;
zs.next_out = (Bytef *) certdata;

if (inflateInit2(&zs, MAX_WBITS | 16) != Z_OK) {
fprintf(stderr, "Failed to initialize certificate decompression\n");
return NULL;
}

int res = inflate(&zs, Z_FINISH);
if (res != Z_STREAM_END) {
if (res == Z_BUF_ERROR) {
fprintf(stderr, "Failed to decompress certificate. Allocated buffer is too small\n");
} else {
fprintf(stderr, "Failed to decompress certificate\n");
}
return NULL;
}
if (inflateEnd(&zs) != Z_OK) {
fprintf(stderr, "Failed to finish certificate decompression\n");
return NULL;
}
certdata_len = zs.total_out;

const unsigned char *certdata_ptr = certdata;
return d2i_X509(NULL, &certdata_ptr, certdata_len);
}


#endif
44 changes: 44 additions & 0 deletions common/x509_compress.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright 2024 Yubico AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* ecdh.h
**
** Implements platform specific operations to compress and uncompress X509Cert
*/

#ifndef YUBIHSM_SHELL_X509_COMPRESS_H
#define YUBIHSM_SHELL_X509_COMPRESS_H

#ifndef _WIN32_BCRYPT
// Only inlcude this if OpenSSL can be used

#include "../common/platform-config.h"
#include <stdlib.h>
#include <stdint.h>
#include <openssl/types.h>

#ifdef __cplusplus
extern "C" {
#endif

#define YH_INTERNAL __attribute__((visibility("hidden")))

int YH_INTERNAL compress_cert(X509 *cert, uint8_t *compressed_data);
X509* uncompress_cert(uint8_t *data, size_t data_len);

#endif

#endif // YUBIHSM_SHELL_X509_COMPRESS_H
7 changes: 7 additions & 0 deletions lib/yubihsm.h
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,10 @@ typedef enum {
YH_ALGO_AES_CBC = 54,
/// aes-kwp
YH_ALGO_AES_KWP = 55,
#ifdef USE_CERT_COMPRESS
/// Compressed certificate
YH_ALGO_OPAQUE_X509_COMPRESSED = 128,
#endif
} yh_algorithm;

/**
Expand Down Expand Up @@ -748,6 +752,9 @@ static const struct {
{"mgf1-sha512", YH_ALGO_MGF1_SHA512},
{"opaque-data", YH_ALGO_OPAQUE_DATA},
{"opaque-x509-certificate", YH_ALGO_OPAQUE_X509_CERTIFICATE},
#ifdef USE_CERT_COMPRESS
{"opaque-x509-compressed", YH_ALGO_OPAQUE_X509_COMPRESSED},
#endif
{"rsa-oaep-sha1", YH_ALGO_RSA_OAEP_SHA1},
{"rsa-oaep-sha256", YH_ALGO_RSA_OAEP_SHA256},
{"rsa-oaep-sha384", YH_ALGO_RSA_OAEP_SHA384},
Expand Down
13 changes: 13 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,17 @@ else(WIN32)
find_gengetopt ()
add_gengetopt_files (cmdline "--conf-parser")
set(SOURCE ${SOURCE} ${GGO_C})

if (ENABLE_CERT_COMPRESS)
set(SOURCE ${SOURCE} ../common/x509_compress.c)

find_library(ZLIB zlib PATHS ${ZLIB_LIB_DIR})
include_directories(${ZLIB_INCL_DIR})

find_package(ZLIB REQUIRED)

set(ZLIB_LIBS "ZLIB::ZLIB")
endif()
endif(WIN32)

include_directories (
Expand Down Expand Up @@ -69,6 +80,7 @@ if (ENABLE_STATIC)
${LIBCRYPTO_LDFLAGS}
${LIBEDIT_LDFLAGS}
${GETOPT_LIBS}
${ZLIB_LIBS}
yubihsm_static
${YKHSMAUTH_LIB_STATIC})
add_coverage (yubihsm-shell_static)
Expand All @@ -79,6 +91,7 @@ target_link_libraries (
${LIBCRYPTO_LDFLAGS}
${LIBEDIT_LDFLAGS}
${GETOPT_LIBS}
${ZLIB_LIBS}
yubihsm
${YKHSMAUTH_LIB})

Expand Down
43 changes: 36 additions & 7 deletions src/commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
#include "yubihsm-shell.h"
#include "../common/insecure_memzero.h"
#include "../common/parsing.h"
#ifdef USE_CERT_COMPRESS
#include "../common/x509_compress.h"
#endif
#include "time_win.h"

#include "hash.h"
Expand Down Expand Up @@ -914,8 +917,17 @@ int yh_com_get_opaque(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt,
const unsigned char *ptr = response;
X509 *x509 = d2i_X509(NULL, &ptr, response_len);
if (!x509) {
fprintf(stderr, "Failed parsing x509 information\n");
} else {
fprintf(stderr, "Failed parsing x509 information.\n");
#ifdef USE_CERT_COMPRESS
fprintf(stderr, "Trying to parse it as compressed certificate\n");
x509 = uncompress_cert(response, response_len);
if(!x509) {
fprintf(stderr, "Failed parsing x509 information.\n");
}
#endif
}

if (x509) {
if (PEM_write_X509(ctx->out, x509) == 1) {
ret = 0;
} else {
Expand Down Expand Up @@ -2277,6 +2289,8 @@ int yh_com_put_opaque(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt,
unsigned char buf[YH_MSG_BUF_SIZE], *data = argv[6].x;
size_t len = argv[6].len;

X509 *cert = NULL;

if (in_fmt == fmt_PEM) {
// Decode X.509 Certificate regardless of algorithm in case fmt_PEM is
// explicitly set
Expand All @@ -2285,7 +2299,7 @@ int yh_com_put_opaque(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt,
fprintf(stderr, "Couldn't wrap PEM-encoded certificate data\n");
return 0;
}
X509 *cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
if (!cert) {
fprintf(stderr, "Couldn't parse PEM-encoded certificate\n");
BIO_free(bio);
Expand All @@ -2301,18 +2315,33 @@ int yh_com_put_opaque(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt,
data = buf;
i2d_X509(cert, &data);
data = buf;
X509_free(cert);
} else if (argv[5].a == YH_ALGO_OPAQUE_X509_CERTIFICATE) {
}

if (!cert && (argv[5].a == YH_ALGO_OPAQUE_X509_CERTIFICATE
#ifdef USE_CERT_COMPRESS
|| argv[5].a == YH_ALGO_OPAQUE_X509_COMPRESSED
#endif
)) {
// Enforce valid X.509 certificate
const unsigned char *p = data;
X509 *cert = d2i_X509(NULL, &p, len);
cert = d2i_X509(NULL, &p, len);
if (!cert) {
fprintf(stderr, "Couldn't parse DER-encoded certificate\n");
return 0;
}
X509_free(cert);
}

#ifdef USE_CERT_COMPRESS
if (argv[5].a == YH_ALGO_OPAQUE_X509_COMPRESSED) {
len = compress_cert(cert, data);
if (len == 0) {
fprintf(stderr, "Couldn't compress certificate\n");
return 0;
}
}
#endif
X509_free(cert);

yh_rc yrc = yh_util_import_opaque(argv[0].e, &argv[1].w, argv[2].s, argv[3].w,
&argv[4].c, argv[5].a, data, len);
if (yrc != YHR_SUCCESS) {
Expand Down

0 comments on commit f0c6d5a

Please sign in to comment.