Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions tool-openssl/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ add_executable(
dhparam.cc
ecparam.cc
ec.cc
enc.cc
genrsa.cc
pass_util.cc
pkcs8.cc
Expand Down Expand Up @@ -99,6 +100,8 @@ if(BUILD_TESTING)
ec_test.cc
ecparam.cc
ecparam_test.cc
enc.cc
enc_test.cc
genrsa.cc
genrsa_test.cc
ordered_args.cc
Expand Down
204 changes: 204 additions & 0 deletions tool-openssl/enc.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC

#include <openssl/evp.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include "../tool/internal.h"
#include "internal.h"

#define BUF_SIZE 1024

static const argument_t kArguments[] = {
// General options
{"-help", kBooleanArgument, "Display option summary"},
{"-in", kOptionalArgument, "Input file, default stdin"},
{"-out", kOptionalArgument, "Output file, default stdout"},
{"-e", kBooleanArgument, "Encrypt"},
{"-d", kBooleanArgument, "Decrypt"},
{"-K", kOptionalArgument, "Raw key to use, in hex form"},
{"-iv", kOptionalArgument, "IV to use, in hex form"},
{"-aes-128-cbc", kExclusiveBooleanArgument, "Supported cipher"},
{"", kOptionalArgument, ""}};

static bool HexToBinary(bssl::UniquePtr<uint8_t[]> &buffer,
const std::string &hex_string, unsigned int size) {
// First validate that the string contains only valid hex characters
for (char c : hex_string) {
if (!OPENSSL_isxdigit(c)) {
return false;
}
}

if (hex_string.size() != size * 2) {
return false;
}

BIGNUM *raw = NULL;
if (BN_hex2bn(&raw, hex_string.c_str()) == 0) {
return false;
}

int ret = BN_bn2binpad(raw, buffer.get(), size);
BN_free(raw);
return ret != -1;
}

bool encTool(const args_list_t &args) {
ordered_args::ordered_args_map_t parsed_args;
args_list_t extra_args;
if (!ordered_args::ParseOrderedKeyValueArguments(parsed_args, extra_args,
args, kArguments) ||
extra_args.size() > 0) {
PrintUsage(kArguments);
return false;
}

std::string in_path, out_path, hiv, cipher_name;
bssl::UniquePtr<std::string> hkey(new std::string);
bool help = false, e = false, d = false;

ordered_args::GetBoolArgument(&help, "-help", parsed_args);
ordered_args::GetString(&in_path, "-in", "", parsed_args);
ordered_args::GetString(&out_path, "-out", "", parsed_args);
ordered_args::GetBoolArgument(&e, "-e", parsed_args);
ordered_args::GetBoolArgument(&d, "-d", parsed_args);
ordered_args::GetString(hkey.get(), "-K", "", parsed_args);
ordered_args::GetString(&hiv, "-iv", "", parsed_args);
ordered_args::GetExclusiveBoolArgument(&cipher_name, kArguments, "",
parsed_args);

// Display enc tool option summary
if (help) {
PrintUsage(kArguments);
return true;
}

// Since we do not implement key generation, a raw key is required
// TODO: remove/modify if we ever implement -k, -kfile, or -S
if (hkey->empty()) {
fprintf(stderr, "Error: A raw key is required\n");
return false;
}

if (e && d) {
fprintf(stderr, "Error: -e and -d are mutually exclusive\n");
return false;
}

bool enc = true;
if (d) {
enc = false;
}

// Read from stdin if no -in path provided
ScopedFILE in_file;
if (in_path.empty()) {
in_file.reset(stdin);
} else {
in_file.reset(fopen(in_path.c_str(), "rb"));
if (!in_file) {
fprintf(stderr, "Error: unable to load data from '%s'\n",
in_path.c_str());
return false;
}
}

if (cipher_name.empty()) {
cipher_name = "aes-128-cbc";
} else {
cipher_name = cipher_name.substr(1);
}
const EVP_CIPHER *cipher = EVP_get_cipherbyname(cipher_name.c_str());

if (cipher == nullptr) {
fprintf(stderr, "Error: Unknown cipher %s\n", cipher_name.c_str());
return false;
}

unsigned int iv_length = EVP_CIPHER_iv_length(cipher);
bssl::UniquePtr<uint8_t[]> iv((uint8_t *)OPENSSL_zalloc(EVP_MAX_IV_LENGTH));
Comment on lines +121 to +122
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you unconditionally allocate EVP_MAX_IV_LENGTH, why not make iv a stack value?


if (!hiv.empty()) {
if (iv_length == 0) {
fprintf(stderr, "Warning: IV is not used by cipher %s\n",
cipher_name.c_str());
} else {
if (!HexToBinary(iv, hiv, iv_length)) {
fprintf(stderr, "Error: Invalid hex IV value\n");
return false;
}
}
} else {
if (iv_length != 0) {
fprintf(stderr, "Error: IV is required for cipher %s\n",
cipher_name.c_str());
return false;
}
}

bssl::UniquePtr<uint8_t[]> key((uint8_t *)OPENSSL_zalloc(EVP_MAX_KEY_LENGTH));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly here, key can be a stack value if you're always allocating a constant size, EVP_MAX_KEY_LENGTH.


if (!hkey->empty()) {
if (!HexToBinary(key, *hkey, EVP_CIPHER_key_length(cipher))) {
fprintf(stderr, "Error: Invalid hex key value\n");
return false;
}
}

bssl::UniquePtr<BIO> output_bio;
if (out_path.empty()) {
output_bio.reset(BIO_new_fp(stdout, BIO_NOCLOSE));
} else {
output_bio.reset(BIO_new(BIO_s_file()));
if (1 != BIO_write_filename(output_bio.get(), out_path.c_str())) {
fprintf(stderr, "Error: unable to write to '%s'\n", out_path.c_str());
return false;
}
}

// Create and initialize cipher context
bssl::UniquePtr<EVP_CIPHER_CTX> ctx(EVP_CIPHER_CTX_new());
if (!EVP_CipherInit_ex(ctx.get(), cipher, nullptr, key.get(), iv.get(),
enc)) {
fprintf(stderr, "Error: Failed to initialize cipher\n");
return false;
}

// Process the input file
uint8_t inbuf[BUF_SIZE];
bssl::UniquePtr<uint8_t[]> outbuf(
(uint8_t *)OPENSSL_zalloc(BUF_SIZE + EVP_CIPHER_block_size(cipher)));
int inlen = 0, outlen = 0;
Comment on lines +171 to +174
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NP: You could also keep outbuf on the stack as long as you assume a large enough block size. (I believe 16 is the largest block size we support.)


for (;;) {
if (feof(in_file.get())) {
break;
}

inlen = fread(inbuf, 1, sizeof(inbuf), in_file.get());

if (ferror(in_file.get())) {
fprintf(stderr, "Error reading from '%s'.\n", in_path.c_str());
return false;
}

if (!EVP_CipherUpdate(ctx.get(), outbuf.get(), &outlen, inbuf, inlen)) {
fprintf(stderr, "Error: Cipher update failed\n");
return false;
}
BIO_write(output_bio.get(), outbuf.get(), outlen);
}

// Finalize
if (!EVP_CipherFinal_ex(ctx.get(), outbuf.get(), &outlen)) {
fprintf(stderr, "Error: Cipher final failed\n");
return false;
}

BIO_write(output_bio.get(), outbuf.get(), outlen);

return true;
}
Loading
Loading