File: keys.cc

package info (click to toggle)
monotone 0.48-3
  • links: PTS
  • area: main
  • in suites: squeeze
  • size: 20,096 kB
  • ctags: 8,077
  • sloc: cpp: 81,000; sh: 6,402; perl: 1,241; lisp: 1,045; makefile: 655; python: 566; sql: 112; ansic: 52
file content (244 lines) | stat: -rw-r--r-- 6,484 bytes parent folder | download
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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
// Copyright (C) 2002 Graydon Hoare <graydon@pobox.com>
//
// This program is made available under the GNU GPL version 2.0 or
// greater. See the accompanying file COPYING for details.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE.

#include "base.hh"
#include <cstring>

#include "keys.hh"
#include "sanity.hh"
#include "constants.hh"
#include "platform.hh"
#include "transforms.hh"
#include "simplestring_xform.hh"
#include "charset.hh"
#include "lua_hooks.hh"
#include "options.hh"
#include "project.hh"
#include "key_store.hh"
#include "database.hh"

using std::string;
using std::vector;
using std::memset;

// there will probably forever be bugs in this file. it's very
// hard to get right, portably and securely. sorry about that.

// Loads a key pair for a given key id, considering it a user error
// if that key pair is not available.

void
load_key_pair(key_store & keys, key_id const & id)
{
  E(keys.key_pair_exists(id), origin::user,
    F("no key pair '%s' found in key store '%s'")
    % id % keys.get_key_dir());
}

void
load_key_pair(key_store & keys,
              key_id const & id,
              keypair & kp)
{
  load_key_pair(keys, id);
  keys.get_key_pair(id, kp);
}

void
load_key_pair(key_store & keys,
              key_id const & id,
              key_name & name,
              keypair & kp)
{
  load_key_pair(keys, id);
  keys.get_key_pair(id, name, kp);
}

namespace {
  void check_and_save_chosen_key(database & db,
                                 key_store & keys,
                                 key_id const & chosen_key)
  {
    // Ensure that the specified key actually exists.
    key_name name;
    keypair priv_key;
    load_key_pair(keys, chosen_key, name, priv_key);

    if (db.database_specified())
      {
        // If the database doesn't have this public key, add it now; otherwise
        // make sure the database and key-store agree on the public key.
        if (!db.public_key_exists(chosen_key))
          db.put_key(name, priv_key.pub);
        else
          {
            rsa_pub_key pub_key;
            db.get_key(chosen_key, pub_key);
            E(keys_match(name, pub_key, name, priv_key.pub),
              origin::no_fault,
              F("The key '%s' stored in your database does\n"
                "not match the version in your local key store!")
              % chosen_key);
          }
      }

    // Decrypt and cache the key now.
    keys.cache_decrypted_key(chosen_key);
  }
  bool get_only_key(key_store & keys, bool required, key_id & key)
  {
    vector<key_id> all_privkeys;
    keys.get_key_ids(all_privkeys);
    E(!required || !all_privkeys.empty(), origin::user,
      F("you have no private key to make signatures with\n"
        "perhaps you need to 'genkey <your email>'"));
    E(!required || all_privkeys.size() < 2, origin::user,
      F("you have multiple private keys\n"
        "pick one to use for signatures by adding "
        "'-k<keyname>' to your command"));

    if (all_privkeys.size() == 1)
      {
        key = all_privkeys[0];
        return true;
      }
    else
      {
        return false;
      }
  }
}

void
get_user_key(options const & opts, lua_hooks & lua,
             database & db, key_store & keys,
             project_t & project, key_id & key,
             key_cache_flag const cache)
{
  if (keys.have_signing_key())
    {
      key = keys.signing_key;
      return;
    }

  // key_given is not set if the key option was extracted from the workspace
  if (opts.key_given || !opts.signing_key().empty())
    {
      if (!opts.signing_key().empty())
        {
          key_identity_info identity;
          project.get_key_identity(keys, lua, opts.signing_key, identity);
          key = identity.id;
        }
      else
        {
          E(false, origin::user,
            F("a key is required for this operation, but the --key option "
              "was given with an empty argument"));
        }
    }
  else if (lua.hook_get_branch_key(opts.branch, keys, project, key))
    ; // the lua hook sets the key
  else
    {
      get_only_key(keys, true, key);
    }

  if (cache == cache_enable)
    check_and_save_chosen_key(db, keys, key);
}

void
cache_netsync_key(options const & opts,
                  database & db,
                  key_store & keys,
                  lua_hooks & lua,
                  project_t & project,
                  utf8 const & host,
                  globish const & include,
                  globish const & exclude,
                  netsync_key_requiredness key_requiredness)
{
  if (keys.have_signing_key())
    {
      return;
    }

  bool found_key = false;
  key_id key;

  // key_given is not set if the key option was extracted from the workspace
  if (opts.key_given || !opts.signing_key().empty())
    {
      key_identity_info identity;
      // maybe they specifically requested no key ("--key ''")
      if (!opts.signing_key().empty())
        {
          project.get_key_identity(keys, lua, opts.signing_key, identity);
          key = identity.id;
          found_key = true;
        }
    }
  else if (lua.hook_get_netsync_key(host, include, exclude, keys, project, key))
    {
      found_key = true;
    }
  else
    {
      found_key = get_only_key(keys, key_requiredness == KEY_REQUIRED, key);
    }

  if (found_key)
    {
      check_and_save_chosen_key(db, keys, key);
    }
}

void
cache_user_key(options const & opts, lua_hooks & lua,
               database & db, key_store & keys,
               project_t & project)
{
  key_id key;
  get_user_key(opts, lua, db, keys, project, key);
}

void
key_hash_code(key_name const & ident,
              rsa_pub_key const & pub,
              key_id & out)
{
  data tdat(ident() + ":" + remove_ws(encode_base64(pub)()),
            origin::internal);
  id tmp;
  calculate_ident(tdat, tmp);
  out = key_id(tmp);
}

// helper to compare if two keys have the same hash
// (ie are the same key)
bool
keys_match(key_name const & id1,
           rsa_pub_key const & key1,
           key_name const & id2,
           rsa_pub_key const & key2)
{
  key_id hash1, hash2;
  key_hash_code(id1, key1, hash1);
  key_hash_code(id2, key2, hash2);
  return hash1 == hash2;
}

// Local Variables:
// mode: C++
// fill-column: 76
// c-file-style: "gnu"
// indent-tabs-mode: nil
// End:
// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s: