a3d76c63f0089bca12fab84a4224052cbe0e42f2
[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 <sys/syscall.h>
25 #include <unistd.h>
26
27 #include <cstring>
28 #include <cctype>
29 #include <new>
30 #include <algorithm>
31 #include <iostream>
32
33 #include "net/http/transport_security_state.h"
34 #include "net/http/transport_security_state_static.h"
35
36 #include "tpkp_parser.h"
37
38 namespace {
39
40 template <typename T>
41 inline size_t _arraySize(const T &t)
42 {
43         return sizeof(t) / sizeof(*t);
44 }
45
46 } // anonymous namespace
47
48 namespace TPKP {
49
50 pid_t getThreadId()
51 {
52         return syscall(SYS_gettid);
53 }
54
55 Exception::Exception(tpkp_e code, const std::string &message)
56         : m_code(code)
57         , m_message(message)
58 {}
59
60 const char *Exception::what(void) const noexcept
61 {
62         return m_message.c_str();
63 }
64
65 tpkp_e Exception::code(void) const noexcept
66 {
67         return m_code;
68 }
69
70 tpkp_e ExceptionSafe(const std::function<void()> &func)
71 {
72         try {
73                 func();
74                 return TPKP_E_NONE;
75         } catch (const Exception &e) {
76                 SLOGE("Exception: %s", e.what());
77                 return e.code();
78         } catch (const std::bad_alloc &e) {
79                 SLOGE("bad_alloc std exception: %s", e.what());
80                 return TPKP_E_MEMORY;
81         } catch (const std::exception &e) {
82                 SLOGE("std exception: %s", e.what());
83                 return TPKP_E_STD_EXCEPTION;
84         } catch (...) {
85                 SLOGE("Unhandled exception occured!");
86                 return TPKP_E_INTERNAL;
87         }
88 }
89
90 class Context::Impl {
91 public:
92         Impl() = delete;
93         virtual ~Impl();
94         explicit Impl(const std::string &url);
95
96         void addPubkeyHash(HashAlgo algo, const RawBuffer &hashBuf);
97         bool checkPubkeyPins(void);
98         bool hasPins(void);
99
100 private:
101         std::string m_host;
102         net::PreloadResult m_preloaded;
103         HashValueVector m_hashes;
104
105         bool LoadPreloadedPins(void);
106         bool HashesIntersect(const char *const *hashesArr);
107
108         class HashValuesEqual {
109         public:
110                 explicit HashValuesEqual(const char *chash);
111                 bool operator()(const HashValue &other) const;
112         private:
113                 const char *m_chash;
114         };
115 };
116
117 Context::Impl::Impl(const std::string &url)
118 {
119         m_host = Parser::extractHostname(url);
120
121         SLOGD("HPKP ready to check on host[%s]", m_host.c_str());
122
123         LoadPreloadedPins();
124         if (!m_preloaded.has_pins) {
125                 SLOGD("no pins on static pubkey list.");
126                 return;
127         }
128 }
129
130 Context::Impl::~Impl() {}
131
132 void Context::Impl::addPubkeyHash(HashAlgo algo, const RawBuffer &hashBuf)
133 {
134         m_hashes.emplace_back(algo, hashBuf);
135 }
136
137 bool Context::Impl::checkPubkeyPins(void)
138 {
139         if (!hasPins()) {
140                 SLOGD("no pins on static pubkey list.");
141                 return true;
142         }
143
144         const Pinset &pinset = kPinsets[m_preloaded.pinset_id];
145
146         if (HashesIntersect(pinset.rejected_pins)) {
147                 SLOGE("pubkey is in rejected pin!");
148                 return false;
149         }
150
151         if (!HashesIntersect(pinset.accepted_pins)) {
152                 SLOGE("pubkey cannot be found in accepted pins!");
153                 return false;
154         }
155
156         SLOGD("pubkey is pinned one!");
157
158         return true;
159 }
160
161 bool Context::Impl::hasPins(void)
162 {
163         return m_preloaded.has_pins;
164 }
165
166 bool Context::Impl::LoadPreloadedPins(void)
167 {
168         m_preloaded.has_pins = false;
169         if (!net::DecodeHSTSPreload(m_host, &m_preloaded))
170                 return false;
171
172         size_t arrsize = _arraySize(kPinsets);
173         if (m_preloaded.pinset_id >= static_cast<uint32>(arrsize))
174                 return false;
175
176         return true;
177 }
178
179 bool Context::Impl::HashesIntersect(const char *const *hashesArr)
180 {
181         if (!hashesArr)
182                 return false;
183
184         for (; *hashesArr != nullptr; hashesArr++) {
185                 if (std::find_if(
186                                 m_hashes.begin(),
187                                 m_hashes.end(),
188                                 HashValuesEqual{*hashesArr}) != m_hashes.end()) {
189                         SLOGD("hash intersect found!");
190                         return true;
191                 }
192         }
193
194         return false;
195 }
196
197 Context::Impl::HashValuesEqual::HashValuesEqual(const char *chash) : m_chash(chash) {}
198
199 bool Context::Impl::HashValuesEqual::operator()(const HashValue &other) const
200 {
201         size_t len = other.hash.size();
202
203         /*
204          * hash from decode preloaded value is base64 encoded,
205          * so it can be get length by strlen.
206          */
207         if (strlen(m_chash) != len)
208                 return false;
209
210         for (size_t i = 0; i < len; i++) {
211                 if (m_chash[i] != other.hash[i])
212                         return false;
213         }
214
215         return true;
216 }
217
218 Context::Context(const std::string &url) : pImpl(new Impl{url}) {}
219 Context::~Context() {}
220
221 void Context::addPubkeyHash(HashAlgo algo, const RawBuffer &hashBuf)
222 {
223         SLOGD("add public key hash of algo[%d]", algo);
224         pImpl->addPubkeyHash(algo, hashBuf);
225 }
226
227 bool Context::checkPubkeyPins(void)
228 {
229         return pImpl->checkPubkeyPins();
230 }
231
232 bool Context::hasPins(void)
233 {
234         return pImpl->hasPins();
235 }
236
237 }