Invoking a PGP encrypted lambda web service with 5 lines of code

I have just now released a new version of Phosphorus Five, where one of the major new features is that it is now dead simple to invoke and create “PGP enabled lambda web services”. A “lambda web service” is basically a web service where the client supplies the code that is to be executed in the web service endpoint. Since this is done through a cryptographically signed HTTP request, it’s actually highly secure, since this allows you to whitelist only a bunch of specific pre-defined PGP keys, and decide which Active Events the caller is legally allowed to execute on your server. You can also control which Active Events a caller is allowed to execute according to which PGP key was used to cryptographically sign the invocation.

If you’d like to test this, you can immediately do so in fact, by first downloading the source code for Phosphorus Five, get it up running, start “Hypereval”, and start invoking lambda web services on my private server. My server is at home.gaiasoul.com, and the first thing you’ll need to do, is to import my public PGP key into your own server. This can be done by pasting the following code into “Hypereval”, and click the “lightning” button, which will execute your code.

p5.http.get:"https://home.gaiasoul.com/micro/pgp"
p5.crypto.pgp-keys.public.import:x:/-/**/content?value
eval-x:x:/+/*/*/*/*
create-widgets
  micro.widgets.modal
    widgets
      pre
        innerValue:x:/../*/p5.crypto.pgp-keys.public.import/0?name

The above code will result in something resembling the following.

My server’s PGP key is as follows.

ea630b9a61448b7f647045c210e1e050ce6feb1d

The above snippet will import my public PGP key into your server, and display the “fingerprint” for my server’s PGP key in a modal window. This is important to later use as you invoke web services on my server. After you have copied the above fingerprint, just click anywhere outside of the modal window to close it, and replace your code in Hypereval with the following code.

/*
 * Invoking web service, passing in Hyperlambda to
 * be evaluated on my server.
 */
micro.web-service.invoke:"https://home.gaiasoul.com/hypereval/lambda-ws"
  fingerprint:ea630b9a61448b7f647045c210e1e050ce6feb1d
  lambda
    /*
     * This code will be executed on my web server!
     */
    .foo:This code was evaluated on the web service endpoint
    split:x:/-?value
      =:" "
    return:x:/-/*?name
/*
 * Displaying a modal widget with the result of
 * the web service invocation.
 */
lambda2hyper:x:/../*/micro.web-service.invoke/**/content/*
create-widgets
  micro.widgets.modal
    widgets
      pre
        innerValue:x:/@lambda2hyper?value

I want to emphasise that the above Hyperlambda was actually executed on *MY WEB SERVER*! And this was done *SECURELY*! The way I have setup my web service endpoint, is to simply use the default implementation. This is a “Hypereval” page snippet, which you can visit with a normal browser by going to home.gaiasoul.com/hypereval/lambda-ws. This will create an HTTP GET request, which will load the web service endpoint’s GUI, allowing you to play around with code, and execute it on my server, using a CodeMirror code editor. Click CTRL+SPACE or CMD+SPACE (OSX) in it to trigger AutoCompletion.

If you create an HTTP POST request towards the same URL, this will assume you want to create a web service invocation towards my server, and act accordingly. The above [micro.web-service.invoke] Active Event is simply a convenience wrapper to create a client connection towards such a web service endpoint.

Implementation

What is actually occurring beneath the hoods, is that a MIME envelope is created internally. Then the entire MIME message is cryptographically signed, using your server’s private PGP key. This signature is verified in my server when the MIME message is parsed, and only if the signature verifies, the execution of your code is allowed. Before it is being executed though, if will invoke [hypereval.lambda-ws.authorize-fingerprint], to check if the PGP key that signed the invocation has “extended rights”. This allows me to “whitelist” things that I normally don’t consider safe only for a handful of PGP keys which I trust comes from sources that I know I can trust. For instance, if a friend of mine had a Phosphorus Five server, and I trust his server, I could allow him to create SQL select invocations, etc – While a normal request, originating from an untrusted source, does not have these rights. I can also choose to entirely shut down access for all keys except a bunch of pre-defined “whitelisted” keys if I want to.

When the web service invocation returns to the client, it will verify that the returned request was cryptographically signed with the [fingerprint] I supplied during invocation, and if not, it will not accept the invocation, but rather throw an exception. This completely eliminate any “man in the middle” attacks, assuming I have verified the PGP fingerprint of the server I am invoking me request towards.

If I wanted to encrypt the entire invocation, I could add a simple [encrypt] node, and set its value to boolean “true”, at which point both the request from my client, and the response from the server, will be encrypted using PGP cryptography. Below is a code snippet demonstrating this.

/*
 * Invoking web service, passing in Hyperlambda to
 * be evaluated on my server.
 */
