From: Lucian Mogosanu Date: Mon, 2 Jul 2018 12:04:09 +0000 (+0300) Subject: posts: 071-075 X-Git-Tag: v0.11~118 X-Git-Url: https://git.mogosanu.ro/?a=commitdiff_plain;h=9799d63536f42f9e21eb1110af4c1492eca6c1d1;p=thetarpit.git posts: 071-075 --- diff --git a/posts/y04/071-cl-gpg.markdown b/posts/y04/071-cl-gpg.markdown new file mode 100644 index 0000000..c1468f6 --- /dev/null +++ b/posts/y04/071-cl-gpg.markdown @@ -0,0 +1,445 @@ +--- +postid: 071 +title: Interfacing Common Lisp programs with GPG the (nearly) painless way +date: April 27, 2018 +author: Lucian Mogoșanu +tags: tech +--- + +A short introduction, for the atechnical: Lisp is one of the definitory +programming languages for the fields of computer science and +engineering. Common Lisp is a particular instance of Lisp, designed at +some point in history as a unifying standard for various Lisp +dialects. GPG is for the time being the one and only swiss army knife of +cryptography, a tool that can aid one at identity (read: asymmetric key) +management, encryption, sealing[^1] and [many others][v]. Unfortunately, +in the last few years the integrity of GPG itself has been a target for +[the usual wreckers][koch] and thus [its days are numbered][ffa]. + +It very often happens in systems that two such seemingly unrelated +components need to be interfaced; more specifically, in our case we +would like to call GPG functionality from Common Lisp. "Traditionally" +-- whatever that might mean -- this is supposedly achieved through the +[GPGME][gpgme] library, which includes a set of Common Lisp bindings. + +I specifically added the "supposedly" above because I for one couldn't +achieve this feat. While GPGME compiles without errors on my systems, +the "language binding" glue code, written in Lisp and depending on +[CFFI][cffi], throws very weird type errors that I for one am not +willing to debug, especially given the general lack of documentation on +[Google's interwebs][https]. It may very well be the case that I am +technically incompetent. But I am rather willing to stick my hand in the +fire[^2] that GPGME makes dishonest assumptions about the version of +CFFI; and moreover, that both CFFI and GPGME (especially the latter) are +balls of [doesn't-fit-in-head][fits-in-head] that aren't worth half the +attention that I gave them. + +This blog post proposes a more elegant alternative for CL-GPG +interfacing, a. that makes minimal assumptions about the underlying +environment (e.g. Common Lisp implementation, GPG version), i.e. works +on any "modern" Common Lisp implementation running inside a Unix +system[^3]; and b. whose implementation fits in a blog post, and thus in +the reader's head. + +First, the assumptions. We assume that the CL implementation can launch +other Unix processes and communicate with them through pipes connecting +the usual [standard I/O streams][unix-stdio] (std{in,out,err}). The +astute reader may have already figured out that this approach is not +fundamentally different from the Bash/Perl approach of launching a new +process, feeding it input, then piping the output back to our script or +program. That is all there is to it. + +For this purpose we will define a primitive `lispy-run-program` +function, that can rely on whatever Unix process management primitives +the underlying Common Lisp implementation provides. The example below +uses [UIOP][uiop]'s `run-program` function. + +~~~~ {.commonlisp} +(defun lispy-run-program (command &optional (input "")) + "Run `command' with optional `input'. + +Returns two strings, representing what the command wrote on the standard +output and standard error respectively. The result (error code) of the +command is ignored." + (let ((stdout (make-string-output-stream)) + (stderr (make-string-output-stream))) + (with-input-from-string (stdin input) + (uiop:run-program command + :input stdin + :output stdout + :error-output stderr + :ignore-error-status t)) + (values (get-output-stream-string stdout) + (get-output-stream-string stderr)))) +~~~~ + +`lispy-run-program` takes as parameters a string `command` (the command +to be run) and an optional string `input` (the input to be piped to the +child process's standard input). It creates three (one input and two +output) Common Lisp streams: `stdin`, `stdout` and `stderr`, and passes +them to `run-program`. Then it "gets" the strings from the two output +streams and returns both of them. Also note that the return code of the +child process is ignored -- this can be changed by the programmer +howsowever he or she wishes. + +Let's try out a few invocations of `lispy-run-program`: + +~~~~ {.commonlisp} +; Listing /etc/sh* +CL-USER> (lispy-run-program "ls /etc/sh*") +"/etc/shadow +/etc/shells +" +"" + +; Listing a non-existing file +CL-USER> (lispy-run-program "ls /bla") +"" +"ls: cannot access '/bla': No such file or directory +" + +; The equivalent of "echo 1 2 3 4 | cut -d' ' -f3" +CL-USER> (lispy-run-program "cut -d' ' -f3" "1 2 3 4") +"3 +" +"" +~~~~ + +As seen above, this simple function puts a very powerful +[composition][pipes] tool directly at our fingertips. We will use it to +interface with the following set of GPG functionalities in Common Lisp: +encryption, decryption and signature verification. Note that all our +processing will be performed on plain-text [ASCII-armored][ascii-armor] +files, though the functions in this post can in principle be adapted to +use binary inputs/outputs. + +Looking at the [GPG man page][man-gpg], and judging by the known +behaviour of GPG, we notice that we can run it outside of a +terminal-based environment -- and thus obtain full control over its +input and output -- by passing the `--no-tty` argument. Thus, encryption +can for example be written as: + +~~~~ {.commonlisp} +(defun gpg-encrypt (input recipient) + "Encrypt `input' to `recipient'." + (lispy-run-program + (format nil "/path/to/gpg --no-tty -ea -r ~a" recipient) + input)) +~~~~ + +Where `/path/to/gpg` is the path to the GPG executable inside the Unix +file system. Similarly, decryption: + +~~~~ {.commonlisp} +(defun gpg-decrypt (input) + "Decrypt ASCII-armored `input'." + (lispy-run-program "/path/to/gpg --no-tty -d" input)) +~~~~ + +Similarly, clearsigning (not-quite-properly named "seal" here): + +~~~~ {.commonlisp} +(defun gpg-seal (input &optional (uid "")) + "Make a cleartext signature of `input'. + +If `uid' is provided, the input will be signed using that specific key." + (let ((uid-flag (if (string/= "" uid) + (format nil "-u ~a" uid) + ""))) + (lispy-run-program + (format nil "/path/to/gpg --no-tty --clearsign ~a" uid-flag) + input))) +~~~~ + +Notice that `gpg-seal` gets an optional `uid` parameter that can be used +to sign as a specific key (using GPG's `-u` flag). + +Finally, seal verification: + +~~~~ {.commonlisp} +(defun gpg-verify-seal (input) + "Verify cleartext-signed `input'." + (lispy-run-program "/path/to/gpg --no-tty --verify" input)) +~~~~ + +Now let's test our functions. First, we create a test key: + +~~~~ +$ gpg --gen-key + +# usual GnuPG key generation follows +# ... + +gpg: key 48BECFE5 marked as ultimately trusted +public and secret key created and signed. + +pub 4096R/48BECFE5 2018-04-23 + Key fingerprint = 533B 174D 1962 36B1 C066 670F CDD8 A167 48BE CFE5 +uid Tarpiter +sub 4096R/CF9F3670 2018-04-23 +~~~~ + +And now back in the Lisp console, we play with the four +functions. First, encryption and decryption. + +~~~~ {.commonlisp} +CL-USER> (defvar *secret*) +*SECRET* +; Encrypt the string "a very sikrit test" to tarpiter's key and store it +; in `*secret*'. +CL-USER> (setq *secret* (gpg-encrypt "a very sikrit text" "tarpiter")) +"-----BEGIN PGP MESSAGE----- + +hQIMAzPNTIDPnzZwAQ//X+f2PGhEbouCYSxtaOXfY4XQU6vFjlts63PEM+5qD5b3 +dSPVVJ+wjLZOK97eB0WGAqnGHRmIxpjr1vHVwK/b/uh+KPWTrCLbWZscGoSIUxrG +GJpsQLmRJx4NEStDhjZ1+PwFod0aHqFJ32chiP4bTQfKt1vLDi0Cs7eEDAZzaBXV +UrEa2t+IfY6BpOn4SUfMPJZDBnK+b1n7QV0gpRCp+x1qrf4Sun/PD7PuUy9zy66i ++HcVWnju4tEdpA7sNV/nsHHUzaDlVriJkFOTDNOvs4n8ku7Zvcl6GQjPFvrFYRo3 +e/G7+1GxphyWZN6ARd3PNybMAopZ+FlhwYHIvfGJgt8E3p9HgrHR18RQt1WadJF1 +NAYlVcF2LkY8qj4wdGwdhHnbDhuu8w+OsTF+RZS7c5FbtEKYaUgIT1lhiU5iPpkL +tzwbVXNn6VOCcG3O65yaxAxf6RFO1vSo1hcB0xRDBJeMhhBeU+bJt9mVIQDV7FKt +2mZQstZXOOFLT3CfIE4A0Nbe4F6/KYg+tImptnWtG2XP2a4RR39D4uAXV5mHpyDI +FkcmyK7Qw2gfVV5slxmRZdzna3dkCe9MQUZLj0oD5sAXAJlXroBWOG0yyJHKCgnN +AEhq4HEksMrQLkAVmpMIw4pYSLwxI6rlIJdWqDGUJICYj6qRh/OQPA9p0dclZJ/S +TQH9UnRvNzfdCaitQrEndwe1l7BjZtHJYAT/3AFVTJU4nC9kSNFqLVYH71itGEr8 +5RhiNKHkdbNzb6HLmmuiZoQVzy+C60ofBy+F2ilR +=s/mJ +-----END PGP MESSAGE----- +" +; Decrypt the encrypted content of `*secret*'. +CL-USER> (gpg-decrypt *secret*) +"a very sikrit text" +"gpg: encrypted with 4096-bit RSA key, ID CF9F3670, created 2018-04-23 + \"Tarpiter \" +" +~~~~ + +Then, sealing and verification: + +~~~~ {.commonlisp} +CL-USER> (defvar *clearsigned*) +*CLEARSIGNED* +; Clearsign the string "a very authentic text" and store it in +; `*clearsigned*'. +CL-USER> (setq *clearsigned* (gpg-seal "a very authentic text" "tarpiter")) +"-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +a very authentic text +-----BEGIN PGP SIGNATURE----- + +iQIcBAEBCgAGBQJa3jHGAAoJEM3YoWdIvs/leKYQALexMI0Md83qYwnvcjnxmqSw +H7hGJWGir8UCBtR3nmzVUTj3dybwclwpkeAdbCMPlwfOuLtVlEi3u122t1fphq8g +TJw+2OMWeX0A5TdhD/t9ZXXy9yS7QjOgEyRhzORO+Lsb4lPf7FOdKKXLpFfihmkv +7Ew8IXoEUmvgcb0bEs5dX4Y7DKt6M9v0xdpUb9qrRa+Cp6qqwIpc+FCKq58dMGyA +SL41hswKCXSnhrdVVi97QzJKyj3QJ33OdyNfXjM12AbFEDHQHkYYtKYk5DiH82lS +lRVAIOwpD4xu7DdbUTRQJcQcBTJlN8RDCjwRx9E++ebzi/Mm5+8nXiiVyp2EQ4nk +AiUEoTIoWUAouPVzj9fdxijejKx/yQmRegRR9drTJkZ3cX5YYbiiILMll0gNPBYw +z4YMweLlpf6OyqdtlgwYym8nWEGOYmDA468h4NiEdzwaFEgoi5gzL5abHYnhoNLx +q+GVHE5imblo4to/iDnv6lhVKcU4IwhDzw9Ku9WtaXa8gOYcdKyru5PQm1EogpCG +kABFlikrvtPjg7RCJBpR1m/EmQW4t1BeOLyTk7Sc5RVimHc2V7C6cSmATHeDlLIY +qWYafbdRSo/b+bbHU9c47KSlaYhpElbmY7fj3uv6dUvKMBSo+i+8U3BBeNXF5O5N +zNIemhVEmp8cnnGY9Jf9 +=B6SZ +-----END PGP SIGNATURE----- +" +; Verify the content and the seal in `*clearsigned*'. +CL-USER> (gpg-verify-seal *clearsigned*) +"" +"gpg: Signature made Mon 23 Apr 2018 22:19:34 EEST using RSA key ID 48BECFE5 +gpg: Good signature from \"Tarpiter \" +" +; Modify a character in the clearsigned payload. +CL-USER> (aref *clearsigned* 62) +#\t +CL-USER> (setf (aref *clearsigned* 62) #\c) +#\c +CL-USER> *clearsigned* +"-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +a very authencic text +-----BEGIN PGP SIGNATURE----- + +iQIcBAEBCgAGBQJa3jHGAAoJEM3YoWdIvs/leKYQALexMI0Md83qYwnvcjnxmqSw +H7hGJWGir8UCBtR3nmzVUTj3dybwclwpkeAdbCMPlwfOuLtVlEi3u122t1fphq8g +TJw+2OMWeX0A5TdhD/t9ZXXy9yS7QjOgEyRhzORO+Lsb4lPf7FOdKKXLpFfihmkv +7Ew8IXoEUmvgcb0bEs5dX4Y7DKt6M9v0xdpUb9qrRa+Cp6qqwIpc+FCKq58dMGyA +SL41hswKCXSnhrdVVi97QzJKyj3QJ33OdyNfXjM12AbFEDHQHkYYtKYk5DiH82lS +lRVAIOwpD4xu7DdbUTRQJcQcBTJlN8RDCjwRx9E++ebzi/Mm5+8nXiiVyp2EQ4nk +AiUEoTIoWUAouPVzj9fdxijejKx/yQmRegRR9drTJkZ3cX5YYbiiILMll0gNPBYw +z4YMweLlpf6OyqdtlgwYym8nWEGOYmDA468h4NiEdzwaFEgoi5gzL5abHYnhoNLx +q+GVHE5imblo4to/iDnv6lhVKcU4IwhDzw9Ku9WtaXa8gOYcdKyru5PQm1EogpCG +kABFlikrvtPjg7RCJBpR1m/EmQW4t1BeOLyTk7Sc5RVimHc2V7C6cSmATHeDlLIY +qWYafbdRSo/b+bbHU9c47KSlaYhpElbmY7fj3uv6dUvKMBSo+i+8U3BBeNXF5O5N +zNIemhVEmp8cnnGY9Jf9 +=B6SZ +-----END PGP SIGNATURE----- +" +; Verify it again. +CL-USER> (gpg-verify-seal *clearsigned*) +"" +"gpg: Signature made Mon 23 Apr 2018 22:19:34 EEST using RSA key ID 48BECFE5 +gpg: BAD signature from \"Tarpiter \" +" +~~~~ + +and so on. + +Finally, some considerations. First, the functions presented above +assume that the `recipient` and `uid` arguments -- or for that matter +any other arguments that might be passed from the program to the command +line -- are either trusted or pre-processed by the programmer. Consider +the following common example: + +~~~~ {.commonlisp} +; We seal to tarpiter, but in the process also execute an arbitrary +; command. +CL-USER> (gpg-seal "" "tarpiter; ls /etc/passwd") +"*snip'ed GPG output* +/etc/passwd +" +"" +~~~~ + +There is no way to avoid this other than by doing very strict +parsing/sanitization on the inputs, which is outside the scope of this +article. It's also worth mentioning that some classes of problems, +e.g. [deedbot][deedbot] authentication[^4], use (constant) +programmer-defined sensitive inputs, and thus are not susceptible to +this leaky abstraction problem. + +A similar issue is related to passphrase processing, assuming that the +programmer uses such keys. For this purpose, GPG provides the +`--passphrase*` class of command-line flags, of which `--passphrase-fd` +is the sanest, as it can map to an explicit communication pipe between +the two processes; on the other hand, `--passphrase` should be avoided +at all costs, as anyone with the ability to execute e.g. `ps` can see +the command-line of processes in the system. + +Additionally, we notice that our functions do not process the output in +any way, relying on the programmer for e.g. error checking or automation +of signature verification, the latter being crucial to the correct +implementation of [V][v]. + +Finally, the reader has probably noticed that there is a lot of +functionality missing from these examples. There are many ways to use +GPG; for example the programmer may need to verify detached signatures +instead of the clearsigned variety that we've provided. It would be +pointless and potentially dangerous to attempt devising a general +interface to GPG using this approach, which is maybe the only aspect +giving GPGME an advantage. That aside, the examples above can be +extended without much hassle to work with most of the well-known GPG +recipes. + +Of course, I'm not the first to have thought of this. [v.pl][v.pl] +interfaces with GPG this way, albeit from Perl rather than Common +Lisp. Moreover I've found out that [Andrew][esthlos] has written a Lisp +V that uses SBCL's `run-program` in pretty much the same way as +described here. I'm just adding my report to the journal in the hope +that it'll be useful to other people attempting similar things in the +future. + +**Update 1**: Trinque has [brought to my attention][trinque-comment], +and phf has [followed up][phf-comment], that sending command-line inputs +via `format` is very bad practice. In particular UIOP provides a +somewhat saner approach to shell command passing, via `escape-sh-token` +and similar functions. For example we can have `uiop:run-program` take a +list of tokens instead of a single string, e.g. `("/bin/ls" "-l" +"/")`. Our `lispy-run-program` then becomes: + +~~~~ {.commonlisp} +(defun lispy-run-program (command &optional (input "")) + "Run `command' with optional `input'. + +`command' is a list whose first element is the name of the program to be +executed and the rest of the elements are each a command-line argument +to be passed to the program. + +Returns two strings, representing what the command wrote on the standard +output and standard error respectively. The result (error code) of the +command is ignored." + (check-type command list) + (let ((stdout (make-string-output-stream)) + (stderr (make-string-output-stream))) + (with-input-from-string (stdin input) + (uiop:run-program command + :input stdin + :output stdout + :error-output stderr + :ignore-error-status t)) + (values (get-output-stream-string stdout) + (get-output-stream-string stderr)))) +~~~~ + +and `gpg-encrypt` and `gpg-decrypt` are implemented as: + +~~~~ {.commonlisp} +(defun gpg-encrypt (input recipient) + "Encrypt `input' to `recipient'." + (lispy-run-program + (list "/path/to/gpg" "--no-tty" "-ea" "-r" recipient) + input)) + +(defun gpg-decrypt (input) + "Decrypt ASCII-armored `input'." + (lispy-run-program + (list "/path/to/gpg" "--no-tty" "-d") + input)) +~~~~ + +A reimplementation of the GPG interface using UIOP is provided in +[gpg-uiop.lisp][gpg-uiop.lisp]. The reader is encouraged to read them +and try them out, e.g. against the examples above. + +**Update 2**: Trinque and phf [also][trinque-comment2] +[comment][phf-comment2] that UIOP, or rather ASDF3's inclusion of said +library, is generally ill-regarded due to the former's inclusion as an +implicit dependency. An implementation of `lispy-run-program` using +SBCL's own `run-program` is provided in [gpg-sbcl.lisp][gpg-sbcl.lisp] +for the reader to inspect. Note that the GPG functions using it remain +the same as gpg-uiop.lisp. Also note that `sb-ext:run-program` is by +default stricter than UIOP's, in that it requires the full path of the +program to be executed, `PATH` (and other environment variables) +requiring explicit specification. + +[^1]: A brief yet very useful discussion on the subject of sealing is + given by Nick Szabo's "[The Playdough Protocols][sealing]". + +[^2]: "A băga mâna în foc că [...]". Romanian expression, connoting, as + the astute reader might intuit, the willingness to bet one's ass on + the veracity of the statement that follows it. + + Since I'm doing Romanian-English translations, I might as well give + these short expressions a shot, so expect to see more of them in the + future. + +[^3]: Though I don't see why this approach wouldn't work with any + "non-modern" Common Lisp implementations running on a Unix machine, + as long as the former can access the latter's functionality. + +[^4]: Which incidentally is the problem I was working on that caused me + to trip the GPGME landmine. + +[sealing]: http://archive.is/9i7mx +[v]: /posts/y04/069-on-intellectual-ownership.html#selection-39.0-43.14 +[koch]: http://trilema.com/2016/werner-koch-lies/ +[ffa]: http://www.loper-os.org/?cat=49 +[gpgme]: http://archive.is/AHhfP +[cffi]: http://archive.is/Xr6V4 +[https]: /posts/y03/05b-https-war-declaration.html +[fits-in-head]: http://btcbase.org/log-search?q=fits+in+head +[unix-stdio]: http://archive.is/FGN6n +[uiop]: http://quickdocs.org/uiop/ +[pipes]: /posts/y00/00d-composition-operators-pipes.html +[ascii-armor]: https://archive.is/h0o1u#selection-3411.503-3421.0 +[man-gpg]: http://archive.is/kj2Bv +[deedbot]: http://deedbot.org/ +[v.pl]: http://thebitcoin.foundation/index.html +[esthlos]: http://www.esthlos.com/posts/2018/03/17/1.html +[trinque-comment]: http://btcbase.org/log/2018-04-27#1805898 +[phf-comment]: http://btcbase.org/log/2018-04-27#1805919 +[trinque-comment2]: http://btcbase.org/log/2018-04-27#1805904 +[phf-comment2]: http://btcbase.org/log/2018-04-27#1805912 +[gpg-uiop.lisp]: /uploads/2018/04/gpg-uiop.lisp +[gpg-sbcl.lisp]: /uploads/2018/04/gpg-sbcl.lisp diff --git a/posts/y04/072-trilemabot-i.markdown b/posts/y04/072-trilemabot-i.markdown new file mode 100644 index 0000000..d32f006 --- /dev/null +++ b/posts/y04/072-trilemabot-i.markdown @@ -0,0 +1,117 @@ +--- +postid: 072 +title: trilemabot [i]: introduction and self-voicing patch proposal +date: June 5, 2018 +author: Lucian Mogoșanu +tags: tech +--- + +First, the rationale and scope definition. In an ideal, purely platonic +world, trilemabot represents a perfect cut of the points in the +[#trilema bot spec][trilema-bot-spec] that are common to all #trilema +bots, past, present and future. In practice, basing his or her bot upon +the Republican [ircbot][ircbot] standard, an operator could be served by +some or all of the following pieces of scaffolding: self-voicing, a +means to implement prefixed commands, querying [deedbot][deedbot] to get +[WoT][wot] ratings or perform automated payments, and others -- the +list, I suspect, is far from exhaustive. This is where trilemabot should +prove itself useful. + +Following discussions in [the forum][btcbase-1819034], this post +presents a first patch proposal for trilemabot, that adds self-voicing +on top of ircbot. **Please note** that this proposal is at the moment merely **a +draft**: the final patch will be posted here after review by the +Lordship, and as a regrind *on top* of ircbot[^1]. + +Now, without further ado, the README: + +~~~~ +`trilemabot` extends `ircbot` with #trilema-specific functionality. See the +following for more details: + + * http://trilema.com/2016/how-to-participate-in-the-affairs-of-the-most-serene-republic/ + * http://trilema.com/2016/trilema-bot-spec/ + * http://thetarpit.org/posts/y04/072-trilemabot-i.html + +Currently the following functionalities are implemented: + + * self-voicing with deedbot, using a list of pre-provided OTPs +~~~~ + +INSTALL: + +~~~~ + * Install and load cl-irc and its dependencies + + * Install ircbot + + * Use V to press `trilemabot` + +mkdir -p ~/src/trilemabot +cd ~/src/trilemabot + +mkdir .wot +cd .wot && wget http://lucian.mogosanu.ro/spyked.asc && cd .. + +v.pl init http://lucian.mogosanu.ro/src/trilemabot/ +v.pl press trilemabot-voicer trilemabot-voicer.vpatch + + * Load `trilemabot` via Quicklisp or directly using ASDF: + +(dolist (path '("/path/to/ircbot/" "~/src/trilemabot/trilemabot-voicer/")) + (pushnew path asdf:*central-registry* :test #'string=)) +(asdf:load-system :trilemabot) +~~~~ + +And last but not least, USAGE: + +~~~~ {.commonlisp} +(asdf:load-system :trilemabot) +(defvar *bot*) +(setq *bot* + (trlb:make-trilemabot + "chat.freenode.net" 6667 "nick" "password" '("#trilema"))) + +;; connect in separate thread, returning thread +(ircbot:ircbot-connect-thread *bot*) + +;;;; 1. Self-voicing + +;; ask for n !!up OTPs from deedbot +(loop for i from 1 to n do + (trlb:trilemabot-send-up *bot*) + (sleep 0.5)) + +;; get messages received from deedbot +(trlb:trilemabot-inbox *bot*) + +;; add decrypted !!up OTPs +(trlb:trilemabot-add-voice-otps *bot* + "decrypted-otp-1" "decrypted-otp2" ... "decrypted-otpn") + +;; get the list of !!up OTPs +(trlb:trilemabot-voice-otp-stash *bot*) + +;; save !!up OTPs to disk +(trlb:trilemabot-save-voice-otp-stash *bot* "voice-otps.sexp") + +;; self-voice +(trlb:trilemabot-voice *bot*) + +;; if `voice-otp-stash' is not empty, the bot will automatically +;; self-voice on reconnect +(ircbot:ircbot-reconnect *bot*) +~~~~ + +[^1]: At the time of writing this is not possible because ircbot and + trilemabot are separate modules, and they absolutely require a + [manifest][manifest] as the binder that puts them in the same [V][v] + tree. + +[trilema-bot-spec]: http://trilema.com/2016/trilema-bot-spec/ +[ircbot]: http://trinque.org/2016/08/10/ircbot-genesis/ +[deedbot]: http://deedbot.org/help.html +[wot]: http://trilema.com/2014/what-the-wot-is-for-how-it-works-and-how-to-use-it/ +[btcbase-1819034]: http://btcbase.org/log/2018-05-25#1819034 +[manifest]: http://trinque.org/2018/06/02/v-manifest-specification/ +[v]: http://cascadianhacker.com/07_v-tronics-101-a-gentle-introduction-to-the-most-serene-republic-of-bitcoins-cryptographically-backed-version-control-system diff --git a/posts/y04/073-prikoke.markdown b/posts/y04/073-prikoke.markdown new file mode 100644 index 0000000..1b97394 --- /dev/null +++ b/posts/y04/073-prikoke.markdown @@ -0,0 +1,126 @@ +--- +postid: 073 +title: The story of Prikoke +date: June 12, 2018 +author: Lucian Mogoșanu +tags: storytime +--- + +*Below lies a translation of Prikoke, an animated fable from Planeta +Moldova. The original is still available somewhere on +[the youtubes][youtube], grab it while you still can.* + +**Narrator-raven** [standing on the roof of a pigsty, in a wintery +setting]: This is the sty of Prikoke, the happy piglet. The two-legged +beasts tend him and feed him with steaming swill a few times a day, and +Prikoke is extremely pleased with his life -- caw-caw! Lately, the +two-legged beasts have visited him more often than usual, talking +somethings about hundreds of kilograms, about lard, puddings and +sausages. This got Prikoke thinking. + +Inside the pigsty, Prikoke. + +**Prikoke**: I wonder what these beasts have in store for me? Maybe they +want to swill me more. Hm... But I can't eat more. Or maybe they want to +set me free, I believe -- why else would they feed me so much? They want +me to be strong so I can romp about carefree and happy. These two-legged +beasts have such a big heart, bless them. + +**Narrator-raven**: Caw-caw! This is Mefi[^1], the cat from the +two-legged beasts' household. + +**Mefi**: Meow! Bless the two-legged beasts, you say? Meow... You believe +they have a big heart? You silly Prikoke! + +**Prikoke**: What do you mean, silly, Mefi? I'm the happiest pig alive. I +have everything I need. +**Mefi**: Meow! Everything you need? Heheh... Look at you, Prikoke, you're +as naive as you're fat. Listen to me, Prikoke. I don't mean you any +harm. I don't want to see you end up like those other ones. + +**Prikoke**: What other ones? +**Mefi**: Prikoke, you have no idea how many like you have lived here in +this rotten shack. Pinkers, Jointy, Curly, Snouter -- oh! how many like +you wound up in jellies and soups, in sausages, pasties, scrapples and +steaks -- all of them ended up in the two-legged beasts' bellies. + +**Prikoke**: Huh, what do you mean? That the beasts are evil? You think +they want to eat me? [Upset.] Get outta here with your lies. The beasts +are my friends: they scratch me behind my ears, they talk to me, they +laugh, give me smiles and swill me; their children pet me and indulge me +with their sweet words. But you, you sly beast, you come here to stir me +up against them? + +**Mefi**: Meow! Prikoke! Listen carefully! Look around you! Don't you +see you're living in filth up your neck? What do you think? that the +refuse they give you they tear out from their liver[^2]? They give you +only scraps; if you only saw what they eat! But I don't want to spin it +out; we're out of time, tomorrow's a big feast and I came here to +help. Meow! + +**Prikoke**: Oink-oink! Help me? Help me with what? +**Mefi**: Meow! Listen to me carefully, Prikoke. Make an effort and get +up; go to that wall at the end of the shack and rush towards it with all +you have. This shack is rotten by now, Prikoke. And you, with your +weight, will break through the wall like butter. And don't fear, you +need to be brave! That's the only way you can escape. But then you'll +run into the woods, to freedom. There you'll become friends with the +wild swine. They'll show you what to eat, how to live, and you'll live +happily ever after. Listen well to what I'm saying, Prikoke. + +**Prikoke**: Huh, freedom, you say? But what, I don't have freedom? I +have everything I need here! Food, a home, a bed... +**Mefi**: All you want my ass! You know nothing of this world, Prikoke. You +never had anything, so you think you have everything. +**Prikoke**: But more than this I don't even need! I'm satisfied with what +I have! + +**Mefi**: Meow! Satisfied? Tomorrow you'll be satisfied! with the skewer in +your heart! +**Prikoke**: With what in my heart? + +**Mefi**: Oh, I'll shut up... or I'll scare you. Better do as I said, +Prikoke! Come, fast! Budge that huge ass of yours and rush towards +freedom! Go, Prikoke, I know you can. Hurry and break this filthy +shack! Run to your freedom! + +While the cat tries to convince him, Prikoke gets up slowly. Meanwhile, +the pig notices the swill trough and heads that way. + +**Mefi**: [Perplexed.] Meow! + +**Prikoke**: Oink-oink! [Eating.] Mefi... So, come to me tomorrow and +we'll talk, yes? 'Cause I have some beet with bread to finish up. Want +some? +**Mefi**: [Sighs.] Prikoke... Well, then. Go, then. Whatever, goodbye. By +now, who knows if we'll ever see each other. In other words, farewell, +Prikoke, and merry Christmas! + +The cat goes away. We're back on the roof of the pigsty, with the +narrator-raven speaking. + +**Narrator-raven**: Caw-caw! Well, this is the story of Prikoke. Now +I'll leave you to draw the conclusion yourself -- I'll go search for an +earthworm 'cause I'm hungry. + +Narrator-raven flies. The view goes down towards the shack, the shadow +of a two-legged beast looming over its door. While the door opens and +the view moves at the border of the yard, sounds of sharpening knives +can be heard in the background. + +That's all. + +[^1]: Some obscure transcripts on the web erroneously call him + Nefi. It is quite obvious that the cat depicted herein is a + mephistophelian agent; thus, Mefi. + +[^2]: The original reads, I quote: "Miau! Prikoke! Ascultă-mă cu + atenție! Uite în jurul tău! Tu nu vezi că trăiești pân' la gât în + mizerie? Ce crezi tu? că lăturile care ți le dau ei le rup de la + sufletul lor?" Upon careful consideration and council with people + much better than me at this English thing, I have come to the + conclusion that that last bit is not really translatable. So I took + the liberty to place the two-legged-beastly soul in their + liver. This makes perfect sense, doesn't it? + +[youtube]: https://www.youtube.com/watch?v=aerj4qXPHJc diff --git a/posts/y04/074-adalisp-prototype.markdown b/posts/y04/074-adalisp-prototype.markdown new file mode 100644 index 0000000..2d6a067 --- /dev/null +++ b/posts/y04/074-adalisp-prototype.markdown @@ -0,0 +1,114 @@ +--- +postid: 074 +title: An early Lisp scriptlang prototype in Ada +date: June 24, 2018 +author: Lucian Mogoșanu +tags: tech +--- + +This document describes the result of my attempts at implementing a Lisp +(more precisely a Scheme) scripting language in Ada. The effort spawned +from numerous discussions in the logs[^1] and used a few important +references[^2]. In short, the goal is to have a Lisp interpreter that is +a. small, easy to read, i.e. fits-in-head; and b. written in a sane +language, sans pointers and other cockroaches inhabiting today's +[broken computing machines][software-engineering]. + +I have attached below: + +* a tarball: [adalisp-proto.tar.gz][adalisp-proto.tar.gz]; and +* a detached signature by yours truly: + [adalisp-proto.tar.gz.spyked.sig][adalisp-proto.tar.gz.spyked.sig]. + +The source code is organized as follows: + +* a set of restrictions for the implementation (`restrict.adc`)[^3]; +* a Lisp machine representation in Ada (`lispm.{ads,adb}`); +* a S-expression parser (`parser.{ads,adb}`); +* a Scheme evaluator (`evaler.{ads,adb}`); and finally, +* some glue that puts everything together in a Read-Eval-Print Loop + (`test_repl.adb`). + +Additionally, the tarball includes a GPRbuild project file and a small +set of tests. To compile and run the tests, run in the project +directory: + +~~~~ +$ gprbuild +$ cat test.scm | ./bin/test_repl +~~~~ + +In the end, some comments about the implementation: + +[i] The implementation is incomplete. A lot of functionality is still +unimplemented, e.g. `<`, `>`, `gensym`, quasiquotes, `let*`, +`letrec`. This is just a matter of adding them into the `evaler`. + +[ii] The implementation is incomplete also because it has no mechanism +for recovery from parser and evaler errors, etc. + +[iii] Reading from standard input is utterly broken in many ways, +because Ada's `Get_Char` doesn't know how to read newlines among +others. These should be replaced with FFA's [I/O routines][ffacalc-io]. + +[iv] The author's cockroaches led him to implement Scheme booleans +(`#t`, `#f`) as a subset of integers. These are traditionally +symbols. On another note, it's not immediately clear that schemisms such +as `#t`/`#f` are relevant at all, one might as well use `t`/`nil` as +truth values. + +[vi] The code should be refactored in more than one direction. One +important direction is separating all the mechanisms in a `libadalisp` +to be used for embedding into other applications. Another direction +involves improving readability by e.g. using keyword arguments instead +of the positional ones that are so popular in C programs. But more +importantly, the user should be able to swallow the program piece by +piece, as is healthy and has been [demonstrated before][ffa-ch1]. + +[vii] I suspect the `evaler` is [far from correct][logs-evaluation], but +this can only be evaluated [sic!] after the completion of point vi +above. + +[viii] Both the parser[^4] and printer can be replaced with `lispm` +bytecode that gets loaded at the beginning of the world and evaluated as +needed. The current `parser` module could be possibly used to bootstrap +the bytecode generation process. + +[ix] Currently, `lispm` memory has a fixed static size. As +[FFACalc][ffacalc-stack] demonstrates, the heap size can be passed as a +command-line argument and initialized when the program boots. This gives +the user some flexibility in choosing how much memory their Lisp program +can use, especially given the fact that there is no garbage collection +implemented. + +[x] The list is far from exhaustive. Some aspects are already being +discussed in the logs; other threads will inevitably arise. + +[^1]: See for example: [1][logs-adalisp], [2][logs-ada-scheme], + [3][logs-ada-lisp]. + +[^2]: Kogge's "The Architecture of Symbolic Computers", Queinnec's "Lisp + in Small Pieces", (in some cases) the [tinyscheme][tinyscheme] + implementation, the [FFA][ffa] implementation and last but not + least, the [Ada reference manual][ada]. + +[^3]: For more details, consult the [FFA][ffa] series, in particular the + [first chapter][ffa-ch1]. + +[^4]: Discussion in the logs: [1][logs-bytecode], [2][logs-bc-detailed]. + +[logs-adalisp]: http://btcbase.org/log-search?q=adalisp +[logs-ada-scheme]: http://btcbase.org/log-search?q=ada+scheme +[logs-ada-lisp]: http://btcbase.org/log-search?q=ada+lisp +[tinyscheme]: http://tinyscheme.sourceforge.net/ +[ffa]: http://www.loper-os.org/?cat=49 +[ffa-ch1]: http://www.loper-os.org/?p=1913 +[ada]: http://ada-auth.org/standards/ada12.html +[software-engineering]: /posts/y02/03c-the-myth-of-software-engineering.html +[adalisp-proto.tar.gz]: /uploads/2018/06/adalisp-proto.tar.gz +[adalisp-proto.tar.gz.spyked.sig]: /uploads/2018/06/adalisp-proto.tar.gz.spyked.sig +[ffacalc-io]: http://btcbase.org/patches/ffa_ch4_ffacalc#L816 +[logs-evaluation]: http://btcbase.org/log/2018-06-21#1828378 +[logs-bytecode]: http://btcbase.org/log/2018-06-22#1828584 +[logs-bc-detailed]: http://btcbase.org/log/2018-06-25#1829352 +[ffacalc-stack]: http://btcbase.org/patches/ffa_ch4_ffacalc#L202 diff --git a/posts/y04/075-adalisp-prototype-ii.markdown b/posts/y04/075-adalisp-prototype-ii.markdown new file mode 100644 index 0000000..5404a0c --- /dev/null +++ b/posts/y04/075-adalisp-prototype-ii.markdown @@ -0,0 +1,69 @@ +--- +postid: 075 +title: The return of the lost son of the father of adalisp, now in a genesis +date: July 2, 2018 +author: Lucian Mogoșanu +tags: tech +--- + +[Prelude][logs-discussion]: + +> **mircea_popescu**: spyked> I think there's great benefit in the ffa +> chapter-based approach << that ~started with a genesis~. +> **mircea_popescu**: so did diana_coman 's eucrypt. +> **mircea_popescu**: genesis should have included exactly the thing you +> published. +> +> **mircea_popescu**: **asciilifeform**: +> http://btcbase.org/log/2018-06-25#1829397 << imho a genesis oughta be +> a proggy, if a minimal one << nah, a genesis can well be nothing more +> than the manifest, if the manifest is a one line poem. +> a111: Logged on 2018-06-25 12:20 **spyked**: I think there's great +> benefit in the ffa chapter-based approach, so I'm not sure yet what +> the genesis should include. maybe only the lispm piece? or only the +> spec file from lispm? my sense so far is that after some discussion, +> at least some of the parts will be rewritten +> **mircea_popescu**: there is \~no\~ benefit to delaying +> genesis-ification. all it buys one is the naggum stupidity, +> http://btcbase.org/log/2018-06-25#1829365 +> a111: Logged on 2018-06-25 06:15 **mircea_popescu**: this habit of +> "i'll publish" is on the record having killed more smart men than the +> police. +> **mircea_popescu**: that's the one part of naggum you absolutely do +> not want. +> **mircea_popescu**: we already have a mechanism to deal with +> irrelevance, there's no need for an ad-hoc, broken, personal +> implementation whenever something useful may be occuring, to fuck it +> up. +> +> **mircea_popescu**: but to reiterate : there's nothing wrong with a +> broken, not complete, not working, not pretty etc genesis. most +> neonates are also not worth the fucking. + +The item in question: + +* [adalisp_genesis.vpatch][adalisp_genesis.vpatch] +* [adalisp_genesis.vpatch.spyked.sig][adalisp_genesis.vpatch.spyked.sig] + +As per the readme, one can press it directly using e.g. mod6's [V][v] +[implementation][mod6-v]: + +~~~~ +mkdir ~/src/adalisp +cd ~/src/adalisp + +mkdir .wot +cd .wot && wget http://lucian.mogosanu.ro/spyked.asc && cd .. + +v.pl init http://lucian.mogosanu.ro/src/adalisp +v.pl press adalisp_genesis adalisp_genesis.vpatch +~~~~ + +Details are available in the [previous post][adalisp-prototype]. + +[logs-discussion]: http://btcbase.org/log/2018-06-25#1829429 +[adalisp_genesis.vpatch]: http://lucian.mogosanu.ro/src/adalisp/v/patches/adalisp_genesis.vpatch +[adalisp_genesis.vpatch.spyked.sig]: http://lucian.mogosanu.ro/src/adalisp/v/seals/adalisp_genesis.vpatch.spyked.sig +[v]: http://cascadianhacker.com/07_v-tronics-101-a-gentle-introduction-to-the-most-serene-republic-of-bitcoins-cryptographically-backed-version-control-system +[mod6-v]: http://thebitcoin.foundation/ +[adalisp-prototype]: /posts/y04/074-adalisp-prototype.html diff --git a/uploads/2018/04/gpg-sbcl.lisp b/uploads/2018/04/gpg-sbcl.lisp new file mode 100644 index 0000000..2a58f52 --- /dev/null +++ b/uploads/2018/04/gpg-sbcl.lisp @@ -0,0 +1,53 @@ +(defun lispy-run-program (command &optional (input "")) + "Run `command' with optional `input'. + +`command' is a list whose first element is the path to the program to be +executed and the rest of the elements are each a command-line argument +to be passed to the program. + +Returns two strings, representing what the command wrote on the standard +output and standard error respectively. The result (error code) of the +command is ignored." + (check-type command list) + (assert (car command) (command) + "Command must contain at least one element.") + (let ((stdout (make-string-output-stream)) + (stderr (make-string-output-stream))) + (with-input-from-string (stdin input) + (sb-ext:run-program (car command) + (cdr command) + :input stdin + :output stdout + :error stderr)) + (values (get-output-stream-string stdout) + (get-output-stream-string stderr)))) + +(defun gpg-encrypt (input recipient) + "Encrypt `input' to `recipient'." + (lispy-run-program + (list "/usr/bin/gpg1" "--no-tty" "-ea" "-r" recipient) + input)) + +(defun gpg-decrypt (input) + "Decrypt ASCII-armored `input'." + (lispy-run-program + (list "/usr/bin/gpg1" "--no-tty" "-d") + input)) + +(defun gpg-seal (input &optional (uid "")) + "Make a cleartext signature of `input'. + +If `uid' is provided, the input will be signed using that specific key." + (let ((uid-flag (if (string/= "" uid) + (list "-u" uid) + nil))) + (lispy-run-program + (append (list "/usr/bin/gpg1" "--no-tty" "--clearsign") + uid-flag) + input))) + +(defun gpg-verify-seal (input) + "Verify cleartext-signed `input'." + (lispy-run-program + (list "/usr/bin/gpg1" "--no-tty" "--verify") + input)) diff --git a/uploads/2018/04/gpg-uiop.lisp b/uploads/2018/04/gpg-uiop.lisp new file mode 100644 index 0000000..f9b1e76 --- /dev/null +++ b/uploads/2018/04/gpg-uiop.lisp @@ -0,0 +1,51 @@ +(defun lispy-run-program (command &optional (input "")) + "Run `command' with optional `input'. + +`command' is a list whose first element is the name of the program to be +executed and the rest of the elements are each a command-line argument +to be passed to the program. + +Returns two strings, representing what the command wrote on the standard +output and standard error respectively. The result (error code) of the +command is ignored." + (check-type command list) + (let ((stdout (make-string-output-stream)) + (stderr (make-string-output-stream))) + (with-input-from-string (stdin input) + (uiop:run-program command + :input stdin + :output stdout + :error-output stderr + :ignore-error-status t)) + (values (get-output-stream-string stdout) + (get-output-stream-string stderr)))) + +(defun gpg-encrypt (input recipient) + "Encrypt `input' to `recipient'." + (lispy-run-program + (list "/usr/bin/gpg1" "--no-tty" "-ea" "-r" recipient) + input)) + +(defun gpg-decrypt (input) + "Decrypt ASCII-armored `input'." + (lispy-run-program + (list "/usr/bin/gpg1" "--no-tty" "-d") + input)) + +(defun gpg-seal (input &optional (uid "")) + "Make a cleartext signature of `input'. + +If `uid' is provided, the input will be signed using that specific key." + (let ((uid-flag (if (string/= "" uid) + (list "-u" uid) + nil))) + (lispy-run-program + (append (list "/usr/bin/gpg1" "--no-tty" "--clearsign") + uid-flag) + input))) + +(defun gpg-verify-seal (input) + "Verify cleartext-signed `input'." + (lispy-run-program + (list "/usr/bin/gpg1" "--no-tty" "--verify") + input)) diff --git a/uploads/2018/06/adalisp-proto.tar.gz b/uploads/2018/06/adalisp-proto.tar.gz new file mode 100644 index 0000000..cb1604a Binary files /dev/null and b/uploads/2018/06/adalisp-proto.tar.gz differ diff --git a/uploads/2018/06/adalisp-proto.tar.gz.spyked.sig b/uploads/2018/06/adalisp-proto.tar.gz.spyked.sig new file mode 100644 index 0000000..9d16b1c --- /dev/null +++ b/uploads/2018/06/adalisp-proto.tar.gz.spyked.sig @@ -0,0 +1,17 @@ +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v1.4.10 (GNU/Linux) + +iQIcBAABCgAGBQJbMAJlAAoJEL2unQUaPTuV4L4P/2RMocQqxgktyrMSBmhyOvm1 +lv0dORIfi7ZBG8Dp7cNRe5naKuZts6j303T5GLd+mmvyDEUQCXUCRn3ciSWAY27R +1hCeONWRSH9uaMt3kOwo1L8z18WvzYIKPVr/8pQ9ywVLOngYf1mTqo4lM0Rfyq/j +B/fJow0VrMvblf4qLu1ry8YtfAdEND8AhaRypK3uhwZN3YRYyNFj9HP3uxoarm+X +H6mc9Nwg+xY99Zq12A3vxSKYmaREvRoB7JZLAaR01YBaiSrBwww8Cup2VkS9m5sM +KXAc75H6sTLl7Z1zl0HQIe62lHkR55HtpAgkM5r8jM17C8epuQuplrP5paje7jW+ +UoDkwb3lhedMsWsNs6xUqSbDyxWUrJyIG4deYeVkd2fV7lrmoGNLnPykrlECdeza +Jk/4dq7mbCmhVviBEbbfxzEqRYM4JPSx5Q/QLsn90utudz9SfRg/fSHmAva18ruH +k0e5qEWqEvACKquqmXzaR2i8T2BTdKXt7n2egKWL6FNGjFD2ATsH2B7autVq8VJS +lbop0AJhXPjr80KN8tW0LNmUDkoiorEZXtKx5B09Y+9nVK4AThRDO2emup+QaWx1 +AUQq8+BgxMlPKnl/YNYU9hckuHIw15iKtToaVOPJcT0PwylOGXLw9himBc6WDuT0 +AAlI4zEXf+LJJPlfEJtZ +=oI/p +-----END PGP SIGNATURE-----