読者です 読者をやめる 読者になる 読者になる

Gauche で arcfour

arcfour という暗号がある。 実質的には擬似乱数生成器で、生成した数列を対象データと xor する仕組みだ。 乱数の種が鍵となるわけである。 この暗号は非常に単純で、短いコードで実装可能という特徴がある。
今回はこの arcfour を Gauche で実装してみようと試みた。
まずは素直に暗号アルゴリズムを書き下したものがこれだ。

(use gauche.uvector)

(define-syntax swap!
  (syntax-rules ()
    ((_ x y)
     (let1 t x (set! x y) (set! y t)))))

(define-class <arcfour> ()
  ((s :init-form (make-u8vector 256))
   (x :init-value 0)
   (y :init-value 0)))

(define-method arcfour-keyset! ((self <arcfour>) (key <u8vector>))
  (let ((keylen (u8vector-length key))
        (s (~ self 's))
        (j 0))
    (dotimes (i 256) (u8vector-set! s i i))
    (dotimes (i 256 s)
      (set! j (modulo (+ j (~ s i) (~ key (modulo i keylen))) 256))
      (swap! (~ s i) (~ s j)))))

(define-method arcfour-crypt! ((self <arcfour>) (data <u8vector>))
  (let ((datalen (size-of data))
        (s (~ self 's))
        (x (~ self 'x))
        (y (~ self 'y)))
    (do ((i 0 (+ i 1)))
        ((= i datalen) data)
      (set! x (modulo (+ x 1) 256))
      (set! y (modulo (+ y (~ s x)) 256))
      (swap! (~ s x) (~ s y))
      (update! (~ data i)
               (^j (logxor j (~ s (modulo (+ (~ s x) (~ s y)) 256))))))
    (set! (~ self 'x) x)
    (set! (~ self 'y) y)
    data))

ユニフォームベクタが活躍している。 Gaucheバイナリデータも簡単に扱え、このテの処理も楽にできる。
とは言え、このままだと使い勝手が悪い。 そこでポートを被せてみた。 クラスを継承する形で flush メソッドだけ書けばとりあえず使えるものにはなる。

(use gauche.vport)

(define-class <arcfour-output-port> (<buffered-output-port>)
  ((arcfour :init-form (make <arcfour>))))

(define-method initialize ((self <arcfour-output-port>) initargs)
  (let-keywords initargs ((key :key #u8())
                          (port :port (standard-output-port)))
    (next-method self
      `(:flush ,(lambda(vec flag)
                  (arcfour-crypt! (~ self 'arcfour) vec)
                  (write-block vec port)
                  (size-of vec))))
    (arcfour-keyset! (~ self 'arcfour) key)))

のインスタンス生成時に暗号鍵と出力ポートを指定すれば、以後はこのインスタンスに対する出力は暗号化された上で指定のポートへ出力されるわけだ。
例えば出力先を文字列ポートにすれば、暗号化した結果を文字列にすることも出来る。 鍵とデータを文字列で与えると暗号化されたデータを文字列で返す手続きはこうなる。

(define (arcfour-encode-string data key)
  (call-with-output-string
    (lambda(p)
      (let1 ac4port (make <arcfour-output-port>
                      :key (string->u8vector key)
                      :port p)
        (display data ac4port)
        (flush ac4port)))))

実際に使うとこんな感じ。

gosh> (arcfour-encode-string "abc" "key")
"j\x0eW"

arcfour を何に使うというわけでもないのだが、 Gauche のユニフォームベクタとか、オブジェクトシステムとか、文字列ポートとか、カスタマイズ可能なポートとかの仕組みが実に良く出来てて、使い勝手が良いことにすごく感心したというただそれだけのことが言いたかったのだった。
Document ID: 4322397d91d92e617327f7fcdf8fe7b1