├── .travis.yml ├── README.md ├── base64.lisp ├── errors.lisp ├── jose.asd ├── jws.lisp ├── jwt.lisp ├── main.lisp └── tests ├── jws.lisp ├── jwt.lisp └── keys ├── rsa-3920bits-priv.pem ├── rsa-3920bits-pub.pem ├── rsa-3925bits-priv.pem ├── rsa-3925bits-pub.pem ├── rsa-priv.pem └── rsa-pub.pem /.travis.yml: -------------------------------------------------------------------------------- 1 | language: common-lisp 2 | sudo: false 3 | 4 | env: 5 | global: 6 | - PATH=~/.roswell/bin:$PATH 7 | - ROSWELL_INSTALL_DIR=$HOME/.roswell 8 | - COVERAGE_EXCLUDE=tests 9 | matrix: 10 | - LISP=sbcl-bin COVERALLS=true 11 | - LISP=ccl-bin 12 | 13 | install: 14 | # Roswell 15 | - curl -L https://raw.githubusercontent.com/roswell/roswell/release/scripts/install-for-ci.sh | sh 16 | # ASN.1 & PEM 17 | - ros install fukamachi/asn1 18 | - ros install fukamachi/pem 19 | # Rove 20 | - ros install fukamachi/rove 21 | 22 | script: 23 | - rove jose.asd 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jose 2 | 3 | [![Quicklisp dist](http://quickdocs.org/badge/jose.svg)](http://quickdocs.org/jose/) 4 | [![Build Status](https://travis-ci.org/fukamachi/jose.svg?branch=master)](https://travis-ci.org/fukamachi/jose) 5 | [![Coverage Status](https://coveralls.io/repos/fukamachi/jose/badge.svg?branch=master)](https://coveralls.io/r/fukamachi/jose) 6 | 7 | A JSON Object Signing and Encryption (JOSE) implementation for Common Lisp. 8 | 9 | ## Usage 10 | 11 | ### HMAC 12 | 13 | ```common-lisp 14 | (defvar *key* (ironclad:ascii-string-to-byte-array "my$ecret")) 15 | 16 | (defvar *token* 17 | (jose:encode :hs256 *key* '(("hello" . "world")))) 18 | 19 | *token* 20 | ;=> "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIn0.Vr0VKL9WHX9lUPWzrE0DX4fEvl0_CgnKlzI2mWiro8E" 21 | 22 | (jose:decode :hs256 *key* *token*) 23 | ;=> (("hello" . "world")) 24 | ; (("alg" . "HS256") ("typ" . "JWT")) 25 | 26 | ;; Decoding without signature verification. 27 | (jose:inspect-token *token*) 28 | ;=> (("hello" . "world")) 29 | ; (("alg" . "HS256") ("typ" . "JWT")) 30 | ; #(142 123 175 222 84 4 134 19 70 182 50 209 29 113 176 40 82 42 241 90 230 91 31 | ; 176 235 254 57 221 93 97 220 6 101) 32 | ``` 33 | 34 | ### RSA 35 | 36 | For RSA algorithm, the key must be an instance of Ironclad public/private key, that can be generated with `ironclad:generate-key-pair`. 37 | 38 | To read from OpenSSH key files, use [cl-ssh-keys](https://github.com/dnaeon/cl-ssh-keys). To parse ASN.1 keys, [asn1](https://github.com/fukamachi/asn1) library will help. 39 | 40 | ```common-lisp 41 | ;; Generate a new key pairs with Ironclad 42 | (defvar *private-key* 43 | (ironclad:generate-key-pair :rsa :num-bits 2048)) 44 | 45 | ;; Or, read a private key file generated by OpenSSH 46 | (defvar *private-key* 47 | (ssh-keys:parse-private-key-file #P"~/.ssh/id_rsa")) 48 | 49 | (defvar *token* 50 | (jose:encode :rs256 *private-key* '(("hello" . "world")))) 51 | ``` 52 | 53 | ## Supported Algorithms 54 | 55 | * HS256 56 | * HS384 57 | * HS512 58 | * RS256 59 | * RS384 60 | * RS512 61 | * PS256 62 | * PS384 63 | * PS512 64 | * none 65 | 66 | ## See Also 67 | 68 | * [JOSE Working Group](https://datatracker.ietf.org/wg/jose/about/) 69 | 70 | ## Author 71 | 72 | * Eitaro Fukamachi (e.arrows@gmail.com) 73 | 74 | ## Copyright 75 | 76 | Copyright (c) 2017 Eitaro Fukamachi (e.arrows@gmail.com) 77 | 78 | ## License 79 | 80 | Licensed under the BSD 2-Clause License. 81 | -------------------------------------------------------------------------------- /base64.lisp: -------------------------------------------------------------------------------- 1 | (defpackage #:jose/base64 2 | (:use #:cl) 3 | (:import-from #:cl-base64 4 | #:integer-to-base64-string 5 | #:string-to-base64-string 6 | #:usb8-array-to-base64-string 7 | #:base64-string-to-integer 8 | #:base64-string-to-usb8-array 9 | #:base64-string-to-string) 10 | (:import-from #:trivial-utf-8 11 | #:string-to-utf-8-bytes 12 | #:utf-8-bytes-to-string) 13 | (:export #:base64url-encode 14 | #:base64url-decode)) 15 | (in-package #:jose/base64) 16 | 17 | (defun add-padding (input) 18 | (let ((rem (mod (length input) 4))) 19 | (if (< 0 rem) 20 | (let ((res (make-array (+ (length input) (- 4 rem)) 21 | :element-type 'character 22 | :initial-element base64::*uri-pad-char*))) 23 | (replace res input) 24 | res) 25 | input))) 26 | 27 | (deftype octets (&optional (len '*)) `(simple-array (unsigned-byte 8) (,len))) 28 | 29 | (defun base64url-encode (input) 30 | (string-right-trim 31 | (list base64::*uri-pad-char*) 32 | (etypecase input 33 | (integer 34 | (integer-to-base64-string input :uri t)) 35 | (string 36 | (usb8-array-to-base64-string 37 | (string-to-utf-8-bytes input) 38 | :uri t)) 39 | (octets 40 | (usb8-array-to-base64-string input :uri t))))) 41 | 42 | (defun base64url-decode (input &key (as :octets)) 43 | (check-type input string) 44 | (ecase as 45 | (:octets (base64-string-to-usb8-array (add-padding input) :uri t)) 46 | (:string (utf-8-bytes-to-string (base64-string-to-usb8-array (add-padding input) :uri t))) 47 | (:integer (base64-string-to-integer (add-padding input) :uri t)))) 48 | -------------------------------------------------------------------------------- /errors.lisp: -------------------------------------------------------------------------------- 1 | (defpackage #:jose/errors 2 | (:use #:cl) 3 | (:export #:jose-error 4 | #:jws-error 5 | #:jwt-error 6 | #:jws-verification-error 7 | #:jws-invalid-format 8 | #:jwt-claims-error 9 | #:jwt-claims-not-yet-valid 10 | #:jwt-claims-expired)) 11 | (in-package #:jose/errors) 12 | 13 | (define-condition jose-error (error) ()) 14 | 15 | (define-condition jws-error (jose-error) ()) 16 | (define-condition jwt-error (jose-error) ()) 17 | 18 | (define-condition jws-verification-error (jws-error) 19 | ((token :initarg :token))) 20 | (define-condition jws-invalid-format (jws-error) 21 | ((token :initarg :token)) 22 | (:report (lambda (condition stream) 23 | (format stream "Token is invalid format.~% token = ~S" 24 | (slot-value condition 'token))))) 25 | 26 | (define-condition jwt-claims-error (jwt-error) 27 | ((key :initarg :key) 28 | (value :initarg :value))) 29 | (define-condition jwt-claims-not-yet-valid (jwt-claims-error) ()) 30 | (define-condition jwt-claims-expired (jwt-claims-error) ()) 31 | -------------------------------------------------------------------------------- /jose.asd: -------------------------------------------------------------------------------- 1 | (defsystem "jose" 2 | :class :package-inferred-system 3 | :version "0.1.0" 4 | :author "Eitaro Fukamachi" 5 | :license "BSD 2-Clause" 6 | :description "JSON Object Signing and Encryption (JOSE) implementation" 7 | :depends-on ("jose/main") 8 | :in-order-to ((test-op (test-op "jose/tests")))) 9 | 10 | (defsystem "jose/tests" 11 | :class :package-inferred-system 12 | :depends-on ("rove" 13 | "jose/tests/jws" 14 | "jose/tests/jwt") 15 | :perform (test-op (o c) (symbol-call :rove '#:run c))) 16 | -------------------------------------------------------------------------------- /jws.lisp: -------------------------------------------------------------------------------- 1 | (defpackage #:jose/jws 2 | (:use #:cl 3 | #:jose/base64 4 | #:jose/errors) 5 | (:import-from #:ironclad) 6 | (:import-from #:cl-json) 7 | (:import-from #:split-sequence 8 | #:split-sequence) 9 | (:import-from #:assoc-utils 10 | #:aget) 11 | (:export #:sign 12 | #:verify 13 | #:decode-token)) 14 | (in-package #:jose/jws) 15 | 16 | (defun hmac-sign-message (digest-spec secret-key message &key (start 0) (end (length message))) 17 | (let ((hmac (ironclad:make-hmac secret-key digest-spec))) 18 | (ironclad:update-hmac hmac message :start start :end end) 19 | (ironclad:hmac-digest hmac))) 20 | 21 | (defun digest-with-pkcs1-padding (digest-spec message &key (start 0) (end (length message)) key-length) 22 | (let ((digest (ironclad:digest-sequence digest-spec message :start start :end end)) 23 | (asn1-digest-info 24 | (case digest-spec 25 | (:sha256 #(#x30 #x31 #x30 #x0d #x06 #x09 #x60 #x86 #x48 26 | #x01 #x65 #x03 #x04 #x02 #x01 #x05 #x00 #x04 #x20)) 27 | (:sha384 #(#x30 #x41 #x30 #x0d #x06 #x09 #x60 #x86 #x48 28 | #x01 #x65 #x03 #x04 #x02 #x02 #x05 #x00 #x04 #x30)) 29 | (:sha512 #(#x30 #x51 #x30 #x0d #x06 #x09 #x60 #x86 #x48 30 | #x01 #x65 #x03 #x04 #x02 #x03 #x05 #x00 #x04 #x40))))) 31 | (concatenate '(SIMPLE-ARRAY (UNSIGNED-BYTE 8) (*)) 32 | #(0 1) 33 | (make-array (max 0 (- key-length 3 (length asn1-digest-info) (length digest))) 34 | :initial-element #xff :element-type '(unsigned-byte 8)) 35 | #(0) 36 | asn1-digest-info 37 | digest))) 38 | 39 | (defun rsa-sign-message (digest-spec private-key message &key (start 0) (end (length message)) pss) 40 | (if pss 41 | (ironclad:sign-message private-key 42 | message 43 | :start start :end end 44 | :pss digest-spec) 45 | (let ((key-length (length 46 | (ironclad:integer-to-octets 47 | (getf (ironclad:destructure-private-key private-key) :n))))) 48 | (ironclad:sign-message private-key 49 | (digest-with-pkcs1-padding digest-spec message 50 | :start start :end end :key-length key-length))))) 51 | 52 | (defun hmac-verify-signature (digest-spec verification-key message signature 53 | &key (start 0) (end (length message))) 54 | (equalp (hmac-sign-message digest-spec verification-key message 55 | :start start :end end) 56 | signature)) 57 | 58 | (defun rsa-verify-signature (digest-spec public-key message signature 59 | &key (start 0) (end (length message)) pss) 60 | (handler-case 61 | (if pss 62 | (ironclad:verify-signature public-key 63 | message 64 | signature 65 | :start start :end end 66 | :pss digest-spec) 67 | (let ((key-length (length 68 | (ironclad:integer-to-octets 69 | (getf (ironclad:destructure-public-key public-key) :n))))) 70 | (ironclad:verify-signature public-key 71 | (digest-with-pkcs1-padding digest-spec message 72 | :start start :end end :key-length key-length) 73 | signature))) 74 | (error (e) 75 | (warn "~A" e) 76 | nil))) 77 | 78 | (defun encode-headers (algorithm additional-headers) 79 | (base64url-encode 80 | (json:encode-json-alist-to-string 81 | `(,@additional-headers 82 | ("alg" . ,(if (eq algorithm :none) 83 | "none" 84 | (symbol-name algorithm))) 85 | ("typ" . "JWT"))))) 86 | 87 | (defun get-signature (algorithm key message &key (start 0) (end (length message))) 88 | (let ((message (ironclad:ascii-string-to-byte-array message :start start :end end))) 89 | (ecase algorithm 90 | (:hs256 91 | (hmac-sign-message :sha256 key message :start start :end end)) 92 | (:hs384 93 | (hmac-sign-message :sha384 key message :start start :end end)) 94 | (:hs512 95 | (hmac-sign-message :sha512 key message :start start :end end)) 96 | (:rs256 97 | (rsa-sign-message :sha256 key message :start start :end end)) 98 | (:rs384 99 | (rsa-sign-message :sha384 key message :start start :end end)) 100 | (:rs512 101 | (rsa-sign-message :sha512 key message :start start :end end)) 102 | (:ps256 103 | (rsa-sign-message :sha256 key message :start start :end end :pss t)) 104 | (:ps384 105 | (rsa-sign-message :sha384 key message :start start :end end :pss t)) 106 | (:ps512 107 | (rsa-sign-message :sha512 key message :start start :end end :pss t)) 108 | (:none "")))) 109 | 110 | (defun sign (algorithm key payload &key headers) 111 | (let* ((encoded-headers (encode-headers algorithm headers)) 112 | (encoded-payload (base64url-encode payload)) 113 | (message (format nil "~A.~A" encoded-headers encoded-payload))) 114 | (format nil "~A.~A" 115 | message 116 | (base64url-encode (get-signature algorithm key message))))) 117 | 118 | (defun %verify-message (algorithm key message signature &key (start 0) (end (length message))) 119 | (ecase algorithm 120 | (:hs256 121 | (hmac-verify-signature :sha256 key message signature :start start :end end)) 122 | (:hs384 123 | (hmac-verify-signature :sha384 key message signature :start start :end end)) 124 | (:hs512 125 | (hmac-verify-signature :sha512 key message signature :start start :end end)) 126 | (:rs256 127 | (rsa-verify-signature :sha256 key message signature :start start :end end)) 128 | (:rs384 129 | (rsa-verify-signature :sha384 key message signature :start start :end end)) 130 | (:rs512 131 | (rsa-verify-signature :sha512 key message signature :start start :end end)) 132 | (:ps256 133 | (rsa-verify-signature :sha256 key message signature :start start :end end :pss t)) 134 | (:ps384 135 | (rsa-verify-signature :sha384 key message signature :start start :end end :pss t)) 136 | (:ps512 137 | (rsa-verify-signature :sha512 key message signature :start start :end end :pss t)) 138 | (:none (zerop (length signature))))) 139 | 140 | (defun check-alg (headers algorithm) 141 | (equal (aget headers "alg") 142 | (if (eq algorithm :none) 143 | "none" 144 | (symbol-name algorithm)))) 145 | 146 | (defun decode-token (token) 147 | (destructuring-bind (&optional headers payload signature &rest rest) 148 | (split-sequence #\. token) 149 | (unless (and headers 150 | payload 151 | signature 152 | (null rest)) 153 | (error 'jws-invalid-format :token token)) 154 | (macrolet ((safety (&body body) 155 | `(handler-case (progn ,@body) 156 | (error () (error 'jws-invalid-format :token token))))) 157 | (let ((headers (safety (let ((json:*json-identifier-name-to-lisp* #'identity) 158 | (json:*identifier-name-to-key* #'identity)) 159 | (json:decode-json-from-string (base64url-decode headers :as :string))))) 160 | (payload (safety (base64url-decode payload))) 161 | (signature (safety (base64url-decode signature)))) 162 | (values headers 163 | payload 164 | signature))))) 165 | 166 | (defun verify (algorithm key token) 167 | (multiple-value-bind (headers payload signature) 168 | (decode-token token) 169 | (let* ((message-end (position #\. token :from-end t)) 170 | (message 171 | (ironclad:ascii-string-to-byte-array token 172 | :start 0 :end message-end))) 173 | (unless (and (%verify-message algorithm key message signature 174 | :start 0 :end message-end) 175 | (check-alg headers algorithm)) 176 | (cerror "Skip signature verification" 177 | 'jws-verification-error :token token)) 178 | 179 | (values payload headers)))) 180 | -------------------------------------------------------------------------------- /jwt.lisp: -------------------------------------------------------------------------------- 1 | (defpackage #:jose/jwt 2 | (:use #:cl 3 | #:jose/errors) 4 | (:import-from #:jose/jws) 5 | (:import-from #:cl-json) 6 | (:import-from #:trivial-utf-8 7 | #:utf-8-bytes-to-string) 8 | (:import-from #:alexandria 9 | #:ensure-list) 10 | (:import-from #:assoc-utils 11 | #:aget) 12 | (:export #:encode 13 | #:inspect-token 14 | #:decode)) 15 | (in-package #:jose/jwt) 16 | 17 | (defun encode (algorithm key claims &key headers) 18 | (jose/jws:sign algorithm key (json:encode-json-alist-to-string claims) 19 | :headers headers)) 20 | 21 | (defun now () 22 | (- (get-universal-time) 2208988800)) 23 | 24 | (defun check-iat (claims) 25 | (let ((iat (assoc "iat" claims :test #'string=))) 26 | (when iat 27 | (unless (integerp (cdr iat)) 28 | (error 'jwt-claims-error :key "iat" :value (cdr iat)))))) 29 | 30 | (defun check-nbf (claims) 31 | (let ((nbf (assoc "nbf" claims :test #'string=))) 32 | (when nbf 33 | (unless (integerp (cdr nbf)) 34 | (error 'jwt-claims-error :key "nbf" :value (cdr nbf))) 35 | (when (< (now) (cdr nbf)) 36 | (cerror "Ignore nbf" 'jwt-claims-not-yet-valid))) 37 | t)) 38 | 39 | (defun check-exp (claims) 40 | (let ((exp (assoc "exp" claims :test #'string=))) 41 | (when exp 42 | (unless (integerp (cdr exp)) 43 | (error 'jwt-claims-error :key "exp" :value (cdr exp))) 44 | (when (< (cdr exp) (now)) 45 | (cerror "Ignore exp" 'jwt-claims-expired))) 46 | t)) 47 | 48 | (defun check-iss (claims issuer) 49 | (let ((issuers (ensure-list issuer))) 50 | (when issuers 51 | (unless (find (aget claims "iss") issuers :test #'equal) 52 | (error 'jwt-claims-error :key "iss" :value (aget claims "iss")))) 53 | t)) 54 | 55 | (defun check-aud (claims audience) 56 | (let ((audiences (ensure-list audience))) 57 | (when audiences 58 | (unless (find (aget claims "aud") audiences :test #'equal) 59 | (error 'jwt-claims-error :key "aud" :value (aget claims "aud")))) 60 | t)) 61 | 62 | (defun check-sub (claims subject) 63 | (let ((subjects (ensure-list subject))) 64 | (when subjects 65 | (unless (find (aget claims "sub") subjects :test #'equal) 66 | (error 'jwt-claims-error :key "sub" :value (aget claims "sub")))) 67 | t)) 68 | 69 | (defun check-jti (claims) 70 | (let ((jti (assoc "jti" claims :test #'string=))) 71 | (when jti 72 | (unless (stringp (cdr jti)) 73 | (error 'jwt-claims-error :key "jti" :value (cdr jti)))) 74 | t)) 75 | 76 | (defun check-claims (claims &key issuer audience subject) 77 | (check-iat claims) 78 | (check-nbf claims) 79 | (check-exp claims) 80 | (check-iss claims issuer) 81 | (check-aud claims audience) 82 | (check-sub claims subject) 83 | (check-jti claims)) 84 | 85 | (defun inspect-token (token) 86 | "Decodes the TOKEN without signature verification." 87 | (multiple-value-bind (headers payload signature) 88 | (jose/jws:decode-token token) 89 | (let* ((payload-string (utf-8-bytes-to-string payload)) 90 | (claims (handler-case 91 | (let ((json:*json-identifier-name-to-lisp* #'identity) 92 | (json:*identifier-name-to-key* #'identity)) 93 | (json:decode-json-from-string payload-string)) 94 | (error () 95 | (error 'jws-invalid-format 96 | :token token))))) 97 | (values claims headers signature)))) 98 | 99 | (defun decode (algorithm key token 100 | &key 101 | issuer 102 | audience 103 | subject) 104 | (multiple-value-bind (payload headers) 105 | (jose/jws:verify algorithm key token) 106 | (let* ((payload-string (utf-8-bytes-to-string payload)) 107 | (claims (nreverse 108 | (handler-case 109 | (let ((json:*json-identifier-name-to-lisp* #'identity) 110 | (json:*identifier-name-to-key* #'identity)) 111 | (json:decode-json-from-string payload-string)) 112 | (error () 113 | (error 'jws-invalid-format 114 | :token token)))))) 115 | (check-claims claims 116 | :issuer issuer 117 | :audience audience 118 | :subject subject) 119 | (values claims headers)))) 120 | -------------------------------------------------------------------------------- /main.lisp: -------------------------------------------------------------------------------- 1 | (uiop:define-package #:jose 2 | (:nicknames #:jose/main) 3 | (:use #:cl) 4 | (:use-reexport #:jose/jwt 5 | #:jose/jws 6 | #:jose/errors)) 7 | -------------------------------------------------------------------------------- /tests/jws.lisp: -------------------------------------------------------------------------------- 1 | (defpackage #:jose/tests/jws 2 | (:use #:cl 3 | #:rove 4 | #:pem 5 | #:jose/jws)) 6 | (in-package #:jose/tests/jws) 7 | 8 | (defvar *secret* 9 | (map '(simple-array (unsigned-byte 8) (*)) 10 | #'char-code "secret")) 11 | 12 | (deftest test-verify-token 13 | (let ((token 14 | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoiYiJ9.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8")) 15 | (multiple-value-bind (payload headers) 16 | (jose/jws:verify :hs256 *secret* token) 17 | (ok (vectorp payload)) 18 | (ok (consp headers))))) 19 | 20 | (deftest test-not-enough-segments 21 | (let ((token 22 | "eyJhIjoiYiJ9.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8")) 23 | (ok (signals 24 | (jose/jws:verify :hs256 *secret* token) 25 | 'jose/errors:jws-invalid-format)))) 26 | 27 | (deftest test-header-invalid-padding 28 | (let ((token 29 | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9A.eyJhIjoiYiJ9.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8")) 30 | (ok (signals 31 | (jose/jws:verify :hs256 *secret* token) 32 | 'jose/errors:jws-invalid-format)))) 33 | 34 | (deftest test-header-not-json 35 | (let ((token 36 | "dGVzdA.eyJhIjoiYiJ9.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8")) 37 | (ok (signals 38 | (jose/jws:verify :hs256 *secret* token) 39 | 'jose/errors:jws-invalid-format)))) 40 | 41 | (deftest test-claims-invalid-padding 42 | (let ((token 43 | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.AeyJhIjoiYiJ9.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8")) 44 | (ok (signals 45 | (jose/jws:verify :hs256 *secret* token) 46 | 'jose/errors:jws-invalid-format)))) 47 | 48 | (deftest test-claims-not-json 49 | (let ((token 50 | "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.dGVzdA.3MkJVAT-b30XkB4EwrYeqShkwa_GrHcJ1fp8xD1MoYk")) 51 | (ok (jose/jws:verify :hs256 *secret* token)))) 52 | 53 | (defvar *public-key* 54 | (asdf:system-relative-pathname :jose #P"tests/keys/rsa-pub.pem")) 55 | (defvar *private-key* 56 | (asdf:system-relative-pathname :jose #P"tests/keys/rsa-priv.pem")) 57 | 58 | (deftest test-rsa 59 | (let ((token 60 | (jose/jws:sign :rs256 (pem:read-from-file *private-key*) "test"))) 61 | (ok (jose/jws:verify :rs256 (pem:read-from-file *public-key*) token)))) 62 | 63 | ;; You may be interested in this site: http://kjur.github.io/jsjws/mobile/tool_jwt.html 64 | (deftest test-rsa-verify-with-reference-vector 65 | (let ((token256 "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2p3dC1pZHAuZXhhbXBsZS5jb20iLCJzdWIiOiJtYWlsdG86bWlrZUBleGFtcGxlLmNvbSIsIm5iZiI6MTUyODU0ODIzMSwiZXhwIjoxNTI4NTUxODMxLCJpYXQiOjE1Mjg1NDgyMzEsImp0aSI6ImlkMTIzNDU2IiwidHlwIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9yZWdpc3RlciJ9.Krl4oBv0ErVDq9bXKHQXYyoTi-jAohWkXAnuHtc5VrYmxmeq1ss4dsJYR27vJrJ7XWNVOa0ttpcI1BPk1J3S2FL4sOCkoeHI03tNB6a7VIeEZnSn2BWv_ISfH3RktyV4I8doA5Rpc0UXePlWYfQasr-qhXzxXPoD254IB-Xzji0tgiE4apCE9WT6m2yIwVF6YcVg_h_sfIDTFlOZEAq9NW6bcxRztEgQOnsCiBUMwlNLsOi3tRNwvbDN1yDL9jurFUjxoYY8F53XfEaxFIeejQTdCXs2taO5jATD65gtkuX4f8iIFZvH_SC4cNIg0wvRaG0xebJPCq33ExwNiMAutg") 66 | (token384 "eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2p3dC1pZHAuZXhhbXBsZS5jb20iLCJzdWIiOiJtYWlsdG86bWlrZUBleGFtcGxlLmNvbSIsIm5iZiI6MTUyODU0ODMzOSwiZXhwIjoxNTI4NTUxOTM5LCJpYXQiOjE1Mjg1NDgzMzksImp0aSI6ImlkMTIzNDU2IiwidHlwIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9yZWdpc3RlciJ9.DY_FKB8c5lufKSA80D4Yb6ahaXcfNDlW9YDNka9ZAOKM2FbI4nR7XCwqjH66FDY0UDgBnpWrmXGLuP5aPU9yGM04mUr__dcQPYRBwoxmmnimuSBBVtYXbTNxdcIAP6ZBlYct3YRnAyZcl-MnFbQZ8FXxD1hHs_wyNcx2QgO267JGlqjRMAZK0g04wM2YU0v-BUdz5Q6c0j4fXDGs-qOGqGgacuYisFheiw2RF2yayd4ruvupfliGMgOXlgeIZ2kX3TjNulHM5YPOeUhwWn6lWY4QW9m-RWtTKS4S8gB-nK9ZsU6iIZvvibr3LezIdvrqR8qju0P28EDvDfslCN_ynw") 67 | (token512 "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2p3dC1pZHAuZXhhbXBsZS5jb20iLCJzdWIiOiJtYWlsdG86bWlrZUBleGFtcGxlLmNvbSIsIm5iZiI6MTUyODU0ODQzMywiZXhwIjoxNTI4NTUyMDMzLCJpYXQiOjE1Mjg1NDg0MzMsImp0aSI6ImlkMTIzNDU2IiwidHlwIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9yZWdpc3RlciJ9.VOB7j5eC_0G_zC6AXjKh-KIZPzol8PLJvzctQcp9Zwufsg7iRgVwad-hCGRUWYefOtwJAf8Kc4jpCIWP5iihKFfeKhk8_s2xh4DOr9SuVd5xR2SH7-Yh0Z7dm54JUpDVBNYPbLi0jwzslqAOqm4h7aOmOed4SGyCkRW9xx8KK_IV2h-tCgySn10pXlanq8UQtLeeBHXNBfcpzY2oyGPFz_RQ1Cz0K0t_rf1Gdruokfk8P2WjBSigrRWjfplAxiiMvJv9KwZt0smomGyK-qm_93TsdWH2UnVj-vHFHdHDdM4gEalsrt3eliICDnZ5EJj3NBm41yuy_ei3qASF7pOS-Q") 68 | (token256-3920 "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2p3dC1pZHAuZXhhbXBsZS5jb20iLCJzdWIiOiJtYWlsdG86bWlrZUBleGFtcGxlLmNvbSIsIm5iZiI6MTUyODYwMDIzMiwiZXhwIjoxNTI4NjAzODMyLCJpYXQiOjE1Mjg2MDAyMzIsImp0aSI6ImlkMTIzNDU2IiwidHlwIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9yZWdpc3RlciJ9.qJPkVWZgrJ73avNHYD0ZX2cv7pXJUD6as7xgXA8yANpztQFOCQc5ZiBuAdPQ0n1e50xHZ0g9ozOCUcNvpXcjnIoB-vvFgg17wrf-jMfeLRBSnwRcUJrB11WwZyl6IxexqSnptJsetLQtda4CSDuZ6kGPvoLEWs93gA_m9BvZ1HhiArlwbHtSPcKvYfv36FnJyfJRCqbycNPzvDiViSh5Fy_yKJicd-1mYi98b0Hpguf6vP0TtF48fl7w3_UG1SOYd0BaRuE7mOheenY5U-iGDnRoU-g7yibSIkdnXYr6kyQihrzpf2e_TnpYbGvYU42CqZthGZu2e0GwtTKV800cmkxUk_SzruGCxafwDa-o9lY9hgZEg8sQKd_tCW4A_Gc8JvPxktQWnDMzTs94a8PQyD0eCcz-jwhTVMqj2k1PnJT4yXUKKAfRPTfcOZZHfV0mbzkZoRRsn_p8KvVAcWYMq2BYOWge50tjD5ycdP08VEkigCMKLYE0ySkmd3TlZEqy-v4GeeSmMaRyBv8MEY9GptKpELIHMLsCbeCx7MlRPBUPA5wTqGbs59FSWLd9QPtuDQ4PSlEd7DKt7LdivM1EXiQa1dPJ6a_L3h2-HOusckdciJP8azMw197g6jsbcR06eO_ZOzI-HdrH7A") 69 | (rsa-3920bits (asdf:system-relative-pathname :jose #P"tests/keys/rsa-3920bits-pub.pem")) 70 | (token256-3925 "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2p3dC1pZHAuZXhhbXBsZS5jb20iLCJzdWIiOiJtYWlsdG86bWlrZUBleGFtcGxlLmNvbSIsIm5iZiI6MTUyODU5OTI5NywiZXhwIjoxNTI4NjAyODk3LCJpYXQiOjE1Mjg1OTkyOTcsImp0aSI6ImlkMTIzNDU2IiwidHlwIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9yZWdpc3RlciJ9.E5MwImyxmWnzlGXs_QYK_vVn6mlrLVb1w-L4OCayMJpTpgvklwM-PXHVXQ-Vup2fVY7Y0yuDyhXVoKcsok8YOiEgHO_h_CP2P9BCCyWXqTrd_5LaItWBfidAXNW9ot0K-1DwdMJjofR-OeOfNiT9yQuo0NvKtpoq_QcBlQklAQ7vOUKwzLMLXTMlDDneG6AmKFdQgwsUclmJOzyU9_3l-p7s0y9d1nqzAEFdMDnEmRR-8MwCIRRKyQmuG2gl2SBK5_dGdOqZxr0VEAD0SB-uyycGUR6-p8Tw5KMo4_641pjIXXjpFvJ6q_imwfgA1VOJQ4KCAjSORMQgQbEI2ATDKbPE-WDPcwdbrQwGIvaUsMQtZrbYCFRPLCriqHXebzLvbGdfV8QvlTkm-26zlPcgLn9K6STHPigdFlH8_vAahInTrGhOtLPALW4psxVkCZ7e81ZoSHcwuc4Tue2YCEVxTL2GUMA-LPc2szm7e9zUltguQacW95VpUmoOuTCYvDnrw7iYnvvg4cPhibvx1Xkqt6mN0oHc5VB8cN-iL8cF_wz0T2yk7rFTtNYrpLn1NbOvg1TlfSojyLNz2KwilQSx5-Htg_HPr7UnLBQzidDbF9v_lBIamksaEp6xLvMNqBXb3RdeReXvoGZH4Ps") 71 | (rsa-3925bits (asdf:system-relative-pathname :jose #P"tests/keys/rsa-3925bits-pub.pem"))) 72 | (ok (jose/jws:verify :rs256 (pem:read-from-file *public-key*) token256)) 73 | (ok (jose/jws:verify :rs384 (pem:read-from-file *public-key*) token384)) 74 | (ok (jose/jws:verify :rs512 (pem:read-from-file *public-key*) token512)) 75 | (ok (jose/jws:verify :rs256 (pem:read-from-file rsa-3920bits) token256-3920)) 76 | ;; This test SHOULD be ok, but it failed miserably because of key size not to be a multiples of 8? 77 | (ok (signals 78 | (jose/jws:verify :rs256 (pem:read-from-file rsa-3925bits) token256-3925) 79 | 'jose/errors:jws-verification-error)))) 80 | -------------------------------------------------------------------------------- /tests/jwt.lisp: -------------------------------------------------------------------------------- 1 | (defpackage #:jose/tests/jwt 2 | (:use #:cl 3 | #:rove 4 | #:jose/jwt)) 5 | (in-package #:jose/tests/jwt) 6 | 7 | (defvar *secret* 8 | (map '(simple-array (unsigned-byte 8) (*)) 9 | #'char-code "secret")) 10 | 11 | (deftest test-number-keys-not-int 12 | (dolist (key '("iat" "nbf" "exp")) 13 | (let ((token 14 | (jose/jwt:encode :hs256 *secret* `((,key . "test"))))) 15 | (ok (signals 16 | (jose/jwt:decode :hs256 *secret* token) 17 | 'jose/errors:jwt-claims-error))))) 18 | 19 | (deftest test-nbf 20 | (let ((token 21 | (jose/jwt:encode :hs256 *secret* 22 | `(("nbf" . ,(- (- (get-universal-time) 2208988800) 1000)))))) 23 | (ok (jose/jwt:decode :hs256 *secret* token)))) 24 | 25 | (deftest test-nbf-in-future 26 | (let ((token 27 | (jose/jwt:encode :hs256 *secret* 28 | `(("nbf" . ,(+ (- (get-universal-time) 2208988800) 1000)))))) 29 | (ok (signals 30 | (jose/jwt:decode :hs256 *secret* token) 31 | 'jose/errors:jwt-claims-not-yet-valid)) 32 | (ok (handler-bind ((jose/errors:jwt-claims-not-yet-valid #'continue)) 33 | (jose/jwt:decode :hs256 *secret* token))))) 34 | 35 | (deftest test-exp 36 | (let ((token 37 | (jose/jwt:encode :hs256 *secret* 38 | `(("exp" . ,(+ (- (get-universal-time) 2208988800) 1000)))))) 39 | (ok (jose/jwt:decode :hs256 *secret* token)))) 40 | 41 | (deftest test-exp-in-past 42 | (let ((token 43 | (jose/jwt:encode :hs256 *secret* 44 | `(("exp" . ,(- (- (get-universal-time) 2208988800) 1000)))))) 45 | (ok (signals 46 | (jose/jwt:decode :hs256 *secret* token) 47 | 'jose/errors:jwt-claims-expired)) 48 | (ok (handler-bind ((jose/errors:jwt-claims-expired #'continue)) 49 | (jose/jwt:decode :hs256 *secret* token))))) 50 | 51 | (deftest test-skip-verify 52 | (multiple-value-bind (claims headers) 53 | (handler-bind ((jose/errors:jws-verification-error #'continue)) 54 | (jose/jwt:decode :hs256 *secret* 55 | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoiYiJ9.3MkJVAT-b30XkB4EwrYeqShkwa_GrHcJ1fp8xD1MoYk")) 56 | (ok (equal claims '(("a" . "b")))) 57 | (ok (equal headers '(("alg" . "HS256") ("typ" . "JWT")))))) 58 | -------------------------------------------------------------------------------- /tests/keys/rsa-3920bits-priv.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | 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 49 | -----END RSA PRIVATE KEY----- 50 | -------------------------------------------------------------------------------- /tests/keys/rsa-3920bits-pub.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIICDDANBgkqhkiG9w0BAQEFAAOCAfkAMIIB9AKCAesArf3jNY3HqoT+5H8LQCvy 3 | SP3QwprDNR74yoPPztbg9GY8w8G2ulkVQHpwPynp/HPJOCmyOpb5uKoL+ms3Euad 4 | KoFVMiQ+eSeTV0YCoXnVepnwp9sGTTMc/HB+ildBZAR2V/lmsbbT9DRvQapHiMeK 5 | oB3PMlIA057UKuQBy4Ri0qpEa/lMGHplwC0ESgvlIoGIIHxlz4mPD1qlFgsoTHN8 6 | PBh+N8ayYNkBBW68vLsoZtjWfLokbKSs9L/bPIj2PrSw/14NoeKDKghYgmnyNpG/ 7 | L7pxyIOnf+lZgSbYpKjjdcP4mbgmA+wojjBDeC57He80zGId+jiDza/vX4NtNZCX 8 | wS543OD1uXr34B8DcBrfS+lmGc/qHl3BwPPGDsM0bJfI3HvBokLFNRQYj0MyJpSu 9 | 4aeD0upB/EHEXY9VIcJzA6TCNPxFK/3rHGvlLAp0zxiKqyKe2FmUyUUelKDdn4lU 10 | D0rLgVOlnFKhd8Jkvz55HrFCmDgfzoq4epFU6LOv0sBE7z3AEomzefQZBDaralZ7 11 | E2+VsaS8oLc1UvC1tJmVMWTyBlaS7037D+6HPNtqwM1N4O2GyGGCk/fEZOiQCos/ 12 | 3Z5O0887ZutPEkGM3mNkmXcb/yhtqBlG7mhovZeIv5fbDU/keRyye5CViQIDAQAB 13 | -----END PUBLIC KEY----- 14 | -------------------------------------------------------------------------------- /tests/keys/rsa-3925bits-priv.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | 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 49 | -----END RSA PRIVATE KEY----- 50 | -------------------------------------------------------------------------------- /tests/keys/rsa-3925bits-pub.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIICDDANBgkqhkiG9w0BAQEFAAOCAfkAMIIB9AKCAesU5UHNPO/0+h4+/GAx+dC3 3 | quzxPKFCLBR9aLiTfRSfLqmajWQuMuQ8VYEPGE5AsgYCKkqd1xcjMUBDbr3ENJj7 4 | 6BLRx9V0GZqtCJjkVCOa4EM/DXkM4rbzwtmE5ie7FetpOf673s3mX0QyqDjd6Knn 5 | /sZQRuf2n3h+dO1bUpnTKJsl97Fr8/2o+/gP1ZFWWs0ggxQw1PkO8ntp+28Lg0PM 6 | q1GhJ03/pSXOeBXqBA0zXxcNfSpRowCvz4am2ON+pZw4G5qX7vjbBKJ88bUeVEDZ 7 | 7lwtTLmvndxA69r5o4ZVzbILxfSmx8cl2Y8lsnUC1xI1fminvg2TyrN3UuUC/sUk 8 | LHK5fZUzZzCXw7ymYqePRbgaAScZNy1wkagTo6gZ7yNIMF7v+XtdtjmT7McONf0L 9 | kXPMhdN+spYTGLVNSXRIoL67L/Pe7+wKvALWpUaGSMelyiidkZ7Xmut34hDROuPG 10 | 069lrfx1hk+JOyWLBHJbK8Z+tA+mcN+cC36tFRauLSPA6599orygtw1TLYcF8H7/ 11 | s6yu9ZIZ/YQFaO2JNsRbV8emByknHrcjCOKxUHlaUNPuFzb0G6JSCJlViJx+wXnO 12 | kZa7j/tXoEJa41kF4ITvzrDsnY57AmT0RVrA5WGckvtEKxUWIKmFxcoPcQIDAQAB 13 | -----END PUBLIC KEY----- 14 | -------------------------------------------------------------------------------- /tests/keys/rsa-priv.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEogIBAAKCAQEAum9xmq7qBsjYU3gNFB6z2DyQypeGvwR3MqbA5x4sevYjeqRu 3 | nFRq+oo6CyEjzC/zR8xh7NvLFwXImSmyYadUd+jstH1Kn5MJtBfCwlGSAXRfn6QV 4 | 8wr+oweWvyDNUgCkgM+6X7Q7wyH8pib9J2WAR6QcY3GRD+P+c/ZNwlgDSBVWzSUE 5 | 2Sw1GBXadgEDdTMq/DnGmGmsMIdgCMxJ+szAAv+dWJhuUPlp5zoFhyxayyJMCAND 6 | 3llFpmv85bIKfQb8EDkQjtFLOEbU0KIY4pPjKL01P4pDiqFFo6PWOJUHO5vyeLDW 7 | WCl1itOKeGxHvyxNQG/0BvQquxpjNjHZYCk0cwIDAQABAoIBABkE+a7ziE6Ox5E0 8 | DDVGBYagYiH+AcRCuihe/oZFo1yBCbPcu0dZgN3MjQuPT/mH+dMJ155sxK17Rjdf 9 | xCOczBYneRSjt88AcY3snmNrhPeTAX4wDA4IzLFeRFmz8jnuAiWTOwS68EY4mmpF 10 | 0zVlRrjWikTCKeCDDVPMmxTYsOAMWl03csr4tyJu9t83fCnDoIc5tH1WwiB+nnSl 11 | 2wOz3vpDf/CL7ZPqBcW2lINE0OD7sb5mlhBfU66Fli9fFlfO+a0YUfPCBqY8gOso 12 | fjqOvZcqdLlj0f66Xj+86uWlHp9hLGgJ6zgIIsca+yPVrOKlP+sYwDs65WQyhkkd 13 | Hvzp+uECgYEA5PVso7D06+cMSTG4QB8tQFBo2b8VXjepV3BLuGukmbw7z2YQQxTV 14 | 0NH2hTTvQ9CY6yy6uhwAwfAP35mRMEzlgzMHhhAc29gvFhWpGAJBz0sC/+O9/v8m 15 | 5VREiLyT76Xk3kz99GbWwNwFIdXkyelSfvxiBO7vsDSPw2zwYyNpj1ECgYEA0HRS 16 | 4a7whcn3C9U6mxK7mMvT2B+HfbN0FXuIADWgKziEdfRIPsy6NYOg/OBcWMvjy0VI 17 | d2TBAje8D6WvG9QN/DHeAFHSX/DW8oW5IWNGolIY/EiKw8lhBjgrNgHl5eJGD5DH 18 | ipe4U0+4Imdq26OqZwL61HYH5SwqUpqONxorfoMCgYBv+QL3jwxI7ocYqMM2QMkN 19 | ogWVMBlQKaKcy6OMfsBSGzeY945OcDsdVAHfJYM6RCL1KLvtVtKcBj6NGPpjh8fb 20 | ATLVwr2KWtC0WUWII1px+XpvEL8TnU81ap/Vy3wCALzMZxTv2Pd//FpaMNQiVwRs 21 | bBu30+7O2vXQGk/5/BCc8QKBgBAEoEnPU5Q0TNOP8wzvh5LaNtEouxShsY3lDDJX 22 | 7JLlqOgXeWW5/aUXFEvaQb5hDIQWMtdZ2qr89WqOZMJSrTBv9Is5vly4+Qtx0yQJ 23 | qOfYPytDt8YLt3Tu5AMmajAcDx4rFepEdlmQiqm6IK/4B6QayoOA/mJR3n6yebMq 24 | Q6VZAoGALlPZ7+FNkAFnXK0m9phXPfQm2H6HyEf8X1gbhimkfh5R/v3BD92CQ35N 25 | RQ7o1mhljwtlH6qDNqfHtuh30m/Hm5mokK/9knUnTza3wmNq6ZA+K1o2LRnlGb+u 26 | u2FIFS1oyxICmSOLsS0x+SRPSJA5vTgxPoM4TYVUESWWl4TNMWg= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /tests/keys/rsa-pub.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAum9xmq7qBsjYU3gNFB6z 3 | 2DyQypeGvwR3MqbA5x4sevYjeqRunFRq+oo6CyEjzC/zR8xh7NvLFwXImSmyYadU 4 | d+jstH1Kn5MJtBfCwlGSAXRfn6QV8wr+oweWvyDNUgCkgM+6X7Q7wyH8pib9J2WA 5 | R6QcY3GRD+P+c/ZNwlgDSBVWzSUE2Sw1GBXadgEDdTMq/DnGmGmsMIdgCMxJ+szA 6 | Av+dWJhuUPlp5zoFhyxayyJMCAND3llFpmv85bIKfQb8EDkQjtFLOEbU0KIY4pPj 7 | KL01P4pDiqFFo6PWOJUHO5vyeLDWWCl1itOKeGxHvyxNQG/0BvQquxpjNjHZYCk0 8 | cwIDAQAB 9 | -----END PUBLIC KEY----- 10 | --------------------------------------------------------------------------------