kennethk has asked for the wisdom of the Perl Monks concerning the following question:

Oh wise and beneficent monks,

I am creating Secret Santa script. Being prone to overengineering, I plan to put a link in cron.yearly and use sendmail via nbsmtp to distribute the results (it's a home machine so I worry about a local mail server getting blacklisted). I found a great example here.

My problem arises because I don't want any pairing repeated in two consecutive years. The only solution I can think of is sending a master list to an independent authority and manually collecting it after the date. This would make the whole automation concept kind of inane.

Is there a way to store this mapping information locally where it would be (practically) inaccessible, at least until January 1? I don't plan on cheating, but a file named SantaList.txt on my desktop would probably raise some eyebrows.

Your humble initiate,

Ken

Replies are listed 'Best First'.
Re: Temporarily Obscuring a Lottery Draw
by Fletch (Bishop) on Oct 31, 2008 at 17:53 UTC
    • Create a GPG / PGP key and put the private half of the key on a USB drive that you give to someone else.
    • Have your program encrypt the master list with the public key and save that to a separate USB drive.
    • After your window recover the private key and decrypt the master list.

    Alternately you could look at some sort of secret sharing implementation where you encrypt and split up the master list into n chunks that you need any m of them to recover the original master list. Everyone gets a chunk of the list with their assignment and you'd have to have m of them collude to get it back (so you'd want to set m sufficiently high, of course).

    (Well, you did say you were over-engineering . . . :)

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

Re: Temporarily Obscuring a Lottery Draw
by Perlbotics (Archbishop) on Oct 31, 2008 at 19:30 UTC

    If I understand your question correct, then you only need to discover repeated pairings, no need to save the concrete names (but see below) ?

    You could normalise a pair (e.g. sort alphabetically → "Lois,Tarzan" or "Clark,Jane"), pipe that through an MD5 or SHA1 and save this obfuscated pair (e.g. a line of hex digits: affe8deadbeef0815.... etc.) into a text file. It should be obfuscated enough to scare the occasional snoop. Your script would test for a duplicate by generating the hash for the pair to test and look it up in the list of historical pairings hashes. When found, start a new draw.

    Instead of running an eMail distribution-script once a year, run the Secret Santa script once a year and let it send the eMails in the same turn. After the script terminates, the concrete names are lost, but the file is updated with a couple of new pairings (hashes) to avoid duplicated pairings for next year's run. If your number of pairs is large, a faster and more compact way to save the pairings is required.

      pipe that through an MD5 or SHA1
      .. or even the brand new Skein algorithm from Schneier et al. available as Digest::Skein on CPAN:
      use strict; use warnings; use Digest::Skein; print Digest::Skein->new(256)->add('foo bar baz')->hexdigest; __END__ $ perl -wl skein.pl 9b575b4eb237749e706e81f54ac35fdcefff522297161770b8a2c3457c317a4d
      --
      No matter how great and destructive your problems may seem now, remember, you've probably only seen the tip of them. [1]
Re: Temporarily Obscuring a Lottery Draw
by BrowserUk (Patriarch) on Oct 31, 2008 at 17:58 UTC
Re: Temporarily Obscuring a Lottery Draw
by JavaFan (Canon) on Oct 31, 2008 at 18:27 UTC
    but a file named SantaList.txt on my desktop would probably raise some eyebrows.
    Call the file "EasterBunny.txt".
Re: Temporarily Obscuring a Lottery Draw
by mr_mischief (Monsignor) on Oct 31, 2008 at 19:31 UTC
    The easiest ways to keep the current year's list from being disclosed to a participant are to either not store the list at all, or to have the administrator of the selection process not participate in the gift exchange.

    If you have a small enough office that you can get everyone to let you know whose name they had after the exchange, you can create this year's list for reference next year after the exchange. Then, you only need to make sure as you select names that you're not repeating, and don't have to store the new selections. Then next year you can collect the list from the participants again. That's a bit of a headache as the number of people involved grows, I understand.

    The other option I mentioned, to have the administrator of the selection process not participate, doesn't mean you need an outside company. It just means you, or whoever runs the selection code you write if it's not you, doesn't give or get a present. Your contribution is the code and the operation of the process, for which you get the warm fuzzy feeling of having facilitated the gift exchange for others. Snooping coworkers may still be an issue. ;-)

    If neither of these are workable, then BrowserUk's email reminder idea or Fletch's secret sharing idea would both give you a better match. I like the simplicity of BrowserUk's suggestion to use an email reminder system. One can think of it as an automated impartial outside party. The secret sharing idea Fletch proposed has some serious geek cred to it, and should be a novel and fun way to handle things.

Re: Temporarily Obscuring a Lottery Draw
by kennethk (Abbot) on Nov 01, 2008 at 20:53 UTC

    Thank you all for your wonderful suggestions. I'm going to try following Perlbotics' suggestions regarding md5ing the pairs and storing that. If that doesn't work (either people complain that this info is on my machine or someone actually bothers to try to crack it), I'll follow BrowserUK's suggestion. The local third party approach mr_mischief suggests is clearly unacceptable, as everyone here is quite fond of presents. And of course, I'm tempted to try Fletch's solution just because. I mean, damn.

      May I recommend two improvements?

      • If you're ok with there being only a single chain — I'd even think it was preferable — then

        @matching{@names} = shuffle(@names); # Match 'em up # No one has themselves foreach (@names) { next GENERATE if $_ eq $matching{$_}; }

        simplifies to

        %matching = ( map { $_, $_ } @names )[ 1..@names*2-1, 0 ];
      • Another common limit is that B doesn't give to A if A gave to B the previous year. That can be achieved by changing

        push @digest, md5_hex($_, '=>', $matching{$_});

        to

        push @digest, md5_hex(join '<=>', sort $_, $matching{$_});

      someone actually bothers to try to crack it

      That's *very* easy to do. Say you have "N" participants.

      • To find out from whom someone specific receives requires at most N hashes, half that on average.
      • To find out to whom someone specific gives requires at most N hashes, half that on average.
      • To find all relations requires at most (N*(N-1)-1)/2 hashes, half that on average.

      It's even easier than attacking passwords of length 2. Hashing the pairings is merely security theatre. You might as well MIME::Base64 them.

        B doesn't give to A if A gave to B the previous year

        That's an interesting one - I may implement it

        That's *very* easy to do

        The real security is chmodding the file to 600. However, there are a few people in the admin group (including myself), so the md5 is more about overhead (making someone take time to write a script) than actual security. The only truly secure choice I can see implementing is the e-mail reminder.