Add client cache class for manage data robustly
[platform/core/security/pubkey-pinning.git] / src / common / src / tpkp_common.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16 /*
17  * @file        tpkp_common.cpp
18  * @author      Kyungwook Tak (k.tak@samsung.com)
19  * @version     1.0
20  * @brief       Https Public Key Pinning common implementation.
21  */
22 #include "tpkp_common.h"
23
24 #include <cstring>
25 #include <cctype>
26 #include <new>
27 #include <algorithm>
28 #include <iostream>
29
30 #include "net/http/transport_security_state.h"
31 #include "net/http/transport_security_state_static.h"
32
33 #include "tpkp_parser.h"
34
35 namespace {
36
37 template <typename T>
38 inline size_t _arraySize(const T &t)
39 {
40         return sizeof(t) / sizeof(*t);
41 }
42
43 } // anonymous namespace
44
45 namespace TPKP {
46
47 Exception::Exception(tpkp_e code, const std::string &message)
48         : m_code(code)
49         , m_message(message)
50 {}
51
52 const char *Exception::what(void) const noexcept
53 {
54         return m_message.c_str();
55 }
56
57 tpkp_e Exception::code(void) const noexcept
58 {
59         return m_code;
60 }
61
62 tpkp_e ExceptionSafe(const std::function<void()> &func)
63 {
64         try {
65                 func();
66                 return TPKP_E_NONE;
67         } catch (const Exception &e) {
68                 SLOGE("Exception: %s", e.what());
69                 return e.code();
70         } catch (const std::bad_alloc &e) {
71                 SLOGE("bad_alloc std exception: %s", e.what());
72                 return TPKP_E_MEMORY;
73         } catch (const std::exception &e) {
74                 SLOGE("std exception: %s", e.what());
75                 return TPKP_E_STD_EXCEPTION;
76         } catch (...) {
77                 SLOGE("Unhandled exception occured!");
78                 return TPKP_E_INTERNAL;
79         }
80 }
81
82 class Context::Impl {
83 public:
84         Impl() = delete;
85         virtual ~Impl();
86         explicit Impl(const std::string &url);
87
88         void addPubkeyHash(HashAlgo algo, const RawBuffer &hashBuf);
89         bool checkPubkeyPins(void);
90         bool hasPins(void);
91
92 private:
93         std::string m_host;
94         net::PreloadResult m_preloaded;
95         HashValueVector m_hashes;
96
97         bool LoadPreloadedPins(void);
98         bool HashesIntersect(const char *const *hashesArr);
99
100         class HashValuesEqual {
101         public:
102                 explicit HashValuesEqual(const char *chash);
103                 bool operator()(const HashValue &other) const;
104         private:
105                 const char *m_chash;
106         };
107 };
108
109 Context::Impl::Impl(const std::string &url)
110 {
111         m_host = Parser::extractHostname(url);
112
113         SLOGD("HPKP ready to check on host[%s]", m_host.c_str());
114
115         LoadPreloadedPins();
116         if (!m_preloaded.has_pins) {
117                 SLOGD("no pins on static pubkey list.");
118                 return;
119         }
120 }
121
122 Context::Impl::~Impl() {}
123
124 void Context::Impl::addPubkeyHash(HashAlgo algo, const RawBuffer &hashBuf)
125 {
126         m_hashes.emplace_back(algo, hashBuf);
127 }
128
129 bool Context::Impl::checkPubkeyPins(void)
130 {
131         if (!hasPins()) {
132                 SLOGD("no pins on static pubkey list.");
133                 return true;
134         }
135
136         const Pinset &pinset = kPinsets[m_preloaded.pinset_id];
137
138         if (HashesIntersect(pinset.rejected_pins)) {
139                 SLOGE("pubkey is in rejected pin!");
140                 return false;
141         }
142
143         if (!HashesIntersect(pinset.accepted_pins)) {
144                 SLOGE("pubkey cannot be found in accepted pins!");
145                 return false;
146         }
147
148         SLOGD("pubkey is pinned one!");
149
150         return true;
151 }
152
153 bool Context::Impl::hasPins(void)
154 {
155         return m_preloaded.has_pins;
156 }
157
158 bool Context::Impl::LoadPreloadedPins(void)
159 {
160         m_preloaded.has_pins = false;
161         if (!net::DecodeHSTSPreload(m_host, &m_preloaded))
162                 return false;
163
164         size_t arrsize = _arraySize(kPinsets);
165         if (m_preloaded.pinset_id >= static_cast<uint32>(arrsize))
166                 return false;
167
168         return true;
169 }
170
171 bool Context::Impl::HashesIntersect(const char *const *hashesArr)
172 {
173         if (!hashesArr)
174                 return false;
175
176         for (; *hashesArr != nullptr; hashesArr++) {
177                 if (std::find_if(
178                                 m_hashes.begin(),
179                                 m_hashes.end(),
180                                 HashValuesEqual{*hashesArr}) != m_hashes.end()) {
181                         SLOGD("hash intersect found!");
182                         return true;
183                 }
184         }
185
186         return false;
187 }
188
189 Context::Impl::HashValuesEqual::HashValuesEqual(const char *chash) : m_chash(chash) {}
190
191 bool Context::Impl::HashValuesEqual::operator()(const HashValue &other) const
192 {
193         size_t len = other.hash.size();
194
195         /*
196          * hash from decode preloaded value is base64 encoded,
197          * so it can be get length by strlen.
198          */
199         if (strlen(m_chash) != len)
200                 return false;
201
202         for (size_t i = 0; i < len; i++) {
203                 if (m_chash[i] != other.hash[i])
204                         return false;
205         }
206
207         return true;
208 }
209
210 Context::Context(const std::string &url) : pImpl(new Impl{url}) {}
211 Context::~Context() {}
212
213 void Context::addPubkeyHash(HashAlgo algo, const RawBuffer &hashBuf)
214 {
215         SLOGD("add public key hash of algo[%d]", algo);
216         pImpl->addPubkeyHash(algo, hashBuf);
217 }
218
219 bool Context::checkPubkeyPins(void)
220 {
221         return pImpl->checkPubkeyPins();
222 }
223
224 bool Context::hasPins(void)
225 {
226         return pImpl->hasPins();
227 }
228
229 }