--- /dev/null
+/******************************************************************
+ *
+ * Copyright 2017 Samsung Electronics All Rights Reserved.
+ *
+ * Author: Jaroslaw Pelczar <j.pelczar@samsung.com>
+ *
+ * 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 "cert_utils.h"
+#include <vector>
+#include <map>
+#include <algorithm>
+#include <cstring>
+#include <cstdlib>
+#include <mbedtls/pem.h>
+#include <stdexcept>
+#include <cassert>
+#include <set>
+#include <list>
+#include "logging.h"
+
+int x509_crt_rewriter::parse(const unsigned char * buffer, size_t length)
+{
+ BOOST_LOG_FUNCTION();
+ return mbedtls_x509_crt_parse(fChain, buffer, length);
+}
+
+/*
+ * Like memcmp, but case-insensitive and always returns -1 if different
+ */
+static int x509_memcasecmp( const void *s1, const void *s2, size_t len )
+{
+ size_t i;
+ unsigned char diff;
+ const unsigned char *n1 = (const unsigned char *)s1, *n2 = (const unsigned char *)s2;
+
+ for( i = 0; i < len; i++ )
+ {
+ diff = n1[i] ^ n2[i];
+
+ if( diff == 0 )
+ continue;
+
+ if( diff == 32 &&
+ ( ( n1[i] >= 'a' && n1[i] <= 'z' ) ||
+ ( n1[i] >= 'A' && n1[i] <= 'Z' ) ) )
+ {
+ continue;
+ }
+
+ return( -1 );
+ }
+
+ return( 0 );
+}
+
+/*
+ * Compare two X.509 strings, case-insensitive, and allowing for some encoding
+ * variations (but not all).
+ *
+ * Return 0 if equal, -1 otherwise.
+ */
+static int x509_string_cmp( const mbedtls_x509_buf *a, const mbedtls_x509_buf *b )
+{
+ if( a->tag == b->tag &&
+ a->len == b->len &&
+ memcmp( a->p, b->p, b->len ) == 0 )
+ {
+ return( 0 );
+ }
+
+ if( ( a->tag == MBEDTLS_ASN1_UTF8_STRING || a->tag == MBEDTLS_ASN1_PRINTABLE_STRING ) &&
+ ( b->tag == MBEDTLS_ASN1_UTF8_STRING || b->tag == MBEDTLS_ASN1_PRINTABLE_STRING ) &&
+ a->len == b->len &&
+ x509_memcasecmp( a->p, b->p, b->len ) == 0 )
+ {
+ return( 0 );
+ }
+
+ return( -1 );
+}
+
+/*
+ * Compare two X.509 Names (aka rdnSequence).
+ *
+ * See RFC 5280 section 7.1, though we don't implement the whole algorithm:
+ * we sometimes return unequal when the full algorithm would return equal,
+ * but never the other way. (In particular, we don't do Unicode normalisation
+ * or space folding.)
+ *
+ * Return 0 if equal, -1 otherwise.
+ */
+static int x509_name_cmp( const mbedtls_x509_name *a, const mbedtls_x509_name *b )
+{
+ /* Avoid recursion, it might not be optimised by the compiler */
+ while( a != NULL || b != NULL )
+ {
+ if( a == NULL || b == NULL )
+ return( -1 );
+
+ /* type */
+ if( a->oid.tag != b->oid.tag ||
+ a->oid.len != b->oid.len ||
+ memcmp( a->oid.p, b->oid.p, b->oid.len ) != 0 )
+ {
+ return( -1 );
+ }
+
+ /* value */
+ if( x509_string_cmp( &a->val, &b->val ) != 0 )
+ return( -1 );
+
+ /* structure of the list of sets */
+ if( a->next_merged != b->next_merged )
+ return( -1 );
+
+ a = a->next;
+ b = b->next;
+ }
+
+ /* a == NULL == b */
+ return( 0 );
+}
+
+void x509_crt_rewriter::sort_chain()
+{
+ BOOST_LOG_FUNCTION();
+
+ // Only 1 certificate - don't bother
+ if(!fChain->next) {
+ fChainSize = fChain->raw.len;
+ fNumCerts = 1;
+ return;
+ }
+
+ std::vector<mbedtls_x509_crt *> list;
+ std::multimap<mbedtls_x509_crt *, mbedtls_x509_crt *> subject_of;
+ std::set<mbedtls_x509_crt *> visited;
+
+ // Build list of all certificates
+ for(auto * cert = fChain ; cert ; cert = cert->next) {
+ list.push_back(cert);
+ }
+
+ // Create graph vertices to map issuer to subject
+ for(size_t i = 0 ; i < list.size() ; ++i) {
+ mbedtls_x509_crt * issuer = nullptr;
+ // Find issuer - ignore ourselves as we always want root CA issuer to be nullptr
+ for(size_t j = 0 ; j < list.size() ; ++j) {
+ if(i != j && x509_name_cmp(&list[i]->issuer, &list[j]->subject) == 0) {
+ issuer = list[j];
+ break;
+ }
+ }
+
+ // In case there are multiple subjects for one issuer, the chain
+ // must be horribly broken
+ subject_of.emplace(issuer, list[i]);
+ }
+
+ // BFS algorithm queue
+ std::list<mbedtls_x509_crt *> queue;
+ // Final output chain
+ std::list<mbedtls_x509_crt *> final_chain;
+
+ // Find root certificates - they will not have any issuer
+ auto root_range = subject_of.equal_range(nullptr);
+
+ // Perform BFS for each root
+ for(auto it = root_range.first ; it != root_range.second ; ++it) {
+ mbedtls_x509_crt * root_cert = it->second;
+
+ if(visited.find(root_cert) == visited.end()) {
+ visited.insert(root_cert);
+ queue.push_back(root_cert);
+
+ while(!queue.empty()) {
+ auto s = queue.front();
+ // Write out chain from root back to leaf
+ final_chain.push_back(s);
+ queue.pop_front();
+
+ auto range = subject_of.equal_range(s);
+ for(auto it = range.first ; it != range.second ; ++it) {
+ mbedtls_x509_crt * cert = it->second;
+ if(visited.find(cert) == visited.end()) {
+ visited.insert(cert);
+ queue.push_back(cert);
+ }
+ }
+ }
+ }
+ }
+
+ fChain = nullptr;
+ fChainSize = 0;
+ fNumCerts = list.size();
+
+ // Rebuild certificate linked list
+ for(auto it = final_chain.begin() ; it != final_chain.end() ; ++it) {
+ auto cert(*it);
+ cert->next = fChain;
+ fChain = cert;
+ fChainSize += cert->raw.len;
+ }
+}
+
+#define PEM_BEGIN_CRT "-----BEGIN CERTIFICATE-----\n"
+#define PEM_END_CRT "-----END CERTIFICATE-----\n"
+
+std::string x509_crt_rewriter::emit_pem()
+{
+ BOOST_LOG_FUNCTION();
+ std::string buffer;
+
+ if(fChainSize == 0)
+ throw std::runtime_error("State failure");
+
+ // Always rewrite the chain as PEM
+
+ buffer.resize(fChainSize * 4 + fNumCerts * (sizeof(PEM_BEGIN_CRT) + sizeof(PEM_END_CRT)));
+
+ unsigned char * out_buffer = (unsigned char *)buffer.c_str();
+ size_t out_capacity = buffer.size();
+ size_t total_size = 0;
+ size_t this_len;
+
+ for(auto cert = fChain ; cert ; cert = cert->next) {
+ int error = mbedtls_pem_write_buffer(PEM_BEGIN_CRT,
+ PEM_END_CRT,
+ cert->raw.p,
+ cert->raw.len,
+ out_buffer + total_size,
+ out_capacity - total_size,
+ &this_len);
+
+ if(error != 0) {
+ throw std::runtime_error("Certificate write failure");
+ }
+
+ // Account for final 0 byte
+ total_size += this_len - 1;
+ }
+
+ buffer.resize(total_size);
+ buffer.push_back(0);
+
+ return buffer;
+}
--- /dev/null
+/******************************************************************
+ *
+ * Copyright 2017 Samsung Electronics All Rights Reserved.
+ *
+ * Author: Jaroslaw Pelczar <j.pelczar@samsung.com>
+ *
+ * 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 DCM_DAEMON_CERT_UTILS_H_
+#define DCM_DAEMON_CERT_UTILS_H_
+
+#include <mbedtls/x509_crt.h>
+#include <string>
+
+struct x509_crt_rewriter {
+private:
+ mbedtls_x509_crt * fChain;
+ size_t fChainSize = 0;
+ size_t fNumCerts = 0;
+
+public:
+ x509_crt_rewriter() :
+ fChain(new mbedtls_x509_crt())
+ {
+ mbedtls_x509_crt_init(fChain);
+ }
+
+ ~x509_crt_rewriter() {
+ mbedtls_x509_crt_free(fChain);
+ delete fChain;
+ }
+
+ int parse(const unsigned char * buffer, size_t length);
+ void sort_chain();
+ std::string emit_pem();
+};
+
+#endif /* DCM_DAEMON_CERT_UTILS_H_ */