diff --git a/src/key.js b/src/key.js
index 6587bd72..5f21d9f0 100644
--- a/src/key.js
+++ b/src/key.js
@@ -468,7 +468,7 @@ Key.prototype.getExpirationTime = async function() {
   if (this.primaryKey.version === 3) {
     return getExpirationTime(this.primaryKey);
   }
-  if (this.primaryKey.version === 4) {
+  if (this.primaryKey.version >= 4) {
     const primaryUser = await this.getPrimaryUser(null);
     const selfCert = primaryUser.selfCertification;
     const keyExpiry = getExpirationTime(this.primaryKey, selfCert);
@@ -1383,7 +1383,7 @@ function getExpirationTime(keyPacket, signature) {
     expirationTime = keyPacket.created.getTime() + keyPacket.expirationTimeV3*24*3600*1000;
   }
   // check V4 expiration time
-  if (keyPacket.version === 4 && signature.keyNeverExpires === false) {
+  if (keyPacket.version >= 4 && signature.keyNeverExpires === false) {
     expirationTime = keyPacket.created.getTime() + signature.keyExpirationTime*1000;
   }
   return expirationTime ? new Date(expirationTime) : Infinity;
diff --git a/src/packet/public_key.js b/src/packet/public_key.js
index de2bb5fc..f76e16fa 100644
--- a/src/packet/public_key.js
+++ b/src/packet/public_key.js
@@ -18,6 +18,7 @@
 /**
  * @requires type/keyid
  * @requires type/mpi
+ * @requires config
  * @requires crypto
  * @requires enums
  * @requires util
@@ -25,6 +26,7 @@
 
 import type_keyid from '../type/keyid';
 import type_mpi from '../type/mpi';
+import config from '../config';
 import crypto from '../crypto';
 import enums from '../enums';
 import util from '../util';
@@ -52,7 +54,7 @@ function PublicKey(date=new Date()) {
    * Packet version
    * @type {Integer}
    */
-  this.version = 4;
+  this.version = config.aead_protect === 'draft04' ? 5 : 4;
   /**
    * Key creation date.
    * @type {Date}
@@ -88,10 +90,10 @@ function PublicKey(date=new Date()) {
  */
 PublicKey.prototype.read = function (bytes) {
   let pos = 0;
-  // A one-octet version number (3 or 4).
+  // A one-octet version number (3, 4 or 5).
   this.version = bytes[pos++];
 
-  if (this.version === 3 || this.version === 4) {
+  if (this.version === 3 || this.version === 4 || this.version === 5) {
     // - A four-octet number denoting the time that the key was created.
     this.created = util.readDate(bytes.subarray(pos, pos + 4));
     pos += 4;
@@ -106,20 +108,25 @@ PublicKey.prototype.read = function (bytes) {
     // - A one-octet number denoting the public-key algorithm of this key.
     this.algorithm = enums.read(enums.publicKey, bytes[pos++]);
     const algo = enums.write(enums.publicKey, this.algorithm);
+
+    if (this.version === 5) {
+      // - A four-octet scalar octet count for the following key material.
+      pos += 4;
+    }
+
+    // - A series of values comprising the key material.  This is
+    //   algorithm-specific and described in section XXXX.
     const types = crypto.getPubKeyParamTypes(algo);
     this.params = crypto.constructParams(types);
 
-    const b = bytes.subarray(pos, bytes.length);
-    let p = 0;
-
-    for (let i = 0; i < types.length && p < b.length; i++) {
-      p += this.params[i].read(b.subarray(p, b.length));
-      if (p > b.length) {
-        throw new Error('Error reading MPI @:' + p);
+    for (let i = 0; i < types.length && pos < bytes.length; i++) {
+      pos += this.params[i].read(bytes.subarray(pos, bytes.length));
+      if (pos > bytes.length) {
+        throw new Error('Error reading MPI @:' + pos);
       }
     }
 
-    return p + 6;
+    return pos;
   }
   throw new Error('Version ' + this.version + ' of the key packet is unsupported.');
 };
@@ -143,14 +150,18 @@ PublicKey.prototype.write = function () {
   if (this.version === 3) {
     arr.push(util.writeNumber(this.expirationTimeV3, 2));
   }
-  // Algorithm-specific params
+  // A one-octet number denoting the public-key algorithm of this key
   const algo = enums.write(enums.publicKey, this.algorithm);
-  const paramCount = crypto.getPubKeyParamTypes(algo).length;
   arr.push(new Uint8Array([algo]));
-  for (let i = 0; i < paramCount; i++) {
-    arr.push(this.params[i].write());
-  }
 
+  const paramCount = crypto.getPubKeyParamTypes(algo).length;
+  const params = util.concatUint8Array(this.params.slice(0, paramCount).map(param => param.write()));
+  if (this.version === 5) {
+    // A four-octet scalar octet count for the following key material
+    arr.push(util.writeNumber(params.length, 4));
+  }
+  // Algorithm-specific params
+  arr.push(params);
   return util.concatUint8Array(arr);
 };
 
@@ -178,7 +189,9 @@ PublicKey.prototype.getKeyId = function () {
     return this.keyid;
   }
   this.keyid = new type_keyid();
-  if (this.version === 4) {
+  if (this.version === 5) {
+    this.keyid.read(util.hex_to_Uint8Array(this.getFingerprint()).subarray(0, 8));
+  } else if (this.version === 4) {
     this.keyid.read(util.hex_to_Uint8Array(this.getFingerprint()).subarray(12, 20));
   } else if (this.version === 3) {
     const arr = this.params[0].write();
@@ -195,13 +208,18 @@ PublicKey.prototype.getFingerprint = function () {
   if (this.fingerprint) {
     return this.fingerprint;
   }
-  let toHash = '';
-  if (this.version === 4) {
+  let toHash;
+  if (this.version === 5) {
+    const bytes = this.writePublicKey();
+    toHash = util.concatUint8Array([new Uint8Array([0x9A]), util.writeNumber(bytes.length, 4), bytes]);
+    this.fingerprint = util.Uint8Array_to_str(crypto.hash.sha256(toHash));
+  } else if (this.version === 4) {
     toHash = this.writeOld();
     this.fingerprint = util.Uint8Array_to_str(crypto.hash.sha1(toHash));
   } else if (this.version === 3) {
     const algo = enums.write(enums.publicKey, this.algorithm);
     const paramCount = crypto.getPubKeyParamTypes(algo).length;
+    toHash = '';
     for (let i = 0; i < paramCount; i++) {
       toHash += this.params[i].toString();
     }
diff --git a/src/packet/secret_key.js b/src/packet/secret_key.js
index 80135a98..f764ff80 100644
--- a/src/packet/secret_key.js
+++ b/src/packet/secret_key.js
@@ -78,15 +78,17 @@ function get_hash_fn(hash) {
 // Helper function
 
 function parse_cleartext_params(hash_algorithm, cleartext, algorithm) {
-  const hashlen = get_hash_len(hash_algorithm);
-  const hashfn = get_hash_fn(hash_algorithm);
+  if (hash_algorithm) {
+    const hashlen = get_hash_len(hash_algorithm);
+    const hashfn = get_hash_fn(hash_algorithm);
 
-  const hashtext = util.Uint8Array_to_str(cleartext.subarray(cleartext.length - hashlen, cleartext.length));
-  cleartext = cleartext.subarray(0, cleartext.length - hashlen);
-  const hash = util.Uint8Array_to_str(hashfn(cleartext));
+    const hashtext = util.Uint8Array_to_str(cleartext.subarray(cleartext.length - hashlen, cleartext.length));
+    cleartext = cleartext.subarray(0, cleartext.length - hashlen);
+    const hash = util.Uint8Array_to_str(hashfn(cleartext));
 
-  if (hash !== hashtext) {
-    return new Error("Incorrect key passphrase");
+    if (hash !== hashtext) {
+      throw new Error("Incorrect key passphrase");
+    }
   }
 
   const algo = enums.write(enums.publicKey, algorithm);
@@ -115,9 +117,13 @@ function write_cleartext_params(hash_algorithm, algorithm, params) {
 
   const bytes = util.concatUint8Array(arr);
 
-  const hash = get_hash_fn(hash_algorithm)(bytes);
+  if (hash_algorithm) {
+    const hash = get_hash_fn(hash_algorithm)(bytes);
 
-  return util.concatUint8Array([bytes, hash]);
+    return util.concatUint8Array([bytes, hash]);
+  }
+
+  return bytes;
 }
 
 
@@ -125,7 +131,7 @@ function write_cleartext_params(hash_algorithm, algorithm, params) {
 
 /**
  * Internal parser for private keys as specified in
- * {@link https://tools.ietf.org/html/rfc4880#section-5.5.3|RFC 4880 section 5.5.3}
+ * {@link https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-04#section-5.5.3|RFC4880bis-04 section 5.5.3}
  * @param {String} bytes Input string to read the packet from
  */
 SecretKey.prototype.read = function (bytes) {
@@ -148,9 +154,6 @@ SecretKey.prototype.read = function (bytes) {
     //   key data.  These algorithm-specific fields are as described
     //   below.
     const privParams = parse_cleartext_params('mod', bytes.subarray(1, bytes.length), this.algorithm);
-    if (privParams instanceof Error) {
-      throw privParams;
-    }
     this.params = this.params.concat(privParams);
     this.isDecrypted = true;
   }
@@ -194,15 +197,29 @@ SecretKey.prototype.encrypt = async function (passphrase) {
   const s2k = new type_s2k();
   s2k.salt = await crypto.random.getRandomBytes(8);
   const symmetric = 'aes256';
-  const cleartext = write_cleartext_params('sha1', this.algorithm, this.params);
+  const hash = this.version === 5 ? null : 'sha1';
+  const cleartext = write_cleartext_params(hash, this.algorithm, this.params);
   const key = produceEncryptionKey(s2k, passphrase, symmetric);
   const blockLen = crypto.cipher[symmetric].blockSize;
   const iv = await crypto.random.getRandomBytes(blockLen);
 
-  const arr = [new Uint8Array([254, enums.write(enums.symmetric, symmetric)])];
-  arr.push(s2k.write());
-  arr.push(iv);
-  arr.push(crypto.cfb.normalEncrypt(symmetric, key, cleartext, iv));
+  let arr;
+
+  if (this.version === 5) {
+    const aead = 'eax';
+    const optionalFields = util.concatUint8Array([new Uint8Array([enums.write(enums.symmetric, symmetric), enums.write(enums.aead, aead)]), s2k.write(), iv]);
+    arr = [new Uint8Array([253, optionalFields.length])];
+    arr.push(optionalFields);
+    const mode = crypto[aead];
+    const encrypted = await mode.encrypt(symmetric, cleartext, key, iv.subarray(0, mode.ivLength), new Uint8Array());
+    arr.push(util.writeNumber(encrypted.length, 4));
+    arr.push(encrypted);
+  } else {
+    arr = [new Uint8Array([254, enums.write(enums.symmetric, symmetric)])];
+    arr.push(s2k.write());
+    arr.push(iv);
+    arr.push(crypto.cfb.normalEncrypt(symmetric, key, cleartext, iv));
+  }
 
   this.encrypted = util.concatUint8Array(arr);
   return true;
@@ -230,17 +247,31 @@ SecretKey.prototype.decrypt = async function (passphrase) {
 
   let i = 0;
   let symmetric;
+  let aead;
   let key;
 
   const s2k_usage = this.encrypted[i++];
 
-  // - [Optional] If string-to-key usage octet was 255 or 254, a one-
-  //   octet symmetric encryption algorithm.
-  if (s2k_usage === 255 || s2k_usage === 254) {
+  // - Only for a version 5 packet, a one-octet scalar octet count of
+  //   the next 4 optional fields.
+  if (this.version === 5) {
+    i++;
+  }
+
+  // - [Optional] If string-to-key usage octet was 255, 254, or 253, a
+  //   one-octet symmetric encryption algorithm.
+  if (s2k_usage === 255 || s2k_usage === 254 || s2k_usage === 253) {
     symmetric = this.encrypted[i++];
     symmetric = enums.read(enums.symmetric, symmetric);
 
-    // - [Optional] If string-to-key usage octet was 255 or 254, a
+    // - [Optional] If string-to-key usage octet was 253, a one-octet
+    //   AEAD algorithm.
+    if (s2k_usage === 253) {
+      aead = this.encrypted[i++];
+      aead = enums.read(enums.aead, aead);
+    }
+
+    // - [Optional] If string-to-key usage octet was 255, 254, or 253, a
     //   string-to-key specifier.  The length of the string-to-key
     //   specifier is implied by its type, as described above.
     const s2k = new type_s2k();
@@ -263,16 +294,32 @@ SecretKey.prototype.decrypt = async function (passphrase) {
 
   i += iv.length;
 
+  // - Only for a version 5 packet, a four-octet scalar octet count for
+  //   the following key material.
+  if (this.version === 5) {
+    i += 4;
+  }
+
   const ciphertext = this.encrypted.subarray(i, this.encrypted.length);
-  const cleartext = crypto.cfb.normalDecrypt(symmetric, key, ciphertext, iv);
-  const hash = s2k_usage === 254 ?
-    'sha1' :
+  let cleartext;
+  if (aead) {
+    const mode = crypto[aead];
+    try {
+      cleartext = await mode.decrypt(symmetric, ciphertext, key, iv.subarray(0, mode.ivLength), new Uint8Array());
+    } catch(err) {
+      if (err.message.startsWith('Authentication tag mismatch')) {
+        throw new Error('Incorrect key passphrase: ' + err.message);
+      }
+    }
+  } else {
+    cleartext = crypto.cfb.normalDecrypt(symmetric, key, ciphertext, iv);
+  }
+  const hash =
+    s2k_usage === 253 ? null :
+    s2k_usage === 254 ? 'sha1' :
     'mod';
 
   const privParams = parse_cleartext_params(hash, cleartext, this.algorithm);
-  if (privParams instanceof Error) {
-    throw privParams;
-  }
   this.params = this.params.concat(privParams);
   this.isDecrypted = true;
   this.encrypted = null;
diff --git a/test/general/key.js b/test/general/key.js
index 6839f963..3248edcd 100644
--- a/test/general/key.js
+++ b/test/general/key.js
@@ -6,6 +6,23 @@ chai.use(require('chai-as-promised'));
 const { expect } = chai;
 
 describe('Key', function() {
+  describe('V4', tests);
+
+  describe('V5', function() {
+    let aead_protectVal;
+    beforeEach(function() {
+      aead_protectVal = openpgp.config.aead_protect;
+      openpgp.config.aead_protect = 'draft04';
+    });
+    afterEach(function() {
+      openpgp.config.aead_protect = aead_protectVal;
+    });
+
+    tests();
+  });
+});
+
+function tests() {
   const twoKeys =
        ['-----BEGIN PGP PUBLIC KEY BLOCK-----',
         'Version: GnuPG v2.0.19 (GNU/Linux)',
@@ -893,6 +910,21 @@ p92yZgB3r2+f6/GIe2+7
     done();
   });
 
+  it('Parsing V5 public key packet', function() {
+    // Manually modified from https://gitlab.com/openpgp-wg/rfc4880bis/blob/00b2092/back.mkd#sample-eddsa-key
+    let packetBytes = openpgp.util.hex_to_Uint8Array(`
+      98 37 05 53 f3 5f 0b 16  00 00 00 2d  09 2b 06 01 04 01 da 47
+      0f 01 01 07 40 3f 09 89  94 bd d9 16 ed 40 53 19
+      79 34 e4 a8 7c 80 73 3a  12 80 d6 2f 80 10 99 2e
+      43 ee 3b 24 06
+    `.replace(/\s+/g, ''));
+
+    let packetlist = new openpgp.packet.List();
+    packetlist.read(packetBytes);
+    let key = packetlist[0];
+    expect(key).to.exist;
+  });
+
   it('Testing key ID and fingerprint for V3 and V4 keys', function(done) {
     const pubKeysV4 = openpgp.key.readArmored(twoKeys);
     expect(pubKeysV4).to.exist;
@@ -1574,4 +1606,4 @@ p92yZgB3r2+f6/GIe2+7
       expect(error.message).to.equal('Error encrypting message: Could not find valid key packet for encryption in key ' + key.primaryKey.getKeyId().toHex());
     });
   });
-});
+}
diff --git a/test/general/packet.js b/test/general/packet.js
index 2aba6164..3759a6fa 100644
--- a/test/general/packet.js
+++ b/test/general/packet.js
@@ -637,6 +637,38 @@ describe("Packet", function() {
     });
   });
 
+  it('Writing and encryption of a secret key packet. (draft04)', function() {
+    let aead_protectVal = openpgp.config.aead_protect;
+    openpgp.config.aead_protect = 'draft04';
+
+    const key = new openpgp.packet.List();
+    key.push(new openpgp.packet.SecretKey());
+
+    const rsa = openpgp.crypto.publicKey.rsa;
+    const keySize = openpgp.util.getWebCryptoAll() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys
+
+    return rsa.generate(keySize, "10001").then(async function(mpiGen) {
+      let mpi = [mpiGen.n, mpiGen.e, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u];
+      mpi = mpi.map(function(k) {
+        return new openpgp.MPI(k);
+      });
+
+      key[0].params = mpi;
+      key[0].algorithm = "rsa_sign";
+      await key[0].encrypt('hello');
+
+      const raw = key.write();
+
+      const key2 = new openpgp.packet.List();
+      key2.read(raw);
+      await key2[0].decrypt('hello');
+
+      expect(key[0].params.toString()).to.equal(key2[0].params.toString());
+    }).finally(function() {
+      openpgp.config.aead_protect = aead_protectVal;
+    });
+  });
+
   it('Writing and verification of a signature packet.', function() {
     const key = new openpgp.packet.SecretKey();