;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ;;; A Scheme implementation of SipHash, a cryptographically strong ;;; family of hash functions designed by Jean-Philippe Aumasson and ;;; Daniel J. Bernstein. ;;; ;;; http://131002.net/siphash/ ;;; ;;; Copyright (c) 2013, Evan Hanson ;;; BSD-style license. See LICENSE for details. ;;; ;; Shorthand. (define & bitwise-and) (define ⊕ bitwise-xor) (define ❘ bitwise-ior) ;; 64-bit-word-bounded operations. (define-syntax w (syntax-rules () ((_ n) (& n 18446744073709551615)))) (define-syntax « (syntax-rules () ((_ n m) (w (arithmetic-shift n m))))) (define-syntax ↺ (syntax-rules () ((_ n m) (❘ (« n m) (« n (- (- 64 m))))))) ;; Repeat body `b` `n` times. (define-syntax do-times (syntax-rules () ((_ n . b) (do ((i n (- i 1))) ((zero? i)) (begin . b))))) (define-syntax assert (syntax-rules () ((_ t) (or t (error "assertion failed" 't))) ((_ t m) (or t (error (string-append "assertion failed: " m) 't))))) ;; Treats `v` as little-endian. (define bytevector->integer (case-lambda ((v) (bytevector->integer v 0 (bytevector-length v))) ((v e) (bytevector->integer v 0 e)) ((v s e) (do ((s s (+ s 1)) (i 0 (+ i 1)) (a 0 (+ a (« (bytevector-u8-ref v s) (* i 8))))) ((= s e) a))))) (define (->bytevector o) (cond ((bytevector? o) o) ((string? o) (string->utf8 o)) ((error "neither string nor bytevector" o)))) ;; One SipRound. (define-syntax sip-round! (syntax-rules () ((_ v0 v1 v2 v3) (let-syntax ((+ (syntax-rules () ((_ . n) (w (+ . n)))))) (set! v0 (+ v0 v1)) (set! v2 (+ v2 v3)) (set! v1 (↺ v1 13)) (set! v3 (↺ v3 16)) (set! v1 (⊕ v1 v0)) (set! v3 (⊕ v3 v2)) (set! v0 (↺ v0 32)) (set! v2 (+ v2 v1)) (set! v0 (+ v0 v3)) (set! v1 (↺ v1 17)) (set! v3 (↺ v3 21)) (set! v1 (⊕ v1 v2)) (set! v3 (⊕ v3 v0)) (set! v2 (↺ v2 32)))))) (define make-siphash (let ((m0 (string->number "736f6d6570736575" 16)) (m1 (string->number "646f72616e646f6d" 16)) (m2 (string->number "6c7967656e657261" 16)) (m3 (string->number "7465646279746573" 16))) (lambda (c d) (define (siphash-c-d k) (assert (= (bytevector-length k) 16) "key must be 16 bytes") (let* ((k0 (bytevector->integer k 0 8)) (k1 (bytevector->integer k 8 16)) (v0 (⊕ k0 m0)) (v1 (⊕ k1 m1)) (v2 (⊕ k0 m2)) (v3 (⊕ k1 m3))) (let-syntax ((process-message! (syntax-rules () ((_ m) (let ((mi m)) (set! v3 (⊕ v3 mi)) (do-times c (sip-round! v0 v1 v2 v3)) (set! v0 (⊕ v0 mi))))))) (lambda (m) (let ((l (bytevector-length m))) (do ((i 0 (+ i 8))) ((> i (- l 8)) (process-message! (❘ (« (modulo l 256) 56) (bytevector->integer m i l)))) (process-message! (bytevector->integer m i (+ i 8)))) (set! v2 (⊕ v2 255)) (do-times d (sip-round! v0 v1 v2 v3)) (⊕ v0 v1 v2 v3)))))) (case-lambda ((k) (siphash-c-d (->bytevector k))) ((k m) ((siphash-c-d (->bytevector k)) (->bytevector m))))))) (define siphash-2-4 (make-siphash 2 4)) (define siphash-4-8 (make-siphash 4 8))