├── COPYING ├── README ├── bcrypt.lisp ├── monkeylib-bcrypt.asd └── packages.lisp /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, Peter Seibel All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above 11 | copyright notice, this list of conditions and the following 12 | disclaimer in the documentation and/or other materials provided 13 | with the distribution. 14 | 15 | * Neither the name of Peter Seibel nor the names of contributors 16 | may be used to endorse or promote products derived from this 17 | software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | Portions of this code are Copyright (C) 2010 Jon Rosebaugh and 32 | licensed under the following terms: 33 | 34 | Permission is hereby granted, free of charge, to any person obtaining 35 | a copy of this software and associated documentation files (the 36 | "Software"), to deal in the Software without restriction, including 37 | without limitation the rights to use, copy, modify, merge, publish, 38 | distribute, sublicense, and/or sell copies of the Software, and to 39 | permit persons to whom the Software is furnished to do so, subject to 40 | the following conditions: 41 | 42 | The above copyright notice and this permission notice shall be 43 | included in all copies or substantial portions of the Software. 44 | 45 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 46 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 47 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 48 | IN NO EVENT SHALL THE JON ROSEBAUGH BE LIABLE FOR ANY CLAIM, DAMAGES 49 | OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 50 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR 51 | THE USE OR OTHER DEALINGS IN THE SOFTWARE. 52 | 53 | Except as contained in this notice, the name of the Jon Rosebaugh 54 | shall not be used in advertising or otherwise to promote the sale, use 55 | or other dealings in this Software without prior written authorization 56 | from the Jon Rosebaugh. 57 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | -*- mode: markup; -*- 2 | 3 | This code is based on the code from this blog posting: 4 | 5 | http://www.letsyouandhimfight.com/2010/07/14/cl-bcrypt-a-first-attempt/ 6 | 7 | I packaged it up with an .asd file, frobbed the API, and added some 8 | comments. 9 | 10 | * Installing 11 | 12 | Since this code is just a thin wrapper around a C bcrypt library 13 | you'll need one of those installed on your system. (Note that the 14 | bcrypt package available on Ubuntu and Debian does not provide this 15 | library—it is a file encryption program that uses the Blowfish 16 | algorithm.) The easiest solution is probably to get the Openwall 17 | bcrypt from http://www.openwall.com/crypt/. I’ve also put a version on 18 | Github at: 19 | 20 | https://github.com/gigamonkey/openwall-bcrypt 21 | 22 | to which I’ve added a ‘library’ target to the makefile to build the 23 | shared library. If you grab my version you can cd into the directory 24 | and ‘make library’. Then place the resulting libbcrypt.so.1.0.4 25 | somewhere where Lisp can find it. (On OS X ~/lib will work or the 26 | cffi:*foreign-library-directories* variable may be useful here.) 27 | 28 | -------------------------------------------------------------------------------- /bcrypt.lisp: -------------------------------------------------------------------------------- 1 | ;;; 2 | ;;; Copyright (c) 2010, Jon Rosebaugh All rights reserved. 3 | ;;; Copyright (c) 2011, Peter Seibel All rights reserved. 4 | ;;; 5 | 6 | (in-package :bcrypt) 7 | 8 | (define-foreign-library libbcrypt 9 | (:unix (:or "libbcrypt.so.1.0.4" "libbcrypt.so.1" "libbcrypt.so")) 10 | (t (:default "libbcrypt"))) 11 | 12 | (use-foreign-library libbcrypt) 13 | 14 | (defcfun ("_crypt_gensalt_blowfish_rn" crypt-gensalt-rn) :pointer 15 | (count :ulong) 16 | (input :pointer) 17 | (input-size :int) 18 | (output :pointer) 19 | (output-size :int)) 20 | 21 | (defcfun ("_crypt_blowfish_rn" crypt-rn) :pointer 22 | (key :pointer) 23 | (settings :pointer) 24 | (output :pointer) 25 | (output-size :int)) 26 | 27 | ;;; Public API 28 | 29 | (defvar *random-bytes-function* nil 30 | "Set to a function taking a length and returning an array of random 31 | bytes to customize how random bytes are generated for the salt. If 32 | this variable is not set, we will first try to get bytes from 33 | /dev/urandom and if that doesn't work, using the Lisp's built in 34 | random number generator.") 35 | 36 | (defparameter *default-cost* 10 37 | "The default value for the COST parameter to HASH.") 38 | 39 | (defun hash (password &optional cost) 40 | "Encode the given plaintext PASSWORD with the given COST (defaults 41 | to 10). Increasing cost by one approximately doubles the amount of 42 | work required to encode the password (and thus to check it.)" 43 | (with-foreign-pointer (salt 16 salt-size) 44 | (fill-with-random-bytes salt salt-size) 45 | (with-foreign-pointer (settings 30 settings-size) 46 | (zero-memory settings settings-size) 47 | (crypt-gensalt-rn (or cost *default-cost*) salt salt-size settings settings-size) 48 | (with-foreign-pointer-as-string ((data data-size) 61 :encoding :ascii) 49 | (zero-memory data data-size) 50 | (with-foreign-string (password-cstring password) 51 | (crypt-rn password-cstring settings data data-size)))))) 52 | 53 | (defun password= (password hash) 54 | "Return true if the given plaintext PASSWORD hashes to HASH, a hash 55 | returned by BCRYPT:HASH. The check extracts the appropriate cost 56 | parameter and salt from HASH." 57 | (let ((rehash 58 | (with-foreign-pointer-as-string ((data data-size) 61 :encoding :ascii) 59 | (zero-memory data data-size) 60 | (with-foreign-strings ((password-cstring password) (encoded-cstring hash)) 61 | (crypt-rn password-cstring encoded-cstring data data-size))))) 62 | 63 | (string= hash rehash))) 64 | 65 | (defun cost (hash) 66 | "Extract the cost parameter used to produce HASH." 67 | (unless (char= #\$ (char hash 0)) 68 | (error "Hash ~a doesn't start with '$'" hash)) 69 | (let* ((start (1+ (position #\$ hash :start 1))) 70 | (end (position #\$ hash :start start))) 71 | (or (parse-integer (subseq hash start end) :junk-allowed t) 72 | (error "No cost found in ~a" hash)))) 73 | 74 | (defun version (hash) 75 | "Extract the algorithm version from HASH." 76 | (unless (char= #\$ (char hash 0)) 77 | (error "Hash ~a doesn't start with '$'" hash)) 78 | (subseq hash 1 (position #\$ hash :start 1))) 79 | 80 | ;;; Utility code 81 | 82 | (defun zero-memory (mem-pointer n) 83 | "Zero out N bytes of memory starting at MEM-POINTER." 84 | (loop repeat n 85 | for p = mem-pointer then (inc-pointer p 1) 86 | do (setf (mem-ref p :int) 0))) 87 | 88 | (defun fill-with-random-bytes (mem-pointer n) 89 | "Fill the N bytes of memory starting at MEM-POINTER with random 90 | bytes obtained via GET-RANDOM-BYTES." 91 | (loop for byte across (get-random-bytes n) 92 | for p = mem-pointer then (inc-pointer p 1) 93 | do (setf (mem-ref p :int) byte))) 94 | 95 | (defun get-random-bytes (n) 96 | "Get a vector of N random bytes using *RANDOM-BYTES-FUNCTION* if it 97 | is set, /dev/urandom if it works, or, if all else fails, Lisp's 98 | built-in random number generator." 99 | (or 100 | (and *random-bytes-function* (funcall *random-bytes-function* n)) 101 | (urandom n) 102 | (lisp-random n))) 103 | 104 | (defun urandom (n) 105 | "Generate N random bytes by reading from /dev/urandom. Returns NIL 106 | if /dev/urandom doesn't exist or an error occurs." 107 | (when (probe-file "/dev/urandom") 108 | (ignore-errors 109 | (with-open-file (r "/dev/urandom" :element-type '(unsigned-byte 8)) 110 | (let ((bytes (make-array n :element-type '(unsigned-byte 8)))) 111 | (read-sequence bytes r) 112 | bytes))))) 113 | 114 | (defun lisp-random (n) 115 | "Generate N random bytes using Lisp's built in random number generator." 116 | (map-into (make-array n :element-type '(unsigned-byte 8)) (lambda () (random #xff)))) -------------------------------------------------------------------------------- /monkeylib-bcrypt.asd: -------------------------------------------------------------------------------- 1 | ;; 2 | ;; Copyright (c) 2011, Peter Seibel All rights reserved. 3 | ;; 4 | 5 | (defsystem monkeylib-bcrypt 6 | :description "Wrapper around bcrypt C library for hashing passwords." 7 | :components 8 | ((:file "packages") 9 | (:file "bcrypt" :depends-on ("packages"))) 10 | :depends-on (:cffi)) 11 | -------------------------------------------------------------------------------- /packages.lisp: -------------------------------------------------------------------------------- 1 | ;;; 2 | ;;; Copyright (c) 2010, Jon Rosebaugh All rights reserved. 3 | ;;; Copyright (c) 2011, Peter Seibel All rights reserved. 4 | ;;; 5 | 6 | (in-package :cl-user) 7 | 8 | (defpackage :bcrypt 9 | (:use :common-lisp :cffi) 10 | (:export :hash 11 | :password= 12 | :cost 13 | :version 14 | *default-cost* 15 | *random-bytes-function*)) 16 | 17 | --------------------------------------------------------------------------------