fork-openpgpjs/src/packet/openpgp.packet.keymaterial.js

808 lines
33 KiB
JavaScript

// GPG4Browsers - An OpenPGP implementation in javascript
// Copyright (C) 2011 Recurity Labs GmbH
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* @class
* @classdesc Implementation of the Key Material Packet (Tag 5,6,7,14)
*
* RFC4480 5.5:
* A key material packet contains all the information about a public or
* private key. There are four variants of this packet type, and two
* major versions. Consequently, this section is complex.
*/
function openpgp_packet_keymaterial() {
// members:
this.publicKeyAlgorithm = null;
this.tagType = null;
this.creationTime = null;
this.version = null;
this.expiration = null;// V3
this.MPIs = null;
this.secMPIs = null;
this.publicKey = null;
this.symmetricEncryptionAlgorithm = null;
this.s2kUsageConventions = null;
this.IVLength = null;
this.encryptedMPIData = null;
this.hasUnencryptedSecretKeyData = null;
this.checksum = null;
this.parentNode = null;
this.subKeySignature = null;
this.subKeyRevocationSignature = null;
// 5.5.1. Key Packet Variants
// 5.5.1.3. Secret-Key Packet (Tag 5)
/**
* This function reads the payload of a secret key packet (Tag 5)
* and initializes the openpgp_packet_keymaterial
* @param input input string to read the packet from
* @param position start position for the parser
* @param len length of the packet or remaining length of input
* @return openpgp_packet_keymaterial object
*/
function read_tag5(input, position, len) {
this.tagType = 5;
this.read_priv_key(input, position, len);
return this;
}
// 5.5.1.1. Public-Key Packet (Tag 6)
/**
* This function reads the payload of a public key packet (Tag 6)
* and initializes the openpgp_packet_keymaterial
* @param input input string to read the packet from
* @param position start position for the parser
* @param len length of the packet or remaining length of input
* @return openpgp_packet_keymaterial object
*/
function read_tag6(input, position, len) {
// A Public-Key packet starts a series of packets that forms an OpenPGP
// key (sometimes called an OpenPGP certificate).
this.tagType = 6;
this.packetLength = len;
this.read_pub_key(input, position,len);
return this;
}
// 5.5.1.4. Secret-Subkey Packet (Tag 7)
/**
* This function reads the payload of a secret key sub packet (Tag 7)
* and initializes the openpgp_packet_keymaterial
* @param input input string to read the packet from
* @param position start position for the parser
* @param len length of the packet or remaining length of input
* @return openpgp_packet_keymaterial object
*/
function read_tag7(input, position, len) {
this.tagType = 7;
this.packetLength = len;
return this.read_priv_key(input, position, len);
}
// 5.5.1.2. Public-Subkey Packet (Tag 14)
/**
* This function reads the payload of a public key sub packet (Tag 14)
* and initializes the openpgp_packet_keymaterial
* @param input input string to read the packet from
* @param position start position for the parser
* @param len length of the packet or remaining length of input
* @return openpgp_packet_keymaterial object
*/
function read_tag14(input, position, len) {
this.subKeySignature = null;
this.subKeyRevocationSignature = new Array();
this.tagType = 14;
this.packetLength = len;
this.read_pub_key(input, position,len);
return this;
}
/**
* Internal Parser for public keys as specified in RFC 4880 section 5.5.2 Public-Key Packet Formats
* called by read_tag<num>
* @param input input string to read the packet from
* @param position start position for the parser
* @param len length of the packet or remaining length of input
* @return this object with attributes set by the parser
*/
function read_pub_key(input, position, len) {
var mypos = position;
// A one-octet version number (3 or 4).
this.version = input[mypos++].charCodeAt();
if (this.version == 3) {
// A four-octet number denoting the time that the key was created.
this.creationTime = new Date(((input[mypos++].charCodeAt() << 24) |
(input[mypos++].charCodeAt() << 16) |
(input[mypos++].charCodeAt() << 8) |
(input[mypos++].charCodeAt()))*1000);
// - A two-octet number denoting the time in days that this key is
// valid. If this number is zero, then it does not expire.
this.expiration = (input[mypos++].charCodeAt() << 8) & input[mypos++].charCodeAt();
// - A one-octet number denoting the public-key algorithm of this key.
this.publicKeyAlgorithm = input[mypos++].charCodeAt();
var mpicount = 0;
// - A series of multiprecision integers comprising the key material:
// Algorithm-Specific Fields for RSA public keys:
// - a multiprecision integer (MPI) of RSA public modulus n;
// - an MPI of RSA public encryption exponent e.
if (this.publicKeyAlgorithm > 0 && this.publicKeyAlgorithm < 4)
mpicount = 2;
// Algorithm-Specific Fields for Elgamal public keys:
// - MPI of Elgamal prime p;
// - MPI of Elgamal group generator g;
// - MPI of Elgamal public key value y (= g**x mod p where x is secret).
else if (this.publicKeyAlgorithm == 16)
mpicount = 3;
// Algorithm-Specific Fields for DSA public keys:
// - MPI of DSA prime p;
// - MPI of DSA group order q (q is a prime divisor of p-1);
// - MPI of DSA group generator g;
// - MPI of DSA public-key value y (= g**x mod p where x is secret).
else if (this.publicKeyAlgorithm == 17)
mpicount = 4;
this.MPIs = new Array();
for (var i = 0; i < mpicount; i++) {
this.MPIs[i] = new openpgp_type_mpi();
if (this.MPIs[i].read(input, mypos, (mypos-position)) != null &&
!this.packetLength < (mypos-position)) {
mypos += this.MPIs[i].packetLength;
} else {
util.print_error("openpgp.packet.keymaterial.js\n"+'error reading MPI @:'+mypos);
}
}
this.packetLength = mypos-position;
} else if (this.version == 4) {
// - A four-octet number denoting the time that the key was created.
this.creationTime = new Date(((input[mypos++].charCodeAt() << 24) |
(input[mypos++].charCodeAt() << 16) |
(input[mypos++].charCodeAt() << 8) |
(input[mypos++].charCodeAt()))*1000);
// - A one-octet number denoting the public-key algorithm of this key.
this.publicKeyAlgorithm = input[mypos++].charCodeAt();
var mpicount = 0;
// - A series of multiprecision integers comprising the key material:
// Algorithm-Specific Fields for RSA public keys:
// - a multiprecision integer (MPI) of RSA public modulus n;
// - an MPI of RSA public encryption exponent e.
if (this.publicKeyAlgorithm > 0 && this.publicKeyAlgorithm < 4)
mpicount = 2;
// Algorithm-Specific Fields for Elgamal public keys:
// - MPI of Elgamal prime p;
// - MPI of Elgamal group generator g;
// - MPI of Elgamal public key value y (= g**x mod p where x is secret).
else if (this.publicKeyAlgorithm == 16)
mpicount = 3;
// Algorithm-Specific Fields for DSA public keys:
// - MPI of DSA prime p;
// - MPI of DSA group order q (q is a prime divisor of p-1);
// - MPI of DSA group generator g;
// - MPI of DSA public-key value y (= g**x mod p where x is secret).
else if (this.publicKeyAlgorithm == 17)
mpicount = 4;
this.MPIs = new Array();
var i = 0;
for (var i = 0; i < mpicount; i++) {
this.MPIs[i] = new openpgp_type_mpi();
if (this.MPIs[i].read(input, mypos, (mypos-position)) != null &&
!this.packetLength < (mypos-position)) {
mypos += this.MPIs[i].packetLength;
} else {
util.print_error("openpgp.packet.keymaterial.js\n"+'error reading MPI @:'+mypos);
}
}
this.packetLength = mypos-position;
} else {
return null;
}
this.data = input.substring(position, mypos);
this.packetdata = input.substring(position, mypos);
return this;
}
// 5.5.3. Secret-Key Packet Formats
/**
* Internal parser for private keys as specified in RFC 4880 section 5.5.3
* @param input input string to read the packet from
* @param position start position for the parser
* @param len length of the packet or remaining length of input
* @return this object with attributes set by the parser
*/
function read_priv_key(input,position, len) {
// - A Public-Key or Public-Subkey packet, as described above.
this.publicKey = new openpgp_packet_keymaterial();
if (this.publicKey.read_pub_key(input,position, len) == null) {
util.print_error("openpgp.packet.keymaterial.js\n"+"Failed reading public key portion of a private key: "+input[position].charCodeAt()+" "+position+" "+len+"\n Aborting here...");
return null;
}
this.publicKey.header = openpgp_packet.write_old_packet_header(6,this.publicKey.packetLength);
// this.publicKey.header = String.fromCharCode(0x99) + String.fromCharCode(this.publicKey.packetLength >> 8 & 0xFF)+String.fromCharCode(this.publicKey.packetLength & 0xFF);
var mypos = position + this.publicKey.data.length;
this.packetLength = len;
// - One octet indicating string-to-key usage conventions. Zero
// indicates that the secret-key data is not encrypted. 255 or 254
// indicates that a string-to-key specifier is being given. Any
// other value is a symmetric-key encryption algorithm identifier.
this.s2kUsageConventions = input[mypos++].charCodeAt();
if (this.s2kUsageConventions == 0)
this.hasUnencryptedSecretKeyData = true;
// - [Optional] If string-to-key usage octet was 255 or 254, a one-
// octet symmetric encryption algorithm.
if (this.s2kUsageConventions == 255 || this.s2kUsageConventions == 254) {
this.symmetricEncryptionAlgorithm = input[mypos++].charCodeAt();
}
// - [Optional] If string-to-key usage octet was 255 or 254, a
// string-to-key specifier. The length of the string-to-key
// specifier is implied by its type, as described above.
if (this.s2kUsageConventions == 255 || this.s2kUsageConventions == 254) {
this.s2k = new openpgp_type_s2k();
this.s2k.read(input, mypos);
mypos +=this.s2k.s2kLength;
}
// - [Optional] If secret data is encrypted (string-to-key usage octet
// not zero), an Initial Vector (IV) of the same length as the
// cipher's block size.
this.symkeylength = 0;
if (this.s2kUsageConventions != 0 && this.s2kUsageConventions != 255 &&
this.s2kUsageConventions != 254) {
this.symmetricEncryptionAlgorithm = this.s2kUsageConventions;
}
if (this.s2kUsageConventions != 0) {
this.hasIV = true;
switch (this.symmetricEncryptionAlgorithm) {
case 1: // - IDEA [IDEA]
util.print_error("openpgp.packet.keymaterial.js\n"+"symmetric encrytryption algorithim: IDEA is not implemented");
return null;
case 2: // - TripleDES (DES-EDE, [SCHNEIER] [HAC] - 168 bit key derived from 192)
case 3: // - CAST5 (128 bit key, as per [RFC2144])
this.IVLength = 8;
break;
case 4: // - Blowfish (128 bit key, 16 rounds) [BLOWFISH]
case 7: // - AES with 128-bit key [AES]
case 8: // - AES with 192-bit key
case 9: // - AES with 256-bit key
this.IVLength = 16;
break;
case 10: // - Twofish with 256-bit key [TWOFISH]
this.IVLength = 32;
break;
case 5: // - Reserved
case 6: // - Reserved
default:
util.print_error("openpgp.packet.keymaterial.js\n"+"unknown encryption algorithm for secret key :"+this.symmetricEncryptionAlgorithm);
return null;
}
mypos++;
this.IV = input.substring(mypos, mypos+this.IVLength);
mypos += this.IVLength;
}
// - Plain or encrypted multiprecision integers comprising the secret
// key data. These algorithm-specific fields are as described
// below.
//
//
if (!this.hasUnencryptedSecretKeyData) {
this.encryptedMPIData = input.substring(mypos, len);
mypos += this.encryptedMPIData.length;
} else {
if (this.publicKey.publicKeyAlgorithm > 0 && this.publicKey.publicKeyAlgorithm < 4) {
// Algorithm-Specific Fields for RSA secret keys:
// - multiprecision integer (MPI) of RSA secret exponent d.
// - MPI of RSA secret prime value p.
// - MPI of RSA secret prime value q (p < q).
// - MPI of u, the multiplicative inverse of p, mod q.
this.secMPIs = new Array();
this.secMPIs[0] = new openpgp_type_mpi();
this.secMPIs[0].read(input, mypos, len-2- (mypos - position));
mypos += this.secMPIs[0].packetLength;
this.secMPIs[1] = new openpgp_type_mpi();
this.secMPIs[1].read(input, mypos, len-2- (mypos - position));
mypos += this.secMPIs[1].packetLength;
this.secMPIs[2] = new openpgp_type_mpi();
this.secMPIs[2].read(input, mypos, len-2- (mypos - position));
mypos += this.secMPIs[2].packetLength;
this.secMPIs[3] = new openpgp_type_mpi();
this.secMPIs[3].read(input, mypos, len-2- (mypos - position));
mypos += this.secMPIs[3].packetLength;
} else if (this.publicKey.publicKeyAlgorithm == 16) {
// Algorithm-Specific Fields for Elgamal secret keys:
// - MPI of Elgamal secret exponent x.
this.secMPIs = new Array();
this.secMPIs[0] = new openpgp_type_mpi();
this.secMPIs[0].read(input, mypos, len-2- (mypos - position));
mypos += this.secMPIs[0].packetLength;
} else if (this.publicKey.publicKeyAlgorithm == 17) {
// Algorithm-Specific Fields for DSA secret keys:
// - MPI of DSA secret exponent x.
this.secMPIs = new Array();
this.secMPIs[0] = new openpgp_type_mpi();
this.secMPIs[0].read(input, mypos, len-2- (mypos - position));
mypos += this.secMPIs[0].packetLength;
}
// checksum because s2k usage convention is 0
this.checksum = new Array();
this.checksum[0] = input[mypos++].charCodeAt();
this.checksum[1] = input[mypos++].charCodeAt();
}
return this;
}
/**
* Decrypts the private key MPIs which are needed to use the key.
* openpgp_packet_keymaterial.hasUnencryptedSecretKeyData should be false otherwise
* a call to this function is not needed
*
* @param str_passphrase the passphrase for this private key as string
* @return true if the passphrase was correct; false if not
*/
function decryptSecretMPIs(str_passphrase) {
if (this.hasUnencryptedSecretKeyData)
return this.secMPIs;
// creating a key out of the passphrase
var key = this.s2k.produce_key(str_passphrase);
var cleartextMPIs = "";
switch (this.symmetricEncryptionAlgorithm) {
case 1: // - IDEA [IDEA]
util.print_error("openpgp.packet.keymaterial.js\n"+"symmetric encryption algorithim: IDEA is not implemented");
return false;
case 2: // - TripleDES (DES-EDE, [SCHNEIER] [HAC] - 168 bit key derived from 192)
cleartextMPIs = normal_cfb_decrypt(function(block, key) {
return des(key, block,1,null,0);
}, this.IVLength, key, this.encryptedMPIData, this.IV);
break;
case 3: // - CAST5 (128 bit key, as per [RFC2144])
cleartextMPIs = normal_cfb_decrypt(function(block, key) {
var cast5 = new openpgp_symenc_cast5();
cast5.setKey(key);
return cast5.encrypt(util.str2bin(block));
}, this.IVLength, util.str2bin(key.substring(0,16)), this.encryptedMPIData, this.IV);
break;
case 4: // - Blowfish (128 bit key, 16 rounds) [BLOWFISH]
cleartextMPIs = normal_cfb_decrypt(function(block, key) {
var blowfish = new Blowfish(key);
return blowfish.encrypt(block);
}, this.IVLength, key, this.encryptedMPIData, this.IV);
break;
case 7: // - AES with 128-bit key [AES]
case 8: // - AES with 192-bit key
case 9: // - AES with 256-bit key
var numBytes = 16;
//This is a weird way to achieve this. If's within a switch is probably not ideal.
if(this.symmetricEncryptionAlgorithm == 8){
numBytes = 24;
key = this.s2k.produce_key(str_passphrase,numBytes);
}
if(this.symmetricEncryptionAlgorithm == 9){
numBytes = 32;
key = this.s2k.produce_key(str_passphrase,numBytes);
}
cleartextMPIs = normal_cfb_decrypt(function(block,key){
return AESencrypt(util.str2bin(block),key);
},
this.IVLength, keyExpansion(key.substring(0,numBytes)), this.encryptedMPIData, this.IV);
break;
case 10: // - Twofish with 256-bit key [TWOFISH]
util.print_error("openpgp.packet.keymaterial.js\n"+"Key material is encrypted with twofish: not implemented");
return false;
case 5: // - Reserved
case 6: // - Reserved
default:
util.print_error("openpgp.packet.keymaterial.js\n"+"unknown encryption algorithm for secret key :"+this.symmetricEncryptionAlgorithm);
return false;
}
if (cleartextMPIs == null) {
util.print_error("openpgp.packet.keymaterial.js\n"+"cleartextMPIs was null");
return false;
}
var cleartextMPIslength = cleartextMPIs.length;
if (this.s2kUsageConventions == 254 &&
str_sha1(cleartextMPIs.substring(0,cleartextMPIs.length - 20)) ==
cleartextMPIs.substring(cleartextMPIs.length - 20)) {
cleartextMPIslength -= 20;
} else if (this.s2kUsageConventions != 254 && util.calc_checksum(cleartextMPIs.substring(0,cleartextMPIs.length - 2)) ==
(cleartextMPIs.charCodeAt(cleartextMPIs.length -2) << 8 | cleartextMPIs.charCodeAt(cleartextMPIs.length -1))) {
cleartextMPIslength -= 2;
} else {
return false;
}
if (this.publicKey.publicKeyAlgorithm > 0 && this.publicKey.publicKeyAlgorithm < 4) {
// Algorithm-Specific Fields for RSA secret keys:
// - multiprecision integer (MPI) of RSA secret exponent d.
// - MPI of RSA secret prime value p.
// - MPI of RSA secret prime value q (p < q).
// - MPI of u, the multiplicative inverse of p, mod q.
var mypos = 0;
this.secMPIs = new Array();
this.secMPIs[0] = new openpgp_type_mpi();
this.secMPIs[0].read(cleartextMPIs, 0, cleartextMPIslength);
mypos += this.secMPIs[0].packetLength;
this.secMPIs[1] = new openpgp_type_mpi();
this.secMPIs[1].read(cleartextMPIs, mypos, cleartextMPIslength-mypos);
mypos += this.secMPIs[1].packetLength;
this.secMPIs[2] = new openpgp_type_mpi();
this.secMPIs[2].read(cleartextMPIs, mypos, cleartextMPIslength-mypos);
mypos += this.secMPIs[2].packetLength;
this.secMPIs[3] = new openpgp_type_mpi();
this.secMPIs[3].read(cleartextMPIs, mypos, cleartextMPIslength-mypos);
mypos += this.secMPIs[3].packetLength;
} else if (this.publicKey.publicKeyAlgorithm == 16) {
// Algorithm-Specific Fields for Elgamal secret keys:
// - MPI of Elgamal secret exponent x.
this.secMPIs = new Array();
this.secMPIs[0] = new openpgp_type_mpi();
this.secMPIs[0].read(cleartextMPIs, 0, cleartextMPIs);
} else if (this.publicKey.publicKeyAlgorithm == 17) {
// Algorithm-Specific Fields for DSA secret keys:
// - MPI of DSA secret exponent x.
this.secMPIs = new Array();
this.secMPIs[0] = new openpgp_type_mpi();
this.secMPIs[0].read(cleartextMPIs, 0, cleartextMPIslength);
}
return true;
}
/**
* Generates Debug output
* @return String which gives some information about the keymaterial
*/
function toString() {
var result = "";
switch (this.tagType) {
case 6:
result += '5.5.1.1. Public-Key Packet (Tag 6)\n'+
' length: '+this.packetLength+'\n'+
' version: '+this.version+'\n'+
' creation time: '+this.creationTime+'\n'+
' expiration time: '+this.expiration+'\n'+
' publicKeyAlgorithm: '+this.publicKeyAlgorithm+'\n';
break;
case 14:
result += '5.5.1.2. Public-Subkey Packet (Tag 14)\n'+
' length: '+this.packetLength+'\n'+
' version: '+this.version+'\n'+
' creation time: '+this.creationTime+'\n'+
' expiration time: '+this.expiration+'\n'+
' publicKeyAlgorithm: '+this.publicKeyAlgorithm+'\n';
break;
case 5:
result +='5.5.1.3. Secret-Key Packet (Tag 5)\n'+
' length: '+this.packetLength+'\n'+
' version: '+this.publicKey.version+'\n'+
' creation time: '+this.publicKey.creationTime+'\n'+
' expiration time: '+this.publicKey.expiration+'\n'+
' publicKeyAlgorithm: '+this.publicKey.publicKeyAlgorithm+'\n';
break;
case 7:
result += '5.5.1.4. Secret-Subkey Packet (Tag 7)\n'+
' length: '+this.packetLength+'\n'+
' version[1]: '+(this.version == 4)+'\n'+
' creationtime[4]: '+this.creationTime+'\n'+
' expiration[2]: '+this.expiration+'\n'+
' publicKeyAlgorithm: '+this.publicKeyAlgorithm+'\n';
break;
default:
result += 'unknown key material packet\n';
}
if (this.MPIs != null) {
result += "Public Key MPIs:\n";
for (var i = 0; i < this.MPIs.length; i++) {
result += this.MPIs[i].toString();
}
}
if (this.publicKey != null && this.publicKey.MPIs != null) {
result += "Public Key MPIs:\n";
for (var i = 0; i < this.publicKey.MPIs.length; i++) {
result += this.publicKey.MPIs[i].toString();
}
}
if (this.secMPIs != null) {
result += "Secret Key MPIs:\n";
for (var i = 0; i < this.secMPIs.length; i++) {
result += this.secMPIs[i].toString();
}
}
if (this.subKeySignature != null)
result += "subKey Signature:\n"+this.subKeySignature.toString();
if (this.subKeyRevocationSignature != null )
result += "subKey Revocation Signature:\n"+this.subKeyRevocationSignature.toString();
return result;
}
/**
* Continue parsing packets belonging to the key material such as signatures
* @param {openpgp_*} parent_node the parent object
* @param {String} input input string to read the packet(s) from
* @param {integer} position start position for the parser
* @param {integer} len length of the packet(s) or remaining length of input
* @return {integer} length of nodes read
*/
function read_nodes(parent_node, input, position, len) {
this.parentNode = parent_node;
if (this.tagType == 14) { // public sub-key packet
var pos = position;
var result = null;
while (input.length != pos) {
var l = input.length - pos;
result = openpgp_packet.read_packet(input, pos, l);
if (result == null) {
util.print_error("openpgp.packet.keymaterial.js\n"+'[user_keymat_pub]parsing ends here @:' + pos + " l:" + l);
break;
} else {
switch (result.tagType) {
case 2: // Signature Packet certification signature
if (result.signatureType == 24) { // subkey binding signature
this.subKeySignature = result;
pos += result.packetLength + result.headerLength;
break;
} else if (result.signatureType == 40) { // subkey revocation signature
this.subKeyRevocationSignature[this.subKeyRevocationSignature.length] = result;
pos += result.packetLength + result.headerLength;
break;
} else {
util.print_error("openpgp.packet.keymaterial.js\nunknown signature:"+result.toString());
}
default:
this.data = input;
this.position = position - this.parentNode.packetLength;
this.len = pos - position;
return this.len;
break;
}
}
}
this.data = input;
this.position = position - this.parentNode.packetLength;
this.len = pos - position;
return this.len;
} else if (this.tagType == 7) { // private sub-key packet
var pos = position;
while (input.length != pos) {
var result = openpgp_packet.read_packet(input, pos, len - (pos - position));
if (result == null) {
util.print_error("openpgp.packet.keymaterial.js\n"+'[user_keymat_priv] parsing ends here @:' + pos);
break;
} else {
switch (result.tagType) {
case 2: // Signature Packet certification signature
if (result.signatureType == 24) // subkey embedded signature
this.subKeySignature = result;
else if (result.signatureType == 40) // subkey revocation signature
this.subKeyRevocationSignature[this.subKeyRevocationSignature.length] = result;
pos += result.packetLength + result.headerLength;
break;
default:
this.data = input;
this.position = position - this.parentNode.packetLength;
this.len = pos - position;
return this.len;
}
}
}
this.data = input;
this.position = position - this.parentNode.packetLength;
this.len = pos - position;
return this.len;
} else {
util.print_error("openpgp.packet.keymaterial.js\n"+"unknown parent node for a key material packet "+parent_node.tagType);
}
}
/**
* Checks the validity for usage of this (sub)key
* @return 0 = bad key, 1 = expired, 2 = revoked, 3 = valid
*/
function verifyKey() {
if (this.tagType == 14) {
if (this.subKeySignature == null) {
return 0;
}
if (this.subKeySignature.version == 4 &&
this.subKeySignature.keyNeverExpires != null &&
!this.subKeySignature.keyNeverExpires &&
new Date((this.subKeySignature.keyExpirationTime*1000)+ this.creationTime.getTime()) < new Date()) {
return 1;
}
var hashdata = String.fromCharCode(0x99)+this.parentNode.header.substring(1)+this.parentNode.data+
String.fromCharCode(0x99)+this.header.substring(1)+this.packetdata;
if (!this.subKeySignature.verify(hashdata,this.parentNode)) {
return 0;
}
for (var i = 0; i < this.subKeyRevocationSignature.length; i++) {
if (this.getKeyId() == this.subKeyRevocationSignature[i].keyId){
return 2;
}
}
}
return 3;
}
/**
* calculates the key id of they key
* @return {String} a 8 byte key id
*/
function getKeyId() {
if (this.version == 4) {
var f = this.getFingerprint();
return f.substring(12,20);
} else if (this.version == 3 && this.publicKeyAlgorithm > 0 && this.publicKeyAlgorithm < 4) {
var key_id = this.MPIs[0].substring((this.MPIs[0].mpiByteLength-8));
util.print_debug("openpgp.msg.publickey read_nodes:\n"+"V3 key ID: "+key_id);
return key_id;
}
}
/**
* calculates the fingerprint of the key
* @return {String} a string containing the fingerprint
*/
function getFingerprint() {
if (this.version == 4) {
tohash = String.fromCharCode(0x99)+ String.fromCharCode(((this.packetdata.length) >> 8) & 0xFF)
+ String.fromCharCode((this.packetdata.length) & 0xFF)+this.packetdata;
util.print_debug("openpgp.msg.publickey creating subkey fingerprint by hashing:"+util.hexstrdump(tohash)+"\npublickeyalgorithm: "+this.publicKeyAlgorithm);
return str_sha1(tohash, tohash.length);
} else if (this.version == 3 && this.publicKeyAlgorithm > 0 && this.publicKeyAlgorithm < 4) {
return MD5(this.MPIs[0].MPI);
}
}
/*
* creates an OpenPGP key packet for the given key. much TODO in regards to s2k, subkeys.
* @param {int} keyType follows the OpenPGP algorithm standard, IE 1 corresponds to RSA.
* @param {RSA.keyObject} key
* @param password
* @param s2kHash
* @param symmetricEncryptionAlgorithm
* @param timePacket
* @return {body: [string]OpenPGP packet body contents, header: [string] OpenPGP packet header, string: [string] header+body}
*/
function write_private_key(keyType, key, password, s2kHash, symmetricEncryptionAlgorithm, timePacket){
this.symmetricEncryptionAlgorithm = symmetricEncryptionAlgorithm;
var tag = 5;
var body = String.fromCharCode(4);
body += timePacket;
switch(keyType){
case 1:
body += String.fromCharCode(keyType);//public key algo
body += key.n.toMPI();
body += key.ee.toMPI();
var algorithmStart = body.length;
//below shows ske/s2k
if(password){
body += String.fromCharCode(254); //octet of 254 indicates s2k with SHA1
//if s2k == 255,254 then 1 octet symmetric encryption algo
body += String.fromCharCode(this.symmetricEncryptionAlgorithm);
//if s2k == 255,254 then s2k specifier
body += String.fromCharCode(3); //s2k salt+iter
body += String.fromCharCode(s2kHash);
//8 octet salt value
//1 octet count
var cleartextMPIs = key.d.toMPI() + key.p.toMPI() + key.q.toMPI() + key.u.toMPI();
var sha1Hash = str_sha1(cleartextMPIs);
util.print_debug_hexstr_dump('write_private_key sha1: ',sha1Hash);
var salt = openpgp_crypto_getRandomBytes(8);
util.print_debug_hexstr_dump('write_private_key Salt: ',salt);
body += salt;
var c = 96; //c of 96 translates to count of 65536
body += String.fromCharCode(c);
util.print_debug('write_private_key c: '+ c);
var s2k = new openpgp_type_s2k();
var hashKey = s2k.write(3, s2kHash, password, salt, c);
//if s2k, IV of same length as cipher's block
switch(this.symmetricEncryptionAlgorithm){
case 3:
this.IVLength = 8;
this.IV = openpgp_crypto_getRandomBytes(this.IVLength);
ciphertextMPIs = normal_cfb_encrypt(function(block, key) {
var cast5 = new openpgp_symenc_cast5();
cast5.setKey(key);
return cast5.encrypt(util.str2bin(block));
}, this.IVLength, util.str2bin(hashKey.substring(0,16)), cleartextMPIs + sha1Hash, this.IV);
body += this.IV + ciphertextMPIs;
break;
case 7:
case 8:
case 9:
this.IVLength = 16;
this.IV = openpgp_crypto_getRandomBytes(this.IVLength);
ciphertextMPIs = normal_cfb_encrypt(AESencrypt,
this.IVLength, hashKey, cleartextMPIs + sha1Hash, this.IV);
body += this.IV + ciphertextMPIs;
break;
}
}
else{
body += String.fromCharCode(0);//1 octet -- s2k, 0 for no s2k
body += key.d.toMPI() + key.p.toMPI() + key.q.toMPI() + key.u.toMPI();
var checksum = util.calc_checksum(key.d.toMPI() + key.p.toMPI() + key.q.toMPI() + key.u.toMPI());
body += String.fromCharCode(checksum/0x100) + String.fromCharCode(checksum%0x100);//DEPRECATED:s2k == 0, 255: 2 octet checksum, sum all octets%65536
util.print_debug_hexstr_dump('write_private_key basic checksum: '+ checksum);
}
break;
default :
body = "";
util.print_error("openpgp.packet.keymaterial.js\n"+'error writing private key, unknown type :'+keyType);
}
var header = openpgp_packet.write_packet_header(tag,body.length);
return {string: header+body , header: header, body: body};
}
/*
* same as write_private_key, but has less information because of public key.
* @param {int} keyType follows the OpenPGP algorithm standard, IE 1 corresponds to RSA.
* @param {RSA.keyObject} key
* @param timePacket
* @return {body: [string]OpenPGP packet body contents, header: [string] OpenPGP packet header, string: [string] header+body}
*/
function write_public_key(keyType, key, timePacket){
var tag = 6;
var body = String.fromCharCode(4);
body += timePacket;
switch(keyType){
case 1:
body += String.fromCharCode(1);//public key algo
body += key.n.toMPI();
body += key.ee.toMPI();
break;
default:
util.print_error("openpgp.packet.keymaterial.js\n"+'error writing private key, unknown type :'+keyType);
}
var header = openpgp_packet.write_packet_header(tag,body.length);
return {string: header+body , header: header, body: body};
}
this.read_tag5 = read_tag5;
this.read_tag6 = read_tag6;
this.read_tag7 = read_tag7;
this.read_tag14 = read_tag14;
this.toString = toString;
this.read_pub_key = read_pub_key;
this.read_priv_key = read_priv_key;
this.decryptSecretMPIs = decryptSecretMPIs;
this.read_nodes = read_nodes;
this.verifyKey = verifyKey;
this.getKeyId = getKeyId;
this.getFingerprint = getFingerprint;
this.write_private_key = write_private_key;
this.write_public_key = write_public_key;
}