summaryrefslogtreecommitdiff
path: root/src/hashFFI.cc
blob: ab2003098e167e641baf2b5f731bf8b774b7d065 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// Copyright (C) 2015 the Massachusetts Institute of Technology
//
// 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 "hashFFI.h"

#include <array>
#include <cstdio>

#include <openssl/md5.h>
extern "C" {
#include <urweb/urweb_cpp.h>
}

static_assert(sizeof(char) == 1, "char is not a single byte");
static_assert(sizeof(unsigned char) == 1, "unsigned char is not a single byte");

namespace {

// Asserts a condition without crashing or releasing information about where the
// error occurred.  This function is essential for web programming, where an
// attacker should not be able to bring down the app by causing an assertion
// failure.
void Assert(uw_context* const context, const bool condition,
            const failure_kind action, const char* const message) {
  if (!condition) {
    uw_error(context, action, message);
  }
}

void Assert(uw_context* const context,
            const bool condition, const char* const message) {
  Assert(context, condition, FATAL, message);
}

}  // namespace

uw_Basis_string uw_HashFFI_md5(uw_context* const context,
                               const uw_Basis_blob input) {
  using Digest = std::array<unsigned char, MD5_DIGEST_LENGTH>;
  // Perform the MD5 operation.
  Digest raw_result;
  MD5(reinterpret_cast<unsigned char*>(input.data), input.size,
      raw_result.data());
  // Convert it to a hex string.  This will be twice as large (two hex digits
  // per byte), plus an additional byte for the null terminator.
  const auto result_length = 2 * raw_result.size() + 1;
  uw_Basis_string result =
      reinterpret_cast<uw_Basis_string>(uw_malloc(context, result_length));
  Assert(context, result, BOUNDED_RETRY,
         "unable to allocate memory for digest");
  for (Digest::size_type i = 0; i < raw_result.size(); i++) {
    sprintf(result + 2 * i, "%02x", raw_result[i]);
  }
  // Make sure the string is properly terminated.
  for (std::size_t i = 0; i < result_length - 2; i++) {
    Assert(context, result[i] != '\0', "null byte in digest");
  }
  Assert(context, result[result_length - 1] == '\0',
         "failed to properly terminate digest");
  return result;
}