GOSTCoin addresses vainer
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1108 lines
25 KiB

/*
* Vanitygen, vanity bitcoin address generator
* Copyright (C) 2011 <samr7@cs.washington.edu>
*
* Vanitygen is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Vanitygen 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Vanitygen. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(_WIN32)
#define _USE_MATH_DEFINES
#endif /* defined(_WIN32) */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <openssl/bn.h>
#include <openssl/sha.h>
#include <openssl/ripemd.h>
#include <openssl/hmac.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/pkcs12.h>
#include "pattern.h"
#include "util.h"
#include "streebog.h"
const char *vg_b58_alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
const signed char vg_b58_reverse_map[256] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1,
-1, 9, 10, 11, 12, 13, 14, 15, 16, -1, 17, 18, 19, 20, 21, -1,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, -1, -1,
-1, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, -1, 44, 45, 46,
47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
};
void
fdumphex(FILE *fp, const unsigned char *src, size_t len)
{
size_t i;
for (i = 0; i < len; i++) {
fprintf(fp, "%02x", src[i]);
}
printf("\n");
}
void
fdumpbn(FILE *fp, const BIGNUM *bn)
{
char *buf;
buf = BN_bn2hex(bn);
fprintf(fp, "%s\n", buf ? buf : "0");
if (buf)
OPENSSL_free(buf);
}
void
dumphex(const unsigned char *src, size_t len)
{
fdumphex(stdout, src, len);
}
void
dumpbn(const BIGNUM *bn)
{
fdumpbn(stdout, bn);
}
/*
* Key format encode/decode
*/
void
vg_b58_encode_check(void *buf, size_t len, char *result, int gost)
{
unsigned char hash1[32];
unsigned char hash2[32];
int d, p;
BN_CTX *bnctx;
BIGNUM *bn, *bndiv, *bntmp;
BIGNUM bna, bnb, bnbase, bnrem;
unsigned char *binres;
int brlen, zpfx;
bnctx = BN_CTX_new();
BN_init(&bna);
BN_init(&bnb);
BN_init(&bnbase);
BN_init(&bnrem);
BN_set_word(&bnbase, 58);
bn = &bna;
bndiv = &bnb;
brlen = (2 * len) + 4;
binres = (unsigned char*) malloc(brlen);
memcpy(binres, buf, len);
if (gost)
gostd_hash (hash2, binres, len);
else
{
SHA256(binres, len, hash1);
SHA256(hash1, sizeof(hash1), hash2);
}
memcpy(&binres[len], hash2, 4);
BN_bin2bn(binres, len + 4, bn);
for (zpfx = 0; zpfx < (len + 4) && binres[zpfx] == 0; zpfx++);
p = brlen;
while (!BN_is_zero(bn)) {
BN_div(bndiv, &bnrem, bn, &bnbase, bnctx);
bntmp = bn;
bn = bndiv;
bndiv = bntmp;
d = BN_get_word(&bnrem);
binres[--p] = vg_b58_alphabet[d];
}
while (zpfx--) {
binres[--p] = vg_b58_alphabet[0];
}
memcpy(result, &binres[p], brlen - p);
result[brlen - p] = '\0';
free(binres);
BN_clear_free(&bna);
BN_clear_free(&bnb);
BN_clear_free(&bnbase);
BN_clear_free(&bnrem);
BN_CTX_free(bnctx);
}
#define skip_char(c) \
(((c) == '\r') || ((c) == '\n') || ((c) == ' ') || ((c) == '\t'))
int
vg_b58_decode_check(const char *input, void *buf, size_t len, int gost)
{
int i, l, c;
unsigned char *xbuf = NULL;
BIGNUM bn, bnw, bnbase;
BN_CTX *bnctx;
unsigned char hash1[32], hash2[32];
int zpfx;
int res = 0;
BN_init(&bn);
BN_init(&bnw);
BN_init(&bnbase);
BN_set_word(&bnbase, 58);
bnctx = BN_CTX_new();
/* Build a bignum from the encoded value */
l = strlen(input);
for (i = 0; i < l; i++) {
if (skip_char(input[i]))
continue;
c = vg_b58_reverse_map[(int)input[i]];
if (c < 0)
goto out;
BN_clear(&bnw);
BN_set_word(&bnw, c);
BN_mul(&bn, &bn, &bnbase, bnctx);
BN_add(&bn, &bn, &bnw);
}
/* Copy the bignum to a byte buffer */
for (i = 0, zpfx = 0; input[i]; i++) {
if (skip_char(input[i]))
continue;
if (input[i] != vg_b58_alphabet[0])
break;
zpfx++;
}
c = BN_num_bytes(&bn);
l = zpfx + c;
if (l < 5)
goto out;
xbuf = (unsigned char *) malloc(l);
if (!xbuf)
goto out;
if (zpfx)
memset(xbuf, 0, zpfx);
if (c)
BN_bn2bin(&bn, xbuf + zpfx);
/* Check the hash code */
l -= 4;
if (gost)
gostd_hash (hash2, xbuf, l);
else
{
SHA256(xbuf, l, hash1);
SHA256(hash1, sizeof(hash1), hash2);
}
if (memcmp(hash2, xbuf + l, 4))
goto out;
/* Buffer verified */
if (len) {
if (len > l)
len = l;
memcpy(buf, xbuf, len);
}
res = l;
out:
if (xbuf)
free(xbuf);
BN_clear_free(&bn);
BN_clear_free(&bnw);
BN_clear_free(&bnbase);
BN_CTX_free(bnctx);
return res;
}
void
vg_encode_address(const EC_POINT *ppoint, const EC_GROUP *pgroup,
int addrtype, char *result)
{
unsigned char eckey_buf[128], *pend;
unsigned char binres[21] = {0,};
unsigned char hash1[32], hash2[64];
pend = eckey_buf;
EC_POINT_point2oct(pgroup,
ppoint,
POINT_CONVERSION_UNCOMPRESSED,
eckey_buf,
sizeof(eckey_buf),
NULL);
pend = eckey_buf + 0x41;
binres[0] = addrtype;
if (addrtype == 38)
{
sph_gost512 (hash2, eckey_buf, pend - eckey_buf);
RIPEMD160(hash2, 64, &binres[1]);
}
else
{
SHA256(eckey_buf, pend - eckey_buf, hash1);
RIPEMD160(hash1, sizeof(hash1), &binres[1]);
}
vg_b58_encode_check(binres, sizeof(binres), result, addrtype == 38);
}
void
vg_encode_script_address(const EC_POINT *ppoint, const EC_GROUP *pgroup,
int addrtype, char *result)
{
unsigned char script_buf[69];
unsigned char *eckey_buf = script_buf + 2;
unsigned char binres[21] = {0,};
unsigned char hash1[32];
script_buf[ 0] = 0x51; // OP_1
script_buf[ 1] = 0x41; // pubkey length
// gap for pubkey
script_buf[67] = 0x51; // OP_1
script_buf[68] = 0xae; // OP_CHECKMULTISIG
EC_POINT_point2oct(pgroup,
ppoint,
POINT_CONVERSION_UNCOMPRESSED,
eckey_buf,
65,
NULL);
binres[0] = addrtype;
// TODO
SHA256(script_buf, 69, hash1);
RIPEMD160(hash1, sizeof(hash1), &binres[1]);
vg_b58_encode_check(binres, sizeof(binres), result, 0);
}
void
vg_encode_privkey(const EC_KEY *pkey, int addrtype, char *result)
{
unsigned char eckey_buf[128];
const BIGNUM *bn;
int nbytes;
bn = EC_KEY_get0_private_key(pkey);
eckey_buf[0] = addrtype;
nbytes = BN_num_bytes(bn);
assert(nbytes <= 32);
if (nbytes < 32)
memset(eckey_buf + 1, 0, 32 - nbytes);
BN_bn2bin(bn, &eckey_buf[33 - nbytes]);
vg_b58_encode_check(eckey_buf, 33, result, addrtype == 166);
}
int
vg_set_privkey(const BIGNUM *bnpriv, EC_KEY *pkey)
{
const EC_GROUP *pgroup;
EC_POINT *ppnt;
int res;
pgroup = EC_KEY_get0_group(pkey);
ppnt = EC_POINT_new(pgroup);
res = (ppnt &&
EC_KEY_set_private_key(pkey, bnpriv) &&
EC_POINT_mul(pgroup, ppnt, bnpriv, NULL, NULL, NULL) &&
EC_KEY_set_public_key(pkey, ppnt));
if (ppnt)
EC_POINT_free(ppnt);
if (!res)
return 0;
assert(EC_KEY_check_key(pkey));
return 1;
}
int
vg_decode_privkey(const char *b58encoded, EC_KEY *pkey, int *addrtype)
{
BIGNUM bnpriv;
unsigned char ecpriv[48];
int res;
res = vg_b58_decode_check(b58encoded, ecpriv, sizeof(ecpriv), 0);
if (res != 33)
return 0;
BN_init(&bnpriv);
BN_bin2bn(ecpriv + 1, res - 1, &bnpriv);
res = vg_set_privkey(&bnpriv, pkey);
BN_clear_free(&bnpriv);
*addrtype = ecpriv[0];
return 1;
}
#if OPENSSL_VERSION_NUMBER < 0x10000000L
/* The generic PBKDF2 function first appeared in OpenSSL 1.0 */
/* ====================================================================
* Copyright (c) 1999-2006 The OpenSSL Project. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the OpenSSL Project
* for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
*
* 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* licensing@OpenSSL.org.
*
* 5. Products derived from this software may not be called "OpenSSL"
* nor may "OpenSSL" appear in their names without prior written
* permission of the OpenSSL Project.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the OpenSSL Project
* for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
*
* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This product includes cryptographic software written by Eric Young
* (eay@cryptsoft.com). This product includes software written by Tim
* Hudson (tjh@cryptsoft.com).
*
*/
int
PKCS5_PBKDF2_HMAC(const char *pass, int passlen,
const unsigned char *salt, int saltlen, int iter,
const EVP_MD *digest,
int keylen, unsigned char *out)
{
unsigned char digtmp[EVP_MAX_MD_SIZE], *p, itmp[4];
int cplen, j, k, tkeylen, mdlen;
unsigned long i = 1;
HMAC_CTX hctx;
mdlen = EVP_MD_size(digest);
if (mdlen < 0)
return 0;
HMAC_CTX_init(&hctx);
p = out;
tkeylen = keylen;
if(!pass)
passlen = 0;
else if(passlen == -1)
passlen = strlen(pass);
while(tkeylen)
{
if(tkeylen > mdlen)
cplen = mdlen;
else
cplen = tkeylen;
/* We are unlikely to ever use more than 256 blocks (5120 bits!)
* but just in case...
*/
itmp[0] = (unsigned char)((i >> 24) & 0xff);
itmp[1] = (unsigned char)((i >> 16) & 0xff);
itmp[2] = (unsigned char)((i >> 8) & 0xff);
itmp[3] = (unsigned char)(i & 0xff);
HMAC_Init_ex(&hctx, pass, passlen, digest, NULL);
HMAC_Update(&hctx, salt, saltlen);
HMAC_Update(&hctx, itmp, 4);
HMAC_Final(&hctx, digtmp, NULL);
memcpy(p, digtmp, cplen);
for(j = 1; j < iter; j++)
{
HMAC(digest, pass, passlen,
digtmp, mdlen, digtmp, NULL);
for(k = 0; k < cplen; k++)
p[k] ^= digtmp[k];
}
tkeylen-= cplen;
i++;
p+= cplen;
}
HMAC_CTX_cleanup(&hctx);
return 1;
}
#endif /* OPENSSL_VERSION_NUMBER < 0x10000000L */
typedef struct {
int mode;
int iterations;
const EVP_MD *(*pbkdf_hash_getter)(void);
const EVP_CIPHER *(*cipher_getter)(void);
} vg_protkey_parameters_t;
static const vg_protkey_parameters_t protkey_parameters[] = {
{ 0, 4096, EVP_sha256, EVP_aes_256_cbc },
{ 0, 0, NULL, NULL },
{ 0, 0, NULL, NULL },
{ 0, 0, NULL, NULL },
{ 0, 0, NULL, NULL },
{ 0, 0, NULL, NULL },
{ 0, 0, NULL, NULL },
{ 0, 0, NULL, NULL },
{ 0, 0, NULL, NULL },
{ 0, 0, NULL, NULL },
{ 0, 0, NULL, NULL },
{ 0, 0, NULL, NULL },
{ 0, 0, NULL, NULL },
{ 0, 0, NULL, NULL },
{ 0, 0, NULL, NULL },
{ 0, 0, NULL, NULL },
{ 1, 4096, EVP_sha256, EVP_aes_256_cbc },
};
static int
vg_protect_crypt(int parameter_group,
unsigned char *data_in, int data_in_len,
unsigned char *data_out,
const char *pass, int enc)
{
EVP_CIPHER_CTX *ctx = NULL;
unsigned char *salt;
unsigned char keymaterial[EVP_MAX_KEY_LENGTH + EVP_MAX_IV_LENGTH +
EVP_MAX_MD_SIZE];
unsigned char hmac[EVP_MAX_MD_SIZE];
int hmac_len = 0, hmac_keylen = 0;
int salt_len;
int plaintext_len = 32;
int ciphertext_len;
int pkcs7_padding = 1;
const vg_protkey_parameters_t *params;
const EVP_CIPHER *cipher;
const EVP_MD *pbkdf_digest;
const EVP_MD *hmac_digest;
unsigned int hlen;
int opos, olen, oincr, nbytes;
int ipos;
int ret = 0;
ctx = EVP_CIPHER_CTX_new();
if (!ctx)
goto out;
if (parameter_group < 0) {
if (enc)
parameter_group = 0;
else
parameter_group = data_in[0];
} else {
if (!enc && (parameter_group != data_in[0]))
goto out;
}
if (parameter_group > (sizeof(protkey_parameters) /
sizeof(protkey_parameters[0])))
goto out;
params = &protkey_parameters[parameter_group];
if (!params->iterations || !params->pbkdf_hash_getter)
goto out;
pbkdf_digest = params->pbkdf_hash_getter();
cipher = params->cipher_getter();
if (params->mode == 0) {
/* Brief encoding */
salt_len = 4;
hmac_len = 8;
hmac_keylen = 16;
ciphertext_len = ((plaintext_len + cipher->block_size - 1) /
cipher->block_size) * cipher->block_size;
pkcs7_padding = 0;
hmac_digest = EVP_sha256();
} else {
/* PKCS-compliant encoding */
salt_len = 8;
ciphertext_len = ((plaintext_len + cipher->block_size) /
cipher->block_size) * cipher->block_size;
hmac_digest = NULL;
}
if (!enc && (data_in_len != (1 + ciphertext_len + hmac_len + salt_len)))
goto out;
if (!pass || !data_out) {
/* Format check mode */
ret = plaintext_len;
goto out;
}
if (!enc) {
salt = data_in + 1 + ciphertext_len + hmac_len;
} else if (salt_len) {
salt = data_out + 1 + ciphertext_len + hmac_len;
RAND_bytes(salt, salt_len);
} else {
salt = NULL;
}
PKCS5_PBKDF2_HMAC((const char *) pass, strlen(pass) + 1,
salt, salt_len,
params->iterations,
pbkdf_digest,
cipher->key_len + cipher->iv_len + hmac_keylen,
keymaterial);
if (!EVP_CipherInit(ctx, cipher,
keymaterial,
keymaterial + cipher->key_len,
enc)) {
fprintf(stderr, "ERROR: could not configure cipher\n");
goto out;
}
if (!pkcs7_padding)
EVP_CIPHER_CTX_set_padding(ctx, 0);
if (!enc) {
opos = 0;
olen = plaintext_len;
nbytes = ciphertext_len;
ipos = 1;
} else {
data_out[0] = parameter_group;
opos = 1;
olen = 1 + ciphertext_len + hmac_len + salt_len - opos;
nbytes = plaintext_len;
ipos = 0;
}
oincr = olen;
if (!EVP_CipherUpdate(ctx, data_out + opos, &oincr,
data_in + ipos, nbytes))
goto invalid_pass;
opos += oincr;
olen -= oincr;
oincr = olen;
if (!EVP_CipherFinal(ctx, data_out + opos, &oincr))
goto invalid_pass;
opos += oincr;
if (hmac_len) {
hlen = sizeof(hmac);
HMAC(hmac_digest,
keymaterial + cipher->key_len + cipher->iv_len,
hmac_keylen,
enc ? data_in : data_out, plaintext_len,
hmac, &hlen);
if (enc) {
memcpy(data_out + 1 + ciphertext_len, hmac, hmac_len);
} else if (memcmp(hmac,
data_in + 1 + ciphertext_len,
hmac_len))
goto invalid_pass;
}
if (enc) {
if (opos != (1 + ciphertext_len)) {
fprintf(stderr, "ERROR: plaintext size mismatch\n");
goto out;
}
opos += hmac_len + salt_len;
} else if (opos != plaintext_len) {
fprintf(stderr, "ERROR: plaintext size mismatch\n");
goto out;
}
ret = opos;
if (0) {
invalid_pass:
fprintf(stderr, "ERROR: Invalid password\n");
}
out:
OPENSSL_cleanse(hmac, sizeof(hmac));
OPENSSL_cleanse(keymaterial, sizeof(keymaterial));
if (ctx)
EVP_CIPHER_CTX_free(ctx);
return ret;
}
int
vg_protect_encode_privkey(char *out,
const EC_KEY *pkey, int keytype,
int parameter_group,
const char *pass)
{
unsigned char ecpriv[64];
unsigned char ecenc[128];
const BIGNUM *privkey;
int nbytes;
int restype;
restype = (keytype & 1) ? 79 : 32;
privkey = EC_KEY_get0_private_key(pkey);
nbytes = BN_num_bytes(privkey);
if (nbytes < 32)
memset(ecpriv, 0, 32 - nbytes);
BN_bn2bin(privkey, ecpriv + 32 - nbytes);
nbytes = vg_protect_crypt(parameter_group,
ecpriv, 32,
&ecenc[1], pass, 1);
if (nbytes <= 0)
return 0;
OPENSSL_cleanse(ecpriv, sizeof(ecpriv));
ecenc[0] = restype;
vg_b58_encode_check(ecenc, nbytes + 1, out, keytype == 166);
nbytes = strlen(out);
return nbytes;
}
int
vg_protect_decode_privkey(EC_KEY *pkey, int *keytype,
const char *encoded, const char *pass)
{
unsigned char ecpriv[64];
unsigned char ecenc[128];
BIGNUM bn;
int restype;
int res;
res = vg_b58_decode_check(encoded, ecenc, sizeof(ecenc), 0);
if ((res < 2) || (res > sizeof(ecenc)))
return 0;
switch (ecenc[0]) {
case 32: restype = 128; break;
case 79: restype = 239; break;
default:
return 0;
}
if (!vg_protect_crypt(-1,
ecenc + 1, res - 1,
pkey ? ecpriv : NULL,
pass, 0))
return 0;
res = 1;
if (pkey) {
BN_init(&bn);
BN_bin2bn(ecpriv, 32, &bn);
res = vg_set_privkey(&bn, pkey);
BN_clear_free(&bn);
OPENSSL_cleanse(ecpriv, sizeof(ecpriv));
}
*keytype = restype;
return res;
}
/*
* Besides the bitcoin-adapted formats, we also support PKCS#8.
*/
int
vg_pkcs8_encode_privkey(char *out, int outlen,
const EC_KEY *pkey, const char *pass)
{
EC_KEY *pkey_copy = NULL;
EVP_PKEY *evp_key = NULL;
PKCS8_PRIV_KEY_INFO *pkcs8 = NULL;
X509_SIG *pkcs8_enc = NULL;
BUF_MEM *memptr;
BIO *bio = NULL;
int res = 0;
pkey_copy = EC_KEY_dup(pkey);
if (!pkey_copy)
goto out;
evp_key = EVP_PKEY_new();
if (!evp_key || !EVP_PKEY_set1_EC_KEY(evp_key, pkey_copy))
goto out;
pkcs8 = EVP_PKEY2PKCS8(evp_key);
if (!pkcs8)
goto out;
bio = BIO_new(BIO_s_mem());
if (!bio)
goto out;
if (!pass) {
res = PEM_write_bio_PKCS8_PRIV_KEY_INFO(bio, pkcs8);
} else {
pkcs8_enc = PKCS8_encrypt(-1,
EVP_aes_256_cbc(),
pass, strlen(pass),
NULL, 0,
4096,
pkcs8);
if (!pkcs8_enc)
goto out;
res = PEM_write_bio_PKCS8(bio, pkcs8_enc);
}
BIO_get_mem_ptr(bio, &memptr);
res = memptr->length;
if (res < outlen) {
memcpy(out, memptr->data, res);
out[res] = '\0';
} else {
memcpy(out, memptr->data, outlen - 1);
out[outlen-1] = '\0';
}
out:
if (bio)
BIO_free(bio);
if (pkey_copy)
EC_KEY_free(pkey_copy);
if (evp_key)
EVP_PKEY_free(evp_key);
if (pkcs8)
PKCS8_PRIV_KEY_INFO_free(pkcs8);
if (pkcs8_enc)
X509_SIG_free(pkcs8_enc);
return res;
}
int
vg_pkcs8_decode_privkey(EC_KEY *pkey, const char *pem_in, const char *pass)
{
EC_KEY *pkey_in = NULL;
EC_KEY *test_key = NULL;
EVP_PKEY *evp_key = NULL;
PKCS8_PRIV_KEY_INFO *pkcs8 = NULL;
X509_SIG *pkcs8_enc = NULL;
BIO *bio = NULL;
int res = 0;
bio = BIO_new_mem_buf((char *)pem_in, strlen(pem_in));
if (!bio)
goto out;
pkcs8_enc = PEM_read_bio_PKCS8(bio, NULL, NULL, NULL);
if (pkcs8_enc) {
if (!pass)
return -1;
pkcs8 = PKCS8_decrypt(pkcs8_enc, pass, strlen(pass));
} else {
(void) BIO_reset(bio);
pkcs8 = PEM_read_bio_PKCS8_PRIV_KEY_INFO(bio, NULL, NULL, NULL);
}
if (!pkcs8)
goto out;
evp_key = EVP_PKCS82PKEY(pkcs8);
if (!evp_key)
goto out;
pkey_in = EVP_PKEY_get1_EC_KEY(evp_key);
if (!pkey_in)
goto out;
/* Expect a specific curve */
test_key = EC_KEY_new_by_curve_name(NID_secp256k1);
if (!test_key ||
EC_GROUP_cmp(EC_KEY_get0_group(pkey_in),
EC_KEY_get0_group(test_key),
NULL))
goto out;
if (!EC_KEY_copy(pkey, pkey_in))
goto out;
res = 1;
out:
if (bio)
BIO_free(bio);
if (test_key)
EC_KEY_free(pkey_in);
if (evp_key)
EVP_PKEY_free(evp_key);
if (pkcs8)
PKCS8_PRIV_KEY_INFO_free(pkcs8);
if (pkcs8_enc)
X509_SIG_free(pkcs8_enc);
return res;
}
int
vg_decode_privkey_any(EC_KEY *pkey, int *addrtype, const char *input,
const char *pass)
{
int res;
if (vg_decode_privkey(input, pkey, addrtype))
return 1;
if (vg_protect_decode_privkey(pkey, addrtype, input, NULL)) {
if (!pass)
return -1;
return vg_protect_decode_privkey(pkey, addrtype, input, pass);
}
res = vg_pkcs8_decode_privkey(pkey, input, pass);
if (res > 0) {
/* Assume main network address */
*addrtype = 128;
}
return res;
}
int
vg_read_password(char *buf, size_t size)
{
return !EVP_read_pw_string(buf, size, "Enter new password:", 1);
}
/*
* Password complexity checker
* Heavily inspired by, but a simplification of "How Secure Is My Password?",
* http://howsecureismypassword.net/
*/
static unsigned char ascii_class[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
5, 4, 5, 4, 4, 4, 4, 5, 4, 4, 4, 4, 5, 4, 5, 5,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 5, 5, 4, 5, 5,
4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 4, 4,
5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 0,
};
int
vg_check_password_complexity(const char *pass, int verbose)
{
int i, len;
int classes[6] = { 0, };
const char *crackunit = "seconds";
int char_complexity = 0;
double crackops, cracktime;
int weak;
/*
* This number reflects a resourceful attacker with
* USD >$20K in 2011 hardware
*/
const int rate = 250000000;
/* Consider the password weak if it can be cracked in <1 year */
const int weak_threshold = (60*60*24*365);
len = strlen(pass);
for (i = 0; i < len; i++) {
if (pass[i] > sizeof(ascii_class))
/* FIXME: skip the rest of the UTF8 char */
classes[5]++;
else if (!ascii_class[(int)pass[i]])
continue;
else
classes[(int)ascii_class[(int)pass[i]] - 1]++;
}
if (classes[0])
char_complexity += 26;
if (classes[1])
char_complexity += 26;
if (classes[2])
char_complexity += 10;
if (classes[3])
char_complexity += 14;
if (classes[4])
char_complexity += 19;
if (classes[5])
char_complexity += 32; /* oversimplified */
/* This assumes brute-force and oversimplifies the problem */
crackops = pow((double)char_complexity, (double)len);
cracktime = (crackops * (1 - (1/M_E))) / rate;
weak = (cracktime < weak_threshold);
if (cracktime > 60.0) {
cracktime /= 60.0;
crackunit = "minutes";
if (cracktime > 60.0) {
cracktime /= 60.0;
crackunit = "hours";
if (cracktime > 24.0) {
cracktime /= 24;
crackunit = "days";
if (cracktime > 365.0) {
cracktime /= 365.0;
crackunit = "years";
}
}
}
}
/* Complain by default about weak passwords */
if ((weak && (verbose > 0)) || (verbose > 1)) {
if (cracktime < 1.0) {
fprintf(stderr,
"Estimated password crack time: >1 %s\n",
crackunit);
} else if (cracktime < 1000000) {
fprintf(stderr,
"Estimated password crack time: %.1f %s\n",
cracktime, crackunit);
} else {
fprintf(stderr,
"Estimated password crack time: %e %s\n",
cracktime, crackunit);
}
if (!classes[0] && !classes[1] && classes[2] &&
!classes[3] && !classes[4] && !classes[5]) {
fprintf(stderr,
"WARNING: Password contains only numbers\n");
}
else if (!classes[2] && !classes[3] && !classes[4] &&
!classes[5]) {
if (!classes[0] || !classes[1]) {
fprintf(stderr,
"WARNING: Password contains "
"only %scase letters\n",
classes[0] ? "lower" : "upper");
} else {
fprintf(stderr,
"WARNING: Password contains "
"only letters\n");
}
}
}
return !weak;
}
/*
* Pattern file reader
* Absolutely disgusting, unable to free the pattern list when it's done
*/
int
vg_read_file(FILE *fp, char ***result, int *rescount)
{
int ret = 1;
char **patterns;
char *buf = NULL, *obuf, *pat;
const int blksize = 16*1024;
int nalloc = 16;
int npatterns = 0;
int count, pos;
patterns = (char**) malloc(sizeof(char*) * nalloc);
count = 0;
pos = 0;
while (1) {
obuf = buf;
buf = (char *) malloc(blksize);
if (!buf) {
ret = 0;
break;
}
if (pos < count) {
memcpy(buf, &obuf[pos], count - pos);
}
pos = count - pos;
count = fread(&buf[pos], 1, blksize - pos, fp);
if (count < 0) {
fprintf(stderr,
"Error reading file: %s\n", strerror(errno));
ret = 0;
}
if (count <= 0)
break;
count += pos;
pat = buf;
while (pos < count) {
if ((buf[pos] == '\r') || (buf[pos] == '\n')) {
buf[pos] = '\0';
if (pat) {
if (npatterns == nalloc) {
nalloc *= 2;
patterns = (char**)
realloc(patterns,
sizeof(char*) *
nalloc);
}
patterns[npatterns] = pat;
npatterns++;
pat = NULL;
}
}
else if (!pat) {
pat = &buf[pos];
}
pos++;
}
pos = pat ? (pat - buf) : count;
}
*result = patterns;
*rescount = npatterns;
return ret;
}