/** * @brief Print to a managed string and return the number of bytes written. * @see vsnprintf() * @note If the destination string pointer is NULL the function will simply return how much room would have been necessary. * @param s a pointer to the managed string that will receive the output of the print operation. * @param format a format string specifying the arguments of the print operation. * @param args a va_list containing the parameters to the print format string. * @return -1 on failure, or the number of characters printed to the string, excluding the terminating null byte. */ size_t st_vsprint(stringer_t *s, chr_t *format, va_list args) { int_t length; size_t avail; uint32_t opts = *((uint32_t *)s); #ifdef MAGMA_PEDANTIC if (!st_valid_destination(opts)) { log_pedantic("Invalid string options. { opt = %u = %s }", opts, st_info_opts(opts, MEMORYBUF(128), 128)); return 0; } #endif // If the target string is NULL, we just calculate how much room would be needed. if (!s) { length = vsnprintf(NULL, 0, format, args); return length; } // Print the provided format into the newly allocated buffer. length = vsnprintf(st_data_get(s), (avail = st_avail_get(s)), format, args); #ifdef MAGMA_PEDANTIC if (length > avail) { log_pedantic("The output buffer was not large enough to hold the output, so the result was truncated."); } #endif // If the length of the data segment is tracked explicitly, set the string length. Block strings assume the length of the buffer is the // the length of the data, and NULL strings don't explicitly track of their length so we skip this step for those string types. if (st_valid_tracked(opts)) { st_length_set(s, length > avail ? avail : length); } return length > avail ? avail : length; }
/** * @brief Convert a hex string into a binary data blob. * @note All hex strings should be composed of pairs of two hex characters representing individual bytes. * Invalid hex characters will simply be ignored during processing. * @param h a managed string containing the input hex string to be decoded. * @param output if not NULL, a pointer to a managed string to contain the decoded binary output; if NULL, a new string * will be allocated and returned to the caller. * @return a pointer to a managed string containing the decoded output, or NULL on failure. */ stringer_t * hex_decode_st(stringer_t *h, stringer_t *output) { uint32_t opts = 0; uchr_t *p = NULL, *o, c = 0; size_t w = 0, len = 0, valid; stringer_t *result = NULL; if (output && !st_valid_destination((opts = *((uint32_t *)output)))) { log_pedantic("An output string was supplied but it does not represent a buffer capable of holding the output."); return NULL; } else if (st_empty_out(h, &p, &len) || !(valid = hex_count_st(h))) { log_pedantic("The input block does not appear to hold any data ready for decoding. {%slen = %zu}", p ? "" : "p = NULL / ", len); return NULL; } // Make sure the output buffer is large enough or if output was passed in as NULL we'll attempt the allocation of our own buffer. if ((result = output) && ((st_valid_avail(opts) && st_avail_get(output) < (valid / 2)) || (!st_valid_avail(opts) && st_length_get(output) < (valid / 2)))) { log_pedantic("The output buffer supplied is not large enough to hold the result. {avail = %zu / required = %zu}", st_valid_avail(opts) ? st_avail_get(output) : st_length_get(output), valid / 2); return NULL; } else if (!output && !(result = st_alloc(valid / 2))) { log_pedantic("The output buffer memory allocation request failed. {requested = %zu}", (valid / 2)); return NULL; } // Store the memory address where the output should be written. o = st_data_get(result); // Loop through the input buffer and translate valid characters into a binary octet. for (size_t i = 0; i < len; i++) { if (hex_valid_chr(*p)) { if (!c) { c = *p; } else { *o++ = hex_decode_chr(c, *p); c = 0; w++; } } p++; } // If an output buffer was supplied that is capable of tracking the data length, or a managed string buffer was allocated update the length param. if (!output || st_valid_tracked(opts)) { st_length_set(result, w); } return result; }
/** * @brief Convert a block of binary data into a hex string. * @param b a managed string containing the raw data to be encoded. * @param output if not NULL, a pointer to a managed string that will store the encoded output; if NULL, a new managed string will * be allocated and returned to the caller. * @return NULL on failure, or a pointer to a managed string containing the hex-encoded output on success. */ stringer_t * hex_encode_st(stringer_t *b, stringer_t *output) { size_t len = 0; uint32_t opts = 0; uchr_t *p = NULL, *o; stringer_t *result = NULL; if (output && !st_valid_destination((opts = *((uint32_t *)output)))) { log_pedantic("An output string was supplied but it does not represent a buffer capable of holding the output."); return NULL; } else if (st_empty_out(b, &p, &len)) { log_pedantic("The input block does not appear to hold any data ready for encoding. {%slen = %zu}", p ? "" : "p = NULL / ", len); return NULL; } // Make sure the output buffer is large enough or if output was passed in as NULL we'll attempt the allocation of our own buffer. if ((result = output) && ((st_valid_avail(opts) && st_avail_get(output) < (len * 2)) || (!st_valid_avail(opts) && st_length_get(output) < (len * 2)))) { log_pedantic("The output buffer supplied is not large enough to hold the result. {avail = %zu / required = %zu}", st_valid_avail(opts) ? st_avail_get(output) : st_length_get(output), len * 2); return NULL; } else if (!output && !(result = st_alloc(len * 2))) { log_pedantic("The output buffer memory allocation request failed. {requested = %zu}", len * 2); return NULL; } // Store the memory address where the output should be written. o = st_data_get(result); // Loop through the input buffer and write character pairs to the result string data buffer. for (size_t i = 0; i < len; i++) { hex_encode_chr(*p, o); o += 2; p += 1; } // If an output buffer was supplied that is capable of tracking the data length, or a managed string buffer was allocated update the length param. if (!output || st_valid_tracked(opts)) { st_length_set(result, len * 2); } return result; }
stringer_t * org_signet_get(prime_org_signet_t *org, stringer_t *output) { size_t length; int_t written = 0; stringer_t *result = NULL; if (!org || !(length = org_signet_length(org))) { log_pedantic("An invalid org signet was supplied for serialization."); return NULL; } // See if we have a valid output buffer, or if output is NULL, allocate a buffer to hold the output. else if (output && (!st_valid_destination(st_opt_get(output)) || st_avail_get(output) < length)) { log_pedantic("An output string was supplied but it does not represent a buffer capable of holding the output."); return NULL; } else if (!output && !(result = st_alloc(length))) { log_pedantic("Could not allocate a buffer large enough to hold encoded result. { requested = %zu }", length); return NULL; } else if (!output) { output = result; } st_wipe(output); // Calculate the size, by writing out all the fields (minus the header) using a NULL output. length = st_write(NULL, prime_field_write(PRIME_ORG_SIGNET, 1, ED25519_KEY_PUB_LEN, ed25519_public_get(org->signing, MANAGEDBUF(32)), MANAGEDBUF(34)), prime_field_write(PRIME_ORG_SIGNET, 3, SECP256K1_KEY_PUB_LEN, secp256k1_public_get(org->encryption, MANAGEDBUF(33)), MANAGEDBUF(35)), prime_field_write(PRIME_ORG_SIGNET, 4, ED25519_SIGNATURE_LEN, org->signature, MANAGEDBUF(65))); // Then output them again into the actual output buffer, but this time include the header. This is very primitive serialization logic. if ((written = st_write(output, prime_header_org_signet_write(length, MANAGEDBUF(5)), prime_field_write(PRIME_ORG_SIGNET, 1, ED25519_KEY_PUB_LEN, ed25519_public_get(org->signing, MANAGEDBUF(32)), MANAGEDBUF(34)), prime_field_write(PRIME_ORG_SIGNET, 3, SECP256K1_KEY_PUB_LEN, secp256k1_public_get(org->encryption, MANAGEDBUF(33)), MANAGEDBUF(35)), prime_field_write(PRIME_ORG_SIGNET, 4, ED25519_SIGNATURE_LEN, org->signature, MANAGEDBUF(65)))) != (length + 5)) { log_pedantic("The organizational signet didn't serialize to the expected length. { written = %i }", written); st_cleanup(result); return NULL; } return output; }
/** * @brief Return a managed string containing sprintf()-style formatted data. * @param opts an options value to be passed to the allocation of the resulting managed string. * @param format a format string for the output string data. * @param args a variable argument list of parameters to be formatted as output. * @return NULL on failure or a managed string containing the final formatted data on success. */ stringer_t * st_vaprint_opts(uint32_t opts, chr_t *format, va_list args) { void *out; va_list copy; int_t length, expected; stringer_t *result = NULL; #ifdef MAGMA_PEDANTIC if (!st_valid_destination(opts)) { log_pedantic("Invalid string options. { opt = %u = %s }", opts, st_info_opts(opts, MEMORYBUF(128), 128)); return NULL; } #endif // Calculate the length. va_copy(copy, args); expected = length = vsnprintf(NULL, 0, format, copy); // Allocate a properly sized buffer. if (!length || !(result = st_alloc_opts(opts, length + 1)) || !(out = st_data_get(result))) { if (result) st_free(result); return NULL; } // Print the provided format into the newly allocated buffer. if ((length = vsnprintf(out, length + 1, format, args)) != expected) { log_pedantic("The print operation did not generate the amount of data we expected."); st_free(result); result = NULL; } if (st_valid_tracked(opts)) { st_length_set(result, length); } return result; }
stringer_t * hash_digest(digest_t *digest, stringer_t *s, stringer_t *output) { int_t olen; uint_t rlen; uint32_t opts = 0; EVP_MD_CTX ctx; stringer_t *result = NULL; // Ensure a digest pointer was passed in and that we can retrieve the output length. if (!digest || (olen = EVP_MD_size_d((const EVP_MD *)digest)) <= 0) { log_pedantic("The hash algorithm is missing or invalid."); return NULL; } else if (output && !st_valid_destination((opts = *((uint32_t *)output)))) { log_pedantic("An output string was supplied but it does not represent a buffer capable of holding a result."); return NULL; } else if (st_empty(s)) { log_pedantic("The input string does not appear to have any data ready for encoding. {%slen = %zu}", s ? "" : "s = NULL / ", s ? st_length_get(s) : 0); return NULL; } // Make sure the output buffer is large enough or if output was passed in as NULL we'll attempt the allocation of our own buffer. else if ((result = output) && ((st_valid_avail(opts) && st_avail_get(output) < olen) || (!st_valid_avail(opts) && st_length_get(output) < olen))) { log_pedantic("The output buffer supplied is not large enough to hold the result. {avail = %zu / required = %i}", st_valid_avail(opts) ? st_avail_get(output) : st_length_get(output), olen); return NULL; } else if (!output && !(result = st_alloc(olen))) { log_pedantic("The output buffer memory allocation request failed. {requested = %i}", olen); return NULL; } // Initialize the context. EVP_MD_CTX_init_d(&ctx); rlen = olen; // Setup the digest algorithm. if (EVP_DigestInit_ex_d(&ctx, (const EVP_MD *)digest, NULL) != 1) { log_pedantic("An error occurred while trying to initialize the hash context. {%s}", ssl_error_string(MEMORYBUF(256), 256)); EVP_MD_CTX_cleanup_d(&ctx); if (!output) { st_free(result); } return NULL; } // Process the input data. else if (EVP_DigestUpdate_d(&ctx, st_data_get(s), st_length_get(s)) != 1) { log_pedantic("An error occurred while trying to process the input data. {%s}", ssl_error_string(MEMORYBUF(256), 256)); EVP_MD_CTX_cleanup_d(&ctx); if (!output) { st_free(result); } return NULL; } // Retrieve the hash output. else if (EVP_DigestFinal_d(&ctx, st_data_get(result), &rlen) != 1) { log_pedantic("An error occurred while trying to retrieve the hash result. {%s}", ssl_error_string(MEMORYBUF(256), 256)); EVP_MD_CTX_cleanup_d(&ctx); if (!output) { st_free(result); } return NULL; } // Cleanup. EVP_MD_CTX_cleanup_d(&ctx); if (!output || st_valid_tracked(opts)) { st_length_set(result, rlen); } return result; }