summaryrefslogtreecommitdiff
path: root/node/Identity.hpp
blob: 22e60fdebb3c7494d9ab5827e02e5e3badad2b7c (plain)
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
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
/*
 * ZeroTier One - Global Peer to Peer Ethernet
 * Copyright (C) 2012-2013  ZeroTier Networks LLC
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * --
 *
 * ZeroTier may be used and distributed under the terms of the GPLv3, which
 * are available at: http://www.gnu.org/licenses/gpl-3.0.html
 *
 * If you would like to embed ZeroTier into a commercial application or
 * redistribute it in a modified binary form, please contact ZeroTier Networks
 * LLC. Start here: http://www.zerotier.com/
 */

#ifndef _ZT_IDENTITY_HPP
#define _ZT_IDENTITY_HPP

#include <stdio.h>
#include <stdlib.h>
#include <string>

#include "EllipticCurveKey.hpp"
#include "EllipticCurveKeyPair.hpp"
#include "Array.hpp"
#include "Utils.hpp"
#include "Address.hpp"
#include "Buffer.hpp"

/**
 * Maximum length for a serialized identity
 */
#define IDENTITY_MAX_BINARY_SERIALIZED_LENGTH ((ZT_EC_MAX_BYTES * 2) + 256)

namespace ZeroTier {

/**
 * A ZeroTier identity
 *
 * An identity consists of a public key, a 40-bit ZeroTier address computed
 * from that key in a collision-resistant fashion, and a self-signature.
 *
 * The address derivation algorithm makes it computationally very expensive to
 * search for a different public key that duplicates an existing address. (See
 * code for deriveAddress() for this algorithm.)
 *
 * After derivation, the address must be checked against isReserved(). If the
 * address is reserved, generation is repeated until a valid address results.
 *
 * Serialization of an identity:
 *
 * <[5] address>		- 40-bit ZeroTier network address
 * <[1] type>			- Identity type ID (rest is type-dependent)
 * <[1] key length>		- Length of public key
 * <[n] public key>		- Elliptic curve public key
 * <[1] sig length>		- Length of ECDSA self-signature
 * <[n] signature>		- ECDSA signature of first four fields
 * [<[1] key length>]	- [Optional] Length of private key
 * [<[n] private key>]	- [Optional] Private key
 *
 * Local storage of an identity also requires storage of its private key.
 */
class Identity
{
public:
	/**
	 * Identity types
	 */
	enum Type
	{
		/* Elliptic curve NIST-P-521 and ECDSA signature */
		IDENTITY_TYPE_NIST_P_521 = 1
		/* We won't need another identity type until quantum computers with
		 * tens of thousands of qubits are a reality. */
	};

	Identity() :
		_keyPair((EllipticCurveKeyPair *)0)
	{
	}

	Identity(const Identity &id) :
		_keyPair((id._keyPair) ? new EllipticCurveKeyPair(*id._keyPair) : (EllipticCurveKeyPair *)0),
		_publicKey(id._publicKey),
		_address(id._address),
		_signature(id._signature)
	{
	}

	Identity(const char *str)
		throw(std::invalid_argument) :
		_keyPair((EllipticCurveKeyPair *)0)
	{
		if (!fromString(str))
			throw std::invalid_argument(std::string("invalid string-serialized identity: ") + str);
	}

	Identity(const std::string &str)
		throw(std::invalid_argument) :
		_keyPair((EllipticCurveKeyPair *)0)
	{
		if (!fromString(str))
			throw std::invalid_argument(std::string("invalid string-serialized identity: ") + str);
	}

	template<unsigned int C>
	Identity(const Buffer<C> &b,unsigned int startAt = 0)
		throw(std::out_of_range,std::invalid_argument) :
		_keyPair((EllipticCurveKeyPair *)0)
	{
		deserialize(b,startAt);
	}

	~Identity()
	{
		delete _keyPair;
	}

	inline Identity &operator=(const Identity &id)
	{
		_keyPair = (id._keyPair) ? new EllipticCurveKeyPair(*id._keyPair) : (EllipticCurveKeyPair *)0;
		_publicKey = id._publicKey;
		_address = id._address;
		_signature = id._signature;
		return *this;
	}

