blowfish: Use bytevectors instead of strings.
authorMatt Birkholz <matt@birchwood-abbey.net>
Fri, 24 Feb 2017 15:28:12 +0000 (08:28 -0700)
committerMatt Birkholz <matt@birchwood-abbey.net>
Fri, 24 Feb 2017 20:37:45 +0000 (13:37 -0700)
src/blowfish/blowfish-check.scm
src/blowfish/blowfish.scm

index e5e4e30b334d1d4f3232a440afd09b187026b006..f1bae2a103a545581ebb6487777a7cda34eec184 100644 (file)
@@ -26,21 +26,21 @@ USA.
 
 ;;;; Test the BLOWFISH option.
 
-(let ((sample "Some text to encrypt and decrypt."))
-  (call-with-legacy-binary-output-file "test"
+(let ((sample (string->utf8 "Some text to encrypt and decrypt.")))
+  (call-with-binary-output-file "test"
     (lambda (output)
-      (call-with-input-string sample
-       (lambda (input)
-         (blowfish-encrypt-port input output "secret"
-                                (write-blowfish-file-header output)
-                                #t)))))
+      (blowfish-encrypt-port (open-input-bytevector sample)
+                            output
+                            "secret"
+                            (write-blowfish-file-header output)
+                            #t)))
   (let ((read-back
-        (call-with-legacy-binary-input-file "test"
+        (call-with-binary-input-file "test"
           (lambda (input)
-            (call-with-output-string
+            (call-with-output-bytevector
              (lambda (output)
                (blowfish-encrypt-port input output "secret"
                                       (read-blowfish-file-header input)
                                       #f)))))))
-    (if (not (string=? sample read-back))
+    (if (not (bytevector=? sample read-back))
        (error "sample did not decrypt correctly"))))
\ No newline at end of file
index 718b6d623082cce0c9b716805454f9d052feccb5..8a042a8ce4715b4c18bfedbea64c25ead00e9bef 100644 (file)
@@ -35,54 +35,55 @@ USA.
   ;; Generate a Blowfish key from STRING.
   ;; STRING must be 72 bytes or less in length.
   ;; For text-string keys, use MD5 on the text, and pass the digest here.
-  (guarantee-string string 'blowfish-set-key)
-  (let ((length (string-length string)))
-    (if (> length 72)
-       (error:bad-range-argument string
-                                 "a string of no more than 72 characters"
-                                 'blowfish-set-key))
-    (let ((result (make-legacy-string (C-sizeof "BF_KEY"))))
-      (C-call "BF_set_key" result length string)
-      result)))
+  (guarantee string? string 'blowfish-set-key)
+  (let* ((data (string->utf8 string))
+        (len (bytevector-length data)))
+    (if (> len 72)
+       (error:bad-range-argument
+        string "a string encodable in UTF8 with fewer than 72 bytes"
+        'blowfish-set-key))
+    (let ((key (make-bytevector (C-sizeof "BF_KEY"))))
+      (C-call "BF_set_key" key len data)
+      key)))
 
 (define (blowfish-ecb input output key encrypt?)
   ;; Apply Blowfish in Electronic Code Book mode.
-  ;; INPUT is an 8-byte string.
-  ;; OUTPUT is an 8-byte string.
+  ;; INPUT is an 8-byte bytevector.
+  ;; OUTPUT is an 8-byte bytevector.
   ;; KEY is a Blowfish key.
   ;; ENCRYPT? says whether to encrypt (non-#F) or decrypt (#F).
   (guarantee-bfkey key 'BLOWFISH-ECB)
-  (guarantee-8char-arg input 'BLOWFISH-ECB)
-  (guarantee-8char-arg output 'BLOWFISH-ECB)
+  (guarantee-8byte-arg input 'BLOWFISH-ECB)
+  (guarantee-8byte-arg output 'BLOWFISH-ECB)
   (C-call "BF_ecb_encrypt" input output key (bf-de/encrypt encrypt?)))
 
 (define (blowfish-cbc input output key init-vector encrypt?)
   ;; Apply Blowfish in Cipher Block Chaining mode.
-  ;; INPUT is a string whose length is a multiple of 8 bytes.
-  ;; OUTPUT is a string whose length is the same as INPUT.
+  ;; INPUT is a bytevector whose length is a multiple of 8 bytes.
+  ;; OUTPUT is a bytevector whose length is the same as INPUT.
   ;; KEY is a Blowfish key.
-  ;; INIT-VECTOR is an 8-byte string; it is modified after each call.
+  ;; INIT-VECTOR is an 8-byte bytevector; it is modified after each call.
   ;; The value from any call may be passed in to a later call.
   ;; ENCRYPT? says whether to encrypt (non-#F) or decrypt (#F).
   (guarantee-init-vector init-vector 'BLOWFISH-CBC)
   (guarantee-bfkey key 'BLOWFISH-CBC)
-  (guarantee-8Xchar-arg input 'BLOWFISH-CBC)
+  (guarantee-8Xbyte-arg input 'BLOWFISH-CBC)
   (if (or (eq? input output)
-         (not (= (string-length output) (string-length input))))
+         (not (= (bytevector-length output) (bytevector-length input))))
       (error:bad-range-argument output
-                               "a string as long as the input string"
+                               "a bytevector as long as the input bytevector"
                                'BLOWFISH-CBC))
-  (C-call "BF_cbc_encrypt" input output (string-length input)
+  (C-call "BF_cbc_encrypt" input output (bytevector-length input)
          key init-vector (bf-de/encrypt encrypt?)))
 
 (define (blowfish-cfb64 input istart iend output ostart
                        key init-vector num encrypt?)
   ;; Apply Blowfish in Cipher Feed-Back mode.
-  ;; (INPUT,ISTART,IEND) is an arbitrary substring.
-  ;; OUTPUT is a string as large as the input substring.
-  ;; OSTART says where to start writing to the output string.
+  ;; (INPUT,ISTART,IEND) is an arbitrary subbytevector.
+  ;; OUTPUT is a bytevector as large as the input subbytevector.
+  ;; OSTART says where to start writing to the output bytevector.
   ;; KEY is a Blowfish key.
-  ;; INIT-VECTOR is an 8-byte string; it is modified after each call.
+  ;; INIT-VECTOR is an 8-byte bytevector; it is modified after each call.
   ;; The value from any call may be passed in to a later call.
   ;; The initial value must be unique for each message/key pair.
   ;; NUM is a digit from 0 to 7 inclusive; it is the low 3 bits of the
@@ -91,8 +92,9 @@ USA.
   ;; Returned value is the new value of NUM.
   (guarantee-bfkey key 'BLOWFISH-CFB64)
   (guarantee-init-vector init-vector 'BLOWFISH-CFB64)
-  (guarantee-substring input istart iend 'BLOWFISH-CFB64)
-  (guarantee-substring output ostart (+ ostart (- iend istart)) 'BLOWFISH-CFB64)
+  (guarantee-subbytevector input istart iend 'BLOWFISH-CFB64)
+  (guarantee-subbytevector output ostart (+ ostart (- iend istart))
+                          'BLOWFISH-CFB64)
   (guarantee-init-index num 'BLOWFISH-CFB64)
   (let ((ilen (- iend istart)))
     (if (and (eq? input output)
@@ -100,7 +102,7 @@ USA.
             (< istart (+ ostart ilen)))
        (error:bad-range-argument
         ostart
-        "an index of a substring not overlapping the input substring"
+        "an index of a subbytevector not overlapping the input subbytevector"
         'BLOWFISH-CFB64))
     (C-call "do_BF_cfb64_encrypt" input istart output ostart ilen
            key init-vector num (bf-de/encrypt encrypt?))))
@@ -108,11 +110,11 @@ USA.
 (define (blowfish-ofb64 input istart iend output ostart
                        key init-vector num)
   ;; Apply Blowfish in Output Feed-Back mode.
-  ;; (INPUT,ISTART,IEND) is an arbitrary substring.
-  ;; OUTPUT is a string as large as the input substring.
-  ;; OSTART says where to start writing to the output string.
+  ;; (INPUT,ISTART,IEND) is an arbitrary subbytevector.
+  ;; OUTPUT is a bytevector as large as the input subbytevector.
+  ;; OSTART says where to start writing to the output bytevector.
   ;; KEY is a Blowfish key.
-  ;; INIT-VECTOR is an 8-byte string; it is modified after each call.
+  ;; INIT-VECTOR is an 8-byte bytevector; it is modified after each call.
   ;;   The value from any call may be passed in to a later call.
   ;;   The initial value must be unique for each message/key pair.
   ;; NUM is a digit from 0 to 7 inclusive; it is the low 3 bits of the
@@ -120,8 +122,9 @@ USA.
   ;; Returned value is the new value of NUM.
   (guarantee-bfkey key 'BLOWFISH-OFB64)
   (guarantee-init-vector init-vector 'BLOWFISH-OFB64)
-  (guarantee-substring input istart iend 'BLOWFISH-OFB64)
-  (guarantee-substring output ostart (+ ostart (- iend istart)) 'BLOWFISH-OFB64)
+  (guarantee-subbytevector input istart iend 'BLOWFISH-OFB64)
+  (guarantee-subbytevector output ostart (+ ostart (- iend istart))
+                          'BLOWFISH-OFB64)
   (guarantee-init-index num 'BLOWFISH-OFB64)
   (let ((ilen (- iend istart)))
     (if (and (eq? input output)
@@ -129,7 +132,7 @@ USA.
             (< istart (+ ostart ilen)))
        (error:bad-range-argument
         ostart
-        "an index of a substring not overlapping the input substring"
+        "an index of a subbytevector not overlapping the input subbytevector"
         'BLOWFISH-OFB64))
     (C-call "do_BF_ofb64_encrypt" input istart output ostart ilen
            key init-vector num)))
@@ -137,66 +140,76 @@ USA.
 (define (bf-de/encrypt encrypt?)
   (if encrypt? (C-enum "BF_ENCRYPT") (C-enum "BF_DECRYPT")))
 
-(define (guarantee-8char-arg arg operator)
-  (guarantee-string arg operator)
-  (if (not (= 8 (string-length arg)))
+(define (guarantee-8byte-arg arg operator)
+  (guarantee bytevector? arg operator)
+  (if (not (= 8 (bytevector-length arg)))
       (error:bad-range-argument arg
-                               "an 8 character string"
+                               "8 bytes"
                                operator)))
 
-(define (guarantee-8Xchar-arg arg operator)
-  (guarantee-string arg operator)
-  (if (not (= 0 (modulo (string-length arg) 8)))
+(define (guarantee-8Xbyte-arg arg operator)
+  (guarantee bytevector? arg operator)
+  (if (not (= 0 (modulo (bytevector-length arg) 8)))
       (error:bad-range-argument arg
-                               "a multiple of 8 characters string"
+                               "a multiple of 8 bytes"
                                operator)))
 
 (define (guarantee-bfkey object operator)
-  (if (not (and (string? object)
+  (if (not (and (bytevector? object)
                (fix:= (C-sizeof "BF_KEY")
-                      (string-length object))))
+                      (bytevector-length object))))
       (error:bad-range-argument object "a blowfish key" operator)))
 
 (define (guarantee-init-vector object operator)
-  (guarantee-string object operator)
-  (if (not (= 8 (string-length object)))
+  (guarantee bytevector? object operator)
+  (if (not (= 8 (bytevector-length object)))
       (error:bad-range-argument object
                                "a blowfish init vector"
                                operator)))
 
 (define (guarantee-init-index object operator)
-  (guarantee-fixnum object 'operator)
+  (guarantee fixnum? object 'operator)
   (if (not (and (fix:<= 0 object) (fix:< object 8)))
       (error:bad-range-argument object
                                "a blowfish init-vector index"
                                operator)))
 
+(define (guarantee-subbytevector object start end operator)
+  (guarantee bytevector? object operator)
+  (guarantee index-fixnum? start operator)
+  (guarantee index-fixnum? end operator)
+  (if (not (fix:<= start end))
+      (error:bad-range-argument start operator))
+  (if (not (fix:<= end (bytevector-length object)))
+      (error:bad-range-argument end operator)))
+
 (define (blowfish-encrypt-port input output key init-vector encrypt?)
   ;; Assumes that INPUT is in blocking mode.
   (let ((key (blowfish-set-key key))
-       (input-buffer (make-legacy-string 4096))
-       (output-buffer (make-legacy-string 4096)))
+       (input-buffer (make-bytevector 4096))
+       (output-buffer (make-bytevector 4096)))
     (dynamic-wind
      (lambda ()
        unspecific)
      (lambda ()
        (let loop ((m 0))
-        (let ((n (input-port/read-string! input input-buffer)))
-          (if (not (fix:= 0 n))
+        (let ((n (read-bytevector! input-buffer input)))
+          (if (and (not (eof-object? n))
+                   (not (fix:= 0 n)))
               (let ((m
                      (blowfish-cfb64 input-buffer 0 n output-buffer 0
                                      key init-vector m encrypt?)))
-                (write-substring output-buffer 0 n output)
+                (write-bytevector output-buffer output 0 n)
                 (loop m))))))
      (lambda ()
-       (string-fill! input-buffer #\NUL)
-       (string-fill! output-buffer #\NUL)))))
+       (bytevector-fill! input-buffer 0)
+       (bytevector-fill! output-buffer 0)))))
 
 (define (compute-blowfish-init-vector)
   ;; This init vector includes a timestamp with a resolution of
   ;; milliseconds, plus 20 random bits.  This should make it very
   ;; difficult to generate two identical vectors.
-  (let ((iv (make-legacy-string 8)))
+  (let ((iv (make-bytevector 8)))
     (do ((i 0 (fix:+ i 1))
         (t (+ (* (+ (* (get-universal-time) 1000)
                     (remainder (real-time-clock) 1000))
@@ -204,33 +217,39 @@ USA.
               (random #x100000))
            (quotient t #x100)))
        ((fix:= 8 i))
-      (vector-8b-set! iv i (remainder t #x100)))
+      (bytevector-u8-set! iv i (remainder t #x100)))
     iv))
 
 (define (write-blowfish-file-header port)
-  (write-string blowfish-file-header-v2 port)
-  (newline port)
+  (write-bytevector blowfish-file-header-v2 port)
+  (write-u8 (char->integer #\newline) port)
   (let ((init-vector (compute-blowfish-init-vector)))
-    (write-string init-vector port)
+    (write-bytevector init-vector port)
     init-vector))
 
 (define (read-blowfish-file-header port)
-  (let ((line (read-line port)))
-    (cond ((string=? blowfish-file-header-v1 line)
-          (make-legacy-string 8 #\NUL))
-         ((string=? blowfish-file-header-v2 line)
-          (let ((init-vector (make-legacy-string 8)))
-            (if (not (= 8 (read-string! init-vector port)))
-                (error "Short read while getting init-vector:" port))
-            init-vector))
+  (let ((line (read-header port)))
+    (cond ((bytevector=? blowfish-file-header-v1 line)
+          (make-bytevector 8 #\NUL))
+         ((bytevector=? blowfish-file-header-v2 line)
+          (read-bytevector 8 port))
          (else
           (error:bad-range-argument port 'READ-BLOWFISH-FILE-HEADER)))))
 
+(define (read-header port)
+  (let loop ((bytes '()))
+    (let ((byte (read-u8 port)))
+      (if (eof-object? byte)
+         (apply bytevector (reverse! bytes))
+         (if (fix:= byte (char->integer #\newline))
+             (apply bytevector (reverse! bytes))
+             (loop (cons byte bytes)))))))
+
 (define (blowfish-file? pathname)
-  (let ((line (call-with-legacy-binary-input-file pathname read-line)))
+  (let ((line (call-with-binary-input-file pathname read-header)))
     (and (not (eof-object? line))
-        (or (string=? line blowfish-file-header-v1)
-            (string=? line blowfish-file-header-v2)))))
+        (or (bytevector=? line blowfish-file-header-v1)
+            (bytevector=? line blowfish-file-header-v2)))))
 
-(define blowfish-file-header-v1 "Blowfish, 16 rounds")
-(define blowfish-file-header-v2 "Blowfish, 16 rounds, version 2")
\ No newline at end of file
+(define blowfish-file-header-v1 (string->utf8 "Blowfish, 16 rounds"))
+(define blowfish-file-header-v2 (string->utf8 "Blowfish, 16 rounds, version 2"))
\ No newline at end of file