hash.scm 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. ;;; guile-gcrypt --- crypto tooling for guile
  2. ;;; Copyright © 2012, 2013, 2014, 2015, 2016, 2019 Ludovic Courtès <ludo@gnu.org>
  3. ;;; Copyright © 2019 Mathieu Othacehe <m.othacehe@gmail.com>
  4. ;;;
  5. ;;; This file is part of guile-gcrypt.
  6. ;;;
  7. ;;; guile-gcrypt is free software; you can redistribute it and/or modify it
  8. ;;; under the terms of the GNU General Public License as published by
  9. ;;; the Free Software Foundation; either version 3 of the License, or
  10. ;;; (at your option) any later version.
  11. ;;;
  12. ;;; guile-gcrypt is distributed in the hope that it will be useful, but
  13. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. ;;; General Public License for more details.
  16. ;;;
  17. ;;; You should have received a copy of the GNU General Public License
  18. ;;; along with guile-gcrypt. If not, see <http://www.gnu.org/licenses/>.
  19. (define-module (gcrypt hash)
  20. #:use-module (gcrypt common)
  21. #:use-module (gcrypt utils)
  22. #:use-module (rnrs bytevectors)
  23. #:use-module (ice-9 binary-ports)
  24. #:use-module (system foreign)
  25. #:use-module (srfi srfi-11)
  26. #:use-module (srfi srfi-26)
  27. #:export (sha1
  28. sha256
  29. open-sha256-port
  30. port-sha256
  31. file-sha256
  32. open-sha256-input-port))
  33. ;;; Commentary:
  34. ;;;
  35. ;;; Cryptographic hashes.
  36. ;;;
  37. ;;; Code:
  38. ;;;
  39. ;;; Hash.
  40. ;;;
  41. (define-syntax GCRY_MD_SHA256
  42. ;; Value as of Libgcrypt 1.5.2.
  43. (identifier-syntax 8))
  44. (define-syntax GCRY_MD_SHA1
  45. (identifier-syntax 2))
  46. (define bytevector-hash
  47. (let ((proc (libgcrypt->procedure void
  48. "gcry_md_hash_buffer"
  49. `(,int * * ,size_t))))
  50. (lambda (bv type size)
  51. "Return the hash TYPE, of SIZE bytes, of BV as a bytevector."
  52. (let ((digest (make-bytevector size)))
  53. (proc type (bytevector->pointer digest)
  54. (bytevector->pointer bv) (bytevector-length bv))
  55. digest))))
  56. (define sha1
  57. (cut bytevector-hash <> GCRY_MD_SHA1 20))
  58. (define sha256
  59. (cut bytevector-hash <> GCRY_MD_SHA256 (/ 256 8)))
  60. (define open-sha256-md
  61. (let ((proc (libgcrypt->procedure int
  62. "gcry_md_open"
  63. `(* ,int ,unsigned-int))))
  64. (lambda ()
  65. (let* ((md (bytevector->pointer (make-bytevector (sizeof '*))))
  66. (err (proc md GCRY_MD_SHA256 0)))
  67. (if (zero? err)
  68. (dereference-pointer md)
  69. (throw 'gcrypt-error err))))))
  70. (define md-write
  71. (libgcrypt->procedure void "gcry_md_write" `(* * ,size_t)))
  72. (define md-read
  73. (libgcrypt->procedure '* "gcry_md_read" `(* ,int)))
  74. (define md-close
  75. (libgcrypt->procedure void "gcry_md_close" '(*)))
  76. (define (open-sha256-port)
  77. "Return two values: an output port, and a thunk. When the thunk is called,
  78. it returns the SHA256 hash (a bytevector) of all the data written to the
  79. output port."
  80. (define sha256-md
  81. (open-sha256-md))
  82. (define digest #f)
  83. (define position 0)
  84. (define (finalize!)
  85. (let ((ptr (md-read sha256-md 0)))
  86. (set! digest (bytevector-copy (pointer->bytevector ptr 32)))
  87. (md-close sha256-md)))
  88. (define (write! bv offset len)
  89. (if (zero? len)
  90. (begin
  91. (finalize!)
  92. 0)
  93. (let ((ptr (bytevector->pointer bv offset)))
  94. (md-write sha256-md ptr len)
  95. (set! position (+ position len))
  96. len)))
  97. (define (get-position)
  98. position)
  99. (define (close)
  100. (unless digest
  101. (finalize!)))
  102. (values (make-custom-binary-output-port "sha256"
  103. write! get-position #f
  104. close)
  105. (lambda ()
  106. (unless digest
  107. (finalize!))
  108. digest)))
  109. (define (port-sha256 port)
  110. "Return the SHA256 hash (a bytevector) of all the data drained from PORT."
  111. (let-values (((out get)
  112. (open-sha256-port)))
  113. (dump-port port out)
  114. (close-port out)
  115. (get)))
  116. (define (file-sha256 file)
  117. "Return the SHA256 hash (a bytevector) of FILE."
  118. (call-with-input-file file port-sha256))
  119. (define (open-sha256-input-port port)
  120. "Return an input port that wraps PORT and a thunk to get the hash of all the
  121. data read from PORT. The thunk always returns the same value."
  122. (define md
  123. (open-sha256-md))
  124. (define (read! bv start count)
  125. (let ((n (get-bytevector-n! port bv start count)))
  126. (if (eof-object? n)
  127. 0
  128. (begin
  129. (unless digest
  130. (let ((ptr (bytevector->pointer bv start)))
  131. (md-write md ptr n)))
  132. n))))
  133. (define digest #f)
  134. (define (finalize!)
  135. (let ((ptr (md-read md 0)))
  136. (set! digest (bytevector-copy (pointer->bytevector ptr 32)))
  137. (md-close md)))
  138. (define (get-hash)
  139. (unless digest
  140. (finalize!))
  141. digest)
  142. (define (unbuffered port)
  143. ;; Guile <= 2.0.9 does not support 'setvbuf' on custom binary input ports.
  144. ;; If you get a wrong-type-arg error here, the fix is to upgrade Guile. :-)
  145. (setvbuf port
  146. (cond-expand ((and guile-2 (not guile-2.2)) _IONBF)
  147. (else 'none)))
  148. port)
  149. (values (unbuffered (make-custom-binary-input-port "sha256" read! #f #f #f))
  150. get-hash))
  151. ;;; hash.scm ends here