finalize get from onion
This commit is contained in:
parent
542e31cd96
commit
a9f3348a01
4236
package-lock.json
generated
4236
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -3,6 +3,7 @@
|
|||
"base32": "0.0.6",
|
||||
"curve25519-js": "0.0.4",
|
||||
"futoin-hkdf": "^1.3.2",
|
||||
"jest": "^26.6.3",
|
||||
"tweetnacl": "^1.0.3"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -281,8 +281,34 @@ class Circuit {
|
|||
}
|
||||
|
||||
_on_relay_extended_cell(cell) {
|
||||
//TODO
|
||||
throw new Error('Not implemented')
|
||||
//
|
||||
// The payload of an EXTENDED cell is the same as the payload of a
|
||||
// CREATED cell.
|
||||
//
|
||||
|
||||
//
|
||||
// finish the handshake.
|
||||
//
|
||||
|
||||
const handshake_data = cell.relay_payload;
|
||||
this._extend_node.compute_shared_secret(handshake_data);
|
||||
|
||||
if (this._extend_node.has_valid_crypto_state())
|
||||
{
|
||||
this._node_list.push(this._extend_node);
|
||||
|
||||
//
|
||||
// we're ready here.
|
||||
//
|
||||
this._extend_node = null;
|
||||
this.state = States.ready;
|
||||
}
|
||||
else
|
||||
{
|
||||
this._logger.error("Circuit.handle_relay_extended_cell() extend node [ %s ] has invalid crypto state", this._extend_node.onion_router.name);
|
||||
|
||||
this.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
_on_relay_extended2(cell) {
|
||||
|
@ -752,7 +778,7 @@ class Circuit {
|
|||
//mini_assert(rendezvous_cookie.get_size() == 20);
|
||||
|
||||
const introduction_point = this.get_final_circuit_node().onion_router;
|
||||
const introducee = rendezvous_circuit.get_final_circuit_node.onion_router;
|
||||
const introducee = rendezvous_circuit.get_final_circuit_node().onion_router;
|
||||
|
||||
this._logger.debug("Circuit.rendezvous_introduce() [or: %s, state: introducing]", introduction_point.name);
|
||||
this.state = States.rendezvous_introducing;
|
||||
|
@ -790,7 +816,7 @@ class Circuit {
|
|||
2 + // port
|
||||
20 + // identity_fingerprint
|
||||
2 + // onion key size
|
||||
32 + // onion key
|
||||
introducee.onion_key.length + // onion key
|
||||
20 + // rendezvous cookie
|
||||
128); // DH
|
||||
|
||||
|
@ -808,7 +834,14 @@ class Circuit {
|
|||
rendezvous_cookie.copy(handshake_bytes, 29 + introducee.onion_key.length);
|
||||
rendezvous_circuit._extend_node.key_agreement.public_key.copy(handshake_bytes, 29 + introducee.onion_key.length + rendezvous_cookie.length);
|
||||
|
||||
const handshake_encrypted = hybrid_encrypt(handshake_bytes, introduction_point.service_key);
|
||||
const b64 = introduction_point.service_key.toString('base64');
|
||||
const parts = ['-----BEGIN RSA PUBLIC KEY-----'];
|
||||
for (let i = 0; i < b64.length; i += 64) {
|
||||
parts.push(b64.slice(i, i + 64));
|
||||
}
|
||||
parts.push('-----END RSA PUBLIC KEY-----');
|
||||
const service_key = parts.join('\n')
|
||||
const handshake_encrypted = hybrid_encrypt(handshake_bytes, service_key);
|
||||
|
||||
//
|
||||
// compose the final payload.
|
||||
|
@ -841,7 +874,7 @@ class Circuit {
|
|||
return;
|
||||
}
|
||||
|
||||
if (await rendezvous_circuit.wait_for_state(States.rendezvous_completed))
|
||||
if (await rendezvous_circuit.wait_for_state(States.rendezvous_completed, 90000))
|
||||
{
|
||||
this._logger.debug("Circuit.rendezvous_introduce() [or: %s, state: completed]", introduction_point.name);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const KeyAgreementNtor = require('./KeyAgreementNtor');
|
||||
const KeyAgreementTap = require('./KeyAgreementTap');
|
||||
const CircuitNodeCryptoState = require('./CircuitNodeCryptoState');
|
||||
|
||||
class CircuitNode {
|
||||
|
@ -6,7 +7,7 @@ class CircuitNode {
|
|||
/** @type OnionRouter */
|
||||
_onion_router = null;
|
||||
_type = 'normal';
|
||||
/** @type KeyAgreementNtor */
|
||||
/** @type {KeyAgreementNtor|KeyAgreementTap} */
|
||||
_handshake = null;
|
||||
/** @type CircuitNodeCryptoState */
|
||||
_crypto_state = null;
|
||||
|
@ -22,6 +23,9 @@ class CircuitNode {
|
|||
this._circuit = circuit;
|
||||
this._onion_router = or;
|
||||
this._type = type;
|
||||
if (type === 'introductionpoint') {
|
||||
this._handshake = new KeyAgreementTap(or);
|
||||
}
|
||||
}
|
||||
|
||||
create_onion_skin_ntor() {
|
||||
|
|
|
@ -193,7 +193,7 @@ class Consensus {
|
|||
* @return {Promise<string>}
|
||||
* @private
|
||||
*/
|
||||
_download_from_random_router_impl(path, only_authorities) {
|
||||
async _download_from_random_router_impl(path, only_authorities) {
|
||||
let ip;
|
||||
let port;
|
||||
|
||||
|
@ -222,7 +222,11 @@ class Consensus {
|
|||
|
||||
this._logger.debug(`Consensus._download_from_random_router_impl() [path: http://${ip}:${port}${path}]`);
|
||||
|
||||
return get('http:', ip, port, path);
|
||||
try {
|
||||
return await get('http:', ip, port, path);
|
||||
} catch (err) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
_parse_consensus(content) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const crypto = require('crypto');
|
||||
const base32 = require('base32')
|
||||
const base32 = require('../utils/base32')
|
||||
const OnionRouter = require('./OnionRouter')
|
||||
const {get} = require('../utils/http')
|
||||
const {time} = require('../utils/time')
|
||||
|
@ -18,15 +18,16 @@ class HiddenServiceConnector {
|
|||
|
||||
constructor (rendezvous_circuit, onion, logger) {
|
||||
this._rendezvous_circuit = rendezvous_circuit;
|
||||
this._socket = rendezvous_circuit.tor_socket;
|
||||
this._consensus = rendezvous_circuit.tor_socket.onion_router.consensus;
|
||||
this._socket = rendezvous_circuit?.tor_socket;
|
||||
this._consensus = rendezvous_circuit?.tor_socket.onion_router.consensus;
|
||||
this._onion = onion;
|
||||
this._permanent_id = Buffer.from(base32.decode(onion));
|
||||
this._logger = logger;
|
||||
this._time = time;
|
||||
}
|
||||
|
||||
async connect() {
|
||||
this.find_responsible_directories();
|
||||
this._find_responsible_directories();
|
||||
|
||||
if (this._responsible_directory_list.length)
|
||||
{
|
||||
|
@ -43,7 +44,7 @@ class HiddenServiceConnector {
|
|||
if (this._rendezvous_circuit.is_rendezvous_established())
|
||||
{
|
||||
let responsible_directory_index = 0;
|
||||
while ((responsible_directory_index = await this.fetch_hidden_service_descriptor(responsible_directory_index)) !== -1)
|
||||
while ((responsible_directory_index = await this._fetch_hidden_service_descriptor(responsible_directory_index)) !== -1)
|
||||
{
|
||||
await this.introduce();
|
||||
|
||||
|
@ -58,7 +59,7 @@ class HiddenServiceConnector {
|
|||
return false;
|
||||
}
|
||||
|
||||
find_responsible_directories() {
|
||||
_find_responsible_directories() {
|
||||
//
|
||||
// rend-spec.txt
|
||||
// 1.4.
|
||||
|
@ -88,7 +89,7 @@ class HiddenServiceConnector {
|
|||
{
|
||||
const descriptor_id = this.get_descriptor_id(replica);
|
||||
|
||||
const index = directory_list.findIndex(x => Buffer.compare(x.identity_fingerprint, descriptor_id) < 0);
|
||||
const index = directory_list.findIndex(x => Buffer.compare(x.identity_fingerprint, descriptor_id) > 0);
|
||||
|
||||
for (let i = 0; i < 3; i++)
|
||||
{
|
||||
|
@ -111,10 +112,10 @@ class HiddenServiceConnector {
|
|||
// time-period = (current-time + permanent-id-byte * 86400 / 256)
|
||||
// / 86400
|
||||
//
|
||||
const time_period = (time() + (permanent_id_byte * 86400 / 256)) / 86400;
|
||||
const time_period = Math.floor((this._time() + (permanent_id_byte * 86400 / 256)) / 86400);
|
||||
|
||||
const secret_bytes = Buffer.alloc(5);
|
||||
secret_bytes.writeInt32BE(time_period, 0);
|
||||
secret_bytes.writeUInt32BE(time_period, 0);
|
||||
secret_bytes.writeUInt8(replica, 4);
|
||||
|
||||
return sha1(secret_bytes);
|
||||
|
@ -135,7 +136,7 @@ class HiddenServiceConnector {
|
|||
return sha1(descriptor_id_bytes);
|
||||
}
|
||||
|
||||
async fetch_hidden_service_descriptor(responsible_directory_index) {
|
||||
async _fetch_hidden_service_descriptor(responsible_directory_index) {
|
||||
for (let i = responsible_directory_index; i < this._responsible_directory_list.length; i++)
|
||||
{
|
||||
const responsible_directory = this._responsible_directory_list[i];
|
||||
|
@ -149,7 +150,6 @@ class HiddenServiceConnector {
|
|||
this._socket.onion_router.name,
|
||||
this._socket.onion_router.ip,
|
||||
this._socket.onion_router.or_port);
|
||||
this._logger.info("\tConnected...");
|
||||
|
||||
/** @type Circuit */
|
||||
const directory_circuit = await this._socket.create_circuit();
|
||||
|
@ -164,9 +164,10 @@ class HiddenServiceConnector {
|
|||
//
|
||||
continue;
|
||||
}
|
||||
this._logger.info("\tConnected...");
|
||||
|
||||
this._logger.info(
|
||||
"\tExtending circuit for hidden service, connecting to responsible directory '%s' (%s:%u)",
|
||||
"\tExtending circuit for hidden service, connecting to responsible directory '%s' (%s:%i)",
|
||||
responsible_directory.name,
|
||||
responsible_directory.ip,
|
||||
responsible_directory.or_port);
|
||||
|
@ -205,10 +206,10 @@ class HiddenServiceConnector {
|
|||
const descriptor_path = "/tor/rendezvous2/" + base32.encode(this.get_descriptor_id(replica));
|
||||
|
||||
this._logger.debug(
|
||||
"hidden_service::fetch_hidden_service_descriptor() [path: %s]",
|
||||
"hidden_service::_fetch_hidden_service_descriptor() [path: %s]",
|
||||
descriptor_path);
|
||||
|
||||
this._logger.info("\tSending request for hidden service descriptor...");
|
||||
this._logger.info("\tSending request for hidden service descriptor... %s:%i", responsible_directory.ip, responsible_directory.dir_port);
|
||||
|
||||
const hidden_service_descriptor = await get(
|
||||
'http:',
|
||||
|
@ -222,7 +223,7 @@ class HiddenServiceConnector {
|
|||
//
|
||||
// parse hidden service descriptor.
|
||||
//
|
||||
if (!hidden_service_descriptor &&
|
||||
if (hidden_service_descriptor &&
|
||||
!hidden_service_descriptor.includes("404 Not found"))
|
||||
{
|
||||
this._logger.info("\tHidden service descriptor is valid...");
|
||||
|
@ -246,14 +247,14 @@ class HiddenServiceConnector {
|
|||
for (const line of lines) {
|
||||
const parts = line.split(' ');
|
||||
if (parts[0] === 'introduction-point') {
|
||||
const identity_fingerprint = Buffer.from(base32.decode(parts[1]));
|
||||
const identity_fingerprint = base32.decode(parts[1]);
|
||||
current_router = this._consensus.get_onion_router_by_identity_fingerprint(identity_fingerprint);
|
||||
} else if (parts[0] === 'service-key') {
|
||||
serviceKey = '';
|
||||
} else if (parts[0] === '-----BEGIN RSA PUBLIC KEY-----' && serviceKey !== null) {
|
||||
} else if (line === '-----BEGIN RSA PUBLIC KEY-----' && serviceKey !== null) {
|
||||
capture = true;
|
||||
} else if (parts[0] === '-----END RSA PUBLIC KEY-----' && serviceKey !== null) {
|
||||
current_router.service_key = serviceKey;
|
||||
} else if (line === '-----END RSA PUBLIC KEY-----' && serviceKey !== null) {
|
||||
current_router.service_key = Buffer.from(serviceKey, 'base64');
|
||||
introduction_point_list.push(current_router);
|
||||
capture = false;
|
||||
serviceKey = null;
|
||||
|
@ -336,7 +337,7 @@ class HiddenServiceConnector {
|
|||
if (introduce_circuit.is_rendezvous_introduced())
|
||||
{
|
||||
this._logger.info("\tIntroduced successfully...");
|
||||
break;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
22
src/tor/HiddenServiceConnector.test.js
Normal file
22
src/tor/HiddenServiceConnector.test.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
const HiddenServiceConnector = require('./HiddenServiceConnector');
|
||||
const base32 = require('../utils/base32');
|
||||
|
||||
test('base32 decode', function() {
|
||||
const decoded = base32.decode('duskgytldkxiuqc6');
|
||||
//const decoded_buf = Array.prototype.map.call(decoded, (_, i) => decoded.charCodeAt(i))
|
||||
expect(decoded).toEqual(Buffer.from([0x1d,0x24,0xa3,0x62,0x6b,0x1a,0xae,0x8a,0x40,0x5e]));
|
||||
})
|
||||
test('base32 encode', function() {
|
||||
const encoded = base32.encode(Buffer.from([0x1d,0x24,0xa3,0x62,0x6b,0x1a,0xae,0x8a,0x40,0x5e]));
|
||||
//const decoded_buf = Array.prototype.map.call(decoded, (_, i) => decoded.charCodeAt(i))
|
||||
expect(encoded).toEqual('duskgytldkxiuqc6');
|
||||
})
|
||||
test('test descriptor', function() {
|
||||
const time = 1611534264;
|
||||
const onion = 'duskgytldkxiuqc6';
|
||||
const sut = new HiddenServiceConnector(null, onion, null);
|
||||
sut._time = () => time;
|
||||
const descriptor = sut.get_descriptor_id(0);
|
||||
const descriptor_base32 = base32.encode(descriptor);
|
||||
expect(descriptor_base32).toBe('ek7vymlparcwqimmmfixlbars4xjz5jl');
|
||||
})
|
123
src/tor/KeyAgreementTap.js
Normal file
123
src/tor/KeyAgreementTap.js
Normal file
|
@ -0,0 +1,123 @@
|
|||
const crypto = require('crypto')
|
||||
const dh1024 = require('../utils/dh1024')
|
||||
const {sha1} = require('../utils/crypto')
|
||||
|
||||
const DH_P = new Buffer([
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34,
|
||||
0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74,
|
||||
0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd,
|
||||
0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, 0x37,
|
||||
0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45, 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6,
|
||||
0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x37, 0xed, 0x6b, 0x0b, 0xff, 0x5c, 0xb6, 0xf4, 0x06, 0xb7, 0xed,
|
||||
0xee, 0x38, 0x6b, 0xfb, 0x5a, 0x89, 0x9f, 0xa5, 0xae, 0x9f, 0x24, 0x11, 0x7c, 0x4b, 0x1f, 0xe6,
|
||||
0x49, 0x28, 0x66, 0x51, 0xec, 0xe6, 0x53, 0x81, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
]);
|
||||
|
||||
class KeyAgreementTap {
|
||||
constructor (or) {
|
||||
this._router = or;
|
||||
this._dh = crypto.createDiffieHellman(DH_P);
|
||||
this._dh.generateKeys();
|
||||
}
|
||||
|
||||
get public_key() {
|
||||
return this._dh.getPublicKey();
|
||||
}
|
||||
|
||||
get private_key() {
|
||||
return this._dh.getPrivateKey();
|
||||
}
|
||||
|
||||
compute_shared_secret(handshake_data) {
|
||||
return this._compute_shared_secret(
|
||||
handshake_data.slice(0, dh1024.key_size_in_bytes),
|
||||
handshake_data.slice(dh1024.key_size_in_bytes, dh1024.key_size_in_bytes + 20));
|
||||
}
|
||||
|
||||
_compute_shared_secret(other_public_key, verification_data) {
|
||||
//
|
||||
// 5.1.3. The "TAP" handshake
|
||||
//
|
||||
// This handshake uses Diffie-Hellman in Z_p and RSA to compute a set of
|
||||
// shared keys which the client knows are shared only with a particular
|
||||
// server, and the server knows are shared with whomever sent the
|
||||
// original handshake (or with nobody at all). It's not very fast and
|
||||
// not very good. (See Goldberg's "On the Security of the Tor
|
||||
// Authentication Protocol".)
|
||||
//
|
||||
// Define TAP_C_HANDSHAKE_LEN as DH_LEN+KEY_LEN+PK_PAD_LEN.
|
||||
// Define TAP_S_HANDSHAKE_LEN as DH_LEN+HASH_LEN.
|
||||
//
|
||||
// The payload for a CREATE cell is an 'onion skin', which consists of
|
||||
// the first step of the DH handshake data (also known as g^x). This
|
||||
// value is hybrid-encrypted (see 0.3) to the server's onion key, giving
|
||||
// a client handshake of:
|
||||
//
|
||||
// PK-encrypted:
|
||||
// Padding [PK_PAD_LEN bytes]
|
||||
// Symmetric key [KEY_LEN bytes]
|
||||
// First part of g^x [PK_ENC_LEN-PK_PAD_LEN-KEY_LEN bytes]
|
||||
// Symmetrically encrypted:
|
||||
// Second part of g^x [DH_LEN-(PK_ENC_LEN-PK_PAD_LEN-KEY_LEN)
|
||||
// bytes]
|
||||
//
|
||||
// The payload for a CREATED cell, or the relay payload for an
|
||||
// EXTENDED cell, contains:
|
||||
// DH data (g^y) [DH_LEN bytes]
|
||||
// Derivative key data (KH) [HASH_LEN bytes] <see 5.2 below>
|
||||
//
|
||||
// Once the handshake between the OP and an OR is completed, both can
|
||||
// now calculate g^xy with ordinary DH. Before computing g^xy, both parties
|
||||
// MUST verify that the received g^x or g^y value is not degenerate;
|
||||
// that is, it must be strictly greater than 1 and strictly less than p-1
|
||||
// where p is the DH modulus. Implementations MUST NOT complete a handshake
|
||||
// with degenerate keys. Implementations MUST NOT discard other "weak"
|
||||
// g^x values.
|
||||
//
|
||||
// (Discarding degenerate keys is critical for security; if bad keys
|
||||
// are not discarded, an attacker can substitute the OR's CREATED
|
||||
// cell's g^y with 0 or 1, thus creating a known g^xy and impersonating
|
||||
// the OR. Discarding other keys may allow attacks to learn bits of
|
||||
// the private key.)
|
||||
//
|
||||
// Once both parties have g^xy, they derive their shared circuit keys
|
||||
// and 'derivative key data' value via the KDF-TOR function in 5.2.1.
|
||||
//
|
||||
|
||||
//mini_assert(verification_data.get_size() == crypto::sha1::hash_size_in_bytes);
|
||||
|
||||
const shared_secret = this._dh.computeSecret(other_public_key);
|
||||
const derived = this._derive_keys(shared_secret);
|
||||
|
||||
//
|
||||
// first 20 bytes of the derived key is the verification checksum.
|
||||
// rest of it is the key material.
|
||||
//
|
||||
const computed_verification_data = derived.slice(0, 20);
|
||||
const key_material = derived.slice(20);
|
||||
|
||||
if (computed_verification_data.equals(verification_data))
|
||||
{
|
||||
return key_material;
|
||||
}
|
||||
|
||||
return Buffer.alloc(0);
|
||||
}
|
||||
|
||||
_derive_keys(secret) {
|
||||
const key_material = Buffer.alloc(100);
|
||||
|
||||
const hashdata = Buffer.alloc(secret.length + 1);
|
||||
secret.copy(hashdata, 0);
|
||||
|
||||
for (let i = 0; i < 5; i++)
|
||||
{
|
||||
hashdata[secret.length] = i;
|
||||
sha1(hashdata).copy(key_material, i * 20);
|
||||
}
|
||||
|
||||
return key_material;
|
||||
}
|
||||
|
||||
}
|
||||
module.exports = KeyAgreementTap;
|
|
@ -59,12 +59,26 @@ class OnionRouter {
|
|||
const descriptor = await this._consensus.get_onion_router_descriptor(this._identity_fingerprint);
|
||||
// parse
|
||||
const lines = descriptor.split('\n');
|
||||
let capture = '', onion_key = '';
|
||||
for (const line of lines){
|
||||
const parts = line.split(' ');
|
||||
if (parts[0] === 'ntor-onion-key') {
|
||||
if (parts[0] === 'onion-key') {
|
||||
capture = 'onion-key';
|
||||
}
|
||||
else if (parts[0] === 'ntor-onion-key') {
|
||||
this._ntor_onion_key = Buffer.from(parts[1], 'base64');
|
||||
break;
|
||||
}
|
||||
else if (capture === 'onion-key' && line === '-----BEGIN RSA PUBLIC KEY-----') {
|
||||
onion_key = '';
|
||||
}
|
||||
else if (capture === 'onion-key' && line === '-----END RSA PUBLIC KEY-----') {
|
||||
this._onion_key = Buffer.from(onion_key, 'base64')
|
||||
capture = '';
|
||||
}
|
||||
else if (capture === 'onion-key') {
|
||||
onion_key += line;
|
||||
}
|
||||
}
|
||||
this._descriptor_fetched = true;
|
||||
}
|
||||
|
|
|
@ -40,6 +40,11 @@ class TorStream extends Duplex {
|
|||
set state(state) {
|
||||
this._state = state;
|
||||
if (state === States.destroyed) {
|
||||
if (this._buffer) {
|
||||
this.push(this._buffer);
|
||||
this._buffer = null;
|
||||
}
|
||||
this.push(null);
|
||||
for (const k in this._waits) {
|
||||
for (const wait of this._waits[k]) {
|
||||
wait.resolve(false);
|
||||
|
@ -57,12 +62,12 @@ class TorStream extends Duplex {
|
|||
}
|
||||
|
||||
append_to_recv_buffer(data) {
|
||||
if (this._canRead) {
|
||||
this._canRead = this.push(data);
|
||||
} else {
|
||||
this._buffer = this._buffer
|
||||
? Buffer.concat([this._buffer, data], this._buffer.length + data.length)
|
||||
: data;
|
||||
if (this._canRead) {
|
||||
this._canRead = this.push(this._buffer);
|
||||
this._buffer = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
51
src/utils/base32.js
Normal file
51
src/utils/base32.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
const alphabet = 'abcdefghijklmnopqrstuvwxyz234567'
|
||||
|
||||
function decode_chunk(input, in_offset, output, out_offset) {
|
||||
let b = BigInt(0);
|
||||
for (let i = 0; i < 8; i++) {
|
||||
b = (b << 5n) | BigInt(alphabet.indexOf(input[in_offset + i]));
|
||||
}
|
||||
for (let j = 4; j >= 0; j--) {
|
||||
output[out_offset + 4 - j] = Number(b >> BigInt(j * 8));
|
||||
}
|
||||
}
|
||||
|
||||
function decode(input) {
|
||||
const out_len = Math.ceil((input.length / 8) * 5);
|
||||
const output = Buffer.alloc(out_len);
|
||||
|
||||
const q = Math.floor(input.length / 8);
|
||||
for (let i = 0; i < q; i++) {
|
||||
decode_chunk(input, i * 8, output, i * 5);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
exports.decode = decode;
|
||||
|
||||
function encode_chunk(input, in_offset, output, out_offset) {
|
||||
let b = BigInt(0);
|
||||
for (let i = 0; i < 5; i++) {
|
||||
b = (b << 8n) | BigInt(input[in_offset + i]);
|
||||
}
|
||||
for (let i = 7; i >= 0; i--) {
|
||||
b = b << BigInt(24 + (7 - i) * 5);
|
||||
b = b >> BigInt(24 + (7 - i) * 5);
|
||||
|
||||
const c = Number(b >> BigInt(i * 5)) % 32;
|
||||
output[out_offset + 7 - i] = alphabet.charCodeAt(c);
|
||||
}
|
||||
}
|
||||
|
||||
function encode(input) {
|
||||
const out_len = Math.ceil(input.length / 5 * 8);
|
||||
const output = Buffer.alloc(out_len);
|
||||
|
||||
const q = Math.floor(input.length / 5);
|
||||
for (let i = 0; i < q; i++) {
|
||||
encode_chunk(input, i * 5, output, i * 8);
|
||||
}
|
||||
|
||||
return output.toString('ascii');
|
||||
}
|
||||
exports.encode = encode;
|
|
@ -44,13 +44,13 @@ const PK_DATA_LEN_WITH_KEY = PK_DATA_LEN - KEY_LEN;
|
|||
|
||||
/**
|
||||
* @param {Buffer} data
|
||||
* @param {Buffer} public_key
|
||||
* @param {string} public_key
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
function hybrid_encrypt(data, public_key) {
|
||||
if (data.length < PK_DATA_LEN)
|
||||
{
|
||||
return rsa_1024(public_key).encrypt(data);
|
||||
return crypto.publicEncrypt(public_key, data);
|
||||
}
|
||||
|
||||
const random_key = crypto.randomBytes(KEY_LEN);
|
||||
|
@ -62,7 +62,7 @@ function hybrid_encrypt(data, public_key) {
|
|||
random_key, data.slice(0, PK_DATA_LEN_WITH_KEY)
|
||||
], random_key.length + PK_DATA_LEN_WITH_KEY);
|
||||
|
||||
const c1 = rsa_1024(public_key).encrypt(k_and_m1);
|
||||
const c1 = crypto.publicEncrypt(public_key, k_and_m1);
|
||||
|
||||
//
|
||||
// AES_CTR(M2) --> C2
|
||||
|
|
1
src/utils/dh1024.js
Normal file
1
src/utils/dh1024.js
Normal file
|
@ -0,0 +1 @@
|
|||
exports.key_size_in_bytes = (1024/8);
|
|
@ -28,7 +28,7 @@ async function get(protocol, ip, port, path, stream) {
|
|||
res.on('data', chunk => data += chunk.toString());
|
||||
res.on('end', () => resolve(data));
|
||||
res.on('error', (err) => reject(err));
|
||||
});
|
||||
}).on('error', err => reject(err));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user