madeline-ton
Version:
Pure JS client-side implementation of the Telegram TON blockchain protocol
206 lines (178 loc) • 6.29 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _crypto = require("../crypto-sync/crypto");
var _poly = require("../crypto-sync/poly");
var _tools = require("../tools");
/**
* Webcrypto implementaition
*
* Copyright 2016-2019 Daniil Gentili
* (https://daniil.it)
* This file is part of MadelineNode.
* MadelineNode is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
* MadelineNode is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero General Public License for more details.
* You should have received a copy of the GNU General Public License along with MadelineNode.
* If not, see <http://www.gnu.org/licenses/>.
*
* @author Daniil Gentili <daniil@daniil.it>
* @copyright 2016-2019 Daniil Gentili <daniil@daniil.it>
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
*/
var CtrProcessor =
/*#__PURE__*/
function () {
function CtrProcessor() {
(0, _classCallCheck2["default"])(this, CtrProcessor);
}
(0, _createClass2["default"])(CtrProcessor, [{
key: "init",
/**
* Init processor
* @param {Uint32Array} iv
* @param {Uint32Array} key
*/
value: function init(key, iv) {
var _this = this;
this.name = "AES-CTR";
this.by = 0;
this.counter = iv;
this.length = 16;
this.leftover = new Uint8Array(0);
return _poly.windowObject.crypto.subtle.importKey("raw", key, "AES-CTR", false, ["encrypt"]).then(function (key) {
_this.key = key;
return _this;
});
}
/**
* Encrypt data
* @param {ArrayBufferView} data Data to encrypt
* @returns {ArrayBuffer}
*/
}, {
key: "process",
value: function process(data) {
// Turn block cipher into stream cypher by padding + reusing the last partial block
var lengthOrig = data.byteLength;
var lengthCombined = lengthOrig;
var offset = 0; //console.log(data)
if (offset = this.leftover.byteLength) {
lengthCombined += offset;
var newData = new Uint8Array(lengthCombined + (0, _tools.posMod)(-lengthCombined, 16));
newData.set(this.leftover);
newData.set(new Uint8Array(data.buffer), offset);
data = newData.buffer;
} else {
data = (0, _crypto.pad)(data, 16);
} //console.log(new Uint8Array(data))
(0, _crypto.incCounter)(this.counter, this.by);
this.by = Math.floor(lengthCombined / 16);
this.leftover = new Uint8Array(data.slice(this.by * 16, lengthCombined)); //console.log(this.by, this.leftover)
return _poly.windowObject.crypto.subtle.encrypt(this, this.key, data).then(function (res) {
return res.slice(offset, offset + lengthOrig);
});
}
}, {
key: "close",
value: function close() {}
}]);
return CtrProcessor;
}();
/**
*
* @param {Uint32Array} key
* @param {Uint32Array} data
* @param {number} offset
* @param {Uint32Array} iv1
* @param {Uint32Array} iv2
* @returns {ArrayBuffer}
*/
var processIgeEncrypt = function processIgeEncrypt(key, data, offset, iv1, iv2) {
// I could've also used CBC, but webcrypto's CBC implementation pads the output by default.
// This would be fine in itself if it weren't for the fact that webcrypto is forced to do one extra (& useless) AES round per block
// CTR doesn't pad the output, so it's perfect.
var next = offset + 4;
var block = data.slice(offset, next);
return _poly.windowObject.crypto.subtle.encrypt({
name: 'AES-CTR',
counter: block.map(function (v, i) {
return v ^ iv1[i];
}),
length: 16
}, key, iv2).then(function (newBlock) {
newBlock = new Uint32Array(newBlock);
data.set(newBlock, offset);
if (next < data.length) {
return processIgeEncrypt(key, data, next, newBlock, block);
}
return data.buffer;
});
};
/**
* Webcrypto implementation
*/
var CryptoWebCrypto =
/*#__PURE__*/
function () {
function CryptoWebCrypto() {
(0, _classCallCheck2["default"])(this, CryptoWebCrypto);
}
(0, _createClass2["default"])(CryptoWebCrypto, [{
key: "sha1",
/**
* SHA1
* @param {BufferSource} data Data to hash
* @returns {ArrayBuffer}
*/
value: function sha1(data) {
return _poly.windowObject.crypto.subtle.digest('SHA-1', data);
}
/**
* SHA256
* @param {BufferSource} data Data to hash
* @returns {ArrayBuffer}
*/
}, {
key: "sha256",
value: function sha256(data) {
return _poly.windowObject.crypto.subtle.digest('SHA-256', data);
}
/**
* Get continuous CTR processor
* @param {Uint32Array} iv
* @param {Uint32Array} key
* @returns CtrProcessor
*/
}, {
key: "getCtr",
value: function getCtr(key, iv) {
return new CtrProcessor().init(key, iv);
}
/**
* Encrypt data using AES IGE
* @param {Uint32Array} data Data
* @param {Uint32Array} key Key
* @param {Uint32Array} iv IV
* @returns {ArrayBuffer}
*/
}, {
key: "igeEncrypt",
value: function igeEncrypt(data, key, iv) {
// Implement AES IGE using AES CTR and some additional XOR-ing
// Use native WebCrypto for greater AES performance
// Unfortunately, I can't implement native AES IGE decryption because webcrypto assumes PKCS padding for the cyphertext
return _poly.windowObject.crypto.subtle.importKey("raw", key.buffer, "AES-CTR", false, ["encrypt"]).then(function (key) {
return processIgeEncrypt(key, data, 0, iv.subarray(0, 4), iv.subarray(4, 8));
});
}
}]);
return CryptoWebCrypto;
}();
var _default = CryptoWebCrypto;
exports["default"] = _default;