If you don’t hold it, you don’t own it

If you know anyone who invests in precious metals you have probably heard the phrase “if you don’t hold it, you don’t own it”.

This old adage comes from the risks you are exposed to through use of services that practice things like fractional reserve banking and non-segregated storage of assets. Though personally think these practices can result in significant value for the depositor one can not reasonably argue that they do not come with risks.

This is particularly interesting for Bitcoin; not for political or dogmatic reasons but ones of practicality. Today Bitcoin is in many countries considered “property”, that it has a market capitalization of over 5 billion as of today and that the top 500 Bitcoin addresses control over 30% of all Bitcoin it seems there is quite a lot that these high-net worth users can learn from people who have invested significantly in precious metals.

With that in mind I thought it was worth talking about a set of guidelines people can consider when answering the question of “how should I hold my bitcoin”

Invest proportionally to the risk; if you have forty million dollars worth of Bitcoin you should use different strategies than someone with twenty thousand dollars in Bitcoin additionally the your ability financially to survive the loss must be considered.

Plan for the worst; as they say, “locks keep honest people honest” and “to error is human” as such we must have ready plans on how to handle attacks and compromises when and if they occur. 

Trust but verify; verify the claims and that the technology and service providers you use provide and regularly check on your assets and ensure they are still accesible.

Understand your risks; it’s near impossible to devise a security strategy that will effectively secure anything without having a solid understanding of the risks you are exposed to.

Don’t rely on technology alone; while technology hold promise in securing these assets we in many cases the path of least resistance is to just take physical control of them.

Learn from the past, design for the future; while we all may enjoy pointing out the ridiculousness of the TSA’s reactive strategy of threat analysis understanding attacks that have come before is key to understanding how to physically secure your assets.

Diversification, Diversification, Diversification; whatever approaches you take don’t put all your eggs in one basket. You do not want a failure of a single mechanism to result in the loss of your stored assets.

You can’t hack what you can’t find; though security through obscurity isn’t exactly a solid security design strategy it’s origins come from physical security where it has much more value. Keeping your location and control mechanisms confidential makes it harder for your attacker.

With that in mind you have a few choices to base your strategy on these include:

  • Residential quality home safe
  • Commercial quality safe
  • Bank safe deposit box
  • Bitcoin vaulting service
  • Private vault
  • Depository facilities

If you’re leveraging secret sharing chances are you will use several of these in your final solution. This helps quite a bit in that when it comes to physical storage using a third-party your biggest risk is that facility.

Of course even when you distribute your shares to multiple facilities you have the question is how you secure the secrets that are used to protect those shares.

By ensuring both the shares and the secrets that protect them are geographically distributed and under the control framework of different third-party facilities you reduce your risks significantly.

I am hopeful that within the next few years we will also see increased adoption of P2SH and hardware security modules / smart cards designed around cold wallet scenarios; these have the potential to raise the bar even further but do not replace the need for proper physical storage.

Effectiveness of security controls in physical security

Lately I have been giving some thought to effectiveness of security controls in relationship to physical security.

To do so requires a definition of what one will consider “effective”. If we can accept that an appropriately motivated and well-funded attacker can bypass all mitigations our definition of effectiveness must be based on the skill and motivation of our attacker.

This also means we must give thought to how quickly we can respond when we become aware of an attack.

This is in essence the same approach we take when designing secure software systems. The core difference being we also have to consider the physical properties of the space we are protecting along with the human factors of the design of the system.

On the topic of physical properties of the space we have to consider the materials it was constructed with. One of my favorite examples here is how in the movie Sneakers Robert Redford’s character bypasses a security keypad by kicking the door down.

In this case even if appropriate door hardware was in place he could have simply gone through the wall like in this case of the recent burglaries of some Best Buy stores.

The state of affairs with home security is even in worse shape. Locks on doors are often of low quality and are trivially bump-able the plethora of home alarms that are installed typically use wireless sensors that can be bypassed with just a few dollars of electronics, garage doors can often be bypassed with nothing more than a bent hanger and if that wasn’t enough just throw a rock through a window.

So given this sorry state of affairs what should we do? First we need to be realistic about what the risks, the assets we have to protect and the value to the attacker. Then we should only invest proportionally to those variables.

To do this we need to develop a solid plan of what intrusions we have a chance of detecting, when we do detect them how quickly we can do so and then what our response will be in each of those cases.