	/**
	 * Generate a new identity (address, key pair)
	 * 
	 * This is a somewhat time consuming operation by design, as the address
	 * is derived from the key using a purposefully expensive many-round
	 * hash/encrypt/hash operation. This took about two seconds on a 2.4ghz
	 * Intel Core i5 in 2013.
	 * 
	 * In the very unlikely event that a reserved address is created, generate
	 * will automatically run again.
	 */
	void generate();

	/**
	 * Performs local validation, with two levels available
	 * 
	 * With the parameter false, this performs self-signature verification
	 * which checks the basic integrity of the key and identity. Setting the
	 * parameter to true performs a fairly time consuming computation to
	 * check that the address was properly derived from the key. This is
	 * normally not done unless a conflicting identity is received, in
	 * which case the invalid identity is thrown out.
	 * 
	 * @param doAddressDerivationCheck If true, do the time-consuming address check
	 * @return True if validation check passes
	 */
	bool locallyValidate(bool doAddressDerivationCheck) const;

	/**
	 * @return Private key pair or NULL if not included with this identity
	 */
	inline const EllipticCurveKeyPair *privateKeyPair() const throw() { return _keyPair; }

	/**
	 * @return True if this identity has its private portion
	 */
	inline bool hasPrivate() const throw() { return (_keyPair != (EllipticCurveKeyPair *)0); }

	/**
	 * Shortcut method to perform key agreement with another identity
	 *
	 * This identity must have its private portion.
	 *
	 * @param id Identity to agree with
	 * @param key Result parameter to fill with key bytes
	 * @param klen Length of key in bytes
	 * @return Was agreement successful?
	 */
	inline bool agree(const Identity &id,void *key,unsigned int klen) const
	{
		if ((id)&&(_keyPair))
			return _keyPair->agree(id._publicKey,(unsigned char *)key,klen);
		return false;
	}

	/**
	 * Sign a hash with this identity's private key
	 *
	 * @param sha256 32-byte hash to sign
	 * @return ECDSA signature or empty string on failure or if identity has no private portion
	 */
	inline std::string sign(const void *sha256) const
	{
		if (_keyPair)
			return _keyPair->sign(sha256);
		return std::string();
	}

	/**
	 * Sign a block of data with this identity's private key
	 *
	 * This is a shortcut to SHA-256 hashing then signing.
	 *
	 * @param sha256 32-byte hash to sign
	 * @return ECDSA signature or empty string on failure or if identity has no private portion
	 */
	inline std::string sign(const void *data,unsigned int len) const
	{
		if (_keyPair)
			return _keyPair->sign(data,len);
		return std::string();
	}

	/**
	 * Verify something signed with this identity's public key
	 * 
	 * @param sha256 32-byte hash to verify
	 * @param sigbytes Signature bytes
	 * @param siglen Length of signature
	 * @return True if signature is valid
	 */
	inline bool verifySignature(const void *sha256,const void *sigbytes,unsigned int siglen) const
	{
		return EllipticCurveKeyPair::verify(sha256,_publicKey,sigbytes,siglen);
	}

	/**
	 * Verify something signed with this identity's public key
	 * 
	 * @param data Data to verify
	 * @param len Length of data to verify
	 * @param sigbytes Signature bytes
	 * @param siglen Length of signature
	 * @return True if signature is valid
	 */
	inline bool verifySignature(const void *data,unsigned int len,const void *sigbytes,unsigned int siglen) const
	{
		return EllipticCurveKeyPair::verify(data,len,_publicKey,sigbytes,siglen);
	}

	/**
	 * @return Public key (available in all identities)
	 */
	inline const EllipticCurveKey &publicKey() const throw() { return _publicKey; }

	/**
	 * @return Identity type
	 */
	inline Type type() const throw() { return IDENTITY_TYPE_NIST_P_521; }

	/**
	 * @return This identity's address
	 */
	inline const Address &address() const throw() { return _address; }

