monotone

monotone Mtn Source Tree

Root/src/key_packet.cc

1// Copyright (C) 2010 Stephen Leake <stephen_leake@stephe-leake.org>
2// Copyright (C) 2002 Graydon Hoare <graydon@pobox.com>
3//
4// This program is made available under the GNU GPL version 2.0 or
5// greater. See the accompanying file COPYING for details.
6//
7// This program is distributed WITHOUT ANY WARRANTY; without even the
8// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
9// PURPOSE.
10//
11// This duplicates part of packet.cc; the intent is to deprecate that but
12// keep this.
13
14#include "base.hh"
15#include <sstream>
16#include <botan/botan.h>
17#include <botan/rsa.h>
18
19#include "cset.hh"
20#include "constants.hh"
21#include "key_packet.hh"
22#include "revision.hh"
23#include "sanity.hh"
24#include "transforms.hh"
25#include "simplestring_xform.hh"
26#include "cert.hh"
27#include "key_store.hh" // for keypair
28#include "char_classifiers.hh"
29#include "lazy_rng.hh"
30
31using std::istream;
32using std::istringstream;
33using std::make_pair;
34using std::map;
35using std::ostream;
36using std::pair;
37using std::string;
38
39using boost::shared_ptr;
40
41// --- key_packet writer ---
42
43key_packet_writer::key_packet_writer(ostream & o) : ost(o) {}
44
45void
46key_packet_writer::consume_public_key(key_name const & ident,
47 rsa_pub_key const & k)
48{
49 ost << "[pubkey " << ident() << "]\n"
50 << trim(encode_base64(k)()) << '\n'
51 << "[end]\n";
52}
53
54void
55key_packet_writer::consume_key_pair(key_name const & ident,
56 keypair const & kp)
57{
58 ost << "[keypair " << ident() << "]\n"
59 << trim(encode_base64(kp.pub)()) << "#\n"
60 << trim(encode_base64(kp.priv)()) << '\n'
61 << "[end]\n";
62}
63
64void
65key_packet_writer::consume_old_private_key(key_name const & ident,
66 old_arc4_rsa_priv_key const & k)
67{
68 ost << "[privkey " << ident() << "]\n"
69 << trim(encode_base64(k)()) << '\n'
70 << "[end]\n";
71}
72
73
74// --- reading key_packets from streams ---
75namespace
76{
77 struct
78 feed_key_packet_consumer : public origin_aware
79 {
80 size_t & count;
81 key_packet_consumer & cons;
82 feed_key_packet_consumer(size_t & count, key_packet_consumer & c,
83 origin::type whence)
84 : origin_aware(whence), count(count), cons(c)
85 {}
86 void validate_base64(string const & s) const
87 {
88 E(!s.empty()
89 && s.find_first_not_of(constants::legal_base64_bytes) == string::npos,
90 made_from,
91 F("malformed key_packet: invalid base64 block"));
92 }
93 void validate_arg_base64(string const & s) const
94 {
95 E(s.find_first_not_of(constants::legal_base64_bytes) == string::npos,
96 made_from,
97 F("malformed key_packet: invalid base64 block"));
98 }
99 void validate_key(string const & k) const
100 {
101 E(!k.empty()
102 && k.find_first_not_of(constants::legal_key_name_bytes) == string::npos,
103 made_from,
104 F("malformed key_packet: invalid key name"));
105 }
106 void validate_public_key_data(string const & name, string const & keydata) const
107 {
108 string decoded = decode_base64_as<string>(keydata, origin::user);
109 Botan::SecureVector<Botan::byte> key_block
110 (reinterpret_cast<Botan::byte const *>(decoded.c_str()), decoded.size());
111 try
112 {
113 Botan::X509::load_key(key_block);
114 }
115 catch (Botan::Decoding_Error const & e)
116 {
117 E(false, origin::user,
118 F("malformed key_packet: invalid public key data for '%s': %s")
119 % name % e.what());
120 }
121 }
122 void validate_private_key_data(string const & name, string const & keydata) const
123 {
124 string decoded = decode_base64_as<string>(keydata, origin::user);
125 Botan::DataSource_Memory ds(decoded);
126 try
127 {
128#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7)
129 Botan::PKCS8::load_key(ds, lazy_rng::get(), string());
130#else
131 Botan::PKCS8::load_key(ds, string());
132#endif
133 }
134 catch (Botan::Decoding_Error const & e)
135 {
136 E(false, origin::user,
137 F("malformed key_packet: invalid private key data for '%s': %s")
138 % name % e.what());
139 }
140 // since we do not want to prompt for a password to decode it finally,
141 // we ignore all other exceptions
142 catch (Botan::Invalid_Argument) {}
143 }
144 void validate_no_more_args(istringstream & iss) const
145 {
146 string next;
147 iss >> next;
148 E(next.empty(), made_from,
149 F("malformed key_packet: too many arguments in header"));
150 }
151
152 static void read_rest(istream& in, string& dest)
153 {
154
155 while (true)
156 {
157 string t;
158 in >> t;
159 if (t.empty()) break;
160 dest += t;
161 }
162 }
163 void pubkey_packet(string const & args, string const & body) const
164 {
165 L(FL("read pubkey key_packet"));
166 validate_key(args);
167 validate_base64(body);
168 validate_public_key_data(args, body);
169
170 cons.consume_public_key(key_name(args, made_from),
171 decode_base64_as<rsa_pub_key>(body, made_from));
172 }
173
174 void keypair_packet(string const & args, string const & body) const
175 {
176 L(FL("read keypair key_packet"));
177 string::size_type hashpos = body.find('#');
178 string pub(body, 0, hashpos);
179 string priv(body, hashpos+1);
180
181 validate_key(args);
182 validate_base64(pub);
183 validate_public_key_data(args, pub);
184 validate_base64(priv);
185 validate_private_key_data(args, priv);
186
187 cons.consume_key_pair(key_name(args, made_from),
188 keypair(decode_base64_as<rsa_pub_key>(pub, made_from),
189 decode_base64_as<rsa_priv_key>(priv, made_from)));
190 }
191
192 void privkey_packet(string const & args, string const & body) const
193 {
194 L(FL("read privkey key_packet"));
195 validate_key(args);
196 validate_base64(body);
197 cons.consume_old_private_key(key_name(args, made_from),
198 decode_base64_as<old_arc4_rsa_priv_key>(body, made_from));
199 }
200
201 void operator()(string const & type,
202 string const & args,
203 string const & body) const
204 {
205 if (type == "pubkey")
206 pubkey_packet(args, body);
207 else if (type == "keypair")
208 keypair_packet(args, body);
209 else if (type == "privkey")
210 privkey_packet(args, body);
211 else
212 {
213 W(F("unknown key_packet type: '%s'") % type);
214 return;
215 }
216 ++count;
217 }
218 };
219} // anonymous namespace
220
221static size_t
222extract_key_packets(string const & s, key_packet_consumer & cons)
223{
224 size_t count = 0;
225 feed_key_packet_consumer feeder(count, cons, origin::user);
226
227 string::const_iterator p, tbeg, tend, abeg, aend, bbeg, bend;
228
229 enum extract_state {
230 skipping, open_bracket, scanning_type, found_type,
231 scanning_args, found_args, scanning_body,
232 end_1, end_2, end_3, end_4, end_5
233 } state = skipping;
234
235 for (p = s.begin(); p != s.end(); p++)
236 switch (state)
237 {
238 case skipping: if (*p == '[') state = open_bracket; break;
239 case open_bracket:
240 if (is_alpha (*p))
241 state = scanning_type;
242 else
243 state = skipping;
244 tbeg = p;
245 break;
246 case scanning_type:
247 if (!is_alpha (*p))
248 {
249 state = is_space(*p) ? found_type : skipping;
250 tend = p;
251 }
252 break;
253 case found_type:
254 if (!is_space (*p))
255 {
256 state = (*p != ']') ? scanning_args : skipping;
257 abeg = p;
258 }
259 break;
260 case scanning_args:
261 if (*p == ']')
262 {
263 state = found_args;
264 aend = p;
265 }
266 break;
267 case found_args:
268 state = (*p != '[' && *p != ']') ? scanning_body : skipping;
269 bbeg = p;
270 break;
271 case scanning_body:
272 if (*p == '[')
273 {
274 state = end_1;
275 bend = p;
276 }
277 else if (*p == ']')
278 state = skipping;
279 break;
280
281 case end_1: state = (*p == 'e') ? end_2 : skipping; break;
282 case end_2: state = (*p == 'n') ? end_3 : skipping; break;
283 case end_3: state = (*p == 'd') ? end_4 : skipping; break;
284 case end_4:
285 if (*p == ']')
286 feeder(string(tbeg, tend), string(abeg, aend), string(bbeg, bend));
287 state = skipping;
288 break;
289 default:
290 I(false);
291 }
292 return count;
293}
294
295// this is same as rfind, but search area is haystack[start:] (from start to end of string)
296// haystack is searched, needle is pattern
297static size_t
298rfind_in_substr(std::string const& haystack, size_t start, std::string const& needle)
299{
300 I(start <= haystack.size());
301 const std::string::const_iterator result =
302 std::find_end(haystack.begin() + start, haystack.end(),
303 needle.begin(), needle.end());
304
305 if (result == haystack.end())
306 return std::string::npos;
307 else
308 return distance(haystack.begin(), result);
309}
310
311size_t
312read_key_packets(istream & in, key_packet_consumer & cons)
313{
314 string accum, tmp;
315 size_t count = 0;
316 size_t const bufsz = 0xff;
317 char buf[bufsz];
318 static string const end("[end]");
319 while(in)
320 {
321 size_t const next_search_pos = (accum.size() >= end.size())
322 ? accum.size() - end.size() : 0;
323 in.read(buf, bufsz);
324 accum.append(buf, in.gcount());
325 string::size_type endpos = string::npos;
326 endpos = rfind_in_substr(accum, next_search_pos, end);
327 if (endpos != string::npos)
328 {
329 endpos += end.size();
330 string tmp = accum.substr(0, endpos);
331 count += extract_key_packets(tmp, cons);
332 if (endpos < accum.size() - 1)
333 accum = accum.substr(endpos+1);
334 else
335 accum.clear();
336 }
337 }
338 return count;
339}
340
341// Local Variables:
342// mode: C++
343// fill-column: 76
344// c-file-style: "gnu"
345// indent-tabs-mode: nil
346// End:
347// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status