summaryrefslogtreecommitdiffstats
path: root/xbmc/utils/Digest.cpp
blob: 445a75543523279e652e79c2cfc53c81be719588 (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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/*
 *  Copyright (C) 2018 Team Kodi
 *  This file is part of Kodi - https://kodi.tv
 *
 *  SPDX-License-Identifier: GPL-2.0-or-later
 *  See LICENSES/README.md for more information.
 */

#include "Digest.h"

#include "StringUtils.h"

#include <array>
#include <stdexcept>

#include <openssl/evp.h>

namespace KODI
{
namespace UTILITY
{

namespace
{

EVP_MD const * TypeToEVPMD(CDigest::Type type)
{
  switch (type)
  {
    case CDigest::Type::MD5:
      return EVP_md5();
    case CDigest::Type::SHA1:
      return EVP_sha1();
    case CDigest::Type::SHA256:
      return EVP_sha256();
    case CDigest::Type::SHA512:
      return EVP_sha512();
    default:
      throw std::invalid_argument("Unknown digest type");
  }
}

}

std::ostream& operator<<(std::ostream& os, TypedDigest const& digest)
{
  return os << "{" << CDigest::TypeToString(digest.type) << "}" << digest.value;
}

std::string CDigest::TypeToString(Type type)
{
  switch (type)
  {
    case Type::MD5:
      return "md5";
    case Type::SHA1:
      return "sha1";
    case Type::SHA256:
      return "sha256";
    case Type::SHA512:
      return "sha512";
    case Type::INVALID:
      return "invalid";
    default:
      throw std::invalid_argument("Unknown digest type");
  }
}

CDigest::Type CDigest::TypeFromString(std::string const& type)
{
  std::string typeLower{type};
  StringUtils::ToLower(typeLower);
  if (type == "md5")
  {
    return Type::MD5;
  }
  else if (type == "sha1")
  {
    return Type::SHA1;
  }
  else if (type == "sha256")
  {
    return Type::SHA256;
  }
  else if (type == "sha512")
  {
    return Type::SHA512;
  }
  else
  {
    throw std::invalid_argument(std::string("Unknown digest type \"") + type + "\"");
  }
}

void CDigest::MdCtxDeleter::operator()(EVP_MD_CTX* context)
{
  EVP_MD_CTX_destroy(context);
}

CDigest::CDigest(Type type)
: m_context{EVP_MD_CTX_create()}, m_md(TypeToEVPMD(type))
{
  if (1 != EVP_DigestInit_ex(m_context.get(), m_md, nullptr))
  {
    throw std::runtime_error("EVP_DigestInit_ex failed");
  }
}

void CDigest::Update(std::string const& data)
{
  Update(data.c_str(), data.size());
}

void CDigest::Update(void const* data, std::size_t size)
{
  if (m_finalized)
  {
    throw std::logic_error("Finalized digest cannot be updated any more");
  }

  if (1 != EVP_DigestUpdate(m_context.get(), data, size))
  {
    throw std::runtime_error("EVP_DigestUpdate failed");
  }
}

std::string CDigest::FinalizeRaw()
{
  if (m_finalized)
  {
    throw std::logic_error("Digest can only be finalized once");
  }

  m_finalized = true;

  std::array<unsigned char, 64> digest;
  std::size_t size = EVP_MD_size(m_md);
  if (size > digest.size())
  {
    throw std::runtime_error("Digest unexpectedly long");
  }
  if (1 != EVP_DigestFinal_ex(m_context.get(), digest.data(), nullptr))
  {
    throw std::runtime_error("EVP_DigestFinal_ex failed");
  }
  return {reinterpret_cast<char*> (digest.data()), size};
}

std::string CDigest::Finalize()
{
  return StringUtils::ToHexadecimal(FinalizeRaw());
}

std::string CDigest::Calculate(Type type, std::string const& data)
{
  CDigest digest{type};
  digest.Update(data);
  return digest.Finalize();
}

std::string CDigest::Calculate(Type type, void const* data, std::size_t size)
{
  CDigest digest{type};
  digest.Update(data, size);
  return digest.Finalize();
}

}
}