core/common/buffer_util.c (197 lines of code) (raw):

// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. #include <stddef.h> #include <stdlib.h> #include <string.h> #include "buffer_util.h" #include "common_math.h" /** * Copy data into an output buffer. * * @param src The source data to copy. If this is null, no data will be copied. * @param src_length Length of the source data buffer. * @param offset Offset in the source buffer to start copying. On output, this will value will be * reduced by the number of bytes skipped in the source buffer. If this is null, data will be * copied from the beginning of the source buffer. * @param dest_length Maximum number of bytes to copy. On output, this will be reduced by the * number of bytes copied. If this is null, no data will be copied. * @param dest Output buffer to copy data to. If this is null, no data will be copied. * * @return The number of bytes copied. */ size_t buffer_copy (const uint8_t *src, size_t src_length, size_t *offset, size_t *dest_length, uint8_t *dest) { size_t bytes; size_t start; if ((src == NULL) || (dest == NULL) || (src_length == 0)) { return 0; } if (offset) { start = *offset; if (start >= src_length) { *offset -= src_length; return 0; } } else { start = 0; } if (!dest_length) { return 0; } else { bytes = min (src_length - start, *dest_length); } memcpy (dest, &src[start], bytes); if (offset) { *offset = 0; } *dest_length -= bytes; return bytes; } /** * Reverse the contents of a buffer, i.e. make the last byte first and first byte last. * * @param buffer The buffer to reverse. The reversed data will be stored in the same buffer. * @param length The number of bytes contained in the buffer. */ void buffer_reverse (uint8_t *buffer, size_t length) { if (buffer != NULL) { size_t i; size_t j; uint8_t temp; for (i = 0, j = (length - 1); i < (length / 2); i++, j--) { temp = buffer[i]; buffer[i] = buffer[j]; buffer[j] = temp; } } } /** * Make a copy of a buffer, reversing the buffer contents. If either buffer is null, no operation * is performed. * * These buffers must not be overlapping. * * The arguments on this function are reverse the normal semantics of input args first and output * args last, but this signature more closely maps to memcpy, making it more intuitive. * * @param dest Destination buffer for the reversed data. * @param src The buffer data to copy. * @param length The number of bytes to copy. */ void buffer_reverse_copy (uint8_t *dest, const uint8_t *src, size_t length) { if ((src != NULL) && (dest != NULL)) { size_t i; size_t j; for (i = 0, j = (length - 1); i < length; i++, j--) { dest[i] = src[j]; } } } /** * Make a copy of a buffer, reversing the buffer contents one DWORD at a time. This function will * return error if either buffer is null or not DWORD aligned. * * These buffers must not be overlapping. * * The arguments on this function are reverse the normal semantics of input args first and output * args last, but this signature more closely maps to memcpy, making it more intuitive. * * @param dest Destination buffer for the reversed data. * @param src The buffer data to copy. * @param length The number of dwords to copy. * * @return 0 if the operation was successful or an error code. */ int buffer_reverse_copy_dwords (uint32_t *dest, const uint32_t *src, size_t length) { size_t i; size_t j; if (((uintptr_t) src & 0x3U) || ((uintptr_t) dest & 0x3U)) { return BUFFER_UTIL_UNEXPETCED_ALIGNMENT; } if ((src == NULL) || (dest == NULL)) { return BUFFER_UTIL_INVALID_ARGUMENT; } for (i = 0, j = (length - 1); i < length; i++, j--) { dest[i] = src[j]; } return 0; } /** * A constant time replacement for memcmp for use in secure contexts. Unlike memcmp, this only * checks for matching buffers and provides no information about the relative values if they don't * match. * * @param buf1 First input buffer for the comparison. * @param buf2 Second input buffer for the comparison. * @param length Length of buffers to compare. * * @return 0 if the buffers match exactly or BUFFER_UTIL_DATA_MISMATCH if they do not. */ int buffer_compare (const uint8_t *buf1, const uint8_t *buf2, size_t length) { uint8_t match = 0xff; uint8_t check; size_t i; if ((buf1 == NULL) || (buf2 == NULL)) { if ((buf1 == NULL) && (buf2 == NULL) && (length == 0)) { return 0; } return BUFFER_UTIL_DATA_MISMATCH; } for (i = 0; i < length; i++) { check = buf1[i] ^ 0xff; check ^= buf2[i]; match &= check; } return (match == 0xff) ? 0 : BUFFER_UTIL_DATA_MISMATCH; } /** * A constant time replacement for memcmp for use in secure contexts. Unlike memcmp, this only * checks for matching buffers and provides no information about the relative values if they don't * match. * * This version operates only on buffers of 32-bit arrays, which is useful in scenarios where byte * access is not possible. * * @param buf1 First input buffer for the comparison. * @param buf2 Second input buffer for the comparison. * @param length The number of 32-bit values to compare. * * @return 0 if the buffers match exactly or BUFFER_UTIL_DATA_MISMATCH if they do not. */ int buffer_compare_dwords (const uint32_t *buf1, const uint32_t *buf2, size_t dwords) { uint32_t match = 0xffffffff; uint32_t check; size_t i; if ((buf1 == NULL) || (buf2 == NULL)) { if ((buf1 == NULL) && (buf2 == NULL) && (dwords == 0)) { return 0; } return BUFFER_UTIL_DATA_MISMATCH; } for (i = 0; i < dwords; i++) { check = buf1[i] ^ 0xffffffff; check ^= buf2[i]; match &= check; } return (match == 0xffffffff) ? 0 : BUFFER_UTIL_DATA_MISMATCH; } /* Set up a pointer to abstract memset calls from the compiler. This is not foolproof, but is the * default approach used by mbedTLS. A better alternative is to use memset_s, but compiler support * for that seems to be poor. * * Reference: http://www.daemonology.net/blog/2014-09-04-how-to-zero-a-buffer.html */ static void*(*const volatile memset_ptr) (void*, int, size_t) = memset; /** * Clear a buffer by filling it with zeros. This is not necessarily achieved in the most efficient * way, but is implemented in a way that should keep it from getting optimized out by compilers. * * @param buffer The buffer to clear. * @param length Length of the buffer. */ void buffer_zeroize (void *buffer, size_t length) { if (buffer) { memset_ptr (buffer, 0, length); } } /** * Clear a buffer of dword aligned data with zeros. It is guaranteed that only dword accesses will * be made to clear the buffer. * * @param buffer The buffer to clear. * @param dwords Number of dwords in the buffer. */ void buffer_zeroize_dwords (uint32_t *buffer, size_t dwords) { if (buffer) { size_t i; for (i = 0; i < dwords; i++) { buffer[i] = 0; } } } /** * Copies a 16 bit value between 2 assumed unaligned addresses. * * This does not do any validation on the parameters. * * @param dst A pointer to copy the value to. * @param src The address pointer to read from. */ void buffer_unaligned_copy16 (uint16_t *dst, const uint16_t *src) { #ifdef UNALIGNED_16BIT_MEMORY_ACCESS_NOT_SUPPORTED uint8_t *dst_copy = (uint8_t*) dst; memcpy (dst_copy, src, sizeof (*dst)); #else *dst = *src; #endif } /** * Copies a 24 bit value between 2 assumed unaligned addresses. * * This does not do any validation on the parameters. * * @param dst A pointer to copy the value to. * @param src The address pointer to read from. */ void buffer_unaligned_copy24 (uint8_t *dst, const uint8_t *src) { memcpy (dst, src, 3); } /** * Copies a 32 bit value between 2 assumed unaligned addresses. * * This does not do any validation on the parameters. * * @param dst The address pointer to read from. * @param src A pointer to copy the value to. */ void buffer_unaligned_copy32 (uint32_t *dst, const uint32_t *src) { #ifdef UNALIGNED_32BIT_MEMORY_ACCESS_NOT_SUPPORTED uint8_t *dst_copy = (uint8_t*) dst; memcpy (dst_copy, src, sizeof (*dst)); #else *dst = *src; #endif } /** * Copies a 64 bit value between 2 assumed unaligned addresses. * * This does not do any validation on the parameters. * * @param dst The address pointer to read from. * @param src A pointer to copy the value to. */ void buffer_unaligned_copy64 (uint64_t *dst, const uint64_t *src) { #ifdef UNALIGNED_64BIT_MEMORY_ACCESS_NOT_SUPPORTED uint8_t *dst_copy = (uint8_t*) dst; memcpy (dst_copy, src, sizeof (*dst)); #else *dst = *src; #endif } /** * Reads a 16 bit value from an assumed unaligned address. * * This does not do any validation on the parameters. * * @param buffer The address pointer to read from. */ uint16_t buffer_unaligned_read16 (const uint16_t *buffer) { uint16_t value; buffer_unaligned_copy16 (&value, buffer); return value; } /** * Reads a 24 bit value from an assumed unaligned address. * * This does not do any validation on the parameters. * * @param buffer The address pointer to read from. */ uint32_t buffer_unaligned_read24 (const uint8_t *buffer) { uint32_t value = 0; buffer_unaligned_copy24 ((uint8_t*) &value, buffer); return value; } /** * Reads a 32 bit value from an assumed unaligned address. * * This does not do any validation on the parameters. * * @param buffer The address pointer to read from. */ uint32_t buffer_unaligned_read32 (const uint32_t *buffer) { uint32_t value; buffer_unaligned_copy32 (&value, buffer); return value; } /** * Reads a 64 bit value from an assumed unaligned address. * * This does not do any validation on the parameters. * * @param buffer The address pointer to read from. */ uint64_t buffer_unaligned_read64 (const uint64_t *buffer) { uint64_t value; buffer_unaligned_copy64 (&value, buffer); return value; } /** * Writes a 16 bit value to an assumed unaligned address. * * This does not do any validation on the parameters. * * @param buffer The address pointer to write to. * @param value The value to write. */ void buffer_unaligned_write16 (uint16_t *buffer, uint16_t value) { buffer_unaligned_copy16 (buffer, &value); } /** * Writes a 24 bit value to an assumed unaligned address. * * This does not do any validation on the parameters. * * @param buffer The address pointer to write to. * @param value The value to write. */ void buffer_unaligned_write24 (uint8_t *buffer, uint32_t value) { buffer_unaligned_copy24 (buffer, (const uint8_t*) &value); } /** * Writes a 32 bit value to an assumed unaligned address. * * This does not do any validation on the parameters. * * @param buffer The address pointer to write to. * @param value The value to write. */ void buffer_unaligned_write32 (uint32_t *buffer, uint32_t value) { buffer_unaligned_copy32 (buffer, &value); } /** * Writes a 64 bit value to an assumed unaligned address. * * This does not do any validation on the parameters. * * @param buffer The address pointer to write to. * @param value The value to write. */ void buffer_unaligned_write64 (uint64_t *buffer, uint64_t value) { buffer_unaligned_copy64 (buffer, &value); }