diff options
author | Yu Watanabe <watanabe.yu+github@gmail.com> | 2023-01-17 14:04:02 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-01-17 14:04:02 +0900 |
commit | ab250890c0483fab4ff78591e6c178b7c2257337 (patch) | |
tree | 19f359eabde8d9a8d131b76cdedb960df2e3f0b0 | |
parent | 841dfd3dc0dd370a21f190a5b7b870db1c95f7e6 (diff) | |
parent | 5040b2cfab1f3d778b6d253b5c5c5f50980f6535 (diff) | |
download | systemd-ab250890c0483fab4ff78591e6c178b7c2257337.tar.gz |
Merge pull request #26004 from poettering/cleanuo-erase-moar
tree-wide: use CLEANUP_ERASE() at many places
-rw-r--r-- | src/basic/hexdecoct.c | 114 | ||||
-rw-r--r-- | src/fundamental/memory-util-fundamental.h | 28 | ||||
-rw-r--r-- | src/home/homework-fscrypt.c | 58 | ||||
-rw-r--r-- | src/shared/ask-password-api.c | 66 | ||||
-rw-r--r-- | src/shared/creds-util.c | 24 | ||||
-rw-r--r-- | src/shared/ethtool-util.c | 11 | ||||
-rw-r--r-- | src/shared/tpm2-util.c | 15 | ||||
-rw-r--r-- | src/test/test-hexdecoct.c | 17 |
8 files changed, 156 insertions, 177 deletions
diff --git a/src/basic/hexdecoct.c b/src/basic/hexdecoct.c index dc3b948d8e..898ed83f86 100644 --- a/src/basic/hexdecoct.c +++ b/src/basic/hexdecoct.c @@ -110,12 +110,17 @@ static int unhex_next(const char **p, size_t *l) { return r; } -int unhexmem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_len) { +int unhexmem_full( + const char *p, + size_t l, + bool secure, + void **ret, + size_t *ret_len) { + _cleanup_free_ uint8_t *buf = NULL; size_t buf_size; const char *x; uint8_t *z; - int r; assert(p || l == 0); @@ -128,22 +133,20 @@ int unhexmem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_ if (!buf) return -ENOMEM; + CLEANUP_ERASE_PTR(secure ? &buf : NULL, buf_size); + for (x = p, z = buf;;) { int a, b; a = unhex_next(&x, &l); if (a == -EPIPE) /* End of string */ break; - if (a < 0) { - r = a; - goto on_failure; - } + if (a < 0) + return a; b = unhex_next(&x, &l); - if (b < 0) { - r = b; - goto on_failure; - } + if (b < 0) + return b; *(z++) = (uint8_t) a << 4 | (uint8_t) b; } @@ -156,12 +159,6 @@ int unhexmem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_ *ret = TAKE_PTR(buf); return 0; - -on_failure: - if (secure) - explicit_bzero_safe(buf, buf_size); - - return r; } /* https://tools.ietf.org/html/rfc4648#section-6 @@ -765,12 +762,17 @@ static int unbase64_next(const char **p, size_t *l) { return ret; } -int unbase64mem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_size) { +int unbase64mem_full( + const char *p, + size_t l, + bool secure, + void **ret, + size_t *ret_size) { + _cleanup_free_ uint8_t *buf = NULL; const char *x; uint8_t *z; size_t len; - int r; assert(p || l == 0); @@ -785,60 +787,44 @@ int unbase64mem_full(const char *p, size_t l, bool secure, void **ret, size_t *r if (!buf) return -ENOMEM; + CLEANUP_ERASE_PTR(secure ? &buf : NULL, len); + for (x = p, z = buf;;) { int a, b, c, d; /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */ a = unbase64_next(&x, &l); if (a == -EPIPE) /* End of string */ break; - if (a < 0) { - r = a; - goto on_failure; - } - if (a == INT_MAX) { /* Padding is not allowed at the beginning of a 4ch block */ - r = -EINVAL; - goto on_failure; - } + if (a < 0) + return a; + if (a == INT_MAX) /* Padding is not allowed at the beginning of a 4ch block */ + return -EINVAL; b = unbase64_next(&x, &l); - if (b < 0) { - r = b; - goto on_failure; - } - if (b == INT_MAX) { /* Padding is not allowed at the second character of a 4ch block either */ - r = -EINVAL; - goto on_failure; - } + if (b < 0) + return b; + if (b == INT_MAX) /* Padding is not allowed at the second character of a 4ch block either */ + return -EINVAL; c = unbase64_next(&x, &l); - if (c < 0) { - r = c; - goto on_failure; - } + if (c < 0) + return c; d = unbase64_next(&x, &l); - if (d < 0) { - r = d; - goto on_failure; - } + if (d < 0) + return d; if (c == INT_MAX) { /* Padding at the third character */ - if (d != INT_MAX) { /* If the third character is padding, the fourth must be too */ - r = -EINVAL; - goto on_failure; - } + if (d != INT_MAX) /* If the third character is padding, the fourth must be too */ + return -EINVAL; /* b == 00YY0000 */ - if (b & 15) { - r = -EINVAL; - goto on_failure; - } + if (b & 15) + return -EINVAL; - if (l > 0) { /* Trailing rubbish? */ - r = -ENAMETOOLONG; - goto on_failure; - } + if (l > 0) /* Trailing rubbish? */ + return -ENAMETOOLONG; *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */ break; @@ -846,15 +832,11 @@ int unbase64mem_full(const char *p, size_t l, bool secure, void **ret, size_t *r if (d == INT_MAX) { /* c == 00ZZZZ00 */ - if (c & 3) { - r = -EINVAL; - goto on_failure; - } + if (c & 3) + return -EINVAL; - if (l > 0) { /* Trailing rubbish? */ - r = -ENAMETOOLONG; - goto on_failure; - } + if (l > 0) /* Trailing rubbish? */ + return -ENAMETOOLONG; *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */ *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */ @@ -868,18 +850,14 @@ int unbase64mem_full(const char *p, size_t l, bool secure, void **ret, size_t *r *z = 0; + assert((size_t) (z - buf) <= len); + if (ret_size) *ret_size = (size_t) (z - buf); if (ret) *ret = TAKE_PTR(buf); return 0; - -on_failure: - if (secure) - explicit_bzero_safe(buf, len); - - return r; } void hexdump(FILE *f, const void *p, size_t s) { diff --git a/src/fundamental/memory-util-fundamental.h b/src/fundamental/memory-util-fundamental.h index 67621fdb42..78e2dbec59 100644 --- a/src/fundamental/memory-util-fundamental.h +++ b/src/fundamental/memory-util-fundamental.h @@ -29,6 +29,8 @@ static inline void *explicit_bzero_safe(void *p, size_t l) { #endif struct VarEraser { + /* NB: This is a pointer to memory to erase in case of CLEANUP_ERASE(). Pointer to pointer to memory + * to erase in case of CLEANUP_ERASE_PTR() */ void *p; size_t size; }; @@ -38,5 +40,27 @@ static inline void erase_var(struct VarEraser *e) { } /* Mark var to be erased when leaving scope. */ -#define CLEANUP_ERASE(var) \ - _cleanup_(erase_var) _unused_ struct VarEraser CONCATENATE(_eraser_, UNIQ) = { .p = &var, .size = sizeof(var) } +#define CLEANUP_ERASE(var) \ + _cleanup_(erase_var) _unused_ struct VarEraser CONCATENATE(_eraser_, UNIQ) = { \ + .p = &(var), \ + .size = sizeof(var), \ + } + +static inline void erase_varp(struct VarEraser *e) { + + /* Very similar to erase_var(), but assumes `p` is a pointer to a pointer whose memory shall be destructed. */ + if (!e->p) + return; + + explicit_bzero_safe(*(void**) e->p, e->size); +} + +/* Mark pointer so that memory pointed to is erased when leaving scope. Note: this takes a pointer to the + * specified pointer, instead of just a copy of it. This is to allow callers to invalidate the pointer after + * use, if they like, disabling our automatic erasure (for example because they succeeded with whatever they + * wanted to do and now intend to return the allocated buffer to their caller without it being erased). */ +#define CLEANUP_ERASE_PTR(ptr, sz) \ + _cleanup_(erase_varp) _unused_ struct VarEraser CONCATENATE(_eraser_, UNIQ) = { \ + .p = (ptr), \ + .size = (sz), \ + } diff --git a/src/home/homework-fscrypt.c b/src/home/homework-fscrypt.c index afe3447d62..8b7fdda5b1 100644 --- a/src/home/homework-fscrypt.c +++ b/src/home/homework-fscrypt.c @@ -58,10 +58,10 @@ static int fscrypt_upload_volume_key( }; memcpy(key.raw, volume_key, volume_key_size); + CLEANUP_ERASE(key); + /* Upload to the kernel */ serial = add_key("logon", description, &key, sizeof(key), where); - explicit_bzero_safe(&key, sizeof(key)); - if (serial < 0) return log_error_errno(errno, "Failed to install master key in keyring: %m"); @@ -124,20 +124,18 @@ static int fscrypt_slot_try_one( * resulting hash. */ + CLEANUP_ERASE(derived); + if (PKCS5_PBKDF2_HMAC( password, strlen(password), salt, salt_size, 0xFFFF, EVP_sha512(), - sizeof(derived), derived) != 1) { - r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "PBKDF2 failed"); - goto finish; - } + sizeof(derived), derived) != 1) + return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "PBKDF2 failed"); context = EVP_CIPHER_CTX_new(); - if (!context) { - r = log_oom(); - goto finish; - } + if (!context) + return log_oom(); /* We use AES256 in counter mode */ assert_se(cc = EVP_aes_256_ctr()); @@ -145,13 +143,8 @@ static int fscrypt_slot_try_one( /* We only use the first half of the derived key */ assert(sizeof(derived) >= (size_t) EVP_CIPHER_key_length(cc)); - if (EVP_DecryptInit_ex(context, cc, NULL, derived, NULL) != 1) { - r = log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize decryption context."); - goto finish; - } - - /* Flush out the derived key now, we don't need it anymore */ - explicit_bzero_safe(derived, sizeof(derived)); + if (EVP_DecryptInit_ex(context, cc, NULL, derived, NULL) != 1) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize decryption context."); decrypted_size = encrypted_size + EVP_CIPHER_key_length(cc) * 2; decrypted = malloc(decrypted_size); @@ -184,10 +177,6 @@ static int fscrypt_slot_try_one( *ret_decrypted_size = decrypted_size; return 0; - -finish: - explicit_bzero_safe(derived, sizeof(derived)); - return r; } static int fscrypt_slot_try_many( @@ -413,20 +402,18 @@ static int fscrypt_slot_set( if (r < 0) return log_error_errno(r, "Failed to generate salt: %m"); + CLEANUP_ERASE(derived); + if (PKCS5_PBKDF2_HMAC( password, strlen(password), salt, sizeof(salt), 0xFFFF, EVP_sha512(), - sizeof(derived), derived) != 1) { - r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "PBKDF2 failed"); - goto finish; - } + sizeof(derived), derived) != 1) + return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "PBKDF2 failed"); context = EVP_CIPHER_CTX_new(); - if (!context) { - r = log_oom(); - goto finish; - } + if (!context) + return log_oom(); /* We use AES256 in counter mode */ cc = EVP_aes_256_ctr(); @@ -434,13 +421,8 @@ static int fscrypt_slot_set( /* We only use the first half of the derived key */ assert(sizeof(derived) >= (size_t) EVP_CIPHER_key_length(cc)); - if (EVP_EncryptInit_ex(context, cc, NULL, derived, NULL) != 1) { - r = log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize encryption context."); - goto finish; - } - - /* Flush out the derived key now, we don't need it anymore */ - explicit_bzero_safe(derived, sizeof(derived)); + if (EVP_EncryptInit_ex(context, cc, NULL, derived, NULL) != 1) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize encryption context."); encrypted_size = volume_key_size + EVP_CIPHER_key_length(cc) * 2; encrypted = malloc(encrypted_size); @@ -477,10 +459,6 @@ static int fscrypt_slot_set( log_info("Written key slot %s.", label); return 0; - -finish: - explicit_bzero_safe(derived, sizeof(derived)); - return r; } int home_create_fscrypt( diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c index dc3d70bf1f..fe06f41814 100644 --- a/src/shared/ask-password-api.c +++ b/src/shared/ask-password-api.c @@ -258,6 +258,8 @@ int ask_password_plymouth( if (r < 0) return r; + CLEANUP_ERASE(buffer); + pollfd[POLL_SOCKET].fd = fd; pollfd[POLL_SOCKET].events = POLLIN; pollfd[POLL_INOTIFY].fd = notify; @@ -271,20 +273,16 @@ int ask_password_plymouth( else timeout = USEC_INFINITY; - if (flag_file && access(flag_file, F_OK) < 0) { - r = -errno; - goto finish; - } + if (flag_file && access(flag_file, F_OK) < 0) + return -errno; r = ppoll_usec(pollfd, notify >= 0 ? 2 : 1, timeout); if (r == -EINTR) continue; if (r < 0) - goto finish; - if (r == 0) { - r = -ETIME; - goto finish; - } + return r; + if (r == 0) + return -ETIME; if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0) (void) flush_fd(notify); @@ -297,13 +295,10 @@ int ask_password_plymouth( if (ERRNO_IS_TRANSIENT(errno)) continue; - r = -errno; - goto finish; - } - if (k == 0) { - r = -EIO; - goto finish; + return -errno; } + if (k == 0) + return -EIO; p += k; @@ -315,14 +310,12 @@ int ask_password_plymouth( * with a normal password request */ packet = mfree(packet); - if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0) { - r = -ENOMEM; - goto finish; - } + if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0) + return -ENOMEM; r = loop_write(fd, packet, n+1, true); if (r < 0) - goto finish; + return r; flags &= ~ASK_PASSWORD_ACCEPT_CACHED; p = 0; @@ -330,8 +323,7 @@ int ask_password_plymouth( } /* No password, because UI not shown */ - r = -ENOENT; - goto finish; + return -ENOENT; } else if (IN_SET(buffer[0], 2, 9)) { uint32_t size; @@ -343,35 +335,25 @@ int ask_password_plymouth( memcpy(&size, buffer+1, sizeof(size)); size = le32toh(size); - if (size + 5 > sizeof(buffer)) { - r = -EIO; - goto finish; - } + if (size + 5 > sizeof(buffer)) + return -EIO; if (p-5 < size) continue; l = strv_parse_nulstr(buffer + 5, size); - if (!l) { - r = -ENOMEM; - goto finish; - } + if (!l) + return -ENOMEM; *ret = l; break; - } else { + } else /* Unknown packet */ - r = -EIO; - goto finish; - } + return -EIO; } - r = 0; - -finish: - explicit_bzero_safe(buffer, sizeof(buffer)); - return r; + return 0; } #define NO_ECHO "(no echo) " @@ -433,6 +415,8 @@ int ask_password_tty( return -errno; } + CLEANUP_ERASE(passphrase); + /* If the caller didn't specify a TTY, then use the controlling tty, if we can. */ if (ttyfd < 0) ttyfd = cttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC); @@ -636,7 +620,6 @@ int ask_password_tty( } x = strndup(passphrase, p); - explicit_bzero_safe(passphrase, sizeof(passphrase)); if (!x) { r = -ENOMEM; goto finish; @@ -896,6 +879,8 @@ int ask_password_agent( goto finish; } + CLEANUP_ERASE(passphrase); + cmsg_close_all(&msghdr); if (n == 0) { @@ -920,7 +905,6 @@ int ask_password_agent( l = strv_new(""); else l = strv_parse_nulstr(passphrase+1, n-1); - explicit_bzero_safe(passphrase, n); if (!l) { r = -ENOMEM; goto finish; diff --git a/src/shared/creds-util.c b/src/shared/creds-util.c index 5d04692a5c..b416e873af 100644 --- a/src/shared/creds-util.c +++ b/src/shared/creds-util.c @@ -216,7 +216,6 @@ static int make_credential_host_secret( void **ret_data, size_t *ret_size) { - struct credential_host_secret_format buf; _cleanup_free_ char *t = NULL; _cleanup_close_ int fd = -EBADF; int r; @@ -245,21 +244,23 @@ static int make_credential_host_secret( if (r < 0) log_debug_errno(r, "Failed to set file attributes for secrets file, ignoring: %m"); - buf = (struct credential_host_secret_format) { + struct credential_host_secret_format buf = { .machine_id = machine_id, }; + CLEANUP_ERASE(buf); + r = crypto_random_bytes(buf.data, sizeof(buf.data)); if (r < 0) - goto finish; + goto fail; r = loop_write(fd, &buf, sizeof(buf), false); if (r < 0) - goto finish; + goto fail; if (fsync(fd) < 0) { r = -errno; - goto finish; + goto fail; } warn_not_encrypted(fd, flags, dirname, fn); @@ -267,17 +268,17 @@ static int make_credential_host_secret( if (t) { r = rename_noreplace(dfd, t, dfd, fn); if (r < 0) - goto finish; + goto fail; t = mfree(t); } else if (linkat(fd, "", dfd, fn, AT_EMPTY_PATH) < 0) { r = -errno; - goto finish; + goto fail; } if (fsync(dfd) < 0) { r = -errno; - goto finish; + goto fail; } if (ret_data) { @@ -286,7 +287,7 @@ static int make_credential_host_secret( copy = memdup(buf.data, sizeof(buf.data)); if (!copy) { r = -ENOMEM; - goto finish; + goto fail; } *ret_data = copy; @@ -295,13 +296,12 @@ static int make_credential_host_secret( if (ret_size) *ret_size = sizeof(buf.data); - r = 0; + return 0; -finish: +fail: if (t && unlinkat(dfd, t, 0) < 0) log_debug_errno(errno, "Failed to remove temporary credential key: %m"); - explicit_bzero_safe(&buf, sizeof(buf)); return r; } diff --git a/src/shared/ethtool-util.c b/src/shared/ethtool-util.c index e2b2ca2352..e978b8bf7e 100644 --- a/src/shared/ethtool-util.c +++ b/src/shared/ethtool-util.c @@ -434,6 +434,8 @@ int ethtool_set_wol( strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname); + CLEANUP_ERASE(ecmd); + if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0) return -errno; @@ -466,16 +468,11 @@ int ethtool_set_wol( need_update = true; } - if (!need_update) { - explicit_bzero_safe(&ecmd, sizeof(ecmd)); + if (!need_update) return 0; - } ecmd.cmd = ETHTOOL_SWOL; - r = RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr)); - - explicit_bzero_safe(&ecmd, sizeof(ecmd)); - return r; + return RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr)); } int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netdev_ring_param *ring) { diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index ba8dfb041d..2b34e4fd65 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -735,13 +735,14 @@ static void hash_pin(const char *pin, size_t len, TPM2B_AUTH *auth) { assert(auth); assert(pin); + auth->size = SHA256_DIGEST_SIZE; + CLEANUP_ERASE(hash); + sha256_init_ctx(&hash); sha256_process_bytes(pin, len, &hash); sha256_finish_ctx(&hash, auth->buffer); - - explicit_bzero_safe(&hash, sizeof(hash)); } static int tpm2_make_encryption_session( @@ -773,11 +774,11 @@ static int tpm2_make_encryption_session( if (pin) { TPM2B_AUTH auth = {}; + CLEANUP_ERASE(auth); + hash_pin(pin, strlen(pin), &auth); rc = sym_Esys_TR_SetAuth(c, bind_key, &auth); - /* ESAPI knows about it, so clear it from our memory */ - explicit_bzero_safe(&auth, sizeof(auth)); if (rc != TSS2_RC_SUCCESS) return log_error_errno( SYNTHETIC_ERRNO(ENOTRECOVERABLE), @@ -1369,8 +1370,8 @@ int tpm2_seal(const char *device, static const TPML_PCR_SELECTION creation_pcr = {}; _cleanup_(erase_and_freep) void *secret = NULL; _cleanup_free_ void *blob = NULL, *hash = NULL; - TPM2B_SENSITIVE_CREATE hmac_sensitive; ESYS_TR primary = ESYS_TR_NONE, session = ESYS_TR_NONE; + TPM2B_SENSITIVE_CREATE hmac_sensitive; TPMI_ALG_PUBLIC primary_alg; TPM2B_PUBLIC hmac_template; TPMI_ALG_HASH pcr_bank; @@ -1410,6 +1411,8 @@ int tpm2_seal(const char *device, start = now(CLOCK_MONOTONIC); + CLEANUP_ERASE(hmac_sensitive); + r = tpm2_context_init(device, &c); if (r < 0) return r; @@ -1498,7 +1501,6 @@ int tpm2_seal(const char *device, } secret = memdup(hmac_sensitive.sensitive.data.buffer, hmac_sensitive.sensitive.data.size); - explicit_bzero_safe(hmac_sensitive.sensitive.data.buffer, hmac_sensitive.sensitive.data.size); if (!secret) { r = log_oom(); goto finish; @@ -1559,7 +1561,6 @@ int tpm2_seal(const char *device, r = 0; finish: - explicit_bzero_safe(&hmac_sensitive, sizeof(hmac_sensitive)); primary = tpm2_flush_context_verbose(c.esys_context, primary); session = tpm2_flush_context_verbose(c.esys_context, session); return r; diff --git a/src/test/test-hexdecoct.c b/src/test/test-hexdecoct.c index afdc3b5436..9d71db6ae1 100644 --- a/src/test/test-hexdecoct.c +++ b/src/test/test-hexdecoct.c @@ -322,6 +322,13 @@ TEST(base64mem_linebreak) { assert_se(decoded_size == n); assert_se(memcmp(data, decoded, n) == 0); + /* Also try in secure mode */ + decoded = mfree(decoded); + decoded_size = 0; + assert_se(unbase64mem_full(encoded, SIZE_MAX, /* secure= */ true, &decoded, &decoded_size) >= 0); + assert_se(decoded_size == n); + assert_se(memcmp(data, decoded, n) == 0); + for (size_t j = 0; j < (size_t) l; j++) assert_se((encoded[j] == '\n') == (j % (m + 1) == m)); } @@ -446,7 +453,17 @@ static void test_unbase64mem_one(const char *input, const char *output, int ret) size_t size = 0; assert_se(unbase64mem(input, SIZE_MAX, &buffer, &size) == ret); + if (ret >= 0) { + assert_se(size == strlen(output)); + assert_se(memcmp(buffer, output, size) == 0); + assert_se(((char*) buffer)[size] == 0); + } + + /* also try in secure mode */ + buffer = mfree(buffer); + size = 0; + assert_se(unbase64mem_full(input, SIZE_MAX, /* secure=*/ true, &buffer, &size) == ret); if (ret >= 0) { assert_se(size == strlen(output)); assert_se(memcmp(buffer, output, size) == 0); |