	/**
	 * Serialize this identity (binary)
	 * 
	 * @param b Destination buffer to append to
	 * @param includePrivate If true, include private key component (if present) (default: false)
	 * @throws std::out_of_range Buffer too small
	 */
	template<unsigned int C>
	inline void serialize(Buffer<C> &b,bool includePrivate = false) const
		throw(std::out_of_range)
	{
		_address.appendTo(b);
		b.append((unsigned char)IDENTITY_TYPE_NIST_P_521);
		b.append((unsigned char)(_publicKey.size() & 0xff));
		b.append(_publicKey.data(),_publicKey.size());
		b.append((unsigned char)(_signature.length() & 0xff));
		b.append(_signature);
		if ((includePrivate)&&(_keyPair)) {
			b.append((unsigned char)(_keyPair->priv().size() & 0xff));
			b.append(_keyPair->priv().data(),_keyPair->priv().size());
		} else b.append((unsigned char)0);
	}

	/**
	 * Deserialize a binary serialized identity
	 *
	 * If an exception is thrown, the Identity object is left in an undefined
	 * state and should not be used.
	 *
	 * @param b Buffer containing serialized data
	 * @param startAt Index within buffer of serialized data (default: 0)
	 * @return Length of serialized data read from buffer
	 * @throws std::out_of_range Buffer too small
	 * @throws std::invalid_argument Serialized data invalid
	 */
	template<unsigned int C>
	inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
		throw(std::out_of_range,std::invalid_argument)
	{
		delete _keyPair;
		_keyPair = (EllipticCurveKeyPair *)0;

		unsigned int p = startAt;

		_address.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
		p += ZT_ADDRESS_LENGTH;

		if (b[p++] != IDENTITY_TYPE_NIST_P_521)
			throw std::invalid_argument("Identity: deserialize(): unsupported identity type");

		unsigned int publicKeyLength = b[p++];
		if (!publicKeyLength)
			throw std::invalid_argument("Identity: deserialize(): no public key");
		_publicKey.set(b.field(p,publicKeyLength),publicKeyLength);
		p += publicKeyLength;

		unsigned int signatureLength = b[p++];
		if (!signatureLength)
			throw std::invalid_argument("Identity: deserialize(): no signature");
		_signature.assign((const char *)b.field(p,signatureLength),signatureLength);
		p += signatureLength;

		unsigned int privateKeyLength = b[p++];
		if (privateKeyLength) {
			_keyPair = new EllipticCurveKeyPair(_publicKey,EllipticCurveKey(b.field(p,privateKeyLength),privateKeyLength));
			p += privateKeyLength;
		}

		return (p - startAt);
	}

	/**
	 * Serialize to a more human-friendly string
	 *
	 * @param includePrivate If true, include private key (if it exists)
	 * @return ASCII string representation of identity
	 */
	std::string toString(bool includePrivate) const;

	/**
	 * Deserialize a human-friendly string
	 *
	 * Note: validation is for the format only. The locallyValidate() method
	 * must be used to check signature and address/key correspondence.
	 * 
	 * @param str String to deserialize
	 * @return True if deserialization appears successful
	 */
	bool fromString(const char *str);
	inline bool fromString(const std::string &str) { return fromString(str.c_str()); }

	/**
	 * @return True if this identity contains something
	 */
	inline operator bool() const throw() { return (_publicKey.size() != 0); }

	inline bool operator==(const Identity &id) const
		throw()
	{
		if (_address == id._address) {
			if ((_keyPair)&&(id._keyPair))
				return (*_keyPair == *id._keyPair);
			return (_publicKey == id._publicKey);
		}
		return false;
	}
	inline bool operator<(const Identity &id) const
		throw()
	{
		if (_address < id._address)
			return true;
		else if (_address == id._address)
			return (_publicKey < id._publicKey);
		return false;
	}
	inline bool operator!=(const Identity &id) const throw() { return !(*this == id); }
	inline bool operator>(const Identity &id) const throw() { return (id < *this); }
	inline bool operator<=(const Identity &id) const throw() { return !(id < *this); }
	inline bool operator>=(const Identity &id) const throw() { return !(*this < id); }

private:
	// Compute an address from public key bytes
	static Address deriveAddress(const void *keyBytes,unsigned int keyLen);

	EllipticCurveKeyPair *_keyPair;
	EllipticCurveKey _publicKey;
	Address _address;
	std::string _signature;
};

} // namespace ZeroTier

#endif