/** * @brief * Serialize an EC public key to be shared. * @param * key a pointer to the EC key pair to have its public key serialized. * @param * outsize a pointer to a variable that will receive the length of the * serialized key on success. * @return * a pointer to the serialized EC public key on success, or NULL on failure. */ unsigned char * _serialize_ec_pubkey(EC_KEY *key, size_t *outsize) { unsigned char *buf = NULL; if (!key || !outsize) { RET_ERROR_PTR(ERR_BAD_PARAM, NULL); } stringer_t *pub; if (!(pub = secp256k1_public_get(key, MANAGEDBUF(33)))) { PUSH_ERROR_OPENSSL(); RET_ERROR_PTR(ERR_UNSPEC, "unable to serialize EC public key"); } buf = mm_alloc(33); memmove(buf, st_data_get(pub), st_length_get(pub)); *outsize = st_length_get(pub); // EC_KEY_set_conv_form_d(key, POINT_CONVERSION_COMPRESSED); // if ((bsize = i2o_ECPublicKey_d(key, &buf)) < 0) { // PUSH_ERROR_OPENSSL(); // RET_ERROR_PTR(ERR_UNSPEC, "unable to serialize EC public key"); // } // *outsize = bsize; return buf; }
/** * @brief Retrieves the encryption key from the keys file. * @param filename Null terminated filename string. * @return Pointer to the elliptic curve encryption key. * @free_using{free_ec_key} */ static EC_KEY *keys_fetch_enc_key(const char *filename) { size_t keys_len; unsigned char *keys_bin; EC_KEY *key; if(!filename) { RET_ERROR_PTR(ERR_BAD_PARAM, NULL); } else if(!strlen(filename)) { RET_ERROR_PTR(ERR_BAD_PARAM, NULL); } if(!(keys_bin = keys_file_serialize(filename, &keys_len))) { RET_ERROR_PTR(ERR_UNSPEC, "could not retrieve keys binary string"); } key = keys_serial_get_enc_key(keys_bin, keys_len); _secure_wipe(keys_bin, keys_len); free(keys_bin); if (!key) { RET_ERROR_PTR_FMT(ERR_UNSPEC, "could not retrieve ed25519 signing key from %s", filename); } return key; }
/** * @brief * Clone a pointer chain. * @param buf * a pointer to the pointer chain to be cloned. * @return * a pointer to the cloned pointer chain on success, or NULL on failure. * @free_usingref{ptr_chain_free} */ void * _ptr_chain_clone(void *buf) { unsigned char **result, **inptr = (unsigned char **)buf; size_t totsize; int nitems; if (!buf) { RET_ERROR_PTR(ERR_BAD_PARAM, NULL); } if ((nitems = _count_ptr_chain(buf)) < 0) { RET_ERROR_PTR(ERR_UNSPEC, "unable to count items in pointer chain"); } else if (!nitems) { RET_ERROR_PTR(ERR_UNSPEC, "cannot clone empty pointer chain"); } totsize = (nitems + 1) * sizeof(unsigned char *); if (!(result = malloc(totsize))) { PUSH_ERROR_SYSCALL("malloc"); RET_ERROR_PTR(ERR_NOMEM, NULL); } memset(result, 0, totsize); for (int i = 0; i < nitems; i++) { result[i] = inptr[i]; } return result; }
/** * @brief * Load an EC public key from a file. * @param filename * the name of the file from which the key should be loaded * @return * a pointer to the deserialized public key from the the file. */ EC_KEY * _load_ec_pubkey(char const *filename) { char *filedata; unsigned char *bin; size_t binsize; EC_KEY *result; if (!filename) { RET_ERROR_PTR(ERR_BAD_PARAM, NULL); } if (!(filedata = _read_pem_data(filename, "PUBLIC KEY", 1))) { RET_ERROR_PTR(ERR_UNSPEC, "could not read ec pubkey pem file"); } bin = _b64decode(filedata, strlen(filedata), &binsize); _secure_wipe(filedata, strlen(filedata)); free(filedata); if (!bin) { RET_ERROR_PTR(ERR_UNSPEC, "could not decode b64 data"); } result = _deserialize_ec_pubkey(bin, binsize); _secure_wipe(bin, binsize); free(bin); if (!result) { RET_ERROR_PTR(ERR_UNSPEC, "could not deserialize binary ec pubkey"); } return result; }
/** * @brief * Decode a base64-encoded string without any padding characters and return * the result. * @param buf * a pointer to a buffer containing the data to be decoded. * @param len * the length, in bytes, of the buffer to be decoded. * @param outlen * a pointer to a variable to receive the length of the decoded data. * @return * a pointer to a newly allocated buffer containing the base64-decoded, or * NULL on failure. * @free_using{free} */ unsigned char * _b64decode_nopad(char const *buf, size_t len, size_t *outlen) { unsigned char *result; char *padded; size_t padlen; if (!buf || !len || !outlen) { RET_ERROR_PTR(ERR_BAD_PARAM, NULL); } if (!(len % 4)) { return (_b64decode(buf, len, outlen)); } padlen = (len + 4) & ~(3); if (!(padded = malloc(padlen + 1))) { PUSH_ERROR_SYSCALL("malloc"); RET_ERROR_PTR(ERR_NOMEM, "could not allocate space for temporary string"); } memset(padded, '=', padlen); padded[padlen] = 0; memcpy(padded, buf, len); result = _b64decode(padded, padlen, outlen); free(padded); return result; }
/** * @brief * Sign a body of data using the ECDSA algorithm. * @param hash * a pointer to the hashed data buffer to be signed. * @param hlen * the length, in bytes, of the hashed data to be signed. * @param key * the EC key which will have its private portion used to sign the supplied data. * @param siglen * a pointer to a variable that will receive the length of the data signature * buffer on success. * @return * NULL on failure, or a pointer to the newly allocated signature data buffer * on success. */ unsigned char * _ec_sign_data(unsigned char const *hash, size_t hlen, EC_KEY *key, size_t *siglen) { ECDSA_SIG *signature; unsigned char *buf = NULL; int bsize; if (!hash || !hlen || !key || !siglen) { RET_ERROR_PTR(ERR_BAD_PARAM, NULL); } if (!(signature = ECDSA_do_sign_d(hash, hlen, key))) { PUSH_ERROR_OPENSSL(); RET_ERROR_PTR(ERR_UNSPEC, "unable to take ECDSA signature of hash buffer"); } if ((bsize = i2d_ECDSA_SIG_d(signature, &buf)) < 0) { PUSH_ERROR_OPENSSL(); ECDSA_SIG_free_d(signature); RET_ERROR_PTR(ERR_UNSPEC, "unable to serialize ECDSA signature"); } ECDSA_SIG_free_d(signature); *siglen = bsize; return buf; }
/** * @brief * Get the human-readable, US-specific representation of a specified UNIX * time. * @param time * the time to be converted to a human-readable string. * @param local * if set, return the time string as a local time; if not, as UTC. * @return * a string containing the formatted date-time; or NULL on errors. * @free_using{free} */ char * _get_chr_date(time_t time, int local) { struct tm tmr; char tbuf[64]; char *result; const char *local_fmt = "%H:%M:%S %a %b %d %Y"; char const *utc_fmt = "%H:%M:%S %a %b %d %Y (UTC)"; if (local && (!localtime_r(&time, &tmr))) { RET_ERROR_PTR(ERR_UNSPEC, "error occurred formatting local date-time"); } else if (!local && (!gmtime_r(&time, &tmr))) { RET_ERROR_PTR(ERR_UNSPEC, "error occurred formatting UTC date-time"); } memset(tbuf, 0, sizeof(tbuf)); if (!strftime(tbuf, sizeof(tbuf), (local ? local_fmt : utc_fmt), &tmr)) { RET_ERROR_PTR(ERR_UNSPEC, "error occurred packaging date-time"); } if (!(result = strdup(tbuf))) { PUSH_ERROR_SYSCALL("strdup"); RET_ERROR_PTR(ERR_NOMEM, "could not allocate space for date-time string"); } return result; }
/** * @brief * Deserialize an EC public key stored in binary format. * @param buf * a pointer to the buffer holding the EC public key in binary format. * @param blen * the length, in bytes, of the buffer holding the EC public key. * @return * a pointer to the deserialized EC public key on success, or NULL on failure. */ EC_KEY * _deserialize_ec_pubkey(unsigned char const *buf, size_t blen) { EC_KEY *result; if (!buf || !blen) { RET_ERROR_PTR(ERR_BAD_PARAM, NULL); } if (!(result = EC_KEY_new_d())) { PUSH_ERROR_OPENSSL(); RET_ERROR_PTR(ERR_UNSPEC, "could not generate new EC key for deserialization"); } if (EC_KEY_set_group_d(result, EC_GROUP_new_by_curve_name_d(NID_secp256k1)) != 1) { PUSH_ERROR_OPENSSL(); EC_KEY_free_d(result); RET_ERROR_PTR(ERR_UNSPEC, "could not get curve group for deserialization"); } if (!(result = o2i_ECPublicKey_d(&result, (const unsigned char **)&buf, blen))) { PUSH_ERROR_OPENSSL(); EC_KEY_free_d(result); RET_ERROR_PTR(ERR_UNSPEC, "deserialization of EC public key portion failed"); } return result; }
/** * @brief * Sign a SHA-hashed body of data using the ECDSA algorithm. * @param data * a pointer to the data buffer to be signed. * @param dlen * the length, in bytes, of the data buffer to be signed. * @param * shabits the number of bits for the desired SHA hash (160, 256, or 512). * @param key * the EC key which will have its private portion used to sign the supplied * data. * @param siglen * a pointer to a variable that will receive the length of the data signature * buffer on success. * @return * NULL on failure, or a pointer to the newly allocated signature data buffer * on success. */ unsigned char * _ec_sign_sha_data(unsigned char const *data, size_t dlen, unsigned int shabits, EC_KEY *key, size_t *siglen) { unsigned char hashbuf[SHA_512_SIZE]; if (!data || !dlen || !key || !siglen) { RET_ERROR_PTR(ERR_BAD_PARAM, NULL); } else if ((shabits != 160) && (shabits != 256) && (shabits != 512)) { RET_ERROR_PTR(ERR_BAD_PARAM, "ECDSA signature only accepts SHA hash sizes of 160, 256, or 512 bits"); } if (_compute_sha_hash(shabits, data, dlen, hashbuf) < 0) { RET_ERROR_PTR(ERR_UNSPEC, "unable to compute SHA hash for ECDSA signature operation"); } return (_ec_sign_data(hashbuf, shabits / 8, key, siglen)); }
/** * @brief * Deserializes an ed25519 public key into a public-only ED25519_KEY structure * that can only be used for signature verification, not signing. * @param serial_pubkey * Serialized ed25519 public key. * @return * Pointer to ED25519_KEY structure. */ ED25519_KEY * _deserialize_ed25519_pubkey(unsigned char const *serial_pubkey) { ED25519_KEY *key; if (!serial_pubkey) { RET_ERROR_PTR(ERR_BAD_PARAM, NULL); } if (!(key = malloc(sizeof(ED25519_KEY)))) { PUSH_ERROR_SYSCALL("malloc"); RET_ERROR_PTR(ERR_NOMEM, NULL); } memset(key, 0, sizeof(ED25519_KEY)); memcpy(key->public_key, serial_pubkey, ED25519_KEY_SIZE); return key; }
/** * @brief * Load an ed25519 private key from a file. * @param filename * the path of the armored file from which the ed25519 private key will be * loaded. * @return * a pointer to a newly allocated ed25519 keypair on success, or NULL on * failure. */ ED25519_KEY * _load_ed25519_privkey(char const *filename) { ED25519_KEY *result; unsigned char *keydata; char *pemdata; size_t klen; if (!filename) { RET_ERROR_PTR(ERR_BAD_PARAM, NULL); } if (!(pemdata = _read_pem_data(filename, "ED25519 PRIVATE KEY", 1))) { RET_ERROR_PTR(ERR_UNSPEC, "unable to read ed25519 private key data from PEM file"); } keydata = _b64decode(pemdata, strlen(pemdata), &klen); _secure_wipe(pemdata, strlen(pemdata)); free(pemdata); if (!keydata || (klen != ED25519_KEY_SIZE)) { if (keydata) { _secure_wipe(keydata, klen); free(keydata); } RET_ERROR_PTR(ERR_UNSPEC, "bad ED25519 key data was read from file"); } if (!(result = malloc(sizeof(ED25519_KEY)))) { PUSH_ERROR_SYSCALL("malloc"); _secure_wipe(keydata, klen); free(keydata); RET_ERROR_PTR(ERR_NOMEM, "unable to allocate space for ED25519 key"); } memset(result, 0, sizeof(ED25519_KEY)); memcpy(result->private_key, keydata, sizeof(result->private_key)); _secure_wipe(keydata, klen); free(keydata); ed25519_publickey_donna(result->private_key, result->public_key); return result; }
/** * @brief * Serialize an EC private key into a data buffer. * @param key * a pointer to the EC key pair to have its private key serialized. * @param outsize * a pointer to a variable that will receive the length of the serialized key * on success. * @return * a pointer to the serialized EC private key on success, or NULL on failure. */ unsigned char * _serialize_ec_privkey(EC_KEY *key, size_t *outsize) { unsigned char *buf = NULL; if (!key || !outsize) { RET_ERROR_PTR(ERR_BAD_PARAM, NULL); } stringer_t *priv; buf = mm_alloc(32); if (!(priv = secp256k1_private_get(key, PLACER(buf, 32)))) { // if ((bsize = i2d_ECPrivateKey_d(key, &buf)) < 0) { PUSH_ERROR_OPENSSL(); RET_ERROR_PTR(ERR_UNSPEC, "unable to serialize EC private key"); } *outsize = 32; return buf; }
/** * @brief * Return the base64-encoded string of a data buffer without padding. * @note * This function ensures the smallest possible output length with no trailing * '=' characters. * @param buf * a pointer to the data buffer to be base-64 encoded. * @param len * the length, in bytes, of the buffer to be encoded. * @return * a pointer to a newly allocated null-terminated string containing the * base64-encoded data, or NULL on failure. * @free_using{free} */ char * _b64encode_nopad(unsigned char const *buf, size_t len) { char *result, *ptr; if (!buf || !len) { RET_ERROR_PTR(ERR_BAD_PARAM, NULL); } if (!(result = _b64encode(buf, len))) { RET_ERROR_PTR(ERR_UNSPEC, "could not base64 encode input"); } ptr = result + strlen(result) - 1; while ((ptr >= result) && (*ptr == '=')) { *ptr-- = 0; } return result; }
/** * @brief Retrieves the keys binary from the keys file. * @param filename Null terminated string containing specified filename. * @param len Pointer to the length of the output. * @return Pointer to the keys binary string, this memory needs to be wipe before being freed. NULL on error. * @free_using{free} */ static unsigned char *keys_file_serialize(const char *filename, size_t *len) { char *b64_keys = NULL; unsigned char *serial_keys = NULL; if(!filename || !len) { RET_ERROR_PTR(ERR_BAD_PARAM, NULL); } if(!(b64_keys = _read_pem_data(filename, SIGNET_PRIVATE_KEYCHAIN, 1))) { RET_ERROR_PTR(ERR_UNSPEC, "could not retrieve keys from PEM file"); } if(!(serial_keys = _b64decode(b64_keys, strlen(b64_keys), len))) { free(b64_keys); RET_ERROR_PTR(ERR_UNSPEC, "could not base64 decode the keys"); } free(b64_keys); return serial_keys; }
/** * @brief * Generate an ed25519 key pair. * @return * a newly allocated and generated ed25519 key pair on success, or NULL on * failure. */ ED25519_KEY * _generate_ed25519_keypair(void) { ED25519_KEY *result; if (!(result = malloc(sizeof(ED25519_KEY)))) { PUSH_ERROR_SYSCALL("malloc"); RET_ERROR_PTR(ERR_NOMEM, "could not generate ed25519 key because of " "memory allocation error"); } memset(result, 0, sizeof(ED25519_KEY)); if (RAND_bytes_d(result->private_key, sizeof(result->private_key)) != 1) { PUSH_ERROR_OPENSSL(); _secure_wipe(result, sizeof(ED25519_KEY)); free(result); RET_ERROR_PTR(ERR_UNSPEC, "could not generate ed25519 secret key"); } ed25519_publickey_donna(result->private_key, result->public_key); return result; }
/** * @brief * Return a simple hex string representing a data buffer. * @param buf * a pointer to the data buffer to be encoded as a hex string. * @param len * the size, in bytes, of the buffer to be processed. * @return * NULL on failure, or a newly allocated null-terminated string containing the * hex encoded input on success. * @free_using{free} */ char * _hex_encode(unsigned char const *buf, size_t len) { char *result; size_t newlen; if (!buf || !len) { RET_ERROR_PTR(ERR_BAD_PARAM, NULL); } newlen = (len * 2) + 1; if (!(result = malloc(newlen))) { PUSH_ERROR_SYSCALL("malloc"); RET_ERROR_PTR(ERR_NOMEM, "unable to allocate space for hex string"); } memset(result, 0, newlen); for (size_t i = 0; i < len; i++) { snprintf(&(result[i * 2]), 3, "%.2x", (unsigned char)buf[i]); } return result; }
/** * @brief Retrieves the encryption key from the keys binary. * @param bin_keys Pointer to the keys buffer. * @param len Length of the keys buffer. * @return Pointer to elliptic curve key, NULL if an error occurred. * @free_using{free_ec_key} */ static EC_KEY *keys_serial_get_enc_key(const unsigned char *bin_keys, size_t len) { /* unsigned char sign_fid, enc_fid; sign_fid is unused causing errors on compilation */ unsigned char enc_fid; size_t at = 0, privkeylen; EC_KEY *enc_key = NULL; if(!bin_keys) { RET_ERROR_PTR(ERR_BAD_PARAM, NULL); } else if(keys_check_length(bin_keys, len) < 0) { RET_ERROR_PTR(ERR_BAD_PARAM, NULL); } switch(keys_type_get(bin_keys, len)) { case KEYS_TYPE_ORG: /* sign_fid = KEYS_ORG_PRIVATE_POK; */ enc_fid = KEYS_ORG_PRIVATE_ENC; break; case KEYS_TYPE_USER: /* sign_fid = KEYS_USER_PRIVATE_SIGN; */ enc_fid = KEYS_USER_PRIVATE_ENC; break; default: RET_ERROR_PTR(ERR_UNSPEC, "invalid keys type"); break; } at = KEYS_HEADER_SIZE; while(bin_keys[at++] != enc_fid) { at += bin_keys[at] + 1; if(len <= at) { RET_ERROR_PTR(ERR_UNSPEC, "no private encryption key in keys file"); } } privkeylen = _int_no_get_2b(bin_keys+at); at += 2; if(at + privkeylen > len) { RET_ERROR_PTR(ERR_UNSPEC, "invalid encryption key size"); } if(!(enc_key = _deserialize_ec_privkey(bin_keys + at, privkeylen, 0))) { RET_ERROR_PTR(ERR_UNSPEC, "could not deserialize private EC encryption key"); } return enc_key; }
/** * @brief * Deserialize an EC private key stored in binary format. * @param buf * a pointer to the buffer holding the EC private key in binary format. * @param blen * the length, in bytes, of the buffer holding the EC private key. * @return * a pointer to the deserialized EC private key on success, or NULL on * failure. */ EC_KEY * _deserialize_ec_privkey(unsigned char const *buf, size_t blen) { EC_KEY *result; const unsigned char *bufptr = buf; if (!buf || !blen) { RET_ERROR_PTR(ERR_BAD_PARAM, NULL); } result = secp256k1_private_set(PLACER((unsigned char *)bufptr, blen)); /* * At this point, in most cases bufptr == buf + blen. There may be cases * though where the private key is shorter than the provided buffer. This * is because DER is a variable-length encoding. Parsing any field behind * the privkey must take this into account. */ return result; }
/** * @brief Retrieves the signing key from the keys binary. * @param bin_keys Pointer to the keys buffer. * @param len Length of the keys buffer. * @return Pointer to ed25519 signing key, NULL if an error occurred. * @free_using{free_ed25519_key} */ static ED25519_KEY *keys_serial_get_sign_key(const unsigned char *bin_keys, size_t len) { unsigned char sign_fid; unsigned int at = 0; ED25519_KEY *sign_key; if(!bin_keys) { RET_ERROR_PTR(ERR_BAD_PARAM, NULL); } else if(keys_check_length(bin_keys, len) < 0) { RET_ERROR_PTR(ERR_BAD_PARAM, NULL); } else if(len < KEYS_HEADER_SIZE + 2 + ED25519_KEY_SIZE) { RET_ERROR_PTR(ERR_BAD_PARAM, "keys buffer too small for signing key"); } switch(keys_type_get(bin_keys, len)) { case KEYS_TYPE_ORG: sign_fid = KEYS_ORG_PRIVATE_POK; break; case KEYS_TYPE_USER: sign_fid = KEYS_USER_PRIVATE_SIGN; break; default: RET_ERROR_PTR(ERR_UNSPEC, "invalid keys type"); break; } at = KEYS_HEADER_SIZE; if(bin_keys[at++] != sign_fid) { RET_ERROR_PTR(ERR_UNSPEC, "no signing key was found"); } if(bin_keys[at++] != ED25519_KEY_SIZE) { RET_ERROR_PTR(ERR_UNSPEC, "invalid size of signing key"); } if(!(sign_key = _deserialize_ed25519_privkey(bin_keys + at))) { RET_ERROR_PTR(ERR_UNSPEC, "could not deserialize ed25119 signing key"); } return sign_key; }
/** * @brief * Append an address to the end of a pointer chain. * @param buf * the address of the target pointer chain. If NULL, allocate a new one for * the caller. * @param addr * the address to be appended to the pointer chain. */ void * _ptr_chain_add(void *buf, const void *addr) { unsigned char **newbuf, **ptr = (unsigned char **)buf, **obuf; size_t bufsize = sizeof(unsigned char *); // Get the entire size of the buffer, if it's not NULL. if (buf) { while (*ptr) { ptr++; } ptr++; bufsize = (unsigned long)ptr - (unsigned long)buf; } // Reallocate with space for one more pointer at the end of it. if (!(newbuf = realloc((obuf = buf), bufsize + sizeof(unsigned char *)))) { PUSH_ERROR_SYSCALL("realloc"); if (obuf) { free(obuf); } RET_ERROR_PTR(ERR_NOMEM, "unable to lengthen pointer chain"); } if (!buf) { ptr = (unsigned char **)newbuf; } else { ptr = (unsigned char **)((unsigned char *)newbuf + bufsize - sizeof(unsigned char *)); } // Replace the next-to-last entry in the new pointer chain with our // address, then follow it with a terminating NULL pointer. *ptr++ = (unsigned char *)addr; *ptr = NULL; return newbuf; }
/** * @brief * Perform base64-decoding on a string and return the result. * @param buf * a pointer to a buffer containing the data to be decoded. * @param len * the length, in bytes, of the buffer to be decoded. * @param outlen * a pointer to a variable to receive the length of the decoded data. * @return * a pointer to a newly allocated buffer containing the base64-decoded, or * NULL on failure. * @free_using{free} */ unsigned char * _b64decode(char const *buf, size_t len, size_t *outlen) { unsigned char *o, *result; const char *p; size_t new_len, written = 0; int loop = 0, value = 0; if (!buf || !len || !outlen) { RET_ERROR_PTR(ERR_BAD_PARAM, NULL); } new_len = B64_DECODED_LEN(len); if (!(result = malloc(new_len))) { PUSH_ERROR_SYSCALL("malloc"); RET_ERROR_PTR(ERR_NOMEM, "unable to allocate buffer for base64 decoded data"); } memset(result, 0, new_len); o = result; p = buf; // Get four characters at a time from the input buffer and decode them. for (size_t i = 0; i < len; i++) { // Only process legit base64 characters. if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '+' || *p == '/') { // Do the appropriate operation. switch (loop) { case 0: value = _base64_vals[(int)*p++] << 18; loop++; break; case 1: value += _base64_vals[(int)*p++] << 12; *o++ = (value & 0x00ff0000) >> 16; written++; loop++; break; case 2: value += (unsigned int)_base64_vals[(int)*p++] << 6; *o++ = (value & 0x0000ff00) >> 8; written++; loop++; break; case 3: value += (unsigned int)_base64_vals[(int)*p++]; *o++ = value & 0x000000ff; written++; loop = 0; break; default: free(result); RET_ERROR_PTR(ERR_UNSPEC, "an unexpected error occurred during base64 decoding"); break; } } else if (*p == '=') { i = len; } else { p++; } }