For this reason a good quality alarm system is very important but they don’t do much good if they are not activated. Once activated we need to think about how long it takes for the monitoring service to be called, once contacted what do they do and how long does it take? Also In some cases the police will refuse to respond to a call that has not been confirmed by someone on-site and if they do often times responses can take hours. With that said knowing what the response time is is invaluable to understanding how long your mitigations will need to withstand attack.

But if as they say “Locks are for honest people” why do we bother at all?

The answer is that well-thought out mitigations do act as meaningful deterrents that can significantly reduce your risk but more importantly having proactively considered the risks and built the corresponding mitigations you are positioned to reduce your exposure (see my recent posts on Why shouldn’t you use safe-deposit boxes to store Bitcoin? and Insurance and Bitcoin) and ensure that such events are survivable.

 

PiperWallet First Impressions

So I just got my PiperWallet. For those of you not yet familiar with it the PiperWallet is an open-source hardware bitcoin wallet based Electrum running on a RaspberryPi paired with a built in thermal printer in what looks like a 3D printed chassis.

The basic idea is that managing cold wallets is hard and it doesn’t have to be.

Even though I have only started to play with the device overall I am impressed. Here are my initial observations:

  1. It was packaged well considering the volume in which they are produced;
  2. The quality of the casing is also good considering the volume;
  3. The cut outs are a little rough and are larger than the connectors they expose;
  4. The primary “indicator LED” that is used to show that the device is booting is not terribly bright;
  5. Without reading the instructions (or waiting a sufficiently long time) it’s not  obvious when the device is ready;
  6. The print button LED is bright and of excellent quality;
  7. There is no positive feedback when the print button is pressed.

So far I am happy with the purchase though I need to do some more playing with it before I make any final conclusions.

With that said here are the things I think I would change if it were my product:

  1. Make the serial numbers on the paper wallets randomly generated; you un-necessarily leak information by using monotonically generated serials;
  2. Add tamper evident seals to the casing so that if the device is opened during shipping it is obvious;
  3. Add tamper evident seals or “plugs” over the ports exposed on the device, possibly even dummy plugs with seals so its clear nothing happened to the device as part of shipping;
  4. Add per-device fixed wallet keys to be used as a serial number to the back of each case (there is a wallet address but I believe this is an address of the Piper team);
  5. Use per device passwords shipping them on a form similar to the one I provided here;
  6. Replace the indicator LED with one with a similar brightness and quality to that used in the “print button”;
  7. Add a small LCD display that can be used to provide real-time feedback and status so it’s easier to use when headless;
  8. In the documentation included have the steps to verify what software is running on the device along with hashes to do so.

Verifying a Bitcoin Wallet Address

Before sending someone a large sum of money on the internet via a irreversible transaction you better make sure you are sending the funds to the right address.

There are a few ways to go about doing this and depending on who you are sending funds to, how accessible their keys are and what the capabilities and behavior of their wallet software is you may need to choose different solutions.

Have the recipient sign a message using their wallet key

If we assume the recipient has the key associated with the target wallet online (aka not in cold storage) and that that the software they use for that wallet supports message signing with wallet keys this can be a viable option.

Unfortunately there is not currently a standard for the format of signatures using bitcoin keys with that said thankfully there appear to only be two common formats in-use today.

The first format being in-essence no formatting; client simply present you the three values you will need to verify a message and you do with them as you see fit, for example:

  • Wallet Address: 18neTpQ5MWnXg4n4rpoK5TgxXjEVcg2MYR
  • Message: [email protected] – my voice is my passphrase authenticate me
  • Signature: G0d6BnQem1gT4nd9esfsEyn1k/GfYAxDkNJmkNvmz8wCOI2Ncw9DvIcyP7OJcEvWbUHQNIBFK3V8wYdnhEFhYHI=

This format leaves a little be desired. For one you have to pass these values independently and then you also have issues around introduction of white-space which can invalidate signatures.

