add openssl/md5

Like `openssl/sha1`, the `openssl/md5` library falls back to the
plain Racket `file/md5` implementation if OpenSSL is not available.
This commit is contained in:
Matthew Flatt 2014-03-02 20:04:04 -07:00
parent 7e57274a64
commit 9e716b0422
4 changed files with 95 additions and 1 deletions

View File

@ -8,6 +8,9 @@
@defmodule[file/md5]
See @racketmodname[openssl/md5] for a faster implementation with a
slightly different interface.
@defproc[(md5 [in (or/c input-port? bytes? string?)]
[hex-encode? boolean? #t]) bytes?]{

View File

@ -2,7 +2,8 @@
@(require scribble/manual
(for-label openssl
racket
openssl/sha1))
openssl/sha1
openssl/md5))
@title{OpenSSL: Secure Communication}
@ -649,3 +650,34 @@ until an end-of-file.}
Converts the given byte string to a string representation, where each
byte in @racket[bstr] is converted to its two-digit hexadecimal
representation in the resulting string.}
@defproc[(hex-string->bytes [str string?]) bytes?]{
The inverse of @racket[bytes->hex-string].}
@; ----------------------------------------------------------------------
@section{MD5 Hashing}
@defmodule[openssl/md5]{The @racketmodname[openssl/md5] library
provides a Racket wrapper for the OpenSSL library's MD5 hashing
functions. If the OpenSSL library cannot be opened, this library logs
a warning and falls back to the implementation in
@racketmodname[file/md5].}
@history[#:added "6.0.0.3"]
@defproc[(md5 [in input-port?]) string?]{
Returns a 32-character string that represents the MD5 hash (in
hexadecimal notation) of the content from @racket[in], consuming all
of the input from @racket[in] until an end-of-file.
The @racket[md5] function composes @racket[bytes->hex-string] with
@racket[md5-bytes].}
@defproc[(md5-bytes [in input-port?]) bytes?]{
Returns a 16-byte byte string that represents the MD5 hash of the
content from @racket[in], consuming all of the input from @racket[in]
until an end-of-file.}

View File

@ -0,0 +1,57 @@
#lang racket/base
(require ffi/unsafe
racket/runtime-path
(for-syntax racket/base)
(prefix-in r: file/md5)
"libcrypto.rkt")
(provide md5
md5-bytes)
(define _SHA_CTX-pointer _pointer)
(define MD5_Init
(and libcrypto
(get-ffi-obj 'MD5_Init libcrypto (_fun _SHA_CTX-pointer -> _int) (lambda () #f))))
(define MD5_Update
(and libcrypto
(get-ffi-obj 'MD5_Update libcrypto (_fun _SHA_CTX-pointer _pointer _long -> _int) (lambda () #f))))
(define MD5_Final
(and libcrypto
(get-ffi-obj 'MD5_Final libcrypto (_fun _pointer _SHA_CTX-pointer -> _int) (lambda () #f))))
(define (md5-bytes in)
(unless (input-port? in) (raise-argument-error 'md5-bytes "input-port?" in))
(if MD5_Init
(let ([ctx (malloc 256)]
[tmp (make-bytes 4096)]
[result (make-bytes 16)])
(MD5_Init ctx)
(let loop ()
(let ([n (read-bytes-avail! tmp in)])
(unless (eof-object? n)
(MD5_Update ctx tmp n)
(loop))))
(MD5_Final result ctx)
result)
(r:md5 in #f)))
(define (md5 in)
(unless (input-port? in) (raise-argument-error 'md5 "input-port?" in))
(bytes->hex-string (md5-bytes in)))
;; copied from `file/sha1` --- should be in a separate module,
;; instead
(define (bytes->hex-string bstr)
(let* ([len (bytes-length bstr)]
[bstr2 (make-bytes (* len 2))]
[digit
(lambda (v)
(if (v . < . 10)
(+ v (char->integer #\0))
(+ v (- (char->integer #\a) 10))))])
(for ([i (in-range len)])
(let ([c (bytes-ref bstr i)])
(bytes-set! bstr2 (* 2 i) (digit (arithmetic-shift c -4)))
(bytes-set! bstr2 (+ (* 2 i) 1) (digit (bitwise-and c #xF)))))
(bytes->string/latin-1 bstr2)))

View File

@ -23,6 +23,7 @@
(get-ffi-obj 'SHA1_Final libcrypto (_fun _pointer _SHA_CTX-pointer -> _int) (lambda () #f))))
(define (sha1-bytes in)
(unless (input-port? in) (raise-argument-error 'sha1-bytes "input-port?" in))
(if SHA1_Init
(let ([ctx (malloc 256)]
[tmp (make-bytes 4096)]
@ -38,4 +39,5 @@
(r:sha1-bytes in)))
(define (sha1 in)
(unless (input-port? in) (raise-argument-error 'sha1 "input-port?" in))
(r:bytes->hex-string (sha1-bytes in)))