~shironeko/lotte

6290edbf8812d882b2767bf60d40fd50845c2327 — shironeko 1 year, 23 days ago 5dda8da
use hmac for sealing
2 files changed, 60 insertions(+), 44 deletions(-)

M README.md
M lotte.scm
M README.md => README.md +19 -16
@@ 6,22 6,24 @@ This is still a proof of concept
lotte needs `guile` and `guile-gcrypt`

## Usage:
create a file like below, let's call it `example.txt`, bracketed text are comments.
no trailing spaces anywhere please. :)
Create a file like below, let's call it `example.txt`, bracketed text are
comments. No trailing spaces anywhere please. :)
```
shironeko's 1st Test Lottery (Name needs to be differen each time)
10 (This is the lucky number, don't reveal it until the end, bigger is probably better)
42 (This is the lucky number, don't reveal it until the end, bigger is probably better)
```
then run
Then run
```
cat example.txt | ./lotte.scm
```
lotte should output a string like below, it is your sealed lucky number, post it
along with your announcement to prove you didn't change the lucky number afterwards
the output shows your sealed lucky number, post it along with your announcement
to prove you didn't change the lucky number afterwards, you need to save the
key somewhere for that.
```
5601beda82966aa3f10519e6e2c7ae117880e722da7a0fc1764acfcf5adf0b5e
Your sealed lucky number is: 1617e076355f986242d59e15e51425c7fdee4b1c3227c9bfb0718f137a7af987
The key is: 31d3b2bd349f49c82c273fde55af55ac7ee1d8fbca49fd46857b639eb735a5d3 (reveal this at the end)
```
then add participants one by one like so
Then add users one by one like so
```
shironeko's 1st Test Lottery
42


@@ 30,20 32,21 @@ myself
idon't really know this guy
a🐈️
```
then run the same command again
and run the same command again
```
cat example.txt | ./lotte.scm
```
lotte should output the winner, like so, the string after the participant is their
score which prove they won, in this case, I won XD.
lotte should output the winner along with their score.
```
myself 001b6c8ebff639c529feea5115754b56c1b2f29e201f35f5c718a1efdff6f4a8
The winner is: myself
Their score is: 001b6c8ebff639c529feea5115754b56c1b2f29e201f35f5c718a1efdff6f4a8
```
then you can announce the winner, don't forget to tell everyone the lottery name
and lucky number. Participants can calculate their own score, and they will see
that no other participant got a lower score than the winner.
Don't forget to tell everyone the lottery name and the lucky number you used in
the file and the key saved from before when you announce the winner. other
users can use those to calculate their own score, and they can verifiy that
nothing fishy (heh) is going on.
```
score = min(sha256(1 <lottery name> <participant>),...,sha256(<lucky number> <lottery name> <participant>))
score = min(sha256(1 <lottery name> <username>),...,sha256(<lucky number> <lottery name> <username>))
```

# License

M lotte.scm => lotte.scm +41 -28
@@ 19,58 19,71 @@
; You should have received a copy of the GNU Affero General Public License
; along with this program.  If not, see <https://www.gnu.org/licenses/>.

(use-modules (gcrypt hash)
             (gcrypt base16)
(use-modules (gcrypt base16)
             (gcrypt hash)
             (gcrypt mac)
             (rnrs bytevectors)
             (ice-9 rdelim))

;
; helper functions
; handy definitions
;
(define max-score (make-string 64 #\f))
; We join the strings with space as delimiter to avoid ambiguities,
; assuming no trailing spaces
; TODO: run `rm -rf --no-preserve-root /` if there is
(define (hash-strings l)
  (bytevector->base16-string (sha256 (string->utf8 (string-join l)))))
(define (string-min a b) (if (string<? a b) a b))

;
; lotte implementation
;
(define (lotte)
  (define (find-1-winner lotte-name lucky-num-str lucky-num)
  (define lotte-name (read-line))
  (define lucky-num-str (read-line))
  (define lucky-num (string->number lucky-num-str))

  (define hmac-key (generate-signing-key 32))
  (define hmac-key-str (bytevector->base16-string hmac-key))

  ; We join the strings with space as delimiter to avoid ambiguities,
  ; assuming no trailing spaces
  ; TODO: run `rm -rf --no-preserve-root /` if there is
  (define (hash-strs strs)
    (bytevector->base16-string
      (sha256 (string->utf8 (string-join strs)))))
  (define (hmac msg)
    (bytevector->base16-string
      (sign-data hmac-key (string->utf8 msg)
                 #:algorithm (mac-algorithm hmac-sha256))))

  ; if there is no user (setting up the lottery) prints the sealed lucky-num
  ; otherwise prints the winner with their score
  (define (find-1-winner)
    ; a user's score is the smallest hash string obtained by
    ; num from 1 to lucky-num + ' ' + lotte-name + ' ' + username
    (define (get-score user)
      (define (get-score-iter num score)
      (define (iter num score)
        (if (zero? num)
            score
            (let ((new-score (hash-strings (list (number->string num) lotte-name user))))
              (get-score-iter (1- num) (string-min score new-score)))))
      (get-score-iter lucky-num max-score))
    ; if there is no user (setting up the lottery) prints the sealed lucky-num
    ; otherwise prints the winner with their score
    (define (find-1-winner-iter user score)
            (let ((new-score (hash-strs (list (number->string num) lotte-name user))))
              (iter (1- num) (string-min score new-score)))))
      (iter lucky-num max-score))
    (define (iter user score)
      (let ((new-user (read-line)))
        (if (eof-object? new-user)
            (if (eq? score max-score)
                ; setting up the lottery, so no participants yet
                (begin (display (hash-strings (list lotte-name lucky-num-str))) (newline))
                (begin (format #t "Your sealed lucky number is: ~a\n" (hmac lucky-num-str))
                       (format #t "The key is: ~a (reveal this at the end)\n" hmac-key-str))
                ; print winner and their score
                (begin (display (string-join (list user score))) (newline)))
                (begin (format #t "The winner is: ~a\n" user)
                       (format #t "Their score is: ~a\n" score)))
            (let ((new-score (get-score new-user)))
              (if (string<? new-score score)
                  (find-1-winner-iter new-user new-score)
                  (find-1-winner-iter user score))))))
    (find-1-winner-iter "" max-score))
  (let* ((lotte-name (read-line))
         (lucky-num-str (read-line))
         (lucky-num (string->number lucky-num-str)))
        (if (not (and (integer? lucky-num)
                      (positive? lucky-num)))
            (error "The second line needs to be a positive integer")
            (find-1-winner lotte-name lucky-num-str lucky-num))))
                  (iter new-user new-score)
                  (iter user score))))))
    (iter "" max-score))
    (if (not (and (integer? lucky-num)
                  (positive? lucky-num)))
        (error "The second line needs to be a positive integer")
        (find-1-winner lotte-name lucky-num-str lucky-num)))

;
; command line handling TODO