There is another increasingly common format that leverages ASCII armor and some codified rules to address these issues. This style of formatting originated in a project called Privacy Enhanced Mail (PEM), it was one of the first proposals for how to sign and encrypt mail on the Internet and was later adopted by PGP (RFC https://tools.ietf.org/html/rfc4880).

But don’t confuse this format with these other formats they follow some different rules when it comes to encoding.

What this means is that depending on the implementation of the wallet software the recipient uses you may not be able to validate the signature they produce without some manipulation of the text.

As for what this format looks like, its fairly straight forward:

-----BEGIN BITCOIN SIGNED MESSAGE-----
[email protected] - my voice is my passphrase authenticate me
-----BEGIN SIGNATURE-----
18neTpQ5MWnXg4n4rpoK5TgxXjEVcg2MYR
G0d6BnQem1gT4nd9esfsEyn1k/GfYAxDkNJmkNvmz8wCOI2Ncw9DvIcyP7OJcEvWbUHQNIBFK3V8wYdnhEFhYHI=
-----END BITCOIN SIGNED MESSAGE-----

The core differences with this format (as specified in this thread and the PGP rule-set are:

  • No “empty-line” delineator between the headers and message;
  • Beginning and end whitespace / newlines ignored excluded when verifying the signature;
  • Length of rows are not limited to 80 characters;
  • No concept of header values (like versions).

The reason I point this out is that since there really isn’t a standard for this signature format and the format diverges from what has been used historically you may still encounter interoperability issues when validating messages between clients that have not been tested with each other.

With that said when you have managed to successfully verify a message like this you know that whoever produced the message owns the key associated with the wallet associated with it.

To address the risk of a message substitution the sender would need to communicate a challenge out of band to the recipient. For example you may notice in my message above I included “my voice is my passphrase authenticate me”. My inclusion of this message (presumably exchanged out of band) helps assure the sender that it was me who signed the message.

To make this process a little easier Andrew Yanovsky and I put together a simple site that can validate both formats, it’s all client side so you can save the files locally and run without the dependency on the website if you like.

NOTE: It is worth noting that this workflow does not accommodate P2SH and multi-signature wallets both of which will see increased use as time progresses.

Do a micro-transaction

The simplest way to verify an address is to simply send a small amount of money to that address and verify out of band with the recipient that they confirm seeing it in their balance. This is what most online payment services but again this requires the keys to be accessible to the sender they can perform the transaction.

There are a few things to keep in mind if you go this way, specifically:

  1. Don’t send less that .0001 BTC because the transaction may get “stuck” and not be processed.
  2. Be sure to include some transaction fee even if tiny so it doesn’t stay unprocessed for too long.

Once the transaction has been sent and you use a tool like blockchain.info to see that the transaction has been confirmed you can verify out of band with the address owner again that they see the funds as well.

This approach unlike the wallet signing key approach can also work with multi-signature and P2SH wallets which will be in use increasingly as clients better support these techniques.

Verify the wallet address two times via out of band channel

If they keys are offline (in cold storage) the only viable option is to carefully validate each character of the address via an out of bound secure channel, I would personally not rely on this approach for large sums but if both parties are careful it can work. By doing the check twice you reduce the chance of human error but mistakes can happen and in this case they can not be undone so use this approach with caution.

None of these solutions are perfect and moving forward I expect we will see services like OneName.io and exchanges with authenticated account profiles will become the way that we solve these problems but in the mean time you can reasonably manage the transaction workflow via these two mechanism.

Certificate Path Building in PKIjs

Now that its possible to decode and verify the signature on X.509 certificates within the browser the natural question to ask is what can I do with that?

Well first off to build an interesting application you will need to have the ability to validate that a certificate is trusted the first step in doing that is building the certificate path associated with the certificate.

The defacto standard for path building libraries is the NIST PKITS tests our goal is to create a library that will be able to pass the sane tests from this suite (some are odd for sure).

This is a pretty high bar and will take some time. At the time of writing this blog post we pass 1-33 of this test suite in with flying colors these tests cover all of the basic certificate validation rules. We also think the library will pass all Policy Constraints and Name Constraints but more testing is needed to confirm.

So how does building a chain look like today with this library?

var certs = new Array();

// Load cert to be validated, its intermediates and root
for(var i = 0; i < cert_buffers.length; i++)
{
    var asn1 = org.pkijs.fromBER(cert_buffers[i]);
    certs.push(new org.pkijs.simpl.CERT({ schema: asn1.result }));
}

var crls = new Array();

// Load any CRLs we have
for(var i = 0; i < crl_buffers.length; i++)
{
    var asn1 = org.pkijs.fromBER(crl_buffers[i]);
    crls.push(new org.pkijs.simpl.CRL({ schema: asn1.result }));
}

var cert_chain_simpl = new org.pkijs.simpl.CERT_CHAIN({
    certs: certs,
    crls: crls
});

cert_chain_simpl.verify().then(
    function(result)
    {
        alert("Good result");
    },
    function(error)
    {
        alert("Error: " + error);
    }
);

The current incarnation of the API expects that the bag of certificates that is passed in will include all intermediates as well as all trust anchors. We will be changing this in a future release so that trust anchors are passed in another bag.

This will help ensure that the certificate inputs to be validated don’t contain anything that might accidentally result in the certificate being treated as valid when it should not be. With that said as it is currently structured we can begin developing automated testing which is great.

Note: Updated the post to indicate the goal is to pass the sane PKITS tests, some of which are not and some are not possible to pass in a web environment.

MUST STAPLE and PKI.js

The other day I did a post on how to create a self-signed certificate using PKI.js in that sample we included a Basic Constraints extension but we could have also just as easily defined a custom or new certificate extension. For example thanks to #heartbleed folks are talking about MUST STAPLE again, this is an extension that was proposed several years ago that when present would indicate that clients should hard-fail instead of soft-fail with OCSP.

This proposal is based on a generic concept of expressing a security policy within the certificate. While the OIDs for this extension and the associated policy have not been defined yet one can easily construct a certificate using this extension with PKI.js:

cert_simpl.extensions.push(new org.pkijs.simpl.EXTENSION({
    extnID: "1.2.3", // No OIDs assigned yet
    critical: false,
    extnValue: (new org.pkijs.asn1.SEQUENCE({
        value: [
                   new org.pkijs.asn1.INTEGER({ value: 4 }),
                   new org.pkijs.asn1.INTEGER({ value: 5 }),
                   new org.pkijs.asn1.INTEGER({ value: 6 })
               ]
               })).toBER(false)
}));

NOTE: In the above snip-it we just made up two OID values, hopefully IANA will assign OIDs soon so it is possible for browsers and CAs to implement this extension formally.

Why shouldn’t you use safe-deposit boxes to store Bitcoin?

Banks are not exactly what they used to be. I don’t know exactly when it changed but as a boy I remember banks being these massive buildings with large vault doors, armed guards and cameras everywhere but it seems increasingly they are located in strip malls right next to a Great Clips with nothing more an alarm and a small safe in the back. Frankly most don’t even offer safe deposit services any longer.

The lack of security of these facilities offer is not why I don’t recommend their use; it is because they can’t be trusted to keep your valuables safe. Not only can the federal government seize the contents of these safe deposit boxes at will increasingly the state governments are doing so as well as a means to shore up their own finances via their unclaimed property programs.

You can mitigate some of these risks by using techniques like Shamir Secret Sharing to split your keys up into M of N parts or by utilizing multi-signature wallets where the parts or keys are stored at different facilities reducing the likelihood of these events impacting you (which you should do regardless) but as a general rule I recommend use of private facilities instead.

Private facilities have a number of value propositions above and beyond banks, these include:

  1. Not regulated which makes it more difficult for the contents to be frozen or seized
  2. Identification is often not a requirement to open an account making targeting assets in the vault more difficult
  3. Not subject to bank holidays (for example during 9/11 banks were closed in the US)
  4. May offer:
    1. Insurance that would protect you from loss;
    2. Improved security protections and procedures;
    3. 24x7x365 access to the facility;
    4. 24x7x365 armed guards;
    5. The use of “ceremony rooms” where you can privately perform transactions.

Above these value propositions a few things to keep in mind when looking at these facilities are:

  1. Do they offer dual key control? This is when they keep one key and you keep the other. This helps mitigate the risk of your key being stolen.
  2. Did they their ceremony rooms offer you sufficient confidentiality and space to perform your transactions?
  3. Do they limit how many people can be in the vault at a given time?
  4. Is the construction of the facility done in such a way that physical compromise would be difficult?
  5. Do they have adequate camera coverage and keep the recordings long enough to be useful to identify compromise?
  6. Do they follow strict procedures or are they overly lax?
  7. Do they have any attempted thefts and if so how were they handled?
  8. Have their ever been any legal claims from their customers?
  9. What are the BBB & Yelp reports for the facility?

Regardless of which facilities you choose to store your Bitcoin assets its also important to remember the old adage of “Trust but verify” and periodically access the assets to ensure their integrity and availability.

 

Generating signed messages using CMS and PKI.js

One of the most common signature formats on the web is known as CMS SignedData, this is the signature format used in PDF files, CAdES, S/MIME and several other digital signature solutions.

As a signature it has a few notable features:

  1. Having multiple signers.
  2. Including meta-data that will be signed along with the data that is being signed.
  3. Including meta-data that is outside the scope of the signature.
  4. Signing data contained within the signature or data referenced by it.

These traits mean you can do some interesting things like implementing counter-signing in-turn enabling notarization scenarios.

Utilizing PKI.js you can now create and verify this signature format, bellow is an example of how creating one of these messages looks using this library:

// #region Put a static values 
var sample_data = new Uint8Array(sample_data);
sample_data[0] = 0x00;
sample_data[1] = 0x01;
sample_data[2] = 0x02;
sample_data[3] = 0x03;
sample_data[4] = 0x04;

cms_signed_simpl = new org.pkijs.simpl.CMS_SIGNED_DATA({
    digestAlgorithms: [
        new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ algorithm_id: "1.3.14.3.2.26" }) // SHA-1
    ],
    encapContentInfo: new org.pkijs.simpl.cms.EncapsulatedContentInfo({
        eContentType: "1.2.840.113549.1.7.1", // "data" content type
        eContent: new org.pkijs.asn1.OCTETSTRING({ value_hex: sample_data })
    }),
    signerInfos: [
        new org.pkijs.simpl.CMS_SIGNER_INFO({
            sid: new org.pkijs.simpl.cms.IssuerAndSerialNumber({
                issuer: cert_simpl.issuer,
                serialNumber: cert_simpl.serialNumber
            }),
            digestAlgorithm: new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ algorithm_id: "1.3.14.3.2.26" }), // SHA-1
            signatureAlgorithm: new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ algorithm_id: "1.2.840.113549.1.1.5" }), // RSA + SHA-1
        })
    ],
    certificates: [cert_simpl]
});
// #endregion 

