From 504e155f5de6f11a432ce963f4926b8d77a4bd8c Mon Sep 17 00:00:00 2001 From: Jaidyn Ann <10477760+JadedCtrl@users.noreply.github.com> Date: Thu, 14 Sep 2023 18:45:03 -0500 Subject: [PATCH] Begin (provisional!!) RSA key management --- activitypub-servist.asd | 2 +- activitypub-servist.lisp | 93 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 90 insertions(+), 5 deletions(-) diff --git a/activitypub-servist.asd b/activitypub-servist.asd index 41c10e7..f5d4343 100644 --- a/activitypub-servist.asd +++ b/activitypub-servist.asd @@ -1,3 +1,3 @@ (defsystem "activitypub-servist" - :depends-on ("alexandria" "clack" "purl" "str" "webtentacle" "yason") + :depends-on ("alexandria" "clack" "inferior-shell" "ironclad" "purl" "str" "webtentacle" "yason") :components ((:file "activitypub-servist"))) diff --git a/activitypub-servist.lisp b/activitypub-servist.lisp index 6da8db8..3408bcb 100644 --- a/activitypub-servist.lisp +++ b/activitypub-servist.lisp @@ -19,8 +19,6 @@ (in-package #:activitypub-servist) -;;(ql:quickload '(alexandria asn1 clack cl-base64 ironclad purl str trivia trivial-utf-8 webtentacle yason)) - (defun users () "List of the server's usernames." @@ -29,7 +27,7 @@ (defun directories () "Alist of the server's paths and their response functions." - '(("u/" . http-dir) (".well-known/webfinger" . http-webfinger))) + '(("u/" . http-user-dir) (".well-known/webfinger" . http-webfinger))) @@ -131,7 +129,6 @@ the plist in the docstring for its WEBTENTACLE:SERVER function." ("404, you goddamn fool!"))) - ;; ———————————————————————————————————————— ;; Invocation ;; ———————————————————————————————————————— @@ -204,3 +201,91 @@ or “/bear/apple/” or “/bear/”, but not “/bear” (not a directory)." "Split a pathname into a list of its components. “/u/bear/apple.txt” → '(“u” “bear” “apple.txt”)" (str:split #\/ pathname :omit-nulls 't)) + + + +;; ———————————————————————————————————————— +;; RSA keys +;; ———————————————————————————————————————— +;; At the moment, I’ve yet to use figure out how to create PEM representations of +;; a public keypair properly in Lisp. +;; So at the moment, keys are generated into PEM files by the openssl binary on +;; the host’s system; and the output of the openssl command is used to parse into +;; Ironclad keys. +;; In the future, I’ll stop that. But at the moment,I want to focus on other core +;; parts of ActivityPub; I’ve tired of messing with ASN1 & co. + +(defun parse-openssl-output (lines &optional (results '())) + "When passed the output of the shell command `openssl rsa -text -noout`, will +parse the output into a plist containing relavent numbers: + :n (modulus), :e (public exponent), :d (private exponent), :p (1st prime), + :q (2nd prime), :e1 (1st exponent), :e2 (2nd exponent), and :c (coefficient)." + (let ((line (str:trim (car lines)))) + (cond + ((not lines) + (mapcar + (lambda (result-item) + (if (stringp result-item) + (parse-integer (str:replace-all ":" "" result-item) :radix 16) + result-item)) + results)) + ((str:starts-with-p "Private" line) + (parse-openssl-output (cdr lines) results)) + ((str:starts-with-p "modulus:" line) + (parse-openssl-output (cdr lines) (nconc results '(:n)))) + ((str:starts-with-p "prime1" line) + (parse-openssl-output (cdr lines) (nconc results '(:p)))) + ((str:starts-with-p "prime2" line) + (parse-openssl-output (cdr lines) (nconc results '(:q)))) + ((str:starts-with-p "exponent1" line) + (parse-openssl-output (cdr lines) (nconc results '(:e1)))) + ((str:starts-with-p "exponent2" line) + (parse-openssl-output (cdr lines) (nconc results '(:e2)))) + ((str:starts-with-p "coefficient" line) + (parse-openssl-output (cdr lines) (nconc results '(:c)))) + ((str:starts-with-p "privateExponent" line) + (parse-openssl-output (cdr lines) (nconc results '(:d)))) + ((str:starts-with-p "publicExponent" line) + (parse-openssl-output + (cdr lines) + (nconc + results + (list + :e + (parse-integer + (car (str:split #\space + (str:replace-first + "publicExponent: " + "" + line)))))))) + ('t + (let* ((last-element (car (last results))) + (total-string (if (stringp last-element) + (str:concat last-element line) + line))) + (parse-openssl-output (cdr lines) + (if (stringp last-element) + (nconc (reverse (cdr (reverse results))) + (list total-string)) + (nconc results + (list total-string))))))))) + + +(defun openssl-shell-pem-keypair (pem-file) + "Given a private RSA PEM file, this will parse it into two returned values: +An Ironclad private key, and an Ironclad public key." + (let ((key-values + (parse-openssl-output + (inferior-shell:run/lines + (str:concat "openssl rsa -text -noout -in " pem-file))))) + (values (ironclad:make-private-key + :rsa + :n (getf key-values :n) + :e (getf key-values :e) + :d (getf key-values :d) + :p (getf key-values :p) + :q (getf key-values :q)) + (ironclad:make-public-key + :rsa + :n (getf key-values :n) + :e (getf key-values :e)))))