LCOV - code coverage report
Current view: top level - lib/crypt_ops - crypto_format.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 121 124 97.6 %
Date: 2021-11-24 03:28:48 Functions: 13 13 100.0 %

          Line data    Source code
       1             : /* Copyright (c) 2001, Matej Pfajfar.
       2             :  * Copyright (c) 2001-2004, Roger Dingledine.
       3             :  * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
       4             :  * Copyright (c) 2007-2021, The Tor Project, Inc. */
       5             : /* See LICENSE for licensing information */
       6             : 
       7             : /**
       8             :  * \file crypto_format.c
       9             :  *
      10             :  * \brief Formatting and parsing code for crypto-related data structures.
      11             :  */
      12             : 
      13             : #include "orconfig.h"
      14             : #ifdef HAVE_SYS_STAT_H
      15             : #include <sys/stat.h>
      16             : #endif
      17             : #include "lib/container/smartlist.h"
      18             : #include "lib/crypt_ops/crypto_curve25519.h"
      19             : #include "lib/crypt_ops/crypto_digest.h"
      20             : #include "lib/crypt_ops/crypto_ed25519.h"
      21             : #include "lib/crypt_ops/crypto_format.h"
      22             : #include "lib/crypt_ops/crypto_util.h"
      23             : #include "lib/string/compat_string.h"
      24             : #include "lib/string/util_string.h"
      25             : #include "lib/string/printf.h"
      26             : #include "lib/encoding/binascii.h"
      27             : #include "lib/log/log.h"
      28             : #include "lib/log/util_bug.h"
      29             : #include "lib/fs/files.h"
      30             : 
      31             : #include <string.h>
      32             : #include <errno.h>
      33             : 
      34             : /** Write the <b>datalen</b> bytes from <b>data</b> to the file named
      35             :  * <b>fname</b> in the tagged-data format.  This format contains a
      36             :  * 32-byte header, followed by the data itself.  The header is the
      37             :  * NUL-padded string "== <b>typestring</b>: <b>tag</b> ==".  The length
      38             :  * of <b>typestring</b> and <b>tag</b> must therefore be no more than
      39             :  * 24.
      40             :  **/
      41             : int
      42          77 : crypto_write_tagged_contents_to_file(const char *fname,
      43             :                                      const char *typestring,
      44             :                                      const char *tag,
      45             :                                      const uint8_t *data,
      46             :                                      size_t datalen)
      47             : {
      48          77 :   char header[32];
      49          77 :   smartlist_t *chunks = smartlist_new();
      50          77 :   sized_chunk_t ch0, ch1;
      51          77 :   int r = -1;
      52             : 
      53          77 :   memset(header, 0, sizeof(header));
      54          77 :   if (tor_snprintf(header, sizeof(header),
      55             :                    "== %s: %s ==", typestring, tag) < 0)
      56           0 :     goto end;
      57          77 :   ch0.bytes = header;
      58          77 :   ch0.len = 32;
      59          77 :   ch1.bytes = (const char*) data;
      60          77 :   ch1.len = datalen;
      61          77 :   smartlist_add(chunks, &ch0);
      62          77 :   smartlist_add(chunks, &ch1);
      63             : 
      64          77 :   r = write_chunks_to_file(fname, chunks, 1, 0);
      65             : 
      66          77 :  end:
      67          77 :   smartlist_free(chunks);
      68          77 :   return r;
      69             : }
      70             : 
      71             : /** Read a tagged-data file from <b>fname</b> into the
      72             :  * <b>data_out_len</b>-byte buffer in <b>data_out</b>. Check that the
      73             :  * typestring matches <b>typestring</b>; store the tag into a newly allocated
      74             :  * string in <b>tag_out</b>. Return -1 on failure, and the number of bytes of
      75             :  * data on success.  Preserves the errno from reading the file. */
      76             : ssize_t
      77         246 : crypto_read_tagged_contents_from_file(const char *fname,
      78             :                                       const char *typestring,
      79             :                                       char **tag_out,
      80             :                                       uint8_t *data_out,
      81             :                                       ssize_t data_out_len)
      82             : {
      83         246 :   char prefix[33];
      84         246 :   char *content = NULL;
      85         246 :   struct stat st;
      86         246 :   ssize_t r = -1;
      87         246 :   size_t st_size = 0;
      88         246 :   int saved_errno = 0;
      89             : 
      90         246 :   *tag_out = NULL;
      91         246 :   st.st_size = 0;
      92         246 :   content = read_file_to_str(fname, RFTS_BIN|RFTS_IGNORE_MISSING, &st);
      93         246 :   if (! content) {
      94         113 :     saved_errno = errno;
      95         113 :     goto end;
      96             :   }
      97         133 :   if (st.st_size < 32 || st.st_size > 32 + data_out_len) {
      98           1 :     saved_errno = EINVAL;
      99           1 :     goto end;
     100             :   }
     101         132 :   st_size = (size_t)st.st_size;
     102             : 
     103         132 :   memcpy(prefix, content, 32);
     104         132 :   prefix[32] = 0;
     105             :   /* Check type, extract tag. */
     106         264 :   if (strcmpstart(prefix, "== ") || strcmpend(prefix, " ==") ||
     107         132 :       ! fast_mem_is_zero(prefix+strlen(prefix), 32-strlen(prefix))) {
     108           0 :     saved_errno = EINVAL;
     109           0 :     goto end;
     110             :   }
     111             : 
     112         132 :   if (strcmpstart(prefix+3, typestring) ||
     113         262 :       3+strlen(typestring) >= 32 ||
     114         131 :       strcmpstart(prefix+3+strlen(typestring), ": ")) {
     115           1 :     saved_errno = EINVAL;
     116           1 :     goto end;
     117             :   }
     118             : 
     119         131 :   *tag_out = tor_strndup(prefix+5+strlen(typestring),
     120             :                          strlen(prefix)-8-strlen(typestring));
     121             : 
     122         131 :   memcpy(data_out, content+32, st_size-32);
     123         131 :   r = st_size - 32;
     124             : 
     125         246 :  end:
     126         246 :   if (content)
     127         133 :     memwipe(content, 0, st_size);
     128         246 :   tor_free(content);
     129         246 :   if (saved_errno)
     130         113 :     errno = saved_errno;
     131         246 :   return r;
     132             : }
     133             : 
     134             : /** Encode <b>pkey</b> as a base64-encoded string in the buffer <b>output</b>.
     135             :  * If <b>pad</b> is false do not include trailing "=" characters, otherwise
     136             :  * include them. <b>output</b> must have at least
     137             :  * CURVE25519_BASE64_PADDED_LEN+1 bytes available, even if <b>pad</b> is false.
     138             :  * Can not fail.
     139             :  *
     140             :  * Careful! CURVE25519_BASE64_PADDED_LEN is one byte longer than
     141             :  * ED25519_BASE64_LEN.
     142             :  */
     143             : void
     144         297 : curve25519_public_to_base64(char *output,
     145             :                             const curve25519_public_key_t *pkey, bool pad)
     146             : {
     147         297 :   int n, expected_len;
     148         297 :   if (pad) {
     149         271 :     n = base64_encode(output, CURVE25519_BASE64_PADDED_LEN+1,
     150         271 :                       (const char*)pkey->public_key,
     151             :                       CURVE25519_PUBKEY_LEN, 0);
     152         271 :     expected_len = CURVE25519_BASE64_PADDED_LEN;
     153             :   } else {
     154          26 :     n = base64_encode_nopad(output, CURVE25519_BASE64_PADDED_LEN+1,
     155          26 :                             (const uint8_t*)pkey->public_key,
     156             :                             CURVE25519_PUBKEY_LEN);
     157          26 :     expected_len = CURVE25519_BASE64_LEN;
     158             :   }
     159             : 
     160             :   /* These asserts should always succeed, unless there is a bug in
     161             :    * base64_encode(). */
     162         297 :   tor_assert(n == expected_len);
     163         297 :   tor_assert(output[expected_len] == '\0');
     164         297 : }
     165             : 
     166             : /** Try to decode a base64-encoded curve25519 public key from <b>input</b>
     167             :  * into the object at <b>pkey</b>. Return 0 on success, -1 on failure.
     168             :  * Accepts keys with or without a trailing "=". */
     169             : int
     170         521 : curve25519_public_from_base64(curve25519_public_key_t *pkey,
     171             :                               const char *input)
     172             : {
     173         521 :   size_t len = strlen(input);
     174         521 :   if (len == CURVE25519_BASE64_LEN) {
     175             :     /* not padded */
     176          62 :     return digest256_from_base64((char*)pkey->public_key, input);
     177         459 :   } else if (len == CURVE25519_BASE64_PADDED_LEN) {
     178         454 :     char buf[CURVE25519_BASE64_PADDED_LEN+1];
     179         454 :     if (base64_decode(buf, sizeof(buf), input, len) != CURVE25519_PUBKEY_LEN)
     180             :       return -1;
     181         454 :     memcpy(pkey->public_key, buf, CURVE25519_PUBKEY_LEN);
     182         454 :     return 0;
     183             :   } else {
     184             :     return -1;
     185             :   }
     186             : }
     187             : 
     188             : /** For logging convenience: Convert <b>pkey</b> to a statically allocated
     189             :  * base64 string and return it. Not threadsafe. Format not meant to be
     190             :  * computer-readable; it may change in the future. Subsequent calls invalidate
     191             :  * previous returns. */
     192             : const char *
     193         159 : ed25519_fmt(const ed25519_public_key_t *pkey)
     194             : {
     195         159 :   static char formatted[ED25519_BASE64_LEN+1];
     196         159 :   if (pkey) {
     197         152 :     if (ed25519_public_key_is_zero(pkey)) {
     198          13 :       strlcpy(formatted, "<unset>", sizeof(formatted));
     199             :     } else {
     200         139 :       ed25519_public_to_base64(formatted, pkey);
     201             :     }
     202             :   } else {
     203           7 :     strlcpy(formatted, "<null>", sizeof(formatted));
     204             :   }
     205         159 :   return formatted;
     206             : }
     207             : 
     208             : /** Try to decode the string <b>input</b> into an ed25519 public key. On
     209             :  * success, store the value in <b>pkey</b> and return 0. Otherwise return
     210             :  * -1. */
     211             : int
     212         132 : ed25519_public_from_base64(ed25519_public_key_t *pkey,
     213             :                            const char *input)
     214             : {
     215         132 :   return digest256_from_base64((char*)pkey->pubkey, input);
     216             : }
     217             : 
     218             : /** Encode the public key <b>pkey</b> into the buffer at <b>output</b>,
     219             :  * which must have space for ED25519_BASE64_LEN bytes of encoded key,
     220             :  * plus one byte for a terminating NUL.
     221             :  * Can not fail.
     222             :  *
     223             :  * Careful! ED25519_BASE64_LEN is one byte shorter than
     224             :  * CURVE25519_BASE64_PADDED_LEN.
     225             :  */
     226             : void
     227         395 : ed25519_public_to_base64(char *output,
     228             :                          const ed25519_public_key_t *pkey)
     229             : {
     230         395 :   digest256_to_base64(output, (const char *)pkey->pubkey);
     231         395 : }
     232             : 
     233             : /** Encode the signature <b>sig</b> into the buffer at <b>output</b>,
     234             :  * which must have space for ED25519_SIG_BASE64_LEN bytes of encoded signature,
     235             :  * plus one byte for a terminating NUL.
     236             :  * Can not fail.
     237             :  */
     238             : void
     239          87 : ed25519_signature_to_base64(char *output,
     240             :                             const ed25519_signature_t *sig)
     241             : {
     242          87 :   char buf[256];
     243          87 :   int n = base64_encode_nopad(buf, sizeof(buf), sig->sig, ED25519_SIG_LEN);
     244             :   /* These asserts should always succeed, unless there is a bug in
     245             :    * base64_encode_nopad(). */
     246          87 :   tor_assert(n == ED25519_SIG_BASE64_LEN);
     247          87 :   tor_assert(buf[ED25519_SIG_BASE64_LEN] == '\0');
     248          87 :   memcpy(output, buf, ED25519_SIG_BASE64_LEN+1);
     249          87 : }
     250             : 
     251             : /** Try to decode the string <b>input</b> into an ed25519 signature. On
     252             :  * success, store the value in <b>sig</b> and return 0. Otherwise return
     253             :  * -1. */
     254             : int
     255         227 : ed25519_signature_from_base64(ed25519_signature_t *sig,
     256             :                               const char *input)
     257             : {
     258         227 :   if (strlen(input) != ED25519_SIG_BASE64_LEN)
     259             :     return -1;
     260         220 :   char decoded[128];
     261         220 :   int n = base64_decode(decoded, sizeof(decoded), input,
     262             :                         ED25519_SIG_BASE64_LEN);
     263         220 :   if (n < 0 || n != ED25519_SIG_LEN)
     264             :     return -1;
     265         219 :   memcpy(sig->sig, decoded, ED25519_SIG_LEN);
     266             : 
     267         219 :   return 0;
     268             : }
     269             : 
     270             : /** Base64 encode DIGEST_LEN bytes from <b>digest</b>, remove the trailing =
     271             :  * characters, and store the nul-terminated result in the first
     272             :  * BASE64_DIGEST_LEN+1 bytes of <b>d64</b>.
     273             :  * Can not fail. */
     274             : void
     275         597 : digest_to_base64(char *d64, const char *digest)
     276             : {
     277         597 :   char buf[256];
     278         597 :   int n = base64_encode_nopad(buf, sizeof(buf),
     279             :                               (const uint8_t *)digest, DIGEST_LEN);
     280             :   /* These asserts should always succeed, unless there is a bug in
     281             :    * base64_encode_nopad(). */
     282         597 :   tor_assert(n == BASE64_DIGEST_LEN);
     283         597 :   tor_assert(buf[BASE64_DIGEST_LEN] == '\0');
     284         597 :   memcpy(d64, buf, BASE64_DIGEST_LEN+1);
     285         597 : }
     286             : 
     287             : /** Given a base64 encoded, nul-terminated digest in <b>d64</b> (without
     288             :  * trailing newline or = characters), decode it and store the result in the
     289             :  * first DIGEST_LEN bytes at <b>digest</b>. */
     290             : int
     291        1455 : digest_from_base64(char *digest, const char *d64)
     292             : {
     293        1455 :   if (base64_decode(digest, DIGEST_LEN, d64, strlen(d64)) == DIGEST_LEN)
     294             :     return 0;
     295             :   else
     296           1 :     return -1;
     297             : }
     298             : 
     299             : /** Base64 encode DIGEST256_LINE bytes from <b>digest</b>, remove the
     300             :  * trailing = characters, and store the nul-terminated result in the first
     301             :  * BASE64_DIGEST256_LEN+1 bytes of <b>d64</b>.
     302             :  * Can not fail. */
     303             : void
     304         500 : digest256_to_base64(char *d64, const char *digest)
     305             : {
     306         500 :   char buf[256];
     307         500 :   int n = base64_encode_nopad(buf, sizeof(buf),
     308             :                               (const uint8_t *)digest, DIGEST256_LEN);
     309             :   /* These asserts should always succeed, unless there is a bug in
     310             :    * base64_encode_nopad(). */
     311         500 :   tor_assert(n == BASE64_DIGEST256_LEN);
     312         500 :   tor_assert(buf[BASE64_DIGEST256_LEN] == '\0');
     313         500 :   memcpy(d64, buf, BASE64_DIGEST256_LEN+1);
     314         500 : }
     315             : 
     316             : /** Given a base64 encoded, nul-terminated digest in <b>d64</b> (without
     317             :  * trailing newline or = characters), decode it and store the result in the
     318             :  * first DIGEST256_LEN bytes at <b>digest</b>. */
     319             : int
     320         607 : digest256_from_base64(char *digest, const char *d64)
     321             : {
     322         607 :   if (base64_decode(digest, DIGEST256_LEN, d64, strlen(d64)) == DIGEST256_LEN)
     323             :     return 0;
     324             :   else
     325           7 :     return -1;
     326             : }

Generated by: LCOV version 1.14