From 1b210516b6b91c2eeb26e75d331f4ae099ea62cd Mon Sep 17 00:00:00 2001 From: Matt Birkholz Date: Fri, 24 Feb 2017 08:28:12 -0700 Subject: [PATCH] blowfish: Use bytevectors instead of strings. --- src/blowfish/blowfish-check.scm | 20 ++-- src/blowfish/blowfish.scm | 163 ++++++++++++++++++-------------- 2 files changed, 101 insertions(+), 82 deletions(-) diff --git a/src/blowfish/blowfish-check.scm b/src/blowfish/blowfish-check.scm index e5e4e30b3..f1bae2a10 100644 --- a/src/blowfish/blowfish-check.scm +++ b/src/blowfish/blowfish-check.scm @@ -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 diff --git a/src/blowfish/blowfish.scm b/src/blowfish/blowfish.scm index 718b6d623..8a042a8ce 100644 --- a/src/blowfish/blowfish.scm +++ b/src/blowfish/blowfish.scm @@ -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 -- 2.25.1