summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKrzysztof Jackiewicz <k.jackiewicz@samsung.com>2018-10-02 14:27:27 +0200
committerBartlomiej Grzelewski <b.grzelewski@samsung.com>2018-10-05 21:24:36 +0200
commit3195a9624186ca5b6b681148b192e9ef48cfe872 (patch)
tree1143161576039ff8877adcea5760ed1d0bdcf579
parentf0a0b4b6f6f5047df98a75ec999a964ce772b012 (diff)
downloadkey-manager-3195a9624186ca5b6b681148b192e9ef48cfe872.tar.gz
key-manager-3195a9624186ca5b6b681148b192e9ef48cfe872.tar.bz2
key-manager-3195a9624186ca5b6b681148b192e9ef48cfe872.zip
Initial values tool
Add a tool able to create and/or update an initial values xml. It is also possible to add encrypted ininial values. Add rpm package for potential use in gbs buildroot during image creation. Limitations: - Hardcoded IV & tag length - Hardcoded Data format Testing: dd if=/dev/random of=/tmp/key bs=32 count=1 dd if=/dev/random of=/tmp/data bs=32 count=1 ckm_initial_values -k /tmp/key -d /tmp/data -n name -t Key -s AES -p pass -e -b hardware -a acc1,acc2,acc3 Change-Id: Id29d0eb58d9dba3e78b3437534cb566046a39877
-rw-r--r--LICENSE.BSD-2-Clause24
-rw-r--r--packaging/key-manager.spec17
-rw-r--r--tools/CMakeLists.txt1
-rw-r--r--tools/ckm_initial_values/CMakeLists.txt49
-rw-r--r--tools/ckm_initial_values/base64.cpp204
-rw-r--r--tools/ckm_initial_values/base64.h65
-rw-r--r--tools/ckm_initial_values/main.cpp468
7 files changed, 828 insertions, 0 deletions
diff --git a/LICENSE.BSD-2-Clause b/LICENSE.BSD-2-Clause
new file mode 100644
index 0000000..03f5f58
--- /dev/null
+++ b/LICENSE.BSD-2-Clause
@@ -0,0 +1,24 @@
+Copyright (c) 2014, STMicroelectronics International N.V.
+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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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.
diff --git a/packaging/key-manager.spec b/packaging/key-manager.spec
index 1a3dd0b..4daa444 100644
--- a/packaging/key-manager.spec
+++ b/packaging/key-manager.spec
@@ -125,6 +125,18 @@ Requires(postun): %{sbin_dir}/ldconfig
CKM login/password module to PAM. Used to monitor user login/logout
and password change events from PAM
+%package -n key-manager-initial-values
+Summary: CKM initial values tool
+Group: Security/Libraries
+License: Apache-2.0 and BSD-2-Clause
+BuildRequires: cmake
+BuildRequires: pkgconfig(openssl)
+BuildRequires: pkgconfig(libxml-2.0)
+Requires(post): %{sbin_dir}/ldconfig
+Requires(postun): %{sbin_dir}/ldconfig
+
+%description -n key-manager-initial-values
+Includes ckm_initial_values tool for initial values XML generation
%prep
%setup -q
@@ -322,3 +334,8 @@ fi
%{bin_dir}/ckm_db_merge
%{bin_dir}/ckm_generate_db
%test_dir
+
+%files -n key-manager-initial-values
+%license LICENSE
+%license LICENSE.BSD-2-Clause
+%{bin_dir}/ckm_initial_values
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index c824559..9d12bb7 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -46,3 +46,4 @@ INSTALL(TARGETS ${CKM_TOOL}
WORLD_EXECUTE
)
ADD_SUBDIRECTORY(ckm_db_tool)
+ADD_SUBDIRECTORY(ckm_initial_values) \ No newline at end of file
diff --git a/tools/ckm_initial_values/CMakeLists.txt b/tools/ckm_initial_values/CMakeLists.txt
new file mode 100644
index 0000000..c23f1e6
--- /dev/null
+++ b/tools/ckm_initial_values/CMakeLists.txt
@@ -0,0 +1,49 @@
+#### This is to allow local build without gbs ####
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+INCLUDE(FindPkgConfig)
+INCLUDE(GNUInstallDirs)
+
+IF(NOT DEFINED BIN_DIR)
+ SET(BIN_DIR "${CMAKE_INSTALL_FULL_BINDIR}" CACHE PATH "User executables directory")
+ENDIF()
+
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
+##################################################
+
+
+SET(CKM_INITIAL_VALUES "ckm_initial_values")
+
+PKG_CHECK_MODULES(CKM_INITIAL_VALUES_DEP
+ REQUIRED
+ openssl
+ libxml-2.0
+ )
+
+INCLUDE_DIRECTORIES(
+ ${CKM_INITIAL_VALUES_DEP_INCLUDE_DIRS}
+ )
+
+SET(CKM_INITIAL_VALUES_SOURCES
+ ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/base64.cpp
+ )
+
+LINK_DIRECTORIES(${CKM_INITIAL_VALUES_DEP_LIBRARY_DIRS})
+
+ADD_EXECUTABLE(${CKM_INITIAL_VALUES} ${CKM_INITIAL_VALUES_SOURCES})
+
+TARGET_LINK_LIBRARIES(${CKM_INITIAL_VALUES}
+ ${CKM_INITIAL_VALUES_DEP_LIBRARIES}
+ )
+
+INSTALL(TARGETS ${CKM_INITIAL_VALUES}
+ DESTINATION ${BIN_DIR}
+ PERMISSIONS OWNER_READ
+ OWNER_WRITE
+ OWNER_EXECUTE
+ GROUP_READ
+ GROUP_EXECUTE
+ WORLD_READ
+ WORLD_EXECUTE
+ )
+
diff --git a/tools/ckm_initial_values/base64.cpp b/tools/ckm_initial_values/base64.cpp
new file mode 100644
index 0000000..aa1520a
--- /dev/null
+++ b/tools/ckm_initial_values/base64.cpp
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+#include <algorithm>
+#include <memory>
+
+#include <string.h>
+#include <openssl/bio.h>
+#include <openssl/evp.h>
+#include <openssl/buffer.h>
+
+#include "base64.h"
+
+namespace CKM {
+
+Base64Encoder::Base64Encoder() :
+ m_b64(0),
+ m_bmem(0),
+ m_finalized(false)
+{
+}
+
+void Base64Encoder::append(const RawBuffer &data)
+{
+ if (m_finalized) {
+ throw std::logic_error("Already finalized");
+ }
+
+ if (!m_b64)
+ reset();
+
+ BIO_write(m_b64, data.data(), data.size());
+}
+
+void Base64Encoder::finalize()
+{
+ if (m_finalized) {
+ throw std::logic_error("Already finalized.");
+ }
+
+ m_finalized = true;
+ (void)BIO_flush(m_b64);
+}
+
+RawBuffer Base64Encoder::get()
+{
+ if (!m_finalized) {
+ throw std::logic_error("Not finalized");
+ }
+
+ BUF_MEM *bptr = nullptr;
+ BIO_get_mem_ptr(m_b64, &bptr);
+
+ if (!bptr) {
+ throw std::logic_error("Bio internal error");
+ }
+
+ if (bptr->length > 0)
+ return RawBuffer(bptr->data, bptr->data + bptr->length);
+
+ return RawBuffer();
+}
+
+void Base64Encoder::reset()
+{
+ m_finalized = false;
+ BIO_free_all(m_b64);
+ m_b64 = BIO_new(BIO_f_base64());
+ m_bmem = BIO_new(BIO_s_mem());
+
+ if (!m_b64 || !m_bmem) {
+ throw std::logic_error("Error during allocation memory in BIO");
+ }
+
+ BIO_set_flags(m_b64, BIO_FLAGS_BASE64_NO_NL);
+ m_b64 = BIO_push(m_b64, m_bmem);
+}
+
+Base64Encoder::~Base64Encoder()
+{
+ BIO_free_all(m_b64);
+}
+
+Base64Decoder::Base64Decoder() :
+ m_finalized(false)
+{
+}
+
+void Base64Decoder::append(const RawBuffer &data)
+{
+ if (m_finalized) {
+ throw std::logic_error("Already finalized.");
+ }
+
+ std::copy(data.begin(), data.end(), std::back_inserter(m_input));
+}
+
+static bool whiteCharacter(char a)
+{
+ return a == '\n';
+}
+
+bool Base64Decoder::finalize()
+{
+ if (m_finalized) {
+ throw std::logic_error("Already finalized.");
+ }
+
+ m_finalized = true;
+
+ m_input.erase(std::remove_if(m_input.begin(),
+ m_input.end(),
+ whiteCharacter),
+ m_input.end());
+
+ for (size_t i = 0; i < m_input.size(); ++i) {
+ if (isalnum(m_input[i])
+ || m_input[i] == '+'
+ || m_input[i] == '/'
+ || m_input[i] == '=')
+ continue;
+
+ return false;
+ }
+
+ BIO *b64, *bmem;
+ size_t len = m_input.size();
+
+ RawBuffer buffer(len);
+
+ if (!buffer.data()) {
+ throw std::logic_error("Error in malloc.");
+ }
+
+ memset(buffer.data(), 0, buffer.size());
+ b64 = BIO_new(BIO_f_base64());
+
+ if (!b64) {
+ throw std::logic_error("Couldn't create BIO object.");
+ }
+
+ BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
+ RawBuffer tmp(m_input);
+ m_input.clear();
+
+ bmem = BIO_new_mem_buf(tmp.data(), len);
+
+ if (!bmem) {
+ BIO_free(b64);
+ throw std::logic_error("Internal error in BIO");
+ }
+
+ bmem = BIO_push(b64, bmem);
+
+ if (!bmem) {
+ BIO_free(b64);
+ throw std::logic_error("Internal error in BIO");
+ }
+
+ int readlen = BIO_read(bmem, buffer.data(), buffer.size());
+ m_output.clear();
+
+ bool status = true;
+
+ if (readlen > 0) {
+ buffer.resize(readlen);
+ m_output = std::move(buffer);
+ } else {
+ status = false;
+ }
+
+ BIO_free_all(bmem);
+ return status;
+}
+
+RawBuffer Base64Decoder::get() const
+{
+ if (!m_finalized) {
+ throw std::logic_error("Not finalized");
+ }
+
+ return m_output;
+}
+
+void Base64Decoder::reset()
+{
+ m_finalized = false;
+ m_input.clear();
+ m_output.clear();
+}
+
+} // namespace CKM
diff --git a/tools/ckm_initial_values/base64.h b/tools/ckm_initial_values/base64.h
new file mode 100644
index 0000000..c9085bf
--- /dev/null
+++ b/tools/ckm_initial_values/base64.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 _BASE64_H_
+#define _BASE64_H_
+
+#include <string>
+#include <vector>
+
+typedef std::vector<unsigned char> RawBuffer;
+
+struct bio_st;
+typedef bio_st BIO;
+
+namespace CKM {
+
+class Base64Encoder {
+public:
+ Base64Encoder();
+ void append(const RawBuffer &data);
+ void finalize();
+ RawBuffer get();
+ void reset();
+ ~Base64Encoder();
+
+private:
+ BIO *m_b64;
+ BIO *m_bmem;
+ bool m_finalized;
+};
+
+class Base64Decoder {
+public:
+ Base64Decoder();
+ void append(const RawBuffer &data);
+
+ /*
+ * Function will return false when BIO_read fails
+ * (for example: when string was not in base64 format).
+ */
+ bool finalize();
+ RawBuffer get() const;
+ void reset();
+ ~Base64Decoder() {}
+
+private:
+ RawBuffer m_input;
+ RawBuffer m_output;
+ bool m_finalized;
+};
+} // namespace CKM
+
+#endif
diff --git a/tools/ckm_initial_values/main.cpp b/tools/ckm_initial_values/main.cpp
new file mode 100644
index 0000000..4e9e0a1
--- /dev/null
+++ b/tools/ckm_initial_values/main.cpp
@@ -0,0 +1,468 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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
+ */
+/*
+ * @file main.cpp
+ * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com)
+ * @version 1.0
+ * @brief
+ */
+
+#include <cstdio>
+
+#include <unistd.h>
+#include <getopt.h>
+
+#include <cstdlib>
+
+#include <iostream>
+#include <memory>
+#include <fstream>
+#include <vector>
+#include <set>
+#include <string>
+#include <unordered_map>
+
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+
+#include "base64.h"
+
+typedef std::vector<unsigned char> Buffer;
+typedef std::istreambuf_iterator<char> InputIterator;
+
+const size_t DEFAULT_TAG_LEN = 16;
+const size_t DEFAULT_IV_LEN = 16;
+
+const struct option OPTS[] = {
+ {"xml", required_argument, 0, 'x'},
+ {"key", required_argument, 0, 'k'},
+ {"name", required_argument, 0, 'n'},
+ {"data", required_argument, 0, 'd'},
+ {"type", required_argument, 0, 't'},
+ {"subtype", required_argument, 0, 's'},
+ {"password", required_argument, 0, 'p'},
+ {"exportable", no_argument, 0, 'e'},
+ {"accessors", required_argument, 0, 'a'},
+ {"backend", required_argument, 0, 'b'},
+ {"help", no_argument, 0, 'h'},
+ {0, 0, 0, 0 }
+};
+
+const std::string KEY = "Key";
+const std::string DATA = "Data";
+const std::string CERT = "Cert";
+const std::set<std::string> TYPES = { KEY, DATA, CERT };
+const std::set<std::string> SUBTYPES = { "RSA_PRV", "RSA_PUB",
+ "DSA_PRV", "DSA_PUB",
+ "ECDSA_PRV", "ECDSA_PUB",
+ "AES"
+};
+const std::set<std::string> BACKENDS = { "software", "hardware" };
+
+struct FormatTag {
+ const std::string plain;
+ const std::string encrypted;
+};
+
+const std::unordered_map<std::string, FormatTag> FORMAT = {
+ { KEY, {"DER", "EncryptedDER"}},
+ { DATA, {"Base64", "EncryptedBinary"}},
+ { CERT, {"DER", "EncryptedDER"}},
+};
+
+std::string base64(const Buffer& data)
+{
+ try {
+ CKM::Base64Encoder encoder;
+ encoder.append(data);
+ encoder.finalize();
+ auto result = encoder.get();
+ return std::string(result.begin(), result.end());
+ } catch (std::exception &e) {
+ std::cerr << "Error: " << e.what() << std::endl;
+ }
+ return std::string();
+}
+
+struct InitialValue {
+ InitialValue() {}
+
+ std::string name, type, subType, data, iv, tag, password, format, backend, exportable;
+ std::set<std::string> accessors;
+};
+
+void usage()
+{
+ std::cout << std::endl <<
+ "Usage: ckm_initial_values <options>" << std::endl <<
+ std::endl <<
+ "Mandatory options:" << std::endl <<
+ " -d|--data <dataFile> Path to file containing initial value to be added. Supported" << std::endl <<
+ " formats:" << std::endl <<
+ " - Key:" << std::endl <<
+ " - raw binary (symmetric keys)" << std::endl <<
+ " - DER (asymmetric keys)" << std::endl <<
+ " - Data: raw binary" << std::endl <<
+ " - Cert: DER" << std::endl <<
+ " -n|--name <name> Name, under which the initial value will be saved." << std::endl <<
+ " -t|--type <type> Initial value type. One of: Key, Data, Cert." << std::endl <<
+ " -s|--subtype <subtype> Initial value subtype. For 'Key' type allowed values are:" << std::endl <<
+ " RSA_PRV, RSA_PUB, DSA_PRV, DSA_PUB, ECDSA_PRV, ECDSA_PUB, AES." << std::endl <<
+ " For other types this option should not be used." << std::endl <<
+ std::endl <<
+ "Optional:" << std::endl <<
+ " -x|--xml <xmlFile> Path to XML file that should be modified. If not provided output" << std::endl <<
+ " will be printed to stdout." << std::endl <<
+ " -k|--key <keyFile> Path to file containing AES key in binary form used for initial" << std::endl <<
+ " value encryption." << std::endl <<
+ " -p|--password <password> Password used to encrypt the initial value." << std::endl <<
+ " -e|--exportable If present the stored value can be later extracted via" << std::endl <<
+ " key-manager API." << std::endl <<
+ " -a|--accessors <accessor1>[,<accessor2>[,...]]" << std::endl <<
+ " A list of key-manager clients allowed to access given initial" << std::endl <<
+ " value separated by commas." << std::endl <<
+ " -b|--backend <backend> A key-manager's backed to use when saving the initial values." << std::endl;
+}
+
+Buffer readFile(const std::string& file)
+{
+ if (file.empty())
+ return Buffer();
+
+ std::ifstream stream(file);
+ if (!stream.good()) {
+ std::cerr << "Invalid file " << file << std::endl;
+ ::exit(EXIT_FAILURE);
+ }
+
+ Buffer data(InputIterator(stream), { });
+ return data;
+}
+
+
+bool encrypt(const Buffer& data, const Buffer& key, Buffer& output, Buffer& iv, Buffer& tag)
+{
+ OPENSSL_init();
+
+ iv.resize(DEFAULT_IV_LEN);
+
+ std::ifstream stream("/dev/urandom");
+ if (!stream.good()) {
+ std::cerr << "Can't open /dev/urandom" << std::endl;
+ return false;
+ }
+ // FIXIT
+ stream.read(reinterpret_cast<char*>(iv.data()), DEFAULT_IV_LEN);
+
+ std::unique_ptr<EVP_CIPHER_CTX, void (*)(EVP_CIPHER_CTX *)> ctx(EVP_CIPHER_CTX_new(),
+ EVP_CIPHER_CTX_free);
+
+ if (!ctx) {
+ std::cerr << "EVP_CIPHER_CTX_new() failed" << std::endl;
+ return false;
+ }
+
+ if (1 != EVP_EncryptInit_ex(ctx.get(), EVP_aes_256_gcm(), NULL, NULL, NULL)) {
+ std::cerr << "EVP_EncryptInit_ex() failed" << std::endl;
+ return false;
+ }
+
+ if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IVLEN, iv.size(), NULL)) {
+ std::cerr << "EVP_CIPHER_CTX_ctrl() failed" << std::endl;
+ return false;
+ }
+
+ if (1 != EVP_EncryptInit_ex(ctx.get(), NULL, NULL, key.data(), iv.data())) {
+ std::cerr << "EVP_EncryptInit_ex() failed" << std::endl;
+ return false;
+ }
+
+ int outputLen = 0;
+ output.resize(data.size() + EVP_CIPHER_CTX_block_size(ctx.get()));
+ int written = 0;
+
+ if (1 != EVP_EncryptUpdate(ctx.get(), output.data(), &written, data.data(), data.size())) {
+ std::cerr << "EVP_EncryptUpdate() failed" << std::endl;
+ return false;
+ }
+ outputLen += written;
+
+ if (1 != EVP_EncryptFinal_ex(ctx.get(), &output.data()[written], &written)) {
+ std::cerr << "EVP_EncryptFinal_ex() failed" << std::endl;
+ return false;
+ }
+ outputLen += written;
+ output.resize(outputLen);
+
+ tag.resize(DEFAULT_TAG_LEN);
+ if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, DEFAULT_TAG_LEN, tag.data())) {
+ std::cerr << "EVP_CIPHER_CTX_ctrl() failed" << std::endl;
+ return false;
+ }
+
+ return true;
+}
+
+struct ScopedXmlLib {
+ ScopedXmlLib() : doc(NULL), commit(false) {
+ xmlInitParser();
+ LIBXML_TEST_VERSION;
+ xmlKeepBlanksDefault(0);
+ }
+
+ ~ScopedXmlLib() {
+ xmlFreeDoc(doc);
+ xmlCleanupParser();
+ }
+
+ xmlDocPtr doc;
+ bool commit;
+};
+
+xmlNodePtr addChild(xmlNodePtr parent, const std::string& name)
+{
+ auto childNode = xmlNewNode(NULL, BAD_CAST name.c_str());
+ if (childNode == NULL) {
+ std::cerr << "xmlNewNode() failed for " << name << std::endl;
+ return NULL;
+ }
+
+ if (xmlAddChild(parent, childNode) == NULL) {
+ std::cerr << "xmlAddChild() failed" << std::endl;
+ xmlFreeNode(childNode);
+ return NULL;
+ }
+
+ return childNode;
+}
+
+bool addProperty(xmlNodePtr element, const std::string& name, const std::string& value)
+{
+ if (value.empty())
+ return true;
+
+ if (xmlNewProp(element, BAD_CAST name.c_str(), BAD_CAST value.c_str()) == NULL) {
+ std::cerr << "xmlNewProp() failed for " << name << std::endl;
+ return false;
+ }
+ return true;
+}
+
+bool addInitialValue(const std::string& xmlFile, const InitialValue& val)
+{
+ ScopedXmlLib lib;
+
+ lib.doc = xmlReadFile(xmlFile.c_str(), NULL, XML_PARSE_NOWARNING);
+ if (lib.doc == NULL) {
+ lib.doc = xmlNewDoc(BAD_CAST "1.0");
+ if (lib.doc == NULL) {
+ std::cerr << "xmlNewDoc() failed" << std::endl;
+ return false;
+ }
+ }
+
+ // root node
+ auto rootNode = xmlDocGetRootElement(lib.doc);
+ if (rootNode == NULL) {
+ rootNode = xmlNewNode(NULL, BAD_CAST "InitialValues");
+ if (rootNode == NULL) {
+ std::cerr << "xmlNewNode() failed" << std::endl;
+ return false;
+ }
+
+ xmlDocSetRootElement(lib.doc, rootNode);
+
+ if (!addProperty(rootNode, "version", "2"))
+ return false;
+ }
+
+ // value node
+ auto valNode = addChild(rootNode, val.type);
+ if (valNode == NULL)
+ return false;
+
+ if (!addProperty(valNode, "name", val.name))
+ return false;
+
+ if (!addProperty(valNode, "type", val.subType))
+ return false;
+
+ if (!addProperty(valNode, "password", val.password))
+ return false;
+
+ if (!addProperty(valNode, "exportable", val.exportable))
+ return false;
+
+ if (!addProperty(valNode, "backend", val.backend))
+ return false;
+
+ // data node
+ auto dataNode = xmlNewTextChild(valNode,
+ NULL,
+ BAD_CAST val.format.c_str(),
+ BAD_CAST val.data.c_str());
+ if (dataNode == NULL)
+ return false;
+
+ if (!addProperty(dataNode, "IV", val.iv))
+ return false;
+
+ if (!addProperty(dataNode, "tag", val.tag))
+ return false;
+
+ // accessor nodes
+ for (auto& accessor : val.accessors) {
+ auto accNode = addChild(valNode, "Permission");
+ if (accNode == NULL)
+ return false;
+
+ if (!addProperty(accNode, "accessor", accessor))
+ return false;
+ }
+
+ if (0 >= xmlSaveFormatFile(xmlFile.empty() ? "-" : xmlFile.c_str(), lib.doc, 1)) {
+ std::cerr << "xmlSaveFile() failed" << std::endl;
+ return false;
+ }
+ return true;
+}
+
+int main(int argc, char* argv[])
+{
+ std::string xmlFile, keyFile, dataFile;
+ Buffer key, data, iv, tag;
+ InitialValue val;
+
+ int idx = 0;
+ int c;
+ while ((c = ::getopt_long(argc, argv, "x:k:n:d:t:s:p:ea:b:h", OPTS, &idx)) != -1) {
+ switch (c) {
+ case 'x':
+ xmlFile = optarg;
+ break;
+ case 'k':
+ keyFile = optarg;
+ break;
+ case 'n':
+ val.name = optarg;
+ break;
+ case 'd':
+ dataFile = optarg;
+ break;
+ case 't':
+ val.type = optarg;
+ break;
+ case 's':
+ val.subType = optarg;
+ break;
+ case 'p':
+ val.password = optarg;
+ break;
+ case 'e':
+ val.exportable = "true";
+ break;
+ case 'a':
+ {
+ std::string tmp = optarg;
+ size_t pos = 0;
+ size_t found = 0;
+ while ((found = tmp.find(',', pos)) != std::string::npos) {
+ if (found != pos)
+ val.accessors.insert(tmp.substr(pos, found - pos));
+ pos = found + 1;
+ }
+ if (pos < tmp.size())
+ val.accessors.insert(tmp.substr(pos));
+ break;
+ }
+ case 'b':
+ val.backend = optarg;
+ break;
+ case 'h':
+ usage();
+ return EXIT_SUCCESS;
+ case '?':
+ case ':':
+ default:
+ usage();
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (val.name.empty() || dataFile.empty() || val.type.empty()) {
+ usage();
+ return EXIT_FAILURE;
+ }
+
+ if (TYPES.find(val.type) == TYPES.end()) {
+ usage();
+ return EXIT_FAILURE;
+ }
+
+ if (val.type == KEY && (val.subType.empty() || SUBTYPES.find(val.subType) == SUBTYPES.end())) {
+ usage();
+ return EXIT_FAILURE;
+ }
+
+ if (!val.backend.empty() && BACKENDS.find(val.backend) == BACKENDS.end()) {
+ usage();
+ return EXIT_FAILURE;
+ }
+
+ data = readFile(dataFile);
+ if (data.empty()) {
+ std::cerr << "Empty data file " << dataFile << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ key = readFile(keyFile);
+
+ val.format = FORMAT.at(val.type).plain;
+ if (!keyFile.empty()) {
+ if (key.size() != 32) {
+ std::cerr << "Invalid key size " << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ Buffer output;
+ if (!encrypt(data, key, output, iv, tag))
+ return EXIT_FAILURE;
+
+ val.data = base64(output);
+ if (val.data.empty())
+ return EXIT_FAILURE;
+
+ val.iv = base64(iv);
+ if (val.iv.empty())
+ return EXIT_FAILURE;
+
+ val.tag = base64(tag);
+ if (val.tag.empty())
+ return EXIT_FAILURE;
+
+ val.format = FORMAT.at(val.type).encrypted;
+ } else {
+ val.data = base64(data);
+ }
+
+ if (!addInitialValue(xmlFile, val))
+ return EXIT_FAILURE;
+
+ return EXIT_SUCCESS;
+}