kbpgp.js

Decrypting & verifying

Decrypting and verifying are slightly more complicated than encrypting or signing, because often, you don't know ahead of time which KeyManagers are required. For PGP messages that are signed and encrypted, you only know which verification key is needed after a successful decryption. Also, messages in PGP can be encrypted for multiple receivers, and any given receiver might only have access to one of many possible decryption keys.

In kbpgp, the unbox function handles the nitty-gritty of decryption and verification. You need to pass it a PGP message (encrypted, signed or both), and also a way to fetch keys midstream --- a kbpgp.KeyFetcher object. You can use one of ours out-of-the-box or subclass your own (say, if you want to fetch keys from your server).

Out-of-the-Box: The KeyRing

The first example of a KeyFetcher we'll consider is a KeyRing --- a object that you can load ahead of time with a bunch of KeyManagers.

ring = new kbpgp.keyring.KeyRing
kms = [ alice, bob, charlie ]
for km in kms
  ring.add_key_manager km
      

For convenience, the KeyManager class also implements the KeyFetcher interface. If you know ahead of time that you'll only need one KeyManager in a decryption/verification, then you can use it as a KeyFetcher.

Decryption and Verification Example

Decrypt and verify via the unbox function. Pass the message, the KeyFetcher (like ring above), an ASP if you intend to cancel or monitor progress, and a callback to fire when done:

ring = new kbpgp.keyring.KeyRing
kms = [ alice, bob, charlie ]
pgp_msg = "---- BEGIN PGP MESSAGE ----- ...."
asp = # as in Encryption....
for km in kms
  ring.add_key_manager km
kbpgp.unbox { keyfetch : ring, armored : pgp_msg, asp }, (err, literals) ->
  if err?
    console.log "Problem: #{err}"
  else
     console.log "decrypted message"
     console.log literals[0].toString()
     if (km = literals[0].get_data_signer()?.get_key_manager())?
        console.log "Signed by PGP fingerprint"
        console.log km.get_pgp_fingerprint().toString('hex')
      

unbox calls back with two arguments: an Error if something went wrong, and an array of Literals if not. Literal objects support the toString(enc) and toBuffer() methods. The former call takes an optional parameter which is an encoding; if none is supplied, kbpgp will use the encoding specified in the PGP message; you can specify 'utf8', 'ascii', 'binary', 'base64' or 'hex' if you want to override that encoding.

This example shows that unbox handles both decryption and verification. To check if parts of the message were signed, make a get_data_signer call on each Literal in the message. Note that the same KeyManager that you loaded into your KeyFetcher shows up here. So if you augment that KeyManager with custom fields, they will be available here.

The KeyFetcher Interface

In a more general decryption/verification scenario, you might need to fetch the appropriate decryption and/or verification keys from secondary or remote storage. In this situation, you shouldn't use the KeyRing described above, but should instead provide a custom KeyFetcher.

All usable KeyFetchers must implement one method: fetch. Given several PGP key IDs, and a flag specifying which operation is requested, the fetch method should call back with a KeyManager, if it could find one.

fetch(ids,ops,cb) is called with three arguments:

  1. ids --- An array of Buffers, each one containing a 64-bit ID of a PGP key. These keys might refer to subkeys, which are often employed in encrypting and signing messages.
  2. ops --- Which crypto options are required of this key; a bitwise OR of constants from kbpgp.const.ops, which are:
    • encrypt : 0x1
    • decrypt : 0x2
    • verify : 0x4
    • sign : 0x8
  3. cb --- A callback that when done, calls back with a triple: (err,km,i)
    • err is an Error explaining what went wrong, or null on success.
    • km is, in the case of success, a KeyManager that meets the given requirements
    • i is, in the case of success, an integer indiciating which of the keys was found in the lookup. If 0 is returned here, then ids[0] is the 64-bit ID of a key inside km.

We're just getting started with this tutorial and examples. Hit us up on github if anything is missing.