monotone

monotone Mtn Source Tree

Root/keys.cc

1// Copyright (C) 2002 Graydon Hoare <graydon@pobox.com>
2//
3// This program is made available under the GNU GPL version 2.0 or
4// greater. See the accompanying file COPYING for details.
5//
6// This program is distributed WITHOUT ANY WARRANTY; without even the
7// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8// PURPOSE.
9
10#include "base.hh"
11#include <cstring>
12
13#include "keys.hh"
14#include "sanity.hh"
15#include "ui.hh"
16#include "constants.hh"
17#include "platform.hh"
18#include "transforms.hh"
19#include "simplestring_xform.hh"
20#include "charset.hh"
21#include "lua_hooks.hh"
22#include "options.hh"
23#include "key_store.hh"
24#include "database.hh"
25
26using std::string;
27using std::vector;
28using std::memset;
29
30// there will probably forever be bugs in this file. it's very
31// hard to get right, portably and securely. sorry about that.
32
33// "raw" passphrase prompter; unaware of passphrase caching or the laziness
34// hook. KEYID is used only in prompts. CONFIRM_PHRASE causes the user to
35// be prompted to type the same thing twice, and will loop if they don't
36// match. Prompts are worded slightly differently if GENERATING_KEY is true.
37void
38get_passphrase(utf8 & phrase,
39 rsa_keypair_id const & keyid,
40 bool confirm_phrase,
41 bool generating_key)
42{
43 string prompt1, prompt2;
44 char pass1[constants::maxpasswd];
45 char pass2[constants::maxpasswd];
46 int i = 0;
47
48 if (confirm_phrase && !generating_key)
49 prompt1 = (F("enter new passphrase for key ID [%s]: ") % keyid).str();
50 else
51 prompt1 = (F("enter passphrase for key ID [%s]: ") % keyid).str();
52
53 if (confirm_phrase)
54 prompt2 = (F("confirm passphrase for key ID [%s]: ") % keyid).str();
55
56 try
57 {
58 for (;;)
59 {
60 memset(pass1, 0, constants::maxpasswd);
61 memset(pass2, 0, constants::maxpasswd);
62 ui.ensure_clean_line();
63
64 read_password(prompt1, pass1, constants::maxpasswd);
65 if (!confirm_phrase)
66 break;
67
68 ui.ensure_clean_line();
69 read_password(prompt2, pass2, constants::maxpasswd);
70 if (strcmp(pass1, pass2) == 0)
71 break;
72
73 N(i++ < 2, F("too many failed passphrases"));
74 P(F("passphrases do not match, try again"));
75 }
76
77 external ext_phrase(pass1);
78 system_to_utf8(ext_phrase, phrase);
79 }
80 catch (...)
81 {
82 memset(pass1, 0, constants::maxpasswd);
83 memset(pass2, 0, constants::maxpasswd);
84 throw;
85 }
86 memset(pass1, 0, constants::maxpasswd);
87 memset(pass2, 0, constants::maxpasswd);
88}
89
90// Loads a key pair for a given key id, considering it a user error
91// if that key pair is not available.
92
93void
94load_key_pair(key_store & keys, rsa_keypair_id const & id)
95{
96 N(keys.key_pair_exists(id),
97 F("no key pair '%s' found in key store '%s'")
98 % id % keys.get_key_dir());
99}
100
101void
102load_key_pair(key_store & keys,
103 rsa_keypair_id const & id,
104 keypair & kp)
105{
106 load_key_pair(keys, id);
107 keys.get_key_pair(id, kp);
108}
109
110// Find the key to be used for signing certs. If possible, ensure the
111// database and the key_store agree on that key, and cache it in decrypted
112// form, so as not to bother the user for their passphrase later.
113
114void
115get_user_key(options const & opts, lua_hooks & lua,
116 database & db, key_store & keys, rsa_keypair_id & key)
117{
118 if (!keys.signing_key().empty())
119 {
120 key = keys.signing_key;
121 return;
122 }
123
124 if (!opts.signing_key().empty())
125 key = opts.signing_key;
126 else if (lua.hook_get_branch_key(opts.branchname, key))
127 ; // the lua hook sets the key
128 else
129 {
130 vector<rsa_keypair_id> all_privkeys;
131 keys.get_key_ids(all_privkeys);
132 N(all_privkeys.size() > 0,
133 F("you have no private key to make signatures with\n"
134 "perhaps you need to 'genkey <your email>'"));
135 N(all_privkeys.size() < 2,
136 F("you have multiple private keys\n"
137 "pick one to use for signatures by adding "
138 "'-k<keyname>' to your command"));
139
140 key = all_privkeys[0];
141 }
142
143 // Ensure that the specified key actually exists.
144 keypair priv_key;
145 load_key_pair(keys, key, priv_key);
146
147 if (db.database_specified())
148 {
149 // If the database doesn't have this public key, add it now; otherwise
150 // make sure the database and key-store agree on the public key.
151 if (!db.public_key_exists(key))
152 db.put_key(key, priv_key.pub);
153 else
154 {
155 rsa_pub_key pub_key;
156 db.get_key(key, pub_key);
157 E(keys_match(key, pub_key, key, priv_key.pub),
158 F("The key '%s' stored in your database does\n"
159 "not match the version in your local key store!") % key);
160 }
161 }
162
163 // Decrypt and cache the key now.
164 keys.cache_decrypted_key(key);
165}
166
167// As above, but does not report which key has been selected; for use when
168// the important thing is to have selected one and cached the decrypted key.
169void
170cache_user_key(options const & opts, lua_hooks & lua,
171 database & db, key_store & keys)
172{
173 rsa_keypair_id key;
174 get_user_key(opts, lua, db, keys, key);
175}
176
177void
178key_hash_code(rsa_keypair_id const & ident,
179 rsa_pub_key const & pub,
180 id & out)
181{
182 data tdat(ident() + ":" + remove_ws(encode_base64(pub)()));
183 calculate_ident(tdat, out);
184}
185
186void
187key_hash_code(rsa_keypair_id const & ident,
188 rsa_priv_key const & priv,
189 id & out)
190{
191 data tdat(ident() + ":" + remove_ws(encode_base64(priv)()));
192 calculate_ident(tdat, out);
193}
194
195// helper to compare if two keys have the same hash
196// (ie are the same key)
197bool
198keys_match(rsa_keypair_id const & id1,
199 rsa_pub_key const & key1,
200 rsa_keypair_id const & id2,
201 rsa_pub_key const & key2)
202{
203 id hash1, hash2;
204 key_hash_code(id1, key1, hash1);
205 key_hash_code(id2, key2, hash2);
206 return hash1 == hash2;
207}
208
209bool
210keys_match(rsa_keypair_id const & id1,
211 rsa_priv_key const & key1,
212 rsa_keypair_id const & id2,
213 rsa_priv_key const & key2)
214{
215 id hash1, hash2;
216 key_hash_code(id1, key1, hash1);
217 key_hash_code(id2, key2, hash2);
218 return hash1 == hash2;
219}
220
221// Local Variables:
222// mode: C++
223// fill-column: 76
224// c-file-style: "gnu"
225// indent-tabs-mode: nil
226// End:
227// 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