summaryrefslogtreecommitdiff
path: root/test/lisp
diff options
context:
space:
mode:
Diffstat (limited to 'test/lisp')
-rw-r--r--test/lisp/erc/erc-sasl-tests.el344
-rw-r--r--test/lisp/erc/erc-scenarios-sasl.el144
-rw-r--r--test/lisp/erc/resources/sasl/external.eld33
-rw-r--r--test/lisp/erc/resources/sasl/plain-failed.eld16
-rw-r--r--test/lisp/erc/resources/sasl/plain.eld39
-rw-r--r--test/lisp/erc/resources/sasl/scram-sha-1.eld47
-rw-r--r--test/lisp/erc/resources/sasl/scram-sha-256.eld47
7 files changed, 670 insertions, 0 deletions
diff --git a/test/lisp/erc/erc-sasl-tests.el b/test/lisp/erc/erc-sasl-tests.el
new file mode 100644
index 00000000000..64593ca270c
--- /dev/null
+++ b/test/lisp/erc/erc-sasl-tests.el
@@ -0,0 +1,344 @@
+;;; erc-sasl-tests.el --- Tests for erc-sasl. -*- lexical-binding:t -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+;;
+;; This file is part of GNU Emacs.
+;;
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+;;
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Code:
+
+(require 'ert-x)
+(require 'erc-sasl)
+
+(ert-deftest erc-sasl--mechanism-offered-p ()
+ (let ((erc-sasl--options '((mechanism . external))))
+ (should (erc-sasl--mechanism-offered-p "foo,external"))
+ (should (erc-sasl--mechanism-offered-p "external,bar"))
+ (should (erc-sasl--mechanism-offered-p "foo,external,bar"))
+ (should-not (erc-sasl--mechanism-offered-p "fooexternal"))
+ (should-not (erc-sasl--mechanism-offered-p "externalbar"))))
+
+(ert-deftest erc-sasl--read-password--basic ()
+ (ert-info ("Explicit erc-sasl-password")
+ (let ((erc-sasl--options '((password . "foo"))))
+ (should (string= (erc-sasl--read-password nil) "foo"))))
+
+ (ert-info ("Explicit session password")
+ (let ((erc-session-password "foo")
+ (erc-sasl--options '((password . :password))))
+ (should (string= (erc-sasl--read-password nil) "foo"))))
+
+ (ert-info ("Fallback to prompt skip auth-source")
+ (should-not erc-sasl-auth-source-function)
+ (let ((erc-session-password "bar")
+ (erc-networks--id (erc-networks--id-create nil)))
+ (should (string= (ert-simulate-keys "bar\r"
+ (erc-sasl--read-password "?"))
+ "bar"))))
+
+ (ert-info ("Prompt when auth-source fails and `erc-sasl-password' null")
+ (let ((erc-sasl--options '((password)))
+ (erc-sasl-auth-source-function #'ignore))
+ (should (string= (ert-simulate-keys "baz\r"
+ (erc-sasl--read-password "pwd:"))
+ "baz")))))
+
+(ert-deftest erc-sasl--read-password--auth-source ()
+ (ert-with-temp-file netrc-file
+ :text (string-join
+ (list
+ ;; If you swap these first 2 lines, *1 below fails
+ "machine FSF.chat port 6697 user bob password sesame"
+ "machine GNU/chat port 6697 user bob password spam"
+ "machine MyHost port irc password 123")
+ "\n")
+ (let* ((auth-sources (list netrc-file))
+ (erc-session-server "irc.gnu.org")
+ (erc-session-port 6697)
+ (erc-networks--id (erc-networks--id-create nil))
+ calls
+ (erc-sasl-auth-source-function
+ (lambda (&rest r)
+ (push r calls)
+ (apply #'erc--auth-source-search r)))
+ erc-server-announced-name ; too early
+ auth-source-do-cache)
+
+ (ert-info ("Symbol as password specifies machine")
+ (let ((erc-sasl--options '((user . "bob") (password . FSF.chat)))
+ (erc-networks--id (make-erc-networks--id)))
+ (should (string= (erc-sasl--read-password nil) "sesame"))
+ (should (equal (pop calls) '(:user "bob" :host "FSF.chat")))))
+
+ (ert-info ("ID for :host and `erc-session-username' for :user") ; *1
+ (let ((erc-session-username "bob")
+ (erc-sasl--options '((user . :user) (password)))
+ (erc-networks--id (erc-networks--id-create 'GNU/chat)))
+ (should (string= (erc-sasl--read-password nil) "spam"))
+ (should (equal (pop calls) '(:user "bob" :host "GNU/chat")))))
+
+ (ert-info ("ID for :host and current nick for :user") ; *1
+ (let ((erc-server-current-nick "bob")
+ (erc-sasl--options '((user . :nick) (password)))
+ (erc-networks--id (erc-networks--id-create 'GNU/chat)))
+ (should (string= (erc-sasl--read-password nil) "spam"))
+ (should (equal (pop calls) '(:user "bob" :host "GNU/chat")))))
+
+ (ert-info ("Symbol as password, entry lacks user field")
+ (let ((erc-server-current-nick "fake")
+ (erc-sasl--options '((user . :nick) (password . MyHost)))
+ (erc-networks--id (erc-networks--id-create 'GNU/chat)))
+ (should (string= (erc-sasl--read-password nil) "123"))
+ (should (equal (pop calls) '(:user "fake" :host "MyHost"))))))))
+
+(ert-deftest erc-sasl-create-client--plain ()
+ (let* ((erc-session-password "password123")
+ (erc-session-username "tester")
+ (erc-sasl--options '((user . :user) (password . :password)))
+ (erc-session-port 1667)
+ (erc-session-server "localhost")
+ (client (erc-sasl--create-client 'plain))
+ (result (sasl-next-step client nil)))
+ (should (equal (format "%S" [erc-sasl--plain-response
+ "\0tester\0password123"])
+ (format "%S" result)))
+ (should (string= (sasl-step-data result) "\0tester\0password123"))
+ (should-not (sasl-next-step client result)))
+ (should (equal (assoc-default "PLAIN" sasl-mechanism-alist) '(sasl-plain))))
+
+(ert-deftest erc-sasl-create-client--external ()
+ (let* ((erc-server-current-nick "tester")
+ (erc-sasl--options '((user . :nick) (password . :password)))
+ (client (erc-sasl--create-client 'external)) ; unused ^
+ (result (sasl-next-step client nil)))
+ (should (equal (format "%S" [ignore nil]) (format "%S" result)))
+ (should-not (sasl-step-data result))
+ (should-not (sasl-next-step client result)))
+ (should-not (member "EXTERNAL" sasl-mechanisms))
+ (should-not (assoc-default "EXTERNAL" sasl-mechanism-alist)))
+
+(ert-deftest erc-sasl-create-client--scram-sha-1 ()
+ (let* ((erc-sasl--options '((user . "jilles") (password . "sesame")
+ (authzid . "jilles")))
+ (mock-rvs (list "c5RqLCZy0L4fGkKAZ0hujFBs" ""))
+ (sasl-unique-id-function (lambda () (pop mock-rvs)))
+ (client (erc-sasl--create-client 'scram-sha-1))
+ (step (sasl-next-step client nil)))
+ (ert-info ("Client's initial request")
+ (let ((req "n,a=jilles,n=jilles,r=c5RqLCZy0L4fGkKAZ0hujFBs"))
+ (should (equal (format "%S"
+ `[erc-compat--29-sasl-scram-client-first-message
+ ,req])
+ (format "%S" step)))
+ (should (string= (sasl-step-data step) req))))
+ (ert-info ("Server's initial response")
+ (let ((resp (concat "r=c5RqLCZy0L4fGkKAZ0hujFBsXQoKcivqCw9iDZPSpb,"
+ "s=5mJO6d4rjCnsBU1X,"
+ "i=4096"))
+ (req (concat "c=bixhPWppbGxlcyw=,"
+ "r=c5RqLCZy0L4fGkKAZ0hujFBsXQoKcivqCw9iDZPSpb,"
+ "p=OVUhgPu8wEm2cDoVLfaHzVUYPWU=")))
+ (sasl-step-set-data step resp)
+ (setq step (sasl-next-step client step))
+ (should (equal (format "%S"
+ `[erc-sasl--scram-sha-1-client-final-message
+ ,req])
+ (format "%S" step)))
+ (should (string= (sasl-step-data step) req))))
+ (ert-info ("Server's final message")
+ (let ((resp "v=ZWR23c9MJir0ZgfGf5jEtLOn6Ng="))
+ (sasl-step-set-data step resp)
+ (setq step (sasl-next-step client step))
+ (should-not (sasl-step-data step)))))
+ (should (eq sasl-unique-id-function #'sasl-unique-id-function)))
+
+(ert-deftest erc-sasl-create-client--scram-sha-256 ()
+ (unless (featurep 'sasl-scram-sha256)
+ (ert-skip "Emacs lacks sasl-scram-sha256"))
+ (let* ((erc-server-current-nick "jilles")
+ (erc-session-password "sesame")
+ (erc-sasl--options '((user . :nick) (password . :password)
+ (authzid . "jilles")))
+ (mock-rvs (list "c5RqLCZy0L4fGkKAZ0hujFBs" ""))
+ (sasl-unique-id-function (lambda () (pop mock-rvs)))
+ (client (erc-sasl--create-client 'scram-sha-256))
+ (step (sasl-next-step client nil)))
+ (ert-info ("Client's initial request")
+ (let ((req "n,a=jilles,n=jilles,r=c5RqLCZy0L4fGkKAZ0hujFBs"))
+ (should (equal (format "%S"
+ `[erc-compat--29-sasl-scram-client-first-message
+ ,req])
+ (format "%S" step)))
+ (should (string= (sasl-step-data step) req))))
+ (ert-info ("Server's initial response")
+ (let ((resp (concat
+ "r=c5RqLCZy0L4fGkKAZ0hujFBse697140729d8445fb95ec94ceacb14b3,"
+ "s=MTk2M2VkMzM5ZmU0NDRiYmI0MzIyOGVhN2YwNzYwNmI=,"
+ "i=4096"))
+ (req (concat
+ "c=bixhPWppbGxlcyw=,"
+ "r=c5RqLCZy0L4fGkKAZ0hujFBse697140729d8445fb95ec94ceacb14b3,"
+ "p=1vDesVBzJmv0lX0Ae1kHFtdVHkC6j4gISKVqaR45HFg=")))
+ (sasl-step-set-data step resp)
+ (setq step (sasl-next-step client step))
+ (should (equal (format "%S"
+ `[erc-sasl--scram-sha-256-client-final-message
+ ,req])
+ (format "%S" step)))
+ (should (string= (sasl-step-data step) req))))
+ (ert-info ("Server's final message")
+ (let ((resp "v=gUePTYSZN9xgcE06KSyKO9fUmSwH26qifoapXyEs75s="))
+ (sasl-step-set-data step resp)
+ (setq step (sasl-next-step client step))
+ (should-not (sasl-step-data step)))))
+ (should (eq sasl-unique-id-function #'sasl-unique-id-function)))
+
+(ert-deftest erc-sasl-create-client--scram-sha-256--no-authzid ()
+ (unless (featurep 'sasl-scram-sha256)
+ (ert-skip "Emacs lacks sasl-scram-sha256"))
+ (let* ((erc-server-current-nick "jilles")
+ (erc-session-password "sesame")
+ (erc-sasl--options '((user . :nick) (password . :password) (authzid)))
+ (mock-rvs (list "c5RqLCZy0L4fGkKAZ0hujFBs" ""))
+ (sasl-unique-id-function (lambda () (pop mock-rvs)))
+ (client (erc-sasl--create-client 'scram-sha-256))
+ (step (sasl-next-step client nil)))
+ (ert-info ("Client's initial request")
+ (let ((req "n,,n=jilles,r=c5RqLCZy0L4fGkKAZ0hujFBs"))
+ (should (equal (format "%S"
+ `[erc-compat--29-sasl-scram-client-first-message
+ ,req])
+ (format "%S" step)))
+ (should (string= (sasl-step-data step) req))))
+ (ert-info ("Server's initial response")
+ (let ((resp (concat
+ "r=c5RqLCZy0L4fGkKAZ0hujFBsd4067f0afdb54c3dbd4fe645b84cae37,"
+ "s=ZTg1MmE1YmFhZGI1NDcyMjk3NzYwZmRjZDM3Y2I1OTM=,"
+ "i=4096"))
+ (req (concat
+ "c=biws,"
+ "r=c5RqLCZy0L4fGkKAZ0hujFBsd4067f0afdb54c3dbd4fe645b84cae37,"
+ "p=LP4sjJrjJKp5qTsARyZCppXpKLu4FMM284hNESPvGhI=")))
+ (sasl-step-set-data step resp)
+ (setq step (sasl-next-step client step))
+ (should (equal (format "%S"
+ `[erc-sasl--scram-sha-256-client-final-message
+ ,req])
+ (format "%S" step)))
+ (should (string= (sasl-step-data step) req))))
+ (ert-info ("Server's final message")
+ (let ((resp "v=847WXfnmReGyE1qlq1And6R4bPBNROTZ7EMS/QrJtUM="))
+ (sasl-step-set-data step resp)
+ (setq step (sasl-next-step client step))
+ (should-not (sasl-step-data step)))))
+ (should (eq sasl-unique-id-function #'sasl-unique-id-function)))
+
+(ert-deftest erc-sasl-create-client--scram-sha-512--no-authzid ()
+ (unless (featurep 'sasl-scram-sha256)
+ (ert-skip "Emacs lacks sasl-scram-sha512"))
+ (let* ((erc-server-current-nick "jilles")
+ (erc-session-password "sesame")
+ (erc-sasl--options '((user . :nick) (password . :password) (authzid)))
+ (mock-rvs (list "c5RqLCZy0L4fGkKAZ0hujFBs" ""))
+ (sasl-unique-id-function (lambda () (pop mock-rvs)))
+ (client (erc-sasl--create-client 'scram-sha-512))
+ (step (sasl-next-step client nil)))
+ (ert-info ("Client's initial request")
+ (let ((req "n,,n=jilles,r=c5RqLCZy0L4fGkKAZ0hujFBs"))
+ (should (equal (format "%S"
+ `[erc-compat--29-sasl-scram-client-first-message
+ ,req])
+ (format "%S" step)))
+ (should (string= (sasl-step-data step) req))))
+ (ert-info ("Server's initial response")
+ (let ((resp (concat
+ "r=c5RqLCZy0L4fGkKAZ0hujFBs54c592745ce14e559fcc3f27b15464f6,"
+ "s=YzMzOWZiY2U0YzcwNDA0M2I4ZGE2M2ZjOTBjODExZTM=,"
+ "i=4096"))
+ (req (concat
+ "c=biws,"
+ "r=c5RqLCZy0L4fGkKAZ0hujFBs54c592745ce14e559fcc3f27b15464f6,"
+ "p=vMBb9tKxFAfBtel087/GLbo4objAIYr1wM+mFv/jYLKXE"
+ "NUF0vynm81qQbywQE5ScqFFdAfwYMZq/lj4s0V1OA==")))
+ (sasl-step-set-data step resp)
+ (setq step (sasl-next-step client step))
+ (should (equal (format
+ "%S" `[erc-sasl--scram-sha-512-client-final-message
+ ,req])
+ (format "%S" step)))
+ (should (string= (sasl-step-data step) req))))
+ (ert-info ("Server's final message")
+ (let ((resp (concat "v=Va7NIvt8wCdhvxnv+bZriSxGoto6On5EVnRHO/ece8zs0"
+ "qpQassdqir1Zlwh3e3EmBq+kcSy+ClNCsbzBpXe/w==")))
+ (sasl-step-set-data step resp)
+ (setq step (sasl-next-step client step))
+ (should-not (sasl-step-data step)))))
+ (should (eq sasl-unique-id-function #'sasl-unique-id-function)))
+
+(defconst erc-sasl-tests-ecdsa-key-file "
+-----BEGIN EC PARAMETERS-----
+BggqhkjOPQMBBw==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIIJueQ3W2IrGbe9wKdOI75yGS7PYZSj6W4tg854hlsvmoAoGCCqGSM49
+AwEHoUQDQgAEAZmaVhNSMmV5r8FXPvKuMnqDKyIA9pDHN5TNMfiF3mMeikGgK10W
+IRX9cyi2wdYg9mUUYyh9GKdBCYHGUJAiCA==
+-----END EC PRIVATE KEY-----
+")
+
+(ert-deftest erc-sasl-create-client-ecdsa ()
+ :tags '(:unstable)
+ ;; This is currently useless because it just roundtrips shelling out
+ ;; to pkeyutl.
+ (ert-skip "Placeholder")
+ (unless (executable-find "openssl")
+ (ert-skip "System lacks openssl"))
+ (ert-with-temp-file keyfile
+ :prefix "ecdsa_key"
+ :suffix ".pem"
+ :text erc-sasl-tests-ecdsa-key-file
+ (let* ((erc-server-current-nick "jilles")
+ (erc-sasl--options `((password . ,keyfile)))
+ (client (erc-sasl--create-client 'ecdsa-nist256p-challenge))
+ (step (sasl-next-step client nil)))
+ (ert-info ("Client's initial request")
+ (should (equal (format "%S" [erc-sasl--ecdsa-first "jilles"])
+ (format "%S" step)))
+ (should (string= (sasl-step-data step) "jilles")))
+ (ert-info ("Server's initial response")
+ (let ((resp (concat "\0\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20"
+ "\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37")))
+ (sasl-step-set-data step resp)
+ (setq step (sasl-next-step client step))
+ (ert-with-temp-file sigfile
+ :prefix "ecdsa_sig"
+ :suffix ".sig"
+ :text (sasl-step-data step)
+ (with-temp-buffer
+ (set-buffer-multibyte nil)
+ (insert resp)
+ (let ((ec (call-process-region
+ (point-min) (point-max)
+ "openssl" 'delete t nil "pkeyutl"
+ "-inkey" keyfile "-sigfile" sigfile
+ "-verify")))
+ (unless (zerop ec)
+ (message "%s" (buffer-string)))
+ (should (zerop ec)))))))
+ (should-not (sasl-next-step client step)))))
+
+;;; erc-sasl-tests.el ends here
diff --git a/test/lisp/erc/erc-scenarios-sasl.el b/test/lisp/erc/erc-scenarios-sasl.el
new file mode 100644
index 00000000000..6c5e78d0c8d
--- /dev/null
+++ b/test/lisp/erc/erc-scenarios-sasl.el
@@ -0,0 +1,144 @@
+;;; erc-scenarios-sasl.el --- SASL tests for ERC -*- lexical-binding: t -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+;;
+;; This file is part of GNU Emacs.
+;;
+;; This program is free software: you can redistribute it and/or
+;; modify it under the terms of the GNU General Public License as
+;; published by the Free Software Foundation, either version 3 of the
+;; License, or (at your option) any later version.
+;;
+;; This program is distributed in the hope that it will be useful, but
+;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;; General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see
+;; <https://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(require 'ert-x)
+(eval-and-compile
+ (let ((load-path (cons (ert-resource-directory) load-path)))
+ (require 'erc-scenarios-common)))
+
+(require 'erc-sasl)
+
+(ert-deftest erc-scenarios-sasl--plain ()
+ :tags '(:expensive-test)
+ (erc-scenarios-common-with-cleanup
+ ((erc-scenarios-common-dialog "sasl")
+ (erc-server-flood-penalty 0.1)
+ (dumb-server (erc-d-run "localhost" t 'plain))
+ (port (process-contact dumb-server :service))
+ (erc-modules (cons 'sasl erc-modules))
+ (erc-sasl-password "password123")
+ (expect (erc-d-t-make-expecter)))
+
+ (ert-info ("Connect")
+ (with-current-buffer (erc :server "127.0.0.1"
+ :port port
+ :nick "tester"
+ :user "tester"
+ :full-name "tester")
+ (should (string= (buffer-name) (format "127.0.0.1:%d" port)))))
+
+ (ert-info ("Notices received")
+ (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "ExampleOrg"))
+ (funcall expect 10 "This server is in debug mode")
+ ;; Regression "\0\0\0\0 ..." caused by (fillarray passphrase 0)
+ (should (string= erc-sasl-password "password123"))))))
+
+(ert-deftest erc-scenarios-sasl--external ()
+ :tags '(:expensive-test)
+ (erc-scenarios-common-with-cleanup
+ ((erc-scenarios-common-dialog "sasl")
+ (erc-server-flood-penalty 0.1)
+ (dumb-server (erc-d-run "localhost" t 'external))
+ (port (process-contact dumb-server :service))
+ (erc-modules (cons 'sasl erc-modules))
+ (erc-sasl-mechanism 'external)
+ (expect (erc-d-t-make-expecter)))
+
+ (ert-info ("Connect")
+ (with-current-buffer (erc :server "127.0.0.1"
+ :port port
+ :nick "tester"
+ :user "tester"
+ :full-name "tester")
+ (should (string= (buffer-name) (format "127.0.0.1:%d" port)))))
+
+ (ert-info ("Notices received")
+ (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "ExampleOrg"))
+ (funcall expect 10 "Authentication successful")
+ (funcall expect 10 "This server is in debug mode")))))
+
+(ert-deftest erc-scenarios-sasl--plain-fail ()
+ :tags '(:expensive-test)
+ (erc-scenarios-common-with-cleanup
+ ((erc-scenarios-common-dialog "sasl")
+ (erc-server-flood-penalty 0.1)
+ (dumb-server (erc-d-run "localhost" t 'plain-failed))
+ (port (process-contact dumb-server :service))
+ (erc-modules (cons 'sasl erc-modules))
+ (erc-sasl-password "wrong")
+ (erc-sasl-mechanism 'plain)
+ (expect (erc-d-t-make-expecter))
+ (buf nil))
+
+ (ert-info ("Connect")
+ (setq buf (erc :server "127.0.0.1"
+ :port port
+ :nick "tester"
+ :user "tester"
+ :full-name "tester"))
+ (let ((err (should-error
+ (with-current-buffer buf
+ (funcall expect 20 "Connection failed!")))))
+ (should (string-search "please review" (cadr err)))
+ (with-current-buffer buf
+ (funcall expect 10 "Opening connection")
+ (funcall expect 20 "SASL authentication failed")
+ (should-not (erc-server-process-alive)))))))
+
+(defun erc-scenarios--common--sasl (mech)
+ (erc-scenarios-common-with-cleanup
+ ((erc-scenarios-common-dialog "sasl")
+ (erc-server-flood-penalty 0.1)
+ (dumb-server (erc-d-run "localhost" t mech))
+ (port (process-contact dumb-server :service))
+ (erc-modules (cons 'sasl erc-modules))
+ (erc-sasl-user :nick)
+ (erc-sasl-mechanism mech)
+ (mock-rvs (list "c5RqLCZy0L4fGkKAZ0hujFBs" ""))
+ (sasl-unique-id-function (lambda () (pop mock-rvs)))
+ (expect (erc-d-t-make-expecter)))
+
+ (ert-info ("Connect")
+ (with-current-buffer (erc :server "127.0.0.1"
+ :port port
+ :nick "jilles"
+ :password "sesame"
+ :full-name "jilles")
+ (should (string= (buffer-name) (format "127.0.0.1:%d" port)))))
+
+ (ert-info ("Notices received")
+ (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "jaguar"))
+ (funcall expect 10 "Found your hostname")
+ (funcall expect 20 "marked as being away")))))
+
+(ert-deftest erc-scenarios-sasl--scram-sha-1 ()
+ :tags '(:expensive-test)
+ (let ((erc-sasl-authzid "jilles"))
+ (erc-scenarios--common--sasl 'scram-sha-1)))
+
+(ert-deftest erc-scenarios-sasl--scram-sha-256 ()
+ :tags '(:expensive-test)
+ (unless (featurep 'sasl-scram-sha256)
+ (ert-skip "Emacs lacks sasl-scram-sha256"))
+ (erc-scenarios--common--sasl 'scram-sha-256))
+
+;;; erc-scenarios-sasl.el ends here
diff --git a/test/lisp/erc/resources/sasl/external.eld b/test/lisp/erc/resources/sasl/external.eld
new file mode 100644
index 00000000000..2cd237ec4d4
--- /dev/null
+++ b/test/lisp/erc/resources/sasl/external.eld
@@ -0,0 +1,33 @@
+;; -*- mode: lisp-data; -*-
+((cap-req 10 "CAP REQ :sasl"))
+((nick 1 "NICK tester"))
+((user 1 "USER tester 0 * :tester"))
+
+((auth-req 3.2 "AUTHENTICATE EXTERNAL")
+ (0.0 ":irc.example.org CAP * ACK :sasl")
+ (0.0 "AUTHENTICATE +"))
+
+((auth-noop 3.2 "AUTHENTICATE +")
+ (0.0 ":irc.example.org 900 * * tester :You are now logged in as tester")
+ (0.0 ":irc.example.org 903 * :Authentication successful"))
+
+((cap-end 3.2 "CAP END")
+ (0.0 ":irc.example.org 001 tester :Welcome to the ExampleOrg IRC Network tester")
+ (0.01 ":irc.example.org 002 tester :Your host is irc.example.org, running version oragono-2.6.1")
+ (0.01 ":irc.example.org 003 tester :This server was created Sat, 17 Jul 2021 09:06:42 UTC")
+ (0.01 ":irc.example.org 004 tester irc.example.org oragono-2.6.1 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv")
+ (0.0 ":irc.example.org 005 tester AWAYLEN=200 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server")
+ (0.01 ":irc.example.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=ExampleOrg NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY :are supported by this server")
+ (0.01 ":irc.example.org 005 tester draft/CHATHISTORY=100 :are supported by this server")
+ (0.0 ":irc.example.org 251 tester :There are 1 users and 0 invisible on 1 server(s)")
+ (0.0 ":irc.example.org 252 tester 0 :IRC Operators online")
+ (0.0 ":irc.example.org 253 tester 0 :unregistered connections")
+ (0.0 ":irc.example.org 254 tester 0 :channels formed")
+ (0.0 ":irc.example.org 255 tester :I have 1 clients and 0 servers")
+ (0.0 ":irc.example.org 265 tester 1 1 :Current local users 1, max 1")
+ (0.21 ":irc.example.org 266 tester 1 1 :Current global users 1, max 1")
+ (0.0 ":irc.example.org 422 tester :MOTD File is missing"))
+
+((mode-user 1.2 "MODE tester +i")
+ (0.0 ":irc.example.org 221 tester +Zi")
+ (0.0 ":irc.example.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect."))
diff --git a/test/lisp/erc/resources/sasl/plain-failed.eld b/test/lisp/erc/resources/sasl/plain-failed.eld
new file mode 100644
index 00000000000..336700290c5
--- /dev/null
+++ b/test/lisp/erc/resources/sasl/plain-failed.eld
@@ -0,0 +1,16 @@
+;; -*- mode: lisp-data; -*-
+((cap-req 10 "CAP REQ :sasl"))
+((nick 1 "NICK tester"))
+((user 1 "USER tester 0 * :tester")
+ (0.0 ":irc.foonet.org NOTICE * :*** Looking up your hostname...")
+ (0.0 ":irc.foonet.org NOTICE * :*** Found your hostname")
+ (0.0 ":irc.foonet.org CAP * ACK :cap-notify sasl"))
+
+((authenticate-plain 3.2 "AUTHENTICATE PLAIN")
+ (0.0 ":irc.foonet.org AUTHENTICATE +"))
+
+((authenticate-gimme 3.2 "AUTHENTICATE AHRlc3RlcgB3cm9uZw==")
+ (0.0 ":irc.foonet.org 900 * * tester :You are now logged in as tester")
+ (0.0 ":irc.foonet.org 904 * :SASL authentication failed: Invalid account credentials"))
+
+((cap-end 3.2 "CAP END"))
diff --git a/test/lisp/erc/resources/sasl/plain.eld b/test/lisp/erc/resources/sasl/plain.eld
new file mode 100644
index 00000000000..1341cd78e5e
--- /dev/null
+++ b/test/lisp/erc/resources/sasl/plain.eld
@@ -0,0 +1,39 @@
+;; -*- mode: lisp-data; -*-
+((cap-req 10 "CAP REQ :sasl"))
+((nick 1 "NICK tester"))
+((user 1 "USER tester 0 * :tester")
+ (0.0 ":irc.example.org NOTICE * :*** Looking up your hostname...")
+ (0.0 ":irc.example.org NOTICE * :*** Found your hostname")
+ (0.0 ":irc.example.org CAP * ACK :sasl"))
+
+((authenticate-plain 3.2 "AUTHENTICATE PLAIN")
+ (0.0 ":irc.example.org AUTHENTICATE +"))
+
+((authenticate-gimme 3.2 "AUTHENTICATE AHRlc3RlcgBwYXNzd29yZDEyMw==")
+ (0.0 ":irc.example.org 900 * * tester :You are now logged in as tester")
+ (0.0 ":irc.example.org 903 * :Authentication successful"))
+
+((cap-end 3.2 "CAP END")
+ (0.0 ":irc.example.org 001 tester :Welcome to the ExampleOrg IRC Network tester")
+ (0.01 ":irc.example.org 002 tester :Your host is irc.example.org, running version oragono-2.6.1")
+ (0.01 ":irc.example.org 003 tester :This server was created Sat, 17 Jul 2021 09:06:42 UTC")
+ (0.01 ":irc.example.org 004 tester irc.example.org oragono-2.6.1 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv")
+ (0.0 ":irc.example.org 005 tester AWAYLEN=200 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server")
+ (0.01 ":irc.example.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=ExampleOrg NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY :are supported by this server")
+ (0.01 ":irc.example.org 005 tester draft/CHATHISTORY=100 :are supported by this server")
+ (0.0 ":irc.example.org 251 tester :There are 1 users and 0 invisible on 1 server(s)")
+ (0.0 ":irc.example.org 252 tester 0 :IRC Operators online")
+ (0.0 ":irc.example.org 253 tester 0 :unregistered connections")
+ (0.0 ":irc.example.org 254 tester 0 :channels formed")
+ (0.0 ":irc.example.org 255 tester :I have 1 clients and 0 servers")
+ (0.0 ":irc.example.org 265 tester 1 1 :Current local users 1, max 1")
+ (0.21 ":irc.example.org 266 tester 1 1 :Current global users 1, max 1")
+ (0.0 ":irc.example.org 422 tester :MOTD File is missing"))
+
+((mode-user 1.2 "MODE tester +i")
+ (0.0 ":irc.example.org 221 tester +Zi")
+ (0.0 ":irc.example.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect."))
+
+((quit 5 "QUIT :\2ERC\2")
+ (0 ":tester!~u@yuvqisyu7m7qs.irc QUIT :Quit"))
+((drop 1 DROP))
diff --git a/test/lisp/erc/resources/sasl/scram-sha-1.eld b/test/lisp/erc/resources/sasl/scram-sha-1.eld
new file mode 100644
index 00000000000..49980e9e12a
--- /dev/null
+++ b/test/lisp/erc/resources/sasl/scram-sha-1.eld
@@ -0,0 +1,47 @@
+;;; -*- mode: lisp-data -*-
+((cap-req 5.2 "CAP REQ :sasl"))
+((nick 10 "NICK jilles"))
+((user 10 "USER user 0 * :jilles")
+ (0 "NOTICE AUTH :*** Processing connection to jaguar.test")
+ (0 "NOTICE AUTH :*** Looking up your hostname...")
+ (0 "NOTICE AUTH :*** Checking Ident")
+ (0 "NOTICE AUTH :*** No Ident response")
+ (0 "NOTICE AUTH :*** Found your hostname")
+ (0 ":jaguar.test CAP jilles ACK :sasl"))
+
+((auth-init 10 "AUTHENTICATE SCRAM-SHA-1")
+ (0 "AUTHENTICATE +"))
+
+((auth-challenge 10 "AUTHENTICATE bixhPWppbGxlcyxuPWppbGxlcyxyPWM1UnFMQ1p5MEw0ZkdrS0FaMGh1akZCcw==")
+ (0 "AUTHENTICATE cj1jNVJxTENaeTBMNGZHa0tBWjBodWpGQnNYUW9LY2l2cUN3OWlEWlBTcGIscz01bUpPNmQ0cmpDbnNCVTFYLGk9NDA5Ng=="))
+
+((auth-final 10 "AUTHENTICATE Yz1iaXhoUFdwcGJHeGxjeXc9LHI9YzVScUxDWnkwTDRmR2tLQVowaHVqRkJzWFFvS2NpdnFDdzlpRFpQU3BiLHA9T1ZVaGdQdTh3RW0yY0RvVkxmYUh6VlVZUFdVPQ==")
+ (0 "AUTHENTICATE dj1aV1IyM2M5TUppcjBaZ2ZHZjVqRXRMT242Tmc9"))
+
+((auth-done 10 "AUTHENTICATE +")
+ (0 ":jaguar.test 900 jilles jilles!jilles@localhost.stack.nl jilles :You are now logged in as jilles")
+ (0 ":jaguar.test 903 jilles :SASL authentication successful"))
+
+((cap-end 10.2 "CAP END")
+ (0 ":jaguar.test 001 jilles :Welcome to the jaguar IRC Network jilles!~jilles@127.0.0.1")
+ (0 ":jaguar.test 002 jilles :Your host is jaguar.test, running version InspIRCd-3")
+ (0 ":jaguar.test 003 jilles :This server was created 09:44:05 Dec 24 2020")
+ (0 ":jaguar.test 004 jilles jaguar.test InspIRCd-3 BILRSWcghiorswz ABEFHIJLMNOQRSTXYabcefghijklmnopqrstuvz :BEFHIJLXYabefghjkloqv")
+ (0 ":jaguar.test 005 jilles ACCEPT=30 AWAYLEN=200 BOT=B CALLERID=g CASEMAPPING=rfc1459 CHANLIMIT=#:120 CHANMODES=IXbeg,k,BEFHJLfjl,AMNOQRSTcimnprstuz CHANNELLEN=64 CHANTYPES=# ELIST=CMNTU ESILENCE=CcdiNnPpTtx EXCEPTS=e :are supported by this server")
+ (0 ":jaguar.test 005 jilles EXTBAN=,ANOQRSTUacmnprz HOSTLEN=64 INVEX=I KEYLEN=32 KICKLEN=255 LINELEN=512 MAXLIST=I:100,X:100,b:100,e:100,g:100 MAXTARGETS=20 MODES=20 MONITOR=30 NAMELEN=128 NAMESX NETWORK=jaguar :are supported by this server")
+ (0 ":jaguar.test 005 jilles NICKLEN=31 PREFIX=(Yqaohv)!~&@%+ REMOVE SAFELIST SECURELIST=60 SILENCE=32 STATUSMSG=!~&@%+ TOPICLEN=307 UHNAMES USERIP USERLEN=11 USERMODES=,,s,BILRSWcghiorwz WATCH=30 :are supported by this server")
+ (0 ":jaguar.test 005 jilles :are supported by this server")
+ (0 ":jaguar.test 251 jilles :There are 740 users and 108 invisible on 11 servers")
+ (0 ":jaguar.test 252 jilles 10 :operator(s) online")
+ (0 ":jaguar.test 254 jilles 373 :channels formed")
+ (0 ":jaguar.test 255 jilles :I have 28 clients and 1 servers")
+ (0 ":jaguar.test 265 jilles :Current local users: 28 Max: 29")
+ (0 ":jaguar.test 266 jilles :Current global users: 848 Max: 879")
+ (0 ":jaguar.test 375 jilles :jaguar.test message of the day")
+ (0 ":jaguar.test 372 jilles : ~~ some message of the day ~~")
+ (0 ":jaguar.test 372 jilles : ~~ or rkpryyrag gb rnpu bgure ~~")
+ (0 ":jaguar.test 376 jilles :End of message of the day."))
+
+((mode-user 1.2 "MODE jilles +i")
+ (0 ":jilles!~jilles@127.0.0.1 MODE jilles :+ri")
+ (0 ":jaguar.test 306 jilles :You have been marked as being away"))
diff --git a/test/lisp/erc/resources/sasl/scram-sha-256.eld b/test/lisp/erc/resources/sasl/scram-sha-256.eld
new file mode 100644
index 00000000000..74de9a23ecf
--- /dev/null
+++ b/test/lisp/erc/resources/sasl/scram-sha-256.eld
@@ -0,0 +1,47 @@
+;;; -*- mode: lisp-data -*-
+((cap-req 5.2 "CAP REQ :sasl"))
+((nick 10 "NICK jilles"))
+((user 10 "USER user 0 * :jilles")
+ (0 "NOTICE AUTH :*** Processing connection to jaguar.test")
+ (0 "NOTICE AUTH :*** Looking up your hostname...")
+ (0 "NOTICE AUTH :*** Checking Ident")
+ (0 "NOTICE AUTH :*** No Ident response")
+ (0 "NOTICE AUTH :*** Found your hostname")
+ (0 ":jaguar.test CAP jilles ACK :sasl"))
+
+((auth-init 10 "AUTHENTICATE SCRAM-SHA-256")
+ (0 "AUTHENTICATE +"))
+
+((auth-challenge 10 "AUTHENTICATE biwsbj1qaWxsZXMscj1jNVJxTENaeTBMNGZHa0tBWjBodWpGQnM=")
+ (0 "AUTHENTICATE cj1jNVJxTENaeTBMNGZHa0tBWjBodWpGQnNkNDA2N2YwYWZkYjU0YzNkYmQ0ZmU2NDViODRjYWUzNyxzPVpUZzFNbUUxWW1GaFpHSTFORGN5TWprM056WXdabVJqWkRNM1kySTFPVE09LGk9NDA5Ng=="))
+
+((auth-final 10 "AUTHENTICATE Yz1iaXdzLHI9YzVScUxDWnkwTDRmR2tLQVowaHVqRkJzZDQwNjdmMGFmZGI1NGMzZGJkNGZlNjQ1Yjg0Y2FlMzcscD1MUDRzakpyakpLcDVxVHNBUnlaQ3BwWHBLTHU0Rk1NMjg0aE5FU1B2R2hJPQ==")
+ (0 "AUTHENTICATE dj04NDdXWGZubVJlR3lFMXFscTFBbmQ2UjRiUEJOUk9UWjdFTVMvUXJKdFVNPQ=="))
+
+((auth-done 10 "AUTHENTICATE +")
+ (0 ":jaguar.test 900 jilles jilles!jilles@localhost.stack.nl jilles :You are now logged in as jilles")
+ (0 ":jaguar.test 903 jilles :SASL authentication successful"))
+
+((cap-end 10.2 "CAP END")
+ (0 ":jaguar.test 001 jilles :Welcome to the jaguar IRC Network jilles!~jilles@127.0.0.1")
+ (0 ":jaguar.test 002 jilles :Your host is jaguar.test, running version InspIRCd-3")
+ (0 ":jaguar.test 003 jilles :This server was created 09:44:05 Dec 24 2020")
+ (0 ":jaguar.test 004 jilles jaguar.test InspIRCd-3 BILRSWcghiorswz ABEFHIJLMNOQRSTXYabcefghijklmnopqrstuvz :BEFHIJLXYabefghjkloqv")
+ (0 ":jaguar.test 005 jilles ACCEPT=30 AWAYLEN=200 BOT=B CALLERID=g CASEMAPPING=rfc1459 CHANLIMIT=#:120 CHANMODES=IXbeg,k,BEFHJLfjl,AMNOQRSTcimnprstuz CHANNELLEN=64 CHANTYPES=# ELIST=CMNTU ESILENCE=CcdiNnPpTtx EXCEPTS=e :are supported by this server")
+ (0 ":jaguar.test 005 jilles EXTBAN=,ANOQRSTUacmnprz HOSTLEN=64 INVEX=I KEYLEN=32 KICKLEN=255 LINELEN=512 MAXLIST=I:100,X:100,b:100,e:100,g:100 MAXTARGETS=20 MODES=20 MONITOR=30 NAMELEN=128 NAMESX NETWORK=jaguar :are supported by this server")
+ (0 ":jaguar.test 005 jilles NICKLEN=31 PREFIX=(Yqaohv)!~&@%+ REMOVE SAFELIST SECURELIST=60 SILENCE=32 STATUSMSG=!~&@%+ TOPICLEN=307 UHNAMES USERIP USERLEN=11 USERMODES=,,s,BILRSWcghiorwz WATCH=30 :are supported by this server")
+ (0 ":jaguar.test 005 jilles :are supported by this server")
+ (0 ":jaguar.test 251 jilles :There are 740 users and 108 invisible on 11 servers")
+ (0 ":jaguar.test 252 jilles 10 :operator(s) online")
+ (0 ":jaguar.test 254 jilles 373 :channels formed")
+ (0 ":jaguar.test 255 jilles :I have 28 clients and 1 servers")
+ (0 ":jaguar.test 265 jilles :Current local users: 28 Max: 29")
+ (0 ":jaguar.test 266 jilles :Current global users: 848 Max: 879")
+ (0 ":jaguar.test 375 jilles :jaguar.test message of the day")
+ (0 ":jaguar.test 372 jilles : ~~ some message of the day ~~")
+ (0 ":jaguar.test 372 jilles : ~~ or rkpryyrag gb rnpu bgure ~~")
+ (0 ":jaguar.test 376 jilles :End of message of the day."))
+
+((mode-user 1.2 "MODE jilles +i")
+ (0 ":jilles!~jilles@127.0.0.1 MODE jilles :+ri")
+ (0 ":jaguar.test 306 jilles :You have been marked as being away"))