diff --git a/Makefile b/Makefile index ab7ed556..e83c482b 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,7 @@ $(BUILD_STAMP): \ lib/basex.js \ lib/bn.js \ lib/const.js \ + lib/consttime.js \ lib/dsa.js \ lib/ecc/base.js \ lib/ecc/curves.js \ diff --git a/dev/decrypt.iced b/dev/decrypt.iced index bcc0bd8c..777149f4 100644 --- a/dev/decrypt.iced +++ b/dev/decrypt.iced @@ -6,10 +6,12 @@ C = require '../src/const' {KeyBlock} = require '../src/openpgp/processor' util = require 'util' {ASP} = require '../src/util' -{KeyManager} = require '../src/keymanager' +{KeyManager} = require '../src/openpgp/keymanager' {import_key_pgp} = require '../src/symmetric' {decrypt} = require '../src/openpgp/ocfb' +test_time = Math.floor(new Date(2014, 2, 21)/1000) + msg = """-----BEGIN PGP MESSAGE----- Version: GnuPG/MacGPG2 v2.0.20 (Darwin) Comment: GPGTools - http://gpgtools.org @@ -194,19 +196,23 @@ nMd8vYZjDx7ro+5buf2cPmeiYlJdKQ== passphrase = "catsdogs" asp = new ASP {} -await KeyManager.import_from_armored_pgp { raw : armored_key, asp }, defer err, km +opts = now : test_time +await KeyManager.import_from_armored_pgp { raw : armored_key, asp, opts }, defer err, km throw err if err? await km.unlock_pgp { passphrase }, defer err throw err if err? -await KeyManager.import_from_armored_pgp { raw : armored_verify_key, asp }, defer err, vkm +await KeyManager.import_from_armored_pgp { raw : armored_verify_key, asp, opts }, defer err, vkm throw err if err? km = km.find_pgp_key packets[0].key_id console.log km -console.log packets[0].ekey.y.toString(16) -await km.key.decrypt_and_unpad packets[0].ekey.y, defer err, key +console.log packets[0].ekey.y().toString(16) +await km.key.decrypt_and_unpad packets[0].ekey, {}, defer err, unpad throw err if err? +throw new Error "Unable to decrypt" unless unpad.valid +key = unpad.ret cipher = import_key_pgp key -pt = decrypt { cipher, ciphertext : packets[1].ciphertext } +await decrypt { cipher, ciphertext : packets[1].ciphertext }, defer err, pt +throw err if err console.log util.inspect pt, { depth : null } console.log pt.length [err, packets] = parse pt diff --git a/dev/decrypt2.iced b/dev/decrypt2.iced index 26fa8f9c..da4524e9 100644 --- a/dev/decrypt2.iced +++ b/dev/decrypt2.iced @@ -6,10 +6,12 @@ C = require '../src/const' {KeyBlock} = require '../src/openpgp/processor' util = require 'util' {ASP} = require '../src/util' -{KeyManager} = require '../src/keymanager' +{KeyManager} = require '../src/openpgp/keymanager' {import_key_pgp} = require '../src/symmetric' {decrypt} = require '../src/openpgp/ocfb' +test_time = Math.floor(new Date(2014, 2, 21)/1000) + msg = """-----BEGIN PGP MESSAGE----- Version: GnuPG/MacGPG2 v2.0.22 (Darwin) Comment: GPGTools - http://gpgtools.org @@ -175,19 +177,23 @@ nMd8vYZjDx7ro+5buf2cPmeiYlJdKQ== passphrase = "catsdogs" asp = new ASP {} -await KeyManager.import_from_armored_pgp { raw : armored_key, asp }, defer err, km +opts = now : test_time +await KeyManager.import_from_armored_pgp { raw : armored_key, asp, opts }, defer err, km throw err if err? await km.unlock_pgp { passphrase }, defer err throw err if err? -await KeyManager.import_from_armored_pgp { raw : armored_verify_key, asp }, defer err, vkm +await KeyManager.import_from_armored_pgp { raw : armored_verify_key, asp, opts }, defer err, vkm throw err if err? km = km.find_pgp_key packets[0].key_id console.log km -console.log packets[0].ekey.y.toString(16) -await km.key.decrypt_and_unpad packets[0].ekey.y, defer err, key +console.log packets[0].ekey.y().toString(16) +await km.key.decrypt_and_unpad packets[0].ekey, {}, defer err, unpad throw err if err? +throw new Error "Unable to decrypt" unless unpad.valid +key = unpad.ret cipher = import_key_pgp key -pt = decrypt { cipher, ciphertext : packets[1].ciphertext } +await decrypt { cipher, ciphertext : packets[1].ciphertext }, defer err, pt +throw err if err console.log util.inspect pt, { depth : null } console.log pt.length [err, packets] = parse pt diff --git a/lib/consttime.js b/lib/consttime.js new file mode 100644 index 00000000..22847592 --- /dev/null +++ b/lib/consttime.js @@ -0,0 +1,62 @@ +// Generated by IcedCoffeeScript 108.0.11 +(function() { + var eq_byte, eq_int, is_zero, normalize, read_byte, read_uint16_be, select_buffer, select_byte, select_int; + + exports.is_zero = is_zero = function(x) { + x = x | 0; + return (((x | -x) >>> 31) ^ 1) | 0; + }; + + exports.eq_byte = eq_byte = function(a, b) { + return is_zero(((a ^ b) & 0xff) | 0); + }; + + exports.eq_int = eq_int = function(a, b) { + return is_zero(((a | 0) ^ (b | 0)) | 0); + }; + + exports.normalize = normalize = function(x) { + if (x) { + return 1; + } else { + return 0; + } + }; + + exports.select_byte = select_byte = function(v, x, y) { + var fb, mask, rb; + mask = -v | 0; + rb = x & 0xff; + fb = y & 0xff; + return (fb ^ (mask & (fb ^ rb))) & 0xff; + }; + + exports.select_int = select_int = function(v, x, y) { + var mask, xi, yi; + mask = -v | 0; + xi = x | 0; + yi = y | 0; + return (yi ^ (mask & (yi ^ xi))) | 0; + }; + + exports.read_byte = read_byte = function(buf, i) { + return (buf[i] || 0) & 0xff; + }; + + exports.read_uint16_be = read_uint16_be = function(buf, i) { + return ((read_byte(buf, i) << 8) | read_byte(buf, i + 1)) & 0xffff; + }; + + exports.select_buffer = select_buffer = function(v, x, y) { + var fb, i, mask, out, rb, _i, _ref; + mask = -v | 0; + out = Buffer.alloc(y.length); + for (i = _i = 0, _ref = out.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { + rb = read_byte(x, i) & 0xff; + fb = read_byte(y, i) & 0xff; + out[i] = (fb ^ (mask & (fb ^ rb))) & 0xff; + } + return out; + }; + +}).call(this); diff --git a/lib/ecc/ecdh.js b/lib/ecc/ecdh.js index ad516f8e..6cf40312 100644 --- a/lib/ecc/ecdh.js +++ b/lib/ecc/ecdh.js @@ -95,7 +95,7 @@ return (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/ecc/ecdh.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/ecc/ecdh.iced", funcname: "Pub.encrypt" }); _this.curve.encrypt(_this.R, __iced_deferrals.defer({ @@ -185,7 +185,7 @@ return (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/ecc/ecdh.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/ecc/ecdh.iced", funcname: "Priv.decrypt" }); c.load_V(curve, esc(__iced_deferrals.defer({ @@ -281,7 +281,7 @@ (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/ecc/ecdh.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/ecc/ecdh.iced", funcname: "Pair.pad_and_encrypt" }); _this.pub.encrypt(m, { @@ -315,7 +315,7 @@ }; Pair.prototype.decrypt_and_unpad = function(ciphertext, _arg, cb) { - var err, fingerprint, m, ret, ___iced_passed_deferral, __iced_deferrals, __iced_k; + var err, fingerprint, ret, ___iced_passed_deferral, __iced_deferrals, __iced_k; __iced_k = __iced_k_noop; ___iced_passed_deferral = iced.findDeferral(arguments); fingerprint = _arg.fingerprint; @@ -324,7 +324,7 @@ return (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/ecc/ecdh.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/ecc/ecdh.iced", funcname: "Pair.decrypt_and_unpad" }); _this.priv.decrypt(ciphertext, { @@ -333,7 +333,7 @@ assign_fn: (function() { return function() { err = arguments[0]; - return m = arguments[1]; + return ret = arguments[1]; }; })(), lineno: 207 @@ -342,7 +342,15 @@ }); })(this)((function(_this) { return function() { - return cb(err, m, true); + if (err) { + return cb(err); + } else { + return cb(null, { + ret: ret, + valid: true, + pkcs5: true + }); + } }; })(this)); }; @@ -364,7 +372,7 @@ return (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/ecc/ecdh.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/ecc/ecdh.iced", funcname: "Pair.generate" }); generate({ @@ -379,7 +387,7 @@ return pair = arguments[1]; }; })(), - lineno: 218 + lineno: 219 })); __iced_deferrals._fulfill(); }); diff --git a/lib/elgamal.js b/lib/elgamal.js index 9049e5f1..f0a28271 100644 --- a/lib/elgamal.js +++ b/lib/elgamal.js @@ -52,7 +52,7 @@ return (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/elgamal.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/elgamal.iced", funcname: "Pub.encrypt" }); SRF().random_zn(_this.p.subtract(bn.nbv(2)), __iced_deferrals.defer({ @@ -173,7 +173,7 @@ return (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/elgamal.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/elgamal.iced", funcname: "Pair.pad_and_encrypt" }); eme_pkcs1_encode(data, _this.pub.p.mpi_byte_length(), __iced_deferrals.defer({ @@ -194,7 +194,7 @@ (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/elgamal.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/elgamal.iced", funcname: "Pair.pad_and_encrypt" }); _this.pub.encrypt(m, __iced_deferrals.defer({ @@ -222,15 +222,16 @@ }; Pair.prototype.decrypt_and_unpad = function(ciphertext, params, cb) { - var b, err, m, ret, ___iced_passed_deferral, __iced_deferrals, __iced_k; + var b, err, m, ret, valid, ___iced_passed_deferral, __iced_deferrals, __iced_k; __iced_k = __iced_k_noop; ___iced_passed_deferral = iced.findDeferral(arguments); err = ret = null; + valid = false; (function(_this) { return (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/elgamal.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/elgamal.iced", funcname: "Pair.decrypt_and_unpad" }); _this.priv.decrypt(ciphertext.c(), __iced_deferrals.defer({ @@ -240,7 +241,7 @@ return m = arguments[1]; }; })(), - lineno: 122 + lineno: 123 })); __iced_deferrals._fulfill(); }); @@ -249,9 +250,12 @@ var _ref4; if (err == null) { b = m.to_padded_octets(_this.pub.p); - _ref4 = eme_pkcs1_decode(b), err = _ref4[0], ret = _ref4[1]; + _ref4 = eme_pkcs1_decode(b), err = _ref4[0], valid = _ref4[1], ret = _ref4[2]; } - return cb(err, ret); + return cb(err, { + valid: valid, + ret: ret + }); }; })(this)); }; @@ -332,7 +336,7 @@ (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/elgamal.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/elgamal.iced", funcname: "Output.hide" }); key.hide({ @@ -346,7 +350,7 @@ return tmp = arguments[1]; }; })(), - lineno: 164 + lineno: 165 })); __iced_deferrals._fulfill(); })(function() { diff --git a/lib/elgamalse.js b/lib/elgamalse.js index 3c3def19..576c095e 100644 --- a/lib/elgamalse.js +++ b/lib/elgamalse.js @@ -1,6 +1,6 @@ // Generated by IcedCoffeeScript 108.0.11 (function() { - var ASP, BaseKey, BaseKeyPair, C, K, MRF, Pair, Priv, Pub, SRF, bn, bufeq_secure, eme_pkcs1_decode, eme_pkcs1_encode, konst, make_esc, _ref, _ref1, _ref2, _ref3, + var ASP, BaseKey, BaseKeyPair, C, K, MRF, Pair, Priv, Pub, SRF, bn, bufeq_secure, konst, make_esc, _ref, _ref1, _ref2, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; @@ -20,8 +20,6 @@ _ref2 = require('./rand'), SRF = _ref2.SRF, MRF = _ref2.MRF; - _ref3 = require('./pad'), eme_pkcs1_encode = _ref3.eme_pkcs1_encode, eme_pkcs1_decode = _ref3.eme_pkcs1_decode; - Pub = (function(_super) { __extends(Pub, _super); diff --git a/lib/openpgp/buffer.js b/lib/openpgp/buffer.js index 9dbd22dd..03d294e5 100644 --- a/lib/openpgp/buffer.js +++ b/lib/openpgp/buffer.js @@ -26,43 +26,52 @@ }; SlicerBuffer.prototype.len = function() { - return this.buf.length - this.start; + return this.limit() - this.start; }; SlicerBuffer.prototype.rem = function() { - return this.buf.length - this.i; + return this.limit() - this.i; }; SlicerBuffer.prototype.offset = function() { return this.i - this.start; }; - SlicerBuffer.prototype.check = function() { - if ((this._end && this.i > this._end) || (this.i > this.buf.length)) { + SlicerBuffer.prototype.limit = function() { + if (this._end != null) { + return Math.min(this._end, this.buf.length); + } else { + return this.buf.length; + } + }; + + SlicerBuffer.prototype.check_available = function(n) { + if (n < 0 || this.i + n > this.limit()) { throw new Error("read off the end of the packet @" + this.i + "/" + this.buf.length + "/" + this._end); } }; SlicerBuffer.prototype.read_uint8 = function() { var ret; - ret = this.buf.readUInt8(this.i++); - this.check(); + this.check_available(1); + ret = this.buf.readUInt8(this.i); + this.i++; return ret; }; SlicerBuffer.prototype.read_uint16 = function() { var ret; + this.check_available(2); ret = this.buf.readUInt16BE(this.i); this.i += 2; - this.check(); return ret; }; SlicerBuffer.prototype.read_uint32 = function() { var ret; + this.check_available(4); ret = this.buf.readUInt32BE(this.i); this.i += 4; - this.check(); return ret; }; @@ -72,14 +81,14 @@ SlicerBuffer.prototype.read_buffer = function(l) { var ret; + this.check_available(l); ret = this.buf.slice(this.i, this.i + l); this.i += l; - this.check(); return ret; }; SlicerBuffer.prototype.end = function() { - return this._end || this.buf.length; + return this.limit(); }; SlicerBuffer.prototype.peek_rest_to_buffer = function() { @@ -105,10 +114,12 @@ }; SlicerBuffer.prototype.peek_uint8 = function() { + this.check_available(1); return this.buf.readUInt8(this.i); }; SlicerBuffer.prototype.peek_uint16 = function() { + this.check_available(2); return this.buf.readUInt16BE(this.i); }; diff --git a/lib/openpgp/packet/sess.js b/lib/openpgp/packet/sess.js index d3323715..121575ad 100644 --- a/lib/openpgp/packet/sess.js +++ b/lib/openpgp/packet/sess.js @@ -1,6 +1,6 @@ // Generated by IcedCoffeeScript 108.0.11 (function() { - var ASP, C, Decryptor, MDC, MDC_Parser, PKESK, PKESK_Parser, Packet, SEIPD, SEIPD_Parser, SHA1, asymmetric, bufeq_fast, bufeq_secure, encrypt, iced, make_esc, streamers, uint_to_buffer, __iced_k, __iced_k_noop, _ref, _ref1, _ref2, + var ASP, C, Decryptor, MDC, MDC_Parser, PKESK, PKESK_Parser, Packet, SEIPD, SEIPD_Parser, SHA1, asymmetric, bufeq_secure, encrypt, iced, make_esc, streamers, uint_to_buffer, __iced_k, __iced_k_noop, _ref, _ref1, _ref2, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; @@ -15,7 +15,7 @@ _ref = require('../../hash'), SHA1 = _ref.SHA1, streamers = _ref.streamers; - _ref1 = require('../../util'), uint_to_buffer = _ref1.uint_to_buffer, bufeq_secure = _ref1.bufeq_secure, bufeq_fast = _ref1.bufeq_fast; + _ref1 = require('../../util'), uint_to_buffer = _ref1.uint_to_buffer, bufeq_secure = _ref1.bufeq_secure; _ref2 = require('../ocfb'), encrypt = _ref2.encrypt, Decryptor = _ref2.Decryptor; @@ -59,7 +59,7 @@ return (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/packet/sess.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/packet/sess.iced", funcname: "PKESK.write" }); _this.write_unframed(__iced_deferrals.defer({ @@ -105,7 +105,7 @@ SEIPD.prototype.check = function() {}; SEIPD.prototype.decrypt = function(_arg, cb) { - var asp, cipher, eng, err, esc, mdc, plaintext, prefix, pt, ___iced_passed_deferral, __iced_deferrals, __iced_k; + var asp, cipher, eng, err, esc, mdc, plaintext, prefix, pt, valid_hash, valid_mdc, ___iced_passed_deferral, __iced_deferrals, __iced_k; __iced_k = __iced_k_noop; ___iced_passed_deferral = iced.findDeferral(arguments); cipher = _arg.cipher, asp = _arg.asp; @@ -120,12 +120,17 @@ return (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/packet/sess.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/packet/sess.iced", funcname: "SEIPD.decrypt" }); - eng.check(esc(__iced_deferrals.defer({ + eng.check(__iced_deferrals.defer({ + assign_fn: (function() { + return function() { + return err = arguments[0]; + }; + })(), lineno: 63 - }))); + })); __iced_deferrals._fulfill(); }); })(this)((function(_this) { @@ -133,7 +138,7 @@ (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/packet/sess.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/packet/sess.iced", funcname: "SEIPD.decrypt" }); eng.dec(esc(__iced_deferrals.defer({ @@ -147,12 +152,12 @@ __iced_deferrals._fulfill(); })(function() { var _ref3; - _ref3 = MDC.parse(pt), mdc = _ref3[0], plaintext = _ref3[1]; + _ref3 = MDC.parse(pt), valid_mdc = _ref3[0], mdc = _ref3[1], plaintext = _ref3[2]; prefix = eng.get_prefix(); (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/packet/sess.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/packet/sess.iced", funcname: "SEIPD.decrypt" }); mdc.compute({ @@ -164,7 +169,11 @@ }))); __iced_deferrals._fulfill(); })(function() { - err = mdc.check() ? null : new Error("MDC mismatch"); + valid_hash = mdc.check(); + if (err || !(valid_mdc && valid_hash)) { + plaintext = null; + err = new Error("Unable to decrypt"); + } return cb(err, plaintext); }); }); @@ -184,7 +193,7 @@ return (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/packet/sess.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/packet/sess.iced", funcname: "SEIPD.encrypt" }); mdc.compute({ @@ -197,7 +206,7 @@ return mdc_buf = arguments[0]; }; })(), - lineno: 81 + lineno: 87 }))); __iced_deferrals._fulfill(); }); @@ -207,7 +216,7 @@ (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/packet/sess.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/packet/sess.iced", funcname: "SEIPD.encrypt" }); encrypt({ @@ -220,7 +229,7 @@ return __slot_1.ciphertext = arguments[0]; }; })(_this), - lineno: 83 + lineno: 89 }))); __iced_deferrals._fulfill(); })(function() { @@ -246,7 +255,7 @@ return (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/packet/sess.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/packet/sess.iced", funcname: "SEIPD.write" }); _this.write_unframed(__iced_deferrals.defer({ @@ -256,7 +265,7 @@ return unframed = arguments[1]; }; })(), - lineno: 97 + lineno: 103 })); __iced_deferrals._fulfill(); }); @@ -325,7 +334,7 @@ (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/packet/sess.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/packet/sess.iced", funcname: "MDC.compute" }); asp.progress({ @@ -333,7 +342,7 @@ total: plaintext.length, i: i }, esc(__iced_deferrals.defer({ - lineno: 122 + lineno: 128 }))); __iced_deferrals._fulfill(); })(_next); @@ -364,17 +373,15 @@ } MDC_Parser.prototype.parse = function() { - var chunk, digest, hl, len, rem; + var chunk, digest, hl, len, rem, valid; hl = MDC.header.length; len = SHA1.output_length + hl; rem = this.buf.slice(0, -len); chunk = this.buf.slice(-len); - if (!bufeq_fast(chunk.slice(0, hl), MDC.header)) { - throw new Error('Missing MDC header'); - } + valid = bufeq_secure(chunk.slice(0, hl), MDC.header); digest = chunk.slice(hl); return [ - new MDC({ + valid, new MDC({ digest: digest }), rem ]; diff --git a/lib/openpgp/processor.js b/lib/openpgp/processor.js index 4173a78a..6330eca5 100644 --- a/lib/openpgp/processor.js +++ b/lib/openpgp/processor.js @@ -1,6 +1,6 @@ // Generated by IcedCoffeeScript 108.0.11 (function() { - var C, KeyBlock, Message, OPS, Warnings, armor, athrow, bufeq_secure, do_message, hashmod, iced, import_key_pgp, katch, konst, make_esc, parse, unix_time, util, verify_clearsign, verify_detached, __iced_k, __iced_k_noop, _ref; + var C, KeyBlock, Message, OPS, SRF, Warnings, armor, athrow, bufeq_secure, do_message, hashmod, iced, import_key_pgp_ct, katch, konst, make_esc, parse, unix_time, util, verify_clearsign, verify_detached, __iced_k, __iced_k_noop, _ref; iced = require('iced-runtime'); __iced_k = __iced_k_noop = function() {}; @@ -17,7 +17,9 @@ parse = require('./parser').parse; - import_key_pgp = require('../symmetric').import_key_pgp; + import_key_pgp_ct = require('../symmetric').import_key_pgp_ct; + + SRF = require('../rand').SRF; util = require('util'); @@ -118,7 +120,7 @@ (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/processor.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/processor.iced", funcname: "KeyBlock.process" }); _this._verify_sigs(__iced_deferrals.defer({ @@ -127,7 +129,7 @@ return err = arguments[0]; }; })(), - lineno: 87 + lineno: 88 })); __iced_deferrals._fulfill(); })(__iced_k); @@ -229,7 +231,7 @@ _continue() (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/processor.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/processor.iced", funcname: "KeyBlock._verify_sigs" }); p.verify(working_set, __iced_deferrals.defer({ @@ -238,7 +240,7 @@ _continue() return tmp = arguments[0]; }; })(), - lineno: 137 + lineno: 138 }), _this.opts); __iced_deferrals._fulfill(); })(function() { @@ -277,13 +279,14 @@ _continue() } Message.prototype._get_session_key = function(cb) { - var enc, err, esk_packets, fingerprint, index, key_ids, key_material, km, p, packet, pkcs5, privk, sesskey, ___iced_passed_deferral, __iced_deferrals, __iced_k; + var enc, err, esk_packets, fingerprint, index, key_ids, key_material, km, p, packet, pkcs5, privk, sesskey, unpad, valid, ___iced_passed_deferral, __iced_deferrals, __iced_k; __iced_k = __iced_k_noop; ___iced_passed_deferral = iced.findDeferral(arguments); key_ids = []; esk_packets = []; err = null; pkcs5 = false; + valid = false; key_ids = (function() { var _results; _results = []; @@ -301,7 +304,7 @@ _continue() (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/processor.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/processor.iced", funcname: "Message._get_session_key" }); _this.keyfetch.fetch(key_ids, konst.ops.decrypt, __iced_deferrals.defer({ @@ -312,7 +315,7 @@ _continue() return index = arguments[2]; }; })(), - lineno: 178 + lineno: 180 })); __iced_deferrals._fulfill(); })(function() { @@ -325,7 +328,7 @@ _continue() (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/processor.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/processor.iced", funcname: "Message._get_session_key" }); privk.decrypt_and_unpad(packet.ekey, { @@ -334,15 +337,14 @@ _continue() assign_fn: (function() { return function() { err = arguments[0]; - sesskey = arguments[1]; - return pkcs5 = arguments[2]; + return unpad = arguments[1]; }; })(), - lineno: 184 + lineno: 186 })); __iced_deferrals._fulfill(); })(function() { - return __iced_k(err == null ? _this.encryption_subkey = key_material : void 0); + return __iced_k(err == null ? (_this.encryption_subkey = key_material, (sesskey = unpad.ret, valid = unpad.valid, pkcs5 = unpad.pkcs5, unpad)) : void 0); }); } else { return __iced_k(); @@ -355,7 +357,7 @@ _continue() }); })(this)((function(_this) { return function() { - return cb(err, enc, sesskey, pkcs5); + return cb(err, enc, valid, sesskey, pkcs5); }; })(this)); }; @@ -371,41 +373,39 @@ _continue() return cb(err, ret); }; - Message.prototype._decrypt_with_session_key = function(sesskey, edat, pkcs5, cb) { - var cipher, err, ret, ___iced_passed_deferral, __iced_deferrals, __iced_k, _ref1; + Message.prototype._decrypt_with_session_key = function(valid_key, sesskey, fallback_key, edat, pkcs5, cb) { + var cipher, err, ret, valid, ___iced_passed_deferral, __iced_deferrals, __iced_k, _ref1; __iced_k = __iced_k_noop; ___iced_passed_deferral = iced.findDeferral(arguments); - _ref1 = katch(function() { - return import_key_pgp(sesskey, pkcs5); - }), err = _ref1[0], cipher = _ref1[1]; + _ref1 = import_key_pgp_ct(valid_key, sesskey, fallback_key, pkcs5), valid = _ref1[0], cipher = _ref1[1]; (function(_this) { return (function(__iced_k) { - if (err == null) { - (function(__iced_k) { - __iced_deferrals = new iced.Deferrals(__iced_k, { - parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/processor.iced", - funcname: "Message._decrypt_with_session_key" - }); - edat.decrypt({ - cipher: cipher - }, __iced_deferrals.defer({ - assign_fn: (function() { - return function() { - err = arguments[0]; - return ret = arguments[1]; - }; - })(), - lineno: 206 - })); - __iced_deferrals._fulfill(); - })(__iced_k); - } else { - return __iced_k(); - } + __iced_deferrals = new iced.Deferrals(__iced_k, { + parent: ___iced_passed_deferral, + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/processor.iced", + funcname: "Message._decrypt_with_session_key" + }); + edat.decrypt({ + cipher: cipher + }, __iced_deferrals.defer({ + assign_fn: (function() { + return function() { + err = arguments[0]; + return ret = arguments[1]; + }; + })(), + lineno: 208 + })); + __iced_deferrals._fulfill(); }); })(this)((function(_this) { return function() { + if (!valid) { + err = new Error("Unable to decrypt"); + } + if (err) { + ret = null; + } return cb(err, ret); }; })(this)); @@ -418,7 +418,7 @@ _continue() }; Message.prototype._decrypt = function(cb) { - var edat, esc, is_enc, packets, pkcs5, plaintext, sesskey, ___iced_passed_deferral, __iced_deferrals, __iced_k; + var edat, esc, fallback_key, is_enc, packets, pkcs5, plaintext, sesskey, valid_key, ___iced_passed_deferral, __iced_deferrals, __iced_k; __iced_k = __iced_k_noop; ___iced_passed_deferral = iced.findDeferral(arguments); esc = make_esc(cb, "Message::decrypt"); @@ -426,18 +426,19 @@ _continue() return (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/processor.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/processor.iced", funcname: "Message._decrypt" }); _this._get_session_key(esc(__iced_deferrals.defer({ assign_fn: (function() { return function() { is_enc = arguments[0]; - sesskey = arguments[1]; - return pkcs5 = arguments[2]; + valid_key = arguments[1]; + sesskey = arguments[2]; + return pkcs5 = arguments[3]; }; })(), - lineno: 219 + lineno: 229 }))); __iced_deferrals._fulfill(); }); @@ -448,7 +449,7 @@ _continue() (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/processor.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/processor.iced", funcname: "Message._decrypt" }); _this._find_encrypted_data(esc(__iced_deferrals.defer({ @@ -457,43 +458,60 @@ _continue() return edat = arguments[0]; }; })(), - lineno: 221 + lineno: 231 }))); __iced_deferrals._fulfill(); })(function() { (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/processor.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/processor.iced", funcname: "Message._decrypt" }); - _this._decrypt_with_session_key(sesskey, edat, pkcs5, esc(__iced_deferrals.defer({ + SRF().random_bytes(32, __iced_deferrals.defer({ assign_fn: (function() { return function() { - return plaintext = arguments[0]; + return fallback_key = arguments[0]; }; })(), - lineno: 222 - }))); + lineno: 234 + })); __iced_deferrals._fulfill(); })(function() { (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/processor.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/processor.iced", funcname: "Message._decrypt" }); - _this._parse(plaintext, esc(__iced_deferrals.defer({ + _this._decrypt_with_session_key(valid_key, sesskey, fallback_key, edat, pkcs5, esc(__iced_deferrals.defer({ assign_fn: (function() { return function() { - return packets = arguments[0]; + return plaintext = arguments[0]; }; })(), - lineno: 223 + lineno: 235 }))); __iced_deferrals._fulfill(); })(function() { - return __iced_k(_this.packets = packets.concat(_this.packets)); + (function(__iced_k) { + __iced_deferrals = new iced.Deferrals(__iced_k, { + parent: ___iced_passed_deferral, + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/processor.iced", + funcname: "Message._decrypt" + }); + _this._parse(plaintext, esc(__iced_deferrals.defer({ + assign_fn: (function() { + return function() { + return packets = arguments[0]; + }; + })(), + lineno: 236 + }))); + __iced_deferrals._fulfill(); + })(function() { + return __iced_k(_this.packets = packets.concat(_this.packets)); + }); }); }); }); @@ -536,7 +554,7 @@ _continue() (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/processor.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/processor.iced", funcname: "Message._inflate" }); p.inflate(esc(__iced_deferrals.defer({ @@ -545,7 +563,7 @@ _continue() return inflated = arguments[0]; }; })(), - lineno: 233 + lineno: 246 }))); __iced_deferrals._fulfill(); })(function() { @@ -554,7 +572,7 @@ _continue() (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/processor.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/processor.iced", funcname: "Message._inflate" }); _this._parse(inflated, esc(__iced_deferrals.defer({ @@ -563,7 +581,7 @@ _continue() return p = arguments[0]; }; })(), - lineno: 235 + lineno: 248 }))); __iced_deferrals._fulfill(); })(function() { @@ -629,7 +647,7 @@ _continue() (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/processor.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/processor.iced", funcname: "Message._verify_sig" }); _this.keyfetch.fetch([a], konst.ops.verify, __iced_deferrals.defer({ @@ -640,7 +658,7 @@ _continue() return i = arguments[2]; }; })(), - lineno: 274 + lineno: 287 })); __iced_deferrals._fulfill(); })(function() { @@ -661,7 +679,7 @@ _continue() (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/processor.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/processor.iced", funcname: "Message._verify_sig" }); sig.close.verify(sig.payload, __iced_deferrals.defer({ @@ -670,7 +688,7 @@ _continue() return err = arguments[0]; }; })(), - lineno: 289 + lineno: 302 }), { now: _this.now, assert_pgp_hash: _this.assert_pgp_hash @@ -716,11 +734,11 @@ _continue() (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/processor.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/processor.iced", funcname: "Message._verify" }); _this._verify_sig(sig, esc(__iced_deferrals.defer({ - lineno: 303 + lineno: 316 }))); __iced_deferrals._fulfill(); })(_next); @@ -759,11 +777,11 @@ _continue() return (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/processor.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/processor.iced", funcname: "Message._process_generic" }); _this._decrypt(esc(__iced_deferrals.defer({ - lineno: 316 + lineno: 329 }))); __iced_deferrals._fulfill(); }); @@ -772,22 +790,22 @@ _continue() (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/processor.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/processor.iced", funcname: "Message._process_generic" }); _this._inflate(esc(__iced_deferrals.defer({ - lineno: 317 + lineno: 330 }))); __iced_deferrals._fulfill(); })(function() { (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/processor.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/processor.iced", funcname: "Message._process_generic" }); _this._verify(esc(__iced_deferrals.defer({ - lineno: 318 + lineno: 331 }))); __iced_deferrals._fulfill(); })(function() { @@ -811,7 +829,7 @@ _continue() (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/processor.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/processor.iced", funcname: "Message._verify_clearsign" }); verify_clearsign({ @@ -827,7 +845,7 @@ _continue() return literal = arguments[1]; }; })(), - lineno: 327 + lineno: 340 })); __iced_deferrals._fulfill(); })(__iced_k); @@ -849,7 +867,7 @@ _continue() return (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/processor.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/processor.iced", funcname: "Message.parse_and_inflate" }); _this._parse(body, esc(__iced_deferrals.defer({ @@ -858,7 +876,7 @@ _continue() return __slot_1.packets = arguments[0]; }; })(_this), - lineno: 334 + lineno: 347 }))); __iced_deferrals._fulfill(); }); @@ -867,11 +885,11 @@ _continue() (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/processor.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/processor.iced", funcname: "Message.parse_and_inflate" }); _this._inflate(esc(__iced_deferrals.defer({ - lineno: 335 + lineno: 348 }))); __iced_deferrals._fulfill(); })(function() { @@ -890,7 +908,7 @@ _continue() return (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/processor.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/processor.iced", funcname: "Message.parse_and_process" }); _this._parse(msg.body, esc(__iced_deferrals.defer({ @@ -899,7 +917,7 @@ _continue() return packets = arguments[0]; }; })(), - lineno: 342 + lineno: 355 }))); __iced_deferrals._fulfill(); }); @@ -908,7 +926,7 @@ _continue() (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/processor.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/processor.iced", funcname: "Message.parse_and_process" }); _this._process({ @@ -920,7 +938,7 @@ _continue() return literals = arguments[0]; }; })(), - lineno: 343 + lineno: 356 }))); __iced_deferrals._fulfill(); })(function() { @@ -943,7 +961,7 @@ _continue() (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/processor.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/processor.iced", funcname: "Message._verify_signature" }); verify_detached({ @@ -960,7 +978,7 @@ _continue() return literals = arguments[1]; }; })(), - lineno: 352 + lineno: 365 })); __iced_deferrals._fulfill(); })(__iced_k); @@ -986,7 +1004,7 @@ _continue() (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/processor.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/processor.iced", funcname: "Message._process" }); _this._process_generic({ @@ -998,7 +1016,7 @@ _continue() return literals = arguments[1]; }; })(), - lineno: 361 + lineno: 374 })); __iced_deferrals._fulfill(); })(__iced_k); @@ -1007,7 +1025,7 @@ _continue() (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/processor.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/processor.iced", funcname: "Message._process" }); _this._verify_clearsign({ @@ -1020,7 +1038,7 @@ _continue() return literals = arguments[1]; }; })(), - lineno: 363 + lineno: 376 })); __iced_deferrals._fulfill(); })(__iced_k); @@ -1029,7 +1047,7 @@ _continue() (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/processor.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/processor.iced", funcname: "Message._process" }); _this._verify_signature({ @@ -1041,7 +1059,7 @@ _continue() return literals = arguments[1]; }; })(), - lineno: 365 + lineno: 378 })); __iced_deferrals._fulfill(); })(__iced_k); @@ -1099,7 +1117,7 @@ _continue() (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/openpgp/processor.iced" + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/openpgp/processor.iced" }); proc.parse_and_process(msg, __iced_deferrals.defer({ assign_fn: (function() { @@ -1108,7 +1126,7 @@ _continue() return literals = arguments[1]; }; })(), - lineno: 413 + lineno: 426 })); __iced_deferrals._fulfill(); })(function() { diff --git a/lib/pad.js b/lib/pad.js index 57199fda..9325d769 100644 --- a/lib/pad.js +++ b/lib/pad.js @@ -1,6 +1,6 @@ // Generated by IcedCoffeeScript 108.0.11 (function() { - var C, SHA512, SRF, bufeq_secure, buffer_to_ui8a, eme_random, emsa_pkcs1_decode, emsa_pkcs1_encode, hash_headers, iced, nbs, __iced_k, __iced_k_noop, _ref; + var C, SHA512, SRF, bufeq_secure, buffer_to_ui8a, ct, eme_random, emsa_pkcs1_decode, emsa_pkcs1_encode, hash_headers, iced, nbs, __iced_k, __iced_k_noop, _ref; iced = require('iced-runtime'); __iced_k = __iced_k_noop = function() {}; @@ -15,6 +15,8 @@ SRF = require('./rand').SRF; + ct = require('./consttime'); + hash_headers = { MD5: [0x30, 0x20, 0x30, 0x0C, 0x06, 0x08, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10], SHA1: [0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14], @@ -105,7 +107,7 @@ (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/pad.iced" + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/pad.iced" }); SRF().random_bytes(diff, __iced_deferrals.defer({ assign_fn: (function() { @@ -113,7 +115,7 @@ return b = arguments[0]; }; })(), - lineno: 77 + lineno: 78 })); __iced_deferrals._fulfill(); })(function() { @@ -151,7 +153,7 @@ (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/pad.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/pad.iced", funcname: "eme_pkcs1_encode" }); eme_random(n_randos, __iced_deferrals.defer({ @@ -160,7 +162,7 @@ return PS = arguments[0]; }; })(), - lineno: 93 + lineno: 94 })); __iced_deferrals._fulfill(); })(function() { @@ -177,25 +179,26 @@ }; exports.eme_pkcs1_decode = function(v) { - var err, i, ret; + var eq0, err, header_hi, header_lo, i, index, looking_for_index, ret, valid, valid_ps, _i, _ref1; err = ret = null; + valid = false; if (v.length < 12) { err = new Error("Ciphertext too short, needs to be >= 12 bytes"); - } else if (v.readUInt16BE(0) !== 0x0002) { - err = new Error("Failed to find expected header: 0x00 0x02"); - } else { - i = 2; - while (i < v.length && (v.readUInt8(i) !== 0x0)) { - i++; - } - if (i >= v.length) { - err = new Error("didn't get 0x00 seperator octet"); - } else { - i++; - ret = v.slice(i); - } + return [err, valid, ret]; } - return [err, ret]; + header_lo = ct.eq_byte(v.readUInt8(0), 0x00); + header_hi = ct.eq_byte(v.readUInt8(1), 0x02); + looking_for_index = 1; + for (i = _i = 2, _ref1 = v.length; 2 <= _ref1 ? _i < _ref1 : _i > _ref1; i = 2 <= _ref1 ? ++_i : --_i) { + eq0 = ct.eq_byte(v.readUInt8(i), 0); + index = ct.select_int(looking_for_index & eq0, i, index); + looking_for_index = ct.select_byte(eq0, 0, looking_for_index); + } + valid_ps = ct.normalize((2 + 8) <= index); + valid = header_lo & header_hi & (~looking_for_index & 1) & valid_ps; + index = ct.select_int(valid, index + 1, 0); + ret = v.slice(index); + return [err, !!valid, ret]; }; exports.ecc_pkcs5_pad_data = function(d) { diff --git a/lib/rsa.js b/lib/rsa.js index b368e21c..7d216f88 100644 --- a/lib/rsa.js +++ b/lib/rsa.js @@ -46,7 +46,7 @@ return (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/rsa.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/rsa.iced", funcname: "Priv.decrypt" }); _this.mod_pow_d_crt(c, __iced_deferrals.defer({ @@ -191,7 +191,7 @@ return (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/rsa.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/rsa.iced", funcname: "Priv.mod_pow_d_crt" }); SRF().random_zn(n, __iced_deferrals.defer({ @@ -330,7 +330,7 @@ (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/rsa.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/rsa.iced", funcname: "Pair.sanity_check" }); _this.encrypt(x0, __iced_deferrals.defer({ @@ -346,7 +346,7 @@ (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/rsa.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/rsa.iced", funcname: "Pair.sanity_check" }); _this.decrypt(x1, __iced_deferrals.defer({ @@ -375,7 +375,7 @@ (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/rsa.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/rsa.iced", funcname: "Pair.sanity_check" }); _this.sign(y0, __iced_deferrals.defer({ @@ -391,7 +391,7 @@ (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/rsa.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/rsa.iced", funcname: "Pair.sanity_check" }); _this.verify(y1, __iced_deferrals.defer({ @@ -498,7 +498,7 @@ return (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/rsa.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/rsa.iced", funcname: "Pair.pad_and_encrypt" }); eme_pkcs1_encode(data, _this.pub.n.mpi_byte_length(), __iced_deferrals.defer({ @@ -519,7 +519,7 @@ (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/rsa.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/rsa.iced", funcname: "Pair.pad_and_encrypt" }); _this.encrypt(m, __iced_deferrals.defer({ @@ -547,15 +547,16 @@ }; Pair.prototype.decrypt_and_unpad = function(ciphertext, params, cb) { - var b, err, p, ret, ___iced_passed_deferral, __iced_deferrals, __iced_k; + var b, err, p, ret, valid, ___iced_passed_deferral, __iced_deferrals, __iced_k; __iced_k = __iced_k_noop; ___iced_passed_deferral = iced.findDeferral(arguments); err = ret = null; + valid = false; (function(_this) { return (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/rsa.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/rsa.iced", funcname: "Pair.decrypt_and_unpad" }); _this.decrypt(ciphertext.y(), __iced_deferrals.defer({ @@ -565,7 +566,7 @@ return p = arguments[1]; }; })(), - lineno: 318 + lineno: 319 })); __iced_deferrals._fulfill(); }); @@ -574,9 +575,12 @@ var _ref5; if (err == null) { b = p.to_padded_octets(_this.pub.n); - _ref5 = eme_pkcs1_decode(b), err = _ref5[0], ret = _ref5[1]; + _ref5 = eme_pkcs1_decode(b), err = _ref5[0], valid = _ref5[1], ret = _ref5[2]; } - return cb(err, ret); + return cb(err, { + valid: valid, + ret: ret + }); }; })(this)); }; @@ -595,7 +599,7 @@ return (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/rsa.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/rsa.iced", funcname: "Pair.pad_and_sign" }); _this.sign(m, __iced_deferrals.defer({ @@ -604,7 +608,7 @@ return sig = arguments[0]; }; })(), - lineno: 330 + lineno: 331 })); __iced_deferrals._fulfill(); }); @@ -630,7 +634,7 @@ (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/rsa.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/rsa.iced", funcname: "Pair.verify_unpad_and_check_hash" }); _this.verify(sig, __iced_deferrals.defer({ @@ -639,7 +643,7 @@ return v = arguments[0]; }; })(), - lineno: 339 + lineno: 340 })); __iced_deferrals._fulfill(); })(function() { @@ -691,7 +695,7 @@ (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/rsa.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/rsa.iced", funcname: "Pair.generate" }); random_prime({ @@ -705,28 +709,28 @@ return p = arguments[0]; }; })(), - lineno: 363 + lineno: 364 }))); __iced_deferrals._fulfill(); })(function() { (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/rsa.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/rsa.iced", funcname: "Pair.generate" }); asp.progress({ what: "found", p: p }, esc(__iced_deferrals.defer({ - lineno: 364 + lineno: 365 }))); __iced_deferrals._fulfill(); })(function() { (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/rsa.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/rsa.iced", funcname: "Pair.generate" }); random_prime({ @@ -740,21 +744,21 @@ return q = arguments[0]; }; })(), - lineno: 365 + lineno: 366 }))); __iced_deferrals._fulfill(); })(function() { (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/rsa.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/rsa.iced", funcname: "Pair.generate" }); asp.progress({ what: "found", q: q }, esc(__iced_deferrals.defer({ - lineno: 366 + lineno: 367 }))); __iced_deferrals._fulfill(); })(function() { @@ -809,7 +813,7 @@ return (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/rsa.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/rsa.iced", funcname: "Pair.validity_check" }); _this.pub.validity_check(__iced_deferrals.defer({ @@ -818,7 +822,7 @@ return err = arguments[0]; }; })(), - lineno: 391 + lineno: 392 })); __iced_deferrals._fulfill(); }); @@ -867,7 +871,7 @@ return (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, - filename: "/Users/max/src/keybase/kbpgp/src/rsa.iced", + filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/kbpgp/src/rsa.iced", funcname: "Output.hide" }); key.hide({ @@ -881,7 +885,7 @@ return i = arguments[1]; }; })(), - lineno: 417 + lineno: 418 })); __iced_deferrals._fulfill(); }); diff --git a/lib/symmetric.js b/lib/symmetric.js index 06d0d241..934aa057 100644 --- a/lib/symmetric.js +++ b/lib/symmetric.js @@ -1,6 +1,6 @@ // Generated by IcedCoffeeScript 108.0.11 (function() { - var AES, C, CAST5, SlicerBuffer, WordArray, checksum2, ecc_pkcs5_unpad_data, export_key_pgp, get_cipher, import_key_pgp, triplesec, uint_to_buffer; + var AES, C, CAST5, SlicerBuffer, WordArray, allowed_ciphers, checksum2, ct, ecc_pkcs5_unpad_data, export_key_pgp, get_cipher, import_key_pgp, import_key_pgp_ct, triplesec, uint_to_buffer; C = require('./const').openpgp.symmetric_key_algorithms; @@ -18,6 +18,10 @@ ecc_pkcs5_unpad_data = require('./pad').ecc_pkcs5_unpad_data; + ct = require('./consttime'); + + allowed_ciphers = [C.AES128, C.AES192, C.AES256, C.CAST5]; + exports.get_cipher = get_cipher = function(n) { var ret; n || (n = C.AES256); @@ -60,6 +64,43 @@ return res; }; + exports.import_key_pgp_ct = import_key_pgp_ct = function(valid_msg, msg, random_key, pkcs5_padding) { + var algo, c, checksum, cipher, err, found_cipher, i, key, offset, ret, selected_algo, selected_key, valid, _i, _j, _len, _ref; + if (pkcs5_padding == null) { + pkcs5_padding = false; + } + ret = err = null; + selected_algo = C.AES256; + selected_key = random_key; + found_cipher = 0; + for (_i = 0, _len = allowed_ciphers.length; _i < _len; _i++) { + c = allowed_ciphers[_i]; + cipher = get_cipher(c); + algo = ct.read_byte(msg, 0); + valid = ct.normalize(valid_msg); + valid &= ct.eq_int(algo, c); + key = Buffer.alloc(cipher.key_size); + for (i = _j = 0, _ref = cipher.key_size; 0 <= _ref ? _j < _ref : _j > _ref; i = 0 <= _ref ? ++_j : --_j) { + key[i] = ct.read_byte(msg, i + 1); + } + checksum = ct.read_uint16_be(msg, 1 + cipher.key_size); + valid &= ct.eq_int(checksum2(key), checksum); + offset = 1 + cipher.key_size + 2; + if (pkcs5_padding) { + valid &= ct.normalize(msg.length >= offset); + valid &= ct.eq_int(ct.normalize(ecc_pkcs5_unpad_data(msg, offset)), 0); + } else { + valid &= ct.eq_int(msg.length, offset); + } + selected_algo = ct.select_int(valid, c, selected_algo); + selected_key = ct.select_buffer(valid, key, selected_key); + found_cipher = ct.select_int(valid, 1, found_cipher); + } + cipher = get_cipher(selected_algo); + key = selected_key.slice(0, cipher.key_size); + return [!!found_cipher, new cipher.klass(WordArray.from_buffer(key))]; + }; + exports.import_key_pgp = import_key_pgp = function(msg, pkcs5_padding) { var checksum, cipher, err, key, ret, sb; if (pkcs5_padding == null) { diff --git a/src/consttime.iced b/src/consttime.iced new file mode 100644 index 00000000..e24d90e5 --- /dev/null +++ b/src/consttime.iced @@ -0,0 +1,59 @@ + +exports.is_zero = is_zero = (x) -> + x = x | 0 + + # For nonzero x, either x or -x has the high bit set. + # For zero, both are zero.So (x | -x) >>> 31 becomes 1. + # For zero, both x and -x are zero, so it becomes 0. + # The ^ 1 flips that result. + (((x | -x) >>> 31) ^ 1) | 0 + +exports.eq_byte = eq_byte = (a, b) -> + is_zero(((a ^ b) & 0xff) | 0) + +exports.eq_int = eq_int = (a, b) -> + is_zero(((a | 0) ^ (b | 0)) | 0) + +# Normalize x to exactly 0 or 1 +exports.normalize = normalize = (x) -> + if x then 1 else 0 + +# select_byte returns x if v == 1 and y if v == 0. +# Its behavior is undefined if v takes any other value. +exports.select_byte = select_byte = (v, x, y) -> + mask = -v | 0 # 1 -> -1 (0xffffffff); 0 -> 0 + + rb = x & 0xff + fb = y & 0xff # "fallback" + + (fb ^ (mask & (fb ^ rb))) & 0xff + +# select_int returns x if v == 1 and y if v == 0. +# x and y are treated as signed 32-bit values. +exports.select_int = select_int = (v, x, y) -> + mask = -v | 0 # 1 -> -1 (0xffffffff); 0 -> 0 + xi = x | 0 + yi = y | 0 + (yi ^ (mask & (yi ^ xi))) | 0 + +# read_byte reads a byte from buffer but returns 0 if the index is out of +# range. +exports.read_byte = read_byte = (buf, i) -> + (buf[i] or 0) & 0xff + +# read_byte reads an uint16 (big endian) from buffer but returns 0 if the index +# is out of range. +exports.read_uint16_be = read_uint16_be = (buf, i) -> + ((read_byte(buf, i) << 8) | read_byte(buf, i + 1)) & 0xffff + +# select_buffer returns buffer with the contents of x if v == 1 and +# contents of y if v == 0. The buffer will always have length of y +# buffer length. +exports.select_buffer = select_buffer = (v, x, y) -> + mask = -v | 0 + out = Buffer.alloc(y.length) + for i in [0...out.length] + rb = read_byte(x,i) & 0xff + fb = read_byte(y,i) & 0xff # "fallback" + out[i] = (fb ^ (mask & (fb ^ rb))) & 0xff + out diff --git a/src/ecc/ecdh.iced b/src/ecc/ecdh.iced index 5c996207..a591bd08 100644 --- a/src/ecc/ecdh.iced +++ b/src/ecc/ecdh.iced @@ -205,8 +205,9 @@ class Pair extends BaseKeyPair decrypt_and_unpad : (ciphertext, {fingerprint}, cb) -> err = ret = null - await @priv.decrypt ciphertext, { fingerprint }, defer err, m - cb err, m, true + await @priv.decrypt ciphertext, { fingerprint }, defer err, ret + if err then cb err + else cb null, { ret, valid : true, pkcs5 : true } #---------------- diff --git a/src/elgamal.iced b/src/elgamal.iced index 31065883..9925d133 100644 --- a/src/elgamal.iced +++ b/src/elgamal.iced @@ -120,11 +120,12 @@ class Pair extends BaseKeyPair decrypt_and_unpad : (ciphertext, params, cb) -> err = ret = null + valid = false await @priv.decrypt ciphertext.c(), defer err, m unless err? b = m.to_padded_octets @pub.p - [err, ret] = eme_pkcs1_decode b - cb err, ret + [err, valid, ret] = eme_pkcs1_decode b + cb err, { valid, ret } #---------------- diff --git a/src/elgamalse.iced b/src/elgamalse.iced index 77d655b4..84b3fd75 100644 --- a/src/elgamalse.iced +++ b/src/elgamalse.iced @@ -6,7 +6,6 @@ C = konst.openpgp K = konst.kb {BaseKeyPair,BaseKey} = require './basekeypair' {SRF,MRF} = require './rand' -{eme_pkcs1_encode,eme_pkcs1_decode} = require './pad' #================================================================= diff --git a/src/openpgp/buffer.iced b/src/openpgp/buffer.iced index aac823de..ccd79de4 100644 --- a/src/openpgp/buffer.iced +++ b/src/openpgp/buffer.iced @@ -16,34 +16,36 @@ class SlicerBuffer @start = @i @_end = e - len : () -> @buf.length - @start - rem : () -> @buf.length - @i + len : () -> @limit() - @start + rem : () -> @limit() - @i offset : () -> @i - @start - check : () -> - if (@_end and @i > @_end) or (@i > @buf.length) + limit : () -> if @_end? then Math.min(@_end, @buf.length) else @buf.length + check_available : (n) -> + if n < 0 or @i + n > @limit() throw new Error "read off the end of the packet @#{@i}/#{@buf.length}/#{@_end}" read_uint8 : () -> - ret = @buf.readUInt8 @i++ - @check() + @check_available 1 + ret = @buf.readUInt8 @i + @i++ ret read_uint16 : () -> + @check_available 2 ret = @buf.readUInt16BE @i @i += 2 - @check() ret read_uint32 : () -> + @check_available 4 ret = @buf.readUInt32BE @i @i += 4 - @check() ret read_buffer_at_most : (l) -> @read_buffer (Math.min(l, @rem())) read_buffer : (l) -> + @check_available l ret = @buf[@i...(@i+l)] @i += l - @check() ret - end : () -> @_end or @buf.length + end : () -> @limit() peek_rest_to_buffer : () -> @buf[@i...@end()] consume_rest_to_buffer : () -> ret = @peek_rest_to_buffer() @@ -52,8 +54,12 @@ class SlicerBuffer advance : (i = 1) -> @i += i peek_to_buffer : (len) -> @buf[@i...(@i + len)] - peek_uint8 : () -> @buf.readUInt8 @i - peek_uint16 : () -> @buf.readUInt16BE @i + peek_uint8 : () -> + @check_available 1 + @buf.readUInt8 @i + peek_uint16 : () -> + @check_available 2 + @buf.readUInt16BE @i read_string : () -> @read_buffer @read_uint8() @@ -71,4 +77,3 @@ class SlicerBuffer exports.SlicerBuffer = SlicerBuffer #================================================================================================ - diff --git a/src/openpgp/packet/sess.iced b/src/openpgp/packet/sess.iced index a97e1e81..85cd892e 100644 --- a/src/openpgp/packet/sess.iced +++ b/src/openpgp/packet/sess.iced @@ -3,7 +3,7 @@ C = require('../../const').openpgp asymmetric = require '../../asymmetric' {SHA1,streamers} = require '../../hash' -{uint_to_buffer,bufeq_secure,bufeq_fast} = require '../../util' +{uint_to_buffer,bufeq_secure} = require '../../util' {encrypt,Decryptor} = require '../ocfb' {ASP} = require('pgp-utils').util {make_esc} = require 'iced-error' @@ -61,15 +61,21 @@ class SEIPD extends Packet esc = make_esc cb, "SEIPD::decrypt" asp = ASP.make asp - await eng.check esc defer() + await eng.check defer err await eng.dec esc defer pt - [ mdc, plaintext ] = MDC.parse pt + [ valid_mdc, mdc, plaintext ] = MDC.parse pt prefix = eng.get_prefix() # check that the hash matches what we fetched out of the message await mdc.compute { prefix, plaintext, asp }, esc defer() - err = if mdc.check() then null else new Error "MDC mismatch" + valid_hash = mdc.check() + + # If we got an error or if the MDC is invalid, collapse that into + # one non-descript error. Make sure we don't return plaintext. + if err or not (valid_mdc and valid_hash) + plaintext = null + err = new Error "Unable to decrypt" cb err, plaintext @@ -146,9 +152,9 @@ class MDC_Parser len = SHA1.output_length + hl rem = @buf[0...(-len)] chunk = @buf[(-len)...] - throw new Error 'Missing MDC header' unless bufeq_fast chunk[0...hl], MDC.header + valid = bufeq_secure chunk[0...hl], MDC.header digest = chunk[hl...] - [ new MDC({ digest }), rem ] + [ valid, new MDC({ digest }), rem ] #================================================================================= diff --git a/src/openpgp/processor.iced b/src/openpgp/processor.iced index f3f6b7c4..d38b1d8f 100644 --- a/src/openpgp/processor.iced +++ b/src/openpgp/processor.iced @@ -5,7 +5,8 @@ konst = require '../const' C = konst.openpgp {katch,unix_time,athrow,Warnings,bufeq_secure} = require '../util' {parse} = require './parser' -{import_key_pgp} = require '../symmetric' +{import_key_pgp_ct} = require '../symmetric' +{SRF} = require '../rand' util = require 'util' armor = require './armor' hashmod = require '../hash' @@ -164,6 +165,7 @@ class Message esk_packets = [] err = null pkcs5 = false + valid = false # Handle the case that the Session Key is encrypted N times, and we # only have the key decrypt one of them. This is the case when you send @@ -182,13 +184,14 @@ class Message key_material = km.find_pgp_key_material(key_ids[index]) fingerprint = key_material.get_fingerprint() privk = key_material.key - await privk.decrypt_and_unpad packet.ekey, {fingerprint}, defer err, sesskey, pkcs5 + await privk.decrypt_and_unpad packet.ekey, {fingerprint}, defer err, unpad unless err? @encryption_subkey = key_material + { ret : sesskey, valid, pkcs5 } = unpad else enc = false - cb err, enc, sesskey, pkcs5 + cb err, enc, valid, sesskey, pkcs5 #--------- @@ -201,10 +204,17 @@ class Message #--------- - _decrypt_with_session_key : (sesskey, edat, pkcs5, cb) -> - [err,cipher] = katch () -> import_key_pgp sesskey, pkcs5 - unless err? - await edat.decrypt {cipher}, defer err, ret + _decrypt_with_session_key : (valid_key, sesskey, fallback_key, edat, pkcs5, cb) -> + [valid,cipher] = import_key_pgp_ct valid_key, sesskey, fallback_key, pkcs5 + await edat.decrypt {cipher}, defer err, ret + # Decrypt may succeed, but if there were any failures beforehand, + # return an error. This ensures constant time operation - we.let + # the decryption run all the way even on invalid session key, + # padding, etc. + unless valid + err = new Error "Unable to decrypt" + # make sure no invalid decryption leak into the caller + if err then ret = null cb err, ret #--------- @@ -217,10 +227,13 @@ class Message _decrypt : (cb) -> esc = make_esc cb, "Message::decrypt" - await @_get_session_key esc defer is_enc, sesskey, pkcs5 + await @_get_session_key esc defer is_enc, valid_key, sesskey, pkcs5 if is_enc await @_find_encrypted_data esc defer edat - await @_decrypt_with_session_key sesskey, edat, pkcs5, esc defer plaintext + # Generate random fallback key to be used when session key can't be + # decrypted. This is used to prevent padding oracle attacks. + await SRF().random_bytes 32, defer fallback_key + await @_decrypt_with_session_key valid_key, sesskey, fallback_key, edat, pkcs5, esc defer plaintext await @_parse plaintext, esc defer packets @packets = packets.concat @packets cb null diff --git a/src/pad.iced b/src/pad.iced index 6c4e4ed1..2d3973bd 100644 --- a/src/pad.iced +++ b/src/pad.iced @@ -3,6 +3,7 @@ C = require('./const').openpgp {nbs} = require './bn' {buffer_to_ui8a,bufeq_secure} = require './util' {SRF} = require './rand' +ct = require './consttime' #==================================================================== @@ -37,7 +38,7 @@ exports.emsa_pkcs1_encode = emsa_pkcs1_encode = (hashed_data, len, opts = {}) -> hashed_data ] # We have to convert to a Uint8 array since the JSBN library internally - # uses A[.] rather than A.readUint8(.)... + # uses A[.] rather than A.readUInt8(.)... nbs(buffer_to_ui8a(buf), 256) #==================================================================== @@ -105,19 +106,33 @@ exports.eme_pkcs1_encode = (v, len, cb) -> exports.eme_pkcs1_decode = (v) -> err = ret = null + valid = false if v.length < 12 err = new Error "Ciphertext too short, needs to be >= 12 bytes" - else if v.readUInt16BE(0) isnt 0x0002 - err = new Error "Failed to find expected header: 0x00 0x02" - else - i = 2 - (i++ while i < v.length and (v.readUInt8(i) isnt 0x0)) - if i >= v.length - err = new Error "didn't get 0x00 seperator octet" - else - i++ - ret = v[i...] - [err, ret] + return [err, valid, ret] + + header_lo = ct.eq_byte v.readUInt8(0), 0x00 + header_hi = ct.eq_byte v.readUInt8(1), 0x02 + + # The remainder of the plaintext must be a string of non-zero random + # octets, followed by a 0, followed by the message. + # looking_for_index: 1 iff we are still looking for the zero. + # index: the offset of the first zero byte. + looking_for_index = 1 + for i in [2...v.length] + eq0 = ct.eq_byte v.readUInt8(i), 0 + index = ct.select_int looking_for_index & eq0, i, index + looking_for_index = ct.select_byte eq0, 0, looking_for_index + + # The PS padding must be at least 8 bytes long, and it starts two + # bytes into em. + valid_ps = ct.normalize (2+8) <= index + + valid = header_lo & header_hi & (~looking_for_index & 1) & valid_ps + index = ct.select_int valid, index+1, 0 + ret = v[index...] + + [err, !!valid, ret] #==================================================================== diff --git a/src/rsa.iced b/src/rsa.iced index 1799fd5b..2e43fbab 100644 --- a/src/rsa.iced +++ b/src/rsa.iced @@ -316,11 +316,12 @@ class Pair extends BaseKeyPair # decrypt_and_unpad : (ciphertext, params, cb) -> err = ret = null + valid = false await @decrypt ciphertext.y(), defer err, p unless err? b = p.to_padded_octets @pub.n - [err, ret] = eme_pkcs1_decode b - cb err, ret + [err, valid, ret] = eme_pkcs1_decode b + cb err, { valid, ret } #---------------- diff --git a/src/symmetric.iced b/src/symmetric.iced index c58017d0..1bae35d0 100644 --- a/src/symmetric.iced +++ b/src/symmetric.iced @@ -7,6 +7,9 @@ triplesec = require 'triplesec' {WordArray} = triplesec {uint_to_buffer} = require './util' {ecc_pkcs5_unpad_data} = require './pad' +ct = require './consttime' + +allowed_ciphers = [C.AES128, C.AES192, C.AES256, C.CAST5] exports.get_cipher = get_cipher = (n) -> n or= C.AES256 @@ -26,6 +29,57 @@ exports.checksum2 = checksum2 = (buf) -> res = ((res + buf.readUInt8(i)) & 0xffff) res +exports.import_key_pgp_ct = import_key_pgp_ct = (valid_msg, msg, random_key, pkcs5_padding = false) -> + ret = err = null + + # We are going to iterate all allowed ciphers, but only store selected_algo + # and selected_key for the cipher that matches (and validates checksum + # etc.). This is to ensure import_key_pgp_ct is constant time. + + selected_algo = C.AES256 + selected_key = random_key + found_cipher = 0 + + for c in allowed_ciphers + cipher = get_cipher c + algo = ct.read_byte msg, 0 + # If the msg itself is invalid, mark the candidate as invalid but still + # continue with the checks here. + valid = ct.normalize valid_msg + valid &= ct.eq_int algo, c + # Read key (fill with 0 if buffer is too short) + key = Buffer.alloc(cipher.key_size) + for i in [0...cipher.key_size] + key[i] = ct.read_byte msg, i + 1 + # Check the checksum. + checksum = ct.read_uint16_be msg, 1 + cipher.key_size + valid &= ct.eq_int checksum2(key), checksum + + offset = 1 + cipher.key_size + 2 + if pkcs5_padding + # Check pkcs5_padding if it's been asked for. + valid &= ct.normalize(msg.length >= offset) + # ecc_pkcs5_unpad_data returns null if padding is correct. + valid &= ct.eq_int ct.normalize(ecc_pkcs5_unpad_data(msg, offset)), 0 + else + # If there is junk at the end, key is invalid. + valid &= ct.eq_int msg.length, offset + + selected_algo = ct.select_int valid, c, selected_algo + selected_key = ct.select_buffer valid, key, selected_key + found_cipher = ct.select_int valid, 1, found_cipher + + # Cipher is guaranteed to exist because we enumerated all possible candidates. + # So get_cipher shall not throw. + cipher = get_cipher selected_algo + + # This slice is not constant time, but at this point `cipher` has been + # sanitized, so we are either slicing with a valid key_size or fixed + # fallback size. + key = selected_key[0...cipher.key_size] + + [!!found_cipher, new cipher.klass WordArray.from_buffer key] + exports.import_key_pgp = import_key_pgp = (msg, pkcs5_padding = false) -> sb = new SlicerBuffer msg ret = err = null diff --git a/test/browser/main.iced b/test/browser/main.iced index fa695243..7f315558 100644 --- a/test/browser/main.iced +++ b/test/browser/main.iced @@ -1,20 +1,23 @@ mods = + aead : require '../files/aead.iced' brainpool256 : require '../files/brainpool256.iced' brainpool384: require '../files/brainpool384.iced' brainpool512: require '../files/brainpool512.iced' burner: require '../files/burner.iced' + cv25519: require '../files/cv25519.iced' eddsa : require '../files/eddsa.iced' box : require '../files/box.iced' kbbox : require '../files/kbbox.iced' fp : require '../files/fp.iced' nacl1 : require '../files/nacl1.iced' nacl2 : require '../files/nacl2.iced' + nacl_export : require '../files/nacl_export.iced' ecc3 : require '../files/ecc3.iced' + errors : require '../files/errors.iced' verify_sigs : require '../files/verify_sigs.iced' sigeng : require '../files/sigeng.iced' revoked_subkeys : require '../files/revoked_subkey.iced' - sigs : require "../files/verify_sigs.iced" multiples : require "../files/multiples.iced" unbox_cant_verify : require '../files/unbox_cant_verify.iced' zip : require '../files/zip.iced' @@ -35,6 +38,7 @@ mods = sig_v3 : require '../files/sig_v3.iced' sig_gocli : require '../files/sig_gocli.iced' elgamal : require '../files/elgamal.iced' + elgamal_v3 : require '../files/elgamal_v3.iced' dsa : require '../files/dsa.iced' rsa_8192 : require '../files/rsa_8192.iced' buffer_shfit_right : require '../files/buffer_shift_right.iced' @@ -54,6 +58,18 @@ mods = revokes : require '../files/revokes.iced' p3skb : require '../files/p3skb.iced' p3skb_r : require '../files/p3skb_r.iced' + p521 : require '../files/p521.iced' + padding_oracle : require '../files/padding_oracle.iced' + problem_sigs : require '../files/problem_sigs.iced' + gh_issue_73 : require '../files/gh_issue_73.iced' + key_expirations : require '../files/key_expirations.iced' + subkey_expiration : require '../files/subkey_expiration.iced' + subkey_multiple_sigs : require '../files/subkey_multiple_sigs.iced' + subkey_no_cross_sig : require '../files/subkey_no_cross_sig.iced' + subkey_preference : require '../files/subkey_preference.iced' + subpacket33 : require '../files/subpacket33.iced' + ukm : require '../files/ukm.iced' + merge_pgp_public : require '../files/merge_pgp_public.iced' v = Object.keys(mods) v.sort() diff --git a/test/files/decrypt_verify_msg.iced b/test/files/decrypt_verify_msg.iced index 8e75ffda..be790d75 100644 --- a/test/files/decrypt_verify_msg.iced +++ b/test/files/decrypt_verify_msg.iced @@ -291,8 +291,11 @@ exports.run_test_msg_0 = (T, cb) -> await load_keyring T, defer ring dkey = ring.lookup packets[0].key_id T.assert dkey?, "found the right decryption key" - await dkey.key.decrypt_and_unpad packets[0].ekey, {}, defer err, sesskey + await dkey.key.decrypt_and_unpad packets[0].ekey, {}, defer err, unpad T.no_error err + { ret : sesskey, valid, pkcs5 } = unpad + T.assert valid + T.assert not pkcs5 T.waypoint "decrypted the session key" cipher = import_key_pgp sesskey await decrypt { cipher, ciphertext : packets[1].ciphertext }, defer err, pt diff --git a/test/files/hide.iced b/test/files/hide.iced index 11845d49..e7bf6a7d 100644 --- a/test/files/hide.iced +++ b/test/files/hide.iced @@ -38,9 +38,10 @@ exports.test_hide_rsa = (T,cb) -> T.equal ctext2.y().bitLength(), bl_orig, "the right bitlength after finding" T.assert ctext2.y().equals(y_orig), "we got the right y back" - await key.decrypt_and_unpad ctext2, {}, defer err, plaintext + await key.decrypt_and_unpad ctext2, {}, defer err, { valid, ret } T.no_error err - T.assert bufeq_secure(plaintext, data), "output was same as input" + T.assert valid + T.assert bufeq_secure(ret, data), "output was same as input" cb() @@ -102,9 +103,10 @@ exports.test_hide_elgamal = (T,cb) -> for c_mpi,j in ctext2.c() T.assert c_mpis_orig[j].equals(c_mpi), "we got c_#{j} back" - await key.decrypt_and_unpad ctext2, {}, defer err, plaintext + await key.decrypt_and_unpad ctext2, {}, defer err, { valid, ret } T.no_error err - T.assert bufeq_secure(plaintext, data), "output was same as input" + T.assert valid + T.assert bufeq_secure(ret, data), "output was same as input" T.no_error err cb() diff --git a/test/files/padding_oracle.iced b/test/files/padding_oracle.iced new file mode 100644 index 00000000..e513736e --- /dev/null +++ b/test/files/padding_oracle.iced @@ -0,0 +1,359 @@ +kbpgp = require '../../' +{KeyManager} = kbpgp +{make_esc} = require 'iced-error' +{PgpKeyRing} = require '../../lib/keyring' +{burn} = require '../../lib/openpgp/burner' +{PKESK,SEIPD,MDC} = require '../../lib/openpgp/packet/sess' +{parse} = require '../../lib/openpgp/parser' +{do_message} = require '../../lib/openpgp/processor' +{export_key_pgp,get_cipher} = require '../../lib/symmetric' +{encrypt} = require '../../lib/openpgp/ocfb' +{buffer_to_ui8a} = require '../../lib/util' +{nbs} = require '../../lib/bn' +{WordArray} = require 'triplesec' + +C = kbpgp.const.openpgp + +EXPECTED_ERROR = "Unable to decrypt" +RSA_BITS = 1024 +ELGAMAL_PASSPHRASE = "mmpp" +TEST_TIME = Math.floor(new Date(2014, 2, 21)/1000) + +ELGAMAL_KEY = """ +-----BEGIN PGP PRIVATE KEY BLOCK----- +Version: GnuPG/MacGPG2 v2.0.22 (Darwin) + +lQHhBFMGAboRBAC6X5nu5PxK9eaTRTGI1PUu89SYaDCNf4P82ADVwBy2gQSHZAlH +d1esdd5QI2TYvfLBYHelTLk6nfO/JsPFFTPAKiyCA84GO3MBXebs8JBd5VPl3PuY +YXk+xUVui/oE2bnS2PzUIPIilWwN1g6O4Olz+D70uuoGV8Og2krKUkzsRwCg+KcF +fiJsfgw7to/VXdD651DSZ/0D/3N5l1kiFZvttYSu6VymG76NBnPgbRKH3sYguGPj +c8E6GtJ1HrGQiGoiKN3jfYEQcOqil6/A780Yz/3yW6QK3OIJ9mIpNA8uJghWdk9E +3xhm0QrC4e3ECqQgAp5wGTfTaepsvjZxRyvu+xTQje/QMwEk3ElSOfjfq1nzjoE8 +15YcBACzroFdReanDhMeRb2xjv8fjr98WqMGVjifPwJ2UEwtV8wPPGDNN63BbhYL +RyRxSrUdP3LDKnnNVocNOjOEGzrRtdKRf3S1cB7b+Tc2rphublG1yGIjDeNZ9E9g +mTrxr+mBm3WyFlBU3vEE+UJ3YLPQ37ai83CItaT22OY5FNAW3v4DAwIBuwNTyCVg +19Z/bQbO5Vv7myq59sSwfpLCcnjaII3oYjRYum32OrmIl1a2qPzOGpF1BfeyfT43 +kin3XbQ1TWF4IFBsYW5jayAocGFzc3dvcmQgaXMgJ21tcHAnKSA8cGxhbmNrQGJl +cmxpbi5hYy5kZT6IaAQTEQIAKAUCUwYBugIbAwUJEswDAAYLCQgHAwIGFQgCCQoL +BBYCAwECHgECF4AACgkQkQqdjReS9VtG9ACeKf/N+cRCTEjARwbAWl9VAndRTvIA +mQE+l+Mv2PF8F3TUVVYl9aAXc3JHnQFYBFMGAboQBADSFqRZ8S7vJLXKW7a22iZR +4ezEGM4Rj+3ldbsgs+BHG3qrtILdWFeiXRfh+0XgSJyhZpRfPYeKdF42I0+JvFzF +QE/9pX5LsjeIgeB3P6gMi7IPrF47qWhixQ3F9EvBymlFFCXnJ/9tQsHytIhyXsZH +LD9Vti6bLyz8zkuXbRT8CwADBgP+LPUlmmIuuUu7kYMCLDy5ycRGv/x8WamSZlH3 +6TBY44+6xIpzOGf1Aoag+e7b+5pJE5+dFfWhfvZpGn9tdLdimA7DVxl/YCeTxoXL +25YCnOhlqVFfWMnVr7Ml3hX0Hl3WXqRQT45ZR7qzfR+8xUvl6jTwYZzYElGIJxa5 +hPreyJv+AwMCAbsDU8glYNfWXpn3WV1KYjnXsZwPA1zOth8DoZBvsNFgpJCxQpfI +PCeAcnTQQaF0NEEfXtNGKsbwYFdHTD7aXvAs2h05FReITwQYEQIADwUCUwYBugIb +DAUJEswDAAAKCRCRCp2NF5L1Wx7xAJ0a2tmT1WhB9+7IEHVkwm0b97EbJQCfcoDT +ZbLGiqgjXIjfEuNACFhveec= +=66In +-----END PGP PRIVATE KEY BLOCK----- +""" + +state = null +original_seipd_decrypt = SEIPD::decrypt + +make_key = (byte, len) -> Buffer.from(byte for i in [0...len]) + +modulus_byte_length = () -> state.encryption_key.key.max_value().mpi_byte_length() + +valid_session_key = () -> + export_key_pgp C.symmetric_key_algorithms.AES256, state.session_key + +bad_checksum_session_key = () -> + ret = Buffer.from valid_session_key() + ret[ret.length - 1] ^= 1 + ret + +unknown_cipher_session_key = () -> + Buffer.concat [ + Buffer.from [0xff] + make_key 0x11, 32 + Buffer.from [0, 0] + ] + +wrong_cipher_session_key = () -> + export_key_pgp C.symmetric_key_algorithms.AES128, make_key 0x24, 16 + +eme_with_payload = (payload) -> + k = modulus_byte_length() + ps_len = k - payload.length - 3 + throw new Error "payload too long for RSA modulus" unless ps_len >= 8 + Buffer.concat [ + Buffer.from [0, 2] + make_key 0x7f, ps_len + Buffer.from [0] + payload + ] + +bad_header_eme = () -> + ret = eme_with_payload valid_session_key() + ret[1] = 1 + ret + +missing_separator_eme = () -> + ret = make_key 0x7f, modulus_byte_length() + ret[0] = 0 + ret[1] = 2 + ret + +bad_checksum_eme = () -> eme_with_payload bad_checksum_session_key() + +unknown_cipher_eme = () -> eme_with_payload unknown_cipher_session_key() + +wrong_cipher_eme = () -> eme_with_payload wrong_cipher_session_key() + +# This is a syntactically valid session key, but it does not match state.edat, +# which was produced by burn() with a different random session key. +wrong_session_key_eme = () -> eme_with_payload valid_session_key() + +short_ps_eme = () -> + k = modulus_byte_length() + payload = valid_session_key() + ps = Buffer.from [0x7f] + pad_len = k - payload.length - ps.length - 3 + throw new Error "payload too long for RSA modulus" unless pad_len >= 0 + Buffer.concat [ + Buffer.from [0, 2] + ps + Buffer.from [0] + make_key 0, pad_len + payload + ] + +empty_session_key_eme = () -> eme_with_payload Buffer.from [] + +short_session_key_eme = () -> + eme_with_payload Buffer.from [ C.symmetric_key_algorithms.AES256 ] + +trailing_junk_session_key = () -> + Buffer.concat [ + valid_session_key() + Buffer.from [0x99] + ] + +trailing_junk_eme = () -> eme_with_payload trailing_junk_session_key() + +packet_for_eme = (eme, cb) -> + msg = nbs buffer_to_ui8a(eme), 256 + key = state.encryption_key.key + if key.type is C.public_key_algorithms.ELGAMAL + await key.pub.encrypt msg, defer c_mpis + ekey = key.export_output { c_mpis } + else + await key.encrypt msg, defer ciphertext + ekey = key.export_output { y_mpi : ciphertext } + pkt = new PKESK { + crypto_type : key.type + key_id : state.encryption_key.get_key_id() + ekey + } + await pkt.write defer err, ret + cb err, ret + +check_variant = ({T, name, eme, data_packet}, cb) -> + esc = make_esc cb, name + await packet_for_eme eme, esc defer pkesk + data_packet or= state.edat.replay() + raw = Buffer.concat [ pkesk, data_packet ] + decrypt_calls = 0 + SEIPD::decrypt = (args, cb) -> + decrypt_calls++ + original_seipd_decrypt.call @, args, cb + await do_message { + raw + msg_type : C.message_types.generic + keyfetch : state.ring + }, defer err, out + SEIPD::decrypt = original_seipd_decrypt + T.assert err?, "#{name} failed" + T.equal err?.message, EXPECTED_ERROR, "#{name} uses generic decrypt error" + T.equal decrypt_calls, 1, "#{name} attempted encrypted data decrypt" + T.assert not(out?), "#{name} returned no plaintext" + cb() + +make_cipher = () -> + cipher_info = get_cipher C.symmetric_key_algorithms.AES256 + new cipher_info.klass WordArray.from_buffer state.session_key + +encrypted_data_packet = (plaintext, cb) -> + cipher = make_cipher() + prefixrandom = make_key 0x55, cipher.blockSize + await encrypt { cipher, plaintext, prefixrandom }, defer err, ciphertext + unless err? + pkt = new SEIPD { ciphertext } + await pkt.write defer err, ret + cb err, ret + +mdc_mismatch_data_packet = (cb) -> + plaintext = Buffer.concat [ + Buffer.from "not a packet" + MDC.header + make_key 0, 20 + ] + encrypted_data_packet plaintext, cb + +missing_mdc_header_data_packet = (cb) -> + plaintext = Buffer.concat [ + Buffer.from "not a packet" + make_key 0x33, 22 + ] + encrypted_data_packet plaintext, cb + +exports.init = (T, cb) -> + esc = make_esc cb, "padding_oracle init" + F = C.key_flags + session_key = make_key 0x42, 32 + await KeyManager.generate { + userid : "padding oracle test" + nbits : RSA_BITS + nsubs : 1 + primary_flags : F.certify_keys | F.sign_data + sub_flags : [ F.encrypt_comm | F.encrypt_storage ] + }, esc defer km + await km.sign {}, esc defer() + ring = new PgpKeyRing() + ring.add_key_manager km + encryption_key = km.find_crypt_pgp_key() + T.assert encryption_key?, "found encryption key" + await burn { + msg : "padding oracle regression" + encryption_key + }, esc defer armored, raw + [err, packets] = parse raw + T.no_error err + T.equal packets.length, 2, "encrypted message has two packets" + edat = packets[1].to_enc_data_packet() + T.assert edat?, "found encrypted data packet" + + state = { km, ring, encryption_key, edat, session_key } + cb() + +exports.rsa_bad_pkcs1_header = (T, cb) -> + check_variant { T, name : "bad PKCS#1 header", eme : bad_header_eme() }, cb + +exports.rsa_missing_pkcs1_separator = (T, cb) -> + check_variant { T, name : "missing PKCS#1 separator", eme : missing_separator_eme() }, cb + +exports.rsa_bad_session_key_checksum = (T, cb) -> + check_variant { T, name : "bad session-key checksum", eme : bad_checksum_eme() }, cb + +exports.rsa_unknown_session_cipher = (T, cb) -> + check_variant { T, name : "unknown session-key cipher", eme : unknown_cipher_eme() }, cb + +exports.rsa_valid_padding_wrong_session_cipher = (T, cb) -> + check_variant { T, name : "valid padding with wrong session-key cipher", eme : wrong_cipher_eme() }, cb + +exports.rsa_valid_padding_wrong_session_key = (T, cb) -> + check_variant { T, name : "valid padding with wrong session key", eme : wrong_session_key_eme() }, cb + +exports.rsa_short_pkcs1_padding_string = (T, cb) -> + check_variant { T, name : "short PKCS#1 padding string", eme : short_ps_eme() }, cb + +exports.rsa_empty_session_key_packet = (T, cb) -> + check_variant { T, name : "empty session-key packet", eme : empty_session_key_eme() }, cb + +exports.rsa_short_session_key_packet = (T, cb) -> + check_variant { T, name : "short session-key packet", eme : short_session_key_eme() }, cb + +exports.rsa_session_key_trailing_junk = (T, cb) -> + check_variant { T, name : "session-key trailing junk", eme : trailing_junk_eme() }, cb + +exports.rsa_mdc_mismatch = (T, cb) -> + esc = make_esc cb, "rsa MDC mismatch" + await mdc_mismatch_data_packet esc defer data_packet + check_variant { + T + name : "MDC mismatch" + eme : wrong_session_key_eme() + data_packet + }, cb + +exports.rsa_mdc_missing_header = (T, cb) -> + esc = make_esc cb, "MDC missing header crasher" + await missing_mdc_header_data_packet esc defer data_packet + check_variant { + T + name : "RSA MDC missing header" + eme : wrong_session_key_eme() + data_packet + }, cb + + +# ------ + +exports.elgamal_setup = (T, cb) -> + esc = make_esc cb, "elgamal setup" + opts = now : TEST_TIME + session_key = make_key 0x42, 32 + await KeyManager.import_from_armored_pgp { + raw : ELGAMAL_KEY + opts + }, esc defer km + await km.unlock_pgp { passphrase : ELGAMAL_PASSPHRASE }, esc defer() + ring = new PgpKeyRing() + ring.add_key_manager km + encryption_key = km.find_crypt_pgp_key() + T.assert encryption_key?, "found ElGamal encryption key" + await burn { + msg : "elgamal padding oracle regression" + encryption_key + }, esc defer armored, raw + [err, packets] = parse raw + T.no_error err + T.equal packets.length, 2, "ElGamal encrypted message has two packets" + edat = packets[1].to_enc_data_packet() + T.assert edat?, "found ElGamal encrypted data packet" + + state = { km, ring, encryption_key, edat, session_key } + cb() + +exports.elgamal_bad_pkcs1_header = (T, cb) -> + check_variant { T, name : "ElGamal bad PKCS#1 header", eme : bad_header_eme() }, cb + +exports.elgamal_missing_pkcs1_separator = (T, cb) -> + check_variant { T, name : "ElGamal missing PKCS#1 separator", eme : missing_separator_eme() }, cb + +exports.elgamal_bad_session_key_checksum = (T, cb) -> + check_variant { T, name : "ElGamal bad session-key checksum", eme : bad_checksum_eme() }, cb + +exports.elgamal_unknown_session_cipher = (T, cb) -> + check_variant { T, name : "ElGamal unknown session-key cipher", eme : unknown_cipher_eme() }, cb + +exports.elgamal_valid_padding_wrong_session_cipher = (T, cb) -> + check_variant { T, name : "ElGamal valid padding with wrong session-key cipher", eme : wrong_cipher_eme() }, cb + +exports.elgamal_valid_padding_wrong_session_key = (T, cb) -> + check_variant { T, name : "ElGamal valid padding with wrong session key", eme : wrong_session_key_eme() }, cb + +exports.elgamal_short_pkcs1_padding_string = (T, cb) -> + check_variant { T, name : "ElGamal short PKCS#1 padding string", eme : short_ps_eme() }, cb + +exports.elgamal_empty_session_key_packet = (T, cb) -> + check_variant { T, name : "ElGamal empty session-key packet", eme : empty_session_key_eme() }, cb + +exports.elgamal_short_session_key_packet = (T, cb) -> + check_variant { T, name : "ElGamal short session-key packet", eme : short_session_key_eme() }, cb + +exports.elgamal_session_key_trailing_junk = (T, cb) -> + check_variant { T, name : "ElGamal session-key trailing junk", eme : trailing_junk_eme() }, cb + +exports.elgamal_mdc_mismatch = (T, cb) -> + esc = make_esc cb, "elgamal MDC mismatch" + await mdc_mismatch_data_packet esc defer data_packet + check_variant { + T + name : "ElGamal MDC mismatch" + eme : wrong_session_key_eme() + data_packet + }, cb + +exports.elgamal_mdc_missing_header = (T, cb) -> + esc = make_esc cb, "MDC missing header crasher" + await missing_mdc_header_data_packet esc defer data_packet + check_variant { + T + name : "ElGamal MDC missing header" + eme : wrong_session_key_eme() + data_packet + }, cb