Encryption of secret key packets is working.
This commit is contained in:
parent
945fa0ba51
commit
1e49e8ee23
|
@ -7582,7 +7582,7 @@ function r2s(t) {
|
|||
* or an object with attribute "text" containing the message text
|
||||
* and an attribute "openpgp" containing the bytes.
|
||||
*/
|
||||
function openpgp_encoding_deArmor(text) {
|
||||
function openpgp_encoding_dearmor(text) {
|
||||
text = text.replace(/\r/g, '')
|
||||
|
||||
var type = openpgp_encoding_get_type(text);
|
||||
|
@ -9495,17 +9495,6 @@ function openpgp_packet_compressed() {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pretty printing the packet (useful for debug purposes)
|
||||
* @return {String}
|
||||
*/
|
||||
this.toString = function() {
|
||||
return '5.6. Compressed Data Packet (Tag 8)\n'+
|
||||
' Compression Algorithm = '+this.algorithm+'\n'+
|
||||
' Compressed Data: Byte ['+util.hexstrdump(this.compressed)+']\n';
|
||||
}
|
||||
};
|
||||
// GPG4Browsers - An OpenPGP implementation in javascript
|
||||
// Copyright (C) 2011 Recurity Labs GmbH
|
||||
|
@ -9924,7 +9913,7 @@ function _openpgp_packet() {
|
|||
* @param {integer} len Length of the input from position on
|
||||
* @return {Object} Returns a parsed openpgp_packet
|
||||
*/
|
||||
function read_packet(input, position, len) {
|
||||
this.read_packet = function(input, position, len) {
|
||||
// some sanity checks
|
||||
if (input == null || input.length <= position
|
||||
|| input.substring(position).length < 2
|
||||
|
@ -10103,7 +10092,6 @@ function _openpgp_packet() {
|
|||
};
|
||||
}
|
||||
|
||||
this.read_packet = read_packet;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -10155,6 +10143,9 @@ var openpgp_packet = new _openpgp_packet();
|
|||
* are stored as numerical indices.
|
||||
*/
|
||||
function openpgp_packetlist() {
|
||||
/** The number of packets contained within the list.
|
||||
* @readonly
|
||||
* @type {Integer} */
|
||||
this.length = 0;
|
||||
|
||||
|
||||
|
@ -10374,31 +10365,6 @@ function openpgp_packet_public_key_encrypted_session_key() {
|
|||
this.symmetric_algorithm = decoded.charCodeAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string representation of this object (useful for debug
|
||||
* purposes)
|
||||
*
|
||||
* @return {String} The string containing a openpgp description
|
||||
*/
|
||||
this.toString = function() {
|
||||
var result = '5.1. Public-Key Encrypted Session Key Packets (Tag 1)\n'
|
||||
+ ' KeyId: '
|
||||
+ this.keyId.toString()
|
||||
+ '\n'
|
||||
+ ' length: '
|
||||
+ this.packetLength
|
||||
+ '\n'
|
||||
+ ' version:'
|
||||
+ this.version
|
||||
+ '\n'
|
||||
+ ' pubAlgUs:'
|
||||
+ this.publicKeyAlgorithmUsed + '\n';
|
||||
for ( var i = 0; i < this.encrypted.length; i++) {
|
||||
result += this.encrypted[i].toString();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// GPG4Browsers - An OpenPGP implementation in javascript
|
||||
|
@ -10430,7 +10396,7 @@ function openpgp_packet_public_key_encrypted_session_key() {
|
|||
function openpgp_packet_public_key() {
|
||||
this.tag = 6;
|
||||
this.version = 4;
|
||||
this.created = null;
|
||||
this.created = new Date();
|
||||
this.mpi = [];
|
||||
this.algorithm = openpgp.publickey.rsa_sign;
|
||||
|
||||
|
@ -10612,11 +10578,7 @@ function openpgp_packet_secret_key() {
|
|||
this.tag = 5;
|
||||
this.public_key = new openpgp_packet_public_key();
|
||||
this.mpi = [];
|
||||
this.symmetric_algorithm = openpgp.symmetric.plaintext;
|
||||
this.hash_algorithm = openpgp.hash.sha1;
|
||||
this.s2k = null;
|
||||
this.encrypted = null;
|
||||
this.iv = null;
|
||||
|
||||
|
||||
function get_hash_len(hash) {
|
||||
|
@ -10646,7 +10608,7 @@ function openpgp_packet_secret_key() {
|
|||
var hash = hashfn(cleartext);
|
||||
|
||||
if(hash != hashtext)
|
||||
throw "Hash mismatch!";
|
||||
throw new Error("Hash mismatch.");
|
||||
|
||||
var mpis = openpgp_crypto_getPrivateMpiCount(algorithm);
|
||||
|
||||
|
@ -10660,6 +10622,19 @@ function openpgp_packet_secret_key() {
|
|||
return mpi;
|
||||
}
|
||||
|
||||
function write_cleartext_mpi(hash_algorithm, mpi) {
|
||||
var bytes= '';
|
||||
for(var i in mpi) {
|
||||
bytes += mpi[i].write();
|
||||
}
|
||||
|
||||
|
||||
bytes += get_hash_fn(hash_algorithm)(bytes);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
// 5.5.3. Secret-Key Packet Formats
|
||||
|
||||
/**
|
||||
|
@ -10680,60 +10655,20 @@ function openpgp_packet_secret_key() {
|
|||
// 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.
|
||||
var s2k_usage = bytes[0].charCodeAt();
|
||||
|
||||
var i = 1;
|
||||
var isEncrypted = bytes[0].charCodeAt();
|
||||
|
||||
// - [Optional] If string-to-key usage octet was 255 or 254, a one-
|
||||
// octet symmetric encryption algorithm.
|
||||
if (s2k_usage == 255 || s2k_usage == 254) {
|
||||
this.symmetric_algorithm = bytes[i++].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.
|
||||
this.s2k = new openpgp_type_s2k();
|
||||
i += this.s2k.read(bytes.substr(i));
|
||||
}
|
||||
|
||||
// - [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.
|
||||
if(isEncrypted) {
|
||||
this.encrypted = bytes;
|
||||
} else {
|
||||
|
||||
// - Plain or encrypted multiprecision integers comprising the secret
|
||||
// key data. These algorithm-specific fields are as described
|
||||
// below.
|
||||
|
||||
if (s2k_usage != 0 && s2k_usage != 255 &&
|
||||
s2k_usage != 254) {
|
||||
this.symmetric_algorithm = s2k_usage;
|
||||
}
|
||||
|
||||
if (s2k_usage != 0 && this.s2k.type != 1001) {
|
||||
this.iv = bytes.substr(i,
|
||||
openpgp_crypto_getBlockLength(this.symmetric_algorithm));
|
||||
|
||||
i += this.iv.length;
|
||||
}
|
||||
|
||||
if(s2k_usage == 254)
|
||||
this.hash_algorithm = openpgp.hash.sha1;
|
||||
else
|
||||
this.hash_algorithm = 'checksum';
|
||||
|
||||
// - Plain or encrypted multiprecision integers comprising the secret
|
||||
// key data. These algorithm-specific fields are as described
|
||||
// below.
|
||||
|
||||
// s2k type 1001 corresponds to GPG specific extension without primary key secrets
|
||||
// http://www.gnupg.org/faq/GnuPG-FAQ.html#how-can-i-use-gnupg-in-an-automated-environment
|
||||
if (s2k_usage != 0 && this.s2k.type == 1001) {
|
||||
this.mpi = null;
|
||||
this.encrypted = null;
|
||||
|
||||
} else if (s2k_usage != 0) {
|
||||
this.encrypted = bytes.substr(i);
|
||||
|
||||
} else {
|
||||
this.mpi = parse_cleartext_mpi(this.hash_algorithm, bytes.substr(i),
|
||||
this.mpi = parse_cleartext_mpi('mod', bytes.substr(1),
|
||||
this.public_key.algorithm);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -10742,7 +10677,7 @@ function openpgp_packet_secret_key() {
|
|||
* @param {Integer} keyType Follows the OpenPGP algorithm standard,
|
||||
* IE 1 corresponds to RSA.
|
||||
* @param {RSA.keyObject} key
|
||||
* @param password
|
||||
* @param passphrase
|
||||
* @param s2kHash
|
||||
* @param symmetricEncryptionAlgorithm
|
||||
* @param timePacket
|
||||
|
@ -10752,25 +10687,11 @@ function openpgp_packet_secret_key() {
|
|||
this.write = function() {
|
||||
var bytes = this.public_key.write();
|
||||
|
||||
if(this.encrypted == null) {
|
||||
if(!this.encrypted) {
|
||||
bytes += String.fromCharCode(0);
|
||||
|
||||
var mpi = '';
|
||||
for(var i in this.mpi) {
|
||||
mpi += this.mpi[i].write();
|
||||
}
|
||||
|
||||
bytes += mpi;
|
||||
|
||||
// TODO check the cheksum!
|
||||
bytes += openpgp_packet_number_write(util.calc_checksum(mpi), 2);
|
||||
} else if(this.s2k == null) {
|
||||
bytes += String.fromCharCode(this.symmetric_algorithm);
|
||||
bytes += this.encrypted;
|
||||
bytes += write_cleartext_mpi('mod', this.mpi);
|
||||
} else {
|
||||
bytes += String.fromCharCode(254);
|
||||
bytes += String.fromCharCode(this.symmetric_algorithm);
|
||||
bytes += this.s2k.write();
|
||||
bytes += this.encrypted;
|
||||
}
|
||||
|
||||
|
@ -10780,75 +10701,54 @@ function openpgp_packet_secret_key() {
|
|||
|
||||
|
||||
|
||||
this.encrypt = function(password) {
|
||||
/** Encrypt the payload. By default, we use aes256 and iterated, salted string
|
||||
* to key specifier
|
||||
* @param {String} passphrase
|
||||
*/
|
||||
this.encrypt = function(passphrase) {
|
||||
|
||||
var s2k = new openpgp_type_s2k(),
|
||||
symmetric = openpgp.symmetric.aes256,
|
||||
cleartext = write_cleartext_mpi(openpgp.hash.sha1, this.mpi),
|
||||
key = produceEncryptionKey(s2k, passphrase, symmetric),
|
||||
blockLen = openpgp_crypto_getBlockLength(symmetric),
|
||||
iv = openpgp_crypto_getRandomBytes(blockLen);
|
||||
|
||||
|
||||
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.symmetric_algorithm);
|
||||
//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 cleartext = key.d.toMPI() + key.p.toMPI() + key.q.toMPI() + key.u.toMPI();
|
||||
var sha1Hash = str_sha1(cleartext);
|
||||
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.symmetric_algorithm){
|
||||
case 3:
|
||||
this.iv.length = 8;
|
||||
this.iv = openpgp_crypto_getRandomBytes(this.iv.length);
|
||||
ciphertextMPIs = normal_cfb_encrypt(function(block, key) {
|
||||
var cast5 = new openpgp_symenc_cast5();
|
||||
cast5.setKey(key);
|
||||
return cast5.encrypt(util.str2bin(block));
|
||||
}, this.iv.length, util.str2bin(hashKey.substring(0,16)), cleartext + sha1Hash, this.iv);
|
||||
body += this.iv + ciphertextMPIs;
|
||||
break;
|
||||
case 7:
|
||||
case 8:
|
||||
case 9:
|
||||
this.iv.length = 16;
|
||||
this.iv = openpgp_crypto_getRandomBytes(this.iv.length);
|
||||
ciphertextMPIs = normal_cfb_encrypt(AESencrypt,
|
||||
this.iv.length, hashKey, cleartext + 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};
|
||||
this.encrypted = '';
|
||||
this.encrypted += String.fromCharCode(254);
|
||||
this.encrypted += String.fromCharCode(symmetric);
|
||||
this.encrypted += s2k.write();
|
||||
this.encrypted += iv;
|
||||
|
||||
console.log(cleartext);
|
||||
|
||||
switch(symmetric) {
|
||||
case 3:
|
||||
this.encrypted += normal_cfb_encrypt(function(block, key) {
|
||||
var cast5 = new openpgp_symenc_cast5();
|
||||
cast5.setKey(key);
|
||||
return cast5.encrypt(util.str2bin(block));
|
||||
}, iv.length, key, cleartext, iv);
|
||||
break;
|
||||
case 7:
|
||||
case 8:
|
||||
case 9:
|
||||
var fn = function(block,key) {
|
||||
return AESencrypt(util.str2bin(block),key);
|
||||
}
|
||||
this.encrypted += normal_cfb_encrypt(fn,
|
||||
iv.length, new keyExpansion(key), cleartext, iv);
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unsupported symmetric encryption algorithm.");
|
||||
}
|
||||
}
|
||||
|
||||
function produceEncryptionKey(s2k, passphrase, algorithm) {
|
||||
return s2k.produce_key(passphrase,
|
||||
openpgp_crypto_getKeyLength(algorithm));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts the private key MPIs which are needed to use the key.
|
||||
|
@ -10861,38 +10761,65 @@ function openpgp_packet_secret_key() {
|
|||
* @return {Boolean} True if the passphrase was correct; false if not
|
||||
*/
|
||||
this.decrypt = function(passphrase) {
|
||||
if (this.encrypted == null)
|
||||
if (!this.encrypted)
|
||||
return;
|
||||
|
||||
// creating a key out of the passphrase
|
||||
var key = this.s2k.produce_key(passphrase,
|
||||
openpgp_crypto_getKeyLength(this.symmetric_algorithm));
|
||||
var i = 0,
|
||||
symmetric,
|
||||
key;
|
||||
|
||||
var cleartext = '';
|
||||
var s2k_usage = this.encrypted[i++].charCodeAt();
|
||||
|
||||
// - [Optional] If string-to-key usage octet was 255 or 254, a one-
|
||||
// octet symmetric encryption algorithm.
|
||||
if (s2k_usage == 255 || s2k_usage == 254) {
|
||||
symmetric = this.encrypted[i++].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.
|
||||
var s2k = new openpgp_type_s2k();
|
||||
i += s2k.read(this.encrypted.substr(i));
|
||||
|
||||
key = produceEncryptionKey(s2k, passphrase, symmetric);
|
||||
} else {
|
||||
symmetric = s2k_usage;
|
||||
key = MD5(passphrase);
|
||||
}
|
||||
|
||||
// - [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.
|
||||
var iv = this.encrypted.substr(i,
|
||||
openpgp_crypto_getBlockLength(symmetric));
|
||||
|
||||
i += iv.length;
|
||||
|
||||
var cleartext,
|
||||
ciphertext = this.encrypted.substr(i);
|
||||
|
||||
|
||||
switch (this.symmetric_algorithm) {
|
||||
switch (symmetric) {
|
||||
case 1: // - IDEA [IDEA]
|
||||
util.print_error("openpgp.packet.keymaterial.js\n"
|
||||
+"symmetric encryption algorithim: IDEA is not implemented");
|
||||
throw new Error("IDEA is not implemented.");
|
||||
return false;
|
||||
case 2: // - TripleDES (DES-EDE, [SCHNEIER] [HAC] - 168 bit key derived from 192)
|
||||
cleartext = normal_cfb_decrypt(function(block, key) {
|
||||
return des(key, block,1,null,0);
|
||||
}, this.iv.length, key, this.encrypted, this.iv);
|
||||
}, iv.length, key, ciphertext, iv);
|
||||
break;
|
||||
case 3: // - CAST5 (128 bit key, as per [RFC2144])
|
||||
cleartext = normal_cfb_decrypt(function(block, key) {
|
||||
var cast5 = new openpgp_symenc_cast5();
|
||||
cast5.setKey(key);
|
||||
return cast5.encrypt(util.str2bin(block));
|
||||
}, this.iv.length, util.str2bin(key.substring(0,16)), this.encrypted, this.iv);
|
||||
}, iv.length, util.str2bin(key.substring(0,16)), ciphertext, iv);
|
||||
break;
|
||||
case 4: // - Blowfish (128 bit key, 16 rounds) [BLOWFISH]
|
||||
cleartext = normal_cfb_decrypt(function(block, key) {
|
||||
var blowfish = new Blowfish(key);
|
||||
return blowfish.encrypt(block);
|
||||
}, this.iv.length, key, this.encrypted, this.iv);
|
||||
}, iv.length, key, ciphertext, iv);
|
||||
break;
|
||||
case 7: // - AES with 128-bit key [AES]
|
||||
case 8: // - AES with 192-bit key
|
||||
|
@ -10900,27 +10827,27 @@ function openpgp_packet_secret_key() {
|
|||
cleartext = normal_cfb_decrypt(function(block,key){
|
||||
return AESencrypt(util.str2bin(block),key);
|
||||
},
|
||||
this.iv.length, keyExpansion(key),
|
||||
this.encrypted, this.iv);
|
||||
iv.length, new keyExpansion(key),
|
||||
ciphertext, 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");
|
||||
throw new Error("Twofish is 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.symmetric_algorithm);
|
||||
throw new Error("Unknown symmetric algorithm.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cleartext == null) {
|
||||
util.print_error("openpgp.packet.keymaterial.js\n"+"cleartext was null");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
var hash;
|
||||
if(s2k_usage == 254)
|
||||
hash = openpgp.hash.sha1;
|
||||
else
|
||||
hash = 'mod';
|
||||
|
||||
|
||||
this.mpi = parse_cleartext_mpi(this.hash_algorithm, cleartext,
|
||||
|
||||
this.mpi = parse_cleartext_mpi(hash, cleartext,
|
||||
this.public_key.algorithm);
|
||||
}
|
||||
|
||||
|
@ -11674,21 +11601,6 @@ function openpgp_packet_sym_encrypted_integrity_protected() {
|
|||
else
|
||||
this.packets.read(decrypted.substr(0, decrypted.length - 22));
|
||||
}
|
||||
|
||||
this.toString = function() {
|
||||
var data = '';
|
||||
if(openpgp.config.debug)
|
||||
data = ' data: Bytes ['
|
||||
+ util.hexstrdump(this.encrypted) + ']';
|
||||
|
||||
return '5.13. Sym. Encrypted Integrity Protected Data Packet (Tag 18)\n'
|
||||
+ '\n'
|
||||
+ ' version: '
|
||||
+ this.version
|
||||
+ '\n'
|
||||
+ data;
|
||||
}
|
||||
|
||||
};
|
||||
// GPG4Browsers - An OpenPGP implementation in javascript
|
||||
// Copyright (C) 2011 Recurity Labs GmbH
|
||||
|
@ -11819,21 +11731,6 @@ function openpgp_packet_sym_encrypted_session_key() {
|
|||
openpgp_crypto_getPrefixRandom(this.private_algorithm),
|
||||
this.private_algorithm, key, private_key, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string representation of this object (useful for debug
|
||||
* purposes)
|
||||
*
|
||||
* @return {String} The string containing a openpgp description
|
||||
*/
|
||||
this.toString = function() {
|
||||
return '5.3 Symmetric-Key Encrypted Session Key Packets (Tag 3)\n'
|
||||
+ ' KeyId: ' + this.keyId.toString() + '\n'
|
||||
+ ' length: ' + this.packetLength + '\n'
|
||||
+ ' version:' + this.version + '\n' + ' symKeyA:'
|
||||
+ this.symmetricKeyAlgorithmUsed + '\n' + ' s2k: '
|
||||
+ this.s2k + '\n';
|
||||
}
|
||||
};
|
||||
|
||||
// GPG4Browsers - An OpenPGP implementation in javascript
|
||||
|
@ -11867,6 +11764,7 @@ function openpgp_packet_sym_encrypted_session_key() {
|
|||
function openpgp_packet_symmetrically_encrypted() {
|
||||
this.tag = 9;
|
||||
this.encrypted = null;
|
||||
/** Decrypted packets contained within. */
|
||||
this.packets = new openpgp_packetlist();
|
||||
|
||||
|
||||
|
@ -11902,14 +11800,6 @@ function openpgp_packet_symmetrically_encrypted() {
|
|||
this.encrypted = openpgp_crypto_symmetricEncrypt(
|
||||
openpgp_crypto_getPrefixRandom(algo), algo, key, data, true);
|
||||
}
|
||||
|
||||
|
||||
this.toString = function () {
|
||||
return '5.7. Symmetrically Encrypted Data Packet (Tag 9)\n'
|
||||
+ ' Used symmetric algorithm: ' + this.algorithmType + '\n'
|
||||
+ ' encrypted data: Bytes ['
|
||||
+ util.hexstrdump(this.encryptedData) + ']\n';
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -12400,7 +12290,7 @@ function openpgp_type_s2k() {
|
|||
this.algorithm = openpgp.hash.sha256;
|
||||
/** @type {openpgp_type_s2k.type} */
|
||||
this.type = openpgp_type_s2k.type.iterated;
|
||||
this.c = 10;
|
||||
this.c = 96;
|
||||
/** @type {openpgp_bytearray}
|
||||
* Eight bytes of salt. */
|
||||
this.salt = openpgp_crypto_getRandomBytes(8);
|
||||
|
|
45
resources/openpgp.min.js
vendored
45
resources/openpgp.min.js
vendored
|
@ -288,7 +288,7 @@ JXG.Util.genUUID=function(){for(var a="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcde
|
|||
function openpgp_config(){this.config=null;this.default_config={prefer_hash_algorithm:8,encryption_cipher:9,compression:1,show_version:!0,show_comment:!0,integrity_protect:!0,composition_behavior:0,keyserver:"keyserver.linux.it"};this.versionstring="OpenPGP.js v.1.20130509";this.commentstring="http://openpgpjs.org";this.debug=!1;this.read=function(){var a=JSON.parse(window.localStorage.getItem("config"));null==a?(this.config=this.default_config,this.write()):this.config=a};this.write=function(){window.localStorage.setItem("config",
|
||||
JSON.stringify(this.config))}}var b64s="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";function s2r(a){var b,c,d,e="",f=0,h=0,g=a.length;for(d=0;d<g;d++)c=a.charCodeAt(d),0==h?(e+=b64s.charAt(c>>2&63),b=(c&3)<<4):1==h?(e+=b64s.charAt(b|c>>4&15),b=(c&15)<<2):2==h&&(e+=b64s.charAt(b|c>>6&3),f+=1,0==f%60&&(e+="\n"),e+=b64s.charAt(c&63)),f+=1,0==f%60&&(e+="\n"),h+=1,3==h&&(h=0);0<h&&(e+=b64s.charAt(b),f+=1,0==f%60&&(e+="\n"),e+="=",f+=1);1==h&&(0==f%60&&(e+="\n"),e+="=");return e}
|
||||
function r2s(a){var b,c,d="",e=0,f=0,h=a.length;for(c=0;c<h;c++)b=b64s.indexOf(a.charAt(c)),0<=b&&(e&&(d+=String.fromCharCode(f|b>>6-e&255)),e=e+2&7,f=b<<e&255);return d}
|
||||
function openpgp_encoding_deArmor(a){var a=a.replace(/\r/g,""),b=openpgp_encoding_get_type(a);if(2!=b){a=a.split("-----");b={openpgp:openpgp_encoding_base64_decode(a[2].split("\n\n")[1].split("\n=")[0].replace(/\n- /g,"\n")),type:b};if(verifyCheckSum(b.openpgp,a[2].split("\n\n")[1].split("\n=")[1].split("\n")[0]))return b;util.print_error("Ascii armor integrity check on message failed: '"+a[2].split("\n\n")[1].split("\n=")[1].split("\n")[0]+"' should be '"+getCheckSum(b));return!1}a=a.split("-----");
|
||||
function openpgp_encoding_dearmor(a){var a=a.replace(/\r/g,""),b=openpgp_encoding_get_type(a);if(2!=b){a=a.split("-----");b={openpgp:openpgp_encoding_base64_decode(a[2].split("\n\n")[1].split("\n=")[0].replace(/\n- /g,"\n")),type:b};if(verifyCheckSum(b.openpgp,a[2].split("\n\n")[1].split("\n=")[1].split("\n")[0]))return b;util.print_error("Ascii armor integrity check on message failed: '"+a[2].split("\n\n")[1].split("\n=")[1].split("\n")[0]+"' should be '"+getCheckSum(b));return!1}a=a.split("-----");
|
||||
b={text:a[2].replace(/\n- /g,"\n").split("\n\n")[1],openpgp:openpgp_encoding_base64_decode(a[4].split("\n\n")[1].split("\n=")[0]),type:b};if(verifyCheckSum(b.openpgp,a[4].split("\n\n")[1].split("\n=")[1]))return b;util.print_error("Ascii armor integrity check on message failed");return!1}
|
||||
function openpgp_encoding_get_type(a){a=a.split("-----");if(a[1].match(/BEGIN PGP MESSAGE, PART \d+\/\d+/))return 0;if(a[1].match(/BEGIN PGP MESSAGE, PART \d+/))return 1;if(a[1].match(/BEGIN PGP SIGNED MESSAGE/))return 2;if(a[1].match(/BEGIN PGP MESSAGE/))return 3;if(a[1].match(/BEGIN PGP PUBLIC KEY BLOCK/))return 4;if(a[1].match(/BEGIN PGP PRIVATE KEY BLOCK/))return 5}
|
||||
function openpgp_encoding_armor_addheader(){var a="";openpgp.config.config.show_version&&(a+="Version: "+openpgp.config.versionstring+"\r\n");openpgp.config.config.show_comment&&(a+="Comment: "+openpgp.config.commentstring+"\r\n");return a+"\r\n"}
|
||||
|
@ -355,7 +355,7 @@ b&&4!=this.userIds[a].certificationSignatures[c].verifyBasic(this.publicKeyPacke
|
|||
function openpgp_packet_compressed(){this.tag=8;this.packets=new openpgp_packetlist;this.algorithm=openpgp.compression.uncompressed;this.compressed=null;this.read=function(a){this.algorithm=a.charCodeAt(0);this.compressed=a.substr(1);this.decompress()};this.write=function(){null==this.compressed&&this.compress();return String.fromCharCode(this.type)+this.compressed};this.decompress=function(){var a;switch(this.algorithm){case openpgp.compression.uncompressed:a=this.compressed;break;case openpgp.compression.zip:util.print_info("Decompressed packet [Type 1-ZIP]: "+
|
||||
this.toString());a=this.compressed;a=s2r(a).replace(/\n/g,"");a=new JXG.Util.Unzip(JXG.Util.Base64.decodeAsArray(a));a=unescape(a.deflate()[0][0]);break;case openpgp.compression.zlib:util.print_info("Decompressed packet [Type 2-ZLIB]: "+this.toString());if(8==this.compressed.charCodeAt(0)%16){a=this.compressed.substring(0,this.compressed.length-4);a=s2r(a).replace(/\n/g,"");a=JXG.decompress(a);break}else util.print_error("Compression algorithm ZLIB only supports DEFLATE compression method.");break;
|
||||
case openpgp.compression.bzip2:util.print_error("Compression algorithm BZip2 [BZ2] is not implemented.");break;default:util.print_error("Compression algorithm unknown :"+this.type)}util.print_debug("decompressed:"+util.hexstrdump(a));this.packets.read(a)};this.compress=function(){switch(this.type){case openpgp.compression.uncompressed:this.compressed=this.packets.write();break;case openpgp.compression.zip:util.print_error("Compression algorithm ZIP [RFC1951] is not implemented.");break;case openpgp.compression.zlib:util.print_error("Compression algorithm ZLIB [RFC1950] is not implemented.");
|
||||
break;case openpgp.compression.bzip2:util.print_error("Compression algorithm BZip2 [BZ2] is not implemented.");break;default:util.print_error("Compression algorithm unknown :"+this.type)}};this.toString=function(){return"5.6. Compressed Data Packet (Tag 8)\n Compression Algorithm = "+this.algorithm+"\n Compressed Data: Byte ["+util.hexstrdump(this.compressed)+"]\n"}}
|
||||
break;case openpgp.compression.bzip2:util.print_error("Compression algorithm BZip2 [BZ2] is not implemented.");break;default:util.print_error("Compression algorithm unknown :"+this.type)}}}
|
||||
function openpgp_packet_literal(){this.tag=11;this.format=openpgp_packet_literal.format.utf8;this.data="";this.date=new Date;this.set_data=function(a,b){this.format=b;this.data=a};this.set_data_bytes=function(a,b){this.format=b;b==openpgp_packet_literal.format.utf8&&(a=util.decode_utf8(a));this.data=a};this.get_data_bytes=function(){return this.format==openpgp_packet_literal.format.utf8?util.encode_utf8(this.data):this.data};this.read=function(a){var b=a[0],c=a.charCodeAt(1);this.filename=util.decode_utf8(a.substr(2,
|
||||
c));this.date=openpgp_packet_time_read(a.substr(2+c,4));this.set_data_bytes(a.substring(6+c),b)};this.write=function(){var a=util.encode_utf8("msg.txt"),b=this.get_data_bytes(),c;c=""+this.format;c+=String.fromCharCode(a.length);c=c+a+openpgp_packet_time_write(this.date);return c+b};this.toString=function(){return"5.9. Literal Data Packet (Tag 11)\n length: "+this.packetLength+"\n format: "+this.format+"\n filename:"+this.filename+"\n date: "+this.date+"\n data: |"+this.data+"|\n rdata: |"+
|
||||
this.real_data+"|\n"}}openpgp_packet_literal.format={binary:"b",text:"t",utf8:"u"};function openpgp_packet_marker(){this.tag=10;this.read=function(a){return 80==a[0].charCodeAt()&&71==a[1].charCodeAt()&&80==a[2].charCodeAt()?!0:!1}}
|
||||
|
@ -372,21 +372,17 @@ function openpgp_packetlist(){this.length=0;this.read=function(a){for(var b=0;b<
|
|||
function openpgp_packet_public_key_encrypted_session_key(){this.tag=1;this.version=3;this.public_key_id=new openpgp_type_keyid;this.public_key_algorithm=openpgp.publickey.rsa_encrypt_sign;this.symmetric_key=null;this.symmetric_algorithm=openpgp.symmetric.plaintext;this.encrypted=[];this.read=function(a){if(10>a.length)return util.print_error("openpgp.packet.encryptedsessionkey.js\ninvalid length"),null;this.version=a[0].charCodeAt();this.public_key_id.read_packet(a,1);this.public_key_algorithm=a[9].charCodeAt();
|
||||
var b=10;switch(this.public_key_algorithm){case openpgp.publickey.rsa_encrypt:case openpgp.publickey.rsa_encrypt_sign:this.encrypted=[];this.encrypted[0]=new openpgp_type_mpi;this.encrypted[0].read(a.substr(b));break;case openpgp.publickey.elgamal:this.encrypted=[];this.encrypted[0]=new openpgp_type_mpi;b+=this.encrypted[0].read(a.substr(b));this.encrypted[1]=new openpgp_type_mpi;this.encrypted[1].read(a.substr(b));break;default:util.print_error("openpgp.packet.encryptedsessionkey.js\nunknown public key packet algorithm type "+
|
||||
this.public_key_algorithm)}};this.write=function(){for(var a=String.fromCharCode(this.version),a=a+this.public_key_id.bytes,a=a+String.fromCharCode(this.public_key_algorithm),b=0;b<this.encrypted.length;b++)a+=this.encrypted[b].write();return a};this.encrypt=function(a){var b=String.fromCharCode(this.symmetric_algorithm),b=b+this.symmetric_key,c=util.calc_checksum(this.symmetric_key),b=b+String.fromCharCode(c>>8&255),b=b+String.fromCharCode(c&255),c=new openpgp_type_mpi;c.fromBytes(openpgp_encoding_eme_pkcs1_encode(b,
|
||||
a[0].byteLength()));this.encrypted=openpgp_crypto_asymetricEncrypt(this.public_key_algorithm,a,c)};this.decrypt=function(a,b){var c=openpgp_crypto_asymetricDecrypt(this.public_key_algorithm,a,b,this.encrypted).toBytes(),d=(c.charCodeAt(c.length-2)<<8)+c.charCodeAt(c.length-1),c=openpgp_encoding_eme_pkcs1_decode(c,a[0].byteLength()),e=c.substring(1,c.length-2);d!=util.calc_checksum(e)?util.print_error("Checksum mismatch"):(this.symmetric_key=e,this.symmetric_algorithm=c.charCodeAt(0))};this.toString=
|
||||
function(){for(var a="5.1. Public-Key Encrypted Session Key Packets (Tag 1)\n KeyId: "+this.keyId.toString()+"\n length: "+this.packetLength+"\n version:"+this.version+"\n pubAlgUs:"+this.publicKeyAlgorithmUsed+"\n",b=0;b<this.encrypted.length;b++)a+=this.encrypted[b].toString();return a}}
|
||||
function openpgp_packet_public_key(){this.tag=6;this.version=4;this.created=null;this.mpi=[];this.algorithm=openpgp.publickey.rsa_sign;this.read=function(a){this.version=a[0].charCodeAt();if(3!=this.version){if(4==this.version){this.created=openpgp_packet_time_read(a.substr(1,4));this.algorithm=a[5].charCodeAt();var b=0<this.algorithm&&4>this.algorithm?2:16==this.algorithm?3:17==this.algorithm?4:0;this.mpi=[];for(var a=a.substr(6),c=0,d=0;d<b&&c<a.length;d++)this.mpi[d]=new openpgp_type_mpi,c+=this.mpi[d].read(a.substr(c)),
|
||||
c>a.length&&util.print_error("openpgp.packet.keymaterial.js\nerror reading MPI @:"+c);return c+6}util.print_error("Unknown packet version")}};this.write=function(){var a=String.fromCharCode(4),a=a+openpgp_packet_time_write(this.created),a=a+String.fromCharCode(this.algorithm),b;for(b in this.mpi)a+=this.mpi[b].write();return a}}function openpgp_packet_public_subkey(){openpgp_packet_public_key.call(this);this.tag=14}
|
||||
function openpgp_packet_secret_key(){function a(a){return a==openpgp.hash.sha1?str_sha1:function(a){return openpgp_packet_number_write(util.calc_checksum(a),2)}}function b(b,d,e){var f=b==openpgp.hash.sha1?20:2,b=a(b),h=d.substr(d.length-f),d=d.substr(0,d.length-f);if(b(d)!=h)throw"Hash mismatch!";e=openpgp_crypto_getPrivateMpiCount(e);f=0;b=[];for(h=0;h<e&&f<d.length;h++)b[h]=new openpgp_type_mpi,f+=b[h].read(d.substr(f));return b}this.tag=5;this.public_key=new openpgp_packet_public_key;this.mpi=
|
||||
[];this.symmetric_algorithm=openpgp.symmetric.plaintext;this.hash_algorithm=openpgp.hash.sha1;this.iv=this.encrypted=this.s2k=null;this.read=function(a){var d=this.public_key.read(a),a=a.substr(d),d=a[0].charCodeAt(),e=1;if(255==d||254==d)this.symmetric_algorithm=a[e++].charCodeAt(),this.s2k=new openpgp_type_s2k,e+=this.s2k.read(a.substr(e));if(0!=d&&255!=d&&254!=d)this.symmetric_algorithm=d;if(0!=d&&1001!=this.s2k.type)this.iv=a.substr(e,openpgp_crypto_getBlockLength(this.symmetric_algorithm)),e+=
|
||||
this.iv.length;this.hash_algorithm=254==d?openpgp.hash.sha1:"checksum";0!=d&&1001==this.s2k.type?this.encrypted=this.mpi=null:0!=d?this.encrypted=a.substr(e):this.mpi=b(this.hash_algorithm,a.substr(e),this.public_key.algorithm)};this.write=function(){var a=this.public_key.write();if(null==this.encrypted){var a=a+String.fromCharCode(0),b="",e;for(e in this.mpi)b+=this.mpi[e].write();a=a+b+openpgp_packet_number_write(util.calc_checksum(b),2)}else null==this.s2k?a+=String.fromCharCode(this.symmetric_algorithm):
|
||||
(a+=String.fromCharCode(254),a+=String.fromCharCode(this.symmetric_algorithm),a+=this.s2k.write()),a+=this.encrypted;return a};this.encrypt=function(a){switch(keyType){case 1:body+=String.fromCharCode(keyType);body+=key.n.toMPI();body+=key.ee.toMPI();if(a){body+=String.fromCharCode(254);body+=String.fromCharCode(this.symmetric_algorithm);body+=String.fromCharCode(3);body+=String.fromCharCode(s2kHash);var b=key.d.toMPI()+key.p.toMPI()+key.q.toMPI()+key.u.toMPI(),e=str_sha1(b);util.print_debug_hexstr_dump("write_private_key sha1: ",
|
||||
e);var f=openpgp_crypto_getRandomBytes(8);util.print_debug_hexstr_dump("write_private_key Salt: ",f);body+=f;body+=String.fromCharCode(96);util.print_debug("write_private_key c: 96");a=(new openpgp_type_s2k).write(3,s2kHash,a,f,96);switch(this.symmetric_algorithm){case 3:this.iv.length=8;this.iv=openpgp_crypto_getRandomBytes(this.iv.length);ciphertextMPIs=normal_cfb_encrypt(function(a,b){var c=new openpgp_symenc_cast5;c.setKey(b);return c.encrypt(util.str2bin(a))},this.iv.length,util.str2bin(a.substring(0,
|
||||
16)),b+e,this.iv);body+=this.iv+ciphertextMPIs;break;case 7:case 8:case 9:this.iv.length=16,this.iv=openpgp_crypto_getRandomBytes(this.iv.length),ciphertextMPIs=normal_cfb_encrypt(AESencrypt,this.iv.length,a,b+e,this.iv),body+=this.iv+ciphertextMPIs}}else body+=String.fromCharCode(0),body+=key.d.toMPI()+key.p.toMPI()+key.q.toMPI()+key.u.toMPI(),b=util.calc_checksum(key.d.toMPI()+key.p.toMPI()+key.q.toMPI()+key.u.toMPI()),body+=String.fromCharCode(b/256)+String.fromCharCode(b%256),util.print_debug_hexstr_dump("write_private_key basic checksum: "+
|
||||
b);break;default:body="",util.print_error("openpgp.packet.keymaterial.js\nerror writing private key, unknown type :"+keyType)}b=openpgp_packet.write_packet_header(tag,body.length);return{string:b+body,header:b,body:body}};this.decrypt=function(a){if(null!=this.encrypted){var a=this.s2k.produce_key(a,openpgp_crypto_getKeyLength(this.symmetric_algorithm)),d="";switch(this.symmetric_algorithm){case 1:return util.print_error("openpgp.packet.keymaterial.js\nsymmetric encryption algorithim: IDEA is not implemented"),
|
||||
!1;case 2:d=normal_cfb_decrypt(function(a,b){return des(b,a,1,null,0)},this.iv.length,a,this.encrypted,this.iv);break;case 3:d=normal_cfb_decrypt(function(a,b){var c=new openpgp_symenc_cast5;c.setKey(b);return c.encrypt(util.str2bin(a))},this.iv.length,util.str2bin(a.substring(0,16)),this.encrypted,this.iv);break;case 4:d=normal_cfb_decrypt(function(a,b){return(new Blowfish(b)).encrypt(a)},this.iv.length,a,this.encrypted,this.iv);break;case 7:case 8:case 9:d=normal_cfb_decrypt(function(a,b){return AESencrypt(util.str2bin(a),
|
||||
b)},this.iv.length,keyExpansion(a),this.encrypted,this.iv);break;case 10:return util.print_error("openpgp.packet.keymaterial.js\nKey material is encrypted with twofish: not implemented"),!1;default:return util.print_error("openpgp.packet.keymaterial.js\nunknown encryption algorithm for secret key :"+this.symmetric_algorithm),!1}if(null==d)return util.print_error("openpgp.packet.keymaterial.js\ncleartext was null"),!1;this.mpi=b(this.hash_algorithm,d,this.public_key.algorithm)}};this.getKeyId=function(){if(4==
|
||||
this.version)return this.getFingerprint().substring(12,20);if(3==this.version&&0<this.publicKeyAlgorithm&&4>this.publicKeyAlgorithm){var a=this.MPIs[0].substring(this.MPIs[0].mpiByteLength-8);util.print_debug("openpgp.msg.publickey read_nodes:\nV3 key ID: "+a);return a}};this.getFingerprint=function(){if(4==this.version)return tohash=String.fromCharCode(153)+String.fromCharCode(this.packetdata.length>>8&255)+String.fromCharCode(this.packetdata.length&255)+this.packetdata,util.print_debug("openpgp.msg.publickey creating subkey fingerprint by hashing:"+
|
||||
util.hexstrdump(tohash)+"\npublickeyalgorithm: "+this.publicKeyAlgorithm),str_sha1(tohash,tohash.length);if(3==this.version&&0<this.publicKeyAlgorithm&&4>this.publicKeyAlgorithm)return MD5(this.MPIs[0].MPI)}}function openpgp_packet_secret_subkey(){openpgp_packet_secret_key.call(this);this.tag=7}
|
||||
a[0].byteLength()));this.encrypted=openpgp_crypto_asymetricEncrypt(this.public_key_algorithm,a,c)};this.decrypt=function(a,b){var c=openpgp_crypto_asymetricDecrypt(this.public_key_algorithm,a,b,this.encrypted).toBytes(),d=(c.charCodeAt(c.length-2)<<8)+c.charCodeAt(c.length-1),c=openpgp_encoding_eme_pkcs1_decode(c,a[0].byteLength()),e=c.substring(1,c.length-2);d!=util.calc_checksum(e)?util.print_error("Checksum mismatch"):(this.symmetric_key=e,this.symmetric_algorithm=c.charCodeAt(0))}}
|
||||
function openpgp_packet_public_key(){this.tag=6;this.version=4;this.created=new Date;this.mpi=[];this.algorithm=openpgp.publickey.rsa_sign;this.read=function(a){this.version=a[0].charCodeAt();if(3!=this.version){if(4==this.version){this.created=openpgp_packet_time_read(a.substr(1,4));this.algorithm=a[5].charCodeAt();var b=0<this.algorithm&&4>this.algorithm?2:16==this.algorithm?3:17==this.algorithm?4:0;this.mpi=[];for(var a=a.substr(6),c=0,d=0;d<b&&c<a.length;d++)this.mpi[d]=new openpgp_type_mpi,c+=
|
||||
this.mpi[d].read(a.substr(c)),c>a.length&&util.print_error("openpgp.packet.keymaterial.js\nerror reading MPI @:"+c);return c+6}util.print_error("Unknown packet version")}};this.write=function(){var a=String.fromCharCode(4),a=a+openpgp_packet_time_write(this.created),a=a+String.fromCharCode(this.algorithm),b;for(b in this.mpi)a+=this.mpi[b].write();return a}}function openpgp_packet_public_subkey(){openpgp_packet_public_key.call(this);this.tag=14}
|
||||
function openpgp_packet_secret_key(){function a(a){return a==openpgp.hash.sha1?str_sha1:function(a){return openpgp_packet_number_write(util.calc_checksum(a),2)}}function b(b,c,f){var h=b==openpgp.hash.sha1?20:2,b=a(b),g=c.substr(c.length-h),c=c.substr(0,c.length-h);if(b(c)!=g)throw Error("Hash mismatch.");f=openpgp_crypto_getPrivateMpiCount(f);h=0;b=[];for(g=0;g<f&&h<c.length;g++)b[g]=new openpgp_type_mpi,h+=b[g].read(c.substr(h));return b}function c(b,c){var f="",h;for(h in c)f+=c[h].write();return f+=
|
||||
a(b)(f)}this.tag=5;this.public_key=new openpgp_packet_public_key;this.mpi=[];this.encrypted=null;this.read=function(a){var c=this.public_key.read(a),a=a.substr(c);a[0].charCodeAt()?this.encrypted=a:this.mpi=b("mod",a.substr(1),this.public_key.algorithm)};this.write=function(){var a=this.public_key.write();this.encrypted?a+=this.encrypted:(a+=String.fromCharCode(0),a+=c("mod",this.mpi));return a};this.encrypt=function(a){var b=new openpgp_type_s2k,f=openpgp.symmetric.aes256,h=c(openpgp.hash.sha1,this.mpi),
|
||||
a=b.produce_key(a,openpgp_crypto_getKeyLength(f)),g=openpgp_crypto_getBlockLength(f),g=openpgp_crypto_getRandomBytes(g);this.encrypted="";this.encrypted+=String.fromCharCode(254);this.encrypted+=String.fromCharCode(f);this.encrypted+=b.write();this.encrypted+=g;console.log(h);switch(f){case 3:this.encrypted+=normal_cfb_encrypt(function(a,b){var c=new openpgp_symenc_cast5;c.setKey(b);return c.encrypt(util.str2bin(a))},g.length,a,h,g);break;case 7:case 8:case 9:this.encrypted+=normal_cfb_encrypt(function(a,
|
||||
b){return AESencrypt(util.str2bin(a),b)},g.length,new keyExpansion(a),h,g);break;default:throw Error("Unsupported symmetric encryption algorithm.");}};this.decrypt=function(a){if(this.encrypted){var c=0,f,h=this.encrypted[c++].charCodeAt();if(255==h||254==h){f=this.encrypted[c++].charCodeAt();var g=new openpgp_type_s2k,c=c+g.read(this.encrypted.substr(c)),a=g.produce_key(a,openpgp_crypto_getKeyLength(f))}else f=h,a=MD5(a);g=this.encrypted.substr(c,openpgp_crypto_getBlockLength(f));c+=g.length;c=this.encrypted.substr(c);
|
||||
switch(f){case 1:throw Error("IDEA is not implemented.");case 2:f=normal_cfb_decrypt(function(a,b){return des(b,a,1,null,0)},g.length,a,c,g);break;case 3:f=normal_cfb_decrypt(function(a,b){var c=new openpgp_symenc_cast5;c.setKey(b);return c.encrypt(util.str2bin(a))},g.length,util.str2bin(a.substring(0,16)),c,g);break;case 4:f=normal_cfb_decrypt(function(a,b){return(new Blowfish(b)).encrypt(a)},g.length,a,c,g);break;case 7:case 8:case 9:f=normal_cfb_decrypt(function(a,b){return AESencrypt(util.str2bin(a),
|
||||
b)},g.length,new keyExpansion(a),c,g);break;case 10:throw Error("Twofish is not implemented.");default:throw Error("Unknown symmetric algorithm.");}this.mpi=b(254==h?openpgp.hash.sha1:"mod",f,this.public_key.algorithm)}};this.getKeyId=function(){if(4==this.version)return this.getFingerprint().substring(12,20);if(3==this.version&&0<this.publicKeyAlgorithm&&4>this.publicKeyAlgorithm){var a=this.MPIs[0].substring(this.MPIs[0].mpiByteLength-8);util.print_debug("openpgp.msg.publickey read_nodes:\nV3 key ID: "+
|
||||
a);return a}};this.getFingerprint=function(){if(4==this.version)return tohash=String.fromCharCode(153)+String.fromCharCode(this.packetdata.length>>8&255)+String.fromCharCode(this.packetdata.length&255)+this.packetdata,util.print_debug("openpgp.msg.publickey creating subkey fingerprint by hashing:"+util.hexstrdump(tohash)+"\npublickeyalgorithm: "+this.publicKeyAlgorithm),str_sha1(tohash,tohash.length);if(3==this.version&&0<this.publicKeyAlgorithm&&4>this.publicKeyAlgorithm)return MD5(this.MPIs[0].MPI)}}
|
||||
function openpgp_packet_secret_subkey(){openpgp_packet_secret_key.call(this);this.tag=7}
|
||||
function openpgp_packet_signature(){this.tag=2;this.issuerKeyId=this.revocationKeyFingerprint=this.revocationKeyAlgorithm=this.revocationKeyClass=this.preferredSymmetricAlgorithms=this.keyNeverExpires=this.keyExpirationTime=this.revocable=this.regularExpression=this.trustAmount=this.trustLevel=this.exportable=this.hashAlgorithm=this.publicKeyAlgorithm=this.mpi=this.signedHashValue=this.signatureNeverExpires=this.signatureExpirationTime=this.signatureData=this.created=this.signatureType=null;this.notation=
|
||||
{};this.embeddedSignature=this.signatureTargetHash=this.signatureTargetHashAlgorithm=this.signatureTargetPublicKeyAlgorithm=this.reasonForRevocationString=this.reasonForRevocationFlag=this.signersUserId=this.keyFlags=this.policyURI=this.isPrimaryUserID=this.preferredKeyServer=this.keyServerPreferences=this.preferredCompressionAlgorithms=this.preferredHashAlgorithms=null;this.verified=!1;this.read=function(a){var b=0;this.version=a[b++].charCodeAt();switch(this.version){case 3:5!=a[b++].charCodeAt()&&
|
||||
util.print_debug("openpgp.packet.signature.js\ninvalid One-octet length of following hashed material.MUST be 5. @:"+(b-1));this.signatureType=a[b++].charCodeAt();this.created=openpgp_packet_time_read(a.substr(b,4));b+=4;this.signatureData=a.substring(position,b);this.issuerKeyId=a.substring(b,b+8);b+=8;this.publicKeyAlgorithm=a[b++].charCodeAt();this.hashAlgorithm=a[b++].charCodeAt();break;case 4:this.signatureType=a[b++].charCodeAt();this.publicKeyAlgorithm=a[b++].charCodeAt();this.hashAlgorithm=
|
||||
|
@ -402,23 +398,20 @@ b){var c=this.toSign(this.signatureType,b),d;d=""+String.fromCharCode(this.versi
|
|||
openpgp_packet_signature.type={binary:0,text:1,standalone:2,cert_generic:16,cert_persona:17,cert_casual:18,cert_positive:19,cert_revocation:48,subkey_binding:24,key_binding:25,key:31,key_revocation:32,subkey_revocation:40,timestamp:64,third_party:80};
|
||||
function openpgp_packet_sym_encrypted_integrity_protected(){this.tag=18;this.version=1;this.encrypted=null;this.modification=!1;this.packets=new openpgp_packetlist;this.read=function(a){this.version=a[0].charCodeAt();if(1!=this.version)return util.print_error("openpgp.packet.encryptedintegrityprotecteddata.js\nunknown encrypted integrity protected data packet version: "+this.version+"hex:"+util.hexstrdump(a)),null;this.encrypted=a.substr(1)};this.write=function(){return String.fromCharCode(this.version)+
|
||||
this.encrypted};this.encrypt=function(a,b){var c=this.packets.write(),d=openpgp_crypto_getPrefixRandom(a),e=d+d.charAt(d.length-2)+d.charAt(d.length-1),c=c+String.fromCharCode(211),c=c+String.fromCharCode(20);util.print_debug_hexstr_dump("data to be hashed:",e+c);c+=str_sha1(e+c);util.print_debug_hexstr_dump("hash:",c.substring(c.length-20,c.length));this.encrypted=openpgp_crypto_symmetricEncrypt(d,a,b,c,!1).substring(0,e.length+c.length)};this.decrypt=function(a,b){var c=openpgp_crypto_symmetricDecrypt(a,
|
||||
b,this.encrypted,!1);this.hash=str_sha1(openpgp_crypto_MDCSystemBytes(a,b,this.encrypted)+c.substring(0,c.length-20));util.print_debug_hexstr_dump("calc hash = ",this.hash);this.hash!=c.substr(c.length-20,20)?(this.packets=new openpgp_packetlist,util.print_error("Decryption stopped: discovered a modification of encrypted data.")):this.packets.read(c.substr(0,c.length-22))};this.toString=function(){var a="";openpgp.config.debug&&(a=" data: Bytes ["+util.hexstrdump(this.encrypted)+"]");return"5.13. Sym. Encrypted Integrity Protected Data Packet (Tag 18)\n\n version: "+
|
||||
this.version+"\n"+a}}
|
||||
b,this.encrypted,!1);this.hash=str_sha1(openpgp_crypto_MDCSystemBytes(a,b,this.encrypted)+c.substring(0,c.length-20));util.print_debug_hexstr_dump("calc hash = ",this.hash);this.hash!=c.substr(c.length-20,20)?(this.packets=new openpgp_packetlist,util.print_error("Decryption stopped: discovered a modification of encrypted data.")):this.packets.read(c.substr(0,c.length-22))}}
|
||||
function openpgp_packet_sym_encrypted_session_key(){this.tag=3;this.private_algorithm=null;this.algorithm=openpgp.symmetric.aes256;this.encrypted=null;this.s2k=new openpgp_type_s2k;this.read=function(a){this.version=a[0].charCodeAt();var b=a[1].charCodeAt(),c=this.s2k.read(a.substr(2))+2;c<a.length?(this.encrypted=a.substr(c),this.private_algorithm=b):this.algorithm=b};this.write=function(){var a=null==this.encrypted?this.algorithm:this.private_algorithm,a=String.fromCharCode(this.version)+String.fromCharCode(a)+
|
||||
this.s2k.write();null!=this.encrypted&&(a+=this.encrypted);return a};this.decrypt=function(a){var b=openpgp_crypto_getKeyLength(null!=this.private_algorithm?this.private_algorithm:this.algorithm),a=this.s2k.produce_key(a,b);null==this.encrypted?this.key=a:(a=openpgp_crypto_symmetricDecrypt(this.private_algorithm,a,this.encrypted,!0),this.algorithm=a[0].keyCodeAt(),this.key=a.substr(1))};this.encrypt=function(a){var b=openpgp_crypto_getKeyLength(this.private_algorithm),a=this.s2k.produce_key(a,b),
|
||||
b=String.fromCharCode(this.algorithm)+openpgp_crypto_getRandomBytes(openpgp_crypto_getKeyLength(this.algorithm));this.encrypted=openpgp_crypto_symmetricEncrypt(openpgp_crypto_getPrefixRandom(this.private_algorithm),this.private_algorithm,a,b,!0)};this.toString=function(){return"5.3 Symmetric-Key Encrypted Session Key Packets (Tag 3)\n KeyId: "+this.keyId.toString()+"\n length: "+this.packetLength+"\n version:"+this.version+"\n symKeyA:"+this.symmetricKeyAlgorithmUsed+"\n s2k: "+
|
||||
this.s2k+"\n"}}
|
||||
function openpgp_packet_symmetrically_encrypted(){this.tag=9;this.encrypted=null;this.packets=new openpgp_packetlist;this.read=function(a){this.encrypted=a};this.write=function(){return this.encrypted};this.decrypt=function(a,b){this.packets.read(openpgp_crypto_symmetricDecrypt(a,b,this.encrypted,!0))};this.encrypt=function(a,b){var c=this.packets.write();this.encrypted=openpgp_crypto_symmetricEncrypt(openpgp_crypto_getPrefixRandom(a),a,b,c,!0)};this.toString=function(){return"5.7. Symmetrically Encrypted Data Packet (Tag 9)\n Used symmetric algorithm: "+this.algorithmType+
|
||||
"\n encrypted data: Bytes ["+util.hexstrdump(this.encryptedData)+"]\n"}}function openpgp_packet_number_read(a){for(var b=0,c=0;c<a.length;c++)b<<=8,b+=a[c].charCodeAt();return b}function openpgp_packet_number_write(a,b){for(var c="",d=0;d<b;d++)c+=String.fromCharCode(a>>8*(b-d-1)&255);return c}function openpgp_packet_time_read(a){var a=openpgp_packet_number_read(a),b=new Date;b.setTime(1E3*a);return b}
|
||||
function openpgp_packet_time_write(a){a=Math.round(a.getTime()/1E3);return openpgp_packet_number_write(a,4)}function openpgp_packet_user_attribute(){this.tag=17;this.attributes=[];this.read=function(a){for(var b=0;b<a.length;){var c=openpgp_packet.read_simple_length(a),b=b+c.offset;this.attributes.push(a.substr(b,c.len));b+=c.len}}}
|
||||
function openpgp_packet_userid(){this.userid="";this.tag=13;this.read=function(a){this.userid=util.decode_utf8(a)};this.write=function(){return util.encode_utf8(this.userid)}}
|
||||
b=String.fromCharCode(this.algorithm)+openpgp_crypto_getRandomBytes(openpgp_crypto_getKeyLength(this.algorithm));this.encrypted=openpgp_crypto_symmetricEncrypt(openpgp_crypto_getPrefixRandom(this.private_algorithm),this.private_algorithm,a,b,!0)}}
|
||||
function openpgp_packet_symmetrically_encrypted(){this.tag=9;this.encrypted=null;this.packets=new openpgp_packetlist;this.read=function(a){this.encrypted=a};this.write=function(){return this.encrypted};this.decrypt=function(a,b){this.packets.read(openpgp_crypto_symmetricDecrypt(a,b,this.encrypted,!0))};this.encrypt=function(a,b){var c=this.packets.write();this.encrypted=openpgp_crypto_symmetricEncrypt(openpgp_crypto_getPrefixRandom(a),a,b,c,!0)}}
|
||||
function openpgp_packet_number_read(a){for(var b=0,c=0;c<a.length;c++)b<<=8,b+=a[c].charCodeAt();return b}function openpgp_packet_number_write(a,b){for(var c="",d=0;d<b;d++)c+=String.fromCharCode(a>>8*(b-d-1)&255);return c}function openpgp_packet_time_read(a){var a=openpgp_packet_number_read(a),b=new Date;b.setTime(1E3*a);return b}function openpgp_packet_time_write(a){a=Math.round(a.getTime()/1E3);return openpgp_packet_number_write(a,4)}
|
||||
function openpgp_packet_user_attribute(){this.tag=17;this.attributes=[];this.read=function(a){for(var b=0;b<a.length;){var c=openpgp_packet.read_simple_length(a),b=b+c.offset;this.attributes.push(a.substr(b,c.len));b+=c.len}}}function openpgp_packet_userid(){this.userid="";this.tag=13;this.read=function(a){this.userid=util.decode_utf8(a)};this.write=function(){return util.encode_utf8(this.userid)}}
|
||||
function openpgp_type_mpi(){this.data=null;this.read=function(a){var b=a[0].charCodeAt()<<8|a[1].charCodeAt(),b=Math.ceil(b/8);this.fromBytes(a.substr(2,b));return 2+b};this.fromBytes=function(a){this.data=new BigInteger(util.hexstrdump(a),16)};this.toBytes=function(){return this.write().substr(2)};this.byteLength=function(){return this.toBytes().length};this.write=function(){return this.data.toMPI()};this.toBigInteger=function(){return this.data.clone()};this.fromBigInteger=function(a){this.data=
|
||||
a.clone()};this.toString=function(){var a=" MPI("+this.mpiBitLength+"b/"+this.mpiByteLength+"B) : 0x",a=a+util.hexstrdump(this.MPI);return a+"\n"}}function openpgp_type_keyid(){for(var a="",b=0;8>b;b++)a+=String.fromCharCode(0);this.read_packet=function(a,b){this.bytes=a.substring(b,b+8);return this};this.toString=function(){return util.hexstrdump(this.bytes)}}
|
||||
function openpgp_type_s2k(){this.read=function(a,b){var c=b;this.type=a[c++].charCodeAt();switch(this.type){case 0:this.hashAlgorithm=a[c++].charCodeAt();this.s2kLength=1;break;case 1:this.hashAlgorithm=a[c++].charCodeAt();this.saltValue=a.substring(c,c+8);c+=8;this.s2kLength=9;break;case 3:this.hashAlgorithm=a[c++].charCodeAt();this.saltValue=a.substring(c,c+8);c+=8;this.EXPBIAS=6;var d=a[c++].charCodeAt();this.count=16+(d&15)<<(d>>4)+this.EXPBIAS;this.s2kLength=10;break;case 101:"GNU"==a.substring(c+
|
||||
1,c+4)?(this.hashAlgorithm=a[c++].charCodeAt(),c+=3,d=1E3+a[c++].charCodeAt(),1001==d?(this.type=d,this.s2kLength=5):util.print_error("unknown s2k gnu protection mode! "+this.type)):util.print_error("unknown s2k type! "+this.type);break;default:util.print_error("unknown s2k type! "+this.type)}this.packetLength=c-b;return this};this.write=function(a,b,c,d,e){this.type=a;if(3==this.type)this.saltValue=d,this.hashAlgorithm=b,this.count=16+(e&15)<<(e>>4)+6,this.s2kLength=10;return this.produce_key(c)};
|
||||
this.produce_key=function(a,b){var a=util.encode_utf8(a),c;if(0==this.type)c=openpgp_crypto_hashData(this.hashAlgorithm,a);else if(1==this.type)c=openpgp_crypto_hashData(this.hashAlgorithm,this.saltValue+a);else if(3==this.type){c=[];for(c[0]=this.saltValue+a;c.length*(this.saltValue+a).length<this.count;)c.push(this.saltValue+a);c=c.join("");c.length>this.count&&(c=c.substr(0,this.count));c=b&&(24==b||32==b)?openpgp_crypto_hashData(this.hashAlgorithm,c)+openpgp_crypto_hashData(this.hashAlgorithm,
|
||||
String.fromCharCode(0)+c):openpgp_crypto_hashData(this.hashAlgorithm,c)}else return null;return c.substr(0,b)}}
|
||||
function openpgp_type_s2k(){this.algorithm=openpgp.hash.sha256;this.type=openpgp_type_s2k.type.iterated;this.c=10;this.salt=openpgp_crypto_getRandomBytes(8);this.get_count=function(){return 16+(this.c&15)<<(this.c>>4)+6};this.read=function(a){var b=0;this.type=a[b++].charCodeAt();this.algorithm=a[b++].charCodeAt();var c=openpgp_type_s2k.type;switch(this.type){case c.simple:break;case c.salted:this.salt=a.substr(b,8);b+=8;break;case c.iterated:this.salt=a.substr(b,8);b+=8;this.c=a[b++].charCodeAt();
|
||||
function openpgp_type_s2k(){this.algorithm=openpgp.hash.sha256;this.type=openpgp_type_s2k.type.iterated;this.c=96;this.salt=openpgp_crypto_getRandomBytes(8);this.get_count=function(){return 16+(this.c&15)<<(this.c>>4)+6};this.read=function(a){var b=0;this.type=a[b++].charCodeAt();this.algorithm=a[b++].charCodeAt();var c=openpgp_type_s2k.type;switch(this.type){case c.simple:break;case c.salted:this.salt=a.substr(b,8);b+=8;break;case c.iterated:this.salt=a.substr(b,8);b+=8;this.c=a[b++].charCodeAt();
|
||||
break;case c.gnu:"GNU"==a.substr(b,3)?(b+=3,a=1E3+a[b++].charCodeAt(),1001==a?this.type=a:util.print_error("unknown s2k gnu protection mode! "+this.type)):util.print_error("unknown s2k type! "+this.type);break;default:util.print_error("unknown s2k type! "+this.type)}return b};this.write=function(){var a=String.fromCharCode(this.type),a=a+String.fromCharCode(this.algorithm),b=openpgp_type_s2k.type;switch(this.type){case b.salted:a+=this.salt;break;case b.iterated:a+=this.salt,a+=String.fromCharCode(this.c)}return a};
|
||||
this.produce_key=function(a,b){for(var a=util.encode_utf8(a),c="",d="";c.length<=b;){var e;a:{e=d;var f=openpgp_type_s2k.type;switch(this.type){case f.simple:e=openpgp_crypto_hashData(this.algorithm,e+a);break a;case f.salted:e=openpgp_crypto_hashData(this.algorithm,e+this.salt+a);break a;case f.iterated:var f=[],h=this.get_count();for(data=this.salt+a;f.length*data.length<h;)f.push(data);f=f.join("");f.length>h&&(f=f.substr(0,h));e=openpgp_crypto_hashData(this.algorithm,e+f);break a}e=void 0}c+=
|
||||
e;d+=String.fromCharCode(0)}return c.substr(0,b)}}openpgp_type_s2k.type={simple:0,salted:1,iterated:3,gnu:101};
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
* or an object with attribute "text" containing the message text
|
||||
* and an attribute "openpgp" containing the bytes.
|
||||
*/
|
||||
function openpgp_encoding_deArmor(text) {
|
||||
function openpgp_encoding_dearmor(text) {
|
||||
text = text.replace(/\r/g, '')
|
||||
|
||||
var type = openpgp_encoding_get_type(text);
|
||||
|
|
|
@ -153,15 +153,4 @@ function openpgp_packet_compressed() {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pretty printing the packet (useful for debug purposes)
|
||||
* @return {String}
|
||||
*/
|
||||
this.toString = function() {
|
||||
return '5.6. Compressed Data Packet (Tag 8)\n'+
|
||||
' Compression Algorithm = '+this.algorithm+'\n'+
|
||||
' Compressed Data: Byte ['+util.hexstrdump(this.compressed)+']\n';
|
||||
}
|
||||
};
|
||||
|
|
|
@ -121,7 +121,7 @@ function _openpgp_packet() {
|
|||
* @param {integer} len Length of the input from position on
|
||||
* @return {Object} Returns a parsed openpgp_packet
|
||||
*/
|
||||
function read_packet(input, position, len) {
|
||||
this.read_packet = function(input, position, len) {
|
||||
// some sanity checks
|
||||
if (input == null || input.length <= position
|
||||
|| input.substring(position).length < 2
|
||||
|
@ -300,7 +300,6 @@ function _openpgp_packet() {
|
|||
};
|
||||
}
|
||||
|
||||
this.read_packet = read_packet;
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
* are stored as numerical indices.
|
||||
*/
|
||||
function openpgp_packetlist() {
|
||||
/** The number of packets contained within the list.
|
||||
* @readonly
|
||||
* @type {Integer} */
|
||||
this.length = 0;
|
||||
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
function openpgp_packet_public_key() {
|
||||
this.tag = 6;
|
||||
this.version = 4;
|
||||
this.created = null;
|
||||
this.created = new Date();
|
||||
this.mpi = [];
|
||||
this.algorithm = openpgp.publickey.rsa_sign;
|
||||
|
||||
|
|
|
@ -175,30 +175,5 @@ function openpgp_packet_public_key_encrypted_session_key() {
|
|||
this.symmetric_algorithm = decoded.charCodeAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string representation of this object (useful for debug
|
||||
* purposes)
|
||||
*
|
||||
* @return {String} The string containing a openpgp description
|
||||
*/
|
||||
this.toString = function() {
|
||||
var result = '5.1. Public-Key Encrypted Session Key Packets (Tag 1)\n'
|
||||
+ ' KeyId: '
|
||||
+ this.keyId.toString()
|
||||
+ '\n'
|
||||
+ ' length: '
|
||||
+ this.packetLength
|
||||
+ '\n'
|
||||
+ ' version:'
|
||||
+ this.version
|
||||
+ '\n'
|
||||
+ ' pubAlgUs:'
|
||||
+ this.publicKeyAlgorithmUsed + '\n';
|
||||
for ( var i = 0; i < this.encrypted.length; i++) {
|
||||
result += this.encrypted[i].toString();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -28,11 +28,7 @@ function openpgp_packet_secret_key() {
|
|||
this.tag = 5;
|
||||
this.public_key = new openpgp_packet_public_key();
|
||||
this.mpi = [];
|
||||
this.symmetric_algorithm = openpgp.symmetric.plaintext;
|
||||
this.hash_algorithm = openpgp.hash.sha1;
|
||||
this.s2k = null;
|
||||
this.encrypted = null;
|
||||
this.iv = null;
|
||||
|
||||
|
||||
function get_hash_len(hash) {
|
||||
|
@ -62,7 +58,7 @@ function openpgp_packet_secret_key() {
|
|||
var hash = hashfn(cleartext);
|
||||
|
||||
if(hash != hashtext)
|
||||
throw "Hash mismatch!";
|
||||
throw new Error("Hash mismatch.");
|
||||
|
||||
var mpis = openpgp_crypto_getPrivateMpiCount(algorithm);
|
||||
|
||||
|
@ -76,6 +72,19 @@ function openpgp_packet_secret_key() {
|
|||
return mpi;
|
||||
}
|
||||
|
||||
function write_cleartext_mpi(hash_algorithm, mpi) {
|
||||
var bytes= '';
|
||||
for(var i in mpi) {
|
||||
bytes += mpi[i].write();
|
||||
}
|
||||
|
||||
|
||||
bytes += get_hash_fn(hash_algorithm)(bytes);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
// 5.5.3. Secret-Key Packet Formats
|
||||
|
||||
/**
|
||||
|
@ -96,60 +105,20 @@ function openpgp_packet_secret_key() {
|
|||
// 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.
|
||||
var s2k_usage = bytes[0].charCodeAt();
|
||||
|
||||
var i = 1;
|
||||
var isEncrypted = bytes[0].charCodeAt();
|
||||
|
||||
// - [Optional] If string-to-key usage octet was 255 or 254, a one-
|
||||
// octet symmetric encryption algorithm.
|
||||
if (s2k_usage == 255 || s2k_usage == 254) {
|
||||
this.symmetric_algorithm = bytes[i++].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.
|
||||
this.s2k = new openpgp_type_s2k();
|
||||
i += this.s2k.read(bytes.substr(i));
|
||||
}
|
||||
|
||||
// - [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.
|
||||
if(isEncrypted) {
|
||||
this.encrypted = bytes;
|
||||
} else {
|
||||
|
||||
// - Plain or encrypted multiprecision integers comprising the secret
|
||||
// key data. These algorithm-specific fields are as described
|
||||
// below.
|
||||
|
||||
if (s2k_usage != 0 && s2k_usage != 255 &&
|
||||
s2k_usage != 254) {
|
||||
this.symmetric_algorithm = s2k_usage;
|
||||
}
|
||||
|
||||
if (s2k_usage != 0 && this.s2k.type != 1001) {
|
||||
this.iv = bytes.substr(i,
|
||||
openpgp_crypto_getBlockLength(this.symmetric_algorithm));
|
||||
|
||||
i += this.iv.length;
|
||||
}
|
||||
|
||||
if(s2k_usage == 254)
|
||||
this.hash_algorithm = openpgp.hash.sha1;
|
||||
else
|
||||
this.hash_algorithm = 'checksum';
|
||||
|
||||
// - Plain or encrypted multiprecision integers comprising the secret
|
||||
// key data. These algorithm-specific fields are as described
|
||||
// below.
|
||||
|
||||
// s2k type 1001 corresponds to GPG specific extension without primary key secrets
|
||||
// http://www.gnupg.org/faq/GnuPG-FAQ.html#how-can-i-use-gnupg-in-an-automated-environment
|
||||
if (s2k_usage != 0 && this.s2k.type == 1001) {
|
||||
this.mpi = null;
|
||||
this.encrypted = null;
|
||||
|
||||
} else if (s2k_usage != 0) {
|
||||
this.encrypted = bytes.substr(i);
|
||||
|
||||
} else {
|
||||
this.mpi = parse_cleartext_mpi(this.hash_algorithm, bytes.substr(i),
|
||||
this.mpi = parse_cleartext_mpi('mod', bytes.substr(1),
|
||||
this.public_key.algorithm);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -158,7 +127,7 @@ function openpgp_packet_secret_key() {
|
|||
* @param {Integer} keyType Follows the OpenPGP algorithm standard,
|
||||
* IE 1 corresponds to RSA.
|
||||
* @param {RSA.keyObject} key
|
||||
* @param password
|
||||
* @param passphrase
|
||||
* @param s2kHash
|
||||
* @param symmetricEncryptionAlgorithm
|
||||
* @param timePacket
|
||||
|
@ -168,25 +137,11 @@ function openpgp_packet_secret_key() {
|
|||
this.write = function() {
|
||||
var bytes = this.public_key.write();
|
||||
|
||||
if(this.encrypted == null) {
|
||||
if(!this.encrypted) {
|
||||
bytes += String.fromCharCode(0);
|
||||
|
||||
var mpi = '';
|
||||
for(var i in this.mpi) {
|
||||
mpi += this.mpi[i].write();
|
||||
}
|
||||
|
||||
bytes += mpi;
|
||||
|
||||
// TODO check the cheksum!
|
||||
bytes += openpgp_packet_number_write(util.calc_checksum(mpi), 2);
|
||||
} else if(this.s2k == null) {
|
||||
bytes += String.fromCharCode(this.symmetric_algorithm);
|
||||
bytes += this.encrypted;
|
||||
bytes += write_cleartext_mpi('mod', this.mpi);
|
||||
} else {
|
||||
bytes += String.fromCharCode(254);
|
||||
bytes += String.fromCharCode(this.symmetric_algorithm);
|
||||
bytes += this.s2k.write();
|
||||
bytes += this.encrypted;
|
||||
}
|
||||
|
||||
|
@ -196,75 +151,54 @@ function openpgp_packet_secret_key() {
|
|||
|
||||
|
||||
|
||||
this.encrypt = function(password) {
|
||||
/** Encrypt the payload. By default, we use aes256 and iterated, salted string
|
||||
* to key specifier
|
||||
* @param {String} passphrase
|
||||
*/
|
||||
this.encrypt = function(passphrase) {
|
||||
|
||||
var s2k = new openpgp_type_s2k(),
|
||||
symmetric = openpgp.symmetric.aes256,
|
||||
cleartext = write_cleartext_mpi(openpgp.hash.sha1, this.mpi),
|
||||
key = produceEncryptionKey(s2k, passphrase, symmetric),
|
||||
blockLen = openpgp_crypto_getBlockLength(symmetric),
|
||||
iv = openpgp_crypto_getRandomBytes(blockLen);
|
||||
|
||||
|
||||
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.symmetric_algorithm);
|
||||
//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 cleartext = key.d.toMPI() + key.p.toMPI() + key.q.toMPI() + key.u.toMPI();
|
||||
var sha1Hash = str_sha1(cleartext);
|
||||
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.symmetric_algorithm){
|
||||
case 3:
|
||||
this.iv.length = 8;
|
||||
this.iv = openpgp_crypto_getRandomBytes(this.iv.length);
|
||||
ciphertextMPIs = normal_cfb_encrypt(function(block, key) {
|
||||
var cast5 = new openpgp_symenc_cast5();
|
||||
cast5.setKey(key);
|
||||
return cast5.encrypt(util.str2bin(block));
|
||||
}, this.iv.length, util.str2bin(hashKey.substring(0,16)), cleartext + sha1Hash, this.iv);
|
||||
body += this.iv + ciphertextMPIs;
|
||||
break;
|
||||
case 7:
|
||||
case 8:
|
||||
case 9:
|
||||
this.iv.length = 16;
|
||||
this.iv = openpgp_crypto_getRandomBytes(this.iv.length);
|
||||
ciphertextMPIs = normal_cfb_encrypt(AESencrypt,
|
||||
this.iv.length, hashKey, cleartext + 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};
|
||||
this.encrypted = '';
|
||||
this.encrypted += String.fromCharCode(254);
|
||||
this.encrypted += String.fromCharCode(symmetric);
|
||||
this.encrypted += s2k.write();
|
||||
this.encrypted += iv;
|
||||
|
||||
console.log(cleartext);
|
||||
|
||||
switch(symmetric) {
|
||||
case 3:
|
||||
this.encrypted += normal_cfb_encrypt(function(block, key) {
|
||||
var cast5 = new openpgp_symenc_cast5();
|
||||
cast5.setKey(key);
|
||||
return cast5.encrypt(util.str2bin(block));
|
||||
}, iv.length, key, cleartext, iv);
|
||||
break;
|
||||
case 7:
|
||||
case 8:
|
||||
case 9:
|
||||
var fn = function(block,key) {
|
||||
return AESencrypt(util.str2bin(block),key);
|
||||
}
|
||||
this.encrypted += normal_cfb_encrypt(fn,
|
||||
iv.length, new keyExpansion(key), cleartext, iv);
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unsupported symmetric encryption algorithm.");
|
||||
}
|
||||
}
|
||||
|
||||
function produceEncryptionKey(s2k, passphrase, algorithm) {
|
||||
return s2k.produce_key(passphrase,
|
||||
openpgp_crypto_getKeyLength(algorithm));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts the private key MPIs which are needed to use the key.
|
||||
|
@ -277,38 +211,65 @@ function openpgp_packet_secret_key() {
|
|||
* @return {Boolean} True if the passphrase was correct; false if not
|
||||
*/
|
||||
this.decrypt = function(passphrase) {
|
||||
if (this.encrypted == null)
|
||||
if (!this.encrypted)
|
||||
return;
|
||||
|
||||
// creating a key out of the passphrase
|
||||
var key = this.s2k.produce_key(passphrase,
|
||||
openpgp_crypto_getKeyLength(this.symmetric_algorithm));
|
||||
var i = 0,
|
||||
symmetric,
|
||||
key;
|
||||
|
||||
var cleartext = '';
|
||||
var s2k_usage = this.encrypted[i++].charCodeAt();
|
||||
|
||||
// - [Optional] If string-to-key usage octet was 255 or 254, a one-
|
||||
// octet symmetric encryption algorithm.
|
||||
if (s2k_usage == 255 || s2k_usage == 254) {
|
||||
symmetric = this.encrypted[i++].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.
|
||||
var s2k = new openpgp_type_s2k();
|
||||
i += s2k.read(this.encrypted.substr(i));
|
||||
|
||||
key = produceEncryptionKey(s2k, passphrase, symmetric);
|
||||
} else {
|
||||
symmetric = s2k_usage;
|
||||
key = MD5(passphrase);
|
||||
}
|
||||
|
||||
// - [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.
|
||||
var iv = this.encrypted.substr(i,
|
||||
openpgp_crypto_getBlockLength(symmetric));
|
||||
|
||||
i += iv.length;
|
||||
|
||||
var cleartext,
|
||||
ciphertext = this.encrypted.substr(i);
|
||||
|
||||
|
||||
switch (this.symmetric_algorithm) {
|
||||
switch (symmetric) {
|
||||
case 1: // - IDEA [IDEA]
|
||||
util.print_error("openpgp.packet.keymaterial.js\n"
|
||||
+"symmetric encryption algorithim: IDEA is not implemented");
|
||||
throw new Error("IDEA is not implemented.");
|
||||
return false;
|
||||
case 2: // - TripleDES (DES-EDE, [SCHNEIER] [HAC] - 168 bit key derived from 192)
|
||||
cleartext = normal_cfb_decrypt(function(block, key) {
|
||||
return des(key, block,1,null,0);
|
||||
}, this.iv.length, key, this.encrypted, this.iv);
|
||||
}, iv.length, key, ciphertext, iv);
|
||||
break;
|
||||
case 3: // - CAST5 (128 bit key, as per [RFC2144])
|
||||
cleartext = normal_cfb_decrypt(function(block, key) {
|
||||
var cast5 = new openpgp_symenc_cast5();
|
||||
cast5.setKey(key);
|
||||
return cast5.encrypt(util.str2bin(block));
|
||||
}, this.iv.length, util.str2bin(key.substring(0,16)), this.encrypted, this.iv);
|
||||
}, iv.length, util.str2bin(key.substring(0,16)), ciphertext, iv);
|
||||
break;
|
||||
case 4: // - Blowfish (128 bit key, 16 rounds) [BLOWFISH]
|
||||
cleartext = normal_cfb_decrypt(function(block, key) {
|
||||
var blowfish = new Blowfish(key);
|
||||
return blowfish.encrypt(block);
|
||||
}, this.iv.length, key, this.encrypted, this.iv);
|
||||
}, iv.length, key, ciphertext, iv);
|
||||
break;
|
||||
case 7: // - AES with 128-bit key [AES]
|
||||
case 8: // - AES with 192-bit key
|
||||
|
@ -316,27 +277,27 @@ function openpgp_packet_secret_key() {
|
|||
cleartext = normal_cfb_decrypt(function(block,key){
|
||||
return AESencrypt(util.str2bin(block),key);
|
||||
},
|
||||
this.iv.length, keyExpansion(key),
|
||||
this.encrypted, this.iv);
|
||||
iv.length, new keyExpansion(key),
|
||||
ciphertext, 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");
|
||||
throw new Error("Twofish is 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.symmetric_algorithm);
|
||||
throw new Error("Unknown symmetric algorithm.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cleartext == null) {
|
||||
util.print_error("openpgp.packet.keymaterial.js\n"+"cleartext was null");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
var hash;
|
||||
if(s2k_usage == 254)
|
||||
hash = openpgp.hash.sha1;
|
||||
else
|
||||
hash = 'mod';
|
||||
|
||||
|
||||
this.mpi = parse_cleartext_mpi(this.hash_algorithm, cleartext,
|
||||
|
||||
this.mpi = parse_cleartext_mpi(hash, cleartext,
|
||||
this.public_key.algorithm);
|
||||
}
|
||||
|
||||
|
|
|
@ -126,19 +126,4 @@ function openpgp_packet_sym_encrypted_integrity_protected() {
|
|||
else
|
||||
this.packets.read(decrypted.substr(0, decrypted.length - 22));
|
||||
}
|
||||
|
||||
this.toString = function() {
|
||||
var data = '';
|
||||
if(openpgp.config.debug)
|
||||
data = ' data: Bytes ['
|
||||
+ util.hexstrdump(this.encrypted) + ']';
|
||||
|
||||
return '5.13. Sym. Encrypted Integrity Protected Data Packet (Tag 18)\n'
|
||||
+ '\n'
|
||||
+ ' version: '
|
||||
+ this.version
|
||||
+ '\n'
|
||||
+ data;
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -127,20 +127,5 @@ function openpgp_packet_sym_encrypted_session_key() {
|
|||
openpgp_crypto_getPrefixRandom(this.private_algorithm),
|
||||
this.private_algorithm, key, private_key, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string representation of this object (useful for debug
|
||||
* purposes)
|
||||
*
|
||||
* @return {String} The string containing a openpgp description
|
||||
*/
|
||||
this.toString = function() {
|
||||
return '5.3 Symmetric-Key Encrypted Session Key Packets (Tag 3)\n'
|
||||
+ ' KeyId: ' + this.keyId.toString() + '\n'
|
||||
+ ' length: ' + this.packetLength + '\n'
|
||||
+ ' version:' + this.version + '\n' + ' symKeyA:'
|
||||
+ this.symmetricKeyAlgorithmUsed + '\n' + ' s2k: '
|
||||
+ this.s2k + '\n';
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
function openpgp_packet_symmetrically_encrypted() {
|
||||
this.tag = 9;
|
||||
this.encrypted = null;
|
||||
/** Decrypted packets contained within. */
|
||||
this.packets = new openpgp_packetlist();
|
||||
|
||||
|
||||
|
@ -64,12 +65,4 @@ function openpgp_packet_symmetrically_encrypted() {
|
|||
this.encrypted = openpgp_crypto_symmetricEncrypt(
|
||||
openpgp_crypto_getPrefixRandom(algo), algo, key, data, true);
|
||||
}
|
||||
|
||||
|
||||
this.toString = function () {
|
||||
return '5.7. Symmetrically Encrypted Data Packet (Tag 9)\n'
|
||||
+ ' Used symmetric algorithm: ' + this.algorithmType + '\n'
|
||||
+ ' encrypted data: Bytes ['
|
||||
+ util.hexstrdump(this.encryptedData) + ']\n';
|
||||
}
|
||||
};
|
||||
|
|
|
@ -29,7 +29,7 @@ function openpgp_type_s2k() {
|
|||
this.algorithm = openpgp.hash.sha256;
|
||||
/** @type {openpgp_type_s2k.type} */
|
||||
this.type = openpgp_type_s2k.type.iterated;
|
||||
this.c = 10;
|
||||
this.c = 96;
|
||||
/** @type {openpgp_bytearray}
|
||||
* Eight bytes of salt. */
|
||||
this.salt = openpgp_crypto_getRandomBytes(8);
|
||||
|
|
|
@ -100,7 +100,7 @@ unittests.register("Packet testing", function() {
|
|||
|
||||
|
||||
|
||||
var msgbytes = openpgp_encoding_deArmor(msg).openpgp;
|
||||
var msgbytes = openpgp_encoding_dearmor(msg).openpgp;
|
||||
|
||||
var parsed = new openpgp_packetlist();
|
||||
parsed.read(msgbytes);
|
||||
|
@ -181,7 +181,7 @@ unittests.register("Packet testing", function() {
|
|||
'-----END PGP PRIVATE KEY BLOCK-----';
|
||||
|
||||
key = new openpgp_packetlist();
|
||||
key.read(openpgp_encoding_deArmor(armored_key).openpgp);
|
||||
key.read(openpgp_encoding_dearmor(armored_key).openpgp);
|
||||
key = key[0];
|
||||
|
||||
var enc = new openpgp_packet_public_key_encrypted_session_key(),
|
||||
|
@ -249,11 +249,11 @@ unittests.register("Packet testing", function() {
|
|||
|
||||
|
||||
var key = new openpgp_packetlist();
|
||||
key.read(openpgp_encoding_deArmor(armored_key).openpgp);
|
||||
key.read(openpgp_encoding_dearmor(armored_key).openpgp);
|
||||
key = key[3];
|
||||
|
||||
var msg = new openpgp_packetlist();
|
||||
msg.read(openpgp_encoding_deArmor(armored_msg).openpgp);
|
||||
msg.read(openpgp_encoding_dearmor(armored_msg).openpgp);
|
||||
|
||||
msg[0].decrypt(key.public_key.mpi, key.mpi);
|
||||
msg[1].decrypt(msg[0].symmetric_algorithm, msg[0].symmetric_key);
|
||||
|
@ -310,12 +310,12 @@ unittests.register("Packet testing", function() {
|
|||
'-----END PGP MESSAGE-----';
|
||||
|
||||
var key = new openpgp_packetlist();
|
||||
key.read(openpgp_encoding_deArmor(armored_key).openpgp);
|
||||
key.read(openpgp_encoding_dearmor(armored_key).openpgp);
|
||||
key = key[3];
|
||||
key.decrypt('test');
|
||||
|
||||
var msg = new openpgp_packetlist();
|
||||
msg.read(openpgp_encoding_deArmor(armored_msg).openpgp);
|
||||
msg.read(openpgp_encoding_dearmor(armored_msg).openpgp);
|
||||
|
||||
msg[0].decrypt(key.public_key.mpi, key.mpi);
|
||||
msg[1].decrypt(msg[0].symmetric_algorithm, msg[0].symmetric_key);
|
||||
|
@ -330,7 +330,7 @@ unittests.register("Packet testing", function() {
|
|||
|
||||
|
||||
var key = new openpgp_packetlist();
|
||||
key.read(openpgp_encoding_deArmor(armored_key).openpgp);
|
||||
key.read(openpgp_encoding_dearmor(armored_key).openpgp);
|
||||
|
||||
|
||||
var verified = key[2].verify(key[0].public_key,
|
||||
|
@ -339,7 +339,7 @@ unittests.register("Packet testing", function() {
|
|||
key: key[0].public_key
|
||||
});
|
||||
|
||||
verified = verified == key[4].verify(key[0].public_key,
|
||||
verified = verified && key[4].verify(key[0].public_key,
|
||||
{
|
||||
key: key[0].public_key,
|
||||
bind: key[3].public_key
|
||||
|
@ -367,11 +367,11 @@ unittests.register("Packet testing", function() {
|
|||
'-----END PGP MESSAGE-----'
|
||||
|
||||
var key = new openpgp_packetlist();
|
||||
key.read(openpgp_encoding_deArmor(armored_key).openpgp);
|
||||
key.read(openpgp_encoding_dearmor(armored_key).openpgp);
|
||||
key[3].decrypt('test')
|
||||
|
||||
var msg = new openpgp_packetlist();
|
||||
msg.read(openpgp_encoding_deArmor(armored_msg).openpgp);
|
||||
msg.read(openpgp_encoding_dearmor(armored_msg).openpgp);
|
||||
|
||||
|
||||
msg[0].decrypt(key[3].public_key.mpi, key[3].mpi);
|
||||
|
@ -390,6 +390,40 @@ unittests.register("Packet testing", function() {
|
|||
|
||||
return new test_result('Reading a signed, encrypted message.',
|
||||
verified == true);
|
||||
}, function() {
|
||||
var key = new openpgp_packetlist();
|
||||
key.push(new openpgp_packet_secret_key);
|
||||
|
||||
var rsa = new RSA(),
|
||||
mpi = rsa.generate(512, "10001")
|
||||
|
||||
|
||||
var mpi = [
|
||||
[mpi.d, mpi.p, mpi.q, mpi.u],
|
||||
[mpi.n, mpi.ee]];
|
||||
|
||||
mpi = mpi.map(function(k) {
|
||||
return k.map(function(bn) {
|
||||
var mpi = new openpgp_type_mpi();
|
||||
mpi.fromBigInteger(bn);
|
||||
return mpi;
|
||||
});
|
||||
});
|
||||
|
||||
key[0].public_key.mpi = mpi[1];
|
||||
key[0].mpi = mpi[0];
|
||||
|
||||
key[0].encrypt('hello');
|
||||
|
||||
var raw = key.write();
|
||||
|
||||
var key2 = new openpgp_packetlist();
|
||||
key2.read(raw);
|
||||
key2[0].decrypt('hello');
|
||||
|
||||
|
||||
return new test_result('Writing and encryptio of a secret key packet.',
|
||||
key[0].mpi.toString() == key2[0].mpi.toString());
|
||||
}];
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user