summaryrefslogtreecommitdiffstats
path: root/src/lib/net/SecureUtils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/net/SecureUtils.cpp')
-rw-r--r--src/lib/net/SecureUtils.cpp312
1 files changed, 312 insertions, 0 deletions
diff --git a/src/lib/net/SecureUtils.cpp b/src/lib/net/SecureUtils.cpp
new file mode 100644
index 0000000..c581dd4
--- /dev/null
+++ b/src/lib/net/SecureUtils.cpp
@@ -0,0 +1,312 @@
+/*
+ barrier -- mouse and keyboard sharing utility
+ Copyright (C) Barrier contributors
+
+ This package is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ found in the file LICENSE that should have accompanied this file.
+
+ This package 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/>.
+
+ -----------------------------------------------------------------------
+ create_fingerprint_randomart() has been taken from the OpenSSH project.
+ Copyright information follows.
+
+ Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
+ Copyright (c) 2008 Alexander von Gernler. All rights reserved.
+ Copyright (c) 2010,2011 Damien Miller. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "SecureUtils.h"
+#include "base/String.h"
+#include "base/finally.h"
+#include "io/filesystem.h"
+
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/pem.h>
+#include <algorithm>
+#include <cstdio>
+#include <cstring>
+#include <stdexcept>
+
+#if SYSAPI_WIN32
+// Windows builds require a shim that makes it possible to link to different
+// versions of the Win32 C runtime. See OpenSSL FAQ.
+#include <openssl/applink.c>
+#endif
+
+namespace barrier {
+
+namespace {
+
+const EVP_MD* get_digest_for_type(FingerprintType type)
+{
+ switch (type) {
+ case FingerprintType::SHA1: return EVP_sha1();
+ case FingerprintType::SHA256: return EVP_sha256();
+ }
+ throw std::runtime_error("Unknown fingerprint type " + std::to_string(static_cast<int>(type)));
+}
+
+} // namespace
+
+std::string format_ssl_fingerprint(const std::vector<uint8_t>& fingerprint, bool separator)
+{
+ std::string result = barrier::string::to_hex(fingerprint, 2);
+
+ // all uppercase
+ barrier::string::uppercase(result);
+
+ if (separator) {
+ // add colon to separate each 2 characters
+ size_t separators = result.size() / 2;
+ for (size_t i = 1; i < separators; i++) {
+ result.insert(i * 3 - 1, ":");
+ }
+ }
+ return result;
+}
+
+std::string format_ssl_fingerprint_columns(const std::vector<uint8_t>& fingerprint)
+{
+ auto max_columns = 8;
+
+ std::string hex = barrier::string::to_hex(fingerprint, 2);
+ barrier::string::uppercase(hex);
+ if (hex.empty() || hex.size() % 2 != 0) {
+ return hex;
+ }
+
+ std::string separated;
+ for (std::size_t i = 0; i < hex.size(); i += max_columns * 2) {
+ for (std::size_t j = i; j < i + 16 && j < hex.size() - 1; j += 2) {
+ separated.push_back(hex[j]);
+ separated.push_back(hex[j + 1]);
+ separated.push_back(':');
+ }
+ separated.push_back('\n');
+ }
+ separated.pop_back(); // we don't need last newline character
+ return separated;
+}
+
+FingerprintData get_ssl_cert_fingerprint(X509* cert, FingerprintType type)
+{
+ if (!cert) {
+ throw std::runtime_error("certificate is null");
+ }
+
+ unsigned char digest[EVP_MAX_MD_SIZE];
+ unsigned int digest_length = 0;
+ int result = X509_digest(cert, get_digest_for_type(type), digest, &digest_length);
+
+ if (result <= 0) {
+ throw std::runtime_error("failed to calculate fingerprint, digest result: " +
+ std::to_string(result));
+ }
+
+ std::vector<std::uint8_t> digest_vec;
+ digest_vec.assign(reinterpret_cast<std::uint8_t*>(digest),
+ reinterpret_cast<std::uint8_t*>(digest) + digest_length);
+ return {fingerprint_type_to_string(type), digest_vec};
+}
+
+FingerprintData get_pem_file_cert_fingerprint(const std::string& path, FingerprintType type)
+{
+ auto fp = fopen_utf8_path(path, "r");
+ if (!fp) {
+ throw std::runtime_error("Could not open certificate path");
+ }
+ auto file_close = finally([fp]() { std::fclose(fp); });
+
+ X509* cert = PEM_read_X509(fp, nullptr, nullptr, nullptr);
+ if (!cert) {
+ throw std::runtime_error("Certificate could not be parsed");
+ }
+ auto cert_free = finally([cert]() { X509_free(cert); });
+
+ return get_ssl_cert_fingerprint(cert, type);
+}
+
+void generate_pem_self_signed_cert(const std::string& path)
+{
+ auto expiration_days = 365;
+
+ auto* private_key = EVP_PKEY_new();
+ if (!private_key) {
+ throw std::runtime_error("Could not allocate private key for certificate");
+ }
+ auto private_key_free = finally([private_key](){ EVP_PKEY_free(private_key); });
+
+ auto* rsa = RSA_generate_key(2048, RSA_F4, nullptr, nullptr);
+ if (!rsa) {
+ throw std::runtime_error("Failed to generate RSA key");
+ }
+ EVP_PKEY_assign_RSA(private_key, rsa);
+
+ auto* cert = X509_new();
+ if (!cert) {
+ throw std::runtime_error("Could not allocate certificate");
+ }
+ auto cert_free = finally([cert]() { X509_free(cert); });
+
+ ASN1_INTEGER_set(X509_get_serialNumber(cert), 1);
+ X509_gmtime_adj(X509_get_notBefore(cert), 0);
+ X509_gmtime_adj(X509_get_notAfter(cert), expiration_days * 24 * 3600);
+ X509_set_pubkey(cert, private_key);
+
+ auto* name = X509_get_subject_name(cert);
+ X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
+ reinterpret_cast<const unsigned char *>("Barrier"), -1, -1, 0);
+ X509_set_issuer_name(cert, name);
+
+ X509_sign(cert, private_key, EVP_sha256());
+
+ auto fp = fopen_utf8_path(path.c_str(), "r");
+ if (!fp) {
+ throw std::runtime_error("Could not open certificate output path");
+ }
+ auto file_close = finally([fp]() { std::fclose(fp); });
+
+ PEM_write_PrivateKey(fp, private_key, nullptr, nullptr, 0, nullptr, nullptr);
+ PEM_write_X509(fp, cert);
+}
+
+/*
+ Draw an ASCII-Art representing the fingerprint so human brain can
+ profit from its built-in pattern recognition ability.
+ This technique is called "random art" and can be found in some
+ scientific publications like this original paper:
+
+ "Hash Visualization: a New Technique to improve Real-World Security",
+ Perrig A. and Song D., 1999, International Workshop on Cryptographic
+ Techniques and E-Commerce (CrypTEC '99)
+ sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf
+
+ The subject came up in a talk by Dan Kaminsky, too.
+
+ If you see the picture is different, the key is different.
+ If the picture looks the same, you still know nothing.
+
+ The algorithm used here is a worm crawling over a discrete plane,
+ leaving a trace (augmenting the field) everywhere it goes.
+ Movement is taken from dgst_raw 2bit-wise. Bumping into walls
+ makes the respective movement vector be ignored for this turn.
+ Graphs are not unambiguous, because circles in graphs can be
+walked in either direction.
+ */
+
+/*
+ Field sizes for the random art. Have to be odd, so the starting point
+ can be in the exact middle of the picture, and FLDBASE should be >=8 .
+ Else pictures would be too dense, and drawing the frame would
+ fail, too, because the key type would not fit in anymore.
+*/
+#define FLDBASE 8
+#define FLDSIZE_Y (FLDBASE + 1)
+#define FLDSIZE_X (FLDBASE * 2 + 1)
+
+std::string create_fingerprint_randomart(const std::vector<std::uint8_t>& dgst_raw)
+{
+ /*
+ * Chars to be used after each other every time the worm
+ * intersects with itself. Matter of taste.
+ */
+ const char* augmentation_string = " .o+=*BOX@%&#/^SE";
+ char *p;
+ std::uint8_t field[FLDSIZE_X][FLDSIZE_Y];
+ std::size_t i;
+ std::uint32_t b;
+ int x, y;
+ std::size_t len = strlen(augmentation_string) - 1;
+
+ std::vector<char> retval;
+ retval.reserve((FLDSIZE_X + 3) * (FLDSIZE_Y + 2));
+
+ auto add_char = [&retval](char ch) { retval.push_back(ch); };
+
+ /* initialize field */
+ std::memset(field, 0, FLDSIZE_X * FLDSIZE_Y * sizeof(char));
+ x = FLDSIZE_X / 2;
+ y = FLDSIZE_Y / 2;
+
+ /* process raw key */
+ for (i = 0; i < dgst_raw.size(); i++) {
+ /* each byte conveys four 2-bit move commands */
+ int input = dgst_raw[i];
+ for (b = 0; b < 4; b++) {
+ /* evaluate 2 bit, rest is shifted later */
+ x += (input & 0x1) ? 1 : -1;
+ y += (input & 0x2) ? 1 : -1;
+
+ /* assure we are still in bounds */
+ x = std::max(x, 0);
+ y = std::max(y, 0);
+ x = std::min(x, FLDSIZE_X - 1);
+ y = std::min(y, FLDSIZE_Y - 1);
+
+ /* augment the field */
+ if (field[x][y] < len - 2)
+ field[x][y]++;
+ input = input >> 2;
+ }
+ }
+
+ /* mark starting point and end point*/
+ field[FLDSIZE_X / 2][FLDSIZE_Y / 2] = len - 1;
+ field[x][y] = len;
+
+ /* output upper border */
+ add_char('+');
+ for (i = 0; i < FLDSIZE_X; i++)
+ add_char('-');
+ add_char('+');
+ add_char('\n');
+
+ /* output content */
+ for (y = 0; y < FLDSIZE_Y; y++) {
+ add_char('|');
+ for (x = 0; x < FLDSIZE_X; x++)
+ add_char(augmentation_string[std::min<int>(field[x][y], len)]);
+ add_char('|');
+ add_char('\n');
+ }
+
+ /* output lower border */
+ add_char('+');
+ for (i = 0; i < FLDSIZE_X; i++)
+ add_char('-');
+ add_char('+');
+
+ return std::string{retval.data(), retval.size()};
+}
+
+} // namespace barrier