Created
July 18, 2016 16:32
-
-
Save lexi-lambda/0c0faf001f44da310cbb76fc846079f9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#lang racket/base | |
(require ffi/unsafe | |
ffi/unsafe/define | |
net/base64 | |
openssl/libcrypto | |
(rename-in racket/contract [-> ->/c]) | |
racket/match | |
racket/random | |
racket/string | |
(only-in sha sha256)) | |
(provide | |
(contract-out | |
[encrypt (bytes? bytes? . ->/c . string?)] | |
[decrypt (bytes? string? . ->/c . bytes?)])) | |
;; --------------------------------------------------------------------------------------------------- | |
(define-ffi-definer define-openssl libcrypto) | |
(define-cpointer-type _EVP_CIPHER) | |
(define-cpointer-type _EVP_CIPHER_CTX) | |
(define-cpointer-type _ENGINE) | |
(define-openssl EVP_CIPHER_CTX_new (_fun -> _EVP_CIPHER_CTX)) | |
(define-openssl EVP_CIPHER_CTX_free (_fun _EVP_CIPHER_CTX -> _void)) | |
(define-openssl EVP_aes_256_cbc (_fun -> _EVP_CIPHER)) | |
(define-openssl EVP_EncryptInit_ex | |
(_fun _EVP_CIPHER_CTX _EVP_CIPHER _ENGINE/null _bytes _bytes -> _int)) | |
(define-openssl EVP_DecryptInit_ex | |
(_fun _EVP_CIPHER_CTX _EVP_CIPHER _ENGINE/null _bytes _bytes -> _int)) | |
(define-openssl EVP_EncryptUpdate | |
(_fun _EVP_CIPHER_CTX | |
; this magic 127 number is a lie for non-AES ciphers; should be EVP_CIPHER_block_size - 1 | |
(out : (_bytes o (+ (bytes-length in) 127))) (outl : (_ptr o _int)) | |
(in : _bytes) (_int = (bytes-length in)) | |
-> (r : _int) | |
-> (values r (subbytes out 0 outl)))) | |
(define-openssl EVP_EncryptFinal_ex | |
(_fun (ctx in) :: | |
(ctx : _EVP_CIPHER_CTX) | |
; this magic number has the same issue as the one in EVP_EncryptUpdate | |
(out : (_bytes o 127)) (outl : (_ptr o _int)) | |
-> (r : _int) | |
-> (values r (bytes-append in (subbytes out 0 outl))))) | |
(define-openssl EVP_DecryptUpdate | |
(_fun _EVP_CIPHER_CTX | |
; this magic number has the same issue as the one in EVP_EncryptUpdate | |
(out : (_bytes o (+ (bytes-length in) 127))) (outl : (_ptr o _int)) | |
(in : _bytes) (_int = (bytes-length in)) | |
-> (r : _int) | |
-> (values r (subbytes out 0 outl)))) | |
(define-openssl EVP_DecryptFinal_ex | |
(_fun (ctx in) :: | |
(ctx : _EVP_CIPHER_CTX) | |
; this magic number has the same issue as the one in EVP_EncryptUpdate | |
(out : (_bytes o 127)) (outl : (_ptr o _int)) | |
-> (r : _int) | |
-> (values r (bytes-append in (subbytes out 0 outl))))) | |
;; --------------------------------------------------------------------------------------------------- | |
(define (encrypt secret plaintext) | |
; get a 256 bit key from the input secret | |
(define secret-digest (sha256 secret)) | |
; generate a 128 bit initialization vector | |
(define initialization-vector (crypto-random-bytes 16)) | |
; initialize for encryption | |
(define ctx (EVP_CIPHER_CTX_new)) | |
(define result (EVP_EncryptInit_ex ctx (EVP_aes_256_cbc) #f secret-digest initialization-vector)) | |
(unless (= 1 result) (error 'encrypt-aes-256-cbc "EVP_DecryptInit_ex returned ~v" result)) | |
; perform the actual encryption | |
(match-define-values [result* ciphertext] (EVP_EncryptUpdate ctx plaintext)) | |
(unless (= 1 result*) (error 'encrypt-aes-256-cbc "EVP_EncryptUpdate returned ~v" result*)) | |
(match-define-values [result** ciphertext*] (EVP_EncryptFinal_ex ctx ciphertext)) | |
(unless (= 1 result**) (error 'encrypt-aes-256-cbc "EVP_EncryptFinal_ex returned ~v" result**)) | |
; free the cipher state | |
(EVP_CIPHER_CTX_free ctx) | |
; return the base64-encoded ciphertext and initialization vector | |
(bytes->string/utf-8 | |
(bytes-append (base64-encode ciphertext* #"") #"--" | |
(base64-encode initialization-vector #"")))) | |
(define (decrypt secret ciphertext+initialization-vector) | |
; parse the ciphertext and initialization vector out of the input | |
(match-define (list ciphertext initialization-vector) | |
(map (compose1 base64-decode string->bytes/utf-8) | |
(string-split ciphertext+initialization-vector "--"))) | |
; get a 256 bit key from the input secret | |
(define secret-digest (sha256 secret)) | |
; intialize for decryption | |
(define ctx (EVP_CIPHER_CTX_new)) | |
(define result (EVP_DecryptInit_ex ctx (EVP_aes_256_cbc) #f secret-digest initialization-vector)) | |
(unless (= 1 result) (error 'decrypt-aes-256-cbc "EVP_DecryptInit_ex returned ~v" result)) | |
; perform the actual decryption | |
(match-define-values [result* plaintext] (EVP_DecryptUpdate ctx ciphertext)) | |
(unless (= 1 result*) (error 'decrypt-aes-256-cbc "EVP_DecryptUpdate returned ~v" result*)) | |
(match-define-values [result** plaintext*] (EVP_DecryptFinal_ex ctx plaintext)) | |
(unless (= 1 result**) (error 'decrypt-aes-256-cbc "EVP_DecryptFinal_ex returned ~v" result**)) | |
; free the cipher state | |
(EVP_CIPHER_CTX_free ctx) | |
; return the decrypted data | |
plaintext*) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment