Hi Remco and all,

On Sun, Apr 19, 2020 at 08:02:05AM -0400, Remco Rijnders wrote:
> On the proposal to use a hashed value for the GA12345 part, I now have some
> doubts as I think it would be a small effort to iterate through all
> plausible values for this to regenerate the hashes and still make
> observations based on those results. You would have to salt the values in
> some way before the hash and that might effect the deterministic part you'd
> like to keep.

Practical downsides of a hash:
- renders openssl or gnutls to be required
- Mutt must deal with both alternatives
- wastes space in the message id (even SHA1 leads to 40 chars)

> As you can see from this message, I am still experimenting/entertaining some
> other options, this current one using two random numbers as the date and
> timestamp included are redundant information that is already included in the
> other headers on my system as pointed out by Derek. This really simplifies
> the code even further, but does not yet address the RAND_MAX concern.

Braining about deterministic sources for seeding on machine scope -
with fewest possible meaning in sense of security and information - I
concluded that data of an arbitrary file we know that it likely exists
fits (I chose Muttrc):
Some lstat data is really boring and of no use for attackers because it
is much easier to access files on a direct path.

So with messageid.c I created a demonstration of this approach, which
could inspire you and others. With this it is guaranteed that two
independent instances of mutt on the same machine use different seeds
within the same second.
The 2-step generation of MsgIdSeq further reduces chance of collisions.

IMO this demo combines the best of the worlds "deterministic" and
"randomness for privacy".


Comments about integration

Use of random () elsewhere is for creating names of tmp files. I don't
see this function to be disturbed by the new handling of seeds, as also
this aims to avoid clashes, so guaranteed different seed is a benefit.

When adopting the approach, for location of initialization it should be
deciced whether sendlib.c now owns the random generator, or alternately
stays as a generic resource for mutt with separate management (then
init.c should get the more sophisticated initialization).

Unfortunately the reentrant versions of random generators are not
portable. Those would allow independent seeding for sendlib.c .


What do you think?


Kind regards,
   Gero
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>



#ifndef MUTT_MOCK

#define SHORT_STRING 128
#define NONULL(x) x?x:""

static const char* Fqdn;
static const char* Hostname;

void *safe_malloc (size_t siz)
{
  void *p;

  if (siz == 0)
    return 0;
  if ((p = (void *) malloc (siz)) == 0) /* __MEM_CHECKED__ */
  {
    exit (1);
  }
  return (p);
}

char *safe_strdup (const char *s)
{
  char *p;
  size_t l;

  if (!s || !*s)
    return 0;
  l = strlen (s) + 1;
  p = (char *)safe_malloc (l);
  memcpy (p, s, l);
  return (p);
}

const char *mutt_fqdn (short may_hide_host)
{
  return Fqdn;
}

#endif /* MUTT_MOCK */



#define RANDOM_STATE_SIZE 32

static unsigned int MsgIdMainSeed;
static char MsgIdSeedStateBuf[RANDOM_STATE_SIZE];
static time_t MsgIdSeqLastRefresh;
static long int MsgIdSeq;

void seed_init (const char *seed_path)
{
  struct stat sb;
  pid_t pid;
  unsigned int seed;

  pid = getpid ();
  if (lstat (seed_path, &sb) != 0)
    lstat("/", &sb);

  /* shuffle to circumvent later XOR with times wiping bits */
  seed = (sb.st_ctim.tv_sec >> 10) + (sb.st_ctim.tv_sec << 22);

  puts("Setup");
  printf ("sec  %8x\n", seed);

  seed ^= (sb.st_ctim.tv_nsec << 2) ^ sb.st_ino ^ pid;

  printf ("nsec %8lx\n", sb.st_ctim.tv_nsec << 2);
  printf ("ino  %8lx\n", sb.st_ino);
  printf ("pid  %8x\n", pid);
  printf ("seed %8x\n\n", seed);

  MsgIdMainSeed = seed;
  initstate (seed, MsgIdSeedStateBuf, RANDOM_STATE_SIZE);
}

char *mutt_gen_msgid (void)
{
  char buf[SHORT_STRING];
  time_t now;
  struct tm *tm;
  const char *fqdn;

  now = time (NULL);
  tm = gmtime (&now);
  if (!(fqdn = mutt_fqdn(0)))
    fqdn = NONULL(Hostname);

  if (MsgIdSeqLastRefresh != now)
  {
    MsgIdSeqLastRefresh = now;
    srandom (MsgIdMainSeed ^ now);
    MsgIdSeq = (random () << 20) ^ random ();
  }
  else
  {
    MsgIdSeq += random ();
  }

  snprintf (buf, sizeof (buf), "<%d%02d%02d%02d%02d%02d.G%lX@%s>",
            tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour,
            tm->tm_min, tm->tm_sec, MsgIdSeq, fqdn);
  return (safe_strdup (buf));
}

int main (int argc, char **argv)
{
  int i;

  Hostname = "host";
  Hostname = "host.example.org";

  seed_init ("/etc/Muttrc");

  for (i = 0; i < 30; ++i)
  {
    puts (mutt_gen_msgid ());
    if (i % 10 == 9)
    {
      puts("");
      sleep (1);
    }
  }
}

Reply via email to