#+title: Readme
#+author: Phil Bajsicki
#+options: finline toc:nil

* Intro
This is a coding task from [redacted], which I found
rather fun to tinker with.

This is an org-mode file. The code blocks are tangled using =org-babel= into =cl-secninja.lisp=.

* Dependencies:
- curl
- sbcl with quicklisp
* Usage:
1. Load in SBCL with ~sbcl --load cl-secninja.lisp~.
2. Run ~(four)~
3. Copy the base64 encrypted message into a file called =email=.
4. Run ~(base64)~.
5. Done.
* Code:
** Quicklisp:
#+begin_src lisp :tangle cl-secninja.lisp
(ql:quickload '(:cl-ppcre
				:cl-base64
				:ironclad
				:jsown))
#+end_src

** Defpackage and in-package
#+begin_src lisp :tangle cl-secninja.lisp
(defpackage #:cl-secninja
  (:use #:common-lisp #:cl #:uiop #:cl-ppcre  #:ironclad #:cl-base64))

(in-package #:cl-secninja)
#+end_src

** URL var
The trailing slash is important for consolidating the URL later.
#+begin_src lisp :tangle cl-secninja.lisp
(defvar *url* "redacted")
#+end_src
** Run cURL
#+begin_src lisp :tangle cl-secninja.lisp
(defun run-curl (command)
  (let* ((curl-command (concatenate 'string "curl -i " command))
    (result (uiop:run-program curl-command
                              :output :string
                              :error-output T)))
         result))

#+end_src

** First page
Grab first page, print out the command and result, and return the parameter string.
#+begin_src lisp :tangle cl-secninja.lisp
(defun one ()
  (let* ((command *url*)
		 (result (run-curl *url*)))
	(format t "~%COMMAND: curl ~a" command)
	(format t "~%RESULT: curl ~a" result)
	(car (ppcre:all-matches-as-strings "\\?.*" result))))
#+end_src
** Second page
This function calls ~(one)~, so calling it directly for seeing the second page
works.

Concatenate the URL with the parameter string from the first function, and grab
and return the second page.

#+begin_src lisp :tangle cl-secninja.lisp
(defun two ()
  (let* ((command (concatenate 'string
							   "\"" *url* (one) "\""))
		 (result (run-curl command)))
	result))
#+end_src
** Third
This is where we get interesting. This function calls ~(two)~, which returns the
second page for processing, and then calls ~(run-curl)~ on the command it
constructs, and returns the output.


I wanted to see what the values of the variables are while testing, thus the
print statements and triple let. I know I could consolidate that, but I feel
like this way is more transparent than having a ~(let* (...))~ that does
everything in the background.

The thing that was most tricky here was getting the double quotes to work
properly between this function and ~(run-curl)~, because the =command= string is
being passed around, and it itself includes quotes.



#+begin_src lisp :tangle cl-secninja.lisp
(defun three ()
  (let ((headers (ppcre:all-matches-as-strings "X-.*" (two))))
	(print headers)
	(print (car headers))
	(print (cadr headers))
	(let ((command (concatenate 'string
								"\"" *url* "\?step=2" "\""
								" -H \"" (car headers) "\""
								" -H \"" (cadr headers) "\"")))
	  (let ((result (run-curl command)))
		(format t "~%RESULT: curl ~a" result)
		result))))

#+end_src
** Four
This was the most fun one, given all the processing. I can't say that the loop
worked the first time around.

After running ~(four)~, your terminal will print out the base64 encoded string.
I can't say I want to mess with extracting the base64 string with another
regexp, so I just manually copied the string into a file called =email=.

Then...

#+begin_src lisp :tangle cl-secninja.lisp

(defun four ()
  (let*
	  ((data (three))
	   (input
		 (car (ppcre:all-matches-as-strings "(?sm)\{.*}$" data)))
	   (challenge
		 (string-left-trim "challenge: " (car (ppcre:all-matches-as-strings "challenge:(.*)" data))))
	   (timestamp
		 (string-left-trim "timestamp: " (car (ppcre:all-matches-as-strings "timestamp:(.*)" data))))
	   (sorted (sort (cdr (jsown:parse input)) #'string<= :key #'first))
	   (connected
		 (string-right-trim "\&"
							(format nil "~{~A~}"
									(loop
									  for item in sorted
									  collect
									  (concatenate 'string  (car item) "\=" (cdr item) "\&")))))
	   (hash (sha-256 connected))
	   (command
		 (string-right-trim "\-"
							(concatenate 'string
										 "-X POST \"" *url* "\?step=3" "\""
										 " -H \"Content-Type: application/x-www-form-urlencoded\" "
										 " -d \"challenge=" challenge "\""
										 " -d \"timestamp=" timestamp "\""
										 " -d \"hash=" hash "\""
										 ))))
	(terpri)
	(print challenge)
	(print timestamp)
	(print hash)
	(terpri)
	(format t "~%COMMAND: curl ~a" command)
	(terpri)
	(let ((result (run-curl command)))
	  (format t "~%RESULT: curl ~a" result))
	(terpri)))

#+end_src

*** Sha256
Util function for the above code.
#+begin_src lisp :tangle cl-secninja.lisp
(defun sha-256 (str)
  (ironclad:byte-array-to-hex-string
   (ironclad:digest-sequence :sha256
							 (ironclad:ascii-string-to-byte-array str))))

#+end_src
** Base64
Put the contents of the file into a variable.
#+begin_src lisp :tangle cl-secninja.lisp
(defvar *email* (alexandria:read-file-into-string "email"))
#+end_src
Then, run the base64 decoder over it until the string can't be decoded any more.
#+begin_src lisp :tangle cl-secninja.lisp
(defun base64 ()
  (let ((email *email*))
	(loop repeat 100 do
	  (if (ppcre:scan "@" email)
		  (print email)
		  (setf email (cl-base64:base64-string-to-string email))))))
#+end_src