├── .eslintrc ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── examples ├── decrypt.js └── encrypt.js ├── lib ├── gpg.js └── spawnGPG.js ├── package.json └── test ├── hello.gpg ├── hello.txt ├── index.js ├── test.priv.asc └── test.pub.asc /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "strict": 0, 4 | "quotes": ["error", "single"], 5 | "curly": ["error", "multi-line"], 6 | "no-use-before-define": ["error", "nofunc"], 7 | "no-underscore-dangle": 0, 8 | "no-unused-vars": [2, {"vars": "local", "args": "none"}], 9 | "new-cap": 0, 10 | "consistent-return": 0, 11 | "camelcase": 0 12 | }, 13 | env: { 14 | "node": true 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | - "0.11" 5 | - "0.12" 6 | - "iojs" 7 | - "4.0" 8 | - "4.1" 9 | matrix: 10 | fast_finish: true 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2015 Nicholas Penree 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GPG Encryption/Decryption in Node.js 2 | [![travis][travis-image]][travis-url] 3 | [![npm][npm-image]][npm-url] 4 | [![downloads][downloads-image]][downloads-url] 5 | 6 | [travis-image]: https://travis-ci.org/drudge/node-gpg.svg?branch=master 7 | [travis-url]: https://travis-ci.org/drudge/node-gpg 8 | 9 | [npm-image]: https://img.shields.io/npm/v/gpg.svg?style=flat 10 | [npm-url]: https://npmjs.org/package/gpg 11 | 12 | [downloads-image]: https://img.shields.io/npm/dm/gpg.svg?style=flat 13 | [downloads-url]: https://npmjs.org/package/gpg 14 | 15 | This module is a wrapper around `gpg` for use within Node. Node-GPG takes care of spawning `gpg`, passing it 16 | the correct arguments, and piping input to stdin. It can also pipe input in from files and output out to files. 17 | 18 | Use Node-GPG if you are considering calling `gpg` directly from your application. 19 | 20 | ## Requirements 21 | 22 | In order to use Node-GPG, you'll need to have the `gpg` binary in your $PATH. 23 | 24 | ## Installation 25 | 26 | npm install gpg 27 | 28 | ## Usage 29 | 30 | Node-GPG supports both direct calls to GPG with string arguments, and streaming calls for piping input and output 31 | from/to files. 32 | 33 | See [the source](lib/gpg.js) for more details. 34 | 35 | If a function you need is not implemented, you can call gpg directly with arguments of your choice by 36 | calling `gpg.call(stdinStr, argsArray, cb)`, or `gpg.callStreaming(inputFileName, outputFileName, argsArray, cb)`. 37 | 38 | ## Notes 39 | 40 | Existing implementations of PGP in Javascript are blocking and unfeasibly slow for server use. 41 | In casual testing, encrypting a simple 400-character email to an El-Gamal key took upwards of 11 seconds using 42 | [openpgpjs](https://github.com/openpgpjs/openpgpjs) and 14 seconds with [kbpgp](https://github.com/keybase/kbpgp), 43 | but takes less than 0.1 seconds with `gpg` directly. 44 | 45 | ## Contributors 46 | 47 | The following are the major contributors of `node-gpg` (in no specific order). 48 | 49 | * Nicholas Penree ([drudge](http://github.com/drudge)) 50 | * [freewil](http://github.com/freewil) 51 | * Samuel Reed [strml](http://github.com/strml) 52 | -------------------------------------------------------------------------------- /examples/decrypt.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * node-gpg 3 | * Copyright(c) 2011 Nicholas Penree 4 | * MIT Licensed 5 | */ 6 | 7 | /** 8 | * Module dependencies. 9 | */ 10 | 11 | var gpg = require(__dirname + '/../lib/gpg') 12 | 13 | gpg.decryptFile('/tmp/test.txt', function(err, contents){ 14 | console.log(contents); 15 | }); 16 | -------------------------------------------------------------------------------- /examples/encrypt.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * node-gpg 3 | * Copyright(c) 2011 Nicholas Penree 4 | * MIT Licensed 5 | */ 6 | 7 | /** 8 | * Module dependencies. 9 | */ 10 | 11 | var gpg = require(__dirname + '/../lib/gpg') 12 | 13 | gpg.encryptToFile({source:__dirname + '/../lib/gpg.js', dest: '/tmp/test.txt' }, function(err, data){ 14 | console.log(err); 15 | }); 16 | -------------------------------------------------------------------------------- /lib/gpg.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * node-gpg 3 | * Copyright(c) 2011 Nicholas Penree 4 | * MIT Licensed 5 | */ 6 | 7 | /** 8 | * Module dependencies. 9 | */ 10 | var fs = require('fs'); 11 | var spawnGPG = require('./spawnGPG'); 12 | var keyRegex = /^gpg: key (.*?):/; 13 | 14 | /** 15 | * Base `GPG` object. 16 | */ 17 | var GPG = { 18 | 19 | /** 20 | * Raw call to gpg. 21 | * 22 | * @param {String} stdin String to send to stdin. 23 | * @param {Array} [args] Array of arguments. 24 | * @param {Function} [fn] Callback. 25 | * @api public 26 | */ 27 | call: function(stdin, args, fn) { 28 | spawnGPG(stdin, args, fn); 29 | }, 30 | 31 | /** 32 | * Raw streaming call to gpg. Reads from input file and writes to output file. 33 | * 34 | * @param {String} inputFileName Name of input file. 35 | * @param {String} outputFileName Name of output file. 36 | * @param {Array} [args] Array of arguments. 37 | * @param {Function} [fn] Callback. 38 | * @api public 39 | */ 40 | callStreaming: function(inputFileName, outputFileName, args, fn) { 41 | spawnGPG.streaming({source: inputFileName, dest: outputFileName}, args, fn); 42 | }, 43 | 44 | /** 45 | * Encrypt source file passed as `options.source` and store it in a file specified in `options.dest`. 46 | * 47 | * @param {Object} options Should contain 'source' and 'dest' keys. 48 | * @param {Function} [fn] Callback. 49 | * @api public 50 | */ 51 | encryptToFile: function (options, fn){ 52 | spawnGPG.streaming(options, ['--encrypt'], fn); 53 | }, 54 | 55 | /** 56 | * Encrypt source `file` and pass the encrypted contents to the callback `fn`. 57 | * 58 | * @param {String} file Filename. 59 | * @param {Function} [fn] Callback containing the encrypted file contents. 60 | * @api public 61 | */ 62 | encryptFile: function(file, fn){ 63 | var self = this; 64 | 65 | fs.readFile(file, function(err, content){ 66 | if (err) return fn(err); 67 | self.encrypt(content, fn); 68 | }); 69 | }, 70 | 71 | /** 72 | * Encrypt source stream passed as `options.source` and pass it to the stream specified in `options.dest`. 73 | * Is basicaly the same method as `encryptToFile()`. 74 | * 75 | * @param {Object} options Should contain 'source' and 'dest' keys that are streams. 76 | * @param {Function} [fn] Callback. 77 | * @api public 78 | */ 79 | encryptToStream: function (options, fn){ 80 | spawnGPG.streaming(options, ['--encrypt'], fn); 81 | }, 82 | 83 | /** 84 | * Encrypt source `stream` and pass the encrypted contents to the callback `fn`. 85 | * 86 | * @param {ReadableStream} stream Stream to read from. 87 | * @param {Array} [args] Array of additonal gpg arguments. 88 | * @param {Function} [fn] Callback containing the encrypted file contents. 89 | * @api public 90 | */ 91 | encryptStream: function (stream, args, fn){ 92 | var self = this; 93 | var chunks = []; 94 | 95 | stream.on('data', function (chunk){ 96 | chunks.push(chunk); 97 | }); 98 | 99 | stream.on('end', function (){ 100 | self.encrypt(Buffer.concat(chunks), args, fn); 101 | }); 102 | 103 | stream.on('error', fn); 104 | }, 105 | 106 | /** 107 | * Encrypt `str` and pass the encrypted version to the callback `fn`. 108 | * 109 | * @param {String|Buffer} str String to encrypt. 110 | * @param {Array} [args] Array of additonal gpg arguments. 111 | * @param {Function} [fn] Callback containing the encrypted Buffer. 112 | * @api public 113 | */ 114 | encrypt: function(str, args, fn){ 115 | spawnGPG(str, ['--encrypt'], args, fn); 116 | }, 117 | 118 | /** 119 | * Decrypt `str` and pass the decrypted version to the callback `fn`. 120 | * 121 | * @param {String|Buffer} str Data to decrypt. 122 | * @param {Array} [args] Array of additonal gpg arguments. 123 | * @param {Function} [fn] Callback containing the decrypted Buffer. 124 | * @api public 125 | */ 126 | decrypt: function(str, args, fn){ 127 | spawnGPG(str, ['--decrypt'], args, fn); 128 | }, 129 | 130 | /** 131 | * Decrypt source `file` and pass the decrypted contents to the callback `fn`. 132 | * 133 | * @param {String} file Filename. 134 | * @param {Function} fn Callback containing the decrypted file contents. 135 | * @api public 136 | */ 137 | decryptFile: function(file, fn){ 138 | var self = this; 139 | 140 | fs.readFile(file, function(err, content){ 141 | if (err) return fn(err); 142 | self.decrypt(content, fn); 143 | }); 144 | }, 145 | 146 | /** 147 | * Decrypt source file passed as `options.source` and store it in a file specified in `options.dest`. 148 | * 149 | * @param {Object} options Should contain 'source' and 'dest' keys. 150 | * @param {Function} fn Callback 151 | * @api public 152 | */ 153 | decryptToFile: function (options, fn){ 154 | spawnGPG.streaming(options, ['--decrypt'], fn); 155 | }, 156 | 157 | /** 158 | * Decrypt source `stream` and pass the decrypted contents to the callback `fn`. 159 | * 160 | * @param {ReadableStream} stream Stream to read from. 161 | * @param {Array} [args] Array of additonal gpg arguments. 162 | * @param {Function} [fn] Callback containing the decrypted file contents. 163 | * @api public 164 | */ 165 | decryptStream: function(stream, args, fn){ 166 | var self = this; 167 | var chunks = []; 168 | 169 | stream.on('data', function (chunk){ 170 | chunks.push(chunk); 171 | }); 172 | 173 | stream.on('end', function (){ 174 | self.decrypt(Buffer.concat(chunks), args, fn); 175 | }); 176 | 177 | stream.on('error', fn); 178 | }, 179 | 180 | /** 181 | * Decrypt source stream passed as `options.source` and pass it to the stream specified in `options.dest`. 182 | * This is basicaly the same method as `decryptToFile()`. 183 | * 184 | * @param {Object} options Should contain 'source' and 'dest' keys that are streams. 185 | * @param {Function} fn Callback 186 | * @api public 187 | */ 188 | decryptToStream: function (options, fn){ 189 | spawnGPG.streaming(options, ['--decrypt'], fn); 190 | }, 191 | 192 | /** 193 | * Clearsign `str` and pass the signed message to the callback `fn`. 194 | * 195 | * @param {String|Buffer} str String to clearsign. 196 | * @param {Array} [args] Array of additonal gpg arguments. 197 | * @param {Function} fn Callback containing the signed message Buffer. 198 | * @api public 199 | */ 200 | clearsign: function(str, args, fn){ 201 | spawnGPG(str, ['--clearsign'], args, fn); 202 | }, 203 | 204 | /** 205 | * Verify `str` and pass the output to the callback `fn`. 206 | * 207 | * @param {String|Buffer} str Signature to verify. 208 | * @param {Array} [args] Array of additonal gpg arguments. 209 | * @param {Function} [fn] Callback containing the signed message Buffer. 210 | * @api public 211 | */ 212 | verifySignature: function(str, args, fn){ 213 | // Set logger fd, verify otherwise outputs to stderr for whatever reason 214 | var defaultArgs = ['--logger-fd', '1', '--verify']; 215 | spawnGPG(str, defaultArgs, args, fn); 216 | }, 217 | 218 | /** 219 | * Add a key to the keychain by filename. 220 | * 221 | * @param {String} fileName Key filename. 222 | * @param {Array} [args] Array of additonal gpg arguments. 223 | * @param {Function} [fn] Callback containing the signed message Buffer. 224 | * @api public 225 | */ 226 | importKeyFromFile: function(fileName, args, fn){ 227 | if (typeof args === 'function') { 228 | fn = args; 229 | args = []; 230 | } 231 | 232 | var self = this; 233 | 234 | fs.readFile(fileName, function(readErr, str) { 235 | if (readErr) return fn(readErr); 236 | self.importKey(str, args, fn); 237 | }); 238 | }, 239 | 240 | /** 241 | * Add an ascii-armored key to gpg. Expects the key to be passed as input. 242 | * 243 | * @param {String} keyStr Key string (armored). 244 | * @param {Array} args Optional additional arguments to pass to gpg. 245 | * @param {Function} fn Callback containing the signed message Buffer. 246 | * @api public 247 | */ 248 | importKey: function(keyStr, args, fn){ 249 | if (typeof args === 'function') { 250 | fn = args; 251 | args = []; 252 | } 253 | 254 | // Set logger fd, verify otherwise outputs to stderr for whatever reason 255 | var defaultArgs = ['--logger-fd', '1', '--import']; 256 | 257 | spawnGPG(keyStr, defaultArgs, args, function(importError, result) { 258 | if (importError) { 259 | // Ignorable errors 260 | if (/already in secret keyring/.test(importError.message)) { 261 | result = importError.message; 262 | } else { 263 | return fn(importError); 264 | } 265 | } 266 | // Grab key fingerprint and send it back as second arg 267 | var match = result.toString().match(keyRegex); 268 | fn(null, result.toString(), match && match[1]); 269 | }); 270 | }, 271 | 272 | /** 273 | * Removes a key by fingerprint. Warning: this will remove both pub and privkeys! 274 | * 275 | * @param {String} keyID Key fingerprint. 276 | * @param {Array} [args] Array of additonal gpg arguments. 277 | * @param {Function} fn Callback containing the signed message Buffer. 278 | * @api public 279 | */ 280 | removeKey: function(keyID, args, fn){ 281 | // Set logger fd, verify otherwise outputs to stderr for whatever reason 282 | var defaultArgs = ['--logger-fd', '1', '--delete-secret-and-public-key']; 283 | spawnGPG(keyID, defaultArgs, args, fn); 284 | } 285 | 286 | }; 287 | 288 | /** 289 | * Expose `GPG` object. 290 | */ 291 | module.exports = GPG; 292 | -------------------------------------------------------------------------------- /lib/spawnGPG.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var spawn = require('child_process').spawn; 4 | var globalArgs = ['--batch']; 5 | var readStream = require('fs').createReadStream; 6 | var writeStream = require('fs').createWriteStream; 7 | 8 | /** 9 | * Wrapper around spawning GPG. Handles stdout, stderr, and default args. 10 | * 11 | * @param {String} input Input string. Piped to stdin. 12 | * @param {Array} defaultArgs Default arguments for this task. 13 | * @param {Array} args Arguments to pass to GPG when spawned. 14 | * @param {Function} cb Callback. 15 | */ 16 | module.exports = function(input, defaultArgs, args, cb) { 17 | // Allow calling with (input, defaults, cb) 18 | if (typeof args === 'function'){ 19 | cb = args; 20 | args = []; 21 | } 22 | 23 | cb = once(cb); 24 | 25 | var gpgArgs = (args || []).concat(defaultArgs); 26 | var buffers = []; 27 | var buffersLength = 0; 28 | var error = ''; 29 | var gpg = spawnIt(gpgArgs, cb); 30 | 31 | gpg.stdout.on('data', function (buf){ 32 | buffers.push(buf); 33 | buffersLength += buf.length; 34 | }); 35 | 36 | gpg.stderr.on('data', function(buf){ 37 | error += buf.toString('utf8'); 38 | }); 39 | 40 | gpg.on('close', function(code){ 41 | var msg = Buffer.concat(buffers, buffersLength); 42 | if (code !== 0) { 43 | // If error is empty, we probably redirected stderr to stdout (for verifySignature, import, etc) 44 | return cb(new Error(error || msg)); 45 | } 46 | 47 | cb(null, msg, error); 48 | }); 49 | 50 | gpg.stdin.end(input); 51 | }; 52 | 53 | /** 54 | * Similar to spawnGPG, but sets up a read/write pipe to/from a stream. 55 | * 56 | * @param {Object} options Options. Should have source and dest strings or streams. 57 | * @param {Array} args GPG args. 58 | * @param {Function} cb Callback 59 | */ 60 | module.exports.streaming = function(options, args, cb) { 61 | cb = once(cb); 62 | options = options || {}; 63 | 64 | var isSourceStream = isStream(options.source); 65 | var isDestStream = isStream(options.dest); 66 | 67 | if (typeof options.source !== 'string' && !isSourceStream){ 68 | return cb(new Error('Missing \'source\' option (string or stream)')); 69 | } else if (typeof options.dest !== 'string' && !isDestStream){ 70 | return cb(new Error('Missing \'dest\' option (string or stream)')); 71 | } 72 | 73 | var sourceStream; 74 | if (!isSourceStream) { 75 | // This will throw if the file doesn't exist 76 | try { 77 | sourceStream = readStream(options.source); 78 | } catch(e) { 79 | return cb(new Error(options.source + ' does not exist. Error: ' + e.message)); 80 | } 81 | } else { 82 | sourceStream = options.source; 83 | } 84 | 85 | var destStream; 86 | if (!isDestStream) { 87 | try { 88 | destStream = writeStream(options.dest); 89 | } catch(e) { 90 | return cb(new Error('Error opening ' + options.dest + '. Error: ' + e.message)); 91 | } 92 | } else { 93 | destStream = options.dest; 94 | } 95 | 96 | // Go for it 97 | var gpg = spawnIt(args, cb); 98 | 99 | if (!isDestStream) { 100 | gpg.on('close', function (code){ 101 | cb(null); 102 | }); 103 | } else { 104 | cb(null, destStream); 105 | } 106 | 107 | // Pipe input file into gpg stdin; gpg stdout into output file.. 108 | sourceStream.pipe(gpg.stdin); 109 | gpg.stdout.pipe(destStream); 110 | }; 111 | 112 | // Wrapper around spawn. Catches error events and passed global args. 113 | function spawnIt(args, fn) { 114 | var gpg = spawn('gpg', globalArgs.concat(args || []) ); 115 | gpg.on('error', fn); 116 | return gpg; 117 | } 118 | 119 | // Ensures a callback is only ever called once. 120 | function once(fn) { 121 | var called = false; 122 | return function() { 123 | if (called) return; 124 | called = true; 125 | fn.apply(this, arguments); 126 | }; 127 | } 128 | 129 | // Check if input is stream with duck typing 130 | function isStream (stream) { 131 | return stream != null && typeof stream === 'object' && typeof stream.pipe === 'function'; 132 | }; 133 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Nicholas Penree (http://penr.ee)", 3 | "name": "gpg", 4 | "description": "GPG encryption and decryption in node.js by way of the gpg command-line tool", 5 | "version": "0.6.0", 6 | "repository": { 7 | "type": "git", 8 | "url": "git://github.com/drudge/node-gpg.git" 9 | }, 10 | "keywords": [ 11 | "gpg", 12 | "encrypt", 13 | "decrypt", 14 | "pgp", 15 | "gnupg" 16 | ], 17 | "main": "./lib/gpg", 18 | "engines": { 19 | "node": ">= 0.10.0" 20 | }, 21 | "dependencies": {}, 22 | "devDependencies": { 23 | "eslint": "^2.10.2", 24 | "mocha": "2.x" 25 | }, 26 | "scripts": { 27 | "pretest": "eslint .", 28 | "test": "mocha" 29 | }, 30 | "bugs": { 31 | "url": "https://github.com/drudge/node-gpg/issues" 32 | }, 33 | "homepage": "https://github.com/drudge/node-gpg#readme", 34 | "directories": { 35 | "example": "examples", 36 | "test": "test" 37 | }, 38 | "license": "MIT" 39 | } 40 | -------------------------------------------------------------------------------- /test/hello.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drudge/node-gpg/986cff9c66bc0e51b515d0d95c7eba663d93a16e/test/hello.gpg -------------------------------------------------------------------------------- /test/hello.txt: -------------------------------------------------------------------------------- 1 | Hello World 2 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var gpg = require('../'); 3 | var path = require('path'); 4 | var fs = require('fs'); 5 | var Stream = require('stream'); 6 | var encryptedString; 7 | 8 | /*global describe,it*/ 9 | describe('gpg', function(){ 10 | 11 | describe('import keys', function() { 12 | it('should import the pubkey from file for the remaining tests', function(done) { 13 | gpg.importKeyFromFile(path.join(__dirname, 'test.pub.asc'), function(err, result, fingerprint) { 14 | assert.ifError(err); 15 | assert.ok(/Total number processed: 1/.test(result)); 16 | assert.ok(/key 6F20F59D:/.test(result)); 17 | assert.ok(fingerprint === '6F20F59D'); 18 | done(); 19 | }); 20 | }); 21 | 22 | it('should import the privkey as string for the remaining tests', function(done) { 23 | fs.readFile(path.join(__dirname, 'test.priv.asc'), function(err, file) { 24 | assert.ifError(err); 25 | gpg.importKey(file, function(importErr, result, fingerprint) { 26 | assert.ifError(importErr); 27 | assert.ok(/secret keys read: 1/.test(result)); 28 | assert.ok(/key 6F20F59D:/.test(result)); 29 | assert.ok(fingerprint === '6F20F59D'); 30 | done(); 31 | }); 32 | }); 33 | }); 34 | 35 | it('importing a missing file errors', function(done) { 36 | gpg.importKeyFromFile(path.join(__dirname, 'test.pub.asc1'), function(err, result) { 37 | assert.ok(err); 38 | assert.ok(err.code === 'ENOENT'); 39 | done(); 40 | }); 41 | }); 42 | 43 | it('importing a malformed file errors', function(done) { 44 | gpg.importKeyFromFile(path.join(__dirname, 'index.js'), function(err, result) { 45 | assert.ok(err); 46 | assert.ok(/no valid OpenPGP data found/.test(err.message)); 47 | done(); 48 | }); 49 | }); 50 | }); 51 | 52 | describe('encrypt', function(){ 53 | it('should encrypt data', function(done){ 54 | var mysecret = 'Hello World'; 55 | var args = [ 56 | '--default-key', '6F20F59D', 57 | '--recipient', '6F20F59D', 58 | '--armor', 59 | '--trust-model', 'always' // so we don't get "no assurance this key belongs to the given user" 60 | ]; 61 | gpg.encrypt(mysecret, args, function(err, encrypted){ 62 | assert.ifError(err); 63 | assert.ok(encrypted.length); 64 | encryptedString = encrypted.toString(); 65 | assert.ok(/BEGIN PGP MESSAGE/.test(encryptedString)); 66 | done(); 67 | }); 68 | }); 69 | 70 | it('should encrypt stream with callStreaming()', function (done) { 71 | var args = [ 72 | '--encrypt', 73 | '--default-key', '6F20F59D', 74 | '--recipient', '6F20F59D', 75 | '--armor', 76 | '--trust-model', 'always', // so we don't get "no assurance this key belongs to the given user" 77 | ]; 78 | 79 | var inStream = fs.createReadStream('./test/hello.txt'); 80 | var outStream = new Stream.PassThrough; 81 | 82 | gpg.callStreaming(inStream, outStream, args, function (err) { 83 | assert.ifError(err); 84 | var out = []; 85 | outStream.on('data', function (data) { 86 | out.push(data); 87 | }); 88 | outStream.on('end', function () { 89 | var res = Buffer.concat(out).toString(); 90 | assert.ok(/BEGIN PGP MESSAGE/.test(res)); 91 | done(); 92 | }); 93 | outStream.on('error', function (error) { 94 | console.log('ERROR', error); 95 | done(error); 96 | }); 97 | }); 98 | }); 99 | 100 | it('should encrypt stream with encryptStream()', function (done) { 101 | var args = [ 102 | '--default-key', '6F20F59D', 103 | '--recipient', '6F20F59D', 104 | '--armor', 105 | '--trust-model', 'always', // so we don't get "no assurance this key belongs to the given user" 106 | ]; 107 | 108 | var inStream = fs.createReadStream('./test/hello.txt'); 109 | 110 | gpg.encryptStream(inStream, args, function (err, res) { 111 | assert.ifError(err); 112 | assert.ok(/BEGIN PGP MESSAGE/.test(res)); 113 | done(); 114 | }); 115 | }); 116 | }); 117 | 118 | describe('decrypt', function(){ 119 | it('should decrypt strings', function(done){ 120 | gpg.decrypt(encryptedString, function(err, decrypted){ 121 | assert.ifError(err); 122 | assert.ok(decrypted.length); 123 | assert.equal(decrypted.toString('utf8'), 'Hello World'); 124 | done(); 125 | }); 126 | }); 127 | 128 | it('should provide stderr output for successful calls', function(done) { 129 | gpg.decrypt(encryptedString, function(err, decrypted, stderr){ 130 | assert.ifError(err); 131 | assert.ok(/ID C343C0BC/.test(stderr)); // key information is sent to stderr by gpg 132 | assert.equal(decrypted.toString('utf8'), 'Hello World'); 133 | done(); 134 | }); 135 | }); 136 | 137 | it('should decrypt Buffers', function(done){ 138 | var encryptedBuffer = new Buffer(encryptedString); 139 | gpg.decrypt(encryptedBuffer, function(err, decrypted){ 140 | assert.ifError(err); 141 | assert.ok(decrypted.length); 142 | assert.equal(decrypted.toString('utf8'), 'Hello World'); 143 | done(); 144 | }); 145 | }); 146 | 147 | it('should decrypt files', function(done){ 148 | gpg.call('', [ '--skip-verify', '--passphrase-fd', '0', '--decrypt', './test/hello.gpg' ], function(err, decrypted){ 149 | assert.ifError(err); 150 | assert.ok(decrypted.length); 151 | assert.equal(decrypted.toString('utf8'), 'Hello World\n'); 152 | done(); 153 | }); 154 | }); 155 | 156 | it('should decrypt stream with callStreaming()', function (done) { 157 | var args = [ 158 | '--decrypt', 159 | '--default-key', '6F20F59D', 160 | '--recipient', '6F20F59D', 161 | '--trust-model', 'always', // so we don't get "no assurance this key belongs to the given user" 162 | ]; 163 | 164 | var inStream = fs.createReadStream('./test/hello.gpg'); 165 | var outStream = new Stream.PassThrough; 166 | 167 | gpg.callStreaming(inStream, outStream, args, function (err) { 168 | assert.ifError(err); 169 | var out = []; 170 | outStream.on('data', function (data) { 171 | out.push(data); 172 | }); 173 | outStream.on('end', function () { 174 | var res = Buffer.concat(out).toString(); 175 | assert.ok(/Hello World/.test(res)); 176 | done(); 177 | }); 178 | outStream.on('error', function (error) { 179 | console.log('ERROR', error); 180 | done(error); 181 | }); 182 | }); 183 | }); 184 | 185 | it('should decrypt stream with decryptStream()', function (done) { 186 | var args = [ 187 | '--default-key', '6F20F59D', 188 | '--recipient', '6F20F59D', 189 | '--trust-model', 'always', // so we don't get "no assurance this key belongs to the given user" 190 | ]; 191 | 192 | var inStream = fs.createReadStream('./test/hello.gpg'); 193 | 194 | gpg.decryptStream(inStream, args, function (err, res) { 195 | assert.ifError(err); 196 | assert.ok(/Hello World/.test(res)); 197 | done(); 198 | }); 199 | }); 200 | }); 201 | 202 | describe('clearsign', function(){ 203 | it('should clearsign data', function(done){ 204 | var mymessage = 'Hello, this is me!'; 205 | var args = [ 206 | '--trust-model', 'always' 207 | , '--default-key', '6F20F59D' 208 | ]; 209 | gpg.clearsign(mymessage, args, function(err, clearsigned){ 210 | assert.ifError(err); 211 | assert.ok(clearsigned.length); 212 | done(); 213 | }); 214 | }); 215 | }); 216 | 217 | describe('verifying signature', function(){ 218 | it('should verify signature on data', function(done){ 219 | var mymessage = 'Hello, this is me!'; 220 | var args = [ 221 | '--trust-model', 'always' 222 | , '--default-key', '6F20F59D' 223 | ]; 224 | gpg.clearsign(mymessage, args, function(err, clearsigned){ 225 | assert.ifError(err); 226 | assert.ok(clearsigned.length); 227 | gpg.verifySignature(clearsigned, function(verifyErr, result) { 228 | assert.ifError(verifyErr); 229 | assert.ok(/good signature/i, result.toString()); 230 | done(); 231 | }); 232 | }); 233 | }); 234 | }); 235 | 236 | describe('remove keys', function() { 237 | it('should remove both keys', function(done) { 238 | gpg.removeKey('6F20F59D', function(err, result) { 239 | assert.ifError(err); 240 | console.log(result.toString()); 241 | done(); 242 | }); 243 | }); 244 | }); 245 | 246 | }); 247 | -------------------------------------------------------------------------------- /test/test.priv.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PRIVATE KEY BLOCK----- 2 | Comment: GPGTools - https://gpgtools.org 3 | 4 | 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 56 | k4Pto/xJIwSJ9sM2NALLnqPcAHMJaACPw6s= 57 | =5GE/ 58 | -----END PGP PRIVATE KEY BLOCK----- 59 | -------------------------------------------------------------------------------- /test/test.pub.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | Comment: GPGTools - https://gpgtools.org 3 | 4 | mQENBFVsjloBCACfhbQ7HwMqTeMNsGJTf6GYNG/21WqZT2+TfWbtf9f69BTlV53W 5 | 8DKFEEMHWw+Tq8yURN5hWR+s/BH3wRlfvLyWmThm7Z6c6Py/bcDAmnRirPtXLOU6 6 | dE0Pmr5N9uMHZpZoOrtSpZUbsY9qi0q3yZHZKJPe/+tMScDUKRApjvLGrf2HtyZK 7 | 6ojngWhBpLrlAFzGvftyqjp54S4EXyg04zvuHs6t57MwfoFA0k48vINm5ESingo+ 8 | uHpq+ApUhfsvyVRLIGrMITsLL6yp4EFzDjO50mivnJ9nWkKQclCx/9XfdorzbyvG 9 | MNZdBjMwizdWFzV+XaEEnm1avIWQavMMje9FABEBAAG0Rk5vZGUtR1BHIFRlc3Qg 10 | S2V5IChLZXlwYWlyIHVzZWQgZm9yIG5vZGUtZ3BnIHRlc3RpbmcuKSA8dGVzdEB0 11 | ZXN0LmNvbT6JATcEEwEKACEFAlVsjloCGwMFCwkIBwMFFQoJCAsFFgIDAQACHgEC 12 | F4AACgkQgzdEOG8g9Z1YvQf7BtJkD9AntsVrIN16lTJCWOoTXUC4iDaWfpe4UF11 13 | W+OfWdDrAUC7JFDwTiWMn86oaXMeX2NnP5ve2VYQA3aCw7uW8u3tySeC7cCfwkAM 14 | eBlLbZiv+lYUzN99oRhTx6EmYBa/g8Y8VVgj/kJb8yRfBsVHI5wqlvJOdqATiGUh 15 | dmsuaPvS3blPB0S2obE/2ix1hM9rMERyns8zII7QS5+dkZmcblVK/ltCw6ikHZtY 16 | vSQjw3hMFvIgiChHLKK0BQ5iEuECSA6fsLbMrQQCjsFgV9QmsHKuV6AFd0XDUr+Q 17 | XCeNVAqPmu8NbRiFKOjE41Vqy2bEDRHGhD98eHjvQzNAEbkBDQRVbI5aAQgArIuo 18 | jfSFLw7h2dJhdfwXVSW2CpJo7ubkif222W64N93m0ZeOKb1nv9lT+qr7Hcpbf8uk 19 | wFkONtldHiW+H9W0fC+wctIWYTQhrwVrpUAIjuxATAXqLS/45mEU63tZL6Gkl2IJ 20 | ItQM9BdZLLnkv+hCYLic20CDbv0EoWO70efMLkAJXhmlkLbivE2jdqKqish/1z5R 21 | lRJFJOWob8jdzFbHh3F69zvxEjsYPw/vr27W4+ZtLwprJVbMs3wdS+d5DD1IryZx 22 | F2kHRPmj35eBaz6evDm2NRzVfTbssOQhVWs0eU6QJwQDKj+VGXvGvr+ZvW60eTge 23 | jD63wIHDKfYRH9OqiwARAQABiQEfBBgBCgAJBQJVbI5aAhsMAAoJEIM3RDhvIPWd 24 | m1MH/jK3nvmyuhDZ9rZwizxYFh0BNTgZKRMn2FHMrQYTDo2rwLbKEBC9/6BXQH3A 25 | kz+sNTiDYlY2osUvRfmOVku3QdaN2oloGbbYuym0ZnwI/dhqwZhYL7gLasW8ZAgu 26 | uFOKCo5auY1MjvMjC2Mn14r/f4fYYFoqEYQmKSvLmFktcPou8E4w/qkd+8tKK4xv 27 | fCbJGjoZdRQI2Rflz8mfC3B9NXvePW/wgYXNDck9JG6ARqWrozxy+VXcHSN4HyDZ 28 | jn9fF4Fx706Lp/saiasNl97czOR0WXAsjTBSFPZp3HCRJ/b2TW/SVsLFk4Pto/xJ 29 | IwSJ9sM2NALLnqPcAHMJaACPw6s= 30 | =rxX0 31 | -----END PGP PUBLIC KEY BLOCK----- 32 | --------------------------------------------------------------------------------