diff --git a/src/packet/public_key.js b/src/packet/public_key.js index 15e90857..d9d038f2 100644 --- a/src/packet/public_key.js +++ b/src/packet/public_key.js @@ -106,10 +106,10 @@ class PublicKeyPacket { */ async read(bytes) { let pos = 0; - // A one-octet version number (3, 4 or 5). + // A one-octet version number (4, 5 or 6). this.version = bytes[pos++]; - if (this.version === 4 || this.version === 5) { + if (this.version === 4 || this.version === 5 || this.version === 6) { // - A four-octet number denoting the time that the key was created. this.created = util.readDate(bytes.subarray(pos, pos + 4)); pos += 4; @@ -117,7 +117,7 @@ class PublicKeyPacket { // - A one-octet number denoting the public-key algorithm of this key. this.algorithm = bytes[pos++]; - if (this.version === 5) { + if (this.version >= 5) { // - A four-octet scalar octet count for the following key material. pos += 4; } @@ -147,7 +147,7 @@ class PublicKeyPacket { arr.push(new Uint8Array([this.algorithm])); const params = crypto.serializeParams(this.algorithm, this.publicParams); - if (this.version === 5) { + if (this.version >= 5) { // A four-octet scalar octet count for the following key material arr.push(util.writeNumber(params.length, 4)); } @@ -163,10 +163,9 @@ class PublicKeyPacket { writeForHash(version) { const bytes = this.writePublicKey(); - if (version === 5) { - return util.concatUint8Array([new Uint8Array([0x9A]), util.writeNumber(bytes.length, 4), bytes]); - } - return util.concatUint8Array([new Uint8Array([0x99]), util.writeNumber(bytes.length, 2), bytes]); + const versionOctet = 0x95 + version; + const lengthOctets = version >= 5 ? 4 : 2; + return util.concatUint8Array([new Uint8Array([versionOctet]), util.writeNumber(bytes.length, lengthOctets), bytes]); } /** @@ -201,7 +200,7 @@ class PublicKeyPacket { await this.computeFingerprint(); this.keyID = new KeyID(); - if (this.version === 5) { + if (this.version >= 5) { this.keyID.read(this.fingerprint.subarray(0, 8)); } else if (this.version === 4) { this.keyID.read(this.fingerprint.subarray(12, 20)); @@ -216,7 +215,7 @@ class PublicKeyPacket { async computeFingerprint() { const toHash = this.writeForHash(this.version); - if (this.version === 5) { + if (this.version >= 5) { this.fingerprint = await crypto.hash.sha256(toHash); } else if (this.version === 4) { this.fingerprint = await crypto.hash.sha1(toHash); diff --git a/src/packet/secret_key.js b/src/packet/secret_key.js index cab07335..d3e8616f 100644 --- a/src/packet/secret_key.js +++ b/src/packet/secret_key.js @@ -100,6 +100,14 @@ class SecretKeyPacket extends PublicKeyPacket { i++; } + // - Only for a version 6 packet where the secret key material is + // encrypted (that is, where the previous octet is not zero), a one- + // octet scalar octet count of the cumulative length of all the + // following optional string-to-key parameter fields. + if (this.version === 6 && this.s2kUsage) { + i++; + } + try { // - [Optional] If string-to-key usage octet was 255, 254, or 253, a // one-octet symmetric encryption algorithm. @@ -112,6 +120,12 @@ class SecretKeyPacket extends PublicKeyPacket { this.aead = bytes[i++]; } + // - [Optional] Only for a version 6 packet, and if string-to-key usage + // octet was 255, 254, or 253, an one-octet count of the following field. + if (this.version === 6) { + i++; + } + // - [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. @@ -157,9 +171,14 @@ class SecretKeyPacket extends PublicKeyPacket { this.isEncrypted = !!this.s2kUsage; if (!this.isEncrypted) { - const cleartext = this.keyMaterial.subarray(0, -2); - if (!util.equalsUint8Array(util.writeChecksum(cleartext), this.keyMaterial.subarray(-2))) { - throw new Error('Key checksum mismatch'); + let cleartext; + if (this.version === 6) { + cleartext = this.keyMaterial; + } else { + cleartext = this.keyMaterial.subarray(0, -2); + if (!util.equalsUint8Array(util.writeChecksum(cleartext), this.keyMaterial.subarray(-2))) { + throw new Error('Key checksum mismatch'); + } } try { const { privateParams } = crypto.parsePrivateKeyParams(this.algorithm, cleartext, this.publicParams); @@ -200,10 +219,18 @@ class SecretKeyPacket extends PublicKeyPacket { optionalFieldsArr.push(this.aead); } + const s2k = this.s2k.write(); + + // - [Optional] Only for a version 6 packet, and if string-to-key usage + // octet was 255, 254, or 253, an one-octet count of the following field. + if (this.version === 6) { + optionalFieldsArr.push(s2k.length); + } + // - [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. - optionalFieldsArr.push(...this.s2k.write()); + optionalFieldsArr.push(...s2k); } // - [Optional] If secret data is encrypted (string-to-key usage octet @@ -213,7 +240,7 @@ class SecretKeyPacket extends PublicKeyPacket { optionalFieldsArr.push(...this.iv); } - if (this.version === 5) { + if (this.version === 5 || (this.version === 6 && this.s2kUsage)) { arr.push(new Uint8Array([optionalFieldsArr.length])); } arr.push(new Uint8Array(optionalFieldsArr)); @@ -228,7 +255,7 @@ class SecretKeyPacket extends PublicKeyPacket { } arr.push(this.keyMaterial); - if (!this.s2kUsage) { + if (!this.s2kUsage && this.version !== 6) { arr.push(util.writeChecksum(this.keyMaterial)); } }