From 9799d63536f42f9e21eb1110af4c1492eca6c1d1 Mon Sep 17 00:00:00 2001 From: Lucian Mogosanu Date: Mon, 2 Jul 2018 15:04:09 +0300 Subject: [PATCH] posts: 071-075 --- posts/y04/071-cl-gpg.markdown | 445 +++++++++++++++++++++++ posts/y04/072-trilemabot-i.markdown | 117 ++++++ posts/y04/073-prikoke.markdown | 126 +++++++ posts/y04/074-adalisp-prototype.markdown | 114 ++++++ posts/y04/075-adalisp-prototype-ii.markdown | 69 ++++ uploads/2018/04/gpg-sbcl.lisp | 53 +++ uploads/2018/04/gpg-uiop.lisp | 51 +++ uploads/2018/06/adalisp-proto.tar.gz | Bin 0 -> 22940 bytes uploads/2018/06/adalisp-proto.tar.gz.spyked.sig | 17 + 9 files changed, 992 insertions(+) create mode 100644 posts/y04/071-cl-gpg.markdown create mode 100644 posts/y04/072-trilemabot-i.markdown create mode 100644 posts/y04/073-prikoke.markdown create mode 100644 posts/y04/074-adalisp-prototype.markdown create mode 100644 posts/y04/075-adalisp-prototype-ii.markdown create mode 100644 uploads/2018/04/gpg-sbcl.lisp create mode 100644 uploads/2018/04/gpg-uiop.lisp create mode 100644 uploads/2018/06/adalisp-proto.tar.gz create mode 100644 uploads/2018/06/adalisp-proto.tar.gz.spyked.sig 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 0000000000000000000000000000000000000000..cb1604ada69f62641145648d3e461263022896e4 GIT binary patch literal 22940 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 X`q$6b&)3h_kNf$5t013D0FVIy1uwP( literal 0 HcmV?d00001 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----- -- 1.7.10.4