summaryrefslogtreecommitdiff
path: root/node/EllipticCurveKeyPair.cpp
diff options
context:
space:
mode:
authorAdam Ierymenko <adam.ierymenko@gmail.com>2013-07-04 16:56:19 -0400
committerAdam Ierymenko <adam.ierymenko@gmail.com>2013-07-04 16:56:19 -0400
commit150850b80012f852521c9935145cf966946334d5 (patch)
treec082369f2fef2515cfa2e4acf1b83250a3963158 /node/EllipticCurveKeyPair.cpp
downloadinfinitytier-150850b80012f852521c9935145cf966946334d5.tar.gz
infinitytier-150850b80012f852521c9935145cf966946334d5.zip
New git repository for release - version 0.2.0 tagged
Diffstat (limited to 'node/EllipticCurveKeyPair.cpp')
-rw-r--r--node/EllipticCurveKeyPair.cpp374
1 files changed, 374 insertions, 0 deletions
diff --git a/node/EllipticCurveKeyPair.cpp b/node/EllipticCurveKeyPair.cpp
new file mode 100644
index 00000000..bed0725e
--- /dev/null
+++ b/node/EllipticCurveKeyPair.cpp
@@ -0,0 +1,374 @@
+/*
+ * 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/
+ */
+
+#include <iostream>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <openssl/bn.h>
+#include <openssl/obj_mac.h>
+#include <openssl/rand.h>
+#include <openssl/ec.h>
+#include <openssl/ecdh.h>
+#include <openssl/ecdsa.h>
+#include <openssl/sha.h>
+
+#include "EllipticCurveKey.hpp"
+#include "EllipticCurveKeyPair.hpp"
+
+namespace ZeroTier {
+
+class _EC_Group
+{
+public:
+ _EC_Group()
+ {
+ g = EC_GROUP_new_by_curve_name(ZT_EC_OPENSSL_CURVE);
+ }
+ ~_EC_Group() {}
+ EC_GROUP *g;
+};
+static _EC_Group ZT_EC_GROUP;
+
+/* Key derivation function */
+static void *_zt_EC_KDF(const void *in,size_t inlen,void *out,size_t *outlen)
+{
+ SHA256_CTX sha;
+ unsigned char dig[SHA256_DIGEST_LENGTH];
+
+ SHA256_Init(&sha);
+ SHA256_Update(&sha,(const unsigned char *)in,inlen);
+ SHA256_Final(dig,&sha);
+ for(unsigned long i=0,k=0;i<(unsigned long)*outlen;) {
+ if (k == SHA256_DIGEST_LENGTH) {
+ k = 0;
+ SHA256_Init(&sha);
+ SHA256_Update(&sha,(const unsigned char *)in,inlen);
+ SHA256_Update(&sha,dig,SHA256_DIGEST_LENGTH);
+ SHA256_Final(dig,&sha);
+ }
+ ((unsigned char *)out)[i++] = dig[k++];
+ }
+
+ return out;
+}
+
+EllipticCurveKeyPair::EllipticCurveKeyPair() :
+ _pub(),
+ _priv(),
+ _internal_key((void *)0)
+{
+}
+
+EllipticCurveKeyPair::EllipticCurveKeyPair(const EllipticCurveKeyPair &pair) :
+ _pub(pair._pub),
+ _priv(pair._priv),
+ _internal_key((void *)0)
+{
+}
+
+EllipticCurveKeyPair::EllipticCurveKeyPair(const EllipticCurveKey &pubk,const EllipticCurveKey &privk) :
+ _pub(pubk),
+ _priv(privk),
+ _internal_key((void *)0)
+{
+}
+
+EllipticCurveKeyPair::~EllipticCurveKeyPair()
+{
+ if (_internal_key)
+ EC_KEY_free((EC_KEY *)_internal_key);
+}
+
+const EllipticCurveKeyPair &EllipticCurveKeyPair::operator=(const EllipticCurveKeyPair &pair)
+{
+ if (_internal_key)
+ EC_KEY_free((EC_KEY *)_internal_key);
+ _pub = pair._pub;
+ _priv = pair._priv;
+ _internal_key = (void *)0;
+ return *this;
+}
+
+bool EllipticCurveKeyPair::generate()
+{
+ unsigned char tmp[16384];
+ EC_KEY *key;
+ int len;
+
+ // Make sure OpenSSL libcrypto has sufficient randomness (on most
+ // platforms it auto-seeds, so this is a sanity check).
+ if (!RAND_status()) {
+#if defined(__APPLE__) || defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
+ FILE *rf = fopen("/dev/urandom","r");
+ if (rf) {
+ fread(tmp,sizeof(tmp),1,rf);
+ fclose(rf);
+ } else {
+ fprintf(stderr,"WARNING: cannot open /dev/urandom\n");
+ for(unsigned int i=0;i<sizeof(tmp);++i)
+ tmp[i] = (unsigned char)(rand() >> 3);
+ }
+ RAND_seed(tmp,sizeof(tmp));
+#else
+#ifdef _WIN32
+ error need win32;
+#else
+ error;
+#endif
+#endif
+ }
+
+ key = EC_KEY_new();
+ if (!key) return false;
+
+ if (!EC_KEY_set_group(key,ZT_EC_GROUP.g)) {
+ EC_KEY_free(key);
+ return false;
+ }
+
+ if (!EC_KEY_generate_key(key)) {
+ EC_KEY_free(key);
+ return false;
+ }
+
+ memset(_priv._key,0,sizeof(_priv._key));
+ len = BN_num_bytes(EC_KEY_get0_private_key(key));
+ if ((len > ZT_EC_PRIME_BYTES)||(len < 0)) {
+ EC_KEY_free(key);
+ return false;
+ }
+ BN_bn2bin(EC_KEY_get0_private_key(key),&(_priv._key[ZT_EC_PRIME_BYTES - len]));
+ _priv._bytes = ZT_EC_PRIME_BYTES;
+
+ memset(_pub._key,0,sizeof(_pub._key));
+ len = EC_POINT_point2oct(ZT_EC_GROUP.g,EC_KEY_get0_public_key(key),POINT_CONVERSION_COMPRESSED,_pub._key,sizeof(_pub._key),0);
+ if (len != ZT_EC_PUBLIC_KEY_BYTES) {
+ EC_KEY_free(key);
+ return false;
+ }
+ _pub._bytes = ZT_EC_PUBLIC_KEY_BYTES;
+
+ if (_internal_key)
+ EC_KEY_free((EC_KEY *)_internal_key);
+ _internal_key = key;
+
+ return true;
+}
+
+bool EllipticCurveKeyPair::agree(const EllipticCurveKey &theirPublicKey,unsigned char *agreedUponKey,unsigned int agreedUponKeyLength) const
+{
+ if (theirPublicKey._bytes != ZT_EC_PUBLIC_KEY_BYTES)
+ return false;
+
+ if (!_internal_key) {
+ if (!(const_cast <EllipticCurveKeyPair *> (this))->initInternalKey())
+ return false;
+ }
+
+ EC_POINT *pub = EC_POINT_new(ZT_EC_GROUP.g);
+ if (!pub)
+ return false;
+ EC_POINT_oct2point(ZT_EC_GROUP.g,pub,theirPublicKey._key,ZT_EC_PUBLIC_KEY_BYTES,0);
+
+ int i = ECDH_compute_key(agreedUponKey,agreedUponKeyLength,pub,(EC_KEY *)_internal_key,&_zt_EC_KDF);
+ EC_POINT_free(pub);
+
+ return (i == (int)agreedUponKeyLength);
+}
+
+std::string EllipticCurveKeyPair::sign(const void *sha256) const
+{
+ unsigned char buf[256];
+ std::string sigbin;
+
+ if (!_internal_key) {
+ if (!(const_cast <EllipticCurveKeyPair *> (this))->initInternalKey())
+ return std::string();
+ }
+
+ ECDSA_SIG *sig = ECDSA_do_sign((const unsigned char *)sha256,SHA256_DIGEST_LENGTH,(EC_KEY *)_internal_key);
+ if (!sig)
+ return std::string();
+
+ int rlen = BN_num_bytes(sig->r);
+ if ((rlen > 255)||(rlen <= 0)) {
+ ECDSA_SIG_free(sig);
+ return std::string();
+ }
+ sigbin.push_back((char)rlen);
+ BN_bn2bin(sig->r,buf);
+ sigbin.append((const char *)buf,rlen);
+
+ int slen = BN_num_bytes(sig->s);
+ if ((slen > 255)||(slen <= 0)) {
+ ECDSA_SIG_free(sig);
+ return std::string();
+ }
+ sigbin.push_back((char)slen);
+ BN_bn2bin(sig->s,buf);
+ sigbin.append((const char *)buf,slen);
+
+ ECDSA_SIG_free(sig);
+
+ return sigbin;
+}
+
+std::string EllipticCurveKeyPair::sign(const void *data,unsigned int len) const
+{
+ SHA256_CTX sha;
+ unsigned char dig[SHA256_DIGEST_LENGTH];
+
+ SHA256_Init(&sha);
+ SHA256_Update(&sha,(const unsigned char *)data,len);
+ SHA256_Final(dig,&sha);
+
+ return sign(dig);
+}
+
+bool EllipticCurveKeyPair::verify(const void *sha256,const EllipticCurveKey &pk,const void *sigbytes,unsigned int siglen)
+{
+ bool result = false;
+ ECDSA_SIG *sig = (ECDSA_SIG *)0;
+ EC_POINT *pub = (EC_POINT *)0;
+ EC_KEY *key = (EC_KEY *)0;
+ int rlen,slen;
+
+ if (!siglen)
+ goto verify_sig_return;
+ rlen = ((const unsigned char *)sigbytes)[0];
+ if (!rlen)
+ goto verify_sig_return;
+ if (siglen < (unsigned int)(rlen + 2))
+ goto verify_sig_return;
+ slen = ((const unsigned char *)sigbytes)[rlen + 1];
+ if (!slen)
+ goto verify_sig_return;
+ if (siglen < (unsigned int)(rlen + slen + 2))
+ goto verify_sig_return;
+
+ sig = ECDSA_SIG_new();
+ if (!sig)
+ goto verify_sig_return;
+
+ BN_bin2bn((const unsigned char *)sigbytes + 1,rlen,sig->r);
+ BN_bin2bn((const unsigned char *)sigbytes + (1 + rlen + 1),slen,sig->s);
+
+ pub = EC_POINT_new(ZT_EC_GROUP.g);
+ if (!pub)
+ goto verify_sig_return;
+ EC_POINT_oct2point(ZT_EC_GROUP.g,pub,pk._key,ZT_EC_PUBLIC_KEY_BYTES,0);
+
+ key = EC_KEY_new();
+ if (!key)
+ goto verify_sig_return;
+ if (!EC_KEY_set_group(key,ZT_EC_GROUP.g))
+ goto verify_sig_return;
+ EC_KEY_set_public_key(key,pub);
+
+ result = (ECDSA_do_verify((const unsigned char *)sha256,SHA256_DIGEST_LENGTH,sig,key) == 1);
+
+verify_sig_return:
+ if (key)
+ EC_KEY_free(key);
+ if (pub)
+ EC_POINT_free(pub);
+ if (sig)
+ ECDSA_SIG_free(sig);
+
+ return result;
+}
+
+bool EllipticCurveKeyPair::verify(const void *data,unsigned int len,const EllipticCurveKey &pk,const void *sigbytes,unsigned int siglen)
+{
+ SHA256_CTX sha;
+ unsigned char dig[SHA256_DIGEST_LENGTH];
+
+ SHA256_Init(&sha);
+ SHA256_Update(&sha,(const unsigned char *)data,len);
+ SHA256_Final(dig,&sha);
+
+ return verify(dig,pk,sigbytes,siglen);
+}
+
+bool EllipticCurveKeyPair::initInternalKey()
+{
+ EC_KEY *key;
+ EC_POINT *kxy;
+ BIGNUM *pn;
+
+ if (_priv._bytes != ZT_EC_PRIME_BYTES) return false;
+ if (_pub._bytes != ZT_EC_PUBLIC_KEY_BYTES) return false;
+
+ key = EC_KEY_new();
+ if (!key) return false;
+
+ if (!EC_KEY_set_group(key,ZT_EC_GROUP.g)) {
+ EC_KEY_free(key);
+ return false;
+ }
+
+ pn = BN_new();
+ if (!pn) {
+ EC_KEY_free(key);
+ return false;
+ }
+ if (!BN_bin2bn(_priv._key,ZT_EC_PRIME_BYTES,pn)) {
+ BN_free(pn);
+ EC_KEY_free(key);
+ return false;
+ }
+ if (!EC_KEY_set_private_key(key,pn)) {
+ BN_free(pn);
+ EC_KEY_free(key);
+ return false;
+ }
+ BN_free(pn);
+
+ kxy = EC_POINT_new(ZT_EC_GROUP.g);
+ if (!kxy) {
+ EC_KEY_free(key);
+ return false;
+ }
+ EC_POINT_oct2point(ZT_EC_GROUP.g,kxy,_pub._key,ZT_EC_PUBLIC_KEY_BYTES,0);
+ if (!EC_KEY_set_public_key(key,kxy)) {
+ EC_POINT_free(kxy);
+ EC_KEY_free(key);
+ return false;
+ }
+ EC_POINT_free(kxy);
+
+ if (_internal_key)
+ EC_KEY_free((EC_KEY *)_internal_key);
+ _internal_key = key;
+
+ return true;
+}
+
+} // namespace ZeroTier
+