Ensure the signature’s domain matches the signee’s
A signature-key with a different omain from the actor of the activity or the actiivty’s ID itself might be indicative of tampering; disallow it.
This commit is contained in:
parent
7b3cf9cb89
commit
7f5b6ffc57
|
@ -143,7 +143,7 @@ Returns the object if it was retrieved or fetched; nil otherwise."
|
||||||
|
|
||||||
;;; Signature HTTP-header parsing
|
;;; Signature HTTP-header parsing
|
||||||
;;; ————————————————————————————————————————
|
;;; ————————————————————————————————————————
|
||||||
(defun signature-valid-p (env &key (current-time (get-universal-time)))
|
(defun signature-valid-p (env activity &key (current-time (get-universal-time)))
|
||||||
"Return whether or not the Clack HTTP-request ENV’s signature is valid.
|
"Return whether or not the Clack HTTP-request ENV’s signature is valid.
|
||||||
Only RSA-SHA256 signatures are supported.
|
Only RSA-SHA256 signatures are supported.
|
||||||
Might provide a condition detailing the reason of the signature’s invalidity as
|
Might provide a condition detailing the reason of the signature’s invalidity as
|
||||||
|
@ -161,6 +161,8 @@ https://swicg.github.io/activitypub-http-signature/"
|
||||||
(signed-str (signed-string env signature-alist :current-time current-time)))
|
(signed-str (signed-string env signature-alist :current-time current-time)))
|
||||||
(when (and algorithm (not (string-equal (cdr algorithm) "rsa-sha256")))
|
(when (and algorithm (not (string-equal (cdr algorithm) "rsa-sha256")))
|
||||||
(signal 'invalid-signature-algorithm :algorithm (cdr algorithm)))
|
(signal 'invalid-signature-algorithm :algorithm (cdr algorithm)))
|
||||||
|
(when (not (matching-domains-p signature-alist activity))
|
||||||
|
(signal 'invalid-signature-domain-mismatch))
|
||||||
(list
|
(list
|
||||||
(gethash "https://w3id.org/security#publicKeyPem" (signature-key signature-alist))
|
(gethash "https://w3id.org/security#publicKeyPem" (signature-key signature-alist))
|
||||||
signed-str
|
signed-str
|
||||||
|
@ -168,6 +170,27 @@ https://swicg.github.io/activitypub-http-signature/"
|
||||||
(invalid-signature (err)
|
(invalid-signature (err)
|
||||||
(values nil err))))
|
(values nil err))))
|
||||||
|
|
||||||
|
(defun matching-domains-p (signature-alist activity)
|
||||||
|
"Returns whether or not the domain names within an ACTIVITY match, for ensuring
|
||||||
|
its signature is applicable: Those of the signature key’s, the actor’s @ID,
|
||||||
|
and the ACTIVITY’s @ID.
|
||||||
|
If these all match the same domain, and the signature is valid, we can safely say
|
||||||
|
the ACTIVITY did indeed come from that domain."
|
||||||
|
(labels ((uri-string (slot-value)
|
||||||
|
(typecase slot-value
|
||||||
|
(string slot-value)
|
||||||
|
(json-ld:object
|
||||||
|
(json-ld:@id slot-value))))
|
||||||
|
(domain-name (slot-value)
|
||||||
|
(let ((uri-string (uri-string slot-value)))
|
||||||
|
(when uri-string
|
||||||
|
(quri:uri-domain (quri:uri uri-string))))))
|
||||||
|
(equal*
|
||||||
|
(remove-if #'not
|
||||||
|
(list (domain-name activity)
|
||||||
|
(domain-name (as/v/a:actor activity))
|
||||||
|
(domain-name (assoc :keyid signature-alist)))))))
|
||||||
|
|
||||||
(defun signature-header-parse (signature-header)
|
(defun signature-header-parse (signature-header)
|
||||||
"Parses the signature header into an associative list of the form:
|
"Parses the signature header into an associative list of the form:
|
||||||
'((:KEYID . “https://jam.xwx.moe/users/jadedctrl#main-key”)
|
'((:KEYID . “https://jam.xwx.moe/users/jadedctrl#main-key”)
|
||||||
|
@ -267,6 +290,13 @@ https://swicg.github.io/activitypub-http-signature/#how-to-obtain-a-signature-s-
|
||||||
(slot-value condition 'algorithm))))
|
(slot-value condition 'algorithm))))
|
||||||
(:documentation "Thrown during HTTP signature-validation, when the algorithm is unsupported."))
|
(:documentation "Thrown during HTTP signature-validation, when the algorithm is unsupported."))
|
||||||
|
|
||||||
|
(define-condition invalid-signature-domain-mismatch (invalid-signature)
|
||||||
|
()
|
||||||
|
(:report (lambda (condition stream)
|
||||||
|
(format stream "There is a domain-name mismatch within the activity, and so we can’t say for sure the signature is valid.~%
|
||||||
|
Check the ID domain-names of the actor, the activity, and the signature-key.~&")))
|
||||||
|
(:documentation "Thrown during HTTP signature-validation, when it's noticed that domains-names for ID URIs don't match."))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;;; Fetching public keys
|
;;; Fetching public keys
|
||||||
|
@ -382,13 +412,14 @@ can be found). Uses the callback :RETRIEVE, defined in *CONFIG*."
|
||||||
(defun http-inbox (env path-items params)
|
(defun http-inbox (env path-items params)
|
||||||
"If one tries to send an activity to our inbox, pass it along to
|
"If one tries to send an activity to our inbox, pass it along to
|
||||||
the overloaded RECEIVE method."
|
the overloaded RECEIVE method."
|
||||||
(let* ((contents (body-contents env)))
|
(let* ((contents (body-contents env))
|
||||||
|
(json-contents (json-ld:parse contents)))
|
||||||
(multiple-value-bind (signature-valid-p signature-error)
|
(multiple-value-bind (signature-valid-p signature-error)
|
||||||
(signature-valid-p env)
|
(signature-valid-p env json-contents)
|
||||||
(cond (signature-error (signal signature-error))
|
(cond (signature-error (signal signature-error))
|
||||||
((not signature-valid-p)
|
((not signature-valid-p)
|
||||||
(signal 'http-result :status 401 :message "Failed to verify signature. Heck! TvT"))
|
(signal 'http-result :status 401 :message "Failed to verify signature. Heck! TvT"))
|
||||||
((receive (json-ld:parse contents))
|
((receive json-contents)
|
||||||
'(200 (:content-type "text/plain") ("You win!")))))))
|
'(200 (:content-type "text/plain") ("You win!")))))))
|
||||||
|
|
||||||
|
|
||||||
|
@ -568,3 +599,7 @@ or “/bear/apple/” or “/bear/”, but not “/bear” (not a directory)."
|
||||||
(loop for number across
|
(loop for number across
|
||||||
sequence
|
sequence
|
||||||
collect (format nil "~X" number))))
|
collect (format nil "~X" number))))
|
||||||
|
(defun equal* (&rest items)
|
||||||
|
"Whether or not all ITEMS are EQUAL to one another."
|
||||||
|
(loop for item in items
|
||||||
|
always (equal item (car items))))
|
||||||
|
|
Ŝarĝante…
Reference in New Issue