micro.web-service.invoke:"https://home.gaiasoul.com/hypereval/lambda-ws"
  fingerprint:ea630b9a61448b7f647045c210e1e050ce6feb1d
  encrypt:true
  lambda
    /*
     * This code will be executed on my web server!
     */
    .foo:This code was evaluated on the web service endpoint
    split:x:/-?value
      =:" "
    return:x:/-/*?name
/*
 * Displaying a modal widget with the result of
 * the web service invocation.
 */
lambda2hyper:x:/../*/micro.web-service.invoke/**/content/*
create-widgets
  micro.widgets.modal
    widgets
      pre
        innerValue:x:/@lambda2hyper?value

Notice, the only difference between my first code and this last snippet is the [encrypt] argument, having a boolean “true” value. This will encrypt the request for my server’s PGP key, and when the response is created on my server, it will encrypt the response for your PGP key. All of this happening “for free”, without you having to do anything to implement it.

I can also submit multiple [lambda] nodes, which will be executed in order of appearance, and I can also supply an ID to each [lambda] node, which helps me identify each snippet evaluates on the server. Below is an example.

micro.web-service.invoke:"https://home.gaiasoul.com/hypereval/lambda-ws"
  fingerprint:ea630b9a61448b7f647045c210e1e050ce6feb1d
  lambda:foo-1
    .foo:This code was evaluated on the web service endpoint
    split:x:/-?value
      =:" "
    return:x:/-/*?name
  lambda:foo-2
    +:int:7
      _:14
    return:x:/-?value
lambda2hyper:x:/../*/micro.web-service.invoke/**/result
create-widgets
  micro.widgets.modal
    widgets
      pre
        innerValue:x:/@lambda2hyper?value

The above will display as the following when being executed.

In addition I can also pass files back and forth between my web service endpoint and my client. I can also reference these files from inside my [lambda] nodes, but this is another article later. However, I want to emphasise that files passed back and forth between the client and the server is serialised directly from disc to the HTTP connection, and hence never loaded up into memory, allowing me to create “ridiculously large” HTTP web service requests, without exhausting neither the server nor the client in any ways. Still everything transmitted is easily PGP encrypted, by simply adding a simple argument to my invocation.

Internally what occurs, is that I actually create a MIME request and response, which internally is usingMimeKit, which is the by far fastest MIME parser in existence out there for .Net/Mono. I have tested it myself and serialised 5,000 5KB small images, and it was able to encrypt and decrypt 5,000 images in roughly 10-12 seconds. And since the Open PGP standard dictates that PGP encrypted envelopes will actually be compressed (think “zipped”), this has the additional bonus of significantly reducing the bandwidth consumption both ways. In general, you can expect the request and response to be some 5-10 percent in size compared to transmitting the files as “raw” (unencrypted) data.

Creating your own web service endpoint

If you’d like to create your own web service endpoint, you’ll basically only have to open up for a couple of URLs using “Peeples”. This allows access to the “guest” account to access the URLs necessary to evaluate a web service invocation on your server. Notice, you can still restrict who actually have access to your server though, by changing the settings for your lambda web service endpoint. Below is what you’ll have to paste into the Access object list of your “Peeples” module.

*
  p5.module.allow:/modules/hypereval/lambda-ws/
    exact:true
*
  p5.module.allow:/modules/micro/pgp/
    exact:true

The first part is to enable your actual web service endpoint, and the second part is to enable clients to download your PGP key. This should look like the following in your Peeples module.

Make sure you click “Save” after having pasted the above into Peeples. To change the settings of your web service endpoint, open up Hypereval and load the “lambda-ws” page, and read the documentation for it.

Download Phosphorus Five here.

Advertisements

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.