return cms_signed_simpl.sign(privateKey, 0);

In this sample you can see we are putting our content to be signed within the SignedData message and then signing it with RSA and SHA1, this is in-the exact same thing that is needed to implement what is called opaque signed email in S/MIME.

 

Frankencerts, new extensions and PKI.js

One of the things that we wanted you to be able to use the PKI.js libraries for is the X.509 Certificates, including those with new extensions (like testing for OCSP MUST Staple or for testing other certificate processing libraries like was done in the Frankencert paper.

Here is an example of what that might look like:

function create_cert()
{
    // #region Initial variables 
    var sequence = Promise.resolve();

    var cert_simpl = new org.pkijs.simpl.CERT();

    var publicKey;
    var privateKey;
    // #endregion 

    // #region Get a "crypto" extension 
    var crypto = org.pkijs.getCrypto();
    if(typeof crypto == "undefined")
    {
        alert("No WebCrypto extension found");

        return;
    }
    // #endregion 

    // #region Put a static values 
    cert_simpl.serialNumber = new org.pkijs.asn1.INTEGER({ value: 1 });
    cert_simpl.issuer.types_and_values.push(new org.pkijs.simpl.ATTR_TYPE_AND_VALUE({
        type: "2.222.333",
        value: new org.pkijs.asn1.PRINTABLESTRING({ value: "RU" })
    }));
    cert_simpl.issuer.types_and_values.push(new org.pkijs.simpl.ATTR_TYPE_AND_VALUE({
        type: "2.222.444",
        value: new org.pkijs.asn1.PRINTABLESTRING({ value: "Test" })
    }));
    cert_simpl.subject.types_and_values.push(new org.pkijs.simpl.ATTR_TYPE_AND_VALUE({
        type: "2.222.333",
        value: new org.pkijs.asn1.PRINTABLESTRING({ value: "RU" })
    }));
    cert_simpl.subject.types_and_values.push(new org.pkijs.simpl.ATTR_TYPE_AND_VALUE({
        type: "2.222.444",
        value: new org.pkijs.asn1.PRINTABLESTRING({ value: "Test" })
    }));
    cert_simpl.notBefore.value = new Date();
    cert_simpl.notAfter.value = new Date(2016, 01, 01);

    cert_simpl.extensions = new Array(); // Extensions are not a part of certificate by default, it's an optional array

    // #region "BasicConstraints" extension
    var basic_constr = new org.pkijs.simpl.x509.BasicConstraints({
        cA: true,
        pathLenConstraint: 3
    });

    cert_simpl.extensions.push(new org.pkijs.simpl.EXTENSION({
        extnID: "2.5.29.19",
        critical: false,
        extnValue: basic_constr.toSchema().toBER(false),
        parsedValue: basic_constr // Parsed value for well-known extensions
    }));
    // #endregion 

    // #region "KeyUsage" extension 
    var bit_array = new ArrayBuffer(1);
    var bit_view = new Uint8Array(bit_array);

    bit_view[0] = bit_view[0] | 0x02; // Key usage "cRLSign" flag
    bit_view[0] = bit_view[0] | 0x04; // Key usage "keyCertSign" flag

    var key_usage = new org.pkijs.asn1.BITSTRING({ value_hex: bit_array });

    cert_simpl.extensions.push(new org.pkijs.simpl.EXTENSION({
        extnID: "2.5.29.15",
        critical: false,
        extnValue: key_usage.toBER(false),
        parsedValue: key_usage // Parsed value for well-known extensions
    }));
    // #endregion 

    cert_simpl.signatureAlgorithm.algorithm_id = "1.2.840.113549.1.1.5"; // RSA + SHA-1
    cert_simpl.signature.algorithm_id = cert_simpl.signatureAlgorithm.algorithm_id; // Must be the same value
    // #endregion 

    // #region Create a new key pair 
    sequence = sequence.then(
        function()
        {
            return crypto.generateKey({ name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: "sha-1" } }, true, ["encrypt", "decrypt", "sign", "verify"]);
        }
    );
    // #endregion 

    // #region Store new key in an interim variables
    sequence = sequence.then(
        function(keyPair)
        {
            publicKey = keyPair.publicKey;
            privateKey = keyPair.privateKey;
        },
        function(error)
        {
            alert("Error during key generation: " + error);
        }
    );
    // #endregion 

    // #region Exporting public key into "subjectPublicKeyInfo"  
    sequence = sequence.then(
        function()
        {
            return cert_simpl.subjectPublicKeyInfo.importKey(publicKey);
         }
    );
    // #endregion 

    // #region Signing final certificate 
    sequence = sequence.then(
       function()
       {
           return cert_simpl.sign(privateKey);
       },
       function(error)
       {
           alert("Error during exporting public key: " + error);
       }
    );
    // #endregion 

    sequence = sequence.then(
        function()
        {
            alert("Good result");
        },
        function(error)
        {
            alert("Error during signing: " + error);
        }
    );

    sequence.then(
        function()
        {
            return cert_simpl.verify();
        }
    ).then(
        function(result)
        {
            alert("Verification passed: " + result);
        },
        function(error)
        {
           alert("Verification failed: " + eror);
        }
    );
}

As you can see the library is designed in such a way you are not limited to the creation of some static pre-conceived layouts of these structures, you can fairly easily construct any type of certificate (or any of the other supported message types).

At a later date (if it makes sense to do so) we may also decide to add a simple layer ontop of this that abstracts out the need to understand encoding concepts as well.

This layered approach ensures the library can be used to create real-applications without the need to hack up the underlying APIs enabling developers to avoid the need to understand low-level  ASN.1 formats in detail.

Potential projects for PKI.JS

This is a list of potential projects that would benefit from PKI.js, can you think of any others that would be interesting?

  1. Adding signing, time-stamping and verifying support to pdf.js.
  2. Creating a smime.js on email.js that provides signing, verifying, encryption, decryption of S/MIME.
  3. Creating a CAdES library for web pages.
  4. Adding signature support to web document management solutions.
  5. Adding encrypted mail support in webmail offerings.
  6. Building a usable and modern certificate based authentication solution using web markup for credential selection.
  7. Creating a rich user experience for the short-lived certificates providing temporary access via SSH and TLS.

With some changes the libraries could be used in server side projects:

  1. Creating an OCSP responder based on node.js.
  2. Creating a simple CA on node.js