Merge pull request 'feature/tg_parser' (#2) from feature/tg_parser into main
Reviewed-on: #2
330
poetry.lock
generated
@ -637,6 +637,20 @@ files = [
|
||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "commonmark"
|
||||
version = "0.9.1"
|
||||
description = "Python parser for the CommonMark Markdown spec"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"},
|
||||
{file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"]
|
||||
|
||||
[[package]]
|
||||
name = "fastapi"
|
||||
version = "0.101.0"
|
||||
@ -656,6 +670,42 @@ typing-extensions = ">=4.5.0"
|
||||
[package.extras]
|
||||
all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "flask"
|
||||
version = "2.2.2"
|
||||
description = "A simple framework for building complex web applications."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "Flask-2.2.2-py3-none-any.whl", hash = "sha256:b9c46cc36662a7949f34b52d8ec7bb59c0d74ba08ba6cb9ce9adc1d8676d9526"},
|
||||
{file = "Flask-2.2.2.tar.gz", hash = "sha256:642c450d19c4ad482f96729bd2a8f6d32554aa1e231f4f6b4e7e5264b16cca2b"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
click = ">=8.0"
|
||||
itsdangerous = ">=2.0"
|
||||
Jinja2 = ">=3.0"
|
||||
Werkzeug = ">=2.2.2"
|
||||
|
||||
[package.extras]
|
||||
async = ["asgiref (>=3.2)"]
|
||||
dotenv = ["python-dotenv"]
|
||||
|
||||
[[package]]
|
||||
name = "flask-login"
|
||||
version = "0.6.2"
|
||||
description = "User authentication and session management for Flask."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "Flask-Login-0.6.2.tar.gz", hash = "sha256:c0a7baa9fdc448cdd3dd6f0939df72eec5177b2f7abe6cb82fc934d29caac9c3"},
|
||||
{file = "Flask_Login-0.6.2-py3-none-any.whl", hash = "sha256:1ef79843f5eddd0f143c2cd994c1b05ac83c0401dc6234c143495af9a939613f"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
Flask = ">=1.0.4"
|
||||
Werkzeug = ">=1.0.1"
|
||||
|
||||
[[package]]
|
||||
name = "frozenlist"
|
||||
version = "1.4.0"
|
||||
@ -832,6 +882,17 @@ files = [
|
||||
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itsdangerous"
|
||||
version = "2.1.2"
|
||||
description = "Safely pass data to untrusted environments and back."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"},
|
||||
{file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jinja2"
|
||||
version = "3.1.2"
|
||||
@ -862,13 +923,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "loguru"
|
||||
version = "0.7.2"
|
||||
version = "0.6.0"
|
||||
description = "Python logging made (stupidly) simple"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
{file = "loguru-0.7.2-py3-none-any.whl", hash = "sha256:003d71e3d3ed35f0f8984898359d65b79e5b21943f78af86aa5491210429b8eb"},
|
||||
{file = "loguru-0.7.2.tar.gz", hash = "sha256:e671a53522515f34fd406340ee968cb9ecafbc4b36c679da03c18fd8d0bd51ac"},
|
||||
{file = "loguru-0.6.0-py3-none-any.whl", hash = "sha256:4e2414d534a2ab57573365b3e6d0234dfb1d84b68b7f3b948e6fb743860a77c3"},
|
||||
{file = "loguru-0.6.0.tar.gz", hash = "sha256:066bd06758d0a513e9836fd9c6b5a75bfb3fd36841f4b996bc60b547a309d41c"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -876,7 +937,7 @@ colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""}
|
||||
win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
|
||||
|
||||
[package.extras]
|
||||
dev = ["Sphinx (==7.2.5)", "colorama (==0.4.5)", "colorama (==0.4.6)", "exceptiongroup (==1.1.3)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v1.4.1)", "mypy (==v1.5.1)", "pre-commit (==3.4.0)", "pytest (==6.1.2)", "pytest (==7.4.0)", "pytest-cov (==2.12.1)", "pytest-cov (==4.1.0)", "pytest-mypy-plugins (==1.9.3)", "pytest-mypy-plugins (==3.0.0)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.3.0)", "tox (==3.27.1)", "tox (==4.11.0)"]
|
||||
dev = ["Sphinx (>=4.1.1)", "black (>=19.10b0)", "colorama (>=0.3.4)", "docutils (==0.16)", "flake8 (>=3.7.7)", "isort (>=5.1.1)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "tox (>=3.9.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "lxml"
|
||||
@ -1244,6 +1305,27 @@ files = [
|
||||
dev = ["pre-commit", "tox"]
|
||||
testing = ["pytest", "pytest-benchmark"]
|
||||
|
||||
[[package]]
|
||||
name = "ply"
|
||||
version = "3.11"
|
||||
description = "Python Lex & Yacc"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "ply-3.11-py2.py3-none-any.whl", hash = "sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce"},
|
||||
{file = "ply-3.11.tar.gz", hash = "sha256:00c7c1aaa88358b9c765b6d3000c6eec0ba42abca5351b095321aef446081da3"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyaes"
|
||||
version = "1.6.1"
|
||||
description = "Pure-Python Implementation of the AES block-cipher and common modes of operation"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "pyaes-1.6.1.tar.gz", hash = "sha256:02c1b1405c38d3c370b085fb952dd8bea3fadcee6411ad99f312cc129c536d8f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pycparser"
|
||||
version = "2.21"
|
||||
@ -1255,6 +1337,47 @@ files = [
|
||||
{file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pycryptodome"
|
||||
version = "3.18.0"
|
||||
description = "Cryptographic library for Python"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
files = [
|
||||
{file = "pycryptodome-3.18.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:d1497a8cd4728db0e0da3c304856cb37c0c4e3d0b36fcbabcc1600f18504fc54"},
|
||||
{file = "pycryptodome-3.18.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:928078c530da78ff08e10eb6cada6e0dff386bf3d9fa9871b4bbc9fbc1efe024"},
|
||||
{file = "pycryptodome-3.18.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:157c9b5ba5e21b375f052ca78152dd309a09ed04703fd3721dce3ff8ecced148"},
|
||||
{file = "pycryptodome-3.18.0-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:d20082bdac9218649f6abe0b885927be25a917e29ae0502eaf2b53f1233ce0c2"},
|
||||
{file = "pycryptodome-3.18.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:e8ad74044e5f5d2456c11ed4cfd3e34b8d4898c0cb201c4038fe41458a82ea27"},
|
||||
{file = "pycryptodome-3.18.0-cp27-cp27m-win32.whl", hash = "sha256:62a1e8847fabb5213ccde38915563140a5b338f0d0a0d363f996b51e4a6165cf"},
|
||||
{file = "pycryptodome-3.18.0-cp27-cp27m-win_amd64.whl", hash = "sha256:16bfd98dbe472c263ed2821284118d899c76968db1a6665ade0c46805e6b29a4"},
|
||||
{file = "pycryptodome-3.18.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:7a3d22c8ee63de22336679e021c7f2386f7fc465477d59675caa0e5706387944"},
|
||||
{file = "pycryptodome-3.18.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:78d863476e6bad2a592645072cc489bb90320972115d8995bcfbee2f8b209918"},
|
||||
{file = "pycryptodome-3.18.0-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:b6a610f8bfe67eab980d6236fdc73bfcdae23c9ed5548192bb2d530e8a92780e"},
|
||||
{file = "pycryptodome-3.18.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:422c89fd8df8a3bee09fb8d52aaa1e996120eafa565437392b781abec2a56e14"},
|
||||
{file = "pycryptodome-3.18.0-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:9ad6f09f670c466aac94a40798e0e8d1ef2aa04589c29faa5b9b97566611d1d1"},
|
||||
{file = "pycryptodome-3.18.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:53aee6be8b9b6da25ccd9028caf17dcdce3604f2c7862f5167777b707fbfb6cb"},
|
||||
{file = "pycryptodome-3.18.0-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:10da29526a2a927c7d64b8f34592f461d92ae55fc97981aab5bbcde8cb465bb6"},
|
||||
{file = "pycryptodome-3.18.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f21efb8438971aa16924790e1c3dba3a33164eb4000106a55baaed522c261acf"},
|
||||
{file = "pycryptodome-3.18.0-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4944defabe2ace4803f99543445c27dd1edbe86d7d4edb87b256476a91e9ffa4"},
|
||||
{file = "pycryptodome-3.18.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:51eae079ddb9c5f10376b4131be9589a6554f6fd84f7f655180937f611cd99a2"},
|
||||
{file = "pycryptodome-3.18.0-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:83c75952dcf4a4cebaa850fa257d7a860644c70a7cd54262c237c9f2be26f76e"},
|
||||
{file = "pycryptodome-3.18.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:957b221d062d5752716923d14e0926f47670e95fead9d240fa4d4862214b9b2f"},
|
||||
{file = "pycryptodome-3.18.0-cp35-abi3-win32.whl", hash = "sha256:795bd1e4258a2c689c0b1f13ce9684fa0dd4c0e08680dcf597cf9516ed6bc0f3"},
|
||||
{file = "pycryptodome-3.18.0-cp35-abi3-win_amd64.whl", hash = "sha256:b1d9701d10303eec8d0bd33fa54d44e67b8be74ab449052a8372f12a66f93fb9"},
|
||||
{file = "pycryptodome-3.18.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:cb1be4d5af7f355e7d41d36d8eec156ef1382a88638e8032215c215b82a4b8ec"},
|
||||
{file = "pycryptodome-3.18.0-pp27-pypy_73-win32.whl", hash = "sha256:fc0a73f4db1e31d4a6d71b672a48f3af458f548059aa05e83022d5f61aac9c08"},
|
||||
{file = "pycryptodome-3.18.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f022a4fd2a5263a5c483a2bb165f9cb27f2be06f2f477113783efe3fe2ad887b"},
|
||||
{file = "pycryptodome-3.18.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:363dd6f21f848301c2dcdeb3c8ae5f0dee2286a5e952a0f04954b82076f23825"},
|
||||
{file = "pycryptodome-3.18.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12600268763e6fec3cefe4c2dcdf79bde08d0b6dc1813887e789e495cb9f3403"},
|
||||
{file = "pycryptodome-3.18.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4604816adebd4faf8810782f137f8426bf45fee97d8427fa8e1e49ea78a52e2c"},
|
||||
{file = "pycryptodome-3.18.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:01489bbdf709d993f3058e2996f8f40fee3f0ea4d995002e5968965fa2fe89fb"},
|
||||
{file = "pycryptodome-3.18.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3811e31e1ac3069988f7a1c9ee7331b942e605dfc0f27330a9ea5997e965efb2"},
|
||||
{file = "pycryptodome-3.18.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f4b967bb11baea9128ec88c3d02f55a3e338361f5e4934f5240afcb667fdaec"},
|
||||
{file = "pycryptodome-3.18.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:9c8eda4f260072f7dbe42f473906c659dcbadd5ae6159dfb49af4da1293ae380"},
|
||||
{file = "pycryptodome-3.18.0.tar.gz", hash = "sha256:c9adee653fc882d98956e33ca2c1fb582e23a8af7ac82fee75bd6113c55a0413"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pycryptodomex"
|
||||
version = "3.18.0"
|
||||
@ -1447,6 +1570,50 @@ files = [
|
||||
[package.dependencies]
|
||||
typing-extensions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "pygments"
|
||||
version = "2.16.1"
|
||||
description = "Pygments is a syntax highlighting package written in Python."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"},
|
||||
{file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
plugins = ["importlib-metadata"]
|
||||
|
||||
[[package]]
|
||||
name = "Pyrogram"
|
||||
version = "2.0.106"
|
||||
description = "Elegant, modern and asynchronous Telegram MTProto API framework in Python for users and bots"
|
||||
optional = false
|
||||
python-versions = "~=3.7"
|
||||
files = [
|
||||
{file = "master.zip", hash = "sha256:722a4be0edc5002dcd05b462f333c860a790b67a1f62466b322377acd3985f77"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pyaes = "1.6.1"
|
||||
pysocks = "1.7.1"
|
||||
|
||||
[package.source]
|
||||
type = "url"
|
||||
url = "https://github.com/Dineshkarthik/pyrogram/archive/refs/heads/master.zip"
|
||||
|
||||
[[package]]
|
||||
name = "pysocks"
|
||||
version = "1.7.1"
|
||||
description = "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information."
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
files = [
|
||||
{file = "PySocks-1.7.1-py27-none-any.whl", hash = "sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299"},
|
||||
{file = "PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5"},
|
||||
{file = "PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "7.4.0"
|
||||
@ -1544,6 +1711,28 @@ text-unidecode = ">=1.3"
|
||||
[package.extras]
|
||||
unidecode = ["Unidecode (>=1.1.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "pyyaml"
|
||||
version = "5.3.1"
|
||||
description = "YAML parser and emitter for Python"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "PyYAML-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f"},
|
||||
{file = "PyYAML-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76"},
|
||||
{file = "PyYAML-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2"},
|
||||
{file = "PyYAML-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c"},
|
||||
{file = "PyYAML-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2"},
|
||||
{file = "PyYAML-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648"},
|
||||
{file = "PyYAML-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"},
|
||||
{file = "PyYAML-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf"},
|
||||
{file = "PyYAML-5.3.1-cp38-cp38-win32.whl", hash = "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97"},
|
||||
{file = "PyYAML-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee"},
|
||||
{file = "PyYAML-5.3.1-cp39-cp39-win32.whl", hash = "sha256:ad9c67312c84def58f3c04504727ca879cb0013b2517c85a9a253f0cb6380c0a"},
|
||||
{file = "PyYAML-5.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:6034f55dab5fea9e53f436aa68fa3ace2634918e8b5994d82f3621c04ff5ed2e"},
|
||||
{file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redis"
|
||||
version = "5.0.0"
|
||||
@ -1583,6 +1772,39 @@ urllib3 = ">=1.21.1,<3"
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
|
||||
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
|
||||
|
||||
[[package]]
|
||||
name = "rich"
|
||||
version = "12.5.1"
|
||||
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
|
||||
optional = false
|
||||
python-versions = ">=3.6.3,<4.0.0"
|
||||
files = [
|
||||
{file = "rich-12.5.1-py3-none-any.whl", hash = "sha256:2eb4e6894cde1e017976d2975ac210ef515d7548bc595ba20e195fb9628acdeb"},
|
||||
{file = "rich-12.5.1.tar.gz", hash = "sha256:63a5c5ce3673d3d5fbbf23cd87e11ab84b6b451436f1b7f19ec54b6bc36ed7ca"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
commonmark = ">=0.9.0,<0.10.0"
|
||||
pygments = ">=2.6.0,<3.0.0"
|
||||
|
||||
[package.extras]
|
||||
jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "ruamel-yaml"
|
||||
version = "0.17.21"
|
||||
description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order"
|
||||
optional = false
|
||||
python-versions = ">=3"
|
||||
files = [
|
||||
{file = "ruamel.yaml-0.17.21-py3-none-any.whl", hash = "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7"},
|
||||
{file = "ruamel.yaml-0.17.21.tar.gz", hash = "sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["ryd"]
|
||||
jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "s3transfer"
|
||||
version = "0.6.2"
|
||||
@ -1684,6 +1906,22 @@ files = [
|
||||
[package.extras]
|
||||
test = ["pytest"]
|
||||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "68.2.2"
|
||||
description = "Easily download, build, install, upgrade, and uninstall Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "setuptools-68.2.2-py3-none-any.whl", hash = "sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a"},
|
||||
{file = "setuptools-68.2.2.tar.gz", hash = "sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
|
||||
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
|
||||
testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
|
||||
|
||||
[[package]]
|
||||
name = "six"
|
||||
version = "1.16.0"
|
||||
@ -1745,6 +1983,71 @@ files = [
|
||||
{file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tgcrypto"
|
||||
version = "1.2.5"
|
||||
description = "Fast and Portable Cryptography Extension Library for Pyrogram"
|
||||
optional = false
|
||||
python-versions = "~=3.7"
|
||||
files = [
|
||||
{file = "TgCrypto-1.2.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4507102377002966f35f2481830b7529e00c9bbff8c7d1e09634f984af801675"},
|
||||
{file = "TgCrypto-1.2.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:38fe25c0d79b41d7a89caba2a78dea0358e17ca73b033cefd16abed680685829"},
|
||||
{file = "TgCrypto-1.2.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c035bf8ef89846f67e77e82ea85c089b6ea30631b32e8ac1a6511b9be52ab065"},
|
||||
{file = "TgCrypto-1.2.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f594e2680daf20dbac6bf56862f567ddc3cc8d6a19757ed07faa8320ff7acee4"},
|
||||
{file = "TgCrypto-1.2.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8723a16076e229ffdf537fdb5e638227d10f44ca43e6939db1eab524de6eaed7"},
|
||||
{file = "TgCrypto-1.2.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c1c8d974b8b2d7132364b6f0f6712b92bfe47ab9c5dcee25c70327ff68d22d95"},
|
||||
{file = "TgCrypto-1.2.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:89d9c143a1fcdb2562a4aa887152abbe9253e1979d7bebef2b489148e0bbe086"},
|
||||
{file = "TgCrypto-1.2.5-cp310-cp310-win32.whl", hash = "sha256:aa4bc1d11d4a90811c162abd45a5981f171679d1b5bd0322cd7ccd16447366a2"},
|
||||
{file = "TgCrypto-1.2.5-cp310-cp310-win_amd64.whl", hash = "sha256:39145103614c5e38fe938549742d355920f4a0778fa8259eb69c0c85ba4b1d28"},
|
||||
{file = "TgCrypto-1.2.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:59597cdb1c87eb1184088563d20b42a8f2e431e9334fed64926079044ad2a4af"},
|
||||
{file = "TgCrypto-1.2.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1283337ae75b02406dd700377b8b783e70033b548492517df6e6c4156b0ed69c"},
|
||||
{file = "TgCrypto-1.2.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1735437df0023a40e5fdd95e6b09ce806ec8f2cd2f8879023818840dfae60cab"},
|
||||
{file = "TgCrypto-1.2.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfa17a20206532c6d2442c9d7a7f6434120bd75896ad9a3e9b9277477afa084f"},
|
||||
{file = "TgCrypto-1.2.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48da3674474839e5619e7430ff1f98aed9f55369f3cfaef7f65511852869572e"},
|
||||
{file = "TgCrypto-1.2.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b49e982e5b156be821a5235bd9102c00dc506a58607e2c8bd50ac872724a951f"},
|
||||
{file = "TgCrypto-1.2.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9d9f13586065a6d86d05c16409054033a84be208acee29b49f6f194e27b08642"},
|
||||
{file = "TgCrypto-1.2.5-cp311-cp311-win32.whl", hash = "sha256:10dd3870aecb1a783c6eafd3b164b2149dbc93a9ee13feb7e6f5c58f87c24cd0"},
|
||||
{file = "TgCrypto-1.2.5-cp311-cp311-win_amd64.whl", hash = "sha256:a1beec47d6af8b509af7cf266e30f7703208076076594714005b42d2c25225b3"},
|
||||
{file = "TgCrypto-1.2.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c133ddc95ae9c6cd6ad742c4b8c30191214db8dc724268bee59a339e22b2028b"},
|
||||
{file = "TgCrypto-1.2.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f6537f6af3d80be67bd2625a0990ee88c6ae58d33bdb88d99591bd6e97ee7a0"},
|
||||
{file = "TgCrypto-1.2.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdebbd9cffd10c42a2f60886dcab0272ddd38330d0cf7ccf026230b826573f59"},
|
||||
{file = "TgCrypto-1.2.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:45683659ec6475ee8ff60e12167ec19aacfd7527decafe446434fa1a7e6760a7"},
|
||||
{file = "TgCrypto-1.2.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:eafad246fd9aa63ff709a6e8c905c24fd7520ef96e33a2c3e1ccdb4fb2b2f331"},
|
||||
{file = "TgCrypto-1.2.5-cp37-cp37m-win32.whl", hash = "sha256:1445217d22101946d38ee7d628cdb3de92db4eb130183a22030c07d7888f21b0"},
|
||||
{file = "TgCrypto-1.2.5-cp37-cp37m-win_amd64.whl", hash = "sha256:b7e8402fe4023dc9666c0bc1b30fcf0d98a294e48d35f311a3eadfe105af04d4"},
|
||||
{file = "TgCrypto-1.2.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:56e1ec34e75fa2e3dcf7f74f1017d8e16c1eb8a8e031eaaa06c57f836e0d3bcc"},
|
||||
{file = "TgCrypto-1.2.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ca8814d6cc412775a43a021fce2d23e83a5336e9e9f38d0998d821cdf55c1d50"},
|
||||
{file = "TgCrypto-1.2.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bb82e53f20ce5653573832f3c05fa525cc1769bdd685408b19f26d82d5e9001b"},
|
||||
{file = "TgCrypto-1.2.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0133936eac63cc9529b497d759b7d0ca21e491bb42481b40b603ee63bb8c10b7"},
|
||||
{file = "TgCrypto-1.2.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76fe0a3ad838dcf300ac88233cbffc2ad63478eb0ae9fa671694e184d88ec1cd"},
|
||||
{file = "TgCrypto-1.2.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7070c6e063befb6d04eab46e8b1ffbee47a497971be11496d23837fc007e7685"},
|
||||
{file = "TgCrypto-1.2.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ed53d0c4a5e6f75d4b1cab17535afe210cd3120fb88f44a8c4562d43c2a3bb16"},
|
||||
{file = "TgCrypto-1.2.5-cp38-cp38-win32.whl", hash = "sha256:f7ec9f0a571fcc38fbee224943ed9918123f752ac19bae5c195d8322f5b20fab"},
|
||||
{file = "TgCrypto-1.2.5-cp38-cp38-win_amd64.whl", hash = "sha256:362ab28fc75e6b066e5bb15fb5296f75f4238d6c1cbcaaa1e5756cd5c168b74b"},
|
||||
{file = "TgCrypto-1.2.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7885a75db09ce8bdba42d2c332085bfe314f232541a729808c7507ffa261ff9a"},
|
||||
{file = "TgCrypto-1.2.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0d28aa317364a5c27317fe97a48267aa1c65c9aaf589909e97489ebe82a714e3"},
|
||||
{file = "TgCrypto-1.2.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:940974e19044dc65bcf7b9c5255173b896dff010142f3833047dc55d59cde21c"},
|
||||
{file = "TgCrypto-1.2.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:457c657dd10ffb4bbbb007132a0f6a7bee5080176a98c51f285fedf636b624cb"},
|
||||
{file = "TgCrypto-1.2.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:539bdc6b9239fb6a6b134591a998dc7f50d4dcc4fed861f80540682acc0c3802"},
|
||||
{file = "TgCrypto-1.2.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4d70d5517d64ca952896b726d22c8a66594e6f6259ee2cb4fa134c02d0e8c3e0"},
|
||||
{file = "TgCrypto-1.2.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:90b6337d3ae4348ed14f89dd2ebf7011fa63d67a48c8a98d955a1e392176c60a"},
|
||||
{file = "TgCrypto-1.2.5-cp39-cp39-win32.whl", hash = "sha256:37c4b9be82716fbc6d2b123caef448eee28683888803db075d842327766f7624"},
|
||||
{file = "TgCrypto-1.2.5-cp39-cp39-win_amd64.whl", hash = "sha256:6e96b3a478fae977228c5750194c20a18cde402bbbea6593de424f84d4a8893b"},
|
||||
{file = "TgCrypto-1.2.5-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4d93686e6254eb0a32a0a60e849b41a867a2770b27b48f978fd391dce2b83aeb"},
|
||||
{file = "TgCrypto-1.2.5-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b7fe81fad7c64479c83f31fc10e79fb20a114bca414e5e17f5e0c8b363153f8"},
|
||||
{file = "TgCrypto-1.2.5-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ace308f5842a0d6c04fc1ae92cb4320b4b13bfc711031c1c18d9124697685ba0"},
|
||||
{file = "TgCrypto-1.2.5-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:36a570ecd12a428222b10ea8b5a8e0d83ea8750e4de1d5ea53d068b84341b450"},
|
||||
{file = "TgCrypto-1.2.5-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ba9e067fd9751b3bbd7c979210431000e44f70001d921237e9c4672bf30f07bc"},
|
||||
{file = "TgCrypto-1.2.5-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80e2414f1a95087d7e46fb54cb387df66424c4ec156fb1a8d8e1d4aa38eb65cf"},
|
||||
{file = "TgCrypto-1.2.5-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66c5b5bf701b5efc3e4c5d83439d767a3dd48f17a1d840eda6b4d1918844a8f9"},
|
||||
{file = "TgCrypto-1.2.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5d27d6c414eb4775022b05fdd571090bfd92854115e86184ac1832060fbaa510"},
|
||||
{file = "TgCrypto-1.2.5-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9b0a088ff2e05b6bbe891da936f62b99bd85202b2b9f4f57f71a408490dd518c"},
|
||||
{file = "TgCrypto-1.2.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f245895c7d518342089d15b5dca3cee9ffa5a0f3534db9d5a930f6a27dff4adf"},
|
||||
{file = "TgCrypto-1.2.5-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7dbf607d645c39a577a0f8571039d11ddd2dcdf9656465be75f9e0f540472444"},
|
||||
{file = "TgCrypto-1.2.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d6b0c2dc84e632ce7b3d0b767cfe20967e557ad7d71ea5dbd7df2dd544323181"},
|
||||
{file = "TgCrypto-1.2.5.tar.gz", hash = "sha256:9bc2cac6fb9a12ef5b08f3dd500174fe374d89b660cce981f57e3138559cb682"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.7.1"
|
||||
@ -1869,6 +2172,23 @@ files = [
|
||||
{file = "websockets-11.0.3.tar.gz", hash = "sha256:88fc51d9a26b10fc331be344f1781224a375b78488fc343620184e95a4b27016"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "werkzeug"
|
||||
version = "2.2.2"
|
||||
description = "The comprehensive WSGI web application library."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "Werkzeug-2.2.2-py3-none-any.whl", hash = "sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5"},
|
||||
{file = "Werkzeug-2.2.2.tar.gz", hash = "sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
MarkupSafe = ">=2.1.1"
|
||||
|
||||
[package.extras]
|
||||
watchdog = ["watchdog"]
|
||||
|
||||
[[package]]
|
||||
name = "win32-setctime"
|
||||
version = "1.1.0"
|
||||
@ -2007,4 +2327,4 @@ websockets = "*"
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.11"
|
||||
content-hash = "1b704294868cb5a6ebb4ce3b9ffb373899bec9ce312ad94810bc1fe0d21ffb7d"
|
||||
content-hash = "272fe31fba150b0b0fcca1b7d60f706dc2a05ea730ef19e34ccb8e5524f47d66"
|
||||
|
@ -24,7 +24,18 @@ lxml = "^4.9.3"
|
||||
minio = "^7.1.16"
|
||||
aiogram = "3.0"
|
||||
pydantic = "^2.3.0"
|
||||
loguru = "^0.7.2"
|
||||
loguru = "0.6.0"
|
||||
setuptools = "^68.2.2"
|
||||
pyrogram = {url = "https://github.com/Dineshkarthik/pyrogram/archive/refs/heads/master.zip"}
|
||||
pyyaml = "5.3.1"
|
||||
rich = "12.5.1"
|
||||
tgcrypto = "1.2.5"
|
||||
werkzeug = "2.2.2"
|
||||
flask = "2.2.2"
|
||||
ply = "3.11"
|
||||
ruamel-yaml = "0.17.21"
|
||||
flask-login = "0.6.2"
|
||||
pycryptodome = "3.18.0"
|
||||
|
||||
|
||||
[build-system]
|
||||
|
@ -1,10 +1,12 @@
|
||||
import asyncio
|
||||
import concurrent.futures as pool
|
||||
import subprocess
|
||||
import pyrogram
|
||||
import traceback
|
||||
|
||||
from functools import partial
|
||||
import traceback
|
||||
from urllib.parse import urlparse
|
||||
from loguru import logger
|
||||
|
||||
from src.core.async_queue import AsyncQueue
|
||||
from src.core.rabbitmq import get_messages, publish_message_with_task_done
|
||||
@ -12,15 +14,17 @@ from src.core.redis_client import RedisClient
|
||||
from src.core.result import Result, ResultTypeEnum
|
||||
from src.exceptions.download_exceptions import FileAlreadyExistException, SiteNotImplementedException
|
||||
from src.parsers.MyMail.my_mail_parser import MyMailParser
|
||||
from src.parsers.Telegram.telegram_media_downloader.media_downloader import app, _check_config
|
||||
from src.parsers.Yappy.yappy_parser import YappyParser
|
||||
from src.parsers.base_parser import BaseParser
|
||||
from loguru import logger
|
||||
from src.parsers.parser_mapping import get_parser
|
||||
from src.parsers.Telegram.telegram_media_downloader.telegram_parser import TelegramParser
|
||||
|
||||
|
||||
class MasterService:
|
||||
def __init__(self):
|
||||
self.loop = asyncio.get_event_loop()
|
||||
|
||||
self.MAX_EXECUTOR_WORKERS = 8
|
||||
self.executor = pool.ProcessPoolExecutor(max_workers=self.MAX_EXECUTOR_WORKERS,
|
||||
initializer=executor_initializer)
|
||||
@ -63,9 +67,27 @@ class MasterService:
|
||||
|
||||
@staticmethod
|
||||
def video_download(video_params: dict):
|
||||
downloader: BaseParser | YappyParser | MyMailParser = MasterService.get_parser(video_params)
|
||||
result = downloader.video_download()
|
||||
return result
|
||||
downloader: BaseParser | YappyParser | MyMailParser | TelegramParser = MasterService.get_parser(video_params)
|
||||
match downloader:
|
||||
case TelegramParser():
|
||||
if _check_config():
|
||||
tg_client = pyrogram.Client(
|
||||
"media_downloader",
|
||||
api_id=app.api_id,
|
||||
api_hash=app.api_hash,
|
||||
proxy=app.proxy,
|
||||
workdir=app.session_file_path,
|
||||
)
|
||||
app.pre_run()
|
||||
app.is_running = True
|
||||
tg_client.start()
|
||||
result = downloader.video_download(client=tg_client)
|
||||
return result
|
||||
case _:
|
||||
result = downloader.video_download()
|
||||
return result
|
||||
|
||||
|
||||
|
||||
@staticmethod
|
||||
def get_parser(params: dict):
|
||||
|
71
src/parsers/Telegram/telegram_media_downloader/.gitignore
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
.idea/
|
||||
*.pyc
|
||||
*.pid
|
||||
*.cfg
|
||||
*.db
|
||||
*.env
|
||||
.DS_Store
|
||||
.cache/
|
||||
.mypy_cache/
|
||||
.coverage
|
||||
settings.json
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
.pytest_cache
|
||||
.python-version
|
||||
env/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
logs/
|
||||
parts/
|
||||
sdist/
|
||||
share/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
*.ipynb
|
||||
|
||||
# virtualenv
|
||||
.venv
|
||||
venv/
|
||||
ENV/
|
||||
bin/
|
||||
include/
|
||||
pip-selfcheck.json
|
||||
lib64
|
||||
|
||||
#Telegram Sessions
|
||||
*.session
|
||||
*.session-journal
|
||||
|
||||
#Downloaded documents
|
||||
documents/
|
||||
audio/
|
||||
document/
|
||||
photo/
|
||||
voice/
|
||||
video/
|
||||
video_note/
|
||||
parser.out
|
||||
parsetab.py
|
||||
local_test/
|
||||
.vscode
|
||||
TODO.md
|
||||
log/
|
||||
temp/
|
||||
data.yaml
|
||||
*.scss
|
||||
*.css.map
|
@ -0,0 +1,76 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at tangyoha@outlook.com. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
199
src/parsers/Telegram/telegram_media_downloader/CONTRIBUTING.md
Normal file
@ -0,0 +1,199 @@
|
||||
## Contributing
|
||||
|
||||
First off, thank you for considering contributing to Telegram Media Downloader. It's people like you that make telegram-media-downloader such a great tool.
|
||||
Please take a moment to review this document in order to make the contribution process easy and effective for everyone involved.
|
||||
|
||||
### Where do I go from here?
|
||||
|
||||
If you've noticed a bug or have a feature request, [make one](https://github.com/tangyoha/telegram_media_downloader/issues)! It's generally best if you get confirmation of your bug or approval for your feature request this way before starting to code.
|
||||
|
||||
If you have a general question about telegram-media-downloader, you can ask it on [Discussion](https://github.com/tangyoha/telegram_media_downloader/discussions) under `Q&A` category and any ideas/suggestions goes under `Ideas` category, the issue tracker is only for bugs and feature requests.
|
||||
|
||||
### Fork & create a branch
|
||||
|
||||
If this is something you think you can fix, then [fork telegram-media-downloader](https://help.github.com/articles/fork-a-repo) and create a branch with a descriptive name.
|
||||
|
||||
A good branch name would be (where issue #52 is the ticket you're working on):
|
||||
|
||||
```sh
|
||||
git checkout -b 52-fix-expired-file-reference
|
||||
```
|
||||
|
||||
### For new Contributors
|
||||
|
||||
If you never created a pull request before, welcome [Here is a great tutorial](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github) on how to send one :)
|
||||
|
||||
1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork, and configure the remotes:
|
||||
```sh
|
||||
# Clone your fork of the repo into the current directory
|
||||
git clone https://github.com/<your-username>/<repo-name>
|
||||
# Navigate to the newly cloned directory
|
||||
cd <repo-name>
|
||||
# Install dependencies
|
||||
make dev_install
|
||||
# Assign the original repo to a remote called "upstream"
|
||||
git remote add upstream https://github.com/Dineshkkarthik/<repo-name>
|
||||
```
|
||||
|
||||
2. If you cloned a while ago, get the latest changes from upstream:
|
||||
```sh
|
||||
git checkout master
|
||||
git pull upstream master
|
||||
```
|
||||
|
||||
3. Create a new branch (off the main project master branch) to contain your feature, change, or fix based on the branch name convention described above:
|
||||
```sh
|
||||
git checkout -b <branch-name>
|
||||
```
|
||||
|
||||
4. Make sure to update, or add to the tests when appropriate. Patches and features will not be accepted without tests. Run `make test` to check that all tests pass after you've made changes.
|
||||
|
||||
5. If you added or changed a feature, make sure to document it accordingly in the `README.md` file.
|
||||
|
||||
6. Push your branch up to your fork:
|
||||
```sh
|
||||
git push origin <branch-name>
|
||||
```
|
||||
|
||||
7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) with a clear title and description.
|
||||
|
||||
|
||||
### Coding Standards
|
||||
|
||||
#### Python style
|
||||
|
||||
Please follow these coding standards when writing code for inclusion in telegram-media-downloader.
|
||||
|
||||
Telegram-media-downloader follows the [PEP8](https://www.python.org/dev/peps/pep-0008/) standard and uses [Black](https://black.readthedocs.io/en/stable/) and [Pylint](https://pylint.pycqa.org/en/latest/) to ensure a consistent code format throughout the project.
|
||||
|
||||
[Continuous Integration](https://github.com/tangyoha/telegram_media_downloader/actions) using GitHub Actions will run those tools and report any stylistic errors in your code. Therefore, it is helpful before submitting code to run the check yourself:
|
||||
```sh
|
||||
black media_downloader.py utils
|
||||
```
|
||||
to auto-format your code. Additionally, many editors have plugins that will apply `black` as you edit files.
|
||||
|
||||
Writing good code is not just about what you write. It is also about _how_ you write it. During [Continuous Integration](https://github.com/tangyoha/telegram_media_downloader/actions) testing, several tools will be run to check your code for stylistic errors. Generating any warnings will cause the test to fail. Thus, good style is a requirement for submitting code to telegram-media-downloader.
|
||||
|
||||
This is already added in the repo to help contributors verify their changes before contributing them to the project:
|
||||
```sh
|
||||
make style_check
|
||||
```
|
||||
|
||||
#### Type hints
|
||||
|
||||
Telegram-media-downloader strongly encourages the use of [**PEP 484**](https://www.python.org/dev/peps/pep-0484) style type hints. New development should contain type hints and pull requests to annotate existing code are accepted as well!
|
||||
|
||||
Types imports should follow the `from typing import ...` convention. So rather than
|
||||
```py
|
||||
import typing
|
||||
|
||||
primes: typing.List[int] = []
|
||||
```
|
||||
You should write
|
||||
```py
|
||||
from typing import List, Optional, Union
|
||||
|
||||
primes: List[int] = []
|
||||
```
|
||||
|
||||
`Optional` should be used where applicable, so instead of
|
||||
```py
|
||||
maybe_primes: List[Union[int, None]] = []
|
||||
```
|
||||
You should write
|
||||
```py
|
||||
maybe_primes: List[Optional[int]] = []
|
||||
```
|
||||
|
||||
#### Validating type hints
|
||||
|
||||
telegram-media-downloader uses [mypy](http://mypy-lang.org/) to statically analyze the code base and type hints. After making any change you can ensure your type hints are correct by running
|
||||
```sh
|
||||
make static_type_check
|
||||
```
|
||||
|
||||
#### Docstrings and standards
|
||||
|
||||
A Python docstring is a string used to document a Python module, class, function or method, so programmers can understand what it does without having to read the details of the implementation.
|
||||
|
||||
The next example gives an idea of what a docstring looks like:
|
||||
```py
|
||||
def add(num1: int, num2: int) -> int:
|
||||
"""
|
||||
Add up two integer numbers.
|
||||
|
||||
This function simply wraps the ``+`` operator, and does not
|
||||
do anything interesting, except for illustrating what
|
||||
the docstring of a very simple function looks like.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
num1: int
|
||||
First number to add.
|
||||
num2: int
|
||||
Second number to add.
|
||||
|
||||
Returns
|
||||
-------
|
||||
int
|
||||
The sum of ``num1`` and ``num2``.
|
||||
|
||||
See Also
|
||||
--------
|
||||
subtract : Subtract one integer from another.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> add(2, 2)
|
||||
4
|
||||
>>> add(25, 0)
|
||||
25
|
||||
>>> add(10, -10)
|
||||
0
|
||||
"""
|
||||
return num1 + num2
|
||||
```
|
||||
Some standards regarding docstrings exist, which make them easier to read, and allow them be easily exported to other formats such as html or pdf.
|
||||
|
||||
### Commit Message
|
||||
|
||||
telegram-media-downloader uses a convention for commit message prefixes and layout. Here are some common prefixes along with general guidelines for when to use them:
|
||||
```
|
||||
<prefix>: <subject>
|
||||
<-- OPTIONAL -->
|
||||
<BLANK LINE>
|
||||
<body>
|
||||
```
|
||||
|
||||
#### Prefix:
|
||||
|
||||
Must be one of the following:
|
||||
- **add**: Adding a new file
|
||||
- **ci**: Changes to CI configuration files and scripts (example: files inside `.github` folder)
|
||||
- **clean**: Code cleanup
|
||||
- **docs**: Additions/updates to documentation
|
||||
- **enh**: Enhancement, new functionality
|
||||
- **fix**: Bug fix
|
||||
- **perf**: A code change that improves performance
|
||||
- **refactor**: A code change that neither fixes a bug nor adds a feature
|
||||
- **style**: Changes that do not affect the meaning of the code (white-space, formatting, etc)
|
||||
- **test**: Additions/updates to tests
|
||||
- **type**: Type annotations
|
||||
|
||||
#### Subject:
|
||||
|
||||
Please reference the relevant GitHub issues in your commit message using #1234.
|
||||
- a subject line with `< 80` chars.
|
||||
- summary in present tense.
|
||||
- not capitalized.
|
||||
- no period at the end.
|
||||
|
||||
#### Commit Message Body
|
||||
|
||||
Just as in the summary, use the imperative, present tense.
|
||||
|
||||
Explain the motivation for the change in the commit message body. This commit message should explain _why_ you are making the change. You can include a comparison of the previous behavior with the new behavior in order to illustrate the impact of the change.
|
||||
|
||||
### Code of Conduct
|
||||
|
||||
As a contributor, you can help us keep the community open and inclusive. Please read and follow our [Code of Conduct](https://github.com/tangyoha/telegram_media_downloader/blob/master/CODE_OF_CONDUCT.md).
|
25
src/parsers/Telegram/telegram_media_downloader/Dockerfile
Normal file
@ -0,0 +1,25 @@
|
||||
FROM python:3.11.2-alpine As compile-image
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY requirements.txt /app/
|
||||
|
||||
RUN apk add --no-cache --virtual .build-deps gcc musl-dev \
|
||||
&& pip install --trusted-host pypi.python.org -r requirements.txt \
|
||||
&& apk del .build-deps && rm -rf requirements.txt
|
||||
|
||||
RUN apk add --no-cache rclone
|
||||
|
||||
FROM python:3.11.2-alpine As runtime-image
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=tangyoha/telegram_media_downloader_compile:latest /usr/bin/rclone /app/rclone/rclone
|
||||
|
||||
COPY --from=tangyoha/telegram_media_downloader_compile:latest /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
|
||||
|
||||
COPY config.yaml data.yaml setup.py media_downloader.py /app/
|
||||
COPY module /app/module
|
||||
COPY utils /app/utils
|
||||
|
||||
CMD ["python", "media_downloader.py"]
|
21
src/parsers/Telegram/telegram_media_downloader/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Dineshkarthik R
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
26
src/parsers/Telegram/telegram_media_downloader/Makefile
Normal file
@ -0,0 +1,26 @@
|
||||
TEST_ARTIFACTS ?= /tmp/coverage
|
||||
|
||||
.PHONY: install dev_install static_type_check pylint style_check test
|
||||
|
||||
install:
|
||||
python3 -m pip install --upgrade pip setuptools
|
||||
python3 -m pip install -r requirements.txt
|
||||
|
||||
dev_install: install
|
||||
python3 -m pip install -r dev-requirements.txt
|
||||
|
||||
static_type_check:
|
||||
mypy media_downloader.py utils module --ignore-missing-imports
|
||||
|
||||
pylint:
|
||||
pylint media_downloader.py utils module -r y
|
||||
|
||||
style_check: static_type_check pylint
|
||||
|
||||
test:
|
||||
py.test --cov media_downloader --doctest-modules \
|
||||
--cov utils \
|
||||
--cov-report term-missing \
|
||||
--cov-report html:${TEST_ARTIFACTS} \
|
||||
--junit-xml=${TEST_ARTIFACTS}/media-downloader.xml \
|
||||
tests/
|
286
src/parsers/Telegram/telegram_media_downloader/README.md
Normal file
@ -0,0 +1,286 @@
|
||||
|
||||
<h1 align="center">Telegram Media Downloader</h1>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/tangyoha/telegram_media_downloader/actions"><img alt="Unittest" src="https://github.com/tangyoha/telegram_media_downloader/workflows/Unittest/badge.svg"></a>
|
||||
<a href="https://codecov.io/gh/tangyoha/telegram_media_downloader"><img alt="Coverage Status" src="https://codecov.io/gh/tangyoha/telegram_media_downloader/branch/master/graph/badge.svg"></a>
|
||||
<a href="https://github.com/tangyoha/telegram_media_downloader/blob/master/LICENSE"><img alt="License: MIT" src="https://black.readthedocs.io/en/stable/_static/license.svg"></a>
|
||||
<a href="https://github.com/python/black"><img alt="Code style: black" src="https://img.shields.io/badge/code%20style-black-000000.svg"></a>
|
||||
<a href="https://github.com/tangyoha/telegram_media_downloader/releases">
|
||||
<img alt="Code style: black" src="https://img.shields.io/github/v/release/tangyoha/telegram_media_downloader?display_name=tag"></a>
|
||||
</p>
|
||||
|
||||
<h3 align="center">
|
||||
<a href="./README_CN.md">中文</a><span> · </span>
|
||||
<a href="https://github.com/tangyoha/telegram_media_downloader/discussions/categories/ideas">Feature request</a>
|
||||
<span> · </span>
|
||||
<a href="https://github.com/tangyoha/telegram_media_downloader/issues">Report a bug</a>
|
||||
<span> · </span>
|
||||
Support: <a href="https://github.com/tangyoha/telegram_media_downloader/discussions">Discussions</a>
|
||||
<span> & </span>
|
||||
<a href="https://t.me/TeegramMediaDownload">Telegram Community</a>
|
||||
</h3>
|
||||
|
||||
## Overview
|
||||
> Support two default running
|
||||
|
||||
* The robot is running, and the command `download` or `forward` is issued from the robot
|
||||
|
||||
* Download as a one-time download tool
|
||||
|
||||
### UI
|
||||
|
||||
#### Web page
|
||||
|
||||
> After running, open a browser and visit `localhost:5000`
|
||||
> If it is a remote machine, you need to configure web_host: 0.0.0.0
|
||||
|
||||
|
||||
<img alt="Code style: black" style="width:100%; high:60%;" src="./screenshot/web_ui.gif"/>
|
||||
|
||||
### Robot
|
||||
|
||||
> Need to configure bot_token, please refer to [Documentation](https://github.com/tangyoha/telegram_media_downloader/wiki/How-to-Download-Using-Robots)
|
||||
|
||||
<img alt="Code style: black" style="width:60%; high:30%; " src="./screenshot/bot.gif"/>
|
||||
|
||||
### Support
|
||||
|
||||
| Category | Support |
|
||||
| -------------------- | ------------------------------------------------ |
|
||||
| Language | `Python 3.7` and above |
|
||||
| Download media types | audio, document, photo, video, video_note, voice |
|
||||
|
||||
### Version release plan
|
||||
|
||||
* [v2.2.0](https://github.com/tangyoha/telegram_media_downloader/issues/2)
|
||||
|
||||
## Installation
|
||||
|
||||
For *nix os distributions with `make` availability
|
||||
|
||||
```sh
|
||||
git clone https://github.com/tangyoha/telegram_media_downloader.git
|
||||
cd telegram_media_downloader
|
||||
make install
|
||||
```
|
||||
|
||||
For Windows which doesn't have `make` inbuilt
|
||||
|
||||
```sh
|
||||
git clone https://github.com/tangyoha/telegram_media_downloader.git
|
||||
cd telegram_media_downloader
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
|
||||
## Docker
|
||||
> For more detailed installation tutorial, please check the wiki
|
||||
|
||||
Make sure you have **docker** and **docker-compose** installed
|
||||
```sh
|
||||
docker pull tangyoha/telegram_media_downloader:latest
|
||||
mkdir -p ~/app && mkdir -p ~/app/log/ && cd ~/app
|
||||
wget https://raw.githubusercontent.com/tangyoha/telegram_media_downloader/master/docker-compose.yaml -O docker-compose.yaml
|
||||
wget https://raw.githubusercontent.com/tangyoha/telegram_media_downloader/master/config.yaml -O config.yaml
|
||||
wget https://raw.githubusercontent.com/tangyoha/telegram_media_downloader/master/data.yaml -O data.yaml
|
||||
# vi config.yaml and docker-compose.yaml
|
||||
vi config.yaml
|
||||
|
||||
# The first time you need to start the foreground
|
||||
# enter your phone number and code, then exit(ctrl + c)
|
||||
docker-compose run --rm telegram_media_downloader
|
||||
|
||||
# After performing the above operations, all subsequent startups will start in the background
|
||||
docker-compose up -d
|
||||
|
||||
# Upgrade
|
||||
docker pull tangyoha/telegram_media_downloader:latest
|
||||
cd ~/app
|
||||
docker-compose down
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
## Upgrade installation
|
||||
|
||||
```sh
|
||||
cd telegram_media_downloader
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
All the configurations are passed to the Telegram Media Downloader via `config.yaml` file.
|
||||
|
||||
**Getting your API Keys:**
|
||||
The very first step requires you to obtain a valid Telegram API key (API id/hash pair):
|
||||
|
||||
1. Visit [https://my.telegram.org/apps](https://my.telegram.org/apps) and log in with your Telegram Account.
|
||||
2. Fill out the form to register a new Telegram application.
|
||||
3. Done! The API key consists of two parts: **api_id** and **api_hash**.
|
||||
|
||||
**Getting chat id:**
|
||||
|
||||
**1. Using web telegram:**
|
||||
|
||||
1. Open <https://web.telegram.org/?legacy=1#/im>
|
||||
|
||||
2. Now go to the chat/channel and you will see the URL as something like
|
||||
- `https://web.telegram.org/?legacy=1#/im?p=u853521067_2449618633394` here `853521067` is the chat id.
|
||||
- `https://web.telegram.org/?legacy=1#/im?p=@somename` here `somename` is the chat id.
|
||||
- `https://web.telegram.org/?legacy=1#/im?p=s1301254321_6925449697188775560` here take `1301254321` and add `-100` to the start of the id => `-1001301254321`.
|
||||
- `https://web.telegram.org/?legacy=1#/im?p=c1301254321_6925449697188775560` here take `1301254321` and add `-100` to the start of the id => `-1001301254321`.
|
||||
|
||||
**2. Using bot:**
|
||||
|
||||
1. Use [@username_to_id_bot](https://t.me/username_to_id_bot) to get the chat_id of
|
||||
- almost any telegram user: send username to the bot or just forward their message to the bot
|
||||
- any chat: send chat username or copy and send its joinchat link to the bot
|
||||
- public or private channel: same as chats, just copy and send to the bot
|
||||
- id of any telegram bot
|
||||
|
||||
### config.yaml
|
||||
|
||||
```yaml
|
||||
api_hash: your_api_hash
|
||||
api_id: your_api_id
|
||||
chat:
|
||||
- chat_id: telegram_chat_id
|
||||
last_read_message_id: 0
|
||||
download_filter: message_date >= 2022-12-01 00:00:00 and message_date <= 2023-01-17 00:00:00
|
||||
- chat_id: telegram_chat_id_2
|
||||
last_read_message_id: 0
|
||||
# note we remove ids_to_retry to data.yaml
|
||||
ids_to_retry: []
|
||||
media_types:
|
||||
- audio
|
||||
- document
|
||||
- photo
|
||||
- video
|
||||
- voice
|
||||
- animation #gif
|
||||
file_formats:
|
||||
audio:
|
||||
- all
|
||||
document:
|
||||
- pdf
|
||||
- epub
|
||||
video:
|
||||
- mp4
|
||||
save_path: D:\telegram_media_downloader
|
||||
file_path_prefix:
|
||||
- chat_title
|
||||
- media_datetime
|
||||
disable_syslog:
|
||||
- INFO
|
||||
upload_drive:
|
||||
# required
|
||||
enable_upload_file: true
|
||||
# required
|
||||
remote_dir: drive:/telegram
|
||||
# required
|
||||
upload_adapter: rclone
|
||||
# option,when config upload_adapter rclone then this config are required
|
||||
rclone_path: D:\rclone\rclone.exe
|
||||
# option
|
||||
before_upload_file_zip: True
|
||||
# option
|
||||
after_upload_file_delete: True
|
||||
hide_file_name: true
|
||||
file_name_prefix:
|
||||
- message_id
|
||||
- file_name
|
||||
file_name_prefix_split: ' - '
|
||||
max_download_task: 5
|
||||
web_host: 127.0.0.1
|
||||
web_port: 5000
|
||||
language: EN
|
||||
web_login_secret: 123
|
||||
```
|
||||
|
||||
- **api_hash** - The api_hash you got from telegram apps
|
||||
- **api_id** - The api_id you got from telegram apps
|
||||
- **bot_token** - Your bot token
|
||||
- **chat** - Chat list
|
||||
- `chat_id` - The id of the chat/channel you want to download media. Which you get from the above-mentioned steps.
|
||||
- `download_filter` - Download filter, see [How to use Filter](https://github.com/tangyoha/telegram_media_downloader/wiki/How-to-use-Filter)
|
||||
- `last_read_message_id` - If it is the first time you are going to read the channel let it be `0` or if you have already used this script to download media it will have some numbers which are auto-updated after the scripts successful execution. Don't change it.
|
||||
- `ids_to_retry` - `Leave it as it is.` This is used by the downloader script to keep track of all skipped downloads so that it can be downloaded during the next execution of the script.
|
||||
- **media_types** - Type of media to download, you can update which type of media you want to download it can be one or any of the available types.
|
||||
- **file_formats** - File types to download for supported media types which are `audio`, `document` and `video`. Default format is `all`, downloads all files.
|
||||
- **save_path** - The root directory where you want to store downloaded files.
|
||||
- **file_path_prefix** - Store file subfolders, the order of the list is not fixed, can be randomly combined.
|
||||
- `chat_title` - Channel or group title, it will be chat id if not exist title.
|
||||
- `media_datetime` - Media date, also see pyrogram.types.Message.date.strftime("%Y_%m").
|
||||
- `media_type` - Media type, also see `media_types`.
|
||||
- **disable_syslog** - You can choose which types of logs to disable,see `logging._nameToLevel`.
|
||||
- **upload_drive** - You can upload file to cloud drive.
|
||||
- `enable_upload_file` - Enable upload file, default `false`.
|
||||
- `remote_dir` - Where you upload, like `drive_id/drive_name`.
|
||||
- `upload_adapter` - Upload file adapter, which can be `rclone`, `aligo`. If it is `rclone`, it supports all `rclone` servers that support uploading. If it is `aligo`, it supports uploading `Ali cloud disk`.
|
||||
- `rclone_path` - RClone exe path, see [How to use rclone](https://github.com/tangyoha/telegram_media_downloader/wiki/Rclone)
|
||||
- `before_upload_file_zip` - Zip file before upload, default `false`.
|
||||
- `after_upload_file_delete` - Delete file after upload success, default `false`.
|
||||
- **file_name_prefix** - Custom file name, use the same as **file_path_prefix**
|
||||
- `message_id` - Message id
|
||||
- `file_name` - File name (may be empty)
|
||||
- `caption` - The title of the message (may be empty)
|
||||
- **file_name_prefix_split** - Custom file name prefix symbol, the default is `-`
|
||||
- **max_download_task** - The maximum number of task download tasks, the default is 5.
|
||||
- **hide_file_name** - Whether to hide the web interface file name, default `false`
|
||||
- **web_host** - Web host
|
||||
- **web_port** - Web port
|
||||
- **language** - Application language, the default is English (`EN`), optional `ZH`(Chinese),`RU`,`UA`
|
||||
- **web_login_secret** - Web page login password, if not configured, no login is required to access the web page
|
||||
|
||||
|
||||
## Execution
|
||||
|
||||
```sh
|
||||
python3 media_downloader.py
|
||||
```
|
||||
|
||||
All downloaded media will be stored at the root of `save_path`.
|
||||
The specific location reference is as follows:
|
||||
|
||||
The complete directory of video download is: `save_path`/`chat_title`/`media_datetime`/`media_type`.
|
||||
The order of the list is not fixed and can be randomly combined.
|
||||
If the configuration is empty, all files are saved under `save_path`.
|
||||
|
||||
## Proxy
|
||||
|
||||
`socks4, socks5, http` proxies are supported in this project currently. To use it, add the following to the bottom of your `config.yaml` file
|
||||
|
||||
```yaml
|
||||
proxy:
|
||||
scheme: socks5
|
||||
hostname: 127.0.0.1
|
||||
port: 1234
|
||||
username: your_username(delete the line if none)
|
||||
password: your_password(delete the line if none)
|
||||
```
|
||||
|
||||
If your proxy doesn’t require authorization you can omit username and password. Then the proxy will automatically be enabled.
|
||||
|
||||
## Contributing
|
||||
|
||||
### Contributing Guidelines
|
||||
|
||||
Read through our [contributing guidelines](https://github.com/tangyoha/telegram_media_downloader/blob/master/CONTRIBUTING.md) to learn about our submission process, coding rules and more.
|
||||
|
||||
### Want to Help?
|
||||
|
||||
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our guidelines for [contributing](https://github.com/tangyoha/telegram_media_downloader/blob/master/CONTRIBUTING.md).
|
||||
|
||||
### Code of Conduct
|
||||
|
||||
Help us keep Telegram Media Downloader open and inclusive. Please read and follow our [Code of Conduct](https://github.com/tangyoha/telegram_media_downloader/blob/master/CODE_OF_CONDUCT.md).
|
||||
|
||||
|
||||
### Sponsor
|
||||
|
||||
[PayPal](https://paypal.me/tangyoha?country.x=C2&locale.x=zh_XC)
|
||||
|
||||
<p>
|
||||
<img alt="Code style: black" style="width:30%" src="./screenshot/alipay.JPG">
|
||||
<img alt="Code style: black" style="width:30%" src="./screenshot/wechat.JPG">
|
||||
</p>
|
286
src/parsers/Telegram/telegram_media_downloader/README_CN.md
Normal file
@ -0,0 +1,286 @@
|
||||
|
||||
<h1 align="center">电报资源下载</h1>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/tangyoha/telegram_media_downloader/actions"><img alt="Unittest" src="https://github.com/tangyoha/telegram_media_downloader/workflows/Unittest/badge.svg"></a>
|
||||
<a href="https://codecov.io/gh/tangyoha/telegram_media_downloader"><img alt="Coverage Status" src="https://codecov.io/gh/tangyoha/telegram_media_downloader/branch/master/graph/badge.svg"></a>
|
||||
<a href="https://github.com/tangyoha/telegram_media_downloader/blob/master/LICENSE"><img alt="License: MIT" src="https://black.readthedocs.io/en/stable/_static/license.svg"></a>
|
||||
<a href="https://github.com/python/black"><img alt="Code style: black" src="https://img.shields.io/badge/code%20style-black-000000.svg"></a>
|
||||
<a href="https://github.com/tangyoha/telegram_media_downloader/releases">
|
||||
<img alt="Code style: black" src="https://img.shields.io/github/v/release/tangyoha/telegram_media_downloader?display_name=tag">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h3 align="center">
|
||||
<a href="./README.md">English</a><span> · </span>
|
||||
<a href="https://github.com/tangyoha/telegram_media_downloader/discussions/categories/ideas">新功能请求</a>
|
||||
<span> · </span>
|
||||
<a href="https://github.com/tangyoha/telegram_media_downloader/issues">报告bug</a>
|
||||
<span> · </span>
|
||||
帮助: <a href="https://github.com/tangyoha/telegram_media_downloader/discussions">讨论</a>
|
||||
<span> & </span>
|
||||
<a href="https://t.me/TeegramMediaDownload">电报讨论群</a>
|
||||
</h3>
|
||||
|
||||
## 概述
|
||||
|
||||
> 支持两种默认运行
|
||||
|
||||
* 机器人运行,从机器人下发命令`下载`或者`转发`
|
||||
|
||||
* 作为一个一次性的下载工具下载
|
||||
|
||||
### 界面
|
||||
|
||||
#### 网页
|
||||
|
||||
> 运行后打开浏览器访问`localhost:5000`
|
||||
> 如果是远程机器需要配置web_host: 0.0.0.0
|
||||
|
||||
|
||||
<img alt="Code style: black" style="width:100%; high:60%;" src="./screenshot/web_ui.gif"/>
|
||||
|
||||
### 机器人
|
||||
|
||||
> 需要配置bot_token,具体参考[文档](https://github.com/tangyoha/telegram_media_downloader/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8%E6%9C%BA%E5%99%A8%E4%BA%BA%E4%B8%8B%E8%BD%BD)
|
||||
|
||||
|
||||
<img alt="Code style: black" style="width:60%; high:30%; " src="./screenshot/bot.gif"/>
|
||||
|
||||
### 支持
|
||||
|
||||
| 类别 | 支持 |
|
||||
| ------------ | ---------------------------------------- |
|
||||
| 语言 | `Python 3.7` 及以上 |
|
||||
| 下载媒体类型 | 音频、文档、照片、视频、video_note、语音 |
|
||||
|
||||
### 版本发布计划
|
||||
|
||||
* [v2.2.0](https://github.com/tangyoha/telegram_media_downloader/issues/2)
|
||||
|
||||
## 安装
|
||||
|
||||
对于具有 `make` 可用性的 *nix 操作系统发行版
|
||||
|
||||
```sh
|
||||
git clone https://github.com/tangyoha/telegram_media_downloader.git
|
||||
cd telegram_media_downloader
|
||||
make install
|
||||
```
|
||||
|
||||
对于没有内置 `make` 的 Windows
|
||||
|
||||
```sh
|
||||
git clone https://github.com/tangyoha/telegram_media_downloader.git
|
||||
cd telegram_media_downloader
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
## Docker容器
|
||||
> 更详细安装教程请查看wiki
|
||||
|
||||
确保安装了 **docker** 和 **docker-compose**
|
||||
```sh
|
||||
docker pull tangyoha/telegram_media_downloader:latest
|
||||
mkdir -p ~/app && mkdir -p ~/app/log/ && cd ~/app
|
||||
wget https://raw.githubusercontent.com/tangyoha/telegram_media_downloader/blob/master/docker-compose.yaml -O docker-compose.yaml
|
||||
wget https://raw.githubusercontent.com/tangyoha/telegram_media_downloader/blob/master/config.yaml -O config.yaml
|
||||
wget https://raw.githubusercontent.com/tangyoha/telegram_media_downloader/blob/master/data.yaml -O data.yaml
|
||||
# vi config.yaml and docker-compose.yaml
|
||||
vi config.yaml
|
||||
|
||||
# 第一次需要前台启动
|
||||
# 输入你的电话号码和密码,然后退出(ctrl + c)
|
||||
docker-compose run --rm telegram_media_downloader
|
||||
|
||||
# 执行完以上操作后,后面的所有启动都在后台启动
|
||||
docker-compose up -d
|
||||
|
||||
# 升级
|
||||
docker pull tangyoha/telegram_media_downloader:latest
|
||||
cd ~/app
|
||||
docker-compose down
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
## 升级安装
|
||||
|
||||
```sh
|
||||
cd telegram_media_downloader
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
|
||||
## 配置
|
||||
|
||||
所有配置都通过 config.yaml 文件传递给 `Telegram Media Downloader`。
|
||||
|
||||
**获取您的 API 密钥:**
|
||||
第一步需要您获得有效的 Telegram API 密钥(API id/hash pair):
|
||||
|
||||
1. 访问 [https://my.telegram.org/apps](https://my.telegram.org/apps) 并使用您的 Telegram 帐户登录。
|
||||
2. 填写表格以注册新的 Telegram 应用程序。
|
||||
3. 完成! API 密钥由两部分组成:**api_id** 和**api_hash**。
|
||||
|
||||
**获取聊天ID:**
|
||||
> 如果你需要下载收藏夹的内容请填`me`
|
||||
|
||||
**1。使用网络电报:**
|
||||
|
||||
1. 打开 <https://web.telegram.org/?legacy=1#/im>
|
||||
2. 现在转到聊天/频道,您将看到 URL 类似
|
||||
|
||||
- `https://web.telegram.org/?legacy=1#/im?p=u853521067_2449618633394` 这里 `853521067` 是聊天 ID。
|
||||
- `https://web.telegram.org/?legacy=1#/im?p=@somename` 这里的 `somename` 是聊天 ID。
|
||||
- `https://web.telegram.org/?legacy=1#/im?p=s1301254321_6925449697188775560` 此处取 `1301254321` 并将 `-100` 添加到 id => `-1001301254321` 的开头。
|
||||
- `https://web.telegram.org/?legacy=1#/im?p=c1301254321_6925449697188775560` 此处取 `1301254321` 并将 `-100` 添加到 id => `-1001301254321` 的开头。
|
||||
|
||||
**2。使用机器人:**
|
||||
1.使用[@username_to_id_bot](https://t.me/username_to_id_bot)获取chat_id
|
||||
- 几乎所有电报用户:将用户名发送给机器人或将他们的消息转发给机器人
|
||||
- 任何聊天:发送聊天用户名或复制并发送其加入聊天链接到机器人
|
||||
- 公共或私人频道:与聊天相同,只需复制并发送给机器人
|
||||
- 任何电报机器人的 ID
|
||||
|
||||
### 配置文件
|
||||
|
||||
```yaml
|
||||
api_hash: your_api_hash
|
||||
api_id: your_api_id
|
||||
bot_token: your_bot_token
|
||||
chat:
|
||||
- chat_id: telegram_chat_id
|
||||
last_read_message_id: 0
|
||||
download_filter: message_date >= 2022-12-01 00:00:00 and message_date <= 2023-01-17 00:00:00
|
||||
- chat_id: telegram_chat_id_2
|
||||
last_read_message_id: 0
|
||||
# 我们将ids_to_retry移到data.yaml
|
||||
ids_to_retry: []
|
||||
media_types:
|
||||
- audio
|
||||
- document
|
||||
- photo
|
||||
- video
|
||||
- voice
|
||||
- animation #gif
|
||||
file_formats:
|
||||
audio:
|
||||
- all
|
||||
document:
|
||||
- pdf
|
||||
- epub
|
||||
video:
|
||||
- mp4
|
||||
save_path: D:\telegram_media_downloader
|
||||
file_path_prefix:
|
||||
- chat_title
|
||||
- media_datetime
|
||||
disable_syslog:
|
||||
- INFO
|
||||
upload_drive:
|
||||
enable_upload_file: true
|
||||
remote_dir: drive:/telegram
|
||||
before_upload_file_zip: True
|
||||
after_upload_file_delete: True
|
||||
hide_file_name: true
|
||||
file_name_prefix:
|
||||
- message_id
|
||||
- file_name
|
||||
file_name_prefix_split: ' - '
|
||||
max_download_task: 5
|
||||
web_host: 127.0.0.1
|
||||
web_port: 5000
|
||||
web_login_secret: 123
|
||||
```
|
||||
|
||||
- **api_hash** - 你从电报应用程序获得的 api_hash
|
||||
- **api_id** - 您从电报应用程序获得的 api_id
|
||||
- **bot_token** - 你的机器人凭证
|
||||
- **chat** - 多频道
|
||||
- `chat_id` - 您要下载媒体的聊天/频道的 ID。你从上述步骤中得到的。
|
||||
- `download_filter` - 下载过滤器, 查阅 [如何使用过滤器](https://github.com/tangyoha/telegram_media_downloader/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8%E8%BF%87%E6%BB%A4%E5%99%A8)
|
||||
- `last_read_message_id` -如果这是您第一次阅读频道,请将其设置为“0”,或者如果您已经使用此脚本下载媒体,它将有一些数字,这些数字会在脚本成功执行后自动更新。不要改变它。
|
||||
- **chat_id** - 您要下载媒体的聊天/频道的 ID。你从上述步骤中得到的。
|
||||
- **last_read_message_id** - 如果这是您第一次阅读频道,请将其设置为“0”,或者如果您已经使用此脚本下载媒体,它将有一些数字,这些数字会在脚本成功执行后自动更新。不要改变它。
|
||||
- **ids_to_retry** - `保持原样。`下载器脚本使用它来跟踪所有跳过的下载,以便在下次执行脚本时可以下载它。
|
||||
- **media_types** - 要下载的媒体类型,您可以更新要下载的媒体类型,它可以是一种或任何可用类型。
|
||||
- **file_formats** - 为支持的媒体类型(“音频”、“文档”和“视频”)下载的文件类型。默认格式为“all”,下载所有文件。
|
||||
- **save_path** - 你想存储下载文件的根目录
|
||||
- **file_path_prefix** - 存储文件子文件夹,列表的顺序不定,可以随机组合
|
||||
- `chat_title` - 聊天频道或者群组标题, 如果找不到标题则为配置文件中的`chat_id`
|
||||
- `media_datetime` - 资源的发布时间
|
||||
- `media_type` - 资源类型,类型查阅 `media_types`
|
||||
- **disable_syslog** - 您可以选择要禁用的日志类型,请参阅 `logging._nameToLevel`
|
||||
- **upload_drive** - 您可以将文件上传到云盘
|
||||
- `enable_upload_file` - [必填]启用上传文件,默认为`false`
|
||||
- `remote_dir` - [必填]你上传的地方
|
||||
- `upload_adapter` - [必填]上传文件适配器,可以为`rclone`,`aligo`。如果为`rclone`,则支持rclone所有支持上传的服务器,如果为aligo,则支持上传阿里云盘
|
||||
- `rclone_path`,如果配置`upload_adapter`为`rclone`则为必填,`rclone`的可执行目录,查阅 [如何使用rclone](https://github.com/tangyoha/telegram_media_downloader/wiki/Rclone)
|
||||
- `before_upload_file_zip` - 上传前压缩文件,默认为`false`
|
||||
- `after_upload_file_delete` - 上传成功后删除文件,默认为`false`
|
||||
- **file_name_prefix** - 自定义文件名称,使用和 **file_path_prefix** 一样
|
||||
- `message_id` - 消息id
|
||||
- `file_name` - 文件名称(可能为空)
|
||||
- `caption` - 消息的标题(可能为空)
|
||||
- **file_name_prefix_split** - 自定义文件名称分割符号,默认为` - `
|
||||
- **max_download_task** - 最大任务下载任务个数,默认为5个。
|
||||
- **hide_file_name** - 是否隐藏web界面文件名称,默认`false`
|
||||
- **web_host** - web界面地址
|
||||
- **web_port** - web界面端口
|
||||
- **language** - 应用语言,默认为英文(`EN`),可选`ZH`(中文),`RU`,`UA`
|
||||
- **web_login_secret** - 网页登录密码,如果不配置则访问网页不需要登录
|
||||
|
||||
## 执行
|
||||
|
||||
```sh
|
||||
python3 media_downloader.py
|
||||
```
|
||||
|
||||
所有下载的媒体都将存储在`save_path`根目录下。
|
||||
具体位置参考如下:
|
||||
|
||||
```yaml
|
||||
file_path_prefix:
|
||||
- chat_title
|
||||
- media_datetime
|
||||
- media_type
|
||||
```
|
||||
|
||||
视频下载完整目录为:`save_path`/`chat_title`/`media_datetime`/`media_type`。
|
||||
列表的顺序不定,可以随机组合。
|
||||
如果配置为空,则所有文件保存在`save_path`下。
|
||||
|
||||
## 代理
|
||||
|
||||
该项目目前支持 socks4、socks5、http 代理。要使用它,请将以下内容添加到`config.yaml`文件的底部
|
||||
|
||||
```yaml
|
||||
proxy:
|
||||
scheme: socks5
|
||||
hostname: 127.0.0.1
|
||||
port: 1234
|
||||
username: 你的用户名(无则删除该行)
|
||||
password: 你的密码(无则删除该行)
|
||||
```
|
||||
|
||||
如果您的代理不需要授权,您可以省略用户名和密码。然后代理将自动启用。
|
||||
|
||||
## 贡献
|
||||
|
||||
### 贡献指南
|
||||
|
||||
通读我们的[贡献指南](CONTRIBUTING.md),了解我们的提交流程、编码规则等。
|
||||
|
||||
### 想帮忙?
|
||||
|
||||
想要提交错误、贡献一些代码或改进文档?出色的!阅读我们的 [贡献指南](CONTRIBUTING.md)。
|
||||
|
||||
### 行为守则
|
||||
|
||||
帮助我们保持 Telegram Media Downloader 的开放性和包容性。请阅读并遵守我们的[行为准则](CODE_OF_CONDUCT.md)。
|
||||
|
||||
|
||||
### 赞助
|
||||
|
||||
<p>
|
||||
<img alt="Code style: black" style="width:30%" src="./screenshot/alipay.JPG">
|
||||
<img alt="Code style: black" style="width:30%" src="./screenshot/wechat.JPG">
|
||||
</p>
|
13
src/parsers/Telegram/telegram_media_downloader/codecov.yml
Normal file
@ -0,0 +1,13 @@
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
target: auto
|
||||
threshold: 1%
|
||||
if_no_uploads: error
|
||||
if_not_found: success
|
||||
if_ci_failed: error
|
||||
patch: no
|
||||
|
||||
comment:
|
||||
require_changes: true
|
22
src/parsers/Telegram/telegram_media_downloader/config.yaml
Normal file
@ -0,0 +1,22 @@
|
||||
api_hash: cb06da2bf01e15627434223242b6446d
|
||||
api_id: 21648766
|
||||
chat:
|
||||
- chat_id: -1001966291562
|
||||
download_filter: id == 2048
|
||||
last_read_message_id: 2048
|
||||
file_formats:
|
||||
video:
|
||||
- all
|
||||
media_types:
|
||||
- video
|
||||
# in linux please use /
|
||||
# save_path: E:\github\telegram_media_downloader
|
||||
disable_syslog: []
|
||||
save_path: downloads/Telegram/
|
||||
language: RU
|
||||
web_host: 0.0.0.0
|
||||
web_port: 51256
|
||||
file_path_prefix:
|
||||
- chat_id
|
||||
file_name_prefix:
|
||||
- message_id
|
@ -0,0 +1,9 @@
|
||||
black==22.6.0
|
||||
isort==5.10.1
|
||||
mock==4.0.3
|
||||
mypy==0.971
|
||||
pre-commit==2.20.0
|
||||
pylint==2.14.5
|
||||
pytest==7.2.1
|
||||
pytest-cov==3.0.0
|
||||
types-PyYAML==6.0.11
|
@ -0,0 +1,29 @@
|
||||
version: "3.3"
|
||||
|
||||
services:
|
||||
telegram_media_downloader:
|
||||
image: tangyoha/telegram_media_downloader:latest
|
||||
build: .
|
||||
ports:
|
||||
# Here is what you need to edit
|
||||
- "5000:5000"
|
||||
#environment:
|
||||
# - http_proxy=http://192.168.101.30:10811
|
||||
# - https_proxy=http://192.168.101.30:10811
|
||||
volumes:
|
||||
# Here is what you need to edit
|
||||
- "./downloads/:/app/downloads/"
|
||||
|
||||
# Rclone
|
||||
- "$HOME/.config/rclone/:$HOME/.config/rclone/"
|
||||
|
||||
# The following is what you don't need to edit
|
||||
- "./config.yaml:/app/config.yaml"
|
||||
- "./data.yaml:/app/data.yaml"
|
||||
- "./log/:/app/log/"
|
||||
- "./sessions/:/app/sessions"
|
||||
- "./temp/:/app/temp"
|
||||
#restart: "unless-stopped"
|
||||
# volumes:
|
||||
# sessions:
|
||||
# temp:
|
@ -0,0 +1,3 @@
|
||||
from module.filter import Filter
|
||||
|
||||
Filter()
|
@ -0,0 +1,630 @@
|
||||
"""Downloads media from telegram."""
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import time
|
||||
from typing import List, Optional, Tuple, Union
|
||||
|
||||
import pyrogram
|
||||
from loguru import logger
|
||||
from pyrogram.types import Audio, Document, Photo, Video, VideoNote, Voice
|
||||
from rich.logging import RichHandler
|
||||
|
||||
from module.app import Application, ChatDownloadConfig, DownloadStatus, TaskNode
|
||||
from module.bot import start_download_bot, stop_download_bot
|
||||
from module.download_stat import update_download_status
|
||||
from module.get_chat_history_v2 import get_chat_history_v2
|
||||
from module.language import _t
|
||||
from module.pyrogram_extension import (
|
||||
fetch_message,
|
||||
get_extension,
|
||||
record_download_status,
|
||||
report_bot_download_status,
|
||||
set_max_concurrent_transmissions,
|
||||
set_meta_data,
|
||||
upload_telegram_chat,
|
||||
)
|
||||
from module.web import init_web
|
||||
from utils.format import truncate_filename, validate_title
|
||||
from utils.log import LogFilter
|
||||
from utils.meta import print_meta
|
||||
from utils.meta_data import MetaData
|
||||
from utils.updates import check_for_updates
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(message)s",
|
||||
datefmt="[%X]",
|
||||
handlers=[RichHandler()],
|
||||
)
|
||||
|
||||
CONFIG_NAME = "config.yaml"
|
||||
DATA_FILE_NAME = "data.yaml"
|
||||
APPLICATION_NAME = "media_downloader"
|
||||
app = Application(CONFIG_NAME, DATA_FILE_NAME, APPLICATION_NAME)
|
||||
|
||||
logger.add(
|
||||
os.path.join(app.log_file_path, "tdl.log"),
|
||||
rotation="10 MB",
|
||||
retention="10 days",
|
||||
level="DEBUG",
|
||||
)
|
||||
|
||||
queue: asyncio.Queue = asyncio.Queue()
|
||||
RETRY_TIME_OUT = 3
|
||||
|
||||
logging.getLogger("pyrogram.session.session").addFilter(LogFilter())
|
||||
logging.getLogger("pyrogram.client").addFilter(LogFilter())
|
||||
|
||||
logging.getLogger("pyrogram").setLevel(logging.WARNING)
|
||||
|
||||
|
||||
def _check_download_finish(media_size: int, download_path: str, ui_file_name: str):
|
||||
"""Check download task if finish
|
||||
|
||||
Parameters
|
||||
----------
|
||||
media_size: int
|
||||
The size of the downloaded resource
|
||||
download_path: str
|
||||
Resource download hold path
|
||||
ui_file_name: str
|
||||
Really show file name
|
||||
|
||||
"""
|
||||
download_size = os.path.getsize(download_path)
|
||||
if media_size == download_size:
|
||||
logger.success(f"{_t('Successfully downloaded')} - {ui_file_name}")
|
||||
else:
|
||||
logger.warning(
|
||||
f"{_t('Media downloaded with wrong size')}: "
|
||||
f"{download_size}, {_t('actual')}: "
|
||||
f"{media_size}, {_t('file name')}: {ui_file_name}"
|
||||
)
|
||||
os.remove(download_path)
|
||||
raise pyrogram.errors.exceptions.bad_request_400.BadRequest()
|
||||
|
||||
|
||||
def _move_to_download_path(temp_download_path: str, download_path: str):
|
||||
"""Move file to download path
|
||||
|
||||
Parameters
|
||||
----------
|
||||
temp_download_path: str
|
||||
Temporary download path
|
||||
|
||||
download_path: str
|
||||
Download path
|
||||
|
||||
"""
|
||||
|
||||
directory, _ = os.path.split(download_path)
|
||||
os.makedirs(directory, exist_ok=True)
|
||||
shutil.move(temp_download_path, download_path)
|
||||
|
||||
|
||||
def _check_timeout(retry: int, _: int):
|
||||
"""Check if message download timeout, then add message id into failed_ids
|
||||
|
||||
Parameters
|
||||
----------
|
||||
retry: int
|
||||
Retry download message times
|
||||
|
||||
message_id: int
|
||||
Try to download message 's id
|
||||
|
||||
"""
|
||||
if retry == 2:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _can_download(_type: str, file_formats: dict, file_format: Optional[str]) -> bool:
|
||||
"""
|
||||
Check if the given file format can be downloaded.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
_type: str
|
||||
Type of media object.
|
||||
file_formats: dict
|
||||
Dictionary containing the list of file_formats
|
||||
to be downloaded for `audio`, `document` & `video`
|
||||
media types
|
||||
file_format: str
|
||||
Format of the current file to be downloaded.
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
True if the file format can be downloaded else False.
|
||||
"""
|
||||
if _type in ["audio", "document", "video"]:
|
||||
allowed_formats: list = file_formats[_type]
|
||||
if not file_format in allowed_formats and allowed_formats[0] != "all":
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _is_exist(file_path: str) -> bool:
|
||||
"""
|
||||
Check if a file exists and it is not a directory.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
file_path: str
|
||||
Absolute path of the file to be checked.
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
True if the file exists else False.
|
||||
"""
|
||||
return not os.path.isdir(file_path) and os.path.exists(file_path)
|
||||
|
||||
|
||||
# pylint: disable = R0912
|
||||
|
||||
|
||||
async def _get_media_meta(
|
||||
chat_id: Union[int, str],
|
||||
message: pyrogram.types.Message,
|
||||
media_obj: Union[Audio, Document, Photo, Video, VideoNote, Voice],
|
||||
_type: str,
|
||||
) -> Tuple[str, str, Optional[str]]:
|
||||
"""Extract file name and file id from media object.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
media_obj: Union[Audio, Document, Photo, Video, VideoNote, Voice]
|
||||
Media object to be extracted.
|
||||
_type: str
|
||||
Type of media object.
|
||||
|
||||
Returns
|
||||
-------
|
||||
Tuple[str, str, Optional[str]]
|
||||
file_name, file_format
|
||||
"""
|
||||
if _type in ["audio", "document", "video"]:
|
||||
# pylint: disable = C0301
|
||||
file_format: Optional[str] = media_obj.mime_type.split("/")[-1] # type: ignore
|
||||
else:
|
||||
file_format = None
|
||||
|
||||
file_name = None
|
||||
temp_file_name = None
|
||||
dirname = validate_title(f"{chat_id}")
|
||||
if message.chat and message.chat.title:
|
||||
dirname = validate_title(f"{message.chat.title}")
|
||||
|
||||
if message.date:
|
||||
datetime_dir_name = message.date.strftime("%Y_%m")
|
||||
else:
|
||||
datetime_dir_name = "0"
|
||||
|
||||
if _type in ["voice", "video_note"]:
|
||||
# pylint: disable = C0209
|
||||
file_format = media_obj.mime_type.split("/")[-1] # type: ignore
|
||||
file_save_path = app.get_file_save_path(_type, dirname, datetime_dir_name)
|
||||
file_name = "{} - {}_{}.{}".format(
|
||||
message.id,
|
||||
_type,
|
||||
media_obj.date.isoformat(), # type: ignore
|
||||
file_format,
|
||||
)
|
||||
file_name = validate_title(file_name)
|
||||
temp_file_name = os.path.join(app.temp_save_path, dirname, file_name)
|
||||
|
||||
file_name = os.path.join(file_save_path, file_name)
|
||||
else:
|
||||
file_name = getattr(media_obj, "file_name", None)
|
||||
caption = getattr(message, "caption", None)
|
||||
|
||||
file_name_suffix = ".unknown"
|
||||
if not file_name:
|
||||
file_name_suffix = get_extension(
|
||||
media_obj.file_id, getattr(media_obj, "mime_type", "")
|
||||
)
|
||||
else:
|
||||
# file_name = file_name.split(".")[0]
|
||||
_, file_name_without_suffix = os.path.split(os.path.normpath(file_name))
|
||||
file_name, file_name_suffix = os.path.splitext(file_name_without_suffix)
|
||||
if not file_name_suffix:
|
||||
file_name_suffix = get_extension(
|
||||
media_obj.file_id, getattr(media_obj, "mime_type", "")
|
||||
)
|
||||
|
||||
if caption:
|
||||
caption = validate_title(caption)
|
||||
app.set_caption_name(chat_id, message.media_group_id, caption)
|
||||
else:
|
||||
caption = app.get_caption_name(chat_id, message.media_group_id)
|
||||
|
||||
if not file_name and message.photo:
|
||||
file_name = f"{message.photo.file_unique_id}"
|
||||
|
||||
gen_file_name = (
|
||||
app.get_file_name(message.id, file_name, caption) + file_name_suffix
|
||||
)
|
||||
|
||||
file_save_path = app.get_file_save_path(_type, dirname, datetime_dir_name)
|
||||
|
||||
temp_file_name = os.path.join(app.temp_save_path, dirname, gen_file_name)
|
||||
|
||||
file_name = os.path.join(file_save_path, gen_file_name)
|
||||
return truncate_filename(file_name), truncate_filename(temp_file_name), file_format
|
||||
|
||||
|
||||
async def add_download_task(message: pyrogram.types.Message, node: TaskNode):
|
||||
"""Add Download task"""
|
||||
if message.empty:
|
||||
return False
|
||||
await queue.put((message, node))
|
||||
node.total_task += 1
|
||||
return True
|
||||
|
||||
|
||||
async def download_task(
|
||||
client: pyrogram.Client, message: pyrogram.types.Message, node: TaskNode
|
||||
):
|
||||
"""Download and Forward media"""
|
||||
|
||||
download_status, file_name = await download_media(
|
||||
client, message, app.media_types, app.file_formats, node.chat_id, node.task_id
|
||||
)
|
||||
|
||||
if not node.bot:
|
||||
app.set_download_id(node.chat_id, message.id, download_status)
|
||||
|
||||
file_size = os.path.getsize(file_name) if file_name else 0
|
||||
|
||||
await upload_telegram_chat(
|
||||
client,
|
||||
node.upload_user if node.upload_user else client,
|
||||
app,
|
||||
node,
|
||||
message,
|
||||
file_name,
|
||||
download_status,
|
||||
)
|
||||
|
||||
# rclone upload
|
||||
if (
|
||||
not node.upload_telegram_chat_id
|
||||
and download_status is DownloadStatus.SuccessDownload
|
||||
):
|
||||
if await app.upload_file(file_name):
|
||||
node.upload_success_count += 1
|
||||
|
||||
await report_bot_download_status(
|
||||
node.bot,
|
||||
node,
|
||||
download_status,
|
||||
file_size,
|
||||
)
|
||||
|
||||
|
||||
# pylint: disable = R0915,R0914
|
||||
|
||||
|
||||
@record_download_status
|
||||
async def download_media(
|
||||
client: pyrogram.client.Client,
|
||||
message: pyrogram.types.Message,
|
||||
media_types: List[str],
|
||||
file_formats: dict,
|
||||
chat_id: Union[int, str],
|
||||
task_id: int = 0,
|
||||
):
|
||||
"""
|
||||
Download media from Telegram.
|
||||
|
||||
Each of the files to download are retried 3 times with a
|
||||
delay of 5 seconds each.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
client: pyrogram.client.Client
|
||||
Client to interact with Telegram APIs.
|
||||
message: pyrogram.types.Message
|
||||
Message object retrieved from telegram.
|
||||
media_types: list
|
||||
List of strings of media types to be downloaded.
|
||||
Ex : `["audio", "photo"]`
|
||||
Supported formats:
|
||||
* audio
|
||||
* document
|
||||
* photo
|
||||
* video
|
||||
* voice
|
||||
file_formats: dict
|
||||
Dictionary containing the list of file_formats
|
||||
to be downloaded for `audio`, `document` & `video`
|
||||
media types.
|
||||
|
||||
Returns
|
||||
-------
|
||||
int
|
||||
Current message id.
|
||||
"""
|
||||
|
||||
# pylint: disable = R0912
|
||||
|
||||
file_name: str = ""
|
||||
ui_file_name: str = ""
|
||||
task_start_time: float = time.time()
|
||||
media_size = 0
|
||||
_media = None
|
||||
message = await fetch_message(client, message)
|
||||
try:
|
||||
for _type in media_types:
|
||||
_media = getattr(message, _type, None)
|
||||
if _media is None:
|
||||
continue
|
||||
file_name, temp_file_name, file_format = await _get_media_meta(
|
||||
chat_id, message, _media, _type
|
||||
)
|
||||
media_size = getattr(_media, "file_size", 0)
|
||||
|
||||
ui_file_name = file_name
|
||||
if app.hide_file_name:
|
||||
ui_file_name = f"****{os.path.splitext(file_name)[-1]}"
|
||||
|
||||
if _can_download(_type, file_formats, file_format):
|
||||
if _is_exist(file_name):
|
||||
file_size = os.path.getsize(file_name)
|
||||
if file_size or file_size == media_size:
|
||||
logger.info(
|
||||
f"id={message.id} {ui_file_name} "
|
||||
f"{_t('already download,download skipped')}.\n"
|
||||
)
|
||||
|
||||
return DownloadStatus.SkipDownload, None
|
||||
else:
|
||||
return DownloadStatus.SkipDownload, None
|
||||
|
||||
break
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Message[{message.id}]: "
|
||||
f"{_t('could not be downloaded due to following exception')}:\n[{e}].",
|
||||
exc_info=True,
|
||||
)
|
||||
return DownloadStatus.FailedDownload, None
|
||||
if _media is None:
|
||||
return DownloadStatus.SkipDownload, None
|
||||
|
||||
message_id = message.id
|
||||
|
||||
for retry in range(3):
|
||||
try:
|
||||
temp_download_path = await client.download_media(
|
||||
message,
|
||||
file_name=temp_file_name,
|
||||
progress=update_download_status,
|
||||
progress_args=(
|
||||
chat_id,
|
||||
message_id,
|
||||
ui_file_name,
|
||||
task_start_time,
|
||||
task_id,
|
||||
),
|
||||
)
|
||||
|
||||
if temp_download_path and isinstance(temp_download_path, str):
|
||||
_check_download_finish(media_size, temp_download_path, ui_file_name)
|
||||
await asyncio.sleep(0.5)
|
||||
_move_to_download_path(temp_download_path, file_name)
|
||||
# TODO: if not exist file size or media
|
||||
return DownloadStatus.SuccessDownload, file_name
|
||||
except pyrogram.errors.exceptions.bad_request_400.BadRequest:
|
||||
logger.warning(
|
||||
f"Message[{message.id}]: {_t('file reference expired, refetching')}..."
|
||||
)
|
||||
await asyncio.sleep(RETRY_TIME_OUT)
|
||||
message = await fetch_message(client, message)
|
||||
if _check_timeout(retry, message.id):
|
||||
# pylint: disable = C0301
|
||||
logger.error(
|
||||
f"Message[{message.id}]: "
|
||||
f"{_t('file reference expired for 3 retries, download skipped.')}"
|
||||
)
|
||||
except pyrogram.errors.exceptions.flood_420.FloodWait as wait_err:
|
||||
await asyncio.sleep(wait_err.value)
|
||||
logger.warning("Message[{}]: FlowWait {}", message.id, wait_err.value)
|
||||
_check_timeout(retry, message.id)
|
||||
except TypeError:
|
||||
# pylint: disable = C0301
|
||||
logger.warning(
|
||||
f"{_t('Timeout Error occurred when downloading Message')}[{message.id}], "
|
||||
f"{_t('retrying after')} {RETRY_TIME_OUT} {_t('seconds')}"
|
||||
)
|
||||
await asyncio.sleep(RETRY_TIME_OUT)
|
||||
if _check_timeout(retry, message.id):
|
||||
logger.error(
|
||||
f"Message[{message.id}]: {_t('Timing out after 3 reties, download skipped.')}"
|
||||
)
|
||||
except Exception as e:
|
||||
# pylint: disable = C0301
|
||||
logger.error(
|
||||
f"Message[{message.id}]: "
|
||||
f"{_t('could not be downloaded due to following exception')}:\n[{e}].",
|
||||
exc_info=True,
|
||||
)
|
||||
break
|
||||
|
||||
return DownloadStatus.FailedDownload, None
|
||||
|
||||
|
||||
def _load_config():
|
||||
"""Load config"""
|
||||
app.load_config()
|
||||
|
||||
|
||||
def _check_config() -> bool:
|
||||
"""Check config"""
|
||||
print_meta(logger)
|
||||
try:
|
||||
_load_config()
|
||||
except Exception as e:
|
||||
logger.exception(f"load config error: {e}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def worker(client: pyrogram.client.Client):
|
||||
"""Work for download task"""
|
||||
while app.is_running:
|
||||
try:
|
||||
item = await queue.get()
|
||||
message = item[0]
|
||||
node: TaskNode = item[1]
|
||||
|
||||
if node.client:
|
||||
await download_task(node.client, message, node)
|
||||
app.is_running = False
|
||||
else:
|
||||
await download_task(client, message, node)
|
||||
app.is_running = False
|
||||
except Exception as e:
|
||||
logger.exception(f"{e}")
|
||||
|
||||
|
||||
async def download_chat_task(
|
||||
client: pyrogram.Client,
|
||||
chat_download_config: ChatDownloadConfig,
|
||||
node: TaskNode,
|
||||
):
|
||||
"""Download all task"""
|
||||
messages_iter = get_chat_history_v2(
|
||||
client,
|
||||
node.chat_id,
|
||||
limit=node.limit,
|
||||
offset_id=chat_download_config.last_read_message_id,
|
||||
reverse=True,
|
||||
)
|
||||
if chat_download_config.ids_to_retry:
|
||||
logger.info(f"{_t('Downloading files failed during last run')}...")
|
||||
skipped_messages: list = await client.get_messages( # type: ignore
|
||||
chat_id=node.chat_id, message_ids=chat_download_config.ids_to_retry
|
||||
)
|
||||
|
||||
for message in skipped_messages:
|
||||
if not await add_download_task(message, node):
|
||||
chat_download_config.downloaded_ids.append(message.id)
|
||||
|
||||
async for message in messages_iter: # type: ignore
|
||||
meta_data = MetaData()
|
||||
|
||||
caption = message.caption
|
||||
if caption:
|
||||
caption = validate_title(caption)
|
||||
app.set_caption_name(node.chat_id, message.media_group_id, caption)
|
||||
else:
|
||||
caption = app.get_caption_name(node.chat_id, message.media_group_id)
|
||||
set_meta_data(meta_data, message, caption)
|
||||
|
||||
if not app.need_skip_message(
|
||||
chat_download_config, message.id, meta_data
|
||||
) and await add_download_task(message, node):
|
||||
chat_download_config.downloaded_ids.append(message.id)
|
||||
|
||||
chat_download_config.need_check = True
|
||||
chat_download_config.total_task = node.total_task
|
||||
node.is_running = True
|
||||
|
||||
|
||||
async def download_all_chat(client: pyrogram.Client):
|
||||
"""Download All chat"""
|
||||
for key, value in app.chat_download_config.items():
|
||||
node = TaskNode(chat_id=key)
|
||||
try:
|
||||
await download_chat_task(client, value, node)
|
||||
except Exception as e:
|
||||
logger.warning(f"Download {key} error: {e}")
|
||||
finally:
|
||||
value.need_check = True
|
||||
|
||||
|
||||
async def run_until_all_task_finish():
|
||||
"""Normal download"""
|
||||
while True:
|
||||
finish: bool = True
|
||||
for _, value in app.chat_download_config.items():
|
||||
if not value.need_check or value.total_task != value.finish_task:
|
||||
finish = False
|
||||
|
||||
if finish:
|
||||
break
|
||||
|
||||
await asyncio.sleep(1)
|
||||
|
||||
|
||||
def _exec_loop():
|
||||
"""Exec loop"""
|
||||
|
||||
if app.bot_token:
|
||||
app.loop.run_forever()
|
||||
else:
|
||||
app.loop.run_until_complete(run_until_all_task_finish())
|
||||
|
||||
|
||||
def main():
|
||||
"""Main function of the downloader."""
|
||||
tasks = []
|
||||
client = pyrogram.Client(
|
||||
"media_downloader",
|
||||
api_id=app.api_id,
|
||||
api_hash=app.api_hash,
|
||||
proxy=app.proxy,
|
||||
workdir=app.session_file_path,
|
||||
)
|
||||
try:
|
||||
app.pre_run()
|
||||
init_web(app)
|
||||
|
||||
set_max_concurrent_transmissions(client, app.max_concurrent_transmissions)
|
||||
|
||||
client.start()
|
||||
logger.success(_t("Successfully started (Press Ctrl+C to stop)"))
|
||||
|
||||
app.loop.create_task(download_all_chat(client))
|
||||
for _ in range(app.max_download_task):
|
||||
task = app.loop.create_task(worker(client))
|
||||
tasks.append(task)
|
||||
|
||||
if app.bot_token:
|
||||
app.loop.create_task(
|
||||
start_download_bot(app, client, add_download_task, download_chat_task)
|
||||
)
|
||||
_exec_loop()
|
||||
except KeyboardInterrupt:
|
||||
logger.info(_t("KeyboardInterrupt"))
|
||||
except Exception as e:
|
||||
logger.exception("{}", e)
|
||||
finally:
|
||||
client.stop()
|
||||
app.is_running = False
|
||||
for task in tasks:
|
||||
task.cancel()
|
||||
logger.info(_t("Stopped!"))
|
||||
check_for_updates()
|
||||
logger.info(f"{_t('update config')}......")
|
||||
app.update_config()
|
||||
if app.bot_token:
|
||||
stop_download_bot()
|
||||
logger.success(
|
||||
f"{_t('Updated last read message_id to config file')},"
|
||||
f"{_t('total download')} {app.total_download_task}, "
|
||||
f"{_t('total upload file')} "
|
||||
f"{app.cloud_drive_config.total_upload_success_file_count}"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if _check_config():
|
||||
main()
|
@ -0,0 +1,50 @@
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
|
||||
block_cipher = None
|
||||
|
||||
|
||||
a = Analysis(
|
||||
['media_downloader.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[('./module/templates','./module/templates'),('./module/static/','./module/static'), ('./module/parsetab.py','./module/'),('./module/parser.out','./module/'),('./config.yaml','./'),('./data.yaml','./')],
|
||||
hiddenimports=[],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
noarchive=False,
|
||||
)
|
||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name='tdl',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
console=True,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
)
|
||||
coll = COLLECT(
|
||||
exe,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
name='tdl',
|
||||
)
|
729
src/parsers/Telegram/telegram_media_downloader/module/app.py
Normal file
@ -0,0 +1,729 @@
|
||||
"""Application module"""
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import time
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from enum import Enum
|
||||
from typing import List, Optional, Union
|
||||
|
||||
import loguru
|
||||
from ruamel import yaml
|
||||
|
||||
from module.cloud_drive import CloudDrive, CloudDriveConfig
|
||||
from module.filter import Filter
|
||||
from module.language import Language, set_language
|
||||
from utils.format import replace_date_time, validate_title
|
||||
from utils.meta_data import MetaData
|
||||
|
||||
_yaml = yaml.YAML()
|
||||
# pylint: disable = R0902
|
||||
|
||||
|
||||
class DownloadStatus(Enum):
|
||||
"""Download status"""
|
||||
|
||||
SkipDownload = 1
|
||||
SuccessDownload = 2
|
||||
FailedDownload = 3
|
||||
Downloading = 4
|
||||
|
||||
|
||||
class ForwardStatus(Enum):
|
||||
"""Forward status"""
|
||||
|
||||
SkipForward = 1
|
||||
SuccessForward = 2
|
||||
FailedForward = 3
|
||||
Forwarding = 4
|
||||
|
||||
|
||||
class TaskType(Enum):
|
||||
"""Task Type"""
|
||||
|
||||
Download = 1
|
||||
Forward = 2
|
||||
ListenForward = 3
|
||||
|
||||
|
||||
class TaskNode:
|
||||
"""Task node"""
|
||||
|
||||
# pylint: disable = R0913
|
||||
def __init__(
|
||||
self,
|
||||
chat_id: Union[int, str],
|
||||
from_user_id: Union[int, str] = None,
|
||||
reply_message_id: int = 0,
|
||||
replay_message: str = None,
|
||||
upload_telegram_chat_id: Union[int, str] = None,
|
||||
has_protected_content: bool = False,
|
||||
download_filter: str = "",
|
||||
limit: int = 0,
|
||||
bot=None,
|
||||
task_type: TaskType = TaskType.Download,
|
||||
task_id: int = 0,
|
||||
):
|
||||
self.chat_id = chat_id
|
||||
self.from_user_id = from_user_id
|
||||
self.upload_telegram_chat_id = upload_telegram_chat_id
|
||||
self.reply_message_id = reply_message_id
|
||||
self.reply_message = replay_message
|
||||
self.has_protected_content = has_protected_content
|
||||
self.download_filter = download_filter
|
||||
self.limit = limit
|
||||
self.bot = bot
|
||||
self.task_id = task_id
|
||||
self.task_type = task_type
|
||||
self.total_task = 0
|
||||
self.total_download_task = 0
|
||||
self.failed_download_task = 0
|
||||
self.success_download_task = 0
|
||||
self.skip_download_task = 0
|
||||
self.last_reply_time = time.time()
|
||||
self.last_edit_msg: str = ""
|
||||
self.total_download_byte = 0
|
||||
self.forward_msg_detail_str: str = ""
|
||||
self.upload_user = None
|
||||
self.total_forward_task: int = 0
|
||||
self.success_forward_task: int = 0
|
||||
self.failed_forward_task: int = 0
|
||||
self.skip_forward_task: int = 0
|
||||
self.is_running: bool = False
|
||||
self.client = None
|
||||
self.upload_success_count: int = 0
|
||||
|
||||
def is_finish(self):
|
||||
"""If is finish"""
|
||||
return (
|
||||
self.task_type != TaskType.ListenForward
|
||||
and self.total_task == self.total_download_task
|
||||
)
|
||||
|
||||
def stat(self, status: DownloadStatus):
|
||||
"""
|
||||
Updates the download status of the task.
|
||||
|
||||
Args:
|
||||
status (DownloadStatus): The status of the download task.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
self.total_download_task += 1
|
||||
if status is DownloadStatus.SuccessDownload:
|
||||
self.success_download_task += 1
|
||||
elif status is DownloadStatus.SkipDownload:
|
||||
self.skip_download_task += 1
|
||||
else:
|
||||
self.failed_download_task += 1
|
||||
|
||||
def stat_forward(self, status: ForwardStatus):
|
||||
"""Stat upload"""
|
||||
self.total_forward_task += 1
|
||||
if status is ForwardStatus.SuccessForward:
|
||||
self.success_forward_task += 1
|
||||
elif status is ForwardStatus.SkipForward:
|
||||
self.skip_forward_task += 1
|
||||
else:
|
||||
self.failed_forward_task += 1
|
||||
|
||||
def can_reply(self):
|
||||
"""
|
||||
Checks if the bot can reply to a message
|
||||
based on the time elapsed since the last reply.
|
||||
|
||||
Returns:
|
||||
True if the time elapsed since
|
||||
the last reply is greater than 1 second, False otherwise.
|
||||
"""
|
||||
cur_time = time.time()
|
||||
if cur_time - self.last_reply_time > 1.0:
|
||||
self.last_reply_time = cur_time
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class ChatDownloadConfig:
|
||||
"""Chat Message Download Status"""
|
||||
|
||||
def __init__(self):
|
||||
self.downloaded_ids: list = []
|
||||
self.failed_ids: list = []
|
||||
self.ids_to_retry_dict: dict = {}
|
||||
|
||||
# need storage
|
||||
self.download_filter: str = None
|
||||
self.ids_to_retry: list = []
|
||||
self.last_read_message_id = 0
|
||||
self.total_task: int = 0
|
||||
self.finish_task: int = 0
|
||||
self.need_check: bool = False
|
||||
self.upload_telegram_chat_id: Union[int, str] = None
|
||||
|
||||
|
||||
class Application:
|
||||
"""Application load config and update config."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config_file: str,
|
||||
app_data_file: str,
|
||||
application_name: str = "UndefineApp",
|
||||
):
|
||||
"""
|
||||
Init and update telegram media downloader config
|
||||
|
||||
Parameters
|
||||
----------
|
||||
config_file: str
|
||||
Config file name
|
||||
|
||||
app_data_file: str
|
||||
App data file
|
||||
|
||||
application_name: str
|
||||
Application Name
|
||||
|
||||
"""
|
||||
self.config_file: str = config_file
|
||||
self.app_data_file: str = app_data_file
|
||||
self.application_name: str = application_name
|
||||
self.download_filter = Filter()
|
||||
self.is_running = True
|
||||
|
||||
self.total_download_task = 0
|
||||
|
||||
self.chat_download_config: dict = {}
|
||||
|
||||
self.disable_syslog: list = []
|
||||
self.save_path = os.path.join(os.path.abspath(""), "src/parsers/Telegram/telegram_media_downloader", "downloads")
|
||||
self.temp_save_path = os.path.join(os.path.abspath(""), "src/parsers/Telegram/telegram_media_downloader", "temp")
|
||||
self.api_id: str = ""
|
||||
self.api_hash: str = ""
|
||||
self.bot_token: str = ""
|
||||
self._chat_id: str = ""
|
||||
self.media_types: List[str] = []
|
||||
self.file_formats: dict = {}
|
||||
self.proxy: dict = {}
|
||||
self.restart_program = False
|
||||
self.config: dict = {}
|
||||
self.app_data: dict = {}
|
||||
self.file_path_prefix: List[str] = ["chat_title", "media_datetime"]
|
||||
self.file_name_prefix: List[str] = ["message_id", "file_name"]
|
||||
self.file_name_prefix_split: str = " - "
|
||||
self.log_file_path = os.path.join(os.path.abspath(""), "src/parsers/Telegram/telegram_media_downloader", "log")
|
||||
self.session_file_path = os.path.join(os.path.abspath(""), "src/parsers/Telegram/telegram_media_downloader", "sessions")
|
||||
self.cloud_drive_config = CloudDriveConfig()
|
||||
self.hide_file_name = False
|
||||
self.caption_name_dict: dict = {}
|
||||
self.max_concurrent_transmissions: int = 1
|
||||
self.web_host: str = "0.0.0.0"
|
||||
self.web_port: int = 5000
|
||||
self.max_download_task: int = 5
|
||||
self.language = Language.EN
|
||||
self.after_upload_telegram_delete: bool = True
|
||||
self.web_login_secret: str = ""
|
||||
self.debug_web: bool = False
|
||||
|
||||
self.loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(self.loop)
|
||||
|
||||
self.executor = ThreadPoolExecutor(
|
||||
min(32, (os.cpu_count() or 0) + 4), thread_name_prefix="multi_task"
|
||||
)
|
||||
|
||||
# pylint: disable = R0915
|
||||
def assign_config(self, _config: dict) -> bool:
|
||||
"""assign config from str.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
_config: dict
|
||||
application config dict
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
"""
|
||||
# pylint: disable = R0912
|
||||
# TODO: judge the storage if enough,and provide more path
|
||||
if _config.get("save_path") is not None:
|
||||
self.save_path = _config["save_path"]
|
||||
|
||||
if _config.get("disable_syslog") is not None:
|
||||
self.disable_syslog = _config["disable_syslog"]
|
||||
|
||||
self.api_id = _config["api_id"]
|
||||
self.api_hash = _config["api_hash"]
|
||||
self.bot_token = _config.get("bot_token", "")
|
||||
|
||||
self.media_types = _config["media_types"]
|
||||
self.file_formats = _config["file_formats"]
|
||||
|
||||
self.hide_file_name = _config.get("hide_file_name", False)
|
||||
|
||||
# option
|
||||
if _config.get("proxy"):
|
||||
self.proxy = _config["proxy"]
|
||||
if _config.get("restart_program"):
|
||||
self.restart_program = _config["restart_program"]
|
||||
if _config.get("file_path_prefix"):
|
||||
self.file_path_prefix = _config["file_path_prefix"]
|
||||
if _config.get("file_name_prefix"):
|
||||
self.file_name_prefix = _config["file_name_prefix"]
|
||||
|
||||
if _config.get("upload_drive"):
|
||||
upload_drive_config = _config["upload_drive"]
|
||||
if upload_drive_config.get("enable_upload_file"):
|
||||
self.cloud_drive_config.enable_upload_file = upload_drive_config[
|
||||
"enable_upload_file"
|
||||
]
|
||||
|
||||
if upload_drive_config.get("rclone_path"):
|
||||
self.cloud_drive_config.rclone_path = upload_drive_config["rclone_path"]
|
||||
|
||||
if upload_drive_config.get("remote_dir"):
|
||||
self.cloud_drive_config.remote_dir = upload_drive_config["remote_dir"]
|
||||
|
||||
if upload_drive_config.get("before_upload_file_zip"):
|
||||
self.cloud_drive_config.before_upload_file_zip = upload_drive_config[
|
||||
"before_upload_file_zip"
|
||||
]
|
||||
|
||||
if upload_drive_config.get("after_upload_file_delete"):
|
||||
self.cloud_drive_config.after_upload_file_delete = upload_drive_config[
|
||||
"after_upload_file_delete"
|
||||
]
|
||||
|
||||
if upload_drive_config.get("upload_adapter"):
|
||||
self.cloud_drive_config.upload_adapter = upload_drive_config[
|
||||
"upload_adapter"
|
||||
]
|
||||
|
||||
self.file_name_prefix_split = _config.get(
|
||||
"file_name_prefix_split", self.file_name_prefix_split
|
||||
)
|
||||
self.web_host = _config.get("web_host", self.web_host)
|
||||
self.web_port = _config.get("web_port", self.web_port)
|
||||
|
||||
# TODO: add check if expression exist syntax error
|
||||
|
||||
self.max_concurrent_transmissions = _config.get(
|
||||
"max_concurrent_transmissions", self.max_concurrent_transmissions
|
||||
)
|
||||
|
||||
self.max_download_task = _config.get(
|
||||
"max_download_task", self.max_download_task
|
||||
)
|
||||
|
||||
language = _config.get("language", "EN")
|
||||
|
||||
try:
|
||||
self.language = Language[language.upper()]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
self.after_upload_telegram_delete = _config.get(
|
||||
"after_upload_telegram_delete", self.after_upload_telegram_delete
|
||||
)
|
||||
|
||||
self.web_login_secret = str(
|
||||
_config.get("web_login_secret", self.web_login_secret)
|
||||
)
|
||||
self.debug_web = _config.get("debug_web", self.debug_web)
|
||||
|
||||
if _config.get("chat"):
|
||||
chat = _config["chat"]
|
||||
for item in chat:
|
||||
if "chat_id" in item:
|
||||
self.chat_download_config[item["chat_id"]] = ChatDownloadConfig()
|
||||
self.chat_download_config[
|
||||
item["chat_id"]
|
||||
].last_read_message_id = item.get("last_read_message_id", 0)
|
||||
self.chat_download_config[
|
||||
item["chat_id"]
|
||||
].download_filter = item.get("download_filter", "")
|
||||
self.chat_download_config[
|
||||
item["chat_id"]
|
||||
].upload_telegram_chat_id = item.get(
|
||||
"upload_telegram_chat_id", None
|
||||
)
|
||||
elif _config.get("chat_id"):
|
||||
# Compatible with lower versions
|
||||
self._chat_id = _config["chat_id"]
|
||||
|
||||
self.chat_download_config[self._chat_id] = ChatDownloadConfig()
|
||||
|
||||
if _config.get("ids_to_retry"):
|
||||
self.chat_download_config[self._chat_id].ids_to_retry = _config[
|
||||
"ids_to_retry"
|
||||
]
|
||||
for it in self.chat_download_config[self._chat_id].ids_to_retry:
|
||||
self.chat_download_config[self._chat_id].ids_to_retry_dict[
|
||||
it
|
||||
] = True
|
||||
|
||||
self.chat_download_config[self._chat_id].last_read_message_id = _config[
|
||||
"last_read_message_id"
|
||||
]
|
||||
download_filter_dict = _config.get("download_filter", None)
|
||||
|
||||
self.config["chat"] = [
|
||||
{
|
||||
"chat_id": self._chat_id,
|
||||
"last_read_message_id": self.chat_download_config[
|
||||
self._chat_id
|
||||
].last_read_message_id,
|
||||
}
|
||||
]
|
||||
|
||||
if download_filter_dict and self._chat_id in download_filter_dict:
|
||||
self.chat_download_config[
|
||||
self._chat_id
|
||||
].download_filter = download_filter_dict[self._chat_id]
|
||||
self.config["chat"][0]["download_filter"] = download_filter_dict[
|
||||
self._chat_id
|
||||
]
|
||||
|
||||
# pylint: disable = R1733
|
||||
for key, value in self.chat_download_config.items():
|
||||
self.chat_download_config[key].download_filter = replace_date_time(
|
||||
value.download_filter
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
def assign_app_data(self, app_data: dict) -> bool:
|
||||
"""Assign config from str.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
app_data: dict
|
||||
application data dict
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
"""
|
||||
if app_data.get("ids_to_retry"):
|
||||
if self._chat_id:
|
||||
self.chat_download_config[self._chat_id].ids_to_retry = app_data[
|
||||
"ids_to_retry"
|
||||
]
|
||||
for it in self.chat_download_config[self._chat_id].ids_to_retry:
|
||||
self.chat_download_config[self._chat_id].ids_to_retry_dict[
|
||||
it
|
||||
] = True
|
||||
self.app_data.pop("ids_to_retry")
|
||||
else:
|
||||
if app_data.get("chat"):
|
||||
chats = app_data["chat"]
|
||||
for chat in chats:
|
||||
if (
|
||||
"chat_id" in chat
|
||||
and chat["chat_id"] in self.chat_download_config
|
||||
):
|
||||
chat_id = chat["chat_id"]
|
||||
self.chat_download_config[chat_id].ids_to_retry = chat.get(
|
||||
"ids_to_retry", []
|
||||
)
|
||||
for it in self.chat_download_config[chat_id].ids_to_retry:
|
||||
self.chat_download_config[chat_id].ids_to_retry_dict[
|
||||
it
|
||||
] = True
|
||||
return True
|
||||
|
||||
async def upload_file(self, local_file_path: str) -> bool:
|
||||
"""Upload file"""
|
||||
|
||||
if not self.cloud_drive_config.enable_upload_file:
|
||||
return False
|
||||
|
||||
ret: bool = False
|
||||
if self.cloud_drive_config.upload_adapter == "rclone":
|
||||
ret = await CloudDrive.rclone_upload_file(
|
||||
self.cloud_drive_config, self.save_path, local_file_path
|
||||
)
|
||||
elif self.cloud_drive_config.upload_adapter == "aligo":
|
||||
ret = await self.loop.run_in_executor(
|
||||
self.executor,
|
||||
CloudDrive.aligo_upload_file(
|
||||
self.cloud_drive_config, self.save_path, local_file_path
|
||||
),
|
||||
)
|
||||
|
||||
return ret
|
||||
|
||||
def get_file_save_path(
|
||||
self, media_type: str, chat_title: str, media_datetime: str
|
||||
) -> str:
|
||||
"""Get file save path prefix.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
media_type: str
|
||||
see config.yaml media_types
|
||||
|
||||
chat_title: str
|
||||
see channel or group title
|
||||
|
||||
media_datetime: str
|
||||
media datetime
|
||||
|
||||
Returns
|
||||
-------
|
||||
str
|
||||
file save path prefix
|
||||
"""
|
||||
|
||||
res: str = self.save_path
|
||||
for prefix in self.file_path_prefix:
|
||||
if prefix == "chat_title":
|
||||
res = os.path.join(res, chat_title)
|
||||
elif prefix == "media_datetime":
|
||||
res = os.path.join(res, media_datetime)
|
||||
elif prefix == "media_type":
|
||||
res = os.path.join(res, media_type)
|
||||
return res
|
||||
|
||||
def get_file_name(
|
||||
self, message_id: int, file_name: Optional[str], caption: Optional[str]
|
||||
) -> str:
|
||||
"""Get file save path prefix.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
message_id: int
|
||||
Message id
|
||||
|
||||
file_name: Optional[str]
|
||||
File name
|
||||
|
||||
caption: Optional[str]
|
||||
Message caption
|
||||
|
||||
Returns
|
||||
-------
|
||||
str
|
||||
File name
|
||||
"""
|
||||
|
||||
res: str = ""
|
||||
for prefix in self.file_name_prefix:
|
||||
if prefix == "message_id":
|
||||
if res != "":
|
||||
res += self.file_name_prefix_split
|
||||
res += f"{message_id}"
|
||||
elif prefix == "file_name" and file_name:
|
||||
if res != "":
|
||||
res += self.file_name_prefix_split
|
||||
res += f"{file_name}"
|
||||
elif prefix == "caption" and caption:
|
||||
if res != "":
|
||||
res += self.file_name_prefix_split
|
||||
res += f"{caption}"
|
||||
if res == "":
|
||||
res = f"{message_id}"
|
||||
|
||||
return validate_title(res)
|
||||
|
||||
def need_skip_message(
|
||||
self, download_config: ChatDownloadConfig, message_id: int, meta_data: MetaData
|
||||
) -> bool:
|
||||
"""if need skip download message.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
chat_id: str
|
||||
Config.yaml defined
|
||||
|
||||
message_id: int
|
||||
Readily to download message id
|
||||
|
||||
meta_data: MetaData
|
||||
Ready to match filter
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
"""
|
||||
if message_id in download_config.ids_to_retry_dict:
|
||||
return True
|
||||
|
||||
if download_config.download_filter:
|
||||
self.download_filter.set_meta_data(meta_data)
|
||||
exec_res = not self.download_filter.exec(download_config.download_filter)
|
||||
return exec_res
|
||||
|
||||
return False
|
||||
|
||||
def update_config(self, immediate: bool = True):
|
||||
"""update config
|
||||
|
||||
Parameters
|
||||
----------
|
||||
immediate: bool
|
||||
If update config immediate,default True
|
||||
"""
|
||||
# TODO: fix this not exist chat
|
||||
if not self.app_data.get("chat") and self.config.get("chat"):
|
||||
self.app_data["chat"] = [
|
||||
{"chat_id": i} for i in range(0, len(self.config["chat"]))
|
||||
]
|
||||
idx = 0
|
||||
# pylint: disable = R1733
|
||||
for key, value in self.chat_download_config.items():
|
||||
# pylint: disable = W0201
|
||||
before_last_read_message_id = self.config["chat"][idx].get(
|
||||
"last_read_message_id", 0
|
||||
)
|
||||
|
||||
unfinished_ids = set(value.ids_to_retry)
|
||||
if before_last_read_message_id != value.last_read_message_id:
|
||||
unfinished_ids = unfinished_ids | set(
|
||||
range(before_last_read_message_id, value.last_read_message_id + 1)
|
||||
)
|
||||
unfinished_ids -= set(value.downloaded_ids)
|
||||
unfinished_ids -= set({0})
|
||||
|
||||
self.chat_download_config[key].ids_to_retry = list(unfinished_ids)
|
||||
|
||||
if idx >= len(self.app_data["chat"]):
|
||||
self.app_data["chat"].append({})
|
||||
|
||||
if value.finish_task:
|
||||
self.config["chat"][idx]["last_read_message_id"] = (
|
||||
value.last_read_message_id + 1
|
||||
)
|
||||
|
||||
self.app_data["chat"][idx]["chat_id"] = key
|
||||
self.app_data["chat"][idx]["ids_to_retry"] = value.ids_to_retry
|
||||
idx += 1
|
||||
|
||||
self.config["disable_syslog"] = self.disable_syslog
|
||||
self.config["save_path"] = self.save_path
|
||||
self.config["file_path_prefix"] = self.file_path_prefix
|
||||
|
||||
if self.config.get("ids_to_retry"):
|
||||
self.config.pop("ids_to_retry")
|
||||
|
||||
if self.config.get("chat_id"):
|
||||
self.config.pop("chat_id")
|
||||
|
||||
if self.config.get("download_filter"):
|
||||
self.config.pop("download_filter")
|
||||
|
||||
if self.config.get("last_read_message_id"):
|
||||
self.config.pop("last_read_message_id")
|
||||
|
||||
self.config["language"] = self.language.name
|
||||
for it in self.downloaded_ids:
|
||||
self.already_download_ids_set.add(it)
|
||||
|
||||
self.app_data["already_download_ids"] = list(self.already_download_ids_set)
|
||||
|
||||
if immediate:
|
||||
with open(self.config_file, "w", encoding="utf-8") as yaml_file:
|
||||
_yaml.dump(self.config, yaml_file)
|
||||
|
||||
if immediate:
|
||||
with open(self.app_data_file, "w", encoding="utf-8") as yaml_file:
|
||||
_yaml.dump(self.app_data, yaml_file)
|
||||
|
||||
def set_language(self, language: Language):
|
||||
"""Set Language"""
|
||||
self.language = language
|
||||
set_language(language)
|
||||
|
||||
def load_config(self):
|
||||
"""Load user config"""
|
||||
with open(
|
||||
os.path.join(os.path.abspath(""), "src/parsers/Telegram/telegram_media_downloader", self.config_file), encoding="utf-8"
|
||||
) as f:
|
||||
config = _yaml.load(f.read())
|
||||
if config:
|
||||
self.config = config
|
||||
self.assign_config(self.config)
|
||||
|
||||
if os.path.exists(os.path.join(os.path.abspath(""), self.app_data_file)):
|
||||
with open(
|
||||
os.path.join(os.path.abspath(""), self.app_data_file),
|
||||
encoding="utf-8",
|
||||
) as f:
|
||||
app_data = _yaml.load(f.read())
|
||||
if app_data:
|
||||
self.app_data = app_data
|
||||
self.assign_app_data(self.app_data)
|
||||
|
||||
def pre_run(self):
|
||||
"""before run application do"""
|
||||
self.cloud_drive_config.pre_run()
|
||||
if not os.path.exists(self.session_file_path):
|
||||
os.makedirs(self.session_file_path)
|
||||
set_language(self.language)
|
||||
|
||||
def set_caption_name(
|
||||
self, chat_id: Union[int, str], media_group_id: Optional[str], caption: str
|
||||
):
|
||||
"""set caption name map
|
||||
|
||||
Parameters
|
||||
----------
|
||||
chat_id: str
|
||||
Unique identifier for this chat.
|
||||
|
||||
media_group_id: Optional[str]
|
||||
The unique identifier of a media message group this message belongs to.
|
||||
|
||||
caption: str
|
||||
Caption for the audio, document, photo, video or voice, 0-1024 characters.
|
||||
"""
|
||||
if not media_group_id:
|
||||
return
|
||||
|
||||
if chat_id in self.caption_name_dict:
|
||||
self.caption_name_dict[chat_id][media_group_id] = caption
|
||||
else:
|
||||
self.caption_name_dict[chat_id] = {media_group_id: caption}
|
||||
|
||||
def get_caption_name(
|
||||
self, chat_id: Union[int, str], media_group_id: Optional[str]
|
||||
) -> Optional[str]:
|
||||
"""set caption name map
|
||||
media_group_id: Optional[str]
|
||||
The unique identifier of a media message group this message belongs to.
|
||||
|
||||
caption: str
|
||||
Caption for the audio, document, photo, video or voice, 0-1024 characters.
|
||||
"""
|
||||
|
||||
if (
|
||||
not media_group_id
|
||||
or chat_id not in self.caption_name_dict
|
||||
or media_group_id not in self.caption_name_dict[chat_id]
|
||||
):
|
||||
return None
|
||||
|
||||
return str(self.caption_name_dict[chat_id][media_group_id])
|
||||
|
||||
def set_download_id(
|
||||
self, chat_id: Union[int, str], message_id: int, download_status: DownloadStatus
|
||||
):
|
||||
"""Set Download status"""
|
||||
if download_status is DownloadStatus.SuccessDownload:
|
||||
self.total_download_task += 1
|
||||
|
||||
if chat_id not in self.chat_download_config:
|
||||
return
|
||||
|
||||
self.chat_download_config[chat_id].finish_task += 1
|
||||
|
||||
self.chat_download_config[chat_id].last_read_message_id = max(
|
||||
self.chat_download_config[chat_id].last_read_message_id, message_id
|
||||
)
|
||||
if download_status is not DownloadStatus.FailedDownload:
|
||||
self.chat_download_config[chat_id].downloaded_ids.append(message_id)
|
||||
else:
|
||||
self.chat_download_config[chat_id].failed_ids.append(message_id)
|
929
src/parsers/Telegram/telegram_media_downloader/module/bot.py
Normal file
@ -0,0 +1,929 @@
|
||||
"""Bot for media downloader"""
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
from datetime import datetime
|
||||
from typing import Callable, List, Union
|
||||
|
||||
import pyrogram
|
||||
from loguru import logger
|
||||
from pyrogram import types
|
||||
from pyrogram.handlers import MessageHandler
|
||||
from ruamel import yaml
|
||||
|
||||
import utils
|
||||
from module.app import (
|
||||
Application,
|
||||
ChatDownloadConfig,
|
||||
ForwardStatus,
|
||||
TaskNode,
|
||||
TaskType,
|
||||
)
|
||||
from module.filter import Filter
|
||||
from module.language import Language, _t
|
||||
from module.pyrogram_extension import (
|
||||
check_user_permission,
|
||||
get_message_with_retry,
|
||||
report_bot_forward_status,
|
||||
report_bot_status,
|
||||
set_meta_data,
|
||||
)
|
||||
from utils.format import extract_info_from_link, replace_date_time, validate_title
|
||||
from utils.meta_data import MetaData
|
||||
|
||||
# from pyrogram.types import (ReplyKeyboardMarkup, InlineKeyboardMarkup,
|
||||
# InlineKeyboardButton)
|
||||
|
||||
# pylint: disable = C0301, R0902
|
||||
|
||||
|
||||
class DownloadBot:
|
||||
"""Download bot"""
|
||||
|
||||
def __init__(self):
|
||||
self.bot = None
|
||||
self.client = None
|
||||
self.add_download_task: Callable = None
|
||||
self.download_chat_task: Callable = None
|
||||
self.app = None
|
||||
self.listen_forward_chat: dict = {}
|
||||
self.config: dict = {}
|
||||
self._yaml = yaml.YAML()
|
||||
self.config_path = os.path.join(os.path.abspath(""), "bot.yaml")
|
||||
self.download_command: dict = {}
|
||||
self.filter = Filter()
|
||||
self.bot_info = None
|
||||
self.task_node: dict = {}
|
||||
self.is_running = True
|
||||
|
||||
meta = MetaData(datetime(2022, 8, 5, 14, 35, 12), 0, "", 0, 0, 0, "", 0)
|
||||
self.filter.set_meta_data(meta)
|
||||
|
||||
self.download_filter: List[str] = []
|
||||
self.task_id: int = 0
|
||||
|
||||
def gen_task_id(self) -> int:
|
||||
"""Gen task id"""
|
||||
self.task_id += 1
|
||||
return self.task_id
|
||||
|
||||
def add_task_node(self, node: TaskNode):
|
||||
"""Add task node"""
|
||||
self.task_node[node.task_id] = node
|
||||
|
||||
def remove_task_node(self, task_id: int):
|
||||
"""Remove task node"""
|
||||
self.task_node.pop(task_id)
|
||||
|
||||
async def update_reply_message(self):
|
||||
"""Update reply message"""
|
||||
while self.is_running:
|
||||
for key, value in self.task_node.copy().items():
|
||||
if value.is_running:
|
||||
await report_bot_status(self.bot, value)
|
||||
|
||||
for key, value in self.task_node.copy().items():
|
||||
if value.is_running and value.is_finish():
|
||||
self.task_node.pop(key)
|
||||
await asyncio.sleep(3)
|
||||
|
||||
def assign_config(self, _config: dict):
|
||||
"""assign config from str.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
_config: dict
|
||||
application config dict
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
"""
|
||||
|
||||
self.download_filter = _config.get("download_filter", self.download_filter)
|
||||
|
||||
return True
|
||||
|
||||
def update_config(self):
|
||||
"""Update config from str."""
|
||||
self.config["download_filter"] = self.download_filter
|
||||
|
||||
with open("d", "w", encoding="utf-8") as yaml_file:
|
||||
self._yaml.dump(self.config, yaml_file)
|
||||
|
||||
async def start(
|
||||
self,
|
||||
app: Application,
|
||||
client: pyrogram.Client,
|
||||
add_download_task: Callable,
|
||||
download_chat_task: Callable,
|
||||
):
|
||||
"""Start bot"""
|
||||
self.bot = pyrogram.Client(
|
||||
app.application_name + "_bot",
|
||||
api_hash=app.api_hash,
|
||||
api_id=app.api_id,
|
||||
bot_token=app.bot_token,
|
||||
workdir=app.session_file_path,
|
||||
proxy=app.proxy,
|
||||
)
|
||||
|
||||
# 命令列表
|
||||
commands = [
|
||||
types.BotCommand("help", _t("Help")),
|
||||
types.BotCommand(
|
||||
"get_info", _t("Get group and user info from message link")
|
||||
),
|
||||
types.BotCommand(
|
||||
"download",
|
||||
_t(
|
||||
"To download the video, use the method to directly enter /download to view"
|
||||
),
|
||||
),
|
||||
types.BotCommand(
|
||||
"forward",
|
||||
_t("Forward video, use the method to directly enter /forward to view"),
|
||||
),
|
||||
types.BotCommand(
|
||||
"listen_forward",
|
||||
_t(
|
||||
"Listen forward, use the method to directly enter /listen_forward to view"
|
||||
),
|
||||
),
|
||||
types.BotCommand(
|
||||
"add_filter",
|
||||
_t(
|
||||
"Add download filter, use the method to directly enter /add_filter to view"
|
||||
),
|
||||
),
|
||||
types.BotCommand("set_language", _t("Set language")),
|
||||
]
|
||||
|
||||
self.bot.add_handler(
|
||||
MessageHandler(
|
||||
download_from_bot, filters=pyrogram.filters.command(["download"])
|
||||
)
|
||||
)
|
||||
self.bot.add_handler(
|
||||
MessageHandler(
|
||||
forward_messages, filters=pyrogram.filters.command(["forward"])
|
||||
)
|
||||
)
|
||||
self.bot.add_handler(
|
||||
MessageHandler(download_forward_media, filters=pyrogram.filters.media)
|
||||
)
|
||||
self.bot.add_handler(
|
||||
MessageHandler(
|
||||
download_from_link, filters=pyrogram.filters.regex(r"^https://t.me.*")
|
||||
)
|
||||
)
|
||||
self.bot.add_handler(
|
||||
MessageHandler(
|
||||
set_listen_forward_msg,
|
||||
filters=pyrogram.filters.command(["listen_forward"]),
|
||||
)
|
||||
)
|
||||
self.bot.add_handler(
|
||||
MessageHandler(help_command, filters=pyrogram.filters.command(["help"]))
|
||||
)
|
||||
self.bot.add_handler(
|
||||
MessageHandler(get_info, filters=pyrogram.filters.command(["get_info"]))
|
||||
)
|
||||
self.bot.add_handler(
|
||||
MessageHandler(help_command, filters=pyrogram.filters.command(["start"]))
|
||||
)
|
||||
self.bot.add_handler(
|
||||
MessageHandler(
|
||||
set_language, filters=pyrogram.filters.command(["set_language"])
|
||||
)
|
||||
)
|
||||
self.bot.add_handler(
|
||||
MessageHandler(add_filter, filters=pyrogram.filters.command(["add_filter"]))
|
||||
)
|
||||
self.client = client
|
||||
|
||||
self.client.add_handler(MessageHandler(listen_forward_msg))
|
||||
|
||||
self.add_download_task = add_download_task
|
||||
self.download_chat_task = download_chat_task
|
||||
|
||||
self.app = app
|
||||
|
||||
# load config
|
||||
if os.path.exists(self.config_path):
|
||||
with open(self.config_path, encoding="utf-8") as f:
|
||||
config = self._yaml.load(f.read())
|
||||
if config:
|
||||
self.config = config
|
||||
self.assign_config(self.config)
|
||||
|
||||
await self.bot.start()
|
||||
|
||||
self.bot_info = self.bot.get_me()
|
||||
|
||||
# 添加命令列表
|
||||
await self.bot.set_bot_commands(commands)
|
||||
|
||||
admin = await self.client.get_me()
|
||||
|
||||
try:
|
||||
await send_help_str(self.bot, admin.id)
|
||||
except Exception:
|
||||
pass
|
||||
# TODO: add admin
|
||||
# self.bot.set_my_commands(commands, scope=types.BotCommandScopeChatAdministrators(self.app.))
|
||||
|
||||
_bot.app.loop.create_task(_bot.update_reply_message())
|
||||
|
||||
|
||||
_bot = DownloadBot()
|
||||
|
||||
|
||||
async def start_download_bot(
|
||||
app: Application,
|
||||
client: pyrogram.Client,
|
||||
add_download_task: Callable,
|
||||
download_chat_task: Callable,
|
||||
):
|
||||
"""Start download bot"""
|
||||
await _bot.start(app, client, add_download_task, download_chat_task)
|
||||
|
||||
|
||||
def stop_download_bot():
|
||||
"""Stop download bot"""
|
||||
_bot.update_config()
|
||||
_bot.is_running = False
|
||||
|
||||
|
||||
async def send_help_str(client: pyrogram.Client, chat_id):
|
||||
"""
|
||||
Sends a help string to the specified chat ID using the provided client.
|
||||
|
||||
Parameters:
|
||||
client (pyrogram.Client): The Pyrogram client used to send the message.
|
||||
chat_id: The ID of the chat to which the message will be sent.
|
||||
|
||||
Returns:
|
||||
str: The help string that was sent.
|
||||
|
||||
Note:
|
||||
The help string includes information about the Telegram Media Downloader bot,
|
||||
its version, and the available commands.
|
||||
"""
|
||||
msg = (
|
||||
f"```\n🤖 {_t('Telegram Media Downloader')}\n"
|
||||
f"🌐 {_t('Version')}: {utils.__version__}```\n\n"
|
||||
f"{_t('Available commands:')}\n"
|
||||
f"/help - {_t('Show available commands')}\n"
|
||||
f"/get_info - {_t('Get group and user info from message link')}\n"
|
||||
# f"/add_filter - {_t('Add download filter')}\n"
|
||||
f"/download - {_t('Download messages')}\n"
|
||||
f"/forward - {_t('Forward messages')}\n"
|
||||
f"/listen_forward - {_t('Listen for forwarded messages')}\n"
|
||||
f"/set_language - {_t('Set language')}\n\n"
|
||||
f"{_t('**Note**: 1 means the start of the entire chat')},"
|
||||
f"{_t('0 means the end of the entire chat')}\n"
|
||||
f"`[` `]` {_t('means optional, not required')}\n"
|
||||
)
|
||||
|
||||
await client.send_message(chat_id, msg)
|
||||
|
||||
|
||||
async def help_command(client: pyrogram.Client, message: pyrogram.types.Message):
|
||||
"""
|
||||
Sends a message with the available commands and their usage.
|
||||
|
||||
Parameters:
|
||||
client (pyrogram.Client): The client instance.
|
||||
message (pyrogram.types.Message): The message object.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
await send_help_str(client, message.chat.id)
|
||||
|
||||
|
||||
async def set_language(client: pyrogram.Client, message: pyrogram.types.Message):
|
||||
"""
|
||||
Set the language of the bot.
|
||||
|
||||
Parameters:
|
||||
client (pyrogram.Client): The pyrogram client.
|
||||
message (pyrogram.types.Message): The message containing the command.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
if len(message.text.split()) != 2:
|
||||
await client.send_message(
|
||||
message.from_user.id,
|
||||
_t("Invalid command format. Please use /set_language en/ru/zh/ua"),
|
||||
)
|
||||
return
|
||||
|
||||
language = message.text.split()[1]
|
||||
|
||||
try:
|
||||
language = Language[language.upper()]
|
||||
_bot.app.set_language(language)
|
||||
await client.send_message(
|
||||
message.from_user.id, f"{_t('Language set to')} {language.name}"
|
||||
)
|
||||
except KeyError:
|
||||
await client.send_message(
|
||||
message.from_user.id,
|
||||
_t("Invalid command format. Please use /set_language en/ru/zh/ua"),
|
||||
)
|
||||
|
||||
|
||||
async def get_info(client: pyrogram.Client, message: pyrogram.types.Message):
|
||||
"""
|
||||
Async function that retrieves information from a group message link.
|
||||
"""
|
||||
|
||||
msg = _t("Invalid command format. Please use /get_info group_message_link")
|
||||
|
||||
args = message.text.split()
|
||||
if len(args) != 2:
|
||||
await client.send_message(
|
||||
message.from_user.id,
|
||||
msg,
|
||||
)
|
||||
return
|
||||
|
||||
chat_id, message_id = extract_info_from_link(args[1])
|
||||
|
||||
entity = None
|
||||
if chat_id:
|
||||
entity = await _bot.client.get_chat(chat_id)
|
||||
|
||||
if entity:
|
||||
if message_id:
|
||||
_message = await get_message_with_retry(_bot.client, chat_id, message_id)
|
||||
if _message:
|
||||
meta_data = MetaData()
|
||||
set_meta_data(meta_data, _message)
|
||||
msg = (
|
||||
f"```\n"
|
||||
f"{_t('Group/Channel')}\n"
|
||||
f"├─ {_t('id')}: {entity.id}\n"
|
||||
f"├─ {_t('first name')}: {entity.first_name}\n"
|
||||
f"├─ {_t('last name')}: {entity.last_name}\n"
|
||||
f"└─ {_t('name')}: {entity.username}\n"
|
||||
f"{_t('Message')}\n"
|
||||
)
|
||||
|
||||
for key, value in meta_data.data().items():
|
||||
if key == "send_name":
|
||||
msg += f"└─ {key}: {value or None}\n"
|
||||
else:
|
||||
msg += f"├─ {key}: {value or None}\n"
|
||||
|
||||
msg += "```"
|
||||
await client.send_message(
|
||||
message.from_user.id,
|
||||
msg,
|
||||
)
|
||||
|
||||
|
||||
async def add_filter(client: pyrogram.Client, message: pyrogram.types.Message):
|
||||
"""
|
||||
Set the download filter of the bot.
|
||||
|
||||
Parameters:
|
||||
client (pyrogram.Client): The pyrogram client.
|
||||
message (pyrogram.types.Message): The message containing the command.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
args = message.text.split(maxsplit=1)
|
||||
if len(args) != 2:
|
||||
await client.send_message(
|
||||
message.from_user.id,
|
||||
_t("Invalid command format. Please use /add_filter your filter"),
|
||||
)
|
||||
return
|
||||
|
||||
filter_str = replace_date_time(args[1])
|
||||
res, err = _bot.filter.check_filter(filter_str)
|
||||
if res:
|
||||
_bot.app.down = args[1]
|
||||
await client.send_message(
|
||||
message.from_user.id, f"{_t('Add download filter')} : {args[1]}"
|
||||
)
|
||||
else:
|
||||
await client.send_message(
|
||||
message.from_user.id, f"{err}\n{_t('Check error, please add again!')}"
|
||||
)
|
||||
return
|
||||
|
||||
|
||||
async def direct_download(
|
||||
download_bot: DownloadBot,
|
||||
chat_id: Union[str, int],
|
||||
message: pyrogram.types.Message,
|
||||
download_message: pyrogram.types.Message,
|
||||
client: pyrogram.Client = None,
|
||||
):
|
||||
"""Direct Download"""
|
||||
|
||||
replay_message = "Direct download..."
|
||||
last_reply_message = await download_bot.bot.send_message(
|
||||
message.from_user.id, replay_message, reply_to_message_id=message.id
|
||||
)
|
||||
|
||||
node = TaskNode(
|
||||
chat_id=chat_id,
|
||||
from_user_id=message.from_user.id,
|
||||
reply_message_id=last_reply_message.id,
|
||||
replay_message=replay_message,
|
||||
limit=1,
|
||||
bot=download_bot.bot,
|
||||
task_id=_bot.gen_task_id(),
|
||||
)
|
||||
|
||||
node.client = client
|
||||
|
||||
_bot.add_task_node(node)
|
||||
|
||||
await _bot.add_download_task(
|
||||
download_message,
|
||||
node,
|
||||
)
|
||||
|
||||
node.is_running = True
|
||||
|
||||
|
||||
async def download_forward_media(
|
||||
client: pyrogram.Client, message: pyrogram.types.Message
|
||||
):
|
||||
"""
|
||||
Downloads the media from a forwarded message.
|
||||
|
||||
Parameters:
|
||||
client (pyrogram.Client): The client instance.
|
||||
message (pyrogram.types.Message): The message object.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
if message.media and getattr(message, message.media.value):
|
||||
await direct_download(_bot, message.from_user.id, message, message, client)
|
||||
return
|
||||
|
||||
await client.send_message(
|
||||
message.from_user.id,
|
||||
f"1. {_t('Direct download, directly forward the message to your robot')}\n\n",
|
||||
parse_mode=pyrogram.enums.ParseMode.HTML,
|
||||
)
|
||||
|
||||
|
||||
async def download_from_link(client: pyrogram.Client, message: pyrogram.types.Message):
|
||||
"""
|
||||
Downloads a single message from a Telegram link.
|
||||
|
||||
Parameters:
|
||||
client (pyrogram.Client): The pyrogram client.
|
||||
message (pyrogram.types.Message): The message containing the Telegram link.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
if not message.text or not message.text.startswith("https://t.me"):
|
||||
return
|
||||
|
||||
msg = (
|
||||
f"1. {_t('Directly download a single message')}\n"
|
||||
"<i>https://t.me/12000000/1</i>\n\n"
|
||||
)
|
||||
|
||||
text = message.text.split()
|
||||
if len(text) != 1:
|
||||
await client.send_message(
|
||||
message.from_user.id, msg, parse_mode=pyrogram.enums.ParseMode.HTML
|
||||
)
|
||||
|
||||
chat_id, message_id = extract_info_from_link(text[0])
|
||||
|
||||
entity = None
|
||||
if chat_id:
|
||||
entity = await _bot.client.get_chat(chat_id)
|
||||
if entity:
|
||||
if message_id:
|
||||
download_message = await get_message_with_retry(
|
||||
_bot.client, chat_id, message_id
|
||||
)
|
||||
if download_message:
|
||||
await direct_download(_bot, entity.id, message, download_message)
|
||||
else:
|
||||
client.send_message(
|
||||
message.from_user.id,
|
||||
f"{_t('From')} {entity.title} {_t('download')} {message_id} {_t('error')}!",
|
||||
reply_to_message_id=message.id,
|
||||
)
|
||||
return
|
||||
|
||||
await client.send_message(
|
||||
message.from_user.id, msg, parse_mode=pyrogram.enums.ParseMode.HTML
|
||||
)
|
||||
|
||||
|
||||
# pylint: disable = R0912, R0915,R0914
|
||||
|
||||
|
||||
async def download_from_bot(client: pyrogram.Client, message: pyrogram.types.Message):
|
||||
"""Download from bot"""
|
||||
|
||||
msg = (
|
||||
f"{_t('Parameter error, please enter according to the reference format')}:\n\n"
|
||||
f"1. {_t('Download all messages of common group')}\n"
|
||||
"<i>/download https://t.me/fkdhlg 1 0</i>\n\n"
|
||||
f"{_t('The private group (channel) link is a random group message link')}\n\n"
|
||||
f"2. {_t('The download starts from the N message to the end of the M message')}. "
|
||||
f"{_t('When M is 0, it means the last message. The filter is optional')}\n"
|
||||
f"<i>/download https://t.me/12000000 N M [filter]</i>\n\n"
|
||||
)
|
||||
|
||||
args = message.text.split(maxsplit=4)
|
||||
if not message.text or len(args) < 4:
|
||||
await client.send_message(
|
||||
message.from_user.id, msg, parse_mode=pyrogram.enums.ParseMode.HTML
|
||||
)
|
||||
return
|
||||
|
||||
url = args[1]
|
||||
try:
|
||||
offset_id = int(args[2])
|
||||
limit = int(args[3])
|
||||
except Exception:
|
||||
await client.send_message(
|
||||
message.from_user.id, msg, parse_mode=pyrogram.enums.ParseMode.HTML
|
||||
)
|
||||
return
|
||||
|
||||
if limit:
|
||||
if limit < offset_id:
|
||||
raise ValueError("M > N")
|
||||
|
||||
limit = limit - offset_id + 1
|
||||
|
||||
download_filter = args[4] if len(args) > 4 else None
|
||||
|
||||
if download_filter:
|
||||
download_filter = replace_date_time(download_filter)
|
||||
res, err = _bot.filter.check_filter(download_filter)
|
||||
if not res:
|
||||
await client.send_message(
|
||||
message.from_user.id, err, reply_to_message_id=message.id
|
||||
)
|
||||
return
|
||||
try:
|
||||
chat_id, _ = extract_info_from_link(url)
|
||||
if chat_id:
|
||||
entity = await _bot.client.get_chat(chat_id)
|
||||
if entity:
|
||||
chat_title = entity.title
|
||||
reply_message = f"from {chat_title} "
|
||||
chat_download_config = ChatDownloadConfig()
|
||||
chat_download_config.last_read_message_id = offset_id
|
||||
chat_download_config.download_filter = download_filter
|
||||
reply_message += f"download message id = {offset_id} limit = {limit} !"
|
||||
last_reply_message = await client.send_message(
|
||||
message.from_user.id, reply_message, reply_to_message_id=message.id
|
||||
)
|
||||
node = TaskNode(
|
||||
chat_id=entity.id,
|
||||
from_user_id=message.from_user.id,
|
||||
reply_message_id=last_reply_message.id,
|
||||
replay_message=reply_message,
|
||||
limit=limit,
|
||||
bot=_bot.bot,
|
||||
task_id=_bot.gen_task_id(),
|
||||
)
|
||||
_bot.add_task_node(node)
|
||||
await _bot.download_chat_task(_bot.client, chat_download_config, node)
|
||||
except Exception as e:
|
||||
await client.send_message(
|
||||
message.from_user.id,
|
||||
f"{_t('chat input error, please enter the channel or group link')}\n\n"
|
||||
f"{_t('Error type')}: {e.__class__}"
|
||||
f"{_t('Exception message')}: {e}",
|
||||
)
|
||||
return
|
||||
|
||||
|
||||
async def get_forward_task_node(
|
||||
client: pyrogram.Client,
|
||||
message: pyrogram.types.Message,
|
||||
src_chat_link: str,
|
||||
dst_chat_link: str,
|
||||
offset_id: int,
|
||||
limit: int,
|
||||
download_filter: str,
|
||||
task_type: TaskType,
|
||||
):
|
||||
"""Get task node"""
|
||||
|
||||
if limit:
|
||||
if limit < offset_id:
|
||||
await client.send_message(
|
||||
message.from_user.id,
|
||||
f"{limit} > {offset_id}",
|
||||
)
|
||||
return None
|
||||
|
||||
limit = limit - offset_id + 1
|
||||
|
||||
src_chat_id, _ = extract_info_from_link(src_chat_link)
|
||||
dst_chat_id, _ = extract_info_from_link(dst_chat_link)
|
||||
|
||||
if not src_chat_id or not dst_chat_id:
|
||||
await client.send_message(
|
||||
message.from_user.id,
|
||||
_t("Invalid chat link"),
|
||||
reply_to_message_id=message.id,
|
||||
)
|
||||
return None
|
||||
|
||||
try:
|
||||
src_chat = await _bot.client.get_chat(src_chat_id)
|
||||
dst_chat = await _bot.client.get_chat(dst_chat_id)
|
||||
except Exception:
|
||||
await client.send_message(
|
||||
message.from_user.id,
|
||||
_t("Invalid chat link"),
|
||||
reply_to_message_id=message.id,
|
||||
)
|
||||
return None
|
||||
|
||||
me = await client.get_me()
|
||||
if dst_chat.id == me.id:
|
||||
# TODO: when bot receive message judge if download
|
||||
await client.send_message(
|
||||
message.from_user.id,
|
||||
_t("Cannot be forwarded to this bot, will cause an infinite loop"),
|
||||
reply_to_message_id=message.id,
|
||||
)
|
||||
return None
|
||||
|
||||
if download_filter:
|
||||
download_filter = replace_date_time(download_filter)
|
||||
res, err = _bot.filter.check_filter(download_filter)
|
||||
if not res:
|
||||
await client.send_message(
|
||||
message.from_user.id, err, reply_to_message_id=message.id
|
||||
)
|
||||
|
||||
last_reply_message = await client.send_message(
|
||||
message.from_user.id,
|
||||
"Forwarding message, please wait...",
|
||||
reply_to_message_id=message.id,
|
||||
)
|
||||
|
||||
node = TaskNode(
|
||||
chat_id=src_chat.id,
|
||||
from_user_id=message.from_user.id,
|
||||
upload_telegram_chat_id=dst_chat_id,
|
||||
reply_message_id=last_reply_message.id,
|
||||
replay_message=last_reply_message.text,
|
||||
has_protected_content=src_chat.has_protected_content,
|
||||
download_filter=download_filter,
|
||||
limit=limit,
|
||||
bot=_bot.bot,
|
||||
task_id=_bot.gen_task_id(),
|
||||
task_type=task_type,
|
||||
)
|
||||
_bot.add_task_node(node)
|
||||
|
||||
node.upload_user = _bot.client
|
||||
if not dst_chat.type is pyrogram.enums.ChatType.BOT:
|
||||
has_permission = await check_user_permission(_bot.client, me.id, dst_chat.id)
|
||||
if has_permission:
|
||||
node.upload_user = _bot.bot
|
||||
|
||||
if node.upload_user is _bot.client:
|
||||
await client.edit_message_text(
|
||||
message.from_user.id,
|
||||
last_reply_message.id,
|
||||
"Note that the robot may not be in the target group,"
|
||||
" use the user account to forward",
|
||||
)
|
||||
|
||||
return node
|
||||
|
||||
|
||||
# pylint: disable = R0914
|
||||
|
||||
|
||||
async def forward_messages(client: pyrogram.Client, message: pyrogram.types.Message):
|
||||
"""
|
||||
Forwards messages from one chat to another.
|
||||
|
||||
Parameters:
|
||||
client (pyrogram.Client): The pyrogram client.
|
||||
message (pyrogram.types.Message): The message containing the command.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
async def report_error(client: pyrogram.Client, message: pyrogram.types.Message):
|
||||
"""Report error"""
|
||||
|
||||
await client.send_message(
|
||||
message.from_user.id,
|
||||
f"{_t('Invalid command format')}."
|
||||
f"{_t('Please use')} "
|
||||
"/forward https://t.me/c/src_chat https://t.me/c/dst_chat "
|
||||
f"1 400 `[`{_t('Filter')}`]`\n",
|
||||
)
|
||||
|
||||
args = message.text.split(maxsplit=5)
|
||||
if len(args) < 5:
|
||||
await report_error(client, message)
|
||||
return
|
||||
|
||||
src_chat_link = args[1]
|
||||
dst_chat_link = args[2]
|
||||
|
||||
try:
|
||||
offset_id = int(args[3])
|
||||
limit = int(args[4])
|
||||
except Exception:
|
||||
await report_error(client, message)
|
||||
return
|
||||
|
||||
download_filter = args[5] if len(args) > 5 else None
|
||||
|
||||
node = await get_forward_task_node(
|
||||
client,
|
||||
message,
|
||||
src_chat_link,
|
||||
dst_chat_link,
|
||||
offset_id,
|
||||
limit,
|
||||
download_filter,
|
||||
TaskType.Forward,
|
||||
)
|
||||
|
||||
if not node:
|
||||
return
|
||||
|
||||
if not node.has_protected_content:
|
||||
last_read_message_id = offset_id
|
||||
try:
|
||||
async for item in _bot.client.get_chat_history(
|
||||
node.chat_id,
|
||||
limit=limit,
|
||||
offset_id=offset_id,
|
||||
reverse=True,
|
||||
):
|
||||
if (
|
||||
await forward_normal_content(client, node, item)
|
||||
is ForwardStatus.SuccessForward
|
||||
):
|
||||
last_read_message_id = item.id
|
||||
except Exception as e:
|
||||
await client.edit_message_text(
|
||||
message.from_user.id,
|
||||
node.reply_message_id,
|
||||
f"{_t('Error forwarding message')} {last_read_message_id}"
|
||||
f" - {offset_id + limit} {e}",
|
||||
)
|
||||
|
||||
await report_bot_status(client, node, immediate_reply=True)
|
||||
else:
|
||||
await forward_msg(node, offset_id)
|
||||
|
||||
|
||||
async def forward_normal_content(
|
||||
client: pyrogram.Client, node: TaskNode, message: pyrogram.types.Message
|
||||
):
|
||||
"""Forward normal content"""
|
||||
|
||||
forward_ret = ForwardStatus.FailedForward
|
||||
if node.download_filter:
|
||||
meta_data = MetaData()
|
||||
caption = message.caption
|
||||
if caption:
|
||||
caption = validate_title(caption)
|
||||
_bot.app.set_caption_name(node.chat_id, message.media_group_id, caption)
|
||||
else:
|
||||
caption = _bot.app.get_caption_name(node.chat_id, message.media_group_id)
|
||||
set_meta_data(meta_data, message, caption)
|
||||
if not _bot.filter.exec(node.download_filter):
|
||||
forward_ret = ForwardStatus.SkipForward
|
||||
await report_bot_forward_status(client, node, forward_ret)
|
||||
return
|
||||
|
||||
timeout_count = 0
|
||||
while timeout_count < 3:
|
||||
timeout_count += 1
|
||||
try:
|
||||
await _bot.client.forward_messages(
|
||||
node.upload_telegram_chat_id, node.chat_id, message.id
|
||||
)
|
||||
forward_ret = ForwardStatus.SuccessForward
|
||||
break
|
||||
except pyrogram.errors.exceptions.bad_request_400.MessageIdInvalid:
|
||||
pass
|
||||
except pyrogram.errors.exceptions.flood_420.FloodWait as wait_err:
|
||||
logger.warning(
|
||||
"bot task: forward message[{}]: FlowWait {}", message.id, wait_err.value
|
||||
)
|
||||
await asyncio.sleep(wait_err.value)
|
||||
except Exception as e:
|
||||
logger.warning("bot task: forward message[{}]: exception {}", message.id, e)
|
||||
break
|
||||
|
||||
await report_bot_forward_status(client, node, forward_ret)
|
||||
return forward_ret
|
||||
|
||||
|
||||
async def forward_msg(node: TaskNode, message_id: int):
|
||||
"""Forward normal message"""
|
||||
|
||||
chat_download_config = ChatDownloadConfig()
|
||||
chat_download_config.last_read_message_id = message_id
|
||||
chat_download_config.download_filter = node.download_filter
|
||||
|
||||
await _bot.download_chat_task(_bot.client, chat_download_config, node)
|
||||
|
||||
|
||||
async def set_listen_forward_msg(
|
||||
client: pyrogram.Client, message: pyrogram.types.Message
|
||||
):
|
||||
"""
|
||||
Set the chat to listen for forwarded messages.
|
||||
|
||||
Args:
|
||||
client (pyrogram.Client): The pyrogram client.
|
||||
message (pyrogram.types.Message): The message sent by the user.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
args = message.text.split(maxsplit=3)
|
||||
|
||||
if len(args) < 3:
|
||||
await client.send_message(
|
||||
message.from_user.id,
|
||||
f"{_t('Invalid command format')}. {_t('Please use')} /listen_forward "
|
||||
f"https://t.me/c/src_chat https://t.me/c/dst_chat [{_t('Filter')}]\n",
|
||||
)
|
||||
return
|
||||
|
||||
src_chat_link = args[1]
|
||||
dst_chat_link = args[2]
|
||||
|
||||
download_filter = args[3] if len(args) > 3 else None
|
||||
|
||||
node = await get_forward_task_node(
|
||||
client,
|
||||
message,
|
||||
src_chat_link,
|
||||
dst_chat_link,
|
||||
0,
|
||||
1,
|
||||
download_filter,
|
||||
task_type=TaskType.ListenForward,
|
||||
)
|
||||
|
||||
if not node:
|
||||
return
|
||||
|
||||
if node.chat_id in _bot.listen_forward_chat:
|
||||
_bot.remove_task_node(_bot.listen_forward_chat[node.chat_id].task_id)
|
||||
|
||||
node.is_running = True
|
||||
_bot.listen_forward_chat[node.chat_id] = node
|
||||
|
||||
|
||||
async def listen_forward_msg(client: pyrogram.Client, message: pyrogram.types.Message):
|
||||
"""
|
||||
Forwards messages from a chat to another chat if the message does not contain protected content.
|
||||
If the message contains protected content, it will be downloaded and forwarded to the other chat.
|
||||
|
||||
Parameters:
|
||||
client (pyrogram.Client): The pyrogram client.
|
||||
message (pyrogram.types.Message): The message to be forwarded.
|
||||
"""
|
||||
|
||||
if message.chat and message.chat.id in _bot.listen_forward_chat:
|
||||
node = _bot.listen_forward_chat[message.chat.id]
|
||||
|
||||
# TODO(tangyoha):fix run time change protected content
|
||||
if not node.has_protected_content:
|
||||
await forward_normal_content(client, node, message)
|
||||
await report_bot_status(client, node, immediate_reply=True)
|
||||
else:
|
||||
await _bot.add_download_task(
|
||||
message,
|
||||
node,
|
||||
)
|
@ -0,0 +1,229 @@
|
||||
"""provide upload cloud drive"""
|
||||
import asyncio
|
||||
import importlib
|
||||
import os
|
||||
from asyncio import subprocess
|
||||
from subprocess import Popen
|
||||
from zipfile import ZipFile
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from utils import platform
|
||||
|
||||
|
||||
# pylint: disable = R0902
|
||||
class CloudDriveConfig:
|
||||
"""Rclone Config"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
enable_upload_file: bool = False,
|
||||
before_upload_file_zip: bool = False,
|
||||
after_upload_file_delete: bool = True,
|
||||
rclone_path: str = os.path.join(
|
||||
os.path.abspath(""), "rclone", f"rclone{platform.get_exe_ext()}"
|
||||
),
|
||||
remote_dir: str = "",
|
||||
upload_adapter: str = "rclone",
|
||||
):
|
||||
self.enable_upload_file = enable_upload_file
|
||||
self.before_upload_file_zip = before_upload_file_zip
|
||||
self.after_upload_file_delete = after_upload_file_delete
|
||||
self.rclone_path = rclone_path
|
||||
self.remote_dir = remote_dir
|
||||
self.upload_adapter = upload_adapter
|
||||
self.dir_cache: dict = {} # for remote mkdir
|
||||
self.total_upload_success_file_count = 0
|
||||
self.aligo = None
|
||||
|
||||
def pre_run(self):
|
||||
"""pre run init aligo"""
|
||||
if self.enable_upload_file and self.upload_adapter == "aligo":
|
||||
CloudDrive.init_upload_adapter(self)
|
||||
|
||||
|
||||
class CloudDrive:
|
||||
"""rclone support"""
|
||||
|
||||
@staticmethod
|
||||
def init_upload_adapter(drive_config: CloudDriveConfig):
|
||||
"""Initialize the upload adapter."""
|
||||
if drive_config.upload_adapter == "aligo":
|
||||
Aligo = importlib.import_module("aligo").Aligo
|
||||
drive_config.aligo = Aligo()
|
||||
|
||||
@staticmethod
|
||||
def rclone_mkdir(drive_config: CloudDriveConfig, remote_dir: str):
|
||||
"""mkdir in remote"""
|
||||
with Popen(
|
||||
f'"{drive_config.rclone_path}" mkdir {remote_dir}/',
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def aligo_mkdir(drive_config: CloudDriveConfig, remote_dir: str):
|
||||
"""mkdir in remote by aligo"""
|
||||
if drive_config.aligo and not drive_config.aligo.get_folder_by_path(remote_dir):
|
||||
drive_config.aligo.create_folder(name=remote_dir, check_name_mode="refuse")
|
||||
|
||||
@staticmethod
|
||||
def zip_file(local_file_path: str) -> str:
|
||||
"""
|
||||
Zip local file
|
||||
"""
|
||||
|
||||
file_path_without_extension = os.path.splitext(local_file_path)[0]
|
||||
zip_file_name = file_path_without_extension + ".zip"
|
||||
|
||||
with ZipFile(zip_file_name, "w") as zip_writer:
|
||||
zip_writer.write(local_file_path)
|
||||
|
||||
return zip_file_name
|
||||
|
||||
@staticmethod
|
||||
async def rclone_upload_file(
|
||||
drive_config: CloudDriveConfig, save_path: str, local_file_path: str
|
||||
) -> bool:
|
||||
"""Use Rclone upload file"""
|
||||
upload_status: bool = False
|
||||
try:
|
||||
remote_dir = (
|
||||
drive_config.remote_dir
|
||||
+ "/"
|
||||
+ os.path.dirname(local_file_path).replace(save_path, "")
|
||||
+ "/"
|
||||
).replace("\\", "/")
|
||||
|
||||
remote_dir = remote_dir.replace(" ", "\ ")
|
||||
|
||||
|
||||
if not drive_config.dir_cache.get(remote_dir):
|
||||
CloudDrive.rclone_mkdir(drive_config, remote_dir)
|
||||
drive_config.dir_cache[remote_dir] = True
|
||||
|
||||
zip_file_path: str = ""
|
||||
file_path = local_file_path
|
||||
if drive_config.before_upload_file_zip:
|
||||
zip_file_path = CloudDrive.zip_file(local_file_path)
|
||||
file_path = zip_file_path
|
||||
else:
|
||||
file_path = local_file_path
|
||||
|
||||
cmd = (
|
||||
f'"{drive_config.rclone_path}" copy "{file_path}" '
|
||||
f"{remote_dir}/ --create-empty-src-dirs --ignore-existing --progress"
|
||||
)
|
||||
proc = await asyncio.create_subprocess_shell(
|
||||
cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
|
||||
)
|
||||
if proc.stdout:
|
||||
async for output in proc.stdout:
|
||||
s = output.decode(errors="replace")
|
||||
print(s)
|
||||
if "Transferred" in s and "100%" in s and "1 / 1" in s:
|
||||
logger.info(f"upload file {local_file_path} success")
|
||||
drive_config.total_upload_success_file_count += 1
|
||||
if drive_config.after_upload_file_delete:
|
||||
os.remove(local_file_path)
|
||||
if drive_config.before_upload_file_zip:
|
||||
os.remove(zip_file_path)
|
||||
upload_status = True
|
||||
|
||||
await proc.wait()
|
||||
except Exception as e:
|
||||
logger.error(f"{e.__class__} {e}")
|
||||
return False
|
||||
|
||||
return upload_status
|
||||
|
||||
@staticmethod
|
||||
def aligo_upload_file(
|
||||
drive_config: CloudDriveConfig, save_path: str, local_file_path: str
|
||||
):
|
||||
"""aliyun upload file"""
|
||||
upload_status: bool = False
|
||||
if not drive_config.aligo:
|
||||
logger.warning("please config aligo! see README.md")
|
||||
return False
|
||||
|
||||
try:
|
||||
remote_dir = (
|
||||
drive_config.remote_dir
|
||||
+ "/"
|
||||
+ os.path.dirname(local_file_path).replace(save_path, "")
|
||||
+ "/"
|
||||
).replace("\\", "/")
|
||||
|
||||
remote_dir = remote_dir.replace(" ", "\ ")
|
||||
if not drive_config.dir_cache.get(remote_dir):
|
||||
CloudDrive.aligo_mkdir(drive_config, remote_dir)
|
||||
aligo_dir = drive_config.aligo.get_folder_by_path(remote_dir)
|
||||
if aligo_dir:
|
||||
drive_config.dir_cache[remote_dir] = aligo_dir.file_id
|
||||
|
||||
zip_file_path: str = ""
|
||||
file_paths = []
|
||||
if drive_config.before_upload_file_zip:
|
||||
zip_file_path = CloudDrive.zip_file(local_file_path)
|
||||
file_paths.append(zip_file_path)
|
||||
else:
|
||||
file_paths.append(local_file_path)
|
||||
|
||||
res = drive_config.aligo.upload_files(
|
||||
file_paths=file_paths,
|
||||
parent_file_id=drive_config.dir_cache[remote_dir],
|
||||
check_name_mode="refuse",
|
||||
)
|
||||
|
||||
if len(res) > 0:
|
||||
drive_config.total_upload_success_file_count += len(res)
|
||||
if drive_config.after_upload_file_delete:
|
||||
os.remove(local_file_path)
|
||||
|
||||
if drive_config.before_upload_file_zip:
|
||||
os.remove(zip_file_path)
|
||||
|
||||
upload_status = True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"{e.__class__} {e}")
|
||||
return False
|
||||
|
||||
return upload_status
|
||||
|
||||
@staticmethod
|
||||
async def upload_file(
|
||||
drive_config: CloudDriveConfig, save_path: str, local_file_path: str
|
||||
) -> bool:
|
||||
"""Upload file
|
||||
Parameters
|
||||
----------
|
||||
drive_config: CloudDriveConfig
|
||||
see @CloudDriveConfig
|
||||
|
||||
save_path: str
|
||||
Local file save path config
|
||||
|
||||
local_file_path: str
|
||||
Local file path
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
True or False
|
||||
"""
|
||||
if not drive_config.enable_upload_file:
|
||||
return False
|
||||
|
||||
ret: bool = False
|
||||
if drive_config.upload_adapter == "rclone":
|
||||
ret = await CloudDrive.rclone_upload_file(
|
||||
drive_config, save_path, local_file_path
|
||||
)
|
||||
elif drive_config.upload_adapter == "aligo":
|
||||
ret = CloudDrive.aligo_upload_file(drive_config, save_path, local_file_path)
|
||||
|
||||
return ret
|
@ -0,0 +1,112 @@
|
||||
"""Download Stat"""
|
||||
import asyncio
|
||||
import time
|
||||
from enum import Enum
|
||||
from typing import Union
|
||||
|
||||
|
||||
class DownloadState(Enum):
|
||||
"""Download state"""
|
||||
|
||||
Downloading = 1
|
||||
StopDownload = 2
|
||||
|
||||
|
||||
_download_result: dict = {}
|
||||
_total_download_speed: int = 0
|
||||
_total_download_size: int = 0
|
||||
_last_download_time: float = time.time()
|
||||
_download_state: DownloadState = DownloadState.Downloading
|
||||
|
||||
|
||||
def get_download_result() -> dict:
|
||||
"""get global download result"""
|
||||
return _download_result
|
||||
|
||||
|
||||
def get_total_download_speed() -> int:
|
||||
"""get total download speed"""
|
||||
return _total_download_speed
|
||||
|
||||
|
||||
def get_download_state() -> DownloadState:
|
||||
"""get download state"""
|
||||
return _download_state
|
||||
|
||||
|
||||
# pylint: disable = W0603
|
||||
def set_download_state(state: DownloadState):
|
||||
"""set download state"""
|
||||
global _download_state
|
||||
_download_state = state
|
||||
|
||||
|
||||
async def update_download_status(
|
||||
down_byte: int,
|
||||
total_size: int,
|
||||
chat_id: Union[int, str],
|
||||
message_id: int,
|
||||
file_name: str,
|
||||
start_time: float,
|
||||
task_id: int,
|
||||
):
|
||||
"""update_download_status"""
|
||||
cur_time = time.time()
|
||||
# pylint: disable = W0603
|
||||
global _total_download_speed
|
||||
global _total_download_size
|
||||
global _last_download_time
|
||||
|
||||
while get_download_state() == DownloadState.StopDownload:
|
||||
await asyncio.sleep(1)
|
||||
|
||||
if not _download_result.get(chat_id):
|
||||
_download_result[chat_id] = {}
|
||||
|
||||
if _download_result[chat_id].get(message_id):
|
||||
last_download_byte = _download_result[chat_id][message_id]["down_byte"]
|
||||
last_time = _download_result[chat_id][message_id]["end_time"]
|
||||
download_speed = _download_result[chat_id][message_id]["download_speed"]
|
||||
each_second_total_download = _download_result[chat_id][message_id][
|
||||
"each_second_total_download"
|
||||
]
|
||||
end_time = _download_result[chat_id][message_id]["end_time"]
|
||||
|
||||
_total_download_size += down_byte - last_download_byte
|
||||
each_second_total_download += down_byte - last_download_byte
|
||||
|
||||
if cur_time - last_time >= 1.0:
|
||||
download_speed = int(each_second_total_download / (cur_time - last_time))
|
||||
end_time = cur_time
|
||||
each_second_total_download = 0
|
||||
|
||||
download_speed = max(download_speed, 0)
|
||||
|
||||
_download_result[chat_id][message_id]["down_byte"] = down_byte
|
||||
_download_result[chat_id][message_id]["end_time"] = end_time
|
||||
_download_result[chat_id][message_id]["download_speed"] = download_speed
|
||||
_download_result[chat_id][message_id][
|
||||
"each_second_total_download"
|
||||
] = each_second_total_download
|
||||
else:
|
||||
each_second_total_download = down_byte
|
||||
_download_result[chat_id][message_id] = {
|
||||
"down_byte": down_byte,
|
||||
"total_size": total_size,
|
||||
"file_name": file_name,
|
||||
"start_time": start_time,
|
||||
"end_time": cur_time,
|
||||
"download_speed": down_byte / (cur_time - start_time),
|
||||
"each_second_total_download": each_second_total_download,
|
||||
"task_id": task_id,
|
||||
}
|
||||
_total_download_size += down_byte
|
||||
|
||||
if cur_time - _last_download_time >= 1.0:
|
||||
# update speed
|
||||
_total_download_speed = int(
|
||||
_total_download_size / (cur_time - _last_download_time)
|
||||
)
|
||||
_total_download_speed = max(_total_download_speed, 0)
|
||||
_total_download_size = 0
|
||||
_last_download_time = cur_time
|
372
src/parsers/Telegram/telegram_media_downloader/module/filter.py
Normal file
@ -0,0 +1,372 @@
|
||||
"""Filter for download"""
|
||||
|
||||
import re
|
||||
from datetime import datetime
|
||||
from typing import Any, Optional, Tuple
|
||||
|
||||
from ply import lex, yacc
|
||||
|
||||
from utils.format import get_byte_from_str
|
||||
from utils.meta_data import MetaData, NoneObj, ReString
|
||||
|
||||
|
||||
# pylint: disable = R0904
|
||||
class BaseFilter:
|
||||
"""for normal filter"""
|
||||
|
||||
def __init__(self, debug: bool = False):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
debug: bool
|
||||
If output debug info
|
||||
|
||||
"""
|
||||
self.names: dict = {}
|
||||
self.debug = debug
|
||||
# Build the lexer and parser
|
||||
# lex.lex(module=self)
|
||||
self.lexer = lex.lex(module=self)
|
||||
self.yacc = yacc.yacc(module=self)
|
||||
|
||||
def reset(self):
|
||||
"""Reset all symbol"""
|
||||
self.names.clear()
|
||||
|
||||
def exec(self, filter_str: str) -> Any:
|
||||
"""Exec filter str"""
|
||||
# ) #
|
||||
# return yacc.parse(filter_str, debug=self.debug)
|
||||
return self.yacc.parse(filter_str, debug=self.debug)
|
||||
|
||||
def _output(self, output_str: str):
|
||||
"""For print debug info"""
|
||||
if self.debug:
|
||||
print(output_str)
|
||||
|
||||
reserved = {
|
||||
"and": "AND",
|
||||
"or": "OR",
|
||||
}
|
||||
|
||||
tokens = (
|
||||
"NAME",
|
||||
"NUMBER",
|
||||
"GE",
|
||||
"LE",
|
||||
"LOR",
|
||||
"LAND",
|
||||
"STRING",
|
||||
"RESTRING",
|
||||
"BYTE",
|
||||
"EQ",
|
||||
"NE",
|
||||
"TIME",
|
||||
"AND",
|
||||
"OR",
|
||||
)
|
||||
|
||||
literals = ["=", "+", "-", "*", "/", "(", ")", ">", "<"]
|
||||
|
||||
# t_NAME = r'[a-zA-Z_][a-zA-Z0-9_]*'
|
||||
t_GE = r">="
|
||||
t_LE = r"<="
|
||||
t_LOR = r"\|\|"
|
||||
t_LAND = r"&&"
|
||||
t_EQ = r"=="
|
||||
t_NE = r"!="
|
||||
|
||||
def t_BYTE(self, t):
|
||||
r"\d{1,}(B|KB|MB|GB|TB)"
|
||||
t.value = get_byte_from_str(t.value)
|
||||
t.type = "NUMBER"
|
||||
return t
|
||||
|
||||
def t_TIME(self, t):
|
||||
r"\d{4}-\d{1,2}-\d{1,2}[ ]{1,}\d{1,2}:\d{1,2}:\d{1,2}"
|
||||
t.value = datetime.strptime(t.value, "%Y-%m-%d %H:%M:%S")
|
||||
return t
|
||||
|
||||
def t_STRING(self, t):
|
||||
r"'.*?'"
|
||||
# r"'([^\\']+|\\'|\\\\)*'"
|
||||
t.value = t.value[1:-1]
|
||||
return t
|
||||
|
||||
def t_RESTRING(self, t):
|
||||
r"r'.*?'"
|
||||
# r"r'([^\\']+|\\'|\\\\)*'"
|
||||
t.value = t.value[2:-1]
|
||||
return t
|
||||
|
||||
def t_NAME(self, t):
|
||||
r"[a-zA-Z_][a-zA-Z0-9_]*"
|
||||
t.type = BaseFilter.reserved.get(t.value, "NAME")
|
||||
return t
|
||||
|
||||
def t_NUMBER(self, t):
|
||||
r"\d+"
|
||||
t.value = int(t.value)
|
||||
return t
|
||||
|
||||
t_ignore = " \t"
|
||||
|
||||
def t_newline(self, t):
|
||||
r"\n+"
|
||||
t.lexer.lineno += t.value.count("\n")
|
||||
|
||||
def t_error(self, t):
|
||||
"""print error"""
|
||||
print(f"Illegal character '{t.value[0]}'")
|
||||
t.lexer.skip(1)
|
||||
|
||||
precedence = (
|
||||
("left", "LOR", "OR"),
|
||||
("left", "LAND", "AND"),
|
||||
("left", "EQ", "NE"),
|
||||
("nonassoc", ">", "<", "GE", "LE"),
|
||||
("left", "+", "-"),
|
||||
("left", "*", "/"),
|
||||
("right", "UMINUS"),
|
||||
)
|
||||
|
||||
def p_statement_assign(self, p):
|
||||
'statement : NAME "=" expression'
|
||||
return self.p_expression_eq(p)
|
||||
# self.names[p[1]] = p[3]
|
||||
|
||||
def p_statement_expr(self, p):
|
||||
"statement : expression"
|
||||
self._output(p[1])
|
||||
p[0] = p[1]
|
||||
|
||||
def p_expression_binop(self, p):
|
||||
"""expression : expression '+' expression
|
||||
| expression '-' expression
|
||||
| expression '*' expression
|
||||
| expression '/' expression"""
|
||||
self.check_type(p)
|
||||
if isinstance(p[1], NoneObj):
|
||||
p[1] = 0
|
||||
if isinstance(p[3], NoneObj):
|
||||
p[3] = 0
|
||||
|
||||
if p[2] == "+":
|
||||
p[0] = p[1] + p[3]
|
||||
elif p[2] == "-":
|
||||
p[0] = p[1] - p[3]
|
||||
elif p[2] == "*":
|
||||
p[0] = p[1] * p[3]
|
||||
elif p[2] == "/":
|
||||
p[0] = p[1] / p[3]
|
||||
|
||||
self._output(f"binop {p[1]} {p[2]} {p[3]} = {p[0]}")
|
||||
|
||||
def p_expression_comp(self, p):
|
||||
"""expression : expression '>' expression
|
||||
| expression '<' expression"""
|
||||
self.check_type(p)
|
||||
if isinstance(p[1], NoneObj) or isinstance(p[3], NoneObj):
|
||||
p[0] = True
|
||||
return
|
||||
|
||||
if p[1] is None or p[3] is None:
|
||||
p[0] = False
|
||||
return
|
||||
if p[2] == ">":
|
||||
p[0] = p[1] > p[3]
|
||||
elif p[2] == "<":
|
||||
p[0] = p[1] < p[3]
|
||||
|
||||
def p_expression_uminus(self, p):
|
||||
"expression : '-' expression %prec UMINUS"
|
||||
p[0] = -p[2]
|
||||
|
||||
def p_expression_ge(self, p):
|
||||
"expression : expression GE expression"
|
||||
self.check_type(p)
|
||||
if isinstance(p[1], NoneObj) or isinstance(p[3], NoneObj):
|
||||
p[0] = True
|
||||
return
|
||||
|
||||
if p[1] is None or p[3] is None:
|
||||
p[0] = False
|
||||
return
|
||||
|
||||
p[0] = p[1] >= p[3]
|
||||
self._output(f"{p[1]} {p[2]} {p[3]} {p[0]}")
|
||||
|
||||
def p_expression_le(self, p):
|
||||
"expression : expression LE expression"
|
||||
self.check_type(p)
|
||||
if isinstance(p[1], NoneObj) or isinstance(p[3], NoneObj):
|
||||
p[0] = True
|
||||
return
|
||||
|
||||
if p[1] is None or p[3] is None:
|
||||
p[0] = False
|
||||
return
|
||||
|
||||
p[0] = p[1] <= p[3]
|
||||
self._output(f"{p[1]} {p[2]} {p[3]} = {p[0]}")
|
||||
|
||||
def p_expression_eq(self, p):
|
||||
"expression : expression EQ expression"
|
||||
self.check_type(p)
|
||||
if isinstance(p[1], NoneObj) or isinstance(p[3], NoneObj):
|
||||
p[0] = True
|
||||
return
|
||||
|
||||
if p[1] is None or p[3] is None:
|
||||
p[0] = False
|
||||
return
|
||||
|
||||
if isinstance(p[3], ReString):
|
||||
if not isinstance(p[1], str):
|
||||
p[0] = 0
|
||||
return
|
||||
p[0] = re.fullmatch(p[3].re_string, p[1], re.MULTILINE) is not None
|
||||
self._output(f"{p[1]} {p[2]} {p[3].re_string} {p[0]}")
|
||||
elif isinstance(p[1], ReString):
|
||||
if not isinstance(p[3], str):
|
||||
p[0] = 0
|
||||
return
|
||||
p[0] = re.fullmatch(p[1].re_string, p[3], re.MULTILINE) is not None
|
||||
self._output(f"{p[1]} {p[2]} {p[3].re_string} {p[0]}")
|
||||
else:
|
||||
p[0] = p[1] == p[3]
|
||||
self._output(f"{p[1]} {p[2]} {p[3]} {p[0]}")
|
||||
|
||||
def p_expression_ne(self, p):
|
||||
"expression : expression NE expression"
|
||||
self.check_type(p)
|
||||
if isinstance(p[1], NoneObj) or isinstance(p[3], NoneObj):
|
||||
p[0] = True
|
||||
return
|
||||
|
||||
if p[1] is None or p[3] is None:
|
||||
p[0] = False
|
||||
return
|
||||
if isinstance(p[3], ReString):
|
||||
if not isinstance(p[1], str):
|
||||
p[0] = 0
|
||||
return
|
||||
p[0] = re.fullmatch(p[3].re_string, p[1], re.MULTILINE) is None
|
||||
self._output(f"{p[1]} {p[2]} {p[3].re_string} {p[0]}")
|
||||
elif isinstance(p[1], ReString):
|
||||
if not isinstance(p[3], str):
|
||||
p[0] = 0
|
||||
return
|
||||
p[0] = re.fullmatch(p[1].re_string, p[3], re.MULTILINE) is None
|
||||
self._output(f"{p[1]} {p[2]} {p[3].re_string} {p[0]}")
|
||||
else:
|
||||
p[0] = p[1] != p[3]
|
||||
self._output(f"{p[1]} {p[2]} {p[3]} = {p[0]}")
|
||||
|
||||
def p_expression_group(self, p):
|
||||
"expression : '(' expression ')'"
|
||||
p[0] = p[2]
|
||||
|
||||
def p_expression_number(self, p):
|
||||
"expression : NUMBER"
|
||||
p[0] = p[1]
|
||||
|
||||
def p_expression_time(self, p):
|
||||
"expression : TIME"
|
||||
p[0] = p[1]
|
||||
|
||||
def p_expression_byte(self, p):
|
||||
"expression : BYTE"
|
||||
p[0] = p[1]
|
||||
|
||||
def p_expression_name(self, p):
|
||||
"expression : NAME"
|
||||
try:
|
||||
p[0] = self.names[p[1]]
|
||||
except Exception as e:
|
||||
self._output(f"Undefined name '{p[1]}'")
|
||||
raise ValueError(f"Undefined name {p[1]}") from e
|
||||
# FIXME: not support not exist name
|
||||
# p[0] = NoneObj()
|
||||
|
||||
def p_expression_lor(self, p):
|
||||
"expression : expression LOR expression"
|
||||
p[0] = p[1] or p[3]
|
||||
|
||||
def p_expression_land(self, p):
|
||||
"expression : expression LAND expression"
|
||||
p[0] = p[1] and p[3]
|
||||
|
||||
def p_expression_or(self, p):
|
||||
"expression : expression OR expression"
|
||||
p[0] = p[1] or p[3]
|
||||
|
||||
def p_expression_and(self, p):
|
||||
"expression : expression AND expression"
|
||||
p[0] = p[1] and p[3]
|
||||
|
||||
def p_expression_string(self, p):
|
||||
"expression : STRING"
|
||||
p[0] = p[1]
|
||||
|
||||
def p_expression_restring(self, p):
|
||||
"expression : RESTRING"
|
||||
p[0] = ReString(p[1])
|
||||
self._output("RESTRING : " + p[0].re_string)
|
||||
|
||||
# pylint: disable = C0116
|
||||
def p_error(self, p):
|
||||
if p:
|
||||
raise ValueError(f"Syntax error at '{p.value}'")
|
||||
|
||||
raise ValueError("Syntax error at EOF")
|
||||
|
||||
def check_type(self, p):
|
||||
"""Check filter type if is right"""
|
||||
if p[1] is None or p[1] is NoneObj or p[3] is None or p[3] is NoneObj:
|
||||
return
|
||||
if isinstance(p[1], str):
|
||||
if not isinstance(p[3], str) and not isinstance(p[3], ReString):
|
||||
raise ValueError(f"{p[1]} is str but {p[3]} is not")
|
||||
elif isinstance(p[1], int):
|
||||
if not isinstance(p[3], int):
|
||||
raise ValueError(f"{p[1]} is int but {p[3]} is not")
|
||||
elif isinstance(p[1], bool):
|
||||
if not isinstance(p[3], bool):
|
||||
raise ValueError(f"{p[1]} is bool but {p[3]} is not")
|
||||
elif isinstance(p[1], datetime):
|
||||
if not isinstance(p[3], datetime):
|
||||
raise ValueError(f"{p[1]} is datetime but {p[3]} is not")
|
||||
|
||||
|
||||
class Filter:
|
||||
"""filter for telegram download"""
|
||||
|
||||
def __init__(self):
|
||||
self.filter = BaseFilter()
|
||||
|
||||
def set_meta_data(self, meta_data: MetaData):
|
||||
"""Set meta data for filter"""
|
||||
self.filter.reset()
|
||||
self.filter.names = meta_data.data()
|
||||
|
||||
def set_debug(self, debug: bool):
|
||||
"""Set Filter Debug Model"""
|
||||
self.filter.debug = debug
|
||||
|
||||
def exec(self, filter_str: str) -> bool:
|
||||
"""Exec filter str"""
|
||||
|
||||
if self.filter.names:
|
||||
res = self.filter.exec(filter_str)
|
||||
if isinstance(res, bool):
|
||||
return res
|
||||
return False
|
||||
raise ValueError("meta data cannot be empty!")
|
||||
|
||||
def check_filter(self, filter_str: str) -> Tuple[bool, Optional[str]]:
|
||||
"""check filter str"""
|
||||
try:
|
||||
return not self.exec(filter_str) is None, None
|
||||
except Exception as e:
|
||||
return False, str(e)
|
@ -0,0 +1,86 @@
|
||||
"""Rewrite pyrogram.get_chat_history"""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import AsyncGenerator, Optional, Union
|
||||
|
||||
import pyrogram
|
||||
|
||||
# pylint: disable = W0611
|
||||
from pyrogram import raw, types, utils
|
||||
|
||||
|
||||
async def get_chunk_v2(
|
||||
*,
|
||||
client: pyrogram.Client,
|
||||
chat_id: Union[int, str],
|
||||
limit: int = 0,
|
||||
offset: int = 0,
|
||||
from_message_id: int = 0,
|
||||
from_date: datetime = utils.zero_datetime(),
|
||||
reverse: bool = False
|
||||
):
|
||||
"""get chunk"""
|
||||
from_message_id = from_message_id or (1 if reverse else 0)
|
||||
|
||||
messages = await utils.parse_messages(
|
||||
client,
|
||||
await client.invoke(
|
||||
raw.functions.messages.GetHistory(
|
||||
peer=await client.resolve_peer(chat_id),
|
||||
offset_id=from_message_id,
|
||||
offset_date=utils.datetime_to_timestamp(from_date),
|
||||
add_offset=offset * (-1 if reverse else 1) - (limit if reverse else 0),
|
||||
limit=limit,
|
||||
max_id=0,
|
||||
min_id=0,
|
||||
hash=0,
|
||||
),
|
||||
sleep_threshold=60,
|
||||
),
|
||||
replies=0,
|
||||
)
|
||||
|
||||
if reverse:
|
||||
messages.reverse()
|
||||
|
||||
return messages
|
||||
|
||||
|
||||
# pylint: disable = C0301
|
||||
async def get_chat_history_v2(
|
||||
self: pyrogram.Client,
|
||||
chat_id: Union[int, str],
|
||||
limit: int = 0,
|
||||
offset: int = 0,
|
||||
offset_id: int = 0,
|
||||
offset_date: datetime = utils.zero_datetime(),
|
||||
reverse: bool = False,
|
||||
) -> Optional[AsyncGenerator["types.Message", None]]:
|
||||
"""Get messages from a chat history."""
|
||||
current = 0
|
||||
total = limit or (1 << 31) - 1
|
||||
limit = min(100, total)
|
||||
|
||||
while True:
|
||||
messages = await get_chunk_v2(
|
||||
client=self,
|
||||
chat_id=chat_id,
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
from_message_id=offset_id,
|
||||
from_date=offset_date,
|
||||
reverse=reverse,
|
||||
)
|
||||
|
||||
if not messages:
|
||||
return
|
||||
|
||||
offset_id = messages[-1].id + (1 if reverse else 0)
|
||||
|
||||
for message in messages:
|
||||
yield message
|
||||
|
||||
current += 1
|
||||
|
||||
if current >= total:
|
||||
return
|
@ -0,0 +1,271 @@
|
||||
"""Multi language support"""
|
||||
|
||||
# disable pylint: disable = C0301
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class Language(Enum):
|
||||
"""Language for ui"""
|
||||
|
||||
EN = 1 # english
|
||||
ZH = 2 # china
|
||||
RU = 3 # russian
|
||||
UA = 4 # ukrainian
|
||||
|
||||
|
||||
_language = Language.EN
|
||||
|
||||
|
||||
def set_language(language: Language):
|
||||
"""Set Lanaguage"""
|
||||
# pylint: disable = W0603
|
||||
global _language
|
||||
_language = language
|
||||
|
||||
|
||||
translations = {
|
||||
"Forward": ["转发", "Переслать", "Переслати"],
|
||||
"Total": ["总数", "Всего", "Всього"],
|
||||
"Success": ["成功", "Успешно", "Успішно"],
|
||||
"Failed": ["失败", "Не удалось", "Не вдалося"],
|
||||
"Skipped": ["跳过", "Пропущено", "Пропущено"],
|
||||
"Message ID": ["消息ID", "ID сообщения", "ID повідомлення"],
|
||||
"Telegram Media Downloader": [
|
||||
"电报媒体下载器",
|
||||
"Telegram Media Downloader",
|
||||
"Telegram Media Downloader",
|
||||
],
|
||||
"Version": ["版本", "Версия", "Версія"],
|
||||
"Downloading": ["下载", "Скачивание", "Скачування"],
|
||||
"Available commands:": ["可用命令:", "Доступные команды:", "Доступні команди:"],
|
||||
"Show available commands": [
|
||||
"显示可用命令",
|
||||
"Показать доступные команды",
|
||||
"Показати доступні команди",
|
||||
],
|
||||
"Download messages": ["下载消息", "Скачать сообщения", "Скачати повідомлення"],
|
||||
"Forward messages": ["转发消息", "Переслать сообщения", "Переслати повідомлення"],
|
||||
"Listen for forwarded messages": [
|
||||
"监听转发消息",
|
||||
"Прослушивать пересланные сообщения",
|
||||
"Прослуховувати переслані повідомлення",
|
||||
],
|
||||
"Set language": ["设置语言", "Установить язык", "Встановити мову"],
|
||||
"**Note**: 1 means the start of the entire chat": [
|
||||
"**注意**: 1表示整个聊天的开始",
|
||||
"**Примечание**: 1 означает начало всего чата",
|
||||
"**Увага**: 1 означає початок всього чату",
|
||||
],
|
||||
"0 means the end of the entire chat": [
|
||||
"0表示整个聊天的结束",
|
||||
"0 означает конец всего чата",
|
||||
"0 означає кінець всього чату",
|
||||
],
|
||||
"means optional, not required": [
|
||||
"表示可选项,非必填",
|
||||
"означает необязательный параметр",
|
||||
"означає необов'язковий параметр",
|
||||
],
|
||||
"To download the video, use the method to directly enter /download to view": [
|
||||
"下载视频,使用方法直接输入/download查看",
|
||||
"Чтобы скачать видео, введите /download для просмотра",
|
||||
"Щоб скачати відео, введіть /download для перегляду",
|
||||
],
|
||||
"Forward video, use the method to directly enter /forward to view": [
|
||||
"转发视频,使用方法直接输入/forward查看",
|
||||
"Переслать видео, введите /forward для просмотра",
|
||||
"Переслати відео, введіть /forward для перегляду",
|
||||
],
|
||||
"Listen forward, use the method to directly enter /listen_forward to view": [
|
||||
"监控转发,使用方法直接输入/listen_forward查看",
|
||||
"Слушать пересылку, введите /listen_forward для просмотра",
|
||||
"Слухати пересилання, введіть /listen_forward для перегляду",
|
||||
],
|
||||
"Add download filter, use the method to directly enter /add_filter to view": [
|
||||
"添加下载过滤器",
|
||||
"Добавить фильтр загрузки, используйте метод, чтобы непосредственно ввести /add_filter для просмотра",
|
||||
"Додати фільтр завантаження, використовуйте метод, щоб безпосередньо ввести /add_filter для перегляду",
|
||||
],
|
||||
"Help": ["帮助", "Помощь", "Допомога"],
|
||||
"Invalid command format": [
|
||||
"无效的命令格式",
|
||||
"Неверный формат команды",
|
||||
"Невірний формат команди",
|
||||
],
|
||||
"Invalid command format. Please use /set_language en/ru/zh/ua": [
|
||||
"无效的命令格式。请使用 /set_language en/ru/zh/ua",
|
||||
"Неверный формат команды. Пожалуйста, используйте /set_language en/ru/zh/ua",
|
||||
"Невірний формат команди. Будь ласка, використовуйте /set_language en/ru/zh/ua",
|
||||
],
|
||||
"Language set to English": [
|
||||
"语言设置为中文",
|
||||
"Выбран английский язык",
|
||||
"Обрано англійську мову",
|
||||
],
|
||||
"Language set to": [
|
||||
"语言设置为",
|
||||
"Выбран язык",
|
||||
"Обрано мову",
|
||||
],
|
||||
"Invalid command format. Please use /add_filter your filter": [
|
||||
"无效的命令格式。请使用 /add_filter 你的过滤规则",
|
||||
"Неверный формат команды. Пожалуйста, используйте /add_filter ВашФильтр",
|
||||
"Невірний формат команди. Будь ласка, використовуйте /add_filter ВашФільтр",
|
||||
],
|
||||
"Add download filter": [
|
||||
"添加下载过滤器",
|
||||
"Добавить фильтр скачивания",
|
||||
"Додати фільтр скачування",
|
||||
],
|
||||
"Check error, please add again": [
|
||||
"检验错误,请重新添加",
|
||||
"Ошибка проверки, пожалуйста, добавьте еще раз",
|
||||
"Помилка перевірки, будь ласка, додайте ще раз",
|
||||
],
|
||||
"Direct download, directly forward the message to your robot": [
|
||||
"直接下载,直接转发消息给你的机器人",
|
||||
"Скачивание напрямую, пересылка сообщения напрямую вашему роботу",
|
||||
"Безпосереднє скачування, безспесередня пересилка повідомлення вашому роботу",
|
||||
],
|
||||
"Directly download a single message": [
|
||||
"直接下载单条消息",
|
||||
"Прямое скачивание одного сообщения",
|
||||
"Безпосереднє скачування одного повідомлення",
|
||||
],
|
||||
"From": ["从", "От", "Від"],
|
||||
"download": ["下载", "скачать", "скачати"],
|
||||
"error": ["错误", "ошибка", "помилка"],
|
||||
"Parameter error, please enter according to the reference format": [
|
||||
"参数错误,请根据参考格式输入",
|
||||
"Ошибка параметра, введите в соответствии с форматом ссылки",
|
||||
"Помилка параметра, введіть відповідно до формату посилання",
|
||||
],
|
||||
"Download all messages of common group": [
|
||||
"下载公共群组的所有消息",
|
||||
"Скачать все сообщения общей группы",
|
||||
"Скачати всі повідомлення спільної групи",
|
||||
],
|
||||
"The private group (channel) link is a random group message link": [
|
||||
"私密群组(频道) 链接为随便复制一条群组消息链接",
|
||||
"Ссылка на частную группу (канал) - это ссылка на случайное сообщение группы",
|
||||
"Посилання на приватну групу (канал) - це посилання на випадкове повідомлення групи",
|
||||
],
|
||||
"The download starts from the N message to the end of the M message": [
|
||||
"下载从第N条消息开始的到第M条信息结束",
|
||||
"Скачивание начинается с сообщения N до конца сообщения M",
|
||||
"Скачування починається з повідомлення N до кінця повідомлення M",
|
||||
],
|
||||
"When M is 0, it means the last message. The filter is optional": [
|
||||
"M为0的时候表示到最后一条信息,过滤器为可选",
|
||||
"Когда M равно 0, это означает последнее сообщение. Фильтр необязателен",
|
||||
"Коли M дорівнює 0, це означає останнє повідомлення. Фільтр необов'язковий",
|
||||
],
|
||||
"chat input error, please enter the channel or group link": [
|
||||
"chat输入错误,请输入频道或群组的链接",
|
||||
"Ошибка ввода чата, введите ссылку на канал или группу",
|
||||
"Помилка введеня чату, введіть посилання на канал або групу",
|
||||
],
|
||||
"Error type": ["错误类型", "Тип ошибки", "Тип помилки"],
|
||||
"Exception message": ["异常消息", "Сообщение исключения", "Повідомлення винятка"],
|
||||
"Invalid chat link": [
|
||||
"无效的聊天链接",
|
||||
"Ошибочная ссылка на чат",
|
||||
"Помилкове посилання на чат",
|
||||
],
|
||||
"Cannot be forwarded to this bot, will cause an infinite loop": [
|
||||
"不能转发给该机器人,会导致无限循环",
|
||||
"Невозможно переслать этому боту, это вызовет бесконечный цикл",
|
||||
"Неможливо переслати цьому боту, це спричинить безкінечний цикл",
|
||||
],
|
||||
"Please use": ["请使用", "Пожалуйста, используйте", "Будь ласка, використовуйте"],
|
||||
"Filter": ["过滤器", "Фильтр", "Фільтр"],
|
||||
"Error forwarding message": [
|
||||
"失败的转发消息",
|
||||
"Ошибка пересылки сообщения",
|
||||
"Помилка пересилки повідомлення",
|
||||
],
|
||||
"file reference expired, refetching": [
|
||||
"文件引用过期,重新获取中",
|
||||
"Ссылка на файл истекла, повторное получение",
|
||||
"Посилання на файл минуло, повторне отримання",
|
||||
],
|
||||
"file reference expired for 3 retries, download skipped": [
|
||||
"文件引用过期重试超过3次,跳过下载",
|
||||
"Ссылка на файл истекла после 3 попыток, загрузка пропущена",
|
||||
"Посилання на файл минуло після 3 спроб, завантаження пропущено",
|
||||
],
|
||||
"Timeout Error occurred when downloading Message": [
|
||||
"下载消息超时错误",
|
||||
"Ошибка времени ожидания при скачивании сообщения",
|
||||
"Помилка часу очікування при скачуванні повідомлення",
|
||||
],
|
||||
"retrying": ["重试", "повторная попытка", "повторна спроба"],
|
||||
"seconds": ["秒", "секунд", "секунд"],
|
||||
"Timing out after 3 reties, download skipped": [
|
||||
"超时重试超过3次,跳过下载",
|
||||
"Истекло время ожидания после 3 попыток, загрузка пропущена",
|
||||
"Час очікування закінчився після 3 спроб, завантаження пропущено",
|
||||
],
|
||||
"could not be downloaded due to following exception": [
|
||||
"无法下载,因为以下异常",
|
||||
"не может быть скачен по следующей причине",
|
||||
"не може бути скачаний з наступної причини",
|
||||
],
|
||||
"Downloading files failed during last run": [
|
||||
"下载最后一次运行失败的文件",
|
||||
"Скачивание файлов не удалось во время последнего запуска",
|
||||
"Скачування файлів не вдалося під час останнього запуску",
|
||||
],
|
||||
"Successfully started (Press Ctrl+C to stop)": [
|
||||
"成功启动(按Ctrl+C停止)",
|
||||
"Запуск успешный (нажмите Ctrl + C для остановки)",
|
||||
"Запуск успішний (натисніть Ctrl + C для зупинки)",
|
||||
],
|
||||
"KeyboardInterrupt": ["键盘中断", "KeyboardInterrupt", "KeyboardInterrupt"],
|
||||
"update config": ["更新配置", "обновить конфигурацию", "оновити конфігурацію"],
|
||||
"Updated last read message_id to config file": [
|
||||
"更新最后阅读消息ID到配置文件",
|
||||
"Обновлен идентификатор последнего прочитанного сообщения в конфигурационном файле",
|
||||
"Оновлено ідентифікатор останнього прочитаного повідомлення у конфігураційному файлі",
|
||||
],
|
||||
"total download": ["总下载", "всего скачено", "всього скачано"],
|
||||
"total upload file": ["总上传文件", "всего скаченных файлов", "всього скачаних файлів"],
|
||||
"Stopped": ["停止", "остановлено", "зупинено"],
|
||||
"already download,download skipped": [
|
||||
"已下载,已跳过下载",
|
||||
"уже скачен, скачивание пропущена",
|
||||
"вже скачан, скачування пропущено",
|
||||
],
|
||||
"Media downloaded with wrong size": [
|
||||
"媒体下载错误的大小",
|
||||
"Медиафайл скачен с неправильным размером",
|
||||
"Медіафайл скачано з неправильним розміром",
|
||||
],
|
||||
"actual": ["实际", "фактический", "фактичний"],
|
||||
"file name": ["文件名", "имя файла", "ім'я файлу"],
|
||||
"Successfully downloaded": ["成功下载", "Успешно скачано", "Успішно скачано"],
|
||||
"Get group and user info from message link": [
|
||||
"从消息链接中获取群组和用户信息",
|
||||
"Получить информацию о группе и пользователе по ссылке на сообщение",
|
||||
"Отримайте інформацію про групу та користувача за посиланням у повідомленні",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def _t(text: str):
|
||||
"""Get translation
|
||||
Parameters
|
||||
----------
|
||||
text : str
|
||||
language : str
|
||||
Returns
|
||||
-------
|
||||
str
|
||||
"""
|
||||
if _language is Language.EN:
|
||||
return text
|
||||
|
||||
if text in translations:
|
||||
return translations[text][_language.value - 2]
|
||||
|
||||
return text
|
@ -0,0 +1,597 @@
|
||||
"""Pyrogram ext"""
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import secrets
|
||||
import struct
|
||||
import time
|
||||
from functools import wraps
|
||||
from io import BytesIO, StringIO
|
||||
from mimetypes import MimeTypes
|
||||
from typing import List, Optional, Union
|
||||
|
||||
import pyrogram
|
||||
from loguru import logger
|
||||
from pyrogram.client import Cache
|
||||
from pyrogram.file_id import (
|
||||
FILE_REFERENCE_FLAG,
|
||||
PHOTO_TYPES,
|
||||
WEB_LOCATION_FLAG,
|
||||
FileType,
|
||||
b64_decode,
|
||||
rle_decode,
|
||||
)
|
||||
from pyrogram.mime_types import mime_types
|
||||
|
||||
from module.app import Application, DownloadStatus, ForwardStatus, TaskNode
|
||||
from module.download_stat import get_download_result
|
||||
from module.language import Language, _t
|
||||
from utils.format import create_progress_bar, format_byte, truncate_filename
|
||||
from utils.meta_data import MetaData
|
||||
|
||||
_mimetypes = MimeTypes()
|
||||
_mimetypes.readfp(StringIO(mime_types))
|
||||
_download_cache = Cache(1024 * 1024 * 1024)
|
||||
|
||||
|
||||
def reset_download_cache():
|
||||
"""Reset download cache"""
|
||||
_download_cache.store.clear()
|
||||
|
||||
|
||||
def _guess_mime_type(filename: str) -> Optional[str]:
|
||||
"""Guess mime type"""
|
||||
return _mimetypes.guess_type(filename)[0]
|
||||
|
||||
|
||||
def _guess_extension(mime_type: str) -> Optional[str]:
|
||||
"""Guess extension"""
|
||||
return _mimetypes.guess_extension(mime_type)
|
||||
|
||||
|
||||
def _get_file_type(file_id: str):
|
||||
"""Get file type"""
|
||||
decoded = rle_decode(b64_decode(file_id))
|
||||
|
||||
# File id versioning. Major versions lower than 4 don't have a minor version
|
||||
major = decoded[-1]
|
||||
|
||||
if major < 4:
|
||||
buffer = BytesIO(decoded[:-1])
|
||||
else:
|
||||
buffer = BytesIO(decoded[:-2])
|
||||
|
||||
file_type, _ = struct.unpack("<ii", buffer.read(8))
|
||||
|
||||
file_type &= ~WEB_LOCATION_FLAG
|
||||
file_type &= ~FILE_REFERENCE_FLAG
|
||||
|
||||
try:
|
||||
file_type = FileType(file_type)
|
||||
except ValueError as exc:
|
||||
raise ValueError(f"Unknown file_type {file_type} of file_id {file_id}") from exc
|
||||
|
||||
return file_type
|
||||
|
||||
|
||||
def get_extension(file_id: str, mime_type: str, dot: bool = True) -> str:
|
||||
"""Get extension"""
|
||||
|
||||
if not file_id:
|
||||
if dot:
|
||||
return ".unknown"
|
||||
return "unknown"
|
||||
|
||||
file_type = _get_file_type(file_id)
|
||||
|
||||
guessed_extension = _guess_extension(mime_type)
|
||||
|
||||
if file_type in PHOTO_TYPES:
|
||||
extension = "jpg"
|
||||
elif file_type == FileType.VOICE:
|
||||
extension = guessed_extension or "ogg"
|
||||
elif file_type in (FileType.VIDEO, FileType.ANIMATION, FileType.VIDEO_NOTE):
|
||||
extension = guessed_extension or "mp4"
|
||||
elif file_type == FileType.DOCUMENT:
|
||||
extension = guessed_extension or "zip"
|
||||
elif file_type == FileType.STICKER:
|
||||
extension = guessed_extension or "webp"
|
||||
elif file_type == FileType.AUDIO:
|
||||
extension = guessed_extension or "mp3"
|
||||
else:
|
||||
extension = "unknown"
|
||||
|
||||
if dot:
|
||||
extension = "." + extension
|
||||
return extension
|
||||
|
||||
|
||||
async def send_message_by_language(
|
||||
client: pyrogram.client.Client,
|
||||
language: Language,
|
||||
chat_id: Union[int, str],
|
||||
reply_to_message_id: int,
|
||||
language_str: List[str],
|
||||
):
|
||||
"""Record download status"""
|
||||
msg = language_str[language.value - 1]
|
||||
|
||||
return await client.send_message(
|
||||
chat_id, msg, reply_to_message_id=reply_to_message_id
|
||||
)
|
||||
|
||||
|
||||
async def download_thumbnail(
|
||||
client: pyrogram.Client,
|
||||
temp_path: str,
|
||||
message: pyrogram.types.Message,
|
||||
):
|
||||
"""Downloads the thumbnail of a video message to a temporary file.
|
||||
|
||||
Args:
|
||||
client: A Pyrogram client instance.
|
||||
temp_path: The path to a temporary directory where the thumbnail file
|
||||
will be stored.
|
||||
message: A Pyrogram Message object representing the video message.
|
||||
|
||||
Returns:
|
||||
A string representing the path of the thumbnail file, or None if the
|
||||
download failed.
|
||||
|
||||
Raises:
|
||||
ValueError: If the downloaded thumbnail file size doesn't match the
|
||||
expected file size.
|
||||
"""
|
||||
thumbnail_file = None
|
||||
if message.video.thumbs:
|
||||
message = await fetch_message(client, message)
|
||||
thumbnail = message.video.thumbs[0] if message.video.thumbs else None
|
||||
unique_name = os.path.join(
|
||||
temp_path,
|
||||
"thumbnail",
|
||||
f"thumb-{int(time.time())}-{secrets.token_hex(8)}.jpg",
|
||||
)
|
||||
|
||||
max_attempts = 3
|
||||
for attempt in range(1, max_attempts + 1):
|
||||
try:
|
||||
thumbnail_file = await client.download_media(
|
||||
thumbnail, file_name=unique_name
|
||||
)
|
||||
|
||||
if os.path.getsize(thumbnail_file) == thumbnail.file_size:
|
||||
break
|
||||
|
||||
raise ValueError(
|
||||
f"Thumbnail file size is {os.path.getsize(thumbnail_file)}"
|
||||
f" bytes, actual {thumbnail.file_size}: {thumbnail_file}"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
if attempt == max_attempts:
|
||||
logger.exception(
|
||||
f"Failed to download thumbnail after {max_attempts}"
|
||||
f" attempts: {e}"
|
||||
)
|
||||
else:
|
||||
message = await fetch_message(client, message)
|
||||
logger.warning(
|
||||
f"Attempt {attempt} to download thumbnail failed: {e}"
|
||||
)
|
||||
# Wait 2 seconds before retrying
|
||||
await asyncio.sleep(2)
|
||||
|
||||
thumbnail = None
|
||||
thumbnail_file = None
|
||||
return thumbnail_file
|
||||
|
||||
|
||||
async def upload_telegram_chat(
|
||||
client: pyrogram.Client,
|
||||
upload_user: pyrogram.Client,
|
||||
app: Application,
|
||||
node: TaskNode,
|
||||
message: pyrogram.types.Message,
|
||||
file_name: str,
|
||||
download_status: DownloadStatus,
|
||||
):
|
||||
"""Upload telegram chat"""
|
||||
# upload telegram
|
||||
forward_ret = ForwardStatus.FailedForward
|
||||
if node.upload_telegram_chat_id:
|
||||
if download_status is DownloadStatus.SkipDownload:
|
||||
forward_ret = ForwardStatus.SkipForward
|
||||
elif download_status is DownloadStatus.SuccessDownload:
|
||||
try:
|
||||
forward_ret = await upload_telegram_chat_message(
|
||||
client,
|
||||
upload_user,
|
||||
app,
|
||||
node.upload_telegram_chat_id,
|
||||
message,
|
||||
file_name,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.exception(f"Upload file {file_name} error: {e}")
|
||||
finally:
|
||||
if app.after_upload_telegram_delete:
|
||||
os.remove(file_name)
|
||||
|
||||
# forward text
|
||||
# FIXME: fix upload text
|
||||
# if (
|
||||
# download_status is DownloadStatus.SkipDownload
|
||||
# and message.text
|
||||
# and bot
|
||||
# ):
|
||||
# await upload_telegram_chat(
|
||||
# client, app, node.upload_telegram_chat_id, message, file_name
|
||||
# )
|
||||
node.stat_forward(forward_ret)
|
||||
|
||||
|
||||
async def upload_telegram_chat_message(
|
||||
client: pyrogram.Client,
|
||||
upload_user: pyrogram.Client,
|
||||
app: Application,
|
||||
upload_telegram_chat_id: Union[int, str],
|
||||
message: pyrogram.types.Message,
|
||||
file_name: str,
|
||||
) -> ForwardStatus:
|
||||
"""See upload telegram_chat"""
|
||||
max_attempts = 3
|
||||
for attempt in range(1, max_attempts + 1):
|
||||
try:
|
||||
await _upload_telegram_chat_message(
|
||||
client, upload_user, app, upload_telegram_chat_id, message, file_name
|
||||
)
|
||||
return ForwardStatus.SuccessForward
|
||||
except pyrogram.errors.exceptions.flood_420.FloodWait as wait_err:
|
||||
await asyncio.sleep(wait_err.value * 2)
|
||||
logger.warning(
|
||||
"Upload Message[{}]: FlowWait {}", message.id, wait_err.value
|
||||
)
|
||||
if attempt == max_attempts:
|
||||
return ForwardStatus.FailedForward
|
||||
|
||||
return ForwardStatus.FailedForward
|
||||
|
||||
|
||||
async def _upload_telegram_chat_message(
|
||||
client: pyrogram.Client,
|
||||
upload_user: pyrogram.Client,
|
||||
app: Application,
|
||||
upload_telegram_chat_id: Union[int, str],
|
||||
message: pyrogram.types.Message,
|
||||
file_name: str,
|
||||
):
|
||||
"""
|
||||
Uploads a video or message to a Telegram chat.
|
||||
|
||||
Parameters:
|
||||
client (pyrogram.Client): The pyrogram client.
|
||||
upload_telegram_chat_id (Union[int, str]): The ID of the chat to upload to.
|
||||
message (pyrogram.types.Message): The message to upload.
|
||||
file_name (str): The name of the file to upload.
|
||||
"""
|
||||
|
||||
if message.video:
|
||||
# Download thumbnail
|
||||
thumbnail_file = await download_thumbnail(client, app.temp_save_path, message)
|
||||
try:
|
||||
# TODO(tangyoha): add more log when upload video more than 2000MB failed
|
||||
# Send video to the destination chat
|
||||
await upload_user.send_video(
|
||||
chat_id=upload_telegram_chat_id,
|
||||
video=file_name,
|
||||
thumb=thumbnail_file,
|
||||
width=message.video.width,
|
||||
height=message.video.height,
|
||||
duration=message.video.duration,
|
||||
caption=message.caption or "",
|
||||
parse_mode=pyrogram.enums.ParseMode.HTML,
|
||||
)
|
||||
except Exception as e:
|
||||
raise e
|
||||
finally:
|
||||
if thumbnail_file:
|
||||
os.remove(str(thumbnail_file))
|
||||
|
||||
elif message.photo:
|
||||
await upload_user.send_photo(
|
||||
upload_telegram_chat_id, file_name, caption=message.caption
|
||||
)
|
||||
elif message.document:
|
||||
await upload_user.send_document(
|
||||
upload_telegram_chat_id, file_name, caption=message.caption
|
||||
)
|
||||
elif message.voice:
|
||||
await upload_user.send_voice(
|
||||
upload_telegram_chat_id, file_name, caption=message.caption
|
||||
)
|
||||
elif message.video_note:
|
||||
await upload_user.send_video_note(
|
||||
upload_telegram_chat_id, file_name, caption=message.caption
|
||||
)
|
||||
elif message.text:
|
||||
await upload_user.send_message(upload_telegram_chat_id, message.text)
|
||||
|
||||
|
||||
def record_download_status(func):
|
||||
"""Record download status"""
|
||||
|
||||
@wraps(func)
|
||||
async def inner(
|
||||
client: pyrogram.client.Client,
|
||||
message: pyrogram.types.Message,
|
||||
media_types: List[str],
|
||||
file_formats: dict,
|
||||
chat_id: Union[int, str],
|
||||
task_id: int = 0,
|
||||
):
|
||||
if _download_cache[(chat_id, message.id)] is DownloadStatus.Downloading:
|
||||
return DownloadStatus.Downloading, None
|
||||
|
||||
_download_cache[(chat_id, message.id)] = DownloadStatus.Downloading
|
||||
|
||||
status, file_name = await func(
|
||||
client, message, media_types, file_formats, chat_id, task_id
|
||||
)
|
||||
|
||||
_download_cache[(chat_id, message.id)] = status
|
||||
|
||||
return status, file_name
|
||||
|
||||
return inner
|
||||
|
||||
|
||||
async def report_bot_download_status(
|
||||
client: pyrogram.Client,
|
||||
node: TaskNode,
|
||||
download_status: DownloadStatus,
|
||||
download_size: int = 0,
|
||||
):
|
||||
"""
|
||||
Sends a message with the current status of the download bot.
|
||||
|
||||
Parameters:
|
||||
client (pyrogram.Client): The client instance.
|
||||
node (TaskNode): The download task node.
|
||||
download_status (DownloadStatus): The current download status.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
node.stat(download_status)
|
||||
node.total_download_byte += download_size
|
||||
await report_bot_status(client, node)
|
||||
|
||||
|
||||
async def report_bot_forward_status(
|
||||
client: pyrogram.Client,
|
||||
node: TaskNode,
|
||||
status: ForwardStatus,
|
||||
):
|
||||
"""
|
||||
Sends a message with the current status of the download bot.
|
||||
|
||||
Parameters:
|
||||
client (pyrogram.Client): The client instance.
|
||||
node (TaskNode): The download task node.
|
||||
status (ForwardStatus): The current forward status.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
node.stat_forward(status)
|
||||
await report_bot_status(client, node)
|
||||
|
||||
|
||||
async def report_bot_status(
|
||||
client: pyrogram.Client,
|
||||
node: TaskNode,
|
||||
immediate_reply=False,
|
||||
):
|
||||
"""
|
||||
Sends a message with the current status of the download bot.
|
||||
|
||||
Parameters:
|
||||
client (pyrogram.Client): The client instance.
|
||||
node (TaskNode): The download task node.
|
||||
immediate_reply(bool): Immediate reply
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
if not node.reply_message_id or not node.bot:
|
||||
return
|
||||
|
||||
if immediate_reply or node.can_reply():
|
||||
if node.upload_telegram_chat_id:
|
||||
node.forward_msg_detail_str = (
|
||||
f"\n📥 {_t('Forward')}\n"
|
||||
f"├─ 📁 {_t('Total')}: {node.total_forward_task}\n"
|
||||
f"├─ ✅ {_t('Success')}: {node.success_forward_task}\n"
|
||||
f"├─ ❌ {_t('Failed')}: {node.failed_forward_task}\n"
|
||||
f"└─ ⏩ {_t('Skipped')}: {node.skip_forward_task}\n"
|
||||
)
|
||||
|
||||
upload_msg_detail_str: str = ""
|
||||
|
||||
if node.upload_success_count:
|
||||
upload_msg_detail_str = (
|
||||
f"\n📥 {_t('Upload')}\n"
|
||||
f"└─ ✅ {_t('Success')}: {node.upload_success_count}\n"
|
||||
)
|
||||
|
||||
download_result_str = ""
|
||||
download_result = get_download_result()
|
||||
if node.chat_id in download_result:
|
||||
messages = download_result[node.chat_id]
|
||||
for idx, value in messages.items():
|
||||
task_id = value["task_id"]
|
||||
if task_id != node.task_id or value["down_byte"] == value["total_size"]:
|
||||
continue
|
||||
|
||||
temp_file_name = truncate_filename(
|
||||
os.path.basename(value["file_name"]), 10
|
||||
)
|
||||
progress = int(value["down_byte"] / value["total_size"] * 100)
|
||||
download_result_str += (
|
||||
f" ├─ 🆔 {_t('Message ID')}: {idx}\n"
|
||||
f" │ ├─ 📁 : {temp_file_name}\n"
|
||||
f" │ ├─ 📏 : {format_byte(value['total_size'])}\n"
|
||||
f" │ ├─ 🚀 : {format_byte(value['download_speed'])}/s\n"
|
||||
f" │ └─ 📊 : [{create_progress_bar(progress)}]"
|
||||
f" ({progress}%)\n"
|
||||
)
|
||||
|
||||
if download_result_str:
|
||||
download_result_str = "\n📈 Download Progresses:\n" + download_result_str
|
||||
|
||||
new_msg_str = (
|
||||
f"```\n"
|
||||
f"🆔 task id: {node.task_id}\n"
|
||||
f"📥 {_t('Downloading')}: {format_byte(node.total_download_byte)}\n"
|
||||
f"├─ 📁 {_t('Total')}: {node.total_download_task}\n"
|
||||
f"├─ ✅ {_t('Success')}: {node.success_download_task}\n"
|
||||
f"├─ ❌ {_t('Failed')}: {node.failed_download_task}\n"
|
||||
f"└─ ⏩ {_t('Skipped')}: {node.skip_download_task}\n"
|
||||
f"{node.forward_msg_detail_str}"
|
||||
f"{upload_msg_detail_str}"
|
||||
f"{download_result_str}\n```"
|
||||
)
|
||||
|
||||
try:
|
||||
if new_msg_str != node.last_edit_msg:
|
||||
await client.edit_message_text(
|
||||
node.from_user_id,
|
||||
node.reply_message_id,
|
||||
new_msg_str,
|
||||
parse_mode=pyrogram.enums.ParseMode.MARKDOWN,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def set_max_concurrent_transmissions(
|
||||
client: pyrogram.Client, max_concurrent_transmissions: int
|
||||
):
|
||||
"""Set maximum concurrent transmissions"""
|
||||
if getattr(client, "max_concurrent_transmissions", None):
|
||||
client.max_concurrent_transmissions = max_concurrent_transmissions
|
||||
client.save_file_semaphore = asyncio.Semaphore(
|
||||
client.max_concurrent_transmissions
|
||||
)
|
||||
client.get_file_semaphore = asyncio.Semaphore(
|
||||
client.max_concurrent_transmissions
|
||||
)
|
||||
|
||||
|
||||
async def fetch_message(client: pyrogram.Client, message: pyrogram.types.Message):
|
||||
"""
|
||||
This function retrieves a message from a specified chat using the Pyrogram library.
|
||||
Args:
|
||||
client (pyrogram.Client): A client instance created using Pyrogram.
|
||||
message (pyrogram.types.Message): A message instance returned from Pyrogram.
|
||||
Returns:
|
||||
pyrogram.types.Message: A message object retrieved from the specified chat.
|
||||
"""
|
||||
return await client.get_messages(
|
||||
chat_id=message.chat.id,
|
||||
message_ids=message.id,
|
||||
)
|
||||
|
||||
|
||||
async def get_message_with_retry(
|
||||
client: pyrogram.Client,
|
||||
chat_id: Union[int, str],
|
||||
message_id: int,
|
||||
max_attempts: int = 3,
|
||||
wait_second: int = 15,
|
||||
):
|
||||
"""
|
||||
This function retrieves a message from a specified chat using the Pyrogram library.
|
||||
Args:
|
||||
client (pyrogram.Client): A client instance created using Pyrogram.
|
||||
chat_id (Union[int, str]): Chat Id
|
||||
message_id (int): message id.
|
||||
Returns:
|
||||
pyrogram.types.Message: A message object retrieved from the specified chat.
|
||||
"""
|
||||
for attempt in range(1, max_attempts + 1):
|
||||
try:
|
||||
return await client.get_messages(
|
||||
chat_id=chat_id,
|
||||
message_ids=message_id,
|
||||
)
|
||||
except Exception as e:
|
||||
if attempt == max_attempts:
|
||||
logger.error("Failed Get Message[{}]", message_id)
|
||||
return None
|
||||
|
||||
logger.exception("Get Message[{}]: Error {}", message_id, e)
|
||||
await asyncio.sleep(wait_second)
|
||||
|
||||
|
||||
async def check_user_permission(
|
||||
client: pyrogram.Client, user_id: Union[int, str], chat_id: Union[int, str]
|
||||
) -> bool:
|
||||
"""
|
||||
Check if the user has permission to send videos in the group.
|
||||
|
||||
Args:
|
||||
client (pyrogram.Client): A client instance created using Pyrogram.
|
||||
user_id (Union[int, str]): User Id
|
||||
chat_id (Union[int, str]): Chat Id
|
||||
|
||||
Returns:
|
||||
if can_send_media_messages return True
|
||||
"""
|
||||
try:
|
||||
member = await client.get_chat_member(chat_id, user_id)
|
||||
return member and (
|
||||
not member.permissions or member.permissions.can_send_media_messages
|
||||
)
|
||||
except Exception:
|
||||
# logger.exception(e)
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def set_meta_data(
|
||||
meta_data: MetaData, message: pyrogram.types.Message, caption: str = None
|
||||
):
|
||||
"""Get all meta data"""
|
||||
# message
|
||||
meta_data.message_date = getattr(message, "date", None)
|
||||
if caption:
|
||||
meta_data.message_caption = caption
|
||||
else:
|
||||
meta_data.message_caption = getattr(message, "caption", None) or ""
|
||||
meta_data.message_id = getattr(message, "id", None)
|
||||
|
||||
from_user = getattr(message, "from_user")
|
||||
meta_data.sender_id = from_user.id if from_user else 0
|
||||
meta_data.sender_name = (from_user.username if from_user else "") or ""
|
||||
meta_data.reply_to_message_id = getattr(
|
||||
message, "reply_to_message_id", 1
|
||||
) # 1 for General
|
||||
|
||||
# media
|
||||
for kind in meta_data.AVAILABLE_MEDIA:
|
||||
media_obj = getattr(message, kind, None)
|
||||
if media_obj is not None:
|
||||
meta_data.media_type = kind
|
||||
break
|
||||
else:
|
||||
return
|
||||
meta_data.media_file_name = getattr(media_obj, "file_name", None) or ""
|
||||
meta_data.media_file_size = getattr(media_obj, "file_size", None)
|
||||
meta_data.media_width = getattr(media_obj, "width", None)
|
||||
meta_data.media_height = getattr(media_obj, "height", None)
|
||||
meta_data.media_duration = getattr(media_obj, "duration", None)
|
||||
meta_data.file_extension = get_extension(
|
||||
media_obj.file_id, getattr(media_obj, "mime_type", ""), False
|
||||
)
|
@ -0,0 +1,28 @@
|
||||
# Contribution
|
||||
|
||||
# Git Flow
|
||||
|
||||
The crypto-js project uses [git flow](https://github.com/nvie/gitflow) to manage branches.
|
||||
Do your changes on the `develop` or even better on a `feature/*` branch. Don't do any changes on the `master` branch.
|
||||
|
||||
# Pull request
|
||||
|
||||
Target your pull request on `develop` branch. Other pull request won't be accepted.
|
||||
|
||||
# How to build
|
||||
|
||||
1. Clone
|
||||
|
||||
2. Run
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
3. Run
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
|
||||
4. Check `build` folder
|
@ -0,0 +1,24 @@
|
||||
# License
|
||||
|
||||
[The MIT License (MIT)](http://opensource.org/licenses/MIT)
|
||||
|
||||
Copyright (c) 2009-2013 Jeff Mott
|
||||
Copyright (c) 2013-2016 Evan Vosberg
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
@ -0,0 +1,259 @@
|
||||
# crypto-js [![Build Status](https://travis-ci.org/brix/crypto-js.svg?branch=develop)](https://travis-ci.org/brix/crypto-js)
|
||||
|
||||
JavaScript library of crypto standards.
|
||||
|
||||
## Node.js (Install)
|
||||
|
||||
Requirements:
|
||||
|
||||
- Node.js
|
||||
- npm (Node.js package manager)
|
||||
|
||||
```bash
|
||||
npm install crypto-js
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
ES6 import for typical API call signing use case:
|
||||
|
||||
```javascript
|
||||
import sha256 from 'crypto-js/sha256';
|
||||
import hmacSHA512 from 'crypto-js/hmac-sha512';
|
||||
import Base64 from 'crypto-js/enc-base64';
|
||||
|
||||
const message, nonce, path, privateKey; // ...
|
||||
const hashDigest = sha256(nonce + message);
|
||||
const hmacDigest = Base64.stringify(hmacSHA512(path + hashDigest, privateKey));
|
||||
```
|
||||
|
||||
Modular include:
|
||||
|
||||
```javascript
|
||||
var AES = require("crypto-js/aes");
|
||||
var SHA256 = require("crypto-js/sha256");
|
||||
...
|
||||
console.log(SHA256("Message"));
|
||||
```
|
||||
|
||||
Including all libraries, for access to extra methods:
|
||||
|
||||
```javascript
|
||||
var CryptoJS = require("crypto-js");
|
||||
console.log(CryptoJS.HmacSHA1("Message", "Key"));
|
||||
```
|
||||
|
||||
## Client (browser)
|
||||
|
||||
Requirements:
|
||||
|
||||
- Node.js
|
||||
- Bower (package manager for frontend)
|
||||
|
||||
```bash
|
||||
bower install crypto-js
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
Modular include:
|
||||
|
||||
```javascript
|
||||
require.config({
|
||||
packages: [
|
||||
{
|
||||
name: 'crypto-js',
|
||||
location: 'path-to/bower_components/crypto-js',
|
||||
main: 'index'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
require(["crypto-js/aes", "crypto-js/sha256"], function (AES, SHA256) {
|
||||
console.log(SHA256("Message"));
|
||||
});
|
||||
```
|
||||
|
||||
Including all libraries, for access to extra methods:
|
||||
|
||||
```javascript
|
||||
// Above-mentioned will work or use this simple form
|
||||
require.config({
|
||||
paths: {
|
||||
'crypto-js': 'path-to/bower_components/crypto-js/crypto-js'
|
||||
}
|
||||
});
|
||||
|
||||
require(["crypto-js"], function (CryptoJS) {
|
||||
console.log(CryptoJS.HmacSHA1("Message", "Key"));
|
||||
});
|
||||
```
|
||||
|
||||
### Usage without RequireJS
|
||||
|
||||
```html
|
||||
<script type="text/javascript" src="path-to/bower_components/crypto-js/crypto-js.js"></script>
|
||||
<script type="text/javascript">
|
||||
var encrypted = CryptoJS.AES(...);
|
||||
var encrypted = CryptoJS.SHA256(...);
|
||||
</script>
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
See: https://cryptojs.gitbook.io/docs/
|
||||
|
||||
### AES Encryption
|
||||
|
||||
#### Plain text encryption
|
||||
|
||||
```javascript
|
||||
var CryptoJS = require("crypto-js");
|
||||
|
||||
// Encrypt
|
||||
var ciphertext = CryptoJS.AES.encrypt('my message', 'secret key 123').toString();
|
||||
|
||||
// Decrypt
|
||||
var bytes = CryptoJS.AES.decrypt(ciphertext, 'secret key 123');
|
||||
var originalText = bytes.toString(CryptoJS.enc.Utf8);
|
||||
|
||||
console.log(originalText); // 'my message'
|
||||
```
|
||||
|
||||
#### Object encryption
|
||||
|
||||
```javascript
|
||||
var CryptoJS = require("crypto-js");
|
||||
|
||||
var data = [{id: 1}, {id: 2}]
|
||||
|
||||
// Encrypt
|
||||
var ciphertext = CryptoJS.AES.encrypt(JSON.stringify(data), 'secret key 123').toString();
|
||||
|
||||
// Decrypt
|
||||
var bytes = CryptoJS.AES.decrypt(ciphertext, 'secret key 123');
|
||||
var decryptedData = JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
|
||||
|
||||
console.log(decryptedData); // [{id: 1}, {id: 2}]
|
||||
```
|
||||
|
||||
### List of modules
|
||||
|
||||
|
||||
- ```crypto-js/core```
|
||||
- ```crypto-js/x64-core```
|
||||
- ```crypto-js/lib-typedarrays```
|
||||
|
||||
---
|
||||
|
||||
- ```crypto-js/md5```
|
||||
- ```crypto-js/sha1```
|
||||
- ```crypto-js/sha256```
|
||||
- ```crypto-js/sha224```
|
||||
- ```crypto-js/sha512```
|
||||
- ```crypto-js/sha384```
|
||||
- ```crypto-js/sha3```
|
||||
- ```crypto-js/ripemd160```
|
||||
|
||||
---
|
||||
|
||||
- ```crypto-js/hmac-md5```
|
||||
- ```crypto-js/hmac-sha1```
|
||||
- ```crypto-js/hmac-sha256```
|
||||
- ```crypto-js/hmac-sha224```
|
||||
- ```crypto-js/hmac-sha512```
|
||||
- ```crypto-js/hmac-sha384```
|
||||
- ```crypto-js/hmac-sha3```
|
||||
- ```crypto-js/hmac-ripemd160```
|
||||
|
||||
---
|
||||
|
||||
- ```crypto-js/pbkdf2```
|
||||
|
||||
---
|
||||
|
||||
- ```crypto-js/aes```
|
||||
- ```crypto-js/tripledes```
|
||||
- ```crypto-js/rc4```
|
||||
- ```crypto-js/rabbit```
|
||||
- ```crypto-js/rabbit-legacy```
|
||||
- ```crypto-js/evpkdf```
|
||||
|
||||
---
|
||||
|
||||
- ```crypto-js/format-openssl```
|
||||
- ```crypto-js/format-hex```
|
||||
|
||||
---
|
||||
|
||||
- ```crypto-js/enc-latin1```
|
||||
- ```crypto-js/enc-utf8```
|
||||
- ```crypto-js/enc-hex```
|
||||
- ```crypto-js/enc-utf16```
|
||||
- ```crypto-js/enc-base64```
|
||||
|
||||
---
|
||||
|
||||
- ```crypto-js/mode-cfb```
|
||||
- ```crypto-js/mode-ctr```
|
||||
- ```crypto-js/mode-ctr-gladman```
|
||||
- ```crypto-js/mode-ofb```
|
||||
- ```crypto-js/mode-ecb```
|
||||
|
||||
---
|
||||
|
||||
- ```crypto-js/pad-pkcs7```
|
||||
- ```crypto-js/pad-ansix923```
|
||||
- ```crypto-js/pad-iso10126```
|
||||
- ```crypto-js/pad-iso97971```
|
||||
- ```crypto-js/pad-zeropadding```
|
||||
- ```crypto-js/pad-nopadding```
|
||||
|
||||
|
||||
## Release notes
|
||||
|
||||
### 4.1.1
|
||||
|
||||
Fix module order in bundled release.
|
||||
|
||||
Include the browser field in the released package.json.
|
||||
|
||||
### 4.1.0
|
||||
|
||||
Added url safe variant of base64 encoding. [357](https://github.com/brix/crypto-js/pull/357)
|
||||
|
||||
Avoid webpack to add crypto-browser package. [364](https://github.com/brix/crypto-js/pull/364)
|
||||
|
||||
### 4.0.0
|
||||
|
||||
This is an update including breaking changes for some environments.
|
||||
|
||||
In this version `Math.random()` has been replaced by the random methods of the native crypto module.
|
||||
|
||||
For this reason CryptoJS might not run in some JavaScript environments without native crypto module. Such as IE 10 or before or React Native.
|
||||
|
||||
### 3.3.0
|
||||
|
||||
Rollback, `3.3.0` is the same as `3.1.9-1`.
|
||||
|
||||
The move of using native secure crypto module will be shifted to a new `4.x.x` version. As it is a breaking change the impact is too big for a minor release.
|
||||
|
||||
### 3.2.1
|
||||
|
||||
The usage of the native crypto module has been fixed. The import and access of the native crypto module has been improved.
|
||||
|
||||
### 3.2.0
|
||||
|
||||
In this version `Math.random()` has been replaced by the random methods of the native crypto module.
|
||||
|
||||
For this reason CryptoJS might does not run in some JavaScript environments without native crypto module. Such as IE 10 or before.
|
||||
|
||||
If it's absolute required to run CryptoJS in such an environment, stay with `3.1.x` version. Encrypting and decrypting stays compatible. But keep in mind `3.1.x` versions still use `Math.random()` which is cryptographically not secure, as it's not random enough.
|
||||
|
||||
This version came along with `CRITICAL` `BUG`.
|
||||
|
||||
DO NOT USE THIS VERSION! Please, go for a newer version!
|
||||
|
||||
### 3.1.x
|
||||
|
||||
The `3.1.x` are based on the original CryptoJS, wrapped in CommonJS modules.
|
@ -0,0 +1,234 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./enc-base64"), require("./md5"), require("./evpkdf"), require("./cipher-core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./enc-base64", "./md5", "./evpkdf", "./cipher-core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
(function () {
|
||||
// Shortcuts
|
||||
var C = CryptoJS;
|
||||
var C_lib = C.lib;
|
||||
var BlockCipher = C_lib.BlockCipher;
|
||||
var C_algo = C.algo;
|
||||
|
||||
// Lookup tables
|
||||
var SBOX = [];
|
||||
var INV_SBOX = [];
|
||||
var SUB_MIX_0 = [];
|
||||
var SUB_MIX_1 = [];
|
||||
var SUB_MIX_2 = [];
|
||||
var SUB_MIX_3 = [];
|
||||
var INV_SUB_MIX_0 = [];
|
||||
var INV_SUB_MIX_1 = [];
|
||||
var INV_SUB_MIX_2 = [];
|
||||
var INV_SUB_MIX_3 = [];
|
||||
|
||||
// Compute lookup tables
|
||||
(function () {
|
||||
// Compute double table
|
||||
var d = [];
|
||||
for (var i = 0; i < 256; i++) {
|
||||
if (i < 128) {
|
||||
d[i] = i << 1;
|
||||
} else {
|
||||
d[i] = (i << 1) ^ 0x11b;
|
||||
}
|
||||
}
|
||||
|
||||
// Walk GF(2^8)
|
||||
var x = 0;
|
||||
var xi = 0;
|
||||
for (var i = 0; i < 256; i++) {
|
||||
// Compute sbox
|
||||
var sx = xi ^ (xi << 1) ^ (xi << 2) ^ (xi << 3) ^ (xi << 4);
|
||||
sx = (sx >>> 8) ^ (sx & 0xff) ^ 0x63;
|
||||
SBOX[x] = sx;
|
||||
INV_SBOX[sx] = x;
|
||||
|
||||
// Compute multiplication
|
||||
var x2 = d[x];
|
||||
var x4 = d[x2];
|
||||
var x8 = d[x4];
|
||||
|
||||
// Compute sub bytes, mix columns tables
|
||||
var t = (d[sx] * 0x101) ^ (sx * 0x1010100);
|
||||
SUB_MIX_0[x] = (t << 24) | (t >>> 8);
|
||||
SUB_MIX_1[x] = (t << 16) | (t >>> 16);
|
||||
SUB_MIX_2[x] = (t << 8) | (t >>> 24);
|
||||
SUB_MIX_3[x] = t;
|
||||
|
||||
// Compute inv sub bytes, inv mix columns tables
|
||||
var t = (x8 * 0x1010101) ^ (x4 * 0x10001) ^ (x2 * 0x101) ^ (x * 0x1010100);
|
||||
INV_SUB_MIX_0[sx] = (t << 24) | (t >>> 8);
|
||||
INV_SUB_MIX_1[sx] = (t << 16) | (t >>> 16);
|
||||
INV_SUB_MIX_2[sx] = (t << 8) | (t >>> 24);
|
||||
INV_SUB_MIX_3[sx] = t;
|
||||
|
||||
// Compute next counter
|
||||
if (!x) {
|
||||
x = xi = 1;
|
||||
} else {
|
||||
x = x2 ^ d[d[d[x8 ^ x2]]];
|
||||
xi ^= d[d[xi]];
|
||||
}
|
||||
}
|
||||
}());
|
||||
|
||||
// Precomputed Rcon lookup
|
||||
var RCON = [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36];
|
||||
|
||||
/**
|
||||
* AES block cipher algorithm.
|
||||
*/
|
||||
var AES = C_algo.AES = BlockCipher.extend({
|
||||
_doReset: function () {
|
||||
var t;
|
||||
|
||||
// Skip reset of nRounds has been set before and key did not change
|
||||
if (this._nRounds && this._keyPriorReset === this._key) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Shortcuts
|
||||
var key = this._keyPriorReset = this._key;
|
||||
var keyWords = key.words;
|
||||
var keySize = key.sigBytes / 4;
|
||||
|
||||
// Compute number of rounds
|
||||
var nRounds = this._nRounds = keySize + 6;
|
||||
|
||||
// Compute number of key schedule rows
|
||||
var ksRows = (nRounds + 1) * 4;
|
||||
|
||||
// Compute key schedule
|
||||
var keySchedule = this._keySchedule = [];
|
||||
for (var ksRow = 0; ksRow < ksRows; ksRow++) {
|
||||
if (ksRow < keySize) {
|
||||
keySchedule[ksRow] = keyWords[ksRow];
|
||||
} else {
|
||||
t = keySchedule[ksRow - 1];
|
||||
|
||||
if (!(ksRow % keySize)) {
|
||||
// Rot word
|
||||
t = (t << 8) | (t >>> 24);
|
||||
|
||||
// Sub word
|
||||
t = (SBOX[t >>> 24] << 24) | (SBOX[(t >>> 16) & 0xff] << 16) | (SBOX[(t >>> 8) & 0xff] << 8) | SBOX[t & 0xff];
|
||||
|
||||
// Mix Rcon
|
||||
t ^= RCON[(ksRow / keySize) | 0] << 24;
|
||||
} else if (keySize > 6 && ksRow % keySize == 4) {
|
||||
// Sub word
|
||||
t = (SBOX[t >>> 24] << 24) | (SBOX[(t >>> 16) & 0xff] << 16) | (SBOX[(t >>> 8) & 0xff] << 8) | SBOX[t & 0xff];
|
||||
}
|
||||
|
||||
keySchedule[ksRow] = keySchedule[ksRow - keySize] ^ t;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute inv key schedule
|
||||
var invKeySchedule = this._invKeySchedule = [];
|
||||
for (var invKsRow = 0; invKsRow < ksRows; invKsRow++) {
|
||||
var ksRow = ksRows - invKsRow;
|
||||
|
||||
if (invKsRow % 4) {
|
||||
var t = keySchedule[ksRow];
|
||||
} else {
|
||||
var t = keySchedule[ksRow - 4];
|
||||
}
|
||||
|
||||
if (invKsRow < 4 || ksRow <= 4) {
|
||||
invKeySchedule[invKsRow] = t;
|
||||
} else {
|
||||
invKeySchedule[invKsRow] = INV_SUB_MIX_0[SBOX[t >>> 24]] ^ INV_SUB_MIX_1[SBOX[(t >>> 16) & 0xff]] ^
|
||||
INV_SUB_MIX_2[SBOX[(t >>> 8) & 0xff]] ^ INV_SUB_MIX_3[SBOX[t & 0xff]];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
encryptBlock: function (M, offset) {
|
||||
this._doCryptBlock(M, offset, this._keySchedule, SUB_MIX_0, SUB_MIX_1, SUB_MIX_2, SUB_MIX_3, SBOX);
|
||||
},
|
||||
|
||||
decryptBlock: function (M, offset) {
|
||||
// Swap 2nd and 4th rows
|
||||
var t = M[offset + 1];
|
||||
M[offset + 1] = M[offset + 3];
|
||||
M[offset + 3] = t;
|
||||
|
||||
this._doCryptBlock(M, offset, this._invKeySchedule, INV_SUB_MIX_0, INV_SUB_MIX_1, INV_SUB_MIX_2, INV_SUB_MIX_3, INV_SBOX);
|
||||
|
||||
// Inv swap 2nd and 4th rows
|
||||
var t = M[offset + 1];
|
||||
M[offset + 1] = M[offset + 3];
|
||||
M[offset + 3] = t;
|
||||
},
|
||||
|
||||
_doCryptBlock: function (M, offset, keySchedule, SUB_MIX_0, SUB_MIX_1, SUB_MIX_2, SUB_MIX_3, SBOX) {
|
||||
// Shortcut
|
||||
var nRounds = this._nRounds;
|
||||
|
||||
// Get input, add round key
|
||||
var s0 = M[offset] ^ keySchedule[0];
|
||||
var s1 = M[offset + 1] ^ keySchedule[1];
|
||||
var s2 = M[offset + 2] ^ keySchedule[2];
|
||||
var s3 = M[offset + 3] ^ keySchedule[3];
|
||||
|
||||
// Key schedule row counter
|
||||
var ksRow = 4;
|
||||
|
||||
// Rounds
|
||||
for (var round = 1; round < nRounds; round++) {
|
||||
// Shift rows, sub bytes, mix columns, add round key
|
||||
var t0 = SUB_MIX_0[s0 >>> 24] ^ SUB_MIX_1[(s1 >>> 16) & 0xff] ^ SUB_MIX_2[(s2 >>> 8) & 0xff] ^ SUB_MIX_3[s3 & 0xff] ^ keySchedule[ksRow++];
|
||||
var t1 = SUB_MIX_0[s1 >>> 24] ^ SUB_MIX_1[(s2 >>> 16) & 0xff] ^ SUB_MIX_2[(s3 >>> 8) & 0xff] ^ SUB_MIX_3[s0 & 0xff] ^ keySchedule[ksRow++];
|
||||
var t2 = SUB_MIX_0[s2 >>> 24] ^ SUB_MIX_1[(s3 >>> 16) & 0xff] ^ SUB_MIX_2[(s0 >>> 8) & 0xff] ^ SUB_MIX_3[s1 & 0xff] ^ keySchedule[ksRow++];
|
||||
var t3 = SUB_MIX_0[s3 >>> 24] ^ SUB_MIX_1[(s0 >>> 16) & 0xff] ^ SUB_MIX_2[(s1 >>> 8) & 0xff] ^ SUB_MIX_3[s2 & 0xff] ^ keySchedule[ksRow++];
|
||||
|
||||
// Update state
|
||||
s0 = t0;
|
||||
s1 = t1;
|
||||
s2 = t2;
|
||||
s3 = t3;
|
||||
}
|
||||
|
||||
// Shift rows, sub bytes, add round key
|
||||
var t0 = ((SBOX[s0 >>> 24] << 24) | (SBOX[(s1 >>> 16) & 0xff] << 16) | (SBOX[(s2 >>> 8) & 0xff] << 8) | SBOX[s3 & 0xff]) ^ keySchedule[ksRow++];
|
||||
var t1 = ((SBOX[s1 >>> 24] << 24) | (SBOX[(s2 >>> 16) & 0xff] << 16) | (SBOX[(s3 >>> 8) & 0xff] << 8) | SBOX[s0 & 0xff]) ^ keySchedule[ksRow++];
|
||||
var t2 = ((SBOX[s2 >>> 24] << 24) | (SBOX[(s3 >>> 16) & 0xff] << 16) | (SBOX[(s0 >>> 8) & 0xff] << 8) | SBOX[s1 & 0xff]) ^ keySchedule[ksRow++];
|
||||
var t3 = ((SBOX[s3 >>> 24] << 24) | (SBOX[(s0 >>> 16) & 0xff] << 16) | (SBOX[(s1 >>> 8) & 0xff] << 8) | SBOX[s2 & 0xff]) ^ keySchedule[ksRow++];
|
||||
|
||||
// Set output
|
||||
M[offset] = t0;
|
||||
M[offset + 1] = t1;
|
||||
M[offset + 2] = t2;
|
||||
M[offset + 3] = t3;
|
||||
},
|
||||
|
||||
keySize: 256/32
|
||||
});
|
||||
|
||||
/**
|
||||
* Shortcut functions to the cipher's object interface.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var ciphertext = CryptoJS.AES.encrypt(message, key, cfg);
|
||||
* var plaintext = CryptoJS.AES.decrypt(ciphertext, key, cfg);
|
||||
*/
|
||||
C.AES = BlockCipher._createHelper(AES);
|
||||
}());
|
||||
|
||||
|
||||
return CryptoJS.AES;
|
||||
|
||||
}));
|
@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "crypto-js",
|
||||
"version": "4.1.1",
|
||||
"description": "JavaScript library of crypto standards.",
|
||||
"license": "MIT",
|
||||
"homepage": "http://github.com/brix/crypto-js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/brix/crypto-js.git"
|
||||
},
|
||||
"keywords": [
|
||||
"security",
|
||||
"crypto",
|
||||
"Hash",
|
||||
"MD5",
|
||||
"SHA1",
|
||||
"SHA-1",
|
||||
"SHA256",
|
||||
"SHA-256",
|
||||
"RC4",
|
||||
"Rabbit",
|
||||
"AES",
|
||||
"DES",
|
||||
"PBKDF2",
|
||||
"HMAC",
|
||||
"OFB",
|
||||
"CFB",
|
||||
"CTR",
|
||||
"CBC",
|
||||
"Base64",
|
||||
"Base64url"
|
||||
],
|
||||
"main": "index.js",
|
||||
"dependencies": {},
|
||||
"browser": {
|
||||
"crypto": false
|
||||
},
|
||||
"ignore": []
|
||||
}
|
@ -0,0 +1,890 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./evpkdf"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./evpkdf"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
/**
|
||||
* Cipher core components.
|
||||
*/
|
||||
CryptoJS.lib.Cipher || (function (undefined) {
|
||||
// Shortcuts
|
||||
var C = CryptoJS;
|
||||
var C_lib = C.lib;
|
||||
var Base = C_lib.Base;
|
||||
var WordArray = C_lib.WordArray;
|
||||
var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm;
|
||||
var C_enc = C.enc;
|
||||
var Utf8 = C_enc.Utf8;
|
||||
var Base64 = C_enc.Base64;
|
||||
var C_algo = C.algo;
|
||||
var EvpKDF = C_algo.EvpKDF;
|
||||
|
||||
/**
|
||||
* Abstract base cipher template.
|
||||
*
|
||||
* @property {number} keySize This cipher's key size. Default: 4 (128 bits)
|
||||
* @property {number} ivSize This cipher's IV size. Default: 4 (128 bits)
|
||||
* @property {number} _ENC_XFORM_MODE A constant representing encryption mode.
|
||||
* @property {number} _DEC_XFORM_MODE A constant representing decryption mode.
|
||||
*/
|
||||
var Cipher = C_lib.Cipher = BufferedBlockAlgorithm.extend({
|
||||
/**
|
||||
* Configuration options.
|
||||
*
|
||||
* @property {WordArray} iv The IV to use for this operation.
|
||||
*/
|
||||
cfg: Base.extend(),
|
||||
|
||||
/**
|
||||
* Creates this cipher in encryption mode.
|
||||
*
|
||||
* @param {WordArray} key The key.
|
||||
* @param {Object} cfg (Optional) The configuration options to use for this operation.
|
||||
*
|
||||
* @return {Cipher} A cipher instance.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var cipher = CryptoJS.algo.AES.createEncryptor(keyWordArray, { iv: ivWordArray });
|
||||
*/
|
||||
createEncryptor: function (key, cfg) {
|
||||
return this.create(this._ENC_XFORM_MODE, key, cfg);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates this cipher in decryption mode.
|
||||
*
|
||||
* @param {WordArray} key The key.
|
||||
* @param {Object} cfg (Optional) The configuration options to use for this operation.
|
||||
*
|
||||
* @return {Cipher} A cipher instance.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var cipher = CryptoJS.algo.AES.createDecryptor(keyWordArray, { iv: ivWordArray });
|
||||
*/
|
||||
createDecryptor: function (key, cfg) {
|
||||
return this.create(this._DEC_XFORM_MODE, key, cfg);
|
||||
},
|
||||
|
||||
/**
|
||||
* Initializes a newly created cipher.
|
||||
*
|
||||
* @param {number} xformMode Either the encryption or decryption transormation mode constant.
|
||||
* @param {WordArray} key The key.
|
||||
* @param {Object} cfg (Optional) The configuration options to use for this operation.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var cipher = CryptoJS.algo.AES.create(CryptoJS.algo.AES._ENC_XFORM_MODE, keyWordArray, { iv: ivWordArray });
|
||||
*/
|
||||
init: function (xformMode, key, cfg) {
|
||||
// Apply config defaults
|
||||
this.cfg = this.cfg.extend(cfg);
|
||||
|
||||
// Store transform mode and key
|
||||
this._xformMode = xformMode;
|
||||
this._key = key;
|
||||
|
||||
// Set initial values
|
||||
this.reset();
|
||||
},
|
||||
|
||||
/**
|
||||
* Resets this cipher to its initial state.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* cipher.reset();
|
||||
*/
|
||||
reset: function () {
|
||||
// Reset data buffer
|
||||
BufferedBlockAlgorithm.reset.call(this);
|
||||
|
||||
// Perform concrete-cipher logic
|
||||
this._doReset();
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds data to be encrypted or decrypted.
|
||||
*
|
||||
* @param {WordArray|string} dataUpdate The data to encrypt or decrypt.
|
||||
*
|
||||
* @return {WordArray} The data after processing.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var encrypted = cipher.process('data');
|
||||
* var encrypted = cipher.process(wordArray);
|
||||
*/
|
||||
process: function (dataUpdate) {
|
||||
// Append
|
||||
this._append(dataUpdate);
|
||||
|
||||
// Process available blocks
|
||||
return this._process();
|
||||
},
|
||||
|
||||
/**
|
||||
* Finalizes the encryption or decryption process.
|
||||
* Note that the finalize operation is effectively a destructive, read-once operation.
|
||||
*
|
||||
* @param {WordArray|string} dataUpdate The final data to encrypt or decrypt.
|
||||
*
|
||||
* @return {WordArray} The data after final processing.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var encrypted = cipher.finalize();
|
||||
* var encrypted = cipher.finalize('data');
|
||||
* var encrypted = cipher.finalize(wordArray);
|
||||
*/
|
||||
finalize: function (dataUpdate) {
|
||||
// Final data update
|
||||
if (dataUpdate) {
|
||||
this._append(dataUpdate);
|
||||
}
|
||||
|
||||
// Perform concrete-cipher logic
|
||||
var finalProcessedData = this._doFinalize();
|
||||
|
||||
return finalProcessedData;
|
||||
},
|
||||
|
||||
keySize: 128/32,
|
||||
|
||||
ivSize: 128/32,
|
||||
|
||||
_ENC_XFORM_MODE: 1,
|
||||
|
||||
_DEC_XFORM_MODE: 2,
|
||||
|
||||
/**
|
||||
* Creates shortcut functions to a cipher's object interface.
|
||||
*
|
||||
* @param {Cipher} cipher The cipher to create a helper for.
|
||||
*
|
||||
* @return {Object} An object with encrypt and decrypt shortcut functions.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var AES = CryptoJS.lib.Cipher._createHelper(CryptoJS.algo.AES);
|
||||
*/
|
||||
_createHelper: (function () {
|
||||
function selectCipherStrategy(key) {
|
||||
if (typeof key == 'string') {
|
||||
return PasswordBasedCipher;
|
||||
} else {
|
||||
return SerializableCipher;
|
||||
}
|
||||
}
|
||||
|
||||
return function (cipher) {
|
||||
return {
|
||||
encrypt: function (message, key, cfg) {
|
||||
return selectCipherStrategy(key).encrypt(cipher, message, key, cfg);
|
||||
},
|
||||
|
||||
decrypt: function (ciphertext, key, cfg) {
|
||||
return selectCipherStrategy(key).decrypt(cipher, ciphertext, key, cfg);
|
||||
}
|
||||
};
|
||||
};
|
||||
}())
|
||||
});
|
||||
|
||||
/**
|
||||
* Abstract base stream cipher template.
|
||||
*
|
||||
* @property {number} blockSize The number of 32-bit words this cipher operates on. Default: 1 (32 bits)
|
||||
*/
|
||||
var StreamCipher = C_lib.StreamCipher = Cipher.extend({
|
||||
_doFinalize: function () {
|
||||
// Process partial blocks
|
||||
var finalProcessedBlocks = this._process(!!'flush');
|
||||
|
||||
return finalProcessedBlocks;
|
||||
},
|
||||
|
||||
blockSize: 1
|
||||
});
|
||||
|
||||
/**
|
||||
* Mode namespace.
|
||||
*/
|
||||
var C_mode = C.mode = {};
|
||||
|
||||
/**
|
||||
* Abstract base block cipher mode template.
|
||||
*/
|
||||
var BlockCipherMode = C_lib.BlockCipherMode = Base.extend({
|
||||
/**
|
||||
* Creates this mode for encryption.
|
||||
*
|
||||
* @param {Cipher} cipher A block cipher instance.
|
||||
* @param {Array} iv The IV words.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var mode = CryptoJS.mode.CBC.createEncryptor(cipher, iv.words);
|
||||
*/
|
||||
createEncryptor: function (cipher, iv) {
|
||||
return this.Encryptor.create(cipher, iv);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates this mode for decryption.
|
||||
*
|
||||
* @param {Cipher} cipher A block cipher instance.
|
||||
* @param {Array} iv The IV words.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var mode = CryptoJS.mode.CBC.createDecryptor(cipher, iv.words);
|
||||
*/
|
||||
createDecryptor: function (cipher, iv) {
|
||||
return this.Decryptor.create(cipher, iv);
|
||||
},
|
||||
|
||||
/**
|
||||
* Initializes a newly created mode.
|
||||
*
|
||||
* @param {Cipher} cipher A block cipher instance.
|
||||
* @param {Array} iv The IV words.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var mode = CryptoJS.mode.CBC.Encryptor.create(cipher, iv.words);
|
||||
*/
|
||||
init: function (cipher, iv) {
|
||||
this._cipher = cipher;
|
||||
this._iv = iv;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Cipher Block Chaining mode.
|
||||
*/
|
||||
var CBC = C_mode.CBC = (function () {
|
||||
/**
|
||||
* Abstract base CBC mode.
|
||||
*/
|
||||
var CBC = BlockCipherMode.extend();
|
||||
|
||||
/**
|
||||
* CBC encryptor.
|
||||
*/
|
||||
CBC.Encryptor = CBC.extend({
|
||||
/**
|
||||
* Processes the data block at offset.
|
||||
*
|
||||
* @param {Array} words The data words to operate on.
|
||||
* @param {number} offset The offset where the block starts.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* mode.processBlock(data.words, offset);
|
||||
*/
|
||||
processBlock: function (words, offset) {
|
||||
// Shortcuts
|
||||
var cipher = this._cipher;
|
||||
var blockSize = cipher.blockSize;
|
||||
|
||||
// XOR and encrypt
|
||||
xorBlock.call(this, words, offset, blockSize);
|
||||
cipher.encryptBlock(words, offset);
|
||||
|
||||
// Remember this block to use with next block
|
||||
this._prevBlock = words.slice(offset, offset + blockSize);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* CBC decryptor.
|
||||
*/
|
||||
CBC.Decryptor = CBC.extend({
|
||||
/**
|
||||
* Processes the data block at offset.
|
||||
*
|
||||
* @param {Array} words The data words to operate on.
|
||||
* @param {number} offset The offset where the block starts.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* mode.processBlock(data.words, offset);
|
||||
*/
|
||||
processBlock: function (words, offset) {
|
||||
// Shortcuts
|
||||
var cipher = this._cipher;
|
||||
var blockSize = cipher.blockSize;
|
||||
|
||||
// Remember this block to use with next block
|
||||
var thisBlock = words.slice(offset, offset + blockSize);
|
||||
|
||||
// Decrypt and XOR
|
||||
cipher.decryptBlock(words, offset);
|
||||
xorBlock.call(this, words, offset, blockSize);
|
||||
|
||||
// This block becomes the previous block
|
||||
this._prevBlock = thisBlock;
|
||||
}
|
||||
});
|
||||
|
||||
function xorBlock(words, offset, blockSize) {
|
||||
var block;
|
||||
|
||||
// Shortcut
|
||||
var iv = this._iv;
|
||||
|
||||
// Choose mixing block
|
||||
if (iv) {
|
||||
block = iv;
|
||||
|
||||
// Remove IV for subsequent blocks
|
||||
this._iv = undefined;
|
||||
} else {
|
||||
block = this._prevBlock;
|
||||
}
|
||||
|
||||
// XOR blocks
|
||||
for (var i = 0; i < blockSize; i++) {
|
||||
words[offset + i] ^= block[i];
|
||||
}
|
||||
}
|
||||
|
||||
return CBC;
|
||||
}());
|
||||
|
||||
/**
|
||||
* Padding namespace.
|
||||
*/
|
||||
var C_pad = C.pad = {};
|
||||
|
||||
/**
|
||||
* PKCS #5/7 padding strategy.
|
||||
*/
|
||||
var Pkcs7 = C_pad.Pkcs7 = {
|
||||
/**
|
||||
* Pads data using the algorithm defined in PKCS #5/7.
|
||||
*
|
||||
* @param {WordArray} data The data to pad.
|
||||
* @param {number} blockSize The multiple that the data should be padded to.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* CryptoJS.pad.Pkcs7.pad(wordArray, 4);
|
||||
*/
|
||||
pad: function (data, blockSize) {
|
||||
// Shortcut
|
||||
var blockSizeBytes = blockSize * 4;
|
||||
|
||||
// Count padding bytes
|
||||
var nPaddingBytes = blockSizeBytes - data.sigBytes % blockSizeBytes;
|
||||
|
||||
// Create padding word
|
||||
var paddingWord = (nPaddingBytes << 24) | (nPaddingBytes << 16) | (nPaddingBytes << 8) | nPaddingBytes;
|
||||
|
||||
// Create padding
|
||||
var paddingWords = [];
|
||||
for (var i = 0; i < nPaddingBytes; i += 4) {
|
||||
paddingWords.push(paddingWord);
|
||||
}
|
||||
var padding = WordArray.create(paddingWords, nPaddingBytes);
|
||||
|
||||
// Add padding
|
||||
data.concat(padding);
|
||||
},
|
||||
|
||||
/**
|
||||
* Unpads data that had been padded using the algorithm defined in PKCS #5/7.
|
||||
*
|
||||
* @param {WordArray} data The data to unpad.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* CryptoJS.pad.Pkcs7.unpad(wordArray);
|
||||
*/
|
||||
unpad: function (data) {
|
||||
// Get number of padding bytes from last byte
|
||||
var nPaddingBytes = data.words[(data.sigBytes - 1) >>> 2] & 0xff;
|
||||
|
||||
// Remove padding
|
||||
data.sigBytes -= nPaddingBytes;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Abstract base block cipher template.
|
||||
*
|
||||
* @property {number} blockSize The number of 32-bit words this cipher operates on. Default: 4 (128 bits)
|
||||
*/
|
||||
var BlockCipher = C_lib.BlockCipher = Cipher.extend({
|
||||
/**
|
||||
* Configuration options.
|
||||
*
|
||||
* @property {Mode} mode The block mode to use. Default: CBC
|
||||
* @property {Padding} padding The padding strategy to use. Default: Pkcs7
|
||||
*/
|
||||
cfg: Cipher.cfg.extend({
|
||||
mode: CBC,
|
||||
padding: Pkcs7
|
||||
}),
|
||||
|
||||
reset: function () {
|
||||
var modeCreator;
|
||||
|
||||
// Reset cipher
|
||||
Cipher.reset.call(this);
|
||||
|
||||
// Shortcuts
|
||||
var cfg = this.cfg;
|
||||
var iv = cfg.iv;
|
||||
var mode = cfg.mode;
|
||||
|
||||
// Reset block mode
|
||||
if (this._xformMode == this._ENC_XFORM_MODE) {
|
||||
modeCreator = mode.createEncryptor;
|
||||
} else /* if (this._xformMode == this._DEC_XFORM_MODE) */ {
|
||||
modeCreator = mode.createDecryptor;
|
||||
// Keep at least one block in the buffer for unpadding
|
||||
this._minBufferSize = 1;
|
||||
}
|
||||
|
||||
if (this._mode && this._mode.__creator == modeCreator) {
|
||||
this._mode.init(this, iv && iv.words);
|
||||
} else {
|
||||
this._mode = modeCreator.call(mode, this, iv && iv.words);
|
||||
this._mode.__creator = modeCreator;
|
||||
}
|
||||
},
|
||||
|
||||
_doProcessBlock: function (words, offset) {
|
||||
this._mode.processBlock(words, offset);
|
||||
},
|
||||
|
||||
_doFinalize: function () {
|
||||
var finalProcessedBlocks;
|
||||
|
||||
// Shortcut
|
||||
var padding = this.cfg.padding;
|
||||
|
||||
// Finalize
|
||||
if (this._xformMode == this._ENC_XFORM_MODE) {
|
||||
// Pad data
|
||||
padding.pad(this._data, this.blockSize);
|
||||
|
||||
// Process final blocks
|
||||
finalProcessedBlocks = this._process(!!'flush');
|
||||
} else /* if (this._xformMode == this._DEC_XFORM_MODE) */ {
|
||||
// Process final blocks
|
||||
finalProcessedBlocks = this._process(!!'flush');
|
||||
|
||||
// Unpad data
|
||||
padding.unpad(finalProcessedBlocks);
|
||||
}
|
||||
|
||||
return finalProcessedBlocks;
|
||||
},
|
||||
|
||||
blockSize: 128/32
|
||||
});
|
||||
|
||||
/**
|
||||
* A collection of cipher parameters.
|
||||
*
|
||||
* @property {WordArray} ciphertext The raw ciphertext.
|
||||
* @property {WordArray} key The key to this ciphertext.
|
||||
* @property {WordArray} iv The IV used in the ciphering operation.
|
||||
* @property {WordArray} salt The salt used with a key derivation function.
|
||||
* @property {Cipher} algorithm The cipher algorithm.
|
||||
* @property {Mode} mode The block mode used in the ciphering operation.
|
||||
* @property {Padding} padding The padding scheme used in the ciphering operation.
|
||||
* @property {number} blockSize The block size of the cipher.
|
||||
* @property {Format} formatter The default formatting strategy to convert this cipher params object to a string.
|
||||
*/
|
||||
var CipherParams = C_lib.CipherParams = Base.extend({
|
||||
/**
|
||||
* Initializes a newly created cipher params object.
|
||||
*
|
||||
* @param {Object} cipherParams An object with any of the possible cipher parameters.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var cipherParams = CryptoJS.lib.CipherParams.create({
|
||||
* ciphertext: ciphertextWordArray,
|
||||
* key: keyWordArray,
|
||||
* iv: ivWordArray,
|
||||
* salt: saltWordArray,
|
||||
* algorithm: CryptoJS.algo.AES,
|
||||
* mode: CryptoJS.mode.CBC,
|
||||
* padding: CryptoJS.pad.PKCS7,
|
||||
* blockSize: 4,
|
||||
* formatter: CryptoJS.format.OpenSSL
|
||||
* });
|
||||
*/
|
||||
init: function (cipherParams) {
|
||||
this.mixIn(cipherParams);
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts this cipher params object to a string.
|
||||
*
|
||||
* @param {Format} formatter (Optional) The formatting strategy to use.
|
||||
*
|
||||
* @return {string} The stringified cipher params.
|
||||
*
|
||||
* @throws Error If neither the formatter nor the default formatter is set.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var string = cipherParams + '';
|
||||
* var string = cipherParams.toString();
|
||||
* var string = cipherParams.toString(CryptoJS.format.OpenSSL);
|
||||
*/
|
||||
toString: function (formatter) {
|
||||
return (formatter || this.formatter).stringify(this);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Format namespace.
|
||||
*/
|
||||
var C_format = C.format = {};
|
||||
|
||||
/**
|
||||
* OpenSSL formatting strategy.
|
||||
*/
|
||||
var OpenSSLFormatter = C_format.OpenSSL = {
|
||||
/**
|
||||
* Converts a cipher params object to an OpenSSL-compatible string.
|
||||
*
|
||||
* @param {CipherParams} cipherParams The cipher params object.
|
||||
*
|
||||
* @return {string} The OpenSSL-compatible string.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var openSSLString = CryptoJS.format.OpenSSL.stringify(cipherParams);
|
||||
*/
|
||||
stringify: function (cipherParams) {
|
||||
var wordArray;
|
||||
|
||||
// Shortcuts
|
||||
var ciphertext = cipherParams.ciphertext;
|
||||
var salt = cipherParams.salt;
|
||||
|
||||
// Format
|
||||
if (salt) {
|
||||
wordArray = WordArray.create([0x53616c74, 0x65645f5f]).concat(salt).concat(ciphertext);
|
||||
} else {
|
||||
wordArray = ciphertext;
|
||||
}
|
||||
|
||||
return wordArray.toString(Base64);
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts an OpenSSL-compatible string to a cipher params object.
|
||||
*
|
||||
* @param {string} openSSLStr The OpenSSL-compatible string.
|
||||
*
|
||||
* @return {CipherParams} The cipher params object.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var cipherParams = CryptoJS.format.OpenSSL.parse(openSSLString);
|
||||
*/
|
||||
parse: function (openSSLStr) {
|
||||
var salt;
|
||||
|
||||
// Parse base64
|
||||
var ciphertext = Base64.parse(openSSLStr);
|
||||
|
||||
// Shortcut
|
||||
var ciphertextWords = ciphertext.words;
|
||||
|
||||
// Test for salt
|
||||
if (ciphertextWords[0] == 0x53616c74 && ciphertextWords[1] == 0x65645f5f) {
|
||||
// Extract salt
|
||||
salt = WordArray.create(ciphertextWords.slice(2, 4));
|
||||
|
||||
// Remove salt from ciphertext
|
||||
ciphertextWords.splice(0, 4);
|
||||
ciphertext.sigBytes -= 16;
|
||||
}
|
||||
|
||||
return CipherParams.create({ ciphertext: ciphertext, salt: salt });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A cipher wrapper that returns ciphertext as a serializable cipher params object.
|
||||
*/
|
||||
var SerializableCipher = C_lib.SerializableCipher = Base.extend({
|
||||
/**
|
||||
* Configuration options.
|
||||
*
|
||||
* @property {Formatter} format The formatting strategy to convert cipher param objects to and from a string. Default: OpenSSL
|
||||
*/
|
||||
cfg: Base.extend({
|
||||
format: OpenSSLFormatter
|
||||
}),
|
||||
|
||||
/**
|
||||
* Encrypts a message.
|
||||
*
|
||||
* @param {Cipher} cipher The cipher algorithm to use.
|
||||
* @param {WordArray|string} message The message to encrypt.
|
||||
* @param {WordArray} key The key.
|
||||
* @param {Object} cfg (Optional) The configuration options to use for this operation.
|
||||
*
|
||||
* @return {CipherParams} A cipher params object.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key);
|
||||
* var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key, { iv: iv });
|
||||
* var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key, { iv: iv, format: CryptoJS.format.OpenSSL });
|
||||
*/
|
||||
encrypt: function (cipher, message, key, cfg) {
|
||||
// Apply config defaults
|
||||
cfg = this.cfg.extend(cfg);
|
||||
|
||||
// Encrypt
|
||||
var encryptor = cipher.createEncryptor(key, cfg);
|
||||
var ciphertext = encryptor.finalize(message);
|
||||
|
||||
// Shortcut
|
||||
var cipherCfg = encryptor.cfg;
|
||||
|
||||
// Create and return serializable cipher params
|
||||
return CipherParams.create({
|
||||
ciphertext: ciphertext,
|
||||
key: key,
|
||||
iv: cipherCfg.iv,
|
||||
algorithm: cipher,
|
||||
mode: cipherCfg.mode,
|
||||
padding: cipherCfg.padding,
|
||||
blockSize: cipher.blockSize,
|
||||
formatter: cfg.format
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Decrypts serialized ciphertext.
|
||||
*
|
||||
* @param {Cipher} cipher The cipher algorithm to use.
|
||||
* @param {CipherParams|string} ciphertext The ciphertext to decrypt.
|
||||
* @param {WordArray} key The key.
|
||||
* @param {Object} cfg (Optional) The configuration options to use for this operation.
|
||||
*
|
||||
* @return {WordArray} The plaintext.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var plaintext = CryptoJS.lib.SerializableCipher.decrypt(CryptoJS.algo.AES, formattedCiphertext, key, { iv: iv, format: CryptoJS.format.OpenSSL });
|
||||
* var plaintext = CryptoJS.lib.SerializableCipher.decrypt(CryptoJS.algo.AES, ciphertextParams, key, { iv: iv, format: CryptoJS.format.OpenSSL });
|
||||
*/
|
||||
decrypt: function (cipher, ciphertext, key, cfg) {
|
||||
// Apply config defaults
|
||||
cfg = this.cfg.extend(cfg);
|
||||
|
||||
// Convert string to CipherParams
|
||||
ciphertext = this._parse(ciphertext, cfg.format);
|
||||
|
||||
// Decrypt
|
||||
var plaintext = cipher.createDecryptor(key, cfg).finalize(ciphertext.ciphertext);
|
||||
|
||||
return plaintext;
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts serialized ciphertext to CipherParams,
|
||||
* else assumed CipherParams already and returns ciphertext unchanged.
|
||||
*
|
||||
* @param {CipherParams|string} ciphertext The ciphertext.
|
||||
* @param {Formatter} format The formatting strategy to use to parse serialized ciphertext.
|
||||
*
|
||||
* @return {CipherParams} The unserialized ciphertext.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var ciphertextParams = CryptoJS.lib.SerializableCipher._parse(ciphertextStringOrParams, format);
|
||||
*/
|
||||
_parse: function (ciphertext, format) {
|
||||
if (typeof ciphertext == 'string') {
|
||||
return format.parse(ciphertext, this);
|
||||
} else {
|
||||
return ciphertext;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Key derivation function namespace.
|
||||
*/
|
||||
var C_kdf = C.kdf = {};
|
||||
|
||||
/**
|
||||
* OpenSSL key derivation function.
|
||||
*/
|
||||
var OpenSSLKdf = C_kdf.OpenSSL = {
|
||||
/**
|
||||
* Derives a key and IV from a password.
|
||||
*
|
||||
* @param {string} password The password to derive from.
|
||||
* @param {number} keySize The size in words of the key to generate.
|
||||
* @param {number} ivSize The size in words of the IV to generate.
|
||||
* @param {WordArray|string} salt (Optional) A 64-bit salt to use. If omitted, a salt will be generated randomly.
|
||||
*
|
||||
* @return {CipherParams} A cipher params object with the key, IV, and salt.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32);
|
||||
* var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32, 'saltsalt');
|
||||
*/
|
||||
execute: function (password, keySize, ivSize, salt) {
|
||||
// Generate random salt
|
||||
if (!salt) {
|
||||
salt = WordArray.random(64/8);
|
||||
}
|
||||
|
||||
// Derive key and IV
|
||||
var key = EvpKDF.create({ keySize: keySize + ivSize }).compute(password, salt);
|
||||
|
||||
// Separate key and IV
|
||||
var iv = WordArray.create(key.words.slice(keySize), ivSize * 4);
|
||||
key.sigBytes = keySize * 4;
|
||||
|
||||
// Return params
|
||||
return CipherParams.create({ key: key, iv: iv, salt: salt });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A serializable cipher wrapper that derives the key from a password,
|
||||
* and returns ciphertext as a serializable cipher params object.
|
||||
*/
|
||||
var PasswordBasedCipher = C_lib.PasswordBasedCipher = SerializableCipher.extend({
|
||||
/**
|
||||
* Configuration options.
|
||||
*
|
||||
* @property {KDF} kdf The key derivation function to use to generate a key and IV from a password. Default: OpenSSL
|
||||
*/
|
||||
cfg: SerializableCipher.cfg.extend({
|
||||
kdf: OpenSSLKdf
|
||||
}),
|
||||
|
||||
/**
|
||||
* Encrypts a message using a password.
|
||||
*
|
||||
* @param {Cipher} cipher The cipher algorithm to use.
|
||||
* @param {WordArray|string} message The message to encrypt.
|
||||
* @param {string} password The password.
|
||||
* @param {Object} cfg (Optional) The configuration options to use for this operation.
|
||||
*
|
||||
* @return {CipherParams} A cipher params object.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var ciphertextParams = CryptoJS.lib.PasswordBasedCipher.encrypt(CryptoJS.algo.AES, message, 'password');
|
||||
* var ciphertextParams = CryptoJS.lib.PasswordBasedCipher.encrypt(CryptoJS.algo.AES, message, 'password', { format: CryptoJS.format.OpenSSL });
|
||||
*/
|
||||
encrypt: function (cipher, message, password, cfg) {
|
||||
// Apply config defaults
|
||||
cfg = this.cfg.extend(cfg);
|
||||
|
||||
// Derive key and other params
|
||||
var derivedParams = cfg.kdf.execute(password, cipher.keySize, cipher.ivSize);
|
||||
|
||||
// Add IV to config
|
||||
cfg.iv = derivedParams.iv;
|
||||
|
||||
// Encrypt
|
||||
var ciphertext = SerializableCipher.encrypt.call(this, cipher, message, derivedParams.key, cfg);
|
||||
|
||||
// Mix in derived params
|
||||
ciphertext.mixIn(derivedParams);
|
||||
|
||||
return ciphertext;
|
||||
},
|
||||
|
||||
/**
|
||||
* Decrypts serialized ciphertext using a password.
|
||||
*
|
||||
* @param {Cipher} cipher The cipher algorithm to use.
|
||||
* @param {CipherParams|string} ciphertext The ciphertext to decrypt.
|
||||
* @param {string} password The password.
|
||||
* @param {Object} cfg (Optional) The configuration options to use for this operation.
|
||||
*
|
||||
* @return {WordArray} The plaintext.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var plaintext = CryptoJS.lib.PasswordBasedCipher.decrypt(CryptoJS.algo.AES, formattedCiphertext, 'password', { format: CryptoJS.format.OpenSSL });
|
||||
* var plaintext = CryptoJS.lib.PasswordBasedCipher.decrypt(CryptoJS.algo.AES, ciphertextParams, 'password', { format: CryptoJS.format.OpenSSL });
|
||||
*/
|
||||
decrypt: function (cipher, ciphertext, password, cfg) {
|
||||
// Apply config defaults
|
||||
cfg = this.cfg.extend(cfg);
|
||||
|
||||
// Convert string to CipherParams
|
||||
ciphertext = this._parse(ciphertext, cfg.format);
|
||||
|
||||
// Derive key and other params
|
||||
var derivedParams = cfg.kdf.execute(password, cipher.keySize, cipher.ivSize, ciphertext.salt);
|
||||
|
||||
// Add IV to config
|
||||
cfg.iv = derivedParams.iv;
|
||||
|
||||
// Decrypt
|
||||
var plaintext = SerializableCipher.decrypt.call(this, cipher, ciphertext, derivedParams.key, cfg);
|
||||
|
||||
return plaintext;
|
||||
}
|
||||
});
|
||||
}());
|
||||
|
||||
|
||||
}));
|
@ -0,0 +1,807 @@
|
||||
;(function (root, factory) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory();
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define([], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
root.CryptoJS = factory();
|
||||
}
|
||||
}(this, function () {
|
||||
|
||||
/*globals window, global, require*/
|
||||
|
||||
/**
|
||||
* CryptoJS core components.
|
||||
*/
|
||||
var CryptoJS = CryptoJS || (function (Math, undefined) {
|
||||
|
||||
var crypto;
|
||||
|
||||
// Native crypto from window (Browser)
|
||||
if (typeof window !== 'undefined' && window.crypto) {
|
||||
crypto = window.crypto;
|
||||
}
|
||||
|
||||
// Native crypto in web worker (Browser)
|
||||
if (typeof self !== 'undefined' && self.crypto) {
|
||||
crypto = self.crypto;
|
||||
}
|
||||
|
||||
// Native crypto from worker
|
||||
if (typeof globalThis !== 'undefined' && globalThis.crypto) {
|
||||
crypto = globalThis.crypto;
|
||||
}
|
||||
|
||||
// Native (experimental IE 11) crypto from window (Browser)
|
||||
if (!crypto && typeof window !== 'undefined' && window.msCrypto) {
|
||||
crypto = window.msCrypto;
|
||||
}
|
||||
|
||||
// Native crypto from global (NodeJS)
|
||||
if (!crypto && typeof global !== 'undefined' && global.crypto) {
|
||||
crypto = global.crypto;
|
||||
}
|
||||
|
||||
// Native crypto import via require (NodeJS)
|
||||
if (!crypto && typeof require === 'function') {
|
||||
try {
|
||||
crypto = require('crypto');
|
||||
} catch (err) {}
|
||||
}
|
||||
|
||||
/*
|
||||
* Cryptographically secure pseudorandom number generator
|
||||
*
|
||||
* As Math.random() is cryptographically not safe to use
|
||||
*/
|
||||
var cryptoSecureRandomInt = function () {
|
||||
if (crypto) {
|
||||
// Use getRandomValues method (Browser)
|
||||
if (typeof crypto.getRandomValues === 'function') {
|
||||
try {
|
||||
return crypto.getRandomValues(new Uint32Array(1))[0];
|
||||
} catch (err) {}
|
||||
}
|
||||
|
||||
// Use randomBytes method (NodeJS)
|
||||
if (typeof crypto.randomBytes === 'function') {
|
||||
try {
|
||||
return crypto.randomBytes(4).readInt32LE();
|
||||
} catch (err) {}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('Native crypto module could not be used to get secure random number.');
|
||||
};
|
||||
|
||||
/*
|
||||
* Local polyfill of Object.create
|
||||
|
||||
*/
|
||||
var create = Object.create || (function () {
|
||||
function F() {}
|
||||
|
||||
return function (obj) {
|
||||
var subtype;
|
||||
|
||||
F.prototype = obj;
|
||||
|
||||
subtype = new F();
|
||||
|
||||
F.prototype = null;
|
||||
|
||||
return subtype;
|
||||
};
|
||||
}());
|
||||
|
||||
/**
|
||||
* CryptoJS namespace.
|
||||
*/
|
||||
var C = {};
|
||||
|
||||
/**
|
||||
* Library namespace.
|
||||
*/
|
||||
var C_lib = C.lib = {};
|
||||
|
||||
/**
|
||||
* Base object for prototypal inheritance.
|
||||
*/
|
||||
var Base = C_lib.Base = (function () {
|
||||
|
||||
|
||||
return {
|
||||
/**
|
||||
* Creates a new object that inherits from this object.
|
||||
*
|
||||
* @param {Object} overrides Properties to copy into the new object.
|
||||
*
|
||||
* @return {Object} The new object.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var MyType = CryptoJS.lib.Base.extend({
|
||||
* field: 'value',
|
||||
*
|
||||
* method: function () {
|
||||
* }
|
||||
* });
|
||||
*/
|
||||
extend: function (overrides) {
|
||||
// Spawn
|
||||
var subtype = create(this);
|
||||
|
||||
// Augment
|
||||
if (overrides) {
|
||||
subtype.mixIn(overrides);
|
||||
}
|
||||
|
||||
// Create default initializer
|
||||
if (!subtype.hasOwnProperty('init') || this.init === subtype.init) {
|
||||
subtype.init = function () {
|
||||
subtype.$super.init.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
// Initializer's prototype is the subtype object
|
||||
subtype.init.prototype = subtype;
|
||||
|
||||
// Reference supertype
|
||||
subtype.$super = this;
|
||||
|
||||
return subtype;
|
||||
},
|
||||
|
||||
/**
|
||||
* Extends this object and runs the init method.
|
||||
* Arguments to create() will be passed to init().
|
||||
*
|
||||
* @return {Object} The new object.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var instance = MyType.create();
|
||||
*/
|
||||
create: function () {
|
||||
var instance = this.extend();
|
||||
instance.init.apply(instance, arguments);
|
||||
|
||||
return instance;
|
||||
},
|
||||
|
||||
/**
|
||||
* Initializes a newly created object.
|
||||
* Override this method to add some logic when your objects are created.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var MyType = CryptoJS.lib.Base.extend({
|
||||
* init: function () {
|
||||
* // ...
|
||||
* }
|
||||
* });
|
||||
*/
|
||||
init: function () {
|
||||
},
|
||||
|
||||
/**
|
||||
* Copies properties into this object.
|
||||
*
|
||||
* @param {Object} properties The properties to mix in.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* MyType.mixIn({
|
||||
* field: 'value'
|
||||
* });
|
||||
*/
|
||||
mixIn: function (properties) {
|
||||
for (var propertyName in properties) {
|
||||
if (properties.hasOwnProperty(propertyName)) {
|
||||
this[propertyName] = properties[propertyName];
|
||||
}
|
||||
}
|
||||
|
||||
// IE won't copy toString using the loop above
|
||||
if (properties.hasOwnProperty('toString')) {
|
||||
this.toString = properties.toString;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a copy of this object.
|
||||
*
|
||||
* @return {Object} The clone.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var clone = instance.clone();
|
||||
*/
|
||||
clone: function () {
|
||||
return this.init.prototype.extend(this);
|
||||
}
|
||||
};
|
||||
}());
|
||||
|
||||
/**
|
||||
* An array of 32-bit words.
|
||||
*
|
||||
* @property {Array} words The array of 32-bit words.
|
||||
* @property {number} sigBytes The number of significant bytes in this word array.
|
||||
*/
|
||||
var WordArray = C_lib.WordArray = Base.extend({
|
||||
/**
|
||||
* Initializes a newly created word array.
|
||||
*
|
||||
* @param {Array} words (Optional) An array of 32-bit words.
|
||||
* @param {number} sigBytes (Optional) The number of significant bytes in the words.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var wordArray = CryptoJS.lib.WordArray.create();
|
||||
* var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607]);
|
||||
* var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607], 6);
|
||||
*/
|
||||
init: function (words, sigBytes) {
|
||||
words = this.words = words || [];
|
||||
|
||||
if (sigBytes != undefined) {
|
||||
this.sigBytes = sigBytes;
|
||||
} else {
|
||||
this.sigBytes = words.length * 4;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts this word array to a string.
|
||||
*
|
||||
* @param {Encoder} encoder (Optional) The encoding strategy to use. Default: CryptoJS.enc.Hex
|
||||
*
|
||||
* @return {string} The stringified word array.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var string = wordArray + '';
|
||||
* var string = wordArray.toString();
|
||||
* var string = wordArray.toString(CryptoJS.enc.Utf8);
|
||||
*/
|
||||
toString: function (encoder) {
|
||||
return (encoder || Hex).stringify(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Concatenates a word array to this word array.
|
||||
*
|
||||
* @param {WordArray} wordArray The word array to append.
|
||||
*
|
||||
* @return {WordArray} This word array.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* wordArray1.concat(wordArray2);
|
||||
*/
|
||||
concat: function (wordArray) {
|
||||
// Shortcuts
|
||||
var thisWords = this.words;
|
||||
var thatWords = wordArray.words;
|
||||
var thisSigBytes = this.sigBytes;
|
||||
var thatSigBytes = wordArray.sigBytes;
|
||||
|
||||
// Clamp excess bits
|
||||
this.clamp();
|
||||
|
||||
// Concat
|
||||
if (thisSigBytes % 4) {
|
||||
// Copy one byte at a time
|
||||
for (var i = 0; i < thatSigBytes; i++) {
|
||||
var thatByte = (thatWords[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
|
||||
thisWords[(thisSigBytes + i) >>> 2] |= thatByte << (24 - ((thisSigBytes + i) % 4) * 8);
|
||||
}
|
||||
} else {
|
||||
// Copy one word at a time
|
||||
for (var j = 0; j < thatSigBytes; j += 4) {
|
||||
thisWords[(thisSigBytes + j) >>> 2] = thatWords[j >>> 2];
|
||||
}
|
||||
}
|
||||
this.sigBytes += thatSigBytes;
|
||||
|
||||
// Chainable
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes insignificant bits.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* wordArray.clamp();
|
||||
*/
|
||||
clamp: function () {
|
||||
// Shortcuts
|
||||
var words = this.words;
|
||||
var sigBytes = this.sigBytes;
|
||||
|
||||
// Clamp
|
||||
words[sigBytes >>> 2] &= 0xffffffff << (32 - (sigBytes % 4) * 8);
|
||||
words.length = Math.ceil(sigBytes / 4);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a copy of this word array.
|
||||
*
|
||||
* @return {WordArray} The clone.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var clone = wordArray.clone();
|
||||
*/
|
||||
clone: function () {
|
||||
var clone = Base.clone.call(this);
|
||||
clone.words = this.words.slice(0);
|
||||
|
||||
return clone;
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a word array filled with random bytes.
|
||||
*
|
||||
* @param {number} nBytes The number of random bytes to generate.
|
||||
*
|
||||
* @return {WordArray} The random word array.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var wordArray = CryptoJS.lib.WordArray.random(16);
|
||||
*/
|
||||
random: function (nBytes) {
|
||||
var words = [];
|
||||
|
||||
for (var i = 0; i < nBytes; i += 4) {
|
||||
words.push(cryptoSecureRandomInt());
|
||||
}
|
||||
|
||||
return new WordArray.init(words, nBytes);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Encoder namespace.
|
||||
*/
|
||||
var C_enc = C.enc = {};
|
||||
|
||||
/**
|
||||
* Hex encoding strategy.
|
||||
*/
|
||||
var Hex = C_enc.Hex = {
|
||||
/**
|
||||
* Converts a word array to a hex string.
|
||||
*
|
||||
* @param {WordArray} wordArray The word array.
|
||||
*
|
||||
* @return {string} The hex string.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var hexString = CryptoJS.enc.Hex.stringify(wordArray);
|
||||
*/
|
||||
stringify: function (wordArray) {
|
||||
// Shortcuts
|
||||
var words = wordArray.words;
|
||||
var sigBytes = wordArray.sigBytes;
|
||||
|
||||
// Convert
|
||||
var hexChars = [];
|
||||
for (var i = 0; i < sigBytes; i++) {
|
||||
var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
|
||||
hexChars.push((bite >>> 4).toString(16));
|
||||
hexChars.push((bite & 0x0f).toString(16));
|
||||
}
|
||||
|
||||
return hexChars.join('');
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts a hex string to a word array.
|
||||
*
|
||||
* @param {string} hexStr The hex string.
|
||||
*
|
||||
* @return {WordArray} The word array.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var wordArray = CryptoJS.enc.Hex.parse(hexString);
|
||||
*/
|
||||
parse: function (hexStr) {
|
||||
// Shortcut
|
||||
var hexStrLength = hexStr.length;
|
||||
|
||||
// Convert
|
||||
var words = [];
|
||||
for (var i = 0; i < hexStrLength; i += 2) {
|
||||
words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4);
|
||||
}
|
||||
|
||||
return new WordArray.init(words, hexStrLength / 2);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Latin1 encoding strategy.
|
||||
*/
|
||||
var Latin1 = C_enc.Latin1 = {
|
||||
/**
|
||||
* Converts a word array to a Latin1 string.
|
||||
*
|
||||
* @param {WordArray} wordArray The word array.
|
||||
*
|
||||
* @return {string} The Latin1 string.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var latin1String = CryptoJS.enc.Latin1.stringify(wordArray);
|
||||
*/
|
||||
stringify: function (wordArray) {
|
||||
// Shortcuts
|
||||
var words = wordArray.words;
|
||||
var sigBytes = wordArray.sigBytes;
|
||||
|
||||
// Convert
|
||||
var latin1Chars = [];
|
||||
for (var i = 0; i < sigBytes; i++) {
|
||||
var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
|
||||
latin1Chars.push(String.fromCharCode(bite));
|
||||
}
|
||||
|
||||
return latin1Chars.join('');
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts a Latin1 string to a word array.
|
||||
*
|
||||
* @param {string} latin1Str The Latin1 string.
|
||||
*
|
||||
* @return {WordArray} The word array.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var wordArray = CryptoJS.enc.Latin1.parse(latin1String);
|
||||
*/
|
||||
parse: function (latin1Str) {
|
||||
// Shortcut
|
||||
var latin1StrLength = latin1Str.length;
|
||||
|
||||
// Convert
|
||||
var words = [];
|
||||
for (var i = 0; i < latin1StrLength; i++) {
|
||||
words[i >>> 2] |= (latin1Str.charCodeAt(i) & 0xff) << (24 - (i % 4) * 8);
|
||||
}
|
||||
|
||||
return new WordArray.init(words, latin1StrLength);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* UTF-8 encoding strategy.
|
||||
*/
|
||||
var Utf8 = C_enc.Utf8 = {
|
||||
/**
|
||||
* Converts a word array to a UTF-8 string.
|
||||
*
|
||||
* @param {WordArray} wordArray The word array.
|
||||
*
|
||||
* @return {string} The UTF-8 string.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var utf8String = CryptoJS.enc.Utf8.stringify(wordArray);
|
||||
*/
|
||||
stringify: function (wordArray) {
|
||||
try {
|
||||
return decodeURIComponent(escape(Latin1.stringify(wordArray)));
|
||||
} catch (e) {
|
||||
throw new Error('Malformed UTF-8 data');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts a UTF-8 string to a word array.
|
||||
*
|
||||
* @param {string} utf8Str The UTF-8 string.
|
||||
*
|
||||
* @return {WordArray} The word array.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var wordArray = CryptoJS.enc.Utf8.parse(utf8String);
|
||||
*/
|
||||
parse: function (utf8Str) {
|
||||
return Latin1.parse(unescape(encodeURIComponent(utf8Str)));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Abstract buffered block algorithm template.
|
||||
*
|
||||
* The property blockSize must be implemented in a concrete subtype.
|
||||
*
|
||||
* @property {number} _minBufferSize The number of blocks that should be kept unprocessed in the buffer. Default: 0
|
||||
*/
|
||||
var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm = Base.extend({
|
||||
/**
|
||||
* Resets this block algorithm's data buffer to its initial state.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* bufferedBlockAlgorithm.reset();
|
||||
*/
|
||||
reset: function () {
|
||||
// Initial values
|
||||
this._data = new WordArray.init();
|
||||
this._nDataBytes = 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds new data to this block algorithm's buffer.
|
||||
*
|
||||
* @param {WordArray|string} data The data to append. Strings are converted to a WordArray using UTF-8.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* bufferedBlockAlgorithm._append('data');
|
||||
* bufferedBlockAlgorithm._append(wordArray);
|
||||
*/
|
||||
_append: function (data) {
|
||||
// Convert string to WordArray, else assume WordArray already
|
||||
if (typeof data == 'string') {
|
||||
data = Utf8.parse(data);
|
||||
}
|
||||
|
||||
// Append
|
||||
this._data.concat(data);
|
||||
this._nDataBytes += data.sigBytes;
|
||||
},
|
||||
|
||||
/**
|
||||
* Processes available data blocks.
|
||||
*
|
||||
* This method invokes _doProcessBlock(offset), which must be implemented by a concrete subtype.
|
||||
*
|
||||
* @param {boolean} doFlush Whether all blocks and partial blocks should be processed.
|
||||
*
|
||||
* @return {WordArray} The processed data.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var processedData = bufferedBlockAlgorithm._process();
|
||||
* var processedData = bufferedBlockAlgorithm._process(!!'flush');
|
||||
*/
|
||||
_process: function (doFlush) {
|
||||
var processedWords;
|
||||
|
||||
// Shortcuts
|
||||
var data = this._data;
|
||||
var dataWords = data.words;
|
||||
var dataSigBytes = data.sigBytes;
|
||||
var blockSize = this.blockSize;
|
||||
var blockSizeBytes = blockSize * 4;
|
||||
|
||||
// Count blocks ready
|
||||
var nBlocksReady = dataSigBytes / blockSizeBytes;
|
||||
if (doFlush) {
|
||||
// Round up to include partial blocks
|
||||
nBlocksReady = Math.ceil(nBlocksReady);
|
||||
} else {
|
||||
// Round down to include only full blocks,
|
||||
// less the number of blocks that must remain in the buffer
|
||||
nBlocksReady = Math.max((nBlocksReady | 0) - this._minBufferSize, 0);
|
||||
}
|
||||
|
||||
// Count words ready
|
||||
var nWordsReady = nBlocksReady * blockSize;
|
||||
|
||||
// Count bytes ready
|
||||
var nBytesReady = Math.min(nWordsReady * 4, dataSigBytes);
|
||||
|
||||
// Process blocks
|
||||
if (nWordsReady) {
|
||||
for (var offset = 0; offset < nWordsReady; offset += blockSize) {
|
||||
// Perform concrete-algorithm logic
|
||||
this._doProcessBlock(dataWords, offset);
|
||||
}
|
||||
|
||||
// Remove processed words
|
||||
processedWords = dataWords.splice(0, nWordsReady);
|
||||
data.sigBytes -= nBytesReady;
|
||||
}
|
||||
|
||||
// Return processed words
|
||||
return new WordArray.init(processedWords, nBytesReady);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a copy of this object.
|
||||
*
|
||||
* @return {Object} The clone.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var clone = bufferedBlockAlgorithm.clone();
|
||||
*/
|
||||
clone: function () {
|
||||
var clone = Base.clone.call(this);
|
||||
clone._data = this._data.clone();
|
||||
|
||||
return clone;
|
||||
},
|
||||
|
||||
_minBufferSize: 0
|
||||
});
|
||||
|
||||
/**
|
||||
* Abstract hasher template.
|
||||
*
|
||||
* @property {number} blockSize The number of 32-bit words this hasher operates on. Default: 16 (512 bits)
|
||||
*/
|
||||
var Hasher = C_lib.Hasher = BufferedBlockAlgorithm.extend({
|
||||
/**
|
||||
* Configuration options.
|
||||
*/
|
||||
cfg: Base.extend(),
|
||||
|
||||
/**
|
||||
* Initializes a newly created hasher.
|
||||
*
|
||||
* @param {Object} cfg (Optional) The configuration options to use for this hash computation.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var hasher = CryptoJS.algo.SHA256.create();
|
||||
*/
|
||||
init: function (cfg) {
|
||||
// Apply config defaults
|
||||
this.cfg = this.cfg.extend(cfg);
|
||||
|
||||
// Set initial values
|
||||
this.reset();
|
||||
},
|
||||
|
||||
/**
|
||||
* Resets this hasher to its initial state.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* hasher.reset();
|
||||
*/
|
||||
reset: function () {
|
||||
// Reset data buffer
|
||||
BufferedBlockAlgorithm.reset.call(this);
|
||||
|
||||
// Perform concrete-hasher logic
|
||||
this._doReset();
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates this hasher with a message.
|
||||
*
|
||||
* @param {WordArray|string} messageUpdate The message to append.
|
||||
*
|
||||
* @return {Hasher} This hasher.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* hasher.update('message');
|
||||
* hasher.update(wordArray);
|
||||
*/
|
||||
update: function (messageUpdate) {
|
||||
// Append
|
||||
this._append(messageUpdate);
|
||||
|
||||
// Update the hash
|
||||
this._process();
|
||||
|
||||
// Chainable
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Finalizes the hash computation.
|
||||
* Note that the finalize operation is effectively a destructive, read-once operation.
|
||||
*
|
||||
* @param {WordArray|string} messageUpdate (Optional) A final message update.
|
||||
*
|
||||
* @return {WordArray} The hash.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var hash = hasher.finalize();
|
||||
* var hash = hasher.finalize('message');
|
||||
* var hash = hasher.finalize(wordArray);
|
||||
*/
|
||||
finalize: function (messageUpdate) {
|
||||
// Final message update
|
||||
if (messageUpdate) {
|
||||
this._append(messageUpdate);
|
||||
}
|
||||
|
||||
// Perform concrete-hasher logic
|
||||
var hash = this._doFinalize();
|
||||
|
||||
return hash;
|
||||
},
|
||||
|
||||
blockSize: 512/32,
|
||||
|
||||
/**
|
||||
* Creates a shortcut function to a hasher's object interface.
|
||||
*
|
||||
* @param {Hasher} hasher The hasher to create a helper for.
|
||||
*
|
||||
* @return {Function} The shortcut function.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var SHA256 = CryptoJS.lib.Hasher._createHelper(CryptoJS.algo.SHA256);
|
||||
*/
|
||||
_createHelper: function (hasher) {
|
||||
return function (message, cfg) {
|
||||
return new hasher.init(cfg).finalize(message);
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a shortcut function to the HMAC's object interface.
|
||||
*
|
||||
* @param {Hasher} hasher The hasher to use in this HMAC helper.
|
||||
*
|
||||
* @return {Function} The shortcut function.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var HmacSHA256 = CryptoJS.lib.Hasher._createHmacHelper(CryptoJS.algo.SHA256);
|
||||
*/
|
||||
_createHmacHelper: function (hasher) {
|
||||
return function (message, key) {
|
||||
return new C_algo.HMAC.init(hasher, key).finalize(message);
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Algorithm namespace.
|
||||
*/
|
||||
var C_algo = C.algo = {};
|
||||
|
||||
return C;
|
||||
}(Math));
|
||||
|
||||
|
||||
return CryptoJS;
|
||||
|
||||
}));
|
@ -0,0 +1,470 @@
|
||||
<wiki:toc/>
|
||||
|
||||
----
|
||||
|
||||
= Quick-start Guide =
|
||||
|
||||
== Hashers ==
|
||||
|
||||
=== The Hasher Algorithms ===
|
||||
|
||||
==== MD5 ====
|
||||
|
||||
MD5 is a widely used hash function. It's been used in a variety of security applications and is also commonly used to check the integrity of files. Though, MD5 is not collision resistant, and it isn't suitable for applications like SSL certificates or digital signatures that rely on this property.
|
||||
|
||||
{{{
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/rollups/md5.js"></script>
|
||||
<script>
|
||||
var hash = CryptoJS.MD5("Message");
|
||||
</script>
|
||||
}}}
|
||||
|
||||
==== SHA-1 ====
|
||||
|
||||
The SHA hash functions were designed by the National Security Agency (NSA). SHA-1 is the most established of the existing SHA hash functions, and it's used in a variety of security applications and protocols. Though, SHA-1's collision resistance has been weakening as new attacks are discovered or improved.
|
||||
|
||||
{{{
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/rollups/sha1.js"></script>
|
||||
<script>
|
||||
var hash = CryptoJS.SHA1("Message");
|
||||
</script>
|
||||
}}}
|
||||
|
||||
==== SHA-2 ====
|
||||
|
||||
SHA-256 is one of the four variants in the SHA-2 set. It isn't as widely used as SHA-1, though it appears to provide much better security.
|
||||
|
||||
{{{
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/rollups/sha256.js"></script>
|
||||
<script>
|
||||
var hash = CryptoJS.SHA256("Message");
|
||||
</script>
|
||||
}}}
|
||||
|
||||
SHA-512 is largely identical to SHA-256 but operates on 64-bit words rather than 32.
|
||||
|
||||
{{{
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/rollups/sha512.js"></script>
|
||||
<script>
|
||||
var hash = CryptoJS.SHA512("Message");
|
||||
</script>
|
||||
}}}
|
||||
|
||||
CryptoJS also supports SHA-224 and SHA-384, which are largely identical but truncated versions of SHA-256 and SHA-512 respectively.
|
||||
|
||||
==== SHA-3 ====
|
||||
|
||||
SHA-3 is the winner of a five-year competition to select a new cryptographic hash algorithm where 64 competing designs were evaluated.
|
||||
|
||||
{{{
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/rollups/sha3.js"></script>
|
||||
<script>
|
||||
var hash = CryptoJS.SHA3("Message");
|
||||
</script>
|
||||
}}}
|
||||
|
||||
SHA-3 can be configured to output hash lengths of one of 224, 256, 384, or 512 bits. The default is 512 bits.
|
||||
|
||||
{{{
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/rollups/sha3.js"></script>
|
||||
<script>
|
||||
var hash = CryptoJS.SHA3("Message", { outputLength: 512 });
|
||||
var hash = CryptoJS.SHA3("Message", { outputLength: 384 });
|
||||
var hash = CryptoJS.SHA3("Message", { outputLength: 256 });
|
||||
var hash = CryptoJS.SHA3("Message", { outputLength: 224 });
|
||||
</script>
|
||||
}}}
|
||||
|
||||
==== RIPEMD-160 ====
|
||||
|
||||
{{{
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/rollups/ripemd160.js"></script>
|
||||
<script>
|
||||
var hash = CryptoJS.RIPEMD160("Message");
|
||||
</script>
|
||||
}}}
|
||||
|
||||
=== The Hasher Input ===
|
||||
|
||||
The hash algorithms accept either strings or instances of CryptoJS.lib.WordArray. A WordArray object represents an array of 32-bit words. When you pass a string, it's automatically converted to a WordArray encoded as UTF-8.
|
||||
|
||||
=== The Hasher Output ===
|
||||
|
||||
The hash you get back isn't a string yet. It's a WordArray object. When you use a WordArray object in a string context, it's automatically converted to a hex string.
|
||||
|
||||
{{{
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/rollups/sha256.js"></script>
|
||||
<script>
|
||||
var hash = CryptoJS.SHA256("Message");
|
||||
|
||||
alert(typeof hash); // object
|
||||
|
||||
alert(hash); // 2f77668a9dfbf8d5848b9eeb4a7145ca94c6ed9236e4a773f6dcafa5132b2f91
|
||||
</script>
|
||||
}}}
|
||||
|
||||
You can convert a WordArray object to other formats by explicitly calling the toString method and passing an encoder.
|
||||
|
||||
{{{
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/rollups/sha256.js"></script>
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/components/enc-base64-min.js"></script>
|
||||
<script>
|
||||
var hash = CryptoJS.SHA256("Message");
|
||||
|
||||
alert(hash.toString(CryptoJS.enc.Base64)); // L3dmip37+NWEi57rSnFFypTG7ZI25Kdz9tyvpRMrL5E=
|
||||
|
||||
alert(hash.toString(CryptoJS.enc.Latin1)); // /wf<77><66>ûøÕ<C3B8><C395><EFBFBD>ëJqEÊ<45>Æí<C386>6ä§söܯ¥+/<2F>
|
||||
|
||||
alert(hash.toString(CryptoJS.enc.Hex)); // 2f77668a9dfbf8d5848b9eeb4a7145ca94c6ed9236e4a773f6dcafa5132b2f91
|
||||
</script>
|
||||
}}}
|
||||
|
||||
=== Progressive Hashing ===
|
||||
|
||||
{{{
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/rollups/sha256.js"></script>
|
||||
<script>
|
||||
var sha256 = CryptoJS.algo.SHA256.create();
|
||||
|
||||
sha256.update("Message Part 1");
|
||||
sha256.update("Message Part 2");
|
||||
sha256.update("Message Part 3");
|
||||
|
||||
var hash = sha256.finalize();
|
||||
</script>
|
||||
}}}
|
||||
|
||||
== HMAC ==
|
||||
|
||||
Keyed-hash message authentication codes (HMAC) is a mechanism for message authentication using cryptographic hash functions.
|
||||
|
||||
HMAC can be used in combination with any iterated cryptographic hash function.
|
||||
|
||||
{{{
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/rollups/hmac-md5.js"></script>
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/rollups/hmac-sha1.js"></script>
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/rollups/hmac-sha256.js"></script>
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/rollups/hmac-sha512.js"></script>
|
||||
<script>
|
||||
var hash = CryptoJS.HmacMD5("Message", "Secret Passphrase");
|
||||
var hash = CryptoJS.HmacSHA1("Message", "Secret Passphrase");
|
||||
var hash = CryptoJS.HmacSHA256("Message", "Secret Passphrase");
|
||||
var hash = CryptoJS.HmacSHA512("Message", "Secret Passphrase");
|
||||
</script>
|
||||
}}}
|
||||
|
||||
=== Progressive HMAC Hashing ===
|
||||
|
||||
{{{
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/rollups/hmac-sha256.js"></script>
|
||||
<script>
|
||||
var hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, "Secret Passphrase");
|
||||
|
||||
hmac.update("Message Part 1");
|
||||
hmac.update("Message Part 2");
|
||||
hmac.update("Message Part 3");
|
||||
|
||||
var hash = hmac.finalize();
|
||||
</script>
|
||||
}}}
|
||||
|
||||
== PBKDF2 ==
|
||||
|
||||
PBKDF2 is a password-based key derivation function. In many applications of cryptography, user security is ultimately dependent on a password, and because a password usually can't be used directly as a cryptographic key, some processing is required.
|
||||
|
||||
A salt provides a large set of keys for any given password, and an iteration count increases the cost of producing keys from a password, thereby also increasing the difficulty of attack.
|
||||
|
||||
{{{
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/rollups/pbkdf2.js"></script>
|
||||
<script>
|
||||
var salt = CryptoJS.lib.WordArray.random(128/8);
|
||||
|
||||
var key128Bits = CryptoJS.PBKDF2("Secret Passphrase", salt, { keySize: 128/32 });
|
||||
var key256Bits = CryptoJS.PBKDF2("Secret Passphrase", salt, { keySize: 256/32 });
|
||||
var key512Bits = CryptoJS.PBKDF2("Secret Passphrase", salt, { keySize: 512/32 });
|
||||
|
||||
var key512Bits1000Iterations = CryptoJS.PBKDF2("Secret Passphrase", salt, { keySize: 512/32, iterations: 1000 });
|
||||
</script>
|
||||
}}}
|
||||
|
||||
== Ciphers ==
|
||||
|
||||
=== The Cipher Algorithms ===
|
||||
|
||||
==== AES ====
|
||||
|
||||
The Advanced Encryption Standard (AES) is a U.S. Federal Information Processing Standard (FIPS). It was selected after a 5-year process where 15 competing designs were evaluated.
|
||||
|
||||
{{{
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/rollups/aes.js"></script>
|
||||
<script>
|
||||
var encrypted = CryptoJS.AES.encrypt("Message", "Secret Passphrase");
|
||||
|
||||
var decrypted = CryptoJS.AES.decrypt(encrypted, "Secret Passphrase");
|
||||
</script>
|
||||
}}}
|
||||
|
||||
CryptoJS supports AES-128, AES-192, and AES-256. It will pick the variant by the size of the key you pass in. If you use a passphrase, then it will generate a 256-bit key.
|
||||
|
||||
==== DES, Triple DES ====
|
||||
|
||||
DES is a previously dominant algorithm for encryption, and was published as an official Federal Information Processing Standard (FIPS). DES is now considered to be insecure due to the small key size.
|
||||
|
||||
{{{
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/rollups/tripledes.js"></script>
|
||||
<script>
|
||||
var encrypted = CryptoJS.DES.encrypt("Message", "Secret Passphrase");
|
||||
|
||||
var decrypted = CryptoJS.DES.decrypt(encrypted, "Secret Passphrase");
|
||||
</script>
|
||||
}}}
|
||||
|
||||
Triple DES applies DES three times to each block to increase the key size. The algorithm is believed to be secure in this form.
|
||||
|
||||
{{{
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/rollups/tripledes.js"></script>
|
||||
<script>
|
||||
var encrypted = CryptoJS.TripleDES.encrypt("Message", "Secret Passphrase");
|
||||
|
||||
var decrypted = CryptoJS.TripleDES.decrypt(encrypted, "Secret Passphrase");
|
||||
</script>
|
||||
}}}
|
||||
|
||||
==== Rabbit ====
|
||||
|
||||
Rabbit is a high-performance stream cipher and a finalist in the eSTREAM Portfolio. It is one of the four designs selected after a 3 1/2-year process where 22 designs were evaluated.
|
||||
|
||||
{{{
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/rollups/rabbit.js"></script>
|
||||
<script>
|
||||
var encrypted = CryptoJS.Rabbit.encrypt("Message", "Secret Passphrase");
|
||||
|
||||
var decrypted = CryptoJS.Rabbit.decrypt(encrypted, "Secret Passphrase");
|
||||
</script>
|
||||
}}}
|
||||
|
||||
==== RC4, RC4Drop ====
|
||||
|
||||
RC4 is a widely-used stream cipher. It's used in popular protocols such as SSL and WEP. Although remarkable for its simplicity and speed, the algorithm's history doesn't inspire confidence in its security.
|
||||
|
||||
{{{
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/rollups/rc4.js"></script>
|
||||
<script>
|
||||
var encrypted = CryptoJS.RC4.encrypt("Message", "Secret Passphrase");
|
||||
|
||||
var decrypted = CryptoJS.RC4.decrypt(encrypted, "Secret Passphrase");
|
||||
</script>
|
||||
}}}
|
||||
|
||||
It was discovered that the first few bytes of keystream are strongly non-random and leak information about the key. We can defend against this attack by discarding the initial portion of the keystream. This modified algorithm is traditionally called RC4-drop.
|
||||
|
||||
By default, 192 words (768 bytes) are dropped, but you can configure the algorithm to drop any number of words.
|
||||
|
||||
{{{
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/rollups/rc4.js"></script>
|
||||
<script>
|
||||
var encrypted = CryptoJS.RC4Drop.encrypt("Message", "Secret Passphrase");
|
||||
|
||||
var encrypted = CryptoJS.RC4Drop.encrypt("Message", "Secret Passphrase", { drop: 3072/4 });
|
||||
|
||||
var decrypted = CryptoJS.RC4Drop.decrypt(encrypted, "Secret Passphrase", { drop: 3072/4 });
|
||||
</script>
|
||||
}}}
|
||||
|
||||
=== Custom Key and IV ===
|
||||
|
||||
{{{
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/rollups/aes.js"></script>
|
||||
<script>
|
||||
var key = CryptoJS.enc.Hex.parse('000102030405060708090a0b0c0d0e0f');
|
||||
var iv = CryptoJS.enc.Hex.parse('101112131415161718191a1b1c1d1e1f');
|
||||
|
||||
var encrypted = CryptoJS.AES.encrypt("Message", key, { iv: iv });
|
||||
</script>
|
||||
}}}
|
||||
|
||||
=== Block Modes and Padding ===
|
||||
|
||||
{{{
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/rollups/aes.js"></script>
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/components/mode-cfb-min.js"></script>
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/components/pad-ansix923-min.js"></script>
|
||||
<script>
|
||||
var encrypted = CryptoJS.AES.encrypt("Message", "Secret Passphrase", { mode: CryptoJS.mode.CFB, padding: CryptoJS.pad.AnsiX923 });
|
||||
</script>
|
||||
}}}
|
||||
|
||||
CryptoJS supports the following modes:
|
||||
|
||||
* CBC (the default)
|
||||
* CFB
|
||||
* CTR
|
||||
* OFB
|
||||
* ECB
|
||||
|
||||
And CryptoJS supports the following padding schemes:
|
||||
|
||||
* Pkcs7 (the default)
|
||||
* Iso97971
|
||||
* AnsiX923
|
||||
* Iso10126
|
||||
* ZeroPadding
|
||||
* NoPadding
|
||||
|
||||
=== The Cipher Input ===
|
||||
|
||||
For the plaintext message, the cipher algorithms accept either strings or instances of CryptoJS.lib.WordArray.
|
||||
|
||||
For the key, when you pass a string, it's treated as a passphrase and used to derive an actual key and IV. Or you can pass a WordArray that represents the actual key. If you pass the actual key, you must also pass the actual IV.
|
||||
|
||||
For the ciphertext, the cipher algorithms accept either strings or instances of CryptoJS.lib.CipherParams. A CipherParams object represents a collection of parameters such as the IV, a salt, and the raw ciphertext itself. When you pass a string, it's automatically converted to a CipherParams object according to a configurable format strategy.
|
||||
|
||||
=== The Cipher Output ===
|
||||
|
||||
The plaintext you get back after decryption is a WordArray object. See Hashers' Output for more detail.
|
||||
|
||||
The ciphertext you get back after encryption isn't a string yet. It's a CipherParams object. A CipherParams object gives you access to all the parameters used during encryption. When you use a CipherParams object in a string context, it's automatically converted to a string according to a format strategy. The default is an OpenSSL-compatible format.
|
||||
|
||||
{{{
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/rollups/aes.js"></script>
|
||||
<script>
|
||||
var encrypted = CryptoJS.AES.encrypt("Message", "Secret Passphrase");
|
||||
|
||||
alert(encrypted.key); // 74eb593087a982e2a6f5dded54ecd96d1fd0f3d44a58728cdcd40c55227522223
|
||||
alert(encrypted.iv); // 7781157e2629b094f0e3dd48c4d786115
|
||||
alert(encrypted.salt); // 7a25f9132ec6a8b34
|
||||
alert(encrypted.ciphertext); // 73e54154a15d1beeb509d9e12f1e462a0
|
||||
|
||||
alert(encrypted); // U2FsdGVkX1+iX5Ey7GqLND5UFUoV0b7rUJ2eEvHkYqA=
|
||||
</script>
|
||||
}}}
|
||||
|
||||
You can define your own formats in order to be compatible with other crypto implementations. A format is an object with two methods—stringify and parse—that converts between CipherParams objects and ciphertext strings.
|
||||
|
||||
Here's how you might write a JSON formatter:
|
||||
|
||||
{{{
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/rollups/aes.js"></script>
|
||||
<script>
|
||||
var JsonFormatter = {
|
||||
stringify: function (cipherParams) {
|
||||
// create json object with ciphertext
|
||||
var jsonObj = {
|
||||
ct: cipherParams.ciphertext.toString(CryptoJS.enc.Base64)
|
||||
};
|
||||
|
||||
// optionally add iv and salt
|
||||
if (cipherParams.iv) {
|
||||
jsonObj.iv = cipherParams.iv.toString();
|
||||
}
|
||||
if (cipherParams.salt) {
|
||||
jsonObj.s = cipherParams.salt.toString();
|
||||
}
|
||||
|
||||
// stringify json object
|
||||
return JSON.stringify(jsonObj);
|
||||
},
|
||||
|
||||
parse: function (jsonStr) {
|
||||
// parse json string
|
||||
var jsonObj = JSON.parse(jsonStr);
|
||||
|
||||
// extract ciphertext from json object, and create cipher params object
|
||||
var cipherParams = CryptoJS.lib.CipherParams.create({
|
||||
ciphertext: CryptoJS.enc.Base64.parse(jsonObj.ct)
|
||||
});
|
||||
|
||||
// optionally extract iv and salt
|
||||
if (jsonObj.iv) {
|
||||
cipherParams.iv = CryptoJS.enc.Hex.parse(jsonObj.iv)
|
||||
}
|
||||
if (jsonObj.s) {
|
||||
cipherParams.salt = CryptoJS.enc.Hex.parse(jsonObj.s)
|
||||
}
|
||||
|
||||
return cipherParams;
|
||||
}
|
||||
};
|
||||
|
||||
var encrypted = CryptoJS.AES.encrypt("Message", "Secret Passphrase", { format: JsonFormatter });
|
||||
|
||||
alert(encrypted); // {"ct":"tZ4MsEnfbcDOwqau68aOrQ==","iv":"8a8c8fd8fe33743d3638737ea4a00698","s":"ba06373c8f57179c"}
|
||||
|
||||
var decrypted = CryptoJS.AES.decrypt(encrypted, "Secret Passphrase", { format: JsonFormatter });
|
||||
|
||||
alert(decrypted.toString(CryptoJS.enc.Utf8)); // Message
|
||||
</script>
|
||||
}}}
|
||||
|
||||
=== Progressive Ciphering ===
|
||||
|
||||
{{{
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/rollups/aes.js"></script>
|
||||
<script>
|
||||
var key = CryptoJS.enc.Hex.parse('000102030405060708090a0b0c0d0e0f');
|
||||
var iv = CryptoJS.enc.Hex.parse('101112131415161718191a1b1c1d1e1f');
|
||||
|
||||
var aesEncryptor = CryptoJS.algo.AES.createEncryptor(key, { iv: iv });
|
||||
|
||||
var ciphertextPart1 = aesEncryptor.process("Message Part 1");
|
||||
var ciphertextPart2 = aesEncryptor.process("Message Part 2");
|
||||
var ciphertextPart3 = aesEncryptor.process("Message Part 3");
|
||||
var ciphertextPart4 = aesEncryptor.finalize();
|
||||
|
||||
var aesDecryptor = CryptoJS.algo.AES.createDecryptor(key, { iv: iv });
|
||||
|
||||
var plaintextPart1 = aesDecryptor.process(ciphertextPart1);
|
||||
var plaintextPart2 = aesDecryptor.process(ciphertextPart2);
|
||||
var plaintextPart3 = aesDecryptor.process(ciphertextPart3);
|
||||
var plaintextPart4 = aesDecryptor.process(ciphertextPart4);
|
||||
var plaintextPart5 = aesDecryptor.finalize();
|
||||
</script>
|
||||
}}}
|
||||
|
||||
=== Interoperability ===
|
||||
|
||||
==== With OpenSSL ====
|
||||
|
||||
Encrypt with OpenSSL:
|
||||
|
||||
{{{
|
||||
openssl enc -aes-256-cbc -in infile -out outfile -pass pass:"Secret Passphrase" -e -base64
|
||||
}}}
|
||||
|
||||
Decrypt with CryptoJS:
|
||||
|
||||
{{{
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/rollups/aes.js"></script>
|
||||
<script>
|
||||
var decrypted = CryptoJS.AES.decrypt(openSSLEncrypted, "Secret Passphrase");
|
||||
</script>
|
||||
}}}
|
||||
|
||||
== Encoders ==
|
||||
|
||||
CryptoJS can convert from encoding formats such as Base64, Latin1 or Hex to WordArray objects and vica versa.
|
||||
|
||||
{{{
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/components/core-min.js"></script>
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/components/enc-utf16-min.js"></script>
|
||||
<script src="http://crypto-js.googlecode.com/svn/tags/3.1/build/components/enc-base64-min.js"></script>
|
||||
<script>
|
||||
var words = CryptoJS.enc.Base64.parse('SGVsbG8sIFdvcmxkIQ==');
|
||||
var base64 = CryptoJS.enc.Base64.stringify(words);
|
||||
|
||||
var words = CryptoJS.enc.Latin1.parse('Hello, World!');
|
||||
var latin1 = CryptoJS.enc.Latin1.stringify(words);
|
||||
|
||||
var words = CryptoJS.enc.Hex.parse('48656c6c6f2c20576f726c6421');
|
||||
var hex = CryptoJS.enc.Hex.stringify(words);
|
||||
|
||||
var words = CryptoJS.enc.Utf8.parse('𤭢');
|
||||
var utf8 = CryptoJS.enc.Utf8.stringify(words);
|
||||
|
||||
var words = CryptoJS.enc.Utf16.parse('Hello, World!');
|
||||
var utf16 = CryptoJS.enc.Utf16.stringify(words);
|
||||
|
||||
var words = CryptoJS.enc.Utf16LE.parse('Hello, World!');
|
||||
var utf16 = CryptoJS.enc.Utf16LE.stringify(words);
|
||||
</script>
|
||||
}}}
|
@ -0,0 +1,136 @@
|
||||
;(function (root, factory) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
(function () {
|
||||
// Shortcuts
|
||||
var C = CryptoJS;
|
||||
var C_lib = C.lib;
|
||||
var WordArray = C_lib.WordArray;
|
||||
var C_enc = C.enc;
|
||||
|
||||
/**
|
||||
* Base64 encoding strategy.
|
||||
*/
|
||||
var Base64 = C_enc.Base64 = {
|
||||
/**
|
||||
* Converts a word array to a Base64 string.
|
||||
*
|
||||
* @param {WordArray} wordArray The word array.
|
||||
*
|
||||
* @return {string} The Base64 string.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var base64String = CryptoJS.enc.Base64.stringify(wordArray);
|
||||
*/
|
||||
stringify: function (wordArray) {
|
||||
// Shortcuts
|
||||
var words = wordArray.words;
|
||||
var sigBytes = wordArray.sigBytes;
|
||||
var map = this._map;
|
||||
|
||||
// Clamp excess bits
|
||||
wordArray.clamp();
|
||||
|
||||
// Convert
|
||||
var base64Chars = [];
|
||||
for (var i = 0; i < sigBytes; i += 3) {
|
||||
var byte1 = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
|
||||
var byte2 = (words[(i + 1) >>> 2] >>> (24 - ((i + 1) % 4) * 8)) & 0xff;
|
||||
var byte3 = (words[(i + 2) >>> 2] >>> (24 - ((i + 2) % 4) * 8)) & 0xff;
|
||||
|
||||
var triplet = (byte1 << 16) | (byte2 << 8) | byte3;
|
||||
|
||||
for (var j = 0; (j < 4) && (i + j * 0.75 < sigBytes); j++) {
|
||||
base64Chars.push(map.charAt((triplet >>> (6 * (3 - j))) & 0x3f));
|
||||
}
|
||||
}
|
||||
|
||||
// Add padding
|
||||
var paddingChar = map.charAt(64);
|
||||
if (paddingChar) {
|
||||
while (base64Chars.length % 4) {
|
||||
base64Chars.push(paddingChar);
|
||||
}
|
||||
}
|
||||
|
||||
return base64Chars.join('');
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts a Base64 string to a word array.
|
||||
*
|
||||
* @param {string} base64Str The Base64 string.
|
||||
*
|
||||
* @return {WordArray} The word array.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var wordArray = CryptoJS.enc.Base64.parse(base64String);
|
||||
*/
|
||||
parse: function (base64Str) {
|
||||
// Shortcuts
|
||||
var base64StrLength = base64Str.length;
|
||||
var map = this._map;
|
||||
var reverseMap = this._reverseMap;
|
||||
|
||||
if (!reverseMap) {
|
||||
reverseMap = this._reverseMap = [];
|
||||
for (var j = 0; j < map.length; j++) {
|
||||
reverseMap[map.charCodeAt(j)] = j;
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore padding
|
||||
var paddingChar = map.charAt(64);
|
||||
if (paddingChar) {
|
||||
var paddingIndex = base64Str.indexOf(paddingChar);
|
||||
if (paddingIndex !== -1) {
|
||||
base64StrLength = paddingIndex;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert
|
||||
return parseLoop(base64Str, base64StrLength, reverseMap);
|
||||
|
||||
},
|
||||
|
||||
_map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
|
||||
};
|
||||
|
||||
function parseLoop(base64Str, base64StrLength, reverseMap) {
|
||||
var words = [];
|
||||
var nBytes = 0;
|
||||
for (var i = 0; i < base64StrLength; i++) {
|
||||
if (i % 4) {
|
||||
var bits1 = reverseMap[base64Str.charCodeAt(i - 1)] << ((i % 4) * 2);
|
||||
var bits2 = reverseMap[base64Str.charCodeAt(i)] >>> (6 - (i % 4) * 2);
|
||||
var bitsCombined = bits1 | bits2;
|
||||
words[nBytes >>> 2] |= bitsCombined << (24 - (nBytes % 4) * 8);
|
||||
nBytes++;
|
||||
}
|
||||
}
|
||||
return WordArray.create(words, nBytes);
|
||||
}
|
||||
}());
|
||||
|
||||
|
||||
return CryptoJS.enc.Base64;
|
||||
|
||||
}));
|
@ -0,0 +1,140 @@
|
||||
;(function (root, factory) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
(function () {
|
||||
// Shortcuts
|
||||
var C = CryptoJS;
|
||||
var C_lib = C.lib;
|
||||
var WordArray = C_lib.WordArray;
|
||||
var C_enc = C.enc;
|
||||
|
||||
/**
|
||||
* Base64url encoding strategy.
|
||||
*/
|
||||
var Base64url = C_enc.Base64url = {
|
||||
/**
|
||||
* Converts a word array to a Base64url string.
|
||||
*
|
||||
* @param {WordArray} wordArray The word array.
|
||||
*
|
||||
* @param {boolean} urlSafe Whether to use url safe
|
||||
*
|
||||
* @return {string} The Base64url string.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var base64String = CryptoJS.enc.Base64url.stringify(wordArray);
|
||||
*/
|
||||
stringify: function (wordArray, urlSafe=true) {
|
||||
// Shortcuts
|
||||
var words = wordArray.words;
|
||||
var sigBytes = wordArray.sigBytes;
|
||||
var map = urlSafe ? this._safe_map : this._map;
|
||||
|
||||
// Clamp excess bits
|
||||
wordArray.clamp();
|
||||
|
||||
// Convert
|
||||
var base64Chars = [];
|
||||
for (var i = 0; i < sigBytes; i += 3) {
|
||||
var byte1 = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
|
||||
var byte2 = (words[(i + 1) >>> 2] >>> (24 - ((i + 1) % 4) * 8)) & 0xff;
|
||||
var byte3 = (words[(i + 2) >>> 2] >>> (24 - ((i + 2) % 4) * 8)) & 0xff;
|
||||
|
||||
var triplet = (byte1 << 16) | (byte2 << 8) | byte3;
|
||||
|
||||
for (var j = 0; (j < 4) && (i + j * 0.75 < sigBytes); j++) {
|
||||
base64Chars.push(map.charAt((triplet >>> (6 * (3 - j))) & 0x3f));
|
||||
}
|
||||
}
|
||||
|
||||
// Add padding
|
||||
var paddingChar = map.charAt(64);
|
||||
if (paddingChar) {
|
||||
while (base64Chars.length % 4) {
|
||||
base64Chars.push(paddingChar);
|
||||
}
|
||||
}
|
||||
|
||||
return base64Chars.join('');
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts a Base64url string to a word array.
|
||||
*
|
||||
* @param {string} base64Str The Base64url string.
|
||||
*
|
||||
* @param {boolean} urlSafe Whether to use url safe
|
||||
*
|
||||
* @return {WordArray} The word array.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var wordArray = CryptoJS.enc.Base64url.parse(base64String);
|
||||
*/
|
||||
parse: function (base64Str, urlSafe=true) {
|
||||
// Shortcuts
|
||||
var base64StrLength = base64Str.length;
|
||||
var map = urlSafe ? this._safe_map : this._map;
|
||||
var reverseMap = this._reverseMap;
|
||||
|
||||
if (!reverseMap) {
|
||||
reverseMap = this._reverseMap = [];
|
||||
for (var j = 0; j < map.length; j++) {
|
||||
reverseMap[map.charCodeAt(j)] = j;
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore padding
|
||||
var paddingChar = map.charAt(64);
|
||||
if (paddingChar) {
|
||||
var paddingIndex = base64Str.indexOf(paddingChar);
|
||||
if (paddingIndex !== -1) {
|
||||
base64StrLength = paddingIndex;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert
|
||||
return parseLoop(base64Str, base64StrLength, reverseMap);
|
||||
|
||||
},
|
||||
|
||||
_map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',
|
||||
_safe_map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_',
|
||||
};
|
||||
|
||||
function parseLoop(base64Str, base64StrLength, reverseMap) {
|
||||
var words = [];
|
||||
var nBytes = 0;
|
||||
for (var i = 0; i < base64StrLength; i++) {
|
||||
if (i % 4) {
|
||||
var bits1 = reverseMap[base64Str.charCodeAt(i - 1)] << ((i % 4) * 2);
|
||||
var bits2 = reverseMap[base64Str.charCodeAt(i)] >>> (6 - (i % 4) * 2);
|
||||
var bitsCombined = bits1 | bits2;
|
||||
words[nBytes >>> 2] |= bitsCombined << (24 - (nBytes % 4) * 8);
|
||||
nBytes++;
|
||||
}
|
||||
}
|
||||
return WordArray.create(words, nBytes);
|
||||
}
|
||||
}());
|
||||
|
||||
return CryptoJS.enc.Base64url;
|
||||
|
||||
}));
|
@ -0,0 +1,18 @@
|
||||
;(function (root, factory) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
return CryptoJS.enc.Hex;
|
||||
|
||||
}));
|
@ -0,0 +1,18 @@
|
||||
;(function (root, factory) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
return CryptoJS.enc.Latin1;
|
||||
|
||||
}));
|
@ -0,0 +1,149 @@
|
||||
;(function (root, factory) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
(function () {
|
||||
// Shortcuts
|
||||
var C = CryptoJS;
|
||||
var C_lib = C.lib;
|
||||
var WordArray = C_lib.WordArray;
|
||||
var C_enc = C.enc;
|
||||
|
||||
/**
|
||||
* UTF-16 BE encoding strategy.
|
||||
*/
|
||||
var Utf16BE = C_enc.Utf16 = C_enc.Utf16BE = {
|
||||
/**
|
||||
* Converts a word array to a UTF-16 BE string.
|
||||
*
|
||||
* @param {WordArray} wordArray The word array.
|
||||
*
|
||||
* @return {string} The UTF-16 BE string.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var utf16String = CryptoJS.enc.Utf16.stringify(wordArray);
|
||||
*/
|
||||
stringify: function (wordArray) {
|
||||
// Shortcuts
|
||||
var words = wordArray.words;
|
||||
var sigBytes = wordArray.sigBytes;
|
||||
|
||||
// Convert
|
||||
var utf16Chars = [];
|
||||
for (var i = 0; i < sigBytes; i += 2) {
|
||||
var codePoint = (words[i >>> 2] >>> (16 - (i % 4) * 8)) & 0xffff;
|
||||
utf16Chars.push(String.fromCharCode(codePoint));
|
||||
}
|
||||
|
||||
return utf16Chars.join('');
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts a UTF-16 BE string to a word array.
|
||||
*
|
||||
* @param {string} utf16Str The UTF-16 BE string.
|
||||
*
|
||||
* @return {WordArray} The word array.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var wordArray = CryptoJS.enc.Utf16.parse(utf16String);
|
||||
*/
|
||||
parse: function (utf16Str) {
|
||||
// Shortcut
|
||||
var utf16StrLength = utf16Str.length;
|
||||
|
||||
// Convert
|
||||
var words = [];
|
||||
for (var i = 0; i < utf16StrLength; i++) {
|
||||
words[i >>> 1] |= utf16Str.charCodeAt(i) << (16 - (i % 2) * 16);
|
||||
}
|
||||
|
||||
return WordArray.create(words, utf16StrLength * 2);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* UTF-16 LE encoding strategy.
|
||||
*/
|
||||
C_enc.Utf16LE = {
|
||||
/**
|
||||
* Converts a word array to a UTF-16 LE string.
|
||||
*
|
||||
* @param {WordArray} wordArray The word array.
|
||||
*
|
||||
* @return {string} The UTF-16 LE string.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var utf16Str = CryptoJS.enc.Utf16LE.stringify(wordArray);
|
||||
*/
|
||||
stringify: function (wordArray) {
|
||||
// Shortcuts
|
||||
var words = wordArray.words;
|
||||
var sigBytes = wordArray.sigBytes;
|
||||
|
||||
// Convert
|
||||
var utf16Chars = [];
|
||||
for (var i = 0; i < sigBytes; i += 2) {
|
||||
var codePoint = swapEndian((words[i >>> 2] >>> (16 - (i % 4) * 8)) & 0xffff);
|
||||
utf16Chars.push(String.fromCharCode(codePoint));
|
||||
}
|
||||
|
||||
return utf16Chars.join('');
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts a UTF-16 LE string to a word array.
|
||||
*
|
||||
* @param {string} utf16Str The UTF-16 LE string.
|
||||
*
|
||||
* @return {WordArray} The word array.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var wordArray = CryptoJS.enc.Utf16LE.parse(utf16Str);
|
||||
*/
|
||||
parse: function (utf16Str) {
|
||||
// Shortcut
|
||||
var utf16StrLength = utf16Str.length;
|
||||
|
||||
// Convert
|
||||
var words = [];
|
||||
for (var i = 0; i < utf16StrLength; i++) {
|
||||
words[i >>> 1] |= swapEndian(utf16Str.charCodeAt(i) << (16 - (i % 2) * 16));
|
||||
}
|
||||
|
||||
return WordArray.create(words, utf16StrLength * 2);
|
||||
}
|
||||
};
|
||||
|
||||
function swapEndian(word) {
|
||||
return ((word << 8) & 0xff00ff00) | ((word >>> 8) & 0x00ff00ff);
|
||||
}
|
||||
}());
|
||||
|
||||
|
||||
return CryptoJS.enc.Utf16;
|
||||
|
||||
}));
|
@ -0,0 +1,18 @@
|
||||
;(function (root, factory) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
return CryptoJS.enc.Utf8;
|
||||
|
||||
}));
|
@ -0,0 +1,134 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./sha1"), require("./hmac"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./sha1", "./hmac"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
(function () {
|
||||
// Shortcuts
|
||||
var C = CryptoJS;
|
||||
var C_lib = C.lib;
|
||||
var Base = C_lib.Base;
|
||||
var WordArray = C_lib.WordArray;
|
||||
var C_algo = C.algo;
|
||||
var MD5 = C_algo.MD5;
|
||||
|
||||
/**
|
||||
* This key derivation function is meant to conform with EVP_BytesToKey.
|
||||
* www.openssl.org/docs/crypto/EVP_BytesToKey.html
|
||||
*/
|
||||
var EvpKDF = C_algo.EvpKDF = Base.extend({
|
||||
/**
|
||||
* Configuration options.
|
||||
*
|
||||
* @property {number} keySize The key size in words to generate. Default: 4 (128 bits)
|
||||
* @property {Hasher} hasher The hash algorithm to use. Default: MD5
|
||||
* @property {number} iterations The number of iterations to perform. Default: 1
|
||||
*/
|
||||
cfg: Base.extend({
|
||||
keySize: 128/32,
|
||||
hasher: MD5,
|
||||
iterations: 1
|
||||
}),
|
||||
|
||||
/**
|
||||
* Initializes a newly created key derivation function.
|
||||
*
|
||||
* @param {Object} cfg (Optional) The configuration options to use for the derivation.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var kdf = CryptoJS.algo.EvpKDF.create();
|
||||
* var kdf = CryptoJS.algo.EvpKDF.create({ keySize: 8 });
|
||||
* var kdf = CryptoJS.algo.EvpKDF.create({ keySize: 8, iterations: 1000 });
|
||||
*/
|
||||
init: function (cfg) {
|
||||
this.cfg = this.cfg.extend(cfg);
|
||||
},
|
||||
|
||||
/**
|
||||
* Derives a key from a password.
|
||||
*
|
||||
* @param {WordArray|string} password The password.
|
||||
* @param {WordArray|string} salt A salt.
|
||||
*
|
||||
* @return {WordArray} The derived key.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var key = kdf.compute(password, salt);
|
||||
*/
|
||||
compute: function (password, salt) {
|
||||
var block;
|
||||
|
||||
// Shortcut
|
||||
var cfg = this.cfg;
|
||||
|
||||
// Init hasher
|
||||
var hasher = cfg.hasher.create();
|
||||
|
||||
// Initial values
|
||||
var derivedKey = WordArray.create();
|
||||
|
||||
// Shortcuts
|
||||
var derivedKeyWords = derivedKey.words;
|
||||
var keySize = cfg.keySize;
|
||||
var iterations = cfg.iterations;
|
||||
|
||||
// Generate key
|
||||
while (derivedKeyWords.length < keySize) {
|
||||
if (block) {
|
||||
hasher.update(block);
|
||||
}
|
||||
block = hasher.update(password).finalize(salt);
|
||||
hasher.reset();
|
||||
|
||||
// Iterations
|
||||
for (var i = 1; i < iterations; i++) {
|
||||
block = hasher.finalize(block);
|
||||
hasher.reset();
|
||||
}
|
||||
|
||||
derivedKey.concat(block);
|
||||
}
|
||||
derivedKey.sigBytes = keySize * 4;
|
||||
|
||||
return derivedKey;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Derives a key from a password.
|
||||
*
|
||||
* @param {WordArray|string} password The password.
|
||||
* @param {WordArray|string} salt A salt.
|
||||
* @param {Object} cfg (Optional) The configuration options to use for this computation.
|
||||
*
|
||||
* @return {WordArray} The derived key.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var key = CryptoJS.EvpKDF(password, salt);
|
||||
* var key = CryptoJS.EvpKDF(password, salt, { keySize: 8 });
|
||||
* var key = CryptoJS.EvpKDF(password, salt, { keySize: 8, iterations: 1000 });
|
||||
*/
|
||||
C.EvpKDF = function (password, salt, cfg) {
|
||||
return EvpKDF.create(cfg).compute(password, salt);
|
||||
};
|
||||
}());
|
||||
|
||||
|
||||
return CryptoJS.EvpKDF;
|
||||
|
||||
}));
|
@ -0,0 +1,66 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./cipher-core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./cipher-core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
(function (undefined) {
|
||||
// Shortcuts
|
||||
var C = CryptoJS;
|
||||
var C_lib = C.lib;
|
||||
var CipherParams = C_lib.CipherParams;
|
||||
var C_enc = C.enc;
|
||||
var Hex = C_enc.Hex;
|
||||
var C_format = C.format;
|
||||
|
||||
var HexFormatter = C_format.Hex = {
|
||||
/**
|
||||
* Converts the ciphertext of a cipher params object to a hexadecimally encoded string.
|
||||
*
|
||||
* @param {CipherParams} cipherParams The cipher params object.
|
||||
*
|
||||
* @return {string} The hexadecimally encoded string.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var hexString = CryptoJS.format.Hex.stringify(cipherParams);
|
||||
*/
|
||||
stringify: function (cipherParams) {
|
||||
return cipherParams.ciphertext.toString(Hex);
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts a hexadecimally encoded ciphertext string to a cipher params object.
|
||||
*
|
||||
* @param {string} input The hexadecimally encoded string.
|
||||
*
|
||||
* @return {CipherParams} The cipher params object.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var cipherParams = CryptoJS.format.Hex.parse(hexString);
|
||||
*/
|
||||
parse: function (input) {
|
||||
var ciphertext = Hex.parse(input);
|
||||
return CipherParams.create({ ciphertext: ciphertext });
|
||||
}
|
||||
};
|
||||
}());
|
||||
|
||||
|
||||
return CryptoJS.format.Hex;
|
||||
|
||||
}));
|
@ -0,0 +1,18 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./cipher-core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./cipher-core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
return CryptoJS.format.OpenSSL;
|
||||
|
||||
}));
|
@ -0,0 +1,18 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./md5"), require("./hmac"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./md5", "./hmac"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
return CryptoJS.HmacMD5;
|
||||
|
||||
}));
|
@ -0,0 +1,18 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./ripemd160"), require("./hmac"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./ripemd160", "./hmac"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
return CryptoJS.HmacRIPEMD160;
|
||||
|
||||
}));
|
@ -0,0 +1,18 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./sha1"), require("./hmac"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./sha1", "./hmac"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
return CryptoJS.HmacSHA1;
|
||||
|
||||
}));
|
@ -0,0 +1,18 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./sha256"), require("./sha224"), require("./hmac"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./sha256", "./sha224", "./hmac"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
return CryptoJS.HmacSHA224;
|
||||
|
||||
}));
|
@ -0,0 +1,18 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./sha256"), require("./hmac"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./sha256", "./hmac"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
return CryptoJS.HmacSHA256;
|
||||
|
||||
}));
|
@ -0,0 +1,18 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./x64-core"), require("./sha3"), require("./hmac"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./x64-core", "./sha3", "./hmac"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
return CryptoJS.HmacSHA3;
|
||||
|
||||
}));
|
@ -0,0 +1,18 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./x64-core"), require("./sha512"), require("./sha384"), require("./hmac"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./x64-core", "./sha512", "./sha384", "./hmac"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
return CryptoJS.HmacSHA384;
|
||||
|
||||
}));
|
@ -0,0 +1,18 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./x64-core"), require("./sha512"), require("./hmac"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./x64-core", "./sha512", "./hmac"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
return CryptoJS.HmacSHA512;
|
||||
|
||||
}));
|
@ -0,0 +1,143 @@
|
||||
;(function (root, factory) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
(function () {
|
||||
// Shortcuts
|
||||
var C = CryptoJS;
|
||||
var C_lib = C.lib;
|
||||
var Base = C_lib.Base;
|
||||
var C_enc = C.enc;
|
||||
var Utf8 = C_enc.Utf8;
|
||||
var C_algo = C.algo;
|
||||
|
||||
/**
|
||||
* HMAC algorithm.
|
||||
*/
|
||||
var HMAC = C_algo.HMAC = Base.extend({
|
||||
/**
|
||||
* Initializes a newly created HMAC.
|
||||
*
|
||||
* @param {Hasher} hasher The hash algorithm to use.
|
||||
* @param {WordArray|string} key The secret key.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var hmacHasher = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, key);
|
||||
*/
|
||||
init: function (hasher, key) {
|
||||
// Init hasher
|
||||
hasher = this._hasher = new hasher.init();
|
||||
|
||||
// Convert string to WordArray, else assume WordArray already
|
||||
if (typeof key == 'string') {
|
||||
key = Utf8.parse(key);
|
||||
}
|
||||
|
||||
// Shortcuts
|
||||
var hasherBlockSize = hasher.blockSize;
|
||||
var hasherBlockSizeBytes = hasherBlockSize * 4;
|
||||
|
||||
// Allow arbitrary length keys
|
||||
if (key.sigBytes > hasherBlockSizeBytes) {
|
||||
key = hasher.finalize(key);
|
||||
}
|
||||
|
||||
// Clamp excess bits
|
||||
key.clamp();
|
||||
|
||||
// Clone key for inner and outer pads
|
||||
var oKey = this._oKey = key.clone();
|
||||
var iKey = this._iKey = key.clone();
|
||||
|
||||
// Shortcuts
|
||||
var oKeyWords = oKey.words;
|
||||
var iKeyWords = iKey.words;
|
||||
|
||||
// XOR keys with pad constants
|
||||
for (var i = 0; i < hasherBlockSize; i++) {
|
||||
oKeyWords[i] ^= 0x5c5c5c5c;
|
||||
iKeyWords[i] ^= 0x36363636;
|
||||
}
|
||||
oKey.sigBytes = iKey.sigBytes = hasherBlockSizeBytes;
|
||||
|
||||
// Set initial values
|
||||
this.reset();
|
||||
},
|
||||
|
||||
/**
|
||||
* Resets this HMAC to its initial state.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* hmacHasher.reset();
|
||||
*/
|
||||
reset: function () {
|
||||
// Shortcut
|
||||
var hasher = this._hasher;
|
||||
|
||||
// Reset
|
||||
hasher.reset();
|
||||
hasher.update(this._iKey);
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates this HMAC with a message.
|
||||
*
|
||||
* @param {WordArray|string} messageUpdate The message to append.
|
||||
*
|
||||
* @return {HMAC} This HMAC instance.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* hmacHasher.update('message');
|
||||
* hmacHasher.update(wordArray);
|
||||
*/
|
||||
update: function (messageUpdate) {
|
||||
this._hasher.update(messageUpdate);
|
||||
|
||||
// Chainable
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Finalizes the HMAC computation.
|
||||
* Note that the finalize operation is effectively a destructive, read-once operation.
|
||||
*
|
||||
* @param {WordArray|string} messageUpdate (Optional) A final message update.
|
||||
*
|
||||
* @return {WordArray} The HMAC.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var hmac = hmacHasher.finalize();
|
||||
* var hmac = hmacHasher.finalize('message');
|
||||
* var hmac = hmacHasher.finalize(wordArray);
|
||||
*/
|
||||
finalize: function (messageUpdate) {
|
||||
// Shortcut
|
||||
var hasher = this._hasher;
|
||||
|
||||
// Compute HMAC
|
||||
var innerHash = hasher.finalize(messageUpdate);
|
||||
hasher.reset();
|
||||
var hmac = hasher.finalize(this._oKey.clone().concat(innerHash));
|
||||
|
||||
return hmac;
|
||||
}
|
||||
});
|
||||
}());
|
||||
|
||||
|
||||
}));
|
@ -0,0 +1,18 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./x64-core"), require("./lib-typedarrays"), require("./enc-utf16"), require("./enc-base64"), require("./enc-base64url"), require("./md5"), require("./sha1"), require("./sha256"), require("./sha224"), require("./sha512"), require("./sha384"), require("./sha3"), require("./ripemd160"), require("./hmac"), require("./pbkdf2"), require("./evpkdf"), require("./cipher-core"), require("./mode-cfb"), require("./mode-ctr"), require("./mode-ctr-gladman"), require("./mode-ofb"), require("./mode-ecb"), require("./pad-ansix923"), require("./pad-iso10126"), require("./pad-iso97971"), require("./pad-zeropadding"), require("./pad-nopadding"), require("./format-hex"), require("./aes"), require("./tripledes"), require("./rc4"), require("./rabbit"), require("./rabbit-legacy"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./x64-core", "./lib-typedarrays", "./enc-utf16", "./enc-base64", "./enc-base64url", "./md5", "./sha1", "./sha256", "./sha224", "./sha512", "./sha384", "./sha3", "./ripemd160", "./hmac", "./pbkdf2", "./evpkdf", "./cipher-core", "./mode-cfb", "./mode-ctr", "./mode-ctr-gladman", "./mode-ofb", "./mode-ecb", "./pad-ansix923", "./pad-iso10126", "./pad-iso97971", "./pad-zeropadding", "./pad-nopadding", "./format-hex", "./aes", "./tripledes", "./rc4", "./rabbit", "./rabbit-legacy"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
root.CryptoJS = factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
return CryptoJS;
|
||||
|
||||
}));
|
@ -0,0 +1,76 @@
|
||||
;(function (root, factory) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
(function () {
|
||||
// Check if typed arrays are supported
|
||||
if (typeof ArrayBuffer != 'function') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Shortcuts
|
||||
var C = CryptoJS;
|
||||
var C_lib = C.lib;
|
||||
var WordArray = C_lib.WordArray;
|
||||
|
||||
// Reference original init
|
||||
var superInit = WordArray.init;
|
||||
|
||||
// Augment WordArray.init to handle typed arrays
|
||||
var subInit = WordArray.init = function (typedArray) {
|
||||
// Convert buffers to uint8
|
||||
if (typedArray instanceof ArrayBuffer) {
|
||||
typedArray = new Uint8Array(typedArray);
|
||||
}
|
||||
|
||||
// Convert other array views to uint8
|
||||
if (
|
||||
typedArray instanceof Int8Array ||
|
||||
(typeof Uint8ClampedArray !== "undefined" && typedArray instanceof Uint8ClampedArray) ||
|
||||
typedArray instanceof Int16Array ||
|
||||
typedArray instanceof Uint16Array ||
|
||||
typedArray instanceof Int32Array ||
|
||||
typedArray instanceof Uint32Array ||
|
||||
typedArray instanceof Float32Array ||
|
||||
typedArray instanceof Float64Array
|
||||
) {
|
||||
typedArray = new Uint8Array(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength);
|
||||
}
|
||||
|
||||
// Handle Uint8Array
|
||||
if (typedArray instanceof Uint8Array) {
|
||||
// Shortcut
|
||||
var typedArrayByteLength = typedArray.byteLength;
|
||||
|
||||
// Extract bytes
|
||||
var words = [];
|
||||
for (var i = 0; i < typedArrayByteLength; i++) {
|
||||
words[i >>> 2] |= typedArray[i] << (24 - (i % 4) * 8);
|
||||
}
|
||||
|
||||
// Initialize this word array
|
||||
superInit.call(this, words, typedArrayByteLength);
|
||||
} else {
|
||||
// Else call normal init
|
||||
superInit.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
|
||||
subInit.prototype = WordArray;
|
||||
}());
|
||||
|
||||
|
||||
return CryptoJS.lib.WordArray;
|
||||
|
||||
}));
|
@ -0,0 +1,268 @@
|
||||
;(function (root, factory) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
(function (Math) {
|
||||
// Shortcuts
|
||||
var C = CryptoJS;
|
||||
var C_lib = C.lib;
|
||||
var WordArray = C_lib.WordArray;
|
||||
var Hasher = C_lib.Hasher;
|
||||
var C_algo = C.algo;
|
||||
|
||||
// Constants table
|
||||
var T = [];
|
||||
|
||||
// Compute constants
|
||||
(function () {
|
||||
for (var i = 0; i < 64; i++) {
|
||||
T[i] = (Math.abs(Math.sin(i + 1)) * 0x100000000) | 0;
|
||||
}
|
||||
}());
|
||||
|
||||
/**
|
||||
* MD5 hash algorithm.
|
||||
*/
|
||||
var MD5 = C_algo.MD5 = Hasher.extend({
|
||||
_doReset: function () {
|
||||
this._hash = new WordArray.init([
|
||||
0x67452301, 0xefcdab89,
|
||||
0x98badcfe, 0x10325476
|
||||
]);
|
||||
},
|
||||
|
||||
_doProcessBlock: function (M, offset) {
|
||||
// Swap endian
|
||||
for (var i = 0; i < 16; i++) {
|
||||
// Shortcuts
|
||||
var offset_i = offset + i;
|
||||
var M_offset_i = M[offset_i];
|
||||
|
||||
M[offset_i] = (
|
||||
(((M_offset_i << 8) | (M_offset_i >>> 24)) & 0x00ff00ff) |
|
||||
(((M_offset_i << 24) | (M_offset_i >>> 8)) & 0xff00ff00)
|
||||
);
|
||||
}
|
||||
|
||||
// Shortcuts
|
||||
var H = this._hash.words;
|
||||
|
||||
var M_offset_0 = M[offset + 0];
|
||||
var M_offset_1 = M[offset + 1];
|
||||
var M_offset_2 = M[offset + 2];
|
||||
var M_offset_3 = M[offset + 3];
|
||||
var M_offset_4 = M[offset + 4];
|
||||
var M_offset_5 = M[offset + 5];
|
||||
var M_offset_6 = M[offset + 6];
|
||||
var M_offset_7 = M[offset + 7];
|
||||
var M_offset_8 = M[offset + 8];
|
||||
var M_offset_9 = M[offset + 9];
|
||||
var M_offset_10 = M[offset + 10];
|
||||
var M_offset_11 = M[offset + 11];
|
||||
var M_offset_12 = M[offset + 12];
|
||||
var M_offset_13 = M[offset + 13];
|
||||
var M_offset_14 = M[offset + 14];
|
||||
var M_offset_15 = M[offset + 15];
|
||||
|
||||
// Working varialbes
|
||||
var a = H[0];
|
||||
var b = H[1];
|
||||
var c = H[2];
|
||||
var d = H[3];
|
||||
|
||||
// Computation
|
||||
a = FF(a, b, c, d, M_offset_0, 7, T[0]);
|
||||
d = FF(d, a, b, c, M_offset_1, 12, T[1]);
|
||||
c = FF(c, d, a, b, M_offset_2, 17, T[2]);
|
||||
b = FF(b, c, d, a, M_offset_3, 22, T[3]);
|
||||
a = FF(a, b, c, d, M_offset_4, 7, T[4]);
|
||||
d = FF(d, a, b, c, M_offset_5, 12, T[5]);
|
||||
c = FF(c, d, a, b, M_offset_6, 17, T[6]);
|
||||
b = FF(b, c, d, a, M_offset_7, 22, T[7]);
|
||||
a = FF(a, b, c, d, M_offset_8, 7, T[8]);
|
||||
d = FF(d, a, b, c, M_offset_9, 12, T[9]);
|
||||
c = FF(c, d, a, b, M_offset_10, 17, T[10]);
|
||||
b = FF(b, c, d, a, M_offset_11, 22, T[11]);
|
||||
a = FF(a, b, c, d, M_offset_12, 7, T[12]);
|
||||
d = FF(d, a, b, c, M_offset_13, 12, T[13]);
|
||||
c = FF(c, d, a, b, M_offset_14, 17, T[14]);
|
||||
b = FF(b, c, d, a, M_offset_15, 22, T[15]);
|
||||
|
||||
a = GG(a, b, c, d, M_offset_1, 5, T[16]);
|
||||
d = GG(d, a, b, c, M_offset_6, 9, T[17]);
|
||||
c = GG(c, d, a, b, M_offset_11, 14, T[18]);
|
||||
b = GG(b, c, d, a, M_offset_0, 20, T[19]);
|
||||
a = GG(a, b, c, d, M_offset_5, 5, T[20]);
|
||||
d = GG(d, a, b, c, M_offset_10, 9, T[21]);
|
||||
c = GG(c, d, a, b, M_offset_15, 14, T[22]);
|
||||
b = GG(b, c, d, a, M_offset_4, 20, T[23]);
|
||||
a = GG(a, b, c, d, M_offset_9, 5, T[24]);
|
||||
d = GG(d, a, b, c, M_offset_14, 9, T[25]);
|
||||
c = GG(c, d, a, b, M_offset_3, 14, T[26]);
|
||||
b = GG(b, c, d, a, M_offset_8, 20, T[27]);
|
||||
a = GG(a, b, c, d, M_offset_13, 5, T[28]);
|
||||
d = GG(d, a, b, c, M_offset_2, 9, T[29]);
|
||||
c = GG(c, d, a, b, M_offset_7, 14, T[30]);
|
||||
b = GG(b, c, d, a, M_offset_12, 20, T[31]);
|
||||
|
||||
a = HH(a, b, c, d, M_offset_5, 4, T[32]);
|
||||
d = HH(d, a, b, c, M_offset_8, 11, T[33]);
|
||||
c = HH(c, d, a, b, M_offset_11, 16, T[34]);
|
||||
b = HH(b, c, d, a, M_offset_14, 23, T[35]);
|
||||
a = HH(a, b, c, d, M_offset_1, 4, T[36]);
|
||||
d = HH(d, a, b, c, M_offset_4, 11, T[37]);
|
||||
c = HH(c, d, a, b, M_offset_7, 16, T[38]);
|
||||
b = HH(b, c, d, a, M_offset_10, 23, T[39]);
|
||||
a = HH(a, b, c, d, M_offset_13, 4, T[40]);
|
||||
d = HH(d, a, b, c, M_offset_0, 11, T[41]);
|
||||
c = HH(c, d, a, b, M_offset_3, 16, T[42]);
|
||||
b = HH(b, c, d, a, M_offset_6, 23, T[43]);
|
||||
a = HH(a, b, c, d, M_offset_9, 4, T[44]);
|
||||
d = HH(d, a, b, c, M_offset_12, 11, T[45]);
|
||||
c = HH(c, d, a, b, M_offset_15, 16, T[46]);
|
||||
b = HH(b, c, d, a, M_offset_2, 23, T[47]);
|
||||
|
||||
a = II(a, b, c, d, M_offset_0, 6, T[48]);
|
||||
d = II(d, a, b, c, M_offset_7, 10, T[49]);
|
||||
c = II(c, d, a, b, M_offset_14, 15, T[50]);
|
||||
b = II(b, c, d, a, M_offset_5, 21, T[51]);
|
||||
a = II(a, b, c, d, M_offset_12, 6, T[52]);
|
||||
d = II(d, a, b, c, M_offset_3, 10, T[53]);
|
||||
c = II(c, d, a, b, M_offset_10, 15, T[54]);
|
||||
b = II(b, c, d, a, M_offset_1, 21, T[55]);
|
||||
a = II(a, b, c, d, M_offset_8, 6, T[56]);
|
||||
d = II(d, a, b, c, M_offset_15, 10, T[57]);
|
||||
c = II(c, d, a, b, M_offset_6, 15, T[58]);
|
||||
b = II(b, c, d, a, M_offset_13, 21, T[59]);
|
||||
a = II(a, b, c, d, M_offset_4, 6, T[60]);
|
||||
d = II(d, a, b, c, M_offset_11, 10, T[61]);
|
||||
c = II(c, d, a, b, M_offset_2, 15, T[62]);
|
||||
b = II(b, c, d, a, M_offset_9, 21, T[63]);
|
||||
|
||||
// Intermediate hash value
|
||||
H[0] = (H[0] + a) | 0;
|
||||
H[1] = (H[1] + b) | 0;
|
||||
H[2] = (H[2] + c) | 0;
|
||||
H[3] = (H[3] + d) | 0;
|
||||
},
|
||||
|
||||
_doFinalize: function () {
|
||||
// Shortcuts
|
||||
var data = this._data;
|
||||
var dataWords = data.words;
|
||||
|
||||
var nBitsTotal = this._nDataBytes * 8;
|
||||
var nBitsLeft = data.sigBytes * 8;
|
||||
|
||||
// Add padding
|
||||
dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);
|
||||
|
||||
var nBitsTotalH = Math.floor(nBitsTotal / 0x100000000);
|
||||
var nBitsTotalL = nBitsTotal;
|
||||
dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = (
|
||||
(((nBitsTotalH << 8) | (nBitsTotalH >>> 24)) & 0x00ff00ff) |
|
||||
(((nBitsTotalH << 24) | (nBitsTotalH >>> 8)) & 0xff00ff00)
|
||||
);
|
||||
dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = (
|
||||
(((nBitsTotalL << 8) | (nBitsTotalL >>> 24)) & 0x00ff00ff) |
|
||||
(((nBitsTotalL << 24) | (nBitsTotalL >>> 8)) & 0xff00ff00)
|
||||
);
|
||||
|
||||
data.sigBytes = (dataWords.length + 1) * 4;
|
||||
|
||||
// Hash final blocks
|
||||
this._process();
|
||||
|
||||
// Shortcuts
|
||||
var hash = this._hash;
|
||||
var H = hash.words;
|
||||
|
||||
// Swap endian
|
||||
for (var i = 0; i < 4; i++) {
|
||||
// Shortcut
|
||||
var H_i = H[i];
|
||||
|
||||
H[i] = (((H_i << 8) | (H_i >>> 24)) & 0x00ff00ff) |
|
||||
(((H_i << 24) | (H_i >>> 8)) & 0xff00ff00);
|
||||
}
|
||||
|
||||
// Return final computed hash
|
||||
return hash;
|
||||
},
|
||||
|
||||
clone: function () {
|
||||
var clone = Hasher.clone.call(this);
|
||||
clone._hash = this._hash.clone();
|
||||
|
||||
return clone;
|
||||
}
|
||||
});
|
||||
|
||||
function FF(a, b, c, d, x, s, t) {
|
||||
var n = a + ((b & c) | (~b & d)) + x + t;
|
||||
return ((n << s) | (n >>> (32 - s))) + b;
|
||||
}
|
||||
|
||||
function GG(a, b, c, d, x, s, t) {
|
||||
var n = a + ((b & d) | (c & ~d)) + x + t;
|
||||
return ((n << s) | (n >>> (32 - s))) + b;
|
||||
}
|
||||
|
||||
function HH(a, b, c, d, x, s, t) {
|
||||
var n = a + (b ^ c ^ d) + x + t;
|
||||
return ((n << s) | (n >>> (32 - s))) + b;
|
||||
}
|
||||
|
||||
function II(a, b, c, d, x, s, t) {
|
||||
var n = a + (c ^ (b | ~d)) + x + t;
|
||||
return ((n << s) | (n >>> (32 - s))) + b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut function to the hasher's object interface.
|
||||
*
|
||||
* @param {WordArray|string} message The message to hash.
|
||||
*
|
||||
* @return {WordArray} The hash.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var hash = CryptoJS.MD5('message');
|
||||
* var hash = CryptoJS.MD5(wordArray);
|
||||
*/
|
||||
C.MD5 = Hasher._createHelper(MD5);
|
||||
|
||||
/**
|
||||
* Shortcut function to the HMAC's object interface.
|
||||
*
|
||||
* @param {WordArray|string} message The message to hash.
|
||||
* @param {WordArray|string} key The secret key.
|
||||
*
|
||||
* @return {WordArray} The HMAC.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var hmac = CryptoJS.HmacMD5(message, key);
|
||||
*/
|
||||
C.HmacMD5 = Hasher._createHmacHelper(MD5);
|
||||
}(Math));
|
||||
|
||||
|
||||
return CryptoJS.MD5;
|
||||
|
||||
}));
|
@ -0,0 +1,80 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./cipher-core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./cipher-core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
/**
|
||||
* Cipher Feedback block mode.
|
||||
*/
|
||||
CryptoJS.mode.CFB = (function () {
|
||||
var CFB = CryptoJS.lib.BlockCipherMode.extend();
|
||||
|
||||
CFB.Encryptor = CFB.extend({
|
||||
processBlock: function (words, offset) {
|
||||
// Shortcuts
|
||||
var cipher = this._cipher;
|
||||
var blockSize = cipher.blockSize;
|
||||
|
||||
generateKeystreamAndEncrypt.call(this, words, offset, blockSize, cipher);
|
||||
|
||||
// Remember this block to use with next block
|
||||
this._prevBlock = words.slice(offset, offset + blockSize);
|
||||
}
|
||||
});
|
||||
|
||||
CFB.Decryptor = CFB.extend({
|
||||
processBlock: function (words, offset) {
|
||||
// Shortcuts
|
||||
var cipher = this._cipher;
|
||||
var blockSize = cipher.blockSize;
|
||||
|
||||
// Remember this block to use with next block
|
||||
var thisBlock = words.slice(offset, offset + blockSize);
|
||||
|
||||
generateKeystreamAndEncrypt.call(this, words, offset, blockSize, cipher);
|
||||
|
||||
// This block becomes the previous block
|
||||
this._prevBlock = thisBlock;
|
||||
}
|
||||
});
|
||||
|
||||
function generateKeystreamAndEncrypt(words, offset, blockSize, cipher) {
|
||||
var keystream;
|
||||
|
||||
// Shortcut
|
||||
var iv = this._iv;
|
||||
|
||||
// Generate keystream
|
||||
if (iv) {
|
||||
keystream = iv.slice(0);
|
||||
|
||||
// Remove IV for subsequent blocks
|
||||
this._iv = undefined;
|
||||
} else {
|
||||
keystream = this._prevBlock;
|
||||
}
|
||||
cipher.encryptBlock(keystream, 0);
|
||||
|
||||
// Encrypt
|
||||
for (var i = 0; i < blockSize; i++) {
|
||||
words[offset + i] ^= keystream[i];
|
||||
}
|
||||
}
|
||||
|
||||
return CFB;
|
||||
}());
|
||||
|
||||
|
||||
return CryptoJS.mode.CFB;
|
||||
|
||||
}));
|
@ -0,0 +1,116 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./cipher-core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./cipher-core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
/** @preserve
|
||||
* Counter block mode compatible with Dr Brian Gladman fileenc.c
|
||||
* derived from CryptoJS.mode.CTR
|
||||
* Jan Hruby jhruby.web@gmail.com
|
||||
*/
|
||||
CryptoJS.mode.CTRGladman = (function () {
|
||||
var CTRGladman = CryptoJS.lib.BlockCipherMode.extend();
|
||||
|
||||
function incWord(word)
|
||||
{
|
||||
if (((word >> 24) & 0xff) === 0xff) { //overflow
|
||||
var b1 = (word >> 16)&0xff;
|
||||
var b2 = (word >> 8)&0xff;
|
||||
var b3 = word & 0xff;
|
||||
|
||||
if (b1 === 0xff) // overflow b1
|
||||
{
|
||||
b1 = 0;
|
||||
if (b2 === 0xff)
|
||||
{
|
||||
b2 = 0;
|
||||
if (b3 === 0xff)
|
||||
{
|
||||
b3 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
++b3;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
++b2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
++b1;
|
||||
}
|
||||
|
||||
word = 0;
|
||||
word += (b1 << 16);
|
||||
word += (b2 << 8);
|
||||
word += b3;
|
||||
}
|
||||
else
|
||||
{
|
||||
word += (0x01 << 24);
|
||||
}
|
||||
return word;
|
||||
}
|
||||
|
||||
function incCounter(counter)
|
||||
{
|
||||
if ((counter[0] = incWord(counter[0])) === 0)
|
||||
{
|
||||
// encr_data in fileenc.c from Dr Brian Gladman's counts only with DWORD j < 8
|
||||
counter[1] = incWord(counter[1]);
|
||||
}
|
||||
return counter;
|
||||
}
|
||||
|
||||
var Encryptor = CTRGladman.Encryptor = CTRGladman.extend({
|
||||
processBlock: function (words, offset) {
|
||||
// Shortcuts
|
||||
var cipher = this._cipher
|
||||
var blockSize = cipher.blockSize;
|
||||
var iv = this._iv;
|
||||
var counter = this._counter;
|
||||
|
||||
// Generate keystream
|
||||
if (iv) {
|
||||
counter = this._counter = iv.slice(0);
|
||||
|
||||
// Remove IV for subsequent blocks
|
||||
this._iv = undefined;
|
||||
}
|
||||
|
||||
incCounter(counter);
|
||||
|
||||
var keystream = counter.slice(0);
|
||||
cipher.encryptBlock(keystream, 0);
|
||||
|
||||
// Encrypt
|
||||
for (var i = 0; i < blockSize; i++) {
|
||||
words[offset + i] ^= keystream[i];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
CTRGladman.Decryptor = Encryptor;
|
||||
|
||||
return CTRGladman;
|
||||
}());
|
||||
|
||||
|
||||
|
||||
|
||||
return CryptoJS.mode.CTRGladman;
|
||||
|
||||
}));
|
@ -0,0 +1,58 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./cipher-core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./cipher-core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
/**
|
||||
* Counter block mode.
|
||||
*/
|
||||
CryptoJS.mode.CTR = (function () {
|
||||
var CTR = CryptoJS.lib.BlockCipherMode.extend();
|
||||
|
||||
var Encryptor = CTR.Encryptor = CTR.extend({
|
||||
processBlock: function (words, offset) {
|
||||
// Shortcuts
|
||||
var cipher = this._cipher
|
||||
var blockSize = cipher.blockSize;
|
||||
var iv = this._iv;
|
||||
var counter = this._counter;
|
||||
|
||||
// Generate keystream
|
||||
if (iv) {
|
||||
counter = this._counter = iv.slice(0);
|
||||
|
||||
// Remove IV for subsequent blocks
|
||||
this._iv = undefined;
|
||||
}
|
||||
var keystream = counter.slice(0);
|
||||
cipher.encryptBlock(keystream, 0);
|
||||
|
||||
// Increment counter
|
||||
counter[blockSize - 1] = (counter[blockSize - 1] + 1) | 0
|
||||
|
||||
// Encrypt
|
||||
for (var i = 0; i < blockSize; i++) {
|
||||
words[offset + i] ^= keystream[i];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
CTR.Decryptor = Encryptor;
|
||||
|
||||
return CTR;
|
||||
}());
|
||||
|
||||
|
||||
return CryptoJS.mode.CTR;
|
||||
|
||||
}));
|
@ -0,0 +1,40 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./cipher-core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./cipher-core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
/**
|
||||
* Electronic Codebook block mode.
|
||||
*/
|
||||
CryptoJS.mode.ECB = (function () {
|
||||
var ECB = CryptoJS.lib.BlockCipherMode.extend();
|
||||
|
||||
ECB.Encryptor = ECB.extend({
|
||||
processBlock: function (words, offset) {
|
||||
this._cipher.encryptBlock(words, offset);
|
||||
}
|
||||
});
|
||||
|
||||
ECB.Decryptor = ECB.extend({
|
||||
processBlock: function (words, offset) {
|
||||
this._cipher.decryptBlock(words, offset);
|
||||
}
|
||||
});
|
||||
|
||||
return ECB;
|
||||
}());
|
||||
|
||||
|
||||
return CryptoJS.mode.ECB;
|
||||
|
||||
}));
|
@ -0,0 +1,54 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./cipher-core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./cipher-core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
/**
|
||||
* Output Feedback block mode.
|
||||
*/
|
||||
CryptoJS.mode.OFB = (function () {
|
||||
var OFB = CryptoJS.lib.BlockCipherMode.extend();
|
||||
|
||||
var Encryptor = OFB.Encryptor = OFB.extend({
|
||||
processBlock: function (words, offset) {
|
||||
// Shortcuts
|
||||
var cipher = this._cipher
|
||||
var blockSize = cipher.blockSize;
|
||||
var iv = this._iv;
|
||||
var keystream = this._keystream;
|
||||
|
||||
// Generate keystream
|
||||
if (iv) {
|
||||
keystream = this._keystream = iv.slice(0);
|
||||
|
||||
// Remove IV for subsequent blocks
|
||||
this._iv = undefined;
|
||||
}
|
||||
cipher.encryptBlock(keystream, 0);
|
||||
|
||||
// Encrypt
|
||||
for (var i = 0; i < blockSize; i++) {
|
||||
words[offset + i] ^= keystream[i];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
OFB.Decryptor = Encryptor;
|
||||
|
||||
return OFB;
|
||||
}());
|
||||
|
||||
|
||||
return CryptoJS.mode.OFB;
|
||||
|
||||
}));
|
@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "crypto-js",
|
||||
"version": "4.1.1",
|
||||
"description": "JavaScript library of crypto standards.",
|
||||
"license": "MIT",
|
||||
"author": {
|
||||
"name": "Evan Vosberg",
|
||||
"url": "http://github.com/evanvosberg"
|
||||
},
|
||||
"homepage": "http://github.com/brix/crypto-js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/brix/crypto-js.git"
|
||||
},
|
||||
"keywords": [
|
||||
"security",
|
||||
"crypto",
|
||||
"Hash",
|
||||
"MD5",
|
||||
"SHA1",
|
||||
"SHA-1",
|
||||
"SHA256",
|
||||
"SHA-256",
|
||||
"RC4",
|
||||
"Rabbit",
|
||||
"AES",
|
||||
"DES",
|
||||
"PBKDF2",
|
||||
"HMAC",
|
||||
"OFB",
|
||||
"CFB",
|
||||
"CTR",
|
||||
"CBC",
|
||||
"Base64",
|
||||
"Base64url"
|
||||
],
|
||||
"main": "index.js",
|
||||
"dependencies": {},
|
||||
"browser": {
|
||||
"crypto": false
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./cipher-core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./cipher-core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
/**
|
||||
* ANSI X.923 padding strategy.
|
||||
*/
|
||||
CryptoJS.pad.AnsiX923 = {
|
||||
pad: function (data, blockSize) {
|
||||
// Shortcuts
|
||||
var dataSigBytes = data.sigBytes;
|
||||
var blockSizeBytes = blockSize * 4;
|
||||
|
||||
// Count padding bytes
|
||||
var nPaddingBytes = blockSizeBytes - dataSigBytes % blockSizeBytes;
|
||||
|
||||
// Compute last byte position
|
||||
var lastBytePos = dataSigBytes + nPaddingBytes - 1;
|
||||
|
||||
// Pad
|
||||
data.clamp();
|
||||
data.words[lastBytePos >>> 2] |= nPaddingBytes << (24 - (lastBytePos % 4) * 8);
|
||||
data.sigBytes += nPaddingBytes;
|
||||
},
|
||||
|
||||
unpad: function (data) {
|
||||
// Get number of padding bytes from last byte
|
||||
var nPaddingBytes = data.words[(data.sigBytes - 1) >>> 2] & 0xff;
|
||||
|
||||
// Remove padding
|
||||
data.sigBytes -= nPaddingBytes;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return CryptoJS.pad.Ansix923;
|
||||
|
||||
}));
|
@ -0,0 +1,44 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./cipher-core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./cipher-core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
/**
|
||||
* ISO 10126 padding strategy.
|
||||
*/
|
||||
CryptoJS.pad.Iso10126 = {
|
||||
pad: function (data, blockSize) {
|
||||
// Shortcut
|
||||
var blockSizeBytes = blockSize * 4;
|
||||
|
||||
// Count padding bytes
|
||||
var nPaddingBytes = blockSizeBytes - data.sigBytes % blockSizeBytes;
|
||||
|
||||
// Pad
|
||||
data.concat(CryptoJS.lib.WordArray.random(nPaddingBytes - 1)).
|
||||
concat(CryptoJS.lib.WordArray.create([nPaddingBytes << 24], 1));
|
||||
},
|
||||
|
||||
unpad: function (data) {
|
||||
// Get number of padding bytes from last byte
|
||||
var nPaddingBytes = data.words[(data.sigBytes - 1) >>> 2] & 0xff;
|
||||
|
||||
// Remove padding
|
||||
data.sigBytes -= nPaddingBytes;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return CryptoJS.pad.Iso10126;
|
||||
|
||||
}));
|
@ -0,0 +1,40 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./cipher-core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./cipher-core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
/**
|
||||
* ISO/IEC 9797-1 Padding Method 2.
|
||||
*/
|
||||
CryptoJS.pad.Iso97971 = {
|
||||
pad: function (data, blockSize) {
|
||||
// Add 0x80 byte
|
||||
data.concat(CryptoJS.lib.WordArray.create([0x80000000], 1));
|
||||
|
||||
// Zero pad the rest
|
||||
CryptoJS.pad.ZeroPadding.pad(data, blockSize);
|
||||
},
|
||||
|
||||
unpad: function (data) {
|
||||
// Remove zero padding
|
||||
CryptoJS.pad.ZeroPadding.unpad(data);
|
||||
|
||||
// Remove one more byte -- the 0x80 byte
|
||||
data.sigBytes--;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return CryptoJS.pad.Iso97971;
|
||||
|
||||
}));
|
@ -0,0 +1,30 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./cipher-core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./cipher-core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
/**
|
||||
* A noop padding strategy.
|
||||
*/
|
||||
CryptoJS.pad.NoPadding = {
|
||||
pad: function () {
|
||||
},
|
||||
|
||||
unpad: function () {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return CryptoJS.pad.NoPadding;
|
||||
|
||||
}));
|
@ -0,0 +1,18 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./cipher-core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./cipher-core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
return CryptoJS.pad.Pkcs7;
|
||||
|
||||
}));
|
@ -0,0 +1,47 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./cipher-core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./cipher-core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
/**
|
||||
* Zero padding strategy.
|
||||
*/
|
||||
CryptoJS.pad.ZeroPadding = {
|
||||
pad: function (data, blockSize) {
|
||||
// Shortcut
|
||||
var blockSizeBytes = blockSize * 4;
|
||||
|
||||
// Pad
|
||||
data.clamp();
|
||||
data.sigBytes += blockSizeBytes - ((data.sigBytes % blockSizeBytes) || blockSizeBytes);
|
||||
},
|
||||
|
||||
unpad: function (data) {
|
||||
// Shortcut
|
||||
var dataWords = data.words;
|
||||
|
||||
// Unpad
|
||||
var i = data.sigBytes - 1;
|
||||
for (var i = data.sigBytes - 1; i >= 0; i--) {
|
||||
if (((dataWords[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff)) {
|
||||
data.sigBytes = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return CryptoJS.pad.ZeroPadding;
|
||||
|
||||
}));
|
@ -0,0 +1,145 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./sha1"), require("./hmac"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./sha1", "./hmac"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
(function () {
|
||||
// Shortcuts
|
||||
var C = CryptoJS;
|
||||
var C_lib = C.lib;
|
||||
var Base = C_lib.Base;
|
||||
var WordArray = C_lib.WordArray;
|
||||
var C_algo = C.algo;
|
||||
var SHA1 = C_algo.SHA1;
|
||||
var HMAC = C_algo.HMAC;
|
||||
|
||||
/**
|
||||
* Password-Based Key Derivation Function 2 algorithm.
|
||||
*/
|
||||
var PBKDF2 = C_algo.PBKDF2 = Base.extend({
|
||||
/**
|
||||
* Configuration options.
|
||||
*
|
||||
* @property {number} keySize The key size in words to generate. Default: 4 (128 bits)
|
||||
* @property {Hasher} hasher The hasher to use. Default: SHA1
|
||||
* @property {number} iterations The number of iterations to perform. Default: 1
|
||||
*/
|
||||
cfg: Base.extend({
|
||||
keySize: 128/32,
|
||||
hasher: SHA1,
|
||||
iterations: 1
|
||||
}),
|
||||
|
||||
/**
|
||||
* Initializes a newly created key derivation function.
|
||||
*
|
||||
* @param {Object} cfg (Optional) The configuration options to use for the derivation.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var kdf = CryptoJS.algo.PBKDF2.create();
|
||||
* var kdf = CryptoJS.algo.PBKDF2.create({ keySize: 8 });
|
||||
* var kdf = CryptoJS.algo.PBKDF2.create({ keySize: 8, iterations: 1000 });
|
||||
*/
|
||||
init: function (cfg) {
|
||||
this.cfg = this.cfg.extend(cfg);
|
||||
},
|
||||
|
||||
/**
|
||||
* Computes the Password-Based Key Derivation Function 2.
|
||||
*
|
||||
* @param {WordArray|string} password The password.
|
||||
* @param {WordArray|string} salt A salt.
|
||||
*
|
||||
* @return {WordArray} The derived key.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var key = kdf.compute(password, salt);
|
||||
*/
|
||||
compute: function (password, salt) {
|
||||
// Shortcut
|
||||
var cfg = this.cfg;
|
||||
|
||||
// Init HMAC
|
||||
var hmac = HMAC.create(cfg.hasher, password);
|
||||
|
||||
// Initial values
|
||||
var derivedKey = WordArray.create();
|
||||
var blockIndex = WordArray.create([0x00000001]);
|
||||
|
||||
// Shortcuts
|
||||
var derivedKeyWords = derivedKey.words;
|
||||
var blockIndexWords = blockIndex.words;
|
||||
var keySize = cfg.keySize;
|
||||
var iterations = cfg.iterations;
|
||||
|
||||
// Generate key
|
||||
while (derivedKeyWords.length < keySize) {
|
||||
var block = hmac.update(salt).finalize(blockIndex);
|
||||
hmac.reset();
|
||||
|
||||
// Shortcuts
|
||||
var blockWords = block.words;
|
||||
var blockWordsLength = blockWords.length;
|
||||
|
||||
// Iterations
|
||||
var intermediate = block;
|
||||
for (var i = 1; i < iterations; i++) {
|
||||
intermediate = hmac.finalize(intermediate);
|
||||
hmac.reset();
|
||||
|
||||
// Shortcut
|
||||
var intermediateWords = intermediate.words;
|
||||
|
||||
// XOR intermediate with block
|
||||
for (var j = 0; j < blockWordsLength; j++) {
|
||||
blockWords[j] ^= intermediateWords[j];
|
||||
}
|
||||
}
|
||||
|
||||
derivedKey.concat(block);
|
||||
blockIndexWords[0]++;
|
||||
}
|
||||
derivedKey.sigBytes = keySize * 4;
|
||||
|
||||
return derivedKey;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Computes the Password-Based Key Derivation Function 2.
|
||||
*
|
||||
* @param {WordArray|string} password The password.
|
||||
* @param {WordArray|string} salt A salt.
|
||||
* @param {Object} cfg (Optional) The configuration options to use for this computation.
|
||||
*
|
||||
* @return {WordArray} The derived key.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var key = CryptoJS.PBKDF2(password, salt);
|
||||
* var key = CryptoJS.PBKDF2(password, salt, { keySize: 8 });
|
||||
* var key = CryptoJS.PBKDF2(password, salt, { keySize: 8, iterations: 1000 });
|
||||
*/
|
||||
C.PBKDF2 = function (password, salt, cfg) {
|
||||
return PBKDF2.create(cfg).compute(password, salt);
|
||||
};
|
||||
}());
|
||||
|
||||
|
||||
return CryptoJS.PBKDF2;
|
||||
|
||||
}));
|
@ -0,0 +1,190 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./enc-base64"), require("./md5"), require("./evpkdf"), require("./cipher-core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./enc-base64", "./md5", "./evpkdf", "./cipher-core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
(function () {
|
||||
// Shortcuts
|
||||
var C = CryptoJS;
|
||||
var C_lib = C.lib;
|
||||
var StreamCipher = C_lib.StreamCipher;
|
||||
var C_algo = C.algo;
|
||||
|
||||
// Reusable objects
|
||||
var S = [];
|
||||
var C_ = [];
|
||||
var G = [];
|
||||
|
||||
/**
|
||||
* Rabbit stream cipher algorithm.
|
||||
*
|
||||
* This is a legacy version that neglected to convert the key to little-endian.
|
||||
* This error doesn't affect the cipher's security,
|
||||
* but it does affect its compatibility with other implementations.
|
||||
*/
|
||||
var RabbitLegacy = C_algo.RabbitLegacy = StreamCipher.extend({
|
||||
_doReset: function () {
|
||||
// Shortcuts
|
||||
var K = this._key.words;
|
||||
var iv = this.cfg.iv;
|
||||
|
||||
// Generate initial state values
|
||||
var X = this._X = [
|
||||
K[0], (K[3] << 16) | (K[2] >>> 16),
|
||||
K[1], (K[0] << 16) | (K[3] >>> 16),
|
||||
K[2], (K[1] << 16) | (K[0] >>> 16),
|
||||
K[3], (K[2] << 16) | (K[1] >>> 16)
|
||||
];
|
||||
|
||||
// Generate initial counter values
|
||||
var C = this._C = [
|
||||
(K[2] << 16) | (K[2] >>> 16), (K[0] & 0xffff0000) | (K[1] & 0x0000ffff),
|
||||
(K[3] << 16) | (K[3] >>> 16), (K[1] & 0xffff0000) | (K[2] & 0x0000ffff),
|
||||
(K[0] << 16) | (K[0] >>> 16), (K[2] & 0xffff0000) | (K[3] & 0x0000ffff),
|
||||
(K[1] << 16) | (K[1] >>> 16), (K[3] & 0xffff0000) | (K[0] & 0x0000ffff)
|
||||
];
|
||||
|
||||
// Carry bit
|
||||
this._b = 0;
|
||||
|
||||
// Iterate the system four times
|
||||
for (var i = 0; i < 4; i++) {
|
||||
nextState.call(this);
|
||||
}
|
||||
|
||||
// Modify the counters
|
||||
for (var i = 0; i < 8; i++) {
|
||||
C[i] ^= X[(i + 4) & 7];
|
||||
}
|
||||
|
||||
// IV setup
|
||||
if (iv) {
|
||||
// Shortcuts
|
||||
var IV = iv.words;
|
||||
var IV_0 = IV[0];
|
||||
var IV_1 = IV[1];
|
||||
|
||||
// Generate four subvectors
|
||||
var i0 = (((IV_0 << 8) | (IV_0 >>> 24)) & 0x00ff00ff) | (((IV_0 << 24) | (IV_0 >>> 8)) & 0xff00ff00);
|
||||
var i2 = (((IV_1 << 8) | (IV_1 >>> 24)) & 0x00ff00ff) | (((IV_1 << 24) | (IV_1 >>> 8)) & 0xff00ff00);
|
||||
var i1 = (i0 >>> 16) | (i2 & 0xffff0000);
|
||||
var i3 = (i2 << 16) | (i0 & 0x0000ffff);
|
||||
|
||||
// Modify counter values
|
||||
C[0] ^= i0;
|
||||
C[1] ^= i1;
|
||||
C[2] ^= i2;
|
||||
C[3] ^= i3;
|
||||
C[4] ^= i0;
|
||||
C[5] ^= i1;
|
||||
C[6] ^= i2;
|
||||
C[7] ^= i3;
|
||||
|
||||
// Iterate the system four times
|
||||
for (var i = 0; i < 4; i++) {
|
||||
nextState.call(this);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_doProcessBlock: function (M, offset) {
|
||||
// Shortcut
|
||||
var X = this._X;
|
||||
|
||||
// Iterate the system
|
||||
nextState.call(this);
|
||||
|
||||
// Generate four keystream words
|
||||
S[0] = X[0] ^ (X[5] >>> 16) ^ (X[3] << 16);
|
||||
S[1] = X[2] ^ (X[7] >>> 16) ^ (X[5] << 16);
|
||||
S[2] = X[4] ^ (X[1] >>> 16) ^ (X[7] << 16);
|
||||
S[3] = X[6] ^ (X[3] >>> 16) ^ (X[1] << 16);
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
// Swap endian
|
||||
S[i] = (((S[i] << 8) | (S[i] >>> 24)) & 0x00ff00ff) |
|
||||
(((S[i] << 24) | (S[i] >>> 8)) & 0xff00ff00);
|
||||
|
||||
// Encrypt
|
||||
M[offset + i] ^= S[i];
|
||||
}
|
||||
},
|
||||
|
||||
blockSize: 128/32,
|
||||
|
||||
ivSize: 64/32
|
||||
});
|
||||
|
||||
function nextState() {
|
||||
// Shortcuts
|
||||
var X = this._X;
|
||||
var C = this._C;
|
||||
|
||||
// Save old counter values
|
||||
for (var i = 0; i < 8; i++) {
|
||||
C_[i] = C[i];
|
||||
}
|
||||
|
||||
// Calculate new counter values
|
||||
C[0] = (C[0] + 0x4d34d34d + this._b) | 0;
|
||||
C[1] = (C[1] + 0xd34d34d3 + ((C[0] >>> 0) < (C_[0] >>> 0) ? 1 : 0)) | 0;
|
||||
C[2] = (C[2] + 0x34d34d34 + ((C[1] >>> 0) < (C_[1] >>> 0) ? 1 : 0)) | 0;
|
||||
C[3] = (C[3] + 0x4d34d34d + ((C[2] >>> 0) < (C_[2] >>> 0) ? 1 : 0)) | 0;
|
||||
C[4] = (C[4] + 0xd34d34d3 + ((C[3] >>> 0) < (C_[3] >>> 0) ? 1 : 0)) | 0;
|
||||
C[5] = (C[5] + 0x34d34d34 + ((C[4] >>> 0) < (C_[4] >>> 0) ? 1 : 0)) | 0;
|
||||
C[6] = (C[6] + 0x4d34d34d + ((C[5] >>> 0) < (C_[5] >>> 0) ? 1 : 0)) | 0;
|
||||
C[7] = (C[7] + 0xd34d34d3 + ((C[6] >>> 0) < (C_[6] >>> 0) ? 1 : 0)) | 0;
|
||||
this._b = (C[7] >>> 0) < (C_[7] >>> 0) ? 1 : 0;
|
||||
|
||||
// Calculate the g-values
|
||||
for (var i = 0; i < 8; i++) {
|
||||
var gx = X[i] + C[i];
|
||||
|
||||
// Construct high and low argument for squaring
|
||||
var ga = gx & 0xffff;
|
||||
var gb = gx >>> 16;
|
||||
|
||||
// Calculate high and low result of squaring
|
||||
var gh = ((((ga * ga) >>> 17) + ga * gb) >>> 15) + gb * gb;
|
||||
var gl = (((gx & 0xffff0000) * gx) | 0) + (((gx & 0x0000ffff) * gx) | 0);
|
||||
|
||||
// High XOR low
|
||||
G[i] = gh ^ gl;
|
||||
}
|
||||
|
||||
// Calculate new state values
|
||||
X[0] = (G[0] + ((G[7] << 16) | (G[7] >>> 16)) + ((G[6] << 16) | (G[6] >>> 16))) | 0;
|
||||
X[1] = (G[1] + ((G[0] << 8) | (G[0] >>> 24)) + G[7]) | 0;
|
||||
X[2] = (G[2] + ((G[1] << 16) | (G[1] >>> 16)) + ((G[0] << 16) | (G[0] >>> 16))) | 0;
|
||||
X[3] = (G[3] + ((G[2] << 8) | (G[2] >>> 24)) + G[1]) | 0;
|
||||
X[4] = (G[4] + ((G[3] << 16) | (G[3] >>> 16)) + ((G[2] << 16) | (G[2] >>> 16))) | 0;
|
||||
X[5] = (G[5] + ((G[4] << 8) | (G[4] >>> 24)) + G[3]) | 0;
|
||||
X[6] = (G[6] + ((G[5] << 16) | (G[5] >>> 16)) + ((G[4] << 16) | (G[4] >>> 16))) | 0;
|
||||
X[7] = (G[7] + ((G[6] << 8) | (G[6] >>> 24)) + G[5]) | 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut functions to the cipher's object interface.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var ciphertext = CryptoJS.RabbitLegacy.encrypt(message, key, cfg);
|
||||
* var plaintext = CryptoJS.RabbitLegacy.decrypt(ciphertext, key, cfg);
|
||||
*/
|
||||
C.RabbitLegacy = StreamCipher._createHelper(RabbitLegacy);
|
||||
}());
|
||||
|
||||
|
||||
return CryptoJS.RabbitLegacy;
|
||||
|
||||
}));
|
@ -0,0 +1,192 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./enc-base64"), require("./md5"), require("./evpkdf"), require("./cipher-core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./enc-base64", "./md5", "./evpkdf", "./cipher-core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
(function () {
|
||||
// Shortcuts
|
||||
var C = CryptoJS;
|
||||
var C_lib = C.lib;
|
||||
var StreamCipher = C_lib.StreamCipher;
|
||||
var C_algo = C.algo;
|
||||
|
||||
// Reusable objects
|
||||
var S = [];
|
||||
var C_ = [];
|
||||
var G = [];
|
||||
|
||||
/**
|
||||
* Rabbit stream cipher algorithm
|
||||
*/
|
||||
var Rabbit = C_algo.Rabbit = StreamCipher.extend({
|
||||
_doReset: function () {
|
||||
// Shortcuts
|
||||
var K = this._key.words;
|
||||
var iv = this.cfg.iv;
|
||||
|
||||
// Swap endian
|
||||
for (var i = 0; i < 4; i++) {
|
||||
K[i] = (((K[i] << 8) | (K[i] >>> 24)) & 0x00ff00ff) |
|
||||
(((K[i] << 24) | (K[i] >>> 8)) & 0xff00ff00);
|
||||
}
|
||||
|
||||
// Generate initial state values
|
||||
var X = this._X = [
|
||||
K[0], (K[3] << 16) | (K[2] >>> 16),
|
||||
K[1], (K[0] << 16) | (K[3] >>> 16),
|
||||
K[2], (K[1] << 16) | (K[0] >>> 16),
|
||||
K[3], (K[2] << 16) | (K[1] >>> 16)
|
||||
];
|
||||
|
||||
// Generate initial counter values
|
||||
var C = this._C = [
|
||||
(K[2] << 16) | (K[2] >>> 16), (K[0] & 0xffff0000) | (K[1] & 0x0000ffff),
|
||||
(K[3] << 16) | (K[3] >>> 16), (K[1] & 0xffff0000) | (K[2] & 0x0000ffff),
|
||||
(K[0] << 16) | (K[0] >>> 16), (K[2] & 0xffff0000) | (K[3] & 0x0000ffff),
|
||||
(K[1] << 16) | (K[1] >>> 16), (K[3] & 0xffff0000) | (K[0] & 0x0000ffff)
|
||||
];
|
||||
|
||||
// Carry bit
|
||||
this._b = 0;
|
||||
|
||||
// Iterate the system four times
|
||||
for (var i = 0; i < 4; i++) {
|
||||
nextState.call(this);
|
||||
}
|
||||
|
||||
// Modify the counters
|
||||
for (var i = 0; i < 8; i++) {
|
||||
C[i] ^= X[(i + 4) & 7];
|
||||
}
|
||||
|
||||
// IV setup
|
||||
if (iv) {
|
||||
// Shortcuts
|
||||
var IV = iv.words;
|
||||
var IV_0 = IV[0];
|
||||
var IV_1 = IV[1];
|
||||
|
||||
// Generate four subvectors
|
||||
var i0 = (((IV_0 << 8) | (IV_0 >>> 24)) & 0x00ff00ff) | (((IV_0 << 24) | (IV_0 >>> 8)) & 0xff00ff00);
|
||||
var i2 = (((IV_1 << 8) | (IV_1 >>> 24)) & 0x00ff00ff) | (((IV_1 << 24) | (IV_1 >>> 8)) & 0xff00ff00);
|
||||
var i1 = (i0 >>> 16) | (i2 & 0xffff0000);
|
||||
var i3 = (i2 << 16) | (i0 & 0x0000ffff);
|
||||
|
||||
// Modify counter values
|
||||
C[0] ^= i0;
|
||||
C[1] ^= i1;
|
||||
C[2] ^= i2;
|
||||
C[3] ^= i3;
|
||||
C[4] ^= i0;
|
||||
C[5] ^= i1;
|
||||
C[6] ^= i2;
|
||||
C[7] ^= i3;
|
||||
|
||||
// Iterate the system four times
|
||||
for (var i = 0; i < 4; i++) {
|
||||
nextState.call(this);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_doProcessBlock: function (M, offset) {
|
||||
// Shortcut
|
||||
var X = this._X;
|
||||
|
||||
// Iterate the system
|
||||
nextState.call(this);
|
||||
|
||||
// Generate four keystream words
|
||||
S[0] = X[0] ^ (X[5] >>> 16) ^ (X[3] << 16);
|
||||
S[1] = X[2] ^ (X[7] >>> 16) ^ (X[5] << 16);
|
||||
S[2] = X[4] ^ (X[1] >>> 16) ^ (X[7] << 16);
|
||||
S[3] = X[6] ^ (X[3] >>> 16) ^ (X[1] << 16);
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
// Swap endian
|
||||
S[i] = (((S[i] << 8) | (S[i] >>> 24)) & 0x00ff00ff) |
|
||||
(((S[i] << 24) | (S[i] >>> 8)) & 0xff00ff00);
|
||||
|
||||
// Encrypt
|
||||
M[offset + i] ^= S[i];
|
||||
}
|
||||
},
|
||||
|
||||
blockSize: 128/32,
|
||||
|
||||
ivSize: 64/32
|
||||
});
|
||||
|
||||
function nextState() {
|
||||
// Shortcuts
|
||||
var X = this._X;
|
||||
var C = this._C;
|
||||
|
||||
// Save old counter values
|
||||
for (var i = 0; i < 8; i++) {
|
||||
C_[i] = C[i];
|
||||
}
|
||||
|
||||
// Calculate new counter values
|
||||
C[0] = (C[0] + 0x4d34d34d + this._b) | 0;
|
||||
C[1] = (C[1] + 0xd34d34d3 + ((C[0] >>> 0) < (C_[0] >>> 0) ? 1 : 0)) | 0;
|
||||
C[2] = (C[2] + 0x34d34d34 + ((C[1] >>> 0) < (C_[1] >>> 0) ? 1 : 0)) | 0;
|
||||
C[3] = (C[3] + 0x4d34d34d + ((C[2] >>> 0) < (C_[2] >>> 0) ? 1 : 0)) | 0;
|
||||
C[4] = (C[4] + 0xd34d34d3 + ((C[3] >>> 0) < (C_[3] >>> 0) ? 1 : 0)) | 0;
|
||||
C[5] = (C[5] + 0x34d34d34 + ((C[4] >>> 0) < (C_[4] >>> 0) ? 1 : 0)) | 0;
|
||||
C[6] = (C[6] + 0x4d34d34d + ((C[5] >>> 0) < (C_[5] >>> 0) ? 1 : 0)) | 0;
|
||||
C[7] = (C[7] + 0xd34d34d3 + ((C[6] >>> 0) < (C_[6] >>> 0) ? 1 : 0)) | 0;
|
||||
this._b = (C[7] >>> 0) < (C_[7] >>> 0) ? 1 : 0;
|
||||
|
||||
// Calculate the g-values
|
||||
for (var i = 0; i < 8; i++) {
|
||||
var gx = X[i] + C[i];
|
||||
|
||||
// Construct high and low argument for squaring
|
||||
var ga = gx & 0xffff;
|
||||
var gb = gx >>> 16;
|
||||
|
||||
// Calculate high and low result of squaring
|
||||
var gh = ((((ga * ga) >>> 17) + ga * gb) >>> 15) + gb * gb;
|
||||
var gl = (((gx & 0xffff0000) * gx) | 0) + (((gx & 0x0000ffff) * gx) | 0);
|
||||
|
||||
// High XOR low
|
||||
G[i] = gh ^ gl;
|
||||
}
|
||||
|
||||
// Calculate new state values
|
||||
X[0] = (G[0] + ((G[7] << 16) | (G[7] >>> 16)) + ((G[6] << 16) | (G[6] >>> 16))) | 0;
|
||||
X[1] = (G[1] + ((G[0] << 8) | (G[0] >>> 24)) + G[7]) | 0;
|
||||
X[2] = (G[2] + ((G[1] << 16) | (G[1] >>> 16)) + ((G[0] << 16) | (G[0] >>> 16))) | 0;
|
||||
X[3] = (G[3] + ((G[2] << 8) | (G[2] >>> 24)) + G[1]) | 0;
|
||||
X[4] = (G[4] + ((G[3] << 16) | (G[3] >>> 16)) + ((G[2] << 16) | (G[2] >>> 16))) | 0;
|
||||
X[5] = (G[5] + ((G[4] << 8) | (G[4] >>> 24)) + G[3]) | 0;
|
||||
X[6] = (G[6] + ((G[5] << 16) | (G[5] >>> 16)) + ((G[4] << 16) | (G[4] >>> 16))) | 0;
|
||||
X[7] = (G[7] + ((G[6] << 8) | (G[6] >>> 24)) + G[5]) | 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut functions to the cipher's object interface.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var ciphertext = CryptoJS.Rabbit.encrypt(message, key, cfg);
|
||||
* var plaintext = CryptoJS.Rabbit.decrypt(ciphertext, key, cfg);
|
||||
*/
|
||||
C.Rabbit = StreamCipher._createHelper(Rabbit);
|
||||
}());
|
||||
|
||||
|
||||
return CryptoJS.Rabbit;
|
||||
|
||||
}));
|
@ -0,0 +1,139 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./enc-base64"), require("./md5"), require("./evpkdf"), require("./cipher-core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./enc-base64", "./md5", "./evpkdf", "./cipher-core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
(function () {
|
||||
// Shortcuts
|
||||
var C = CryptoJS;
|
||||
var C_lib = C.lib;
|
||||
var StreamCipher = C_lib.StreamCipher;
|
||||
var C_algo = C.algo;
|
||||
|
||||
/**
|
||||
* RC4 stream cipher algorithm.
|
||||
*/
|
||||
var RC4 = C_algo.RC4 = StreamCipher.extend({
|
||||
_doReset: function () {
|
||||
// Shortcuts
|
||||
var key = this._key;
|
||||
var keyWords = key.words;
|
||||
var keySigBytes = key.sigBytes;
|
||||
|
||||
// Init sbox
|
||||
var S = this._S = [];
|
||||
for (var i = 0; i < 256; i++) {
|
||||
S[i] = i;
|
||||
}
|
||||
|
||||
// Key setup
|
||||
for (var i = 0, j = 0; i < 256; i++) {
|
||||
var keyByteIndex = i % keySigBytes;
|
||||
var keyByte = (keyWords[keyByteIndex >>> 2] >>> (24 - (keyByteIndex % 4) * 8)) & 0xff;
|
||||
|
||||
j = (j + S[i] + keyByte) % 256;
|
||||
|
||||
// Swap
|
||||
var t = S[i];
|
||||
S[i] = S[j];
|
||||
S[j] = t;
|
||||
}
|
||||
|
||||
// Counters
|
||||
this._i = this._j = 0;
|
||||
},
|
||||
|
||||
_doProcessBlock: function (M, offset) {
|
||||
M[offset] ^= generateKeystreamWord.call(this);
|
||||
},
|
||||
|
||||
keySize: 256/32,
|
||||
|
||||
ivSize: 0
|
||||
});
|
||||
|
||||
function generateKeystreamWord() {
|
||||
// Shortcuts
|
||||
var S = this._S;
|
||||
var i = this._i;
|
||||
var j = this._j;
|
||||
|
||||
// Generate keystream word
|
||||
var keystreamWord = 0;
|
||||
for (var n = 0; n < 4; n++) {
|
||||
i = (i + 1) % 256;
|
||||
j = (j + S[i]) % 256;
|
||||
|
||||
// Swap
|
||||
var t = S[i];
|
||||
S[i] = S[j];
|
||||
S[j] = t;
|
||||
|
||||
keystreamWord |= S[(S[i] + S[j]) % 256] << (24 - n * 8);
|
||||
}
|
||||
|
||||
// Update counters
|
||||
this._i = i;
|
||||
this._j = j;
|
||||
|
||||
return keystreamWord;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut functions to the cipher's object interface.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var ciphertext = CryptoJS.RC4.encrypt(message, key, cfg);
|
||||
* var plaintext = CryptoJS.RC4.decrypt(ciphertext, key, cfg);
|
||||
*/
|
||||
C.RC4 = StreamCipher._createHelper(RC4);
|
||||
|
||||
/**
|
||||
* Modified RC4 stream cipher algorithm.
|
||||
*/
|
||||
var RC4Drop = C_algo.RC4Drop = RC4.extend({
|
||||
/**
|
||||
* Configuration options.
|
||||
*
|
||||
* @property {number} drop The number of keystream words to drop. Default 192
|
||||
*/
|
||||
cfg: RC4.cfg.extend({
|
||||
drop: 192
|
||||
}),
|
||||
|
||||
_doReset: function () {
|
||||
RC4._doReset.call(this);
|
||||
|
||||
// Drop
|
||||
for (var i = this.cfg.drop; i > 0; i--) {
|
||||
generateKeystreamWord.call(this);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Shortcut functions to the cipher's object interface.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var ciphertext = CryptoJS.RC4Drop.encrypt(message, key, cfg);
|
||||
* var plaintext = CryptoJS.RC4Drop.decrypt(ciphertext, key, cfg);
|
||||
*/
|
||||
C.RC4Drop = StreamCipher._createHelper(RC4Drop);
|
||||
}());
|
||||
|
||||
|
||||
return CryptoJS.RC4;
|
||||
|
||||
}));
|
@ -0,0 +1,267 @@
|
||||
;(function (root, factory) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
/** @preserve
|
||||
(c) 2012 by Cédric Mesnil. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
(function (Math) {
|
||||
// Shortcuts
|
||||
var C = CryptoJS;
|
||||
var C_lib = C.lib;
|
||||
var WordArray = C_lib.WordArray;
|
||||
var Hasher = C_lib.Hasher;
|
||||
var C_algo = C.algo;
|
||||
|
||||
// Constants table
|
||||
var _zl = WordArray.create([
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8,
|
||||
3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12,
|
||||
1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2,
|
||||
4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13]);
|
||||
var _zr = WordArray.create([
|
||||
5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12,
|
||||
6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2,
|
||||
15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13,
|
||||
8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14,
|
||||
12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11]);
|
||||
var _sl = WordArray.create([
|
||||
11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8,
|
||||
7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12,
|
||||
11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5,
|
||||
11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12,
|
||||
9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6 ]);
|
||||
var _sr = WordArray.create([
|
||||
8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6,
|
||||
9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11,
|
||||
9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5,
|
||||
15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8,
|
||||
8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11 ]);
|
||||
|
||||
var _hl = WordArray.create([ 0x00000000, 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xA953FD4E]);
|
||||
var _hr = WordArray.create([ 0x50A28BE6, 0x5C4DD124, 0x6D703EF3, 0x7A6D76E9, 0x00000000]);
|
||||
|
||||
/**
|
||||
* RIPEMD160 hash algorithm.
|
||||
*/
|
||||
var RIPEMD160 = C_algo.RIPEMD160 = Hasher.extend({
|
||||
_doReset: function () {
|
||||
this._hash = WordArray.create([0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0]);
|
||||
},
|
||||
|
||||
_doProcessBlock: function (M, offset) {
|
||||
|
||||
// Swap endian
|
||||
for (var i = 0; i < 16; i++) {
|
||||
// Shortcuts
|
||||
var offset_i = offset + i;
|
||||
var M_offset_i = M[offset_i];
|
||||
|
||||
// Swap
|
||||
M[offset_i] = (
|
||||
(((M_offset_i << 8) | (M_offset_i >>> 24)) & 0x00ff00ff) |
|
||||
(((M_offset_i << 24) | (M_offset_i >>> 8)) & 0xff00ff00)
|
||||
);
|
||||
}
|
||||
// Shortcut
|
||||
var H = this._hash.words;
|
||||
var hl = _hl.words;
|
||||
var hr = _hr.words;
|
||||
var zl = _zl.words;
|
||||
var zr = _zr.words;
|
||||
var sl = _sl.words;
|
||||
var sr = _sr.words;
|
||||
|
||||
// Working variables
|
||||
var al, bl, cl, dl, el;
|
||||
var ar, br, cr, dr, er;
|
||||
|
||||
ar = al = H[0];
|
||||
br = bl = H[1];
|
||||
cr = cl = H[2];
|
||||
dr = dl = H[3];
|
||||
er = el = H[4];
|
||||
// Computation
|
||||
var t;
|
||||
for (var i = 0; i < 80; i += 1) {
|
||||
t = (al + M[offset+zl[i]])|0;
|
||||
if (i<16){
|
||||
t += f1(bl,cl,dl) + hl[0];
|
||||
} else if (i<32) {
|
||||
t += f2(bl,cl,dl) + hl[1];
|
||||
} else if (i<48) {
|
||||
t += f3(bl,cl,dl) + hl[2];
|
||||
} else if (i<64) {
|
||||
t += f4(bl,cl,dl) + hl[3];
|
||||
} else {// if (i<80) {
|
||||
t += f5(bl,cl,dl) + hl[4];
|
||||
}
|
||||
t = t|0;
|
||||
t = rotl(t,sl[i]);
|
||||
t = (t+el)|0;
|
||||
al = el;
|
||||
el = dl;
|
||||
dl = rotl(cl, 10);
|
||||
cl = bl;
|
||||
bl = t;
|
||||
|
||||
t = (ar + M[offset+zr[i]])|0;
|
||||
if (i<16){
|
||||
t += f5(br,cr,dr) + hr[0];
|
||||
} else if (i<32) {
|
||||
t += f4(br,cr,dr) + hr[1];
|
||||
} else if (i<48) {
|
||||
t += f3(br,cr,dr) + hr[2];
|
||||
} else if (i<64) {
|
||||
t += f2(br,cr,dr) + hr[3];
|
||||
} else {// if (i<80) {
|
||||
t += f1(br,cr,dr) + hr[4];
|
||||
}
|
||||
t = t|0;
|
||||
t = rotl(t,sr[i]) ;
|
||||
t = (t+er)|0;
|
||||
ar = er;
|
||||
er = dr;
|
||||
dr = rotl(cr, 10);
|
||||
cr = br;
|
||||
br = t;
|
||||
}
|
||||
// Intermediate hash value
|
||||
t = (H[1] + cl + dr)|0;
|
||||
H[1] = (H[2] + dl + er)|0;
|
||||
H[2] = (H[3] + el + ar)|0;
|
||||
H[3] = (H[4] + al + br)|0;
|
||||
H[4] = (H[0] + bl + cr)|0;
|
||||
H[0] = t;
|
||||
},
|
||||
|
||||
_doFinalize: function () {
|
||||
// Shortcuts
|
||||
var data = this._data;
|
||||
var dataWords = data.words;
|
||||
|
||||
var nBitsTotal = this._nDataBytes * 8;
|
||||
var nBitsLeft = data.sigBytes * 8;
|
||||
|
||||
// Add padding
|
||||
dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);
|
||||
dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = (
|
||||
(((nBitsTotal << 8) | (nBitsTotal >>> 24)) & 0x00ff00ff) |
|
||||
(((nBitsTotal << 24) | (nBitsTotal >>> 8)) & 0xff00ff00)
|
||||
);
|
||||
data.sigBytes = (dataWords.length + 1) * 4;
|
||||
|
||||
// Hash final blocks
|
||||
this._process();
|
||||
|
||||
// Shortcuts
|
||||
var hash = this._hash;
|
||||
var H = hash.words;
|
||||
|
||||
// Swap endian
|
||||
for (var i = 0; i < 5; i++) {
|
||||
// Shortcut
|
||||
var H_i = H[i];
|
||||
|
||||
// Swap
|
||||
H[i] = (((H_i << 8) | (H_i >>> 24)) & 0x00ff00ff) |
|
||||
(((H_i << 24) | (H_i >>> 8)) & 0xff00ff00);
|
||||
}
|
||||
|
||||
// Return final computed hash
|
||||
return hash;
|
||||
},
|
||||
|
||||
clone: function () {
|
||||
var clone = Hasher.clone.call(this);
|
||||
clone._hash = this._hash.clone();
|
||||
|
||||
return clone;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function f1(x, y, z) {
|
||||
return ((x) ^ (y) ^ (z));
|
||||
|
||||
}
|
||||
|
||||
function f2(x, y, z) {
|
||||
return (((x)&(y)) | ((~x)&(z)));
|
||||
}
|
||||
|
||||
function f3(x, y, z) {
|
||||
return (((x) | (~(y))) ^ (z));
|
||||
}
|
||||
|
||||
function f4(x, y, z) {
|
||||
return (((x) & (z)) | ((y)&(~(z))));
|
||||
}
|
||||
|
||||
function f5(x, y, z) {
|
||||
return ((x) ^ ((y) |(~(z))));
|
||||
|
||||
}
|
||||
|
||||
function rotl(x,n) {
|
||||
return (x<<n) | (x>>>(32-n));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Shortcut function to the hasher's object interface.
|
||||
*
|
||||
* @param {WordArray|string} message The message to hash.
|
||||
*
|
||||
* @return {WordArray} The hash.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var hash = CryptoJS.RIPEMD160('message');
|
||||
* var hash = CryptoJS.RIPEMD160(wordArray);
|
||||
*/
|
||||
C.RIPEMD160 = Hasher._createHelper(RIPEMD160);
|
||||
|
||||
/**
|
||||
* Shortcut function to the HMAC's object interface.
|
||||
*
|
||||
* @param {WordArray|string} message The message to hash.
|
||||
* @param {WordArray|string} key The secret key.
|
||||
*
|
||||
* @return {WordArray} The HMAC.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var hmac = CryptoJS.HmacRIPEMD160(message, key);
|
||||
*/
|
||||
C.HmacRIPEMD160 = Hasher._createHmacHelper(RIPEMD160);
|
||||
}(Math));
|
||||
|
||||
|
||||
return CryptoJS.RIPEMD160;
|
||||
|
||||
}));
|
@ -0,0 +1,150 @@
|
||||
;(function (root, factory) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
(function () {
|
||||
// Shortcuts
|
||||
var C = CryptoJS;
|
||||
var C_lib = C.lib;
|
||||
var WordArray = C_lib.WordArray;
|
||||
var Hasher = C_lib.Hasher;
|
||||
var C_algo = C.algo;
|
||||
|
||||
// Reusable object
|
||||
var W = [];
|
||||
|
||||
/**
|
||||
* SHA-1 hash algorithm.
|
||||
*/
|
||||
var SHA1 = C_algo.SHA1 = Hasher.extend({
|
||||
_doReset: function () {
|
||||
this._hash = new WordArray.init([
|
||||
0x67452301, 0xefcdab89,
|
||||
0x98badcfe, 0x10325476,
|
||||
0xc3d2e1f0
|
||||
]);
|
||||
},
|
||||
|
||||
_doProcessBlock: function (M, offset) {
|
||||
// Shortcut
|
||||
var H = this._hash.words;
|
||||
|
||||
// Working variables
|
||||
var a = H[0];
|
||||
var b = H[1];
|
||||
var c = H[2];
|
||||
var d = H[3];
|
||||
var e = H[4];
|
||||
|
||||
// Computation
|
||||
for (var i = 0; i < 80; i++) {
|
||||
if (i < 16) {
|
||||
W[i] = M[offset + i] | 0;
|
||||
} else {
|
||||
var n = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16];
|
||||
W[i] = (n << 1) | (n >>> 31);
|
||||
}
|
||||
|
||||
var t = ((a << 5) | (a >>> 27)) + e + W[i];
|
||||
if (i < 20) {
|
||||
t += ((b & c) | (~b & d)) + 0x5a827999;
|
||||
} else if (i < 40) {
|
||||
t += (b ^ c ^ d) + 0x6ed9eba1;
|
||||
} else if (i < 60) {
|
||||
t += ((b & c) | (b & d) | (c & d)) - 0x70e44324;
|
||||
} else /* if (i < 80) */ {
|
||||
t += (b ^ c ^ d) - 0x359d3e2a;
|
||||
}
|
||||
|
||||
e = d;
|
||||
d = c;
|
||||
c = (b << 30) | (b >>> 2);
|
||||
b = a;
|
||||
a = t;
|
||||
}
|
||||
|
||||
// Intermediate hash value
|
||||
H[0] = (H[0] + a) | 0;
|
||||
H[1] = (H[1] + b) | 0;
|
||||
H[2] = (H[2] + c) | 0;
|
||||
H[3] = (H[3] + d) | 0;
|
||||
H[4] = (H[4] + e) | 0;
|
||||
},
|
||||
|
||||
_doFinalize: function () {
|
||||
// Shortcuts
|
||||
var data = this._data;
|
||||
var dataWords = data.words;
|
||||
|
||||
var nBitsTotal = this._nDataBytes * 8;
|
||||
var nBitsLeft = data.sigBytes * 8;
|
||||
|
||||
// Add padding
|
||||
dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);
|
||||
dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = Math.floor(nBitsTotal / 0x100000000);
|
||||
dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = nBitsTotal;
|
||||
data.sigBytes = dataWords.length * 4;
|
||||
|
||||
// Hash final blocks
|
||||
this._process();
|
||||
|
||||
// Return final computed hash
|
||||
return this._hash;
|
||||
},
|
||||
|
||||
clone: function () {
|
||||
var clone = Hasher.clone.call(this);
|
||||
clone._hash = this._hash.clone();
|
||||
|
||||
return clone;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Shortcut function to the hasher's object interface.
|
||||
*
|
||||
* @param {WordArray|string} message The message to hash.
|
||||
*
|
||||
* @return {WordArray} The hash.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var hash = CryptoJS.SHA1('message');
|
||||
* var hash = CryptoJS.SHA1(wordArray);
|
||||
*/
|
||||
C.SHA1 = Hasher._createHelper(SHA1);
|
||||
|
||||
/**
|
||||
* Shortcut function to the HMAC's object interface.
|
||||
*
|
||||
* @param {WordArray|string} message The message to hash.
|
||||
* @param {WordArray|string} key The secret key.
|
||||
*
|
||||
* @return {WordArray} The HMAC.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var hmac = CryptoJS.HmacSHA1(message, key);
|
||||
*/
|
||||
C.HmacSHA1 = Hasher._createHmacHelper(SHA1);
|
||||
}());
|
||||
|
||||
|
||||
return CryptoJS.SHA1;
|
||||
|
||||
}));
|
@ -0,0 +1,80 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./sha256"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./sha256"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
(function () {
|
||||
// Shortcuts
|
||||
var C = CryptoJS;
|
||||
var C_lib = C.lib;
|
||||
var WordArray = C_lib.WordArray;
|
||||
var C_algo = C.algo;
|
||||
var SHA256 = C_algo.SHA256;
|
||||
|
||||
/**
|
||||
* SHA-224 hash algorithm.
|
||||
*/
|
||||
var SHA224 = C_algo.SHA224 = SHA256.extend({
|
||||
_doReset: function () {
|
||||
this._hash = new WordArray.init([
|
||||
0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939,
|
||||
0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4
|
||||
]);
|
||||
},
|
||||
|
||||
_doFinalize: function () {
|
||||
var hash = SHA256._doFinalize.call(this);
|
||||
|
||||
hash.sigBytes -= 4;
|
||||
|
||||
return hash;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Shortcut function to the hasher's object interface.
|
||||
*
|
||||
* @param {WordArray|string} message The message to hash.
|
||||
*
|
||||
* @return {WordArray} The hash.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var hash = CryptoJS.SHA224('message');
|
||||
* var hash = CryptoJS.SHA224(wordArray);
|
||||
*/
|
||||
C.SHA224 = SHA256._createHelper(SHA224);
|
||||
|
||||
/**
|
||||
* Shortcut function to the HMAC's object interface.
|
||||
*
|
||||
* @param {WordArray|string} message The message to hash.
|
||||
* @param {WordArray|string} key The secret key.
|
||||
*
|
||||
* @return {WordArray} The HMAC.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var hmac = CryptoJS.HmacSHA224(message, key);
|
||||
*/
|
||||
C.HmacSHA224 = SHA256._createHmacHelper(SHA224);
|
||||
}());
|
||||
|
||||
|
||||
return CryptoJS.SHA224;
|
||||
|
||||
}));
|
@ -0,0 +1,199 @@
|
||||
;(function (root, factory) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
(function (Math) {
|
||||
// Shortcuts
|
||||
var C = CryptoJS;
|
||||
var C_lib = C.lib;
|
||||
var WordArray = C_lib.WordArray;
|
||||
var Hasher = C_lib.Hasher;
|
||||
var C_algo = C.algo;
|
||||
|
||||
// Initialization and round constants tables
|
||||
var H = [];
|
||||
var K = [];
|
||||
|
||||
// Compute constants
|
||||
(function () {
|
||||
function isPrime(n) {
|
||||
var sqrtN = Math.sqrt(n);
|
||||
for (var factor = 2; factor <= sqrtN; factor++) {
|
||||
if (!(n % factor)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function getFractionalBits(n) {
|
||||
return ((n - (n | 0)) * 0x100000000) | 0;
|
||||
}
|
||||
|
||||
var n = 2;
|
||||
var nPrime = 0;
|
||||
while (nPrime < 64) {
|
||||
if (isPrime(n)) {
|
||||
if (nPrime < 8) {
|
||||
H[nPrime] = getFractionalBits(Math.pow(n, 1 / 2));
|
||||
}
|
||||
K[nPrime] = getFractionalBits(Math.pow(n, 1 / 3));
|
||||
|
||||
nPrime++;
|
||||
}
|
||||
|
||||
n++;
|
||||
}
|
||||
}());
|
||||
|
||||
// Reusable object
|
||||
var W = [];
|
||||
|
||||
/**
|
||||
* SHA-256 hash algorithm.
|
||||
*/
|
||||
var SHA256 = C_algo.SHA256 = Hasher.extend({
|
||||
_doReset: function () {
|
||||
this._hash = new WordArray.init(H.slice(0));
|
||||
},
|
||||
|
||||
_doProcessBlock: function (M, offset) {
|
||||
// Shortcut
|
||||
var H = this._hash.words;
|
||||
|
||||
// Working variables
|
||||
var a = H[0];
|
||||
var b = H[1];
|
||||
var c = H[2];
|
||||
var d = H[3];
|
||||
var e = H[4];
|
||||
var f = H[5];
|
||||
var g = H[6];
|
||||
var h = H[7];
|
||||
|
||||
// Computation
|
||||
for (var i = 0; i < 64; i++) {
|
||||
if (i < 16) {
|
||||
W[i] = M[offset + i] | 0;
|
||||
} else {
|
||||
var gamma0x = W[i - 15];
|
||||
var gamma0 = ((gamma0x << 25) | (gamma0x >>> 7)) ^
|
||||
((gamma0x << 14) | (gamma0x >>> 18)) ^
|
||||
(gamma0x >>> 3);
|
||||
|
||||
var gamma1x = W[i - 2];
|
||||
var gamma1 = ((gamma1x << 15) | (gamma1x >>> 17)) ^
|
||||
((gamma1x << 13) | (gamma1x >>> 19)) ^
|
||||
(gamma1x >>> 10);
|
||||
|
||||
W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16];
|
||||
}
|
||||
|
||||
var ch = (e & f) ^ (~e & g);
|
||||
var maj = (a & b) ^ (a & c) ^ (b & c);
|
||||
|
||||
var sigma0 = ((a << 30) | (a >>> 2)) ^ ((a << 19) | (a >>> 13)) ^ ((a << 10) | (a >>> 22));
|
||||
var sigma1 = ((e << 26) | (e >>> 6)) ^ ((e << 21) | (e >>> 11)) ^ ((e << 7) | (e >>> 25));
|
||||
|
||||
var t1 = h + sigma1 + ch + K[i] + W[i];
|
||||
var t2 = sigma0 + maj;
|
||||
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = (d + t1) | 0;
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = (t1 + t2) | 0;
|
||||
}
|
||||
|
||||
// Intermediate hash value
|
||||
H[0] = (H[0] + a) | 0;
|
||||
H[1] = (H[1] + b) | 0;
|
||||
H[2] = (H[2] + c) | 0;
|
||||
H[3] = (H[3] + d) | 0;
|
||||
H[4] = (H[4] + e) | 0;
|
||||
H[5] = (H[5] + f) | 0;
|
||||
H[6] = (H[6] + g) | 0;
|
||||
H[7] = (H[7] + h) | 0;
|
||||
},
|
||||
|
||||
_doFinalize: function () {
|
||||
// Shortcuts
|
||||
var data = this._data;
|
||||
var dataWords = data.words;
|
||||
|
||||
var nBitsTotal = this._nDataBytes * 8;
|
||||
var nBitsLeft = data.sigBytes * 8;
|
||||
|
||||
// Add padding
|
||||
dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);
|
||||
dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = Math.floor(nBitsTotal / 0x100000000);
|
||||
dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = nBitsTotal;
|
||||
data.sigBytes = dataWords.length * 4;
|
||||
|
||||
// Hash final blocks
|
||||
this._process();
|
||||
|
||||
// Return final computed hash
|
||||
return this._hash;
|
||||
},
|
||||
|
||||
clone: function () {
|
||||
var clone = Hasher.clone.call(this);
|
||||
clone._hash = this._hash.clone();
|
||||
|
||||
return clone;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Shortcut function to the hasher's object interface.
|
||||
*
|
||||
* @param {WordArray|string} message The message to hash.
|
||||
*
|
||||
* @return {WordArray} The hash.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var hash = CryptoJS.SHA256('message');
|
||||
* var hash = CryptoJS.SHA256(wordArray);
|
||||
*/
|
||||
C.SHA256 = Hasher._createHelper(SHA256);
|
||||
|
||||
/**
|
||||
* Shortcut function to the HMAC's object interface.
|
||||
*
|
||||
* @param {WordArray|string} message The message to hash.
|
||||
* @param {WordArray|string} key The secret key.
|
||||
*
|
||||
* @return {WordArray} The HMAC.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var hmac = CryptoJS.HmacSHA256(message, key);
|
||||
*/
|
||||
C.HmacSHA256 = Hasher._createHmacHelper(SHA256);
|
||||
}(Math));
|
||||
|
||||
|
||||
return CryptoJS.SHA256;
|
||||
|
||||
}));
|
@ -0,0 +1,326 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./x64-core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./x64-core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
(function (Math) {
|
||||
// Shortcuts
|
||||
var C = CryptoJS;
|
||||
var C_lib = C.lib;
|
||||
var WordArray = C_lib.WordArray;
|
||||
var Hasher = C_lib.Hasher;
|
||||
var C_x64 = C.x64;
|
||||
var X64Word = C_x64.Word;
|
||||
var C_algo = C.algo;
|
||||
|
||||
// Constants tables
|
||||
var RHO_OFFSETS = [];
|
||||
var PI_INDEXES = [];
|
||||
var ROUND_CONSTANTS = [];
|
||||
|
||||
// Compute Constants
|
||||
(function () {
|
||||
// Compute rho offset constants
|
||||
var x = 1, y = 0;
|
||||
for (var t = 0; t < 24; t++) {
|
||||
RHO_OFFSETS[x + 5 * y] = ((t + 1) * (t + 2) / 2) % 64;
|
||||
|
||||
var newX = y % 5;
|
||||
var newY = (2 * x + 3 * y) % 5;
|
||||
x = newX;
|
||||
y = newY;
|
||||
}
|
||||
|
||||
// Compute pi index constants
|
||||
for (var x = 0; x < 5; x++) {
|
||||
for (var y = 0; y < 5; y++) {
|
||||
PI_INDEXES[x + 5 * y] = y + ((2 * x + 3 * y) % 5) * 5;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute round constants
|
||||
var LFSR = 0x01;
|
||||
for (var i = 0; i < 24; i++) {
|
||||
var roundConstantMsw = 0;
|
||||
var roundConstantLsw = 0;
|
||||
|
||||
for (var j = 0; j < 7; j++) {
|
||||
if (LFSR & 0x01) {
|
||||
var bitPosition = (1 << j) - 1;
|
||||
if (bitPosition < 32) {
|
||||
roundConstantLsw ^= 1 << bitPosition;
|
||||
} else /* if (bitPosition >= 32) */ {
|
||||
roundConstantMsw ^= 1 << (bitPosition - 32);
|
||||
}
|
||||
}
|
||||
|
||||
// Compute next LFSR
|
||||
if (LFSR & 0x80) {
|
||||
// Primitive polynomial over GF(2): x^8 + x^6 + x^5 + x^4 + 1
|
||||
LFSR = (LFSR << 1) ^ 0x71;
|
||||
} else {
|
||||
LFSR <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
ROUND_CONSTANTS[i] = X64Word.create(roundConstantMsw, roundConstantLsw);
|
||||
}
|
||||
}());
|
||||
|
||||
// Reusable objects for temporary values
|
||||
var T = [];
|
||||
(function () {
|
||||
for (var i = 0; i < 25; i++) {
|
||||
T[i] = X64Word.create();
|
||||
}
|
||||
}());
|
||||
|
||||
/**
|
||||
* SHA-3 hash algorithm.
|
||||
*/
|
||||
var SHA3 = C_algo.SHA3 = Hasher.extend({
|
||||
/**
|
||||
* Configuration options.
|
||||
*
|
||||
* @property {number} outputLength
|
||||
* The desired number of bits in the output hash.
|
||||
* Only values permitted are: 224, 256, 384, 512.
|
||||
* Default: 512
|
||||
*/
|
||||
cfg: Hasher.cfg.extend({
|
||||
outputLength: 512
|
||||
}),
|
||||
|
||||
_doReset: function () {
|
||||
var state = this._state = []
|
||||
for (var i = 0; i < 25; i++) {
|
||||
state[i] = new X64Word.init();
|
||||
}
|
||||
|
||||
this.blockSize = (1600 - 2 * this.cfg.outputLength) / 32;
|
||||
},
|
||||
|
||||
_doProcessBlock: function (M, offset) {
|
||||
// Shortcuts
|
||||
var state = this._state;
|
||||
var nBlockSizeLanes = this.blockSize / 2;
|
||||
|
||||
// Absorb
|
||||
for (var i = 0; i < nBlockSizeLanes; i++) {
|
||||
// Shortcuts
|
||||
var M2i = M[offset + 2 * i];
|
||||
var M2i1 = M[offset + 2 * i + 1];
|
||||
|
||||
// Swap endian
|
||||
M2i = (
|
||||
(((M2i << 8) | (M2i >>> 24)) & 0x00ff00ff) |
|
||||
(((M2i << 24) | (M2i >>> 8)) & 0xff00ff00)
|
||||
);
|
||||
M2i1 = (
|
||||
(((M2i1 << 8) | (M2i1 >>> 24)) & 0x00ff00ff) |
|
||||
(((M2i1 << 24) | (M2i1 >>> 8)) & 0xff00ff00)
|
||||
);
|
||||
|
||||
// Absorb message into state
|
||||
var lane = state[i];
|
||||
lane.high ^= M2i1;
|
||||
lane.low ^= M2i;
|
||||
}
|
||||
|
||||
// Rounds
|
||||
for (var round = 0; round < 24; round++) {
|
||||
// Theta
|
||||
for (var x = 0; x < 5; x++) {
|
||||
// Mix column lanes
|
||||
var tMsw = 0, tLsw = 0;
|
||||
for (var y = 0; y < 5; y++) {
|
||||
var lane = state[x + 5 * y];
|
||||
tMsw ^= lane.high;
|
||||
tLsw ^= lane.low;
|
||||
}
|
||||
|
||||
// Temporary values
|
||||
var Tx = T[x];
|
||||
Tx.high = tMsw;
|
||||
Tx.low = tLsw;
|
||||
}
|
||||
for (var x = 0; x < 5; x++) {
|
||||
// Shortcuts
|
||||
var Tx4 = T[(x + 4) % 5];
|
||||
var Tx1 = T[(x + 1) % 5];
|
||||
var Tx1Msw = Tx1.high;
|
||||
var Tx1Lsw = Tx1.low;
|
||||
|
||||
// Mix surrounding columns
|
||||
var tMsw = Tx4.high ^ ((Tx1Msw << 1) | (Tx1Lsw >>> 31));
|
||||
var tLsw = Tx4.low ^ ((Tx1Lsw << 1) | (Tx1Msw >>> 31));
|
||||
for (var y = 0; y < 5; y++) {
|
||||
var lane = state[x + 5 * y];
|
||||
lane.high ^= tMsw;
|
||||
lane.low ^= tLsw;
|
||||
}
|
||||
}
|
||||
|
||||
// Rho Pi
|
||||
for (var laneIndex = 1; laneIndex < 25; laneIndex++) {
|
||||
var tMsw;
|
||||
var tLsw;
|
||||
|
||||
// Shortcuts
|
||||
var lane = state[laneIndex];
|
||||
var laneMsw = lane.high;
|
||||
var laneLsw = lane.low;
|
||||
var rhoOffset = RHO_OFFSETS[laneIndex];
|
||||
|
||||
// Rotate lanes
|
||||
if (rhoOffset < 32) {
|
||||
tMsw = (laneMsw << rhoOffset) | (laneLsw >>> (32 - rhoOffset));
|
||||
tLsw = (laneLsw << rhoOffset) | (laneMsw >>> (32 - rhoOffset));
|
||||
} else /* if (rhoOffset >= 32) */ {
|
||||
tMsw = (laneLsw << (rhoOffset - 32)) | (laneMsw >>> (64 - rhoOffset));
|
||||
tLsw = (laneMsw << (rhoOffset - 32)) | (laneLsw >>> (64 - rhoOffset));
|
||||
}
|
||||
|
||||
// Transpose lanes
|
||||
var TPiLane = T[PI_INDEXES[laneIndex]];
|
||||
TPiLane.high = tMsw;
|
||||
TPiLane.low = tLsw;
|
||||
}
|
||||
|
||||
// Rho pi at x = y = 0
|
||||
var T0 = T[0];
|
||||
var state0 = state[0];
|
||||
T0.high = state0.high;
|
||||
T0.low = state0.low;
|
||||
|
||||
// Chi
|
||||
for (var x = 0; x < 5; x++) {
|
||||
for (var y = 0; y < 5; y++) {
|
||||
// Shortcuts
|
||||
var laneIndex = x + 5 * y;
|
||||
var lane = state[laneIndex];
|
||||
var TLane = T[laneIndex];
|
||||
var Tx1Lane = T[((x + 1) % 5) + 5 * y];
|
||||
var Tx2Lane = T[((x + 2) % 5) + 5 * y];
|
||||
|
||||
// Mix rows
|
||||
lane.high = TLane.high ^ (~Tx1Lane.high & Tx2Lane.high);
|
||||
lane.low = TLane.low ^ (~Tx1Lane.low & Tx2Lane.low);
|
||||
}
|
||||
}
|
||||
|
||||
// Iota
|
||||
var lane = state[0];
|
||||
var roundConstant = ROUND_CONSTANTS[round];
|
||||
lane.high ^= roundConstant.high;
|
||||
lane.low ^= roundConstant.low;
|
||||
}
|
||||
},
|
||||
|
||||
_doFinalize: function () {
|
||||
// Shortcuts
|
||||
var data = this._data;
|
||||
var dataWords = data.words;
|
||||
var nBitsTotal = this._nDataBytes * 8;
|
||||
var nBitsLeft = data.sigBytes * 8;
|
||||
var blockSizeBits = this.blockSize * 32;
|
||||
|
||||
// Add padding
|
||||
dataWords[nBitsLeft >>> 5] |= 0x1 << (24 - nBitsLeft % 32);
|
||||
dataWords[((Math.ceil((nBitsLeft + 1) / blockSizeBits) * blockSizeBits) >>> 5) - 1] |= 0x80;
|
||||
data.sigBytes = dataWords.length * 4;
|
||||
|
||||
// Hash final blocks
|
||||
this._process();
|
||||
|
||||
// Shortcuts
|
||||
var state = this._state;
|
||||
var outputLengthBytes = this.cfg.outputLength / 8;
|
||||
var outputLengthLanes = outputLengthBytes / 8;
|
||||
|
||||
// Squeeze
|
||||
var hashWords = [];
|
||||
for (var i = 0; i < outputLengthLanes; i++) {
|
||||
// Shortcuts
|
||||
var lane = state[i];
|
||||
var laneMsw = lane.high;
|
||||
var laneLsw = lane.low;
|
||||
|
||||
// Swap endian
|
||||
laneMsw = (
|
||||
(((laneMsw << 8) | (laneMsw >>> 24)) & 0x00ff00ff) |
|
||||
(((laneMsw << 24) | (laneMsw >>> 8)) & 0xff00ff00)
|
||||
);
|
||||
laneLsw = (
|
||||
(((laneLsw << 8) | (laneLsw >>> 24)) & 0x00ff00ff) |
|
||||
(((laneLsw << 24) | (laneLsw >>> 8)) & 0xff00ff00)
|
||||
);
|
||||
|
||||
// Squeeze state to retrieve hash
|
||||
hashWords.push(laneLsw);
|
||||
hashWords.push(laneMsw);
|
||||
}
|
||||
|
||||
// Return final computed hash
|
||||
return new WordArray.init(hashWords, outputLengthBytes);
|
||||
},
|
||||
|
||||
clone: function () {
|
||||
var clone = Hasher.clone.call(this);
|
||||
|
||||
var state = clone._state = this._state.slice(0);
|
||||
for (var i = 0; i < 25; i++) {
|
||||
state[i] = state[i].clone();
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Shortcut function to the hasher's object interface.
|
||||
*
|
||||
* @param {WordArray|string} message The message to hash.
|
||||
*
|
||||
* @return {WordArray} The hash.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var hash = CryptoJS.SHA3('message');
|
||||
* var hash = CryptoJS.SHA3(wordArray);
|
||||
*/
|
||||
C.SHA3 = Hasher._createHelper(SHA3);
|
||||
|
||||
/**
|
||||
* Shortcut function to the HMAC's object interface.
|
||||
*
|
||||
* @param {WordArray|string} message The message to hash.
|
||||
* @param {WordArray|string} key The secret key.
|
||||
*
|
||||
* @return {WordArray} The HMAC.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var hmac = CryptoJS.HmacSHA3(message, key);
|
||||
*/
|
||||
C.HmacSHA3 = Hasher._createHmacHelper(SHA3);
|
||||
}(Math));
|
||||
|
||||
|
||||
return CryptoJS.SHA3;
|
||||
|
||||
}));
|
@ -0,0 +1,83 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./x64-core"), require("./sha512"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./x64-core", "./sha512"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
(function () {
|
||||
// Shortcuts
|
||||
var C = CryptoJS;
|
||||
var C_x64 = C.x64;
|
||||
var X64Word = C_x64.Word;
|
||||
var X64WordArray = C_x64.WordArray;
|
||||
var C_algo = C.algo;
|
||||
var SHA512 = C_algo.SHA512;
|
||||
|
||||
/**
|
||||
* SHA-384 hash algorithm.
|
||||
*/
|
||||
var SHA384 = C_algo.SHA384 = SHA512.extend({
|
||||
_doReset: function () {
|
||||
this._hash = new X64WordArray.init([
|
||||
new X64Word.init(0xcbbb9d5d, 0xc1059ed8), new X64Word.init(0x629a292a, 0x367cd507),
|
||||
new X64Word.init(0x9159015a, 0x3070dd17), new X64Word.init(0x152fecd8, 0xf70e5939),
|
||||
new X64Word.init(0x67332667, 0xffc00b31), new X64Word.init(0x8eb44a87, 0x68581511),
|
||||
new X64Word.init(0xdb0c2e0d, 0x64f98fa7), new X64Word.init(0x47b5481d, 0xbefa4fa4)
|
||||
]);
|
||||
},
|
||||
|
||||
_doFinalize: function () {
|
||||
var hash = SHA512._doFinalize.call(this);
|
||||
|
||||
hash.sigBytes -= 16;
|
||||
|
||||
return hash;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Shortcut function to the hasher's object interface.
|
||||
*
|
||||
* @param {WordArray|string} message The message to hash.
|
||||
*
|
||||
* @return {WordArray} The hash.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var hash = CryptoJS.SHA384('message');
|
||||
* var hash = CryptoJS.SHA384(wordArray);
|
||||
*/
|
||||
C.SHA384 = SHA512._createHelper(SHA384);
|
||||
|
||||
/**
|
||||
* Shortcut function to the HMAC's object interface.
|
||||
*
|
||||
* @param {WordArray|string} message The message to hash.
|
||||
* @param {WordArray|string} key The secret key.
|
||||
*
|
||||
* @return {WordArray} The HMAC.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var hmac = CryptoJS.HmacSHA384(message, key);
|
||||
*/
|
||||
C.HmacSHA384 = SHA512._createHmacHelper(SHA384);
|
||||
}());
|
||||
|
||||
|
||||
return CryptoJS.SHA384;
|
||||
|
||||
}));
|
@ -0,0 +1,326 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./x64-core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./x64-core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
(function () {
|
||||
// Shortcuts
|
||||
var C = CryptoJS;
|
||||
var C_lib = C.lib;
|
||||
var Hasher = C_lib.Hasher;
|
||||
var C_x64 = C.x64;
|
||||
var X64Word = C_x64.Word;
|
||||
var X64WordArray = C_x64.WordArray;
|
||||
var C_algo = C.algo;
|
||||
|
||||
function X64Word_create() {
|
||||
return X64Word.create.apply(X64Word, arguments);
|
||||
}
|
||||
|
||||
// Constants
|
||||
var K = [
|
||||
X64Word_create(0x428a2f98, 0xd728ae22), X64Word_create(0x71374491, 0x23ef65cd),
|
||||
X64Word_create(0xb5c0fbcf, 0xec4d3b2f), X64Word_create(0xe9b5dba5, 0x8189dbbc),
|
||||
X64Word_create(0x3956c25b, 0xf348b538), X64Word_create(0x59f111f1, 0xb605d019),
|
||||
X64Word_create(0x923f82a4, 0xaf194f9b), X64Word_create(0xab1c5ed5, 0xda6d8118),
|
||||
X64Word_create(0xd807aa98, 0xa3030242), X64Word_create(0x12835b01, 0x45706fbe),
|
||||
X64Word_create(0x243185be, 0x4ee4b28c), X64Word_create(0x550c7dc3, 0xd5ffb4e2),
|
||||
X64Word_create(0x72be5d74, 0xf27b896f), X64Word_create(0x80deb1fe, 0x3b1696b1),
|
||||
X64Word_create(0x9bdc06a7, 0x25c71235), X64Word_create(0xc19bf174, 0xcf692694),
|
||||
X64Word_create(0xe49b69c1, 0x9ef14ad2), X64Word_create(0xefbe4786, 0x384f25e3),
|
||||
X64Word_create(0x0fc19dc6, 0x8b8cd5b5), X64Word_create(0x240ca1cc, 0x77ac9c65),
|
||||
X64Word_create(0x2de92c6f, 0x592b0275), X64Word_create(0x4a7484aa, 0x6ea6e483),
|
||||
X64Word_create(0x5cb0a9dc, 0xbd41fbd4), X64Word_create(0x76f988da, 0x831153b5),
|
||||
X64Word_create(0x983e5152, 0xee66dfab), X64Word_create(0xa831c66d, 0x2db43210),
|
||||
X64Word_create(0xb00327c8, 0x98fb213f), X64Word_create(0xbf597fc7, 0xbeef0ee4),
|
||||
X64Word_create(0xc6e00bf3, 0x3da88fc2), X64Word_create(0xd5a79147, 0x930aa725),
|
||||
X64Word_create(0x06ca6351, 0xe003826f), X64Word_create(0x14292967, 0x0a0e6e70),
|
||||
X64Word_create(0x27b70a85, 0x46d22ffc), X64Word_create(0x2e1b2138, 0x5c26c926),
|
||||
X64Word_create(0x4d2c6dfc, 0x5ac42aed), X64Word_create(0x53380d13, 0x9d95b3df),
|
||||
X64Word_create(0x650a7354, 0x8baf63de), X64Word_create(0x766a0abb, 0x3c77b2a8),
|
||||
X64Word_create(0x81c2c92e, 0x47edaee6), X64Word_create(0x92722c85, 0x1482353b),
|
||||
X64Word_create(0xa2bfe8a1, 0x4cf10364), X64Word_create(0xa81a664b, 0xbc423001),
|
||||
X64Word_create(0xc24b8b70, 0xd0f89791), X64Word_create(0xc76c51a3, 0x0654be30),
|
||||
X64Word_create(0xd192e819, 0xd6ef5218), X64Word_create(0xd6990624, 0x5565a910),
|
||||
X64Word_create(0xf40e3585, 0x5771202a), X64Word_create(0x106aa070, 0x32bbd1b8),
|
||||
X64Word_create(0x19a4c116, 0xb8d2d0c8), X64Word_create(0x1e376c08, 0x5141ab53),
|
||||
X64Word_create(0x2748774c, 0xdf8eeb99), X64Word_create(0x34b0bcb5, 0xe19b48a8),
|
||||
X64Word_create(0x391c0cb3, 0xc5c95a63), X64Word_create(0x4ed8aa4a, 0xe3418acb),
|
||||
X64Word_create(0x5b9cca4f, 0x7763e373), X64Word_create(0x682e6ff3, 0xd6b2b8a3),
|
||||
X64Word_create(0x748f82ee, 0x5defb2fc), X64Word_create(0x78a5636f, 0x43172f60),
|
||||
X64Word_create(0x84c87814, 0xa1f0ab72), X64Word_create(0x8cc70208, 0x1a6439ec),
|
||||
X64Word_create(0x90befffa, 0x23631e28), X64Word_create(0xa4506ceb, 0xde82bde9),
|
||||
X64Word_create(0xbef9a3f7, 0xb2c67915), X64Word_create(0xc67178f2, 0xe372532b),
|
||||
X64Word_create(0xca273ece, 0xea26619c), X64Word_create(0xd186b8c7, 0x21c0c207),
|
||||
X64Word_create(0xeada7dd6, 0xcde0eb1e), X64Word_create(0xf57d4f7f, 0xee6ed178),
|
||||
X64Word_create(0x06f067aa, 0x72176fba), X64Word_create(0x0a637dc5, 0xa2c898a6),
|
||||
X64Word_create(0x113f9804, 0xbef90dae), X64Word_create(0x1b710b35, 0x131c471b),
|
||||
X64Word_create(0x28db77f5, 0x23047d84), X64Word_create(0x32caab7b, 0x40c72493),
|
||||
X64Word_create(0x3c9ebe0a, 0x15c9bebc), X64Word_create(0x431d67c4, 0x9c100d4c),
|
||||
X64Word_create(0x4cc5d4be, 0xcb3e42b6), X64Word_create(0x597f299c, 0xfc657e2a),
|
||||
X64Word_create(0x5fcb6fab, 0x3ad6faec), X64Word_create(0x6c44198c, 0x4a475817)
|
||||
];
|
||||
|
||||
// Reusable objects
|
||||
var W = [];
|
||||
(function () {
|
||||
for (var i = 0; i < 80; i++) {
|
||||
W[i] = X64Word_create();
|
||||
}
|
||||
}());
|
||||
|
||||
/**
|
||||
* SHA-512 hash algorithm.
|
||||
*/
|
||||
var SHA512 = C_algo.SHA512 = Hasher.extend({
|
||||
_doReset: function () {
|
||||
this._hash = new X64WordArray.init([
|
||||
new X64Word.init(0x6a09e667, 0xf3bcc908), new X64Word.init(0xbb67ae85, 0x84caa73b),
|
||||
new X64Word.init(0x3c6ef372, 0xfe94f82b), new X64Word.init(0xa54ff53a, 0x5f1d36f1),
|
||||
new X64Word.init(0x510e527f, 0xade682d1), new X64Word.init(0x9b05688c, 0x2b3e6c1f),
|
||||
new X64Word.init(0x1f83d9ab, 0xfb41bd6b), new X64Word.init(0x5be0cd19, 0x137e2179)
|
||||
]);
|
||||
},
|
||||
|
||||
_doProcessBlock: function (M, offset) {
|
||||
// Shortcuts
|
||||
var H = this._hash.words;
|
||||
|
||||
var H0 = H[0];
|
||||
var H1 = H[1];
|
||||
var H2 = H[2];
|
||||
var H3 = H[3];
|
||||
var H4 = H[4];
|
||||
var H5 = H[5];
|
||||
var H6 = H[6];
|
||||
var H7 = H[7];
|
||||
|
||||
var H0h = H0.high;
|
||||
var H0l = H0.low;
|
||||
var H1h = H1.high;
|
||||
var H1l = H1.low;
|
||||
var H2h = H2.high;
|
||||
var H2l = H2.low;
|
||||
var H3h = H3.high;
|
||||
var H3l = H3.low;
|
||||
var H4h = H4.high;
|
||||
var H4l = H4.low;
|
||||
var H5h = H5.high;
|
||||
var H5l = H5.low;
|
||||
var H6h = H6.high;
|
||||
var H6l = H6.low;
|
||||
var H7h = H7.high;
|
||||
var H7l = H7.low;
|
||||
|
||||
// Working variables
|
||||
var ah = H0h;
|
||||
var al = H0l;
|
||||
var bh = H1h;
|
||||
var bl = H1l;
|
||||
var ch = H2h;
|
||||
var cl = H2l;
|
||||
var dh = H3h;
|
||||
var dl = H3l;
|
||||
var eh = H4h;
|
||||
var el = H4l;
|
||||
var fh = H5h;
|
||||
var fl = H5l;
|
||||
var gh = H6h;
|
||||
var gl = H6l;
|
||||
var hh = H7h;
|
||||
var hl = H7l;
|
||||
|
||||
// Rounds
|
||||
for (var i = 0; i < 80; i++) {
|
||||
var Wil;
|
||||
var Wih;
|
||||
|
||||
// Shortcut
|
||||
var Wi = W[i];
|
||||
|
||||
// Extend message
|
||||
if (i < 16) {
|
||||
Wih = Wi.high = M[offset + i * 2] | 0;
|
||||
Wil = Wi.low = M[offset + i * 2 + 1] | 0;
|
||||
} else {
|
||||
// Gamma0
|
||||
var gamma0x = W[i - 15];
|
||||
var gamma0xh = gamma0x.high;
|
||||
var gamma0xl = gamma0x.low;
|
||||
var gamma0h = ((gamma0xh >>> 1) | (gamma0xl << 31)) ^ ((gamma0xh >>> 8) | (gamma0xl << 24)) ^ (gamma0xh >>> 7);
|
||||
var gamma0l = ((gamma0xl >>> 1) | (gamma0xh << 31)) ^ ((gamma0xl >>> 8) | (gamma0xh << 24)) ^ ((gamma0xl >>> 7) | (gamma0xh << 25));
|
||||
|
||||
// Gamma1
|
||||
var gamma1x = W[i - 2];
|
||||
var gamma1xh = gamma1x.high;
|
||||
var gamma1xl = gamma1x.low;
|
||||
var gamma1h = ((gamma1xh >>> 19) | (gamma1xl << 13)) ^ ((gamma1xh << 3) | (gamma1xl >>> 29)) ^ (gamma1xh >>> 6);
|
||||
var gamma1l = ((gamma1xl >>> 19) | (gamma1xh << 13)) ^ ((gamma1xl << 3) | (gamma1xh >>> 29)) ^ ((gamma1xl >>> 6) | (gamma1xh << 26));
|
||||
|
||||
// W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16]
|
||||
var Wi7 = W[i - 7];
|
||||
var Wi7h = Wi7.high;
|
||||
var Wi7l = Wi7.low;
|
||||
|
||||
var Wi16 = W[i - 16];
|
||||
var Wi16h = Wi16.high;
|
||||
var Wi16l = Wi16.low;
|
||||
|
||||
Wil = gamma0l + Wi7l;
|
||||
Wih = gamma0h + Wi7h + ((Wil >>> 0) < (gamma0l >>> 0) ? 1 : 0);
|
||||
Wil = Wil + gamma1l;
|
||||
Wih = Wih + gamma1h + ((Wil >>> 0) < (gamma1l >>> 0) ? 1 : 0);
|
||||
Wil = Wil + Wi16l;
|
||||
Wih = Wih + Wi16h + ((Wil >>> 0) < (Wi16l >>> 0) ? 1 : 0);
|
||||
|
||||
Wi.high = Wih;
|
||||
Wi.low = Wil;
|
||||
}
|
||||
|
||||
var chh = (eh & fh) ^ (~eh & gh);
|
||||
var chl = (el & fl) ^ (~el & gl);
|
||||
var majh = (ah & bh) ^ (ah & ch) ^ (bh & ch);
|
||||
var majl = (al & bl) ^ (al & cl) ^ (bl & cl);
|
||||
|
||||
var sigma0h = ((ah >>> 28) | (al << 4)) ^ ((ah << 30) | (al >>> 2)) ^ ((ah << 25) | (al >>> 7));
|
||||
var sigma0l = ((al >>> 28) | (ah << 4)) ^ ((al << 30) | (ah >>> 2)) ^ ((al << 25) | (ah >>> 7));
|
||||
var sigma1h = ((eh >>> 14) | (el << 18)) ^ ((eh >>> 18) | (el << 14)) ^ ((eh << 23) | (el >>> 9));
|
||||
var sigma1l = ((el >>> 14) | (eh << 18)) ^ ((el >>> 18) | (eh << 14)) ^ ((el << 23) | (eh >>> 9));
|
||||
|
||||
// t1 = h + sigma1 + ch + K[i] + W[i]
|
||||
var Ki = K[i];
|
||||
var Kih = Ki.high;
|
||||
var Kil = Ki.low;
|
||||
|
||||
var t1l = hl + sigma1l;
|
||||
var t1h = hh + sigma1h + ((t1l >>> 0) < (hl >>> 0) ? 1 : 0);
|
||||
var t1l = t1l + chl;
|
||||
var t1h = t1h + chh + ((t1l >>> 0) < (chl >>> 0) ? 1 : 0);
|
||||
var t1l = t1l + Kil;
|
||||
var t1h = t1h + Kih + ((t1l >>> 0) < (Kil >>> 0) ? 1 : 0);
|
||||
var t1l = t1l + Wil;
|
||||
var t1h = t1h + Wih + ((t1l >>> 0) < (Wil >>> 0) ? 1 : 0);
|
||||
|
||||
// t2 = sigma0 + maj
|
||||
var t2l = sigma0l + majl;
|
||||
var t2h = sigma0h + majh + ((t2l >>> 0) < (sigma0l >>> 0) ? 1 : 0);
|
||||
|
||||
// Update working variables
|
||||
hh = gh;
|
||||
hl = gl;
|
||||
gh = fh;
|
||||
gl = fl;
|
||||
fh = eh;
|
||||
fl = el;
|
||||
el = (dl + t1l) | 0;
|
||||
eh = (dh + t1h + ((el >>> 0) < (dl >>> 0) ? 1 : 0)) | 0;
|
||||
dh = ch;
|
||||
dl = cl;
|
||||
ch = bh;
|
||||
cl = bl;
|
||||
bh = ah;
|
||||
bl = al;
|
||||
al = (t1l + t2l) | 0;
|
||||
ah = (t1h + t2h + ((al >>> 0) < (t1l >>> 0) ? 1 : 0)) | 0;
|
||||
}
|
||||
|
||||
// Intermediate hash value
|
||||
H0l = H0.low = (H0l + al);
|
||||
H0.high = (H0h + ah + ((H0l >>> 0) < (al >>> 0) ? 1 : 0));
|
||||
H1l = H1.low = (H1l + bl);
|
||||
H1.high = (H1h + bh + ((H1l >>> 0) < (bl >>> 0) ? 1 : 0));
|
||||
H2l = H2.low = (H2l + cl);
|
||||
H2.high = (H2h + ch + ((H2l >>> 0) < (cl >>> 0) ? 1 : 0));
|
||||
H3l = H3.low = (H3l + dl);
|
||||
H3.high = (H3h + dh + ((H3l >>> 0) < (dl >>> 0) ? 1 : 0));
|
||||
H4l = H4.low = (H4l + el);
|
||||
H4.high = (H4h + eh + ((H4l >>> 0) < (el >>> 0) ? 1 : 0));
|
||||
H5l = H5.low = (H5l + fl);
|
||||
H5.high = (H5h + fh + ((H5l >>> 0) < (fl >>> 0) ? 1 : 0));
|
||||
H6l = H6.low = (H6l + gl);
|
||||
H6.high = (H6h + gh + ((H6l >>> 0) < (gl >>> 0) ? 1 : 0));
|
||||
H7l = H7.low = (H7l + hl);
|
||||
H7.high = (H7h + hh + ((H7l >>> 0) < (hl >>> 0) ? 1 : 0));
|
||||
},
|
||||
|
||||
_doFinalize: function () {
|
||||
// Shortcuts
|
||||
var data = this._data;
|
||||
var dataWords = data.words;
|
||||
|
||||
var nBitsTotal = this._nDataBytes * 8;
|
||||
var nBitsLeft = data.sigBytes * 8;
|
||||
|
||||
// Add padding
|
||||
dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);
|
||||
dataWords[(((nBitsLeft + 128) >>> 10) << 5) + 30] = Math.floor(nBitsTotal / 0x100000000);
|
||||
dataWords[(((nBitsLeft + 128) >>> 10) << 5) + 31] = nBitsTotal;
|
||||
data.sigBytes = dataWords.length * 4;
|
||||
|
||||
// Hash final blocks
|
||||
this._process();
|
||||
|
||||
// Convert hash to 32-bit word array before returning
|
||||
var hash = this._hash.toX32();
|
||||
|
||||
// Return final computed hash
|
||||
return hash;
|
||||
},
|
||||
|
||||
clone: function () {
|
||||
var clone = Hasher.clone.call(this);
|
||||
clone._hash = this._hash.clone();
|
||||
|
||||
return clone;
|
||||
},
|
||||
|
||||
blockSize: 1024/32
|
||||
});
|
||||
|
||||
/**
|
||||
* Shortcut function to the hasher's object interface.
|
||||
*
|
||||
* @param {WordArray|string} message The message to hash.
|
||||
*
|
||||
* @return {WordArray} The hash.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var hash = CryptoJS.SHA512('message');
|
||||
* var hash = CryptoJS.SHA512(wordArray);
|
||||
*/
|
||||
C.SHA512 = Hasher._createHelper(SHA512);
|
||||
|
||||
/**
|
||||
* Shortcut function to the HMAC's object interface.
|
||||
*
|
||||
* @param {WordArray|string} message The message to hash.
|
||||
* @param {WordArray|string} key The secret key.
|
||||
*
|
||||
* @return {WordArray} The HMAC.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var hmac = CryptoJS.HmacSHA512(message, key);
|
||||
*/
|
||||
C.HmacSHA512 = Hasher._createHmacHelper(SHA512);
|
||||
}());
|
||||
|
||||
|
||||
return CryptoJS.SHA512;
|
||||
|
||||
}));
|
@ -0,0 +1,779 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./enc-base64"), require("./md5"), require("./evpkdf"), require("./cipher-core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./enc-base64", "./md5", "./evpkdf", "./cipher-core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
(function () {
|
||||
// Shortcuts
|
||||
var C = CryptoJS;
|
||||
var C_lib = C.lib;
|
||||
var WordArray = C_lib.WordArray;
|
||||
var BlockCipher = C_lib.BlockCipher;
|
||||
var C_algo = C.algo;
|
||||
|
||||
// Permuted Choice 1 constants
|
||||
var PC1 = [
|
||||
57, 49, 41, 33, 25, 17, 9, 1,
|
||||
58, 50, 42, 34, 26, 18, 10, 2,
|
||||
59, 51, 43, 35, 27, 19, 11, 3,
|
||||
60, 52, 44, 36, 63, 55, 47, 39,
|
||||
31, 23, 15, 7, 62, 54, 46, 38,
|
||||
30, 22, 14, 6, 61, 53, 45, 37,
|
||||
29, 21, 13, 5, 28, 20, 12, 4
|
||||
];
|
||||
|
||||
// Permuted Choice 2 constants
|
||||
var PC2 = [
|
||||
14, 17, 11, 24, 1, 5,
|
||||
3, 28, 15, 6, 21, 10,
|
||||
23, 19, 12, 4, 26, 8,
|
||||
16, 7, 27, 20, 13, 2,
|
||||
41, 52, 31, 37, 47, 55,
|
||||
30, 40, 51, 45, 33, 48,
|
||||
44, 49, 39, 56, 34, 53,
|
||||
46, 42, 50, 36, 29, 32
|
||||
];
|
||||
|
||||
// Cumulative bit shift constants
|
||||
var BIT_SHIFTS = [1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28];
|
||||
|
||||
// SBOXes and round permutation constants
|
||||
var SBOX_P = [
|
||||
{
|
||||
0x0: 0x808200,
|
||||
0x10000000: 0x8000,
|
||||
0x20000000: 0x808002,
|
||||
0x30000000: 0x2,
|
||||
0x40000000: 0x200,
|
||||
0x50000000: 0x808202,
|
||||
0x60000000: 0x800202,
|
||||
0x70000000: 0x800000,
|
||||
0x80000000: 0x202,
|
||||
0x90000000: 0x800200,
|
||||
0xa0000000: 0x8200,
|
||||
0xb0000000: 0x808000,
|
||||
0xc0000000: 0x8002,
|
||||
0xd0000000: 0x800002,
|
||||
0xe0000000: 0x0,
|
||||
0xf0000000: 0x8202,
|
||||
0x8000000: 0x0,
|
||||
0x18000000: 0x808202,
|
||||
0x28000000: 0x8202,
|
||||
0x38000000: 0x8000,
|
||||
0x48000000: 0x808200,
|
||||
0x58000000: 0x200,
|
||||
0x68000000: 0x808002,
|
||||
0x78000000: 0x2,
|
||||
0x88000000: 0x800200,
|
||||
0x98000000: 0x8200,
|
||||
0xa8000000: 0x808000,
|
||||
0xb8000000: 0x800202,
|
||||
0xc8000000: 0x800002,
|
||||
0xd8000000: 0x8002,
|
||||
0xe8000000: 0x202,
|
||||
0xf8000000: 0x800000,
|
||||
0x1: 0x8000,
|
||||
0x10000001: 0x2,
|
||||
0x20000001: 0x808200,
|
||||
0x30000001: 0x800000,
|
||||
0x40000001: 0x808002,
|
||||
0x50000001: 0x8200,
|
||||
0x60000001: 0x200,
|
||||
0x70000001: 0x800202,
|
||||
0x80000001: 0x808202,
|
||||
0x90000001: 0x808000,
|
||||
0xa0000001: 0x800002,
|
||||
0xb0000001: 0x8202,
|
||||
0xc0000001: 0x202,
|
||||
0xd0000001: 0x800200,
|
||||
0xe0000001: 0x8002,
|
||||
0xf0000001: 0x0,
|
||||
0x8000001: 0x808202,
|
||||
0x18000001: 0x808000,
|
||||
0x28000001: 0x800000,
|
||||
0x38000001: 0x200,
|
||||
0x48000001: 0x8000,
|
||||
0x58000001: 0x800002,
|
||||
0x68000001: 0x2,
|
||||
0x78000001: 0x8202,
|
||||
0x88000001: 0x8002,
|
||||
0x98000001: 0x800202,
|
||||
0xa8000001: 0x202,
|
||||
0xb8000001: 0x808200,
|
||||
0xc8000001: 0x800200,
|
||||
0xd8000001: 0x0,
|
||||
0xe8000001: 0x8200,
|
||||
0xf8000001: 0x808002
|
||||
},
|
||||
{
|
||||
0x0: 0x40084010,
|
||||
0x1000000: 0x4000,
|
||||
0x2000000: 0x80000,
|
||||
0x3000000: 0x40080010,
|
||||
0x4000000: 0x40000010,
|
||||
0x5000000: 0x40084000,
|
||||
0x6000000: 0x40004000,
|
||||
0x7000000: 0x10,
|
||||
0x8000000: 0x84000,
|
||||
0x9000000: 0x40004010,
|
||||
0xa000000: 0x40000000,
|
||||
0xb000000: 0x84010,
|
||||
0xc000000: 0x80010,
|
||||
0xd000000: 0x0,
|
||||
0xe000000: 0x4010,
|
||||
0xf000000: 0x40080000,
|
||||
0x800000: 0x40004000,
|
||||
0x1800000: 0x84010,
|
||||
0x2800000: 0x10,
|
||||
0x3800000: 0x40004010,
|
||||
0x4800000: 0x40084010,
|
||||
0x5800000: 0x40000000,
|
||||
0x6800000: 0x80000,
|
||||
0x7800000: 0x40080010,
|
||||
0x8800000: 0x80010,
|
||||
0x9800000: 0x0,
|
||||
0xa800000: 0x4000,
|
||||
0xb800000: 0x40080000,
|
||||
0xc800000: 0x40000010,
|
||||
0xd800000: 0x84000,
|
||||
0xe800000: 0x40084000,
|
||||
0xf800000: 0x4010,
|
||||
0x10000000: 0x0,
|
||||
0x11000000: 0x40080010,
|
||||
0x12000000: 0x40004010,
|
||||
0x13000000: 0x40084000,
|
||||
0x14000000: 0x40080000,
|
||||
0x15000000: 0x10,
|
||||
0x16000000: 0x84010,
|
||||
0x17000000: 0x4000,
|
||||
0x18000000: 0x4010,
|
||||
0x19000000: 0x80000,
|
||||
0x1a000000: 0x80010,
|
||||
0x1b000000: 0x40000010,
|
||||
0x1c000000: 0x84000,
|
||||
0x1d000000: 0x40004000,
|
||||
0x1e000000: 0x40000000,
|
||||
0x1f000000: 0x40084010,
|
||||
0x10800000: 0x84010,
|
||||
0x11800000: 0x80000,
|
||||
0x12800000: 0x40080000,
|
||||
0x13800000: 0x4000,
|
||||
0x14800000: 0x40004000,
|
||||
0x15800000: 0x40084010,
|
||||
0x16800000: 0x10,
|
||||
0x17800000: 0x40000000,
|
||||
0x18800000: 0x40084000,
|
||||
0x19800000: 0x40000010,
|
||||
0x1a800000: 0x40004010,
|
||||
0x1b800000: 0x80010,
|
||||
0x1c800000: 0x0,
|
||||
0x1d800000: 0x4010,
|
||||
0x1e800000: 0x40080010,
|
||||
0x1f800000: 0x84000
|
||||
},
|
||||
{
|
||||
0x0: 0x104,
|
||||
0x100000: 0x0,
|
||||
0x200000: 0x4000100,
|
||||
0x300000: 0x10104,
|
||||
0x400000: 0x10004,
|
||||
0x500000: 0x4000004,
|
||||
0x600000: 0x4010104,
|
||||
0x700000: 0x4010000,
|
||||
0x800000: 0x4000000,
|
||||
0x900000: 0x4010100,
|
||||
0xa00000: 0x10100,
|
||||
0xb00000: 0x4010004,
|
||||
0xc00000: 0x4000104,
|
||||
0xd00000: 0x10000,
|
||||
0xe00000: 0x4,
|
||||
0xf00000: 0x100,
|
||||
0x80000: 0x4010100,
|
||||
0x180000: 0x4010004,
|
||||
0x280000: 0x0,
|
||||
0x380000: 0x4000100,
|
||||
0x480000: 0x4000004,
|
||||
0x580000: 0x10000,
|
||||
0x680000: 0x10004,
|
||||
0x780000: 0x104,
|
||||
0x880000: 0x4,
|
||||
0x980000: 0x100,
|
||||
0xa80000: 0x4010000,
|
||||
0xb80000: 0x10104,
|
||||
0xc80000: 0x10100,
|
||||
0xd80000: 0x4000104,
|
||||
0xe80000: 0x4010104,
|
||||
0xf80000: 0x4000000,
|
||||
0x1000000: 0x4010100,
|
||||
0x1100000: 0x10004,
|
||||
0x1200000: 0x10000,
|
||||
0x1300000: 0x4000100,
|
||||
0x1400000: 0x100,
|
||||
0x1500000: 0x4010104,
|
||||
0x1600000: 0x4000004,
|
||||
0x1700000: 0x0,
|
||||
0x1800000: 0x4000104,
|
||||
0x1900000: 0x4000000,
|
||||
0x1a00000: 0x4,
|
||||
0x1b00000: 0x10100,
|
||||
0x1c00000: 0x4010000,
|
||||
0x1d00000: 0x104,
|
||||
0x1e00000: 0x10104,
|
||||
0x1f00000: 0x4010004,
|
||||
0x1080000: 0x4000000,
|
||||
0x1180000: 0x104,
|
||||
0x1280000: 0x4010100,
|
||||
0x1380000: 0x0,
|
||||
0x1480000: 0x10004,
|
||||
0x1580000: 0x4000100,
|
||||
0x1680000: 0x100,
|
||||
0x1780000: 0x4010004,
|
||||
0x1880000: 0x10000,
|
||||
0x1980000: 0x4010104,
|
||||
0x1a80000: 0x10104,
|
||||
0x1b80000: 0x4000004,
|
||||
0x1c80000: 0x4000104,
|
||||
0x1d80000: 0x4010000,
|
||||
0x1e80000: 0x4,
|
||||
0x1f80000: 0x10100
|
||||
},
|
||||
{
|
||||
0x0: 0x80401000,
|
||||
0x10000: 0x80001040,
|
||||
0x20000: 0x401040,
|
||||
0x30000: 0x80400000,
|
||||
0x40000: 0x0,
|
||||
0x50000: 0x401000,
|
||||
0x60000: 0x80000040,
|
||||
0x70000: 0x400040,
|
||||
0x80000: 0x80000000,
|
||||
0x90000: 0x400000,
|
||||
0xa0000: 0x40,
|
||||
0xb0000: 0x80001000,
|
||||
0xc0000: 0x80400040,
|
||||
0xd0000: 0x1040,
|
||||
0xe0000: 0x1000,
|
||||
0xf0000: 0x80401040,
|
||||
0x8000: 0x80001040,
|
||||
0x18000: 0x40,
|
||||
0x28000: 0x80400040,
|
||||
0x38000: 0x80001000,
|
||||
0x48000: 0x401000,
|
||||
0x58000: 0x80401040,
|
||||
0x68000: 0x0,
|
||||
0x78000: 0x80400000,
|
||||
0x88000: 0x1000,
|
||||
0x98000: 0x80401000,
|
||||
0xa8000: 0x400000,
|
||||
0xb8000: 0x1040,
|
||||
0xc8000: 0x80000000,
|
||||
0xd8000: 0x400040,
|
||||
0xe8000: 0x401040,
|
||||
0xf8000: 0x80000040,
|
||||
0x100000: 0x400040,
|
||||
0x110000: 0x401000,
|
||||
0x120000: 0x80000040,
|
||||
0x130000: 0x0,
|
||||
0x140000: 0x1040,
|
||||
0x150000: 0x80400040,
|
||||
0x160000: 0x80401000,
|
||||
0x170000: 0x80001040,
|
||||
0x180000: 0x80401040,
|
||||
0x190000: 0x80000000,
|
||||
0x1a0000: 0x80400000,
|
||||
0x1b0000: 0x401040,
|
||||
0x1c0000: 0x80001000,
|
||||
0x1d0000: 0x400000,
|
||||
0x1e0000: 0x40,
|
||||
0x1f0000: 0x1000,
|
||||
0x108000: 0x80400000,
|
||||
0x118000: 0x80401040,
|
||||
0x128000: 0x0,
|
||||
0x138000: 0x401000,
|
||||
0x148000: 0x400040,
|
||||
0x158000: 0x80000000,
|
||||
0x168000: 0x80001040,
|
||||
0x178000: 0x40,
|
||||
0x188000: 0x80000040,
|
||||
0x198000: 0x1000,
|
||||
0x1a8000: 0x80001000,
|
||||
0x1b8000: 0x80400040,
|
||||
0x1c8000: 0x1040,
|
||||
0x1d8000: 0x80401000,
|
||||
0x1e8000: 0x400000,
|
||||
0x1f8000: 0x401040
|
||||
},
|
||||
{
|
||||
0x0: 0x80,
|
||||
0x1000: 0x1040000,
|
||||
0x2000: 0x40000,
|
||||
0x3000: 0x20000000,
|
||||
0x4000: 0x20040080,
|
||||
0x5000: 0x1000080,
|
||||
0x6000: 0x21000080,
|
||||
0x7000: 0x40080,
|
||||
0x8000: 0x1000000,
|
||||
0x9000: 0x20040000,
|
||||
0xa000: 0x20000080,
|
||||
0xb000: 0x21040080,
|
||||
0xc000: 0x21040000,
|
||||
0xd000: 0x0,
|
||||
0xe000: 0x1040080,
|
||||
0xf000: 0x21000000,
|
||||
0x800: 0x1040080,
|
||||
0x1800: 0x21000080,
|
||||
0x2800: 0x80,
|
||||
0x3800: 0x1040000,
|
||||
0x4800: 0x40000,
|
||||
0x5800: 0x20040080,
|
||||
0x6800: 0x21040000,
|
||||
0x7800: 0x20000000,
|
||||
0x8800: 0x20040000,
|
||||
0x9800: 0x0,
|
||||
0xa800: 0x21040080,
|
||||
0xb800: 0x1000080,
|
||||
0xc800: 0x20000080,
|
||||
0xd800: 0x21000000,
|
||||
0xe800: 0x1000000,
|
||||
0xf800: 0x40080,
|
||||
0x10000: 0x40000,
|
||||
0x11000: 0x80,
|
||||
0x12000: 0x20000000,
|
||||
0x13000: 0x21000080,
|
||||
0x14000: 0x1000080,
|
||||
0x15000: 0x21040000,
|
||||
0x16000: 0x20040080,
|
||||
0x17000: 0x1000000,
|
||||
0x18000: 0x21040080,
|
||||
0x19000: 0x21000000,
|
||||
0x1a000: 0x1040000,
|
||||
0x1b000: 0x20040000,
|
||||
0x1c000: 0x40080,
|
||||
0x1d000: 0x20000080,
|
||||
0x1e000: 0x0,
|
||||
0x1f000: 0x1040080,
|
||||
0x10800: 0x21000080,
|
||||
0x11800: 0x1000000,
|
||||
0x12800: 0x1040000,
|
||||
0x13800: 0x20040080,
|
||||
0x14800: 0x20000000,
|
||||
0x15800: 0x1040080,
|
||||
0x16800: 0x80,
|
||||
0x17800: 0x21040000,
|
||||
0x18800: 0x40080,
|
||||
0x19800: 0x21040080,
|
||||
0x1a800: 0x0,
|
||||
0x1b800: 0x21000000,
|
||||
0x1c800: 0x1000080,
|
||||
0x1d800: 0x40000,
|
||||
0x1e800: 0x20040000,
|
||||
0x1f800: 0x20000080
|
||||
},
|
||||
{
|
||||
0x0: 0x10000008,
|
||||
0x100: 0x2000,
|
||||
0x200: 0x10200000,
|
||||
0x300: 0x10202008,
|
||||
0x400: 0x10002000,
|
||||
0x500: 0x200000,
|
||||
0x600: 0x200008,
|
||||
0x700: 0x10000000,
|
||||
0x800: 0x0,
|
||||
0x900: 0x10002008,
|
||||
0xa00: 0x202000,
|
||||
0xb00: 0x8,
|
||||
0xc00: 0x10200008,
|
||||
0xd00: 0x202008,
|
||||
0xe00: 0x2008,
|
||||
0xf00: 0x10202000,
|
||||
0x80: 0x10200000,
|
||||
0x180: 0x10202008,
|
||||
0x280: 0x8,
|
||||
0x380: 0x200000,
|
||||
0x480: 0x202008,
|
||||
0x580: 0x10000008,
|
||||
0x680: 0x10002000,
|
||||
0x780: 0x2008,
|
||||
0x880: 0x200008,
|
||||
0x980: 0x2000,
|
||||
0xa80: 0x10002008,
|
||||
0xb80: 0x10200008,
|
||||
0xc80: 0x0,
|
||||
0xd80: 0x10202000,
|
||||
0xe80: 0x202000,
|
||||
0xf80: 0x10000000,
|
||||
0x1000: 0x10002000,
|
||||
0x1100: 0x10200008,
|
||||
0x1200: 0x10202008,
|
||||
0x1300: 0x2008,
|
||||
0x1400: 0x200000,
|
||||
0x1500: 0x10000000,
|
||||
0x1600: 0x10000008,
|
||||
0x1700: 0x202000,
|
||||
0x1800: 0x202008,
|
||||
0x1900: 0x0,
|
||||
0x1a00: 0x8,
|
||||
0x1b00: 0x10200000,
|
||||
0x1c00: 0x2000,
|
||||
0x1d00: 0x10002008,
|
||||
0x1e00: 0x10202000,
|
||||
0x1f00: 0x200008,
|
||||
0x1080: 0x8,
|
||||
0x1180: 0x202000,
|
||||
0x1280: 0x200000,
|
||||
0x1380: 0x10000008,
|
||||
0x1480: 0x10002000,
|
||||
0x1580: 0x2008,
|
||||
0x1680: 0x10202008,
|
||||
0x1780: 0x10200000,
|
||||
0x1880: 0x10202000,
|
||||
0x1980: 0x10200008,
|
||||
0x1a80: 0x2000,
|
||||
0x1b80: 0x202008,
|
||||
0x1c80: 0x200008,
|
||||
0x1d80: 0x0,
|
||||
0x1e80: 0x10000000,
|
||||
0x1f80: 0x10002008
|
||||
},
|
||||
{
|
||||
0x0: 0x100000,
|
||||
0x10: 0x2000401,
|
||||
0x20: 0x400,
|
||||
0x30: 0x100401,
|
||||
0x40: 0x2100401,
|
||||
0x50: 0x0,
|
||||
0x60: 0x1,
|
||||
0x70: 0x2100001,
|
||||
0x80: 0x2000400,
|
||||
0x90: 0x100001,
|
||||
0xa0: 0x2000001,
|
||||
0xb0: 0x2100400,
|
||||
0xc0: 0x2100000,
|
||||
0xd0: 0x401,
|
||||
0xe0: 0x100400,
|
||||
0xf0: 0x2000000,
|
||||
0x8: 0x2100001,
|
||||
0x18: 0x0,
|
||||
0x28: 0x2000401,
|
||||
0x38: 0x2100400,
|
||||
0x48: 0x100000,
|
||||
0x58: 0x2000001,
|
||||
0x68: 0x2000000,
|
||||
0x78: 0x401,
|
||||
0x88: 0x100401,
|
||||
0x98: 0x2000400,
|
||||
0xa8: 0x2100000,
|
||||
0xb8: 0x100001,
|
||||
0xc8: 0x400,
|
||||
0xd8: 0x2100401,
|
||||
0xe8: 0x1,
|
||||
0xf8: 0x100400,
|
||||
0x100: 0x2000000,
|
||||
0x110: 0x100000,
|
||||
0x120: 0x2000401,
|
||||
0x130: 0x2100001,
|
||||
0x140: 0x100001,
|
||||
0x150: 0x2000400,
|
||||
0x160: 0x2100400,
|
||||
0x170: 0x100401,
|
||||
0x180: 0x401,
|
||||
0x190: 0x2100401,
|
||||
0x1a0: 0x100400,
|
||||
0x1b0: 0x1,
|
||||
0x1c0: 0x0,
|
||||
0x1d0: 0x2100000,
|
||||
0x1e0: 0x2000001,
|
||||
0x1f0: 0x400,
|
||||
0x108: 0x100400,
|
||||
0x118: 0x2000401,
|
||||
0x128: 0x2100001,
|
||||
0x138: 0x1,
|
||||
0x148: 0x2000000,
|
||||
0x158: 0x100000,
|
||||
0x168: 0x401,
|
||||
0x178: 0x2100400,
|
||||
0x188: 0x2000001,
|
||||
0x198: 0x2100000,
|
||||
0x1a8: 0x0,
|
||||
0x1b8: 0x2100401,
|
||||
0x1c8: 0x100401,
|
||||
0x1d8: 0x400,
|
||||
0x1e8: 0x2000400,
|
||||
0x1f8: 0x100001
|
||||
},
|
||||
{
|
||||
0x0: 0x8000820,
|
||||
0x1: 0x20000,
|
||||
0x2: 0x8000000,
|
||||
0x3: 0x20,
|
||||
0x4: 0x20020,
|
||||
0x5: 0x8020820,
|
||||
0x6: 0x8020800,
|
||||
0x7: 0x800,
|
||||
0x8: 0x8020000,
|
||||
0x9: 0x8000800,
|
||||
0xa: 0x20800,
|
||||
0xb: 0x8020020,
|
||||
0xc: 0x820,
|
||||
0xd: 0x0,
|
||||
0xe: 0x8000020,
|
||||
0xf: 0x20820,
|
||||
0x80000000: 0x800,
|
||||
0x80000001: 0x8020820,
|
||||
0x80000002: 0x8000820,
|
||||
0x80000003: 0x8000000,
|
||||
0x80000004: 0x8020000,
|
||||
0x80000005: 0x20800,
|
||||
0x80000006: 0x20820,
|
||||
0x80000007: 0x20,
|
||||
0x80000008: 0x8000020,
|
||||
0x80000009: 0x820,
|
||||
0x8000000a: 0x20020,
|
||||
0x8000000b: 0x8020800,
|
||||
0x8000000c: 0x0,
|
||||
0x8000000d: 0x8020020,
|
||||
0x8000000e: 0x8000800,
|
||||
0x8000000f: 0x20000,
|
||||
0x10: 0x20820,
|
||||
0x11: 0x8020800,
|
||||
0x12: 0x20,
|
||||
0x13: 0x800,
|
||||
0x14: 0x8000800,
|
||||
0x15: 0x8000020,
|
||||
0x16: 0x8020020,
|
||||
0x17: 0x20000,
|
||||
0x18: 0x0,
|
||||
0x19: 0x20020,
|
||||
0x1a: 0x8020000,
|
||||
0x1b: 0x8000820,
|
||||
0x1c: 0x8020820,
|
||||
0x1d: 0x20800,
|
||||
0x1e: 0x820,
|
||||
0x1f: 0x8000000,
|
||||
0x80000010: 0x20000,
|
||||
0x80000011: 0x800,
|
||||
0x80000012: 0x8020020,
|
||||
0x80000013: 0x20820,
|
||||
0x80000014: 0x20,
|
||||
0x80000015: 0x8020000,
|
||||
0x80000016: 0x8000000,
|
||||
0x80000017: 0x8000820,
|
||||
0x80000018: 0x8020820,
|
||||
0x80000019: 0x8000020,
|
||||
0x8000001a: 0x8000800,
|
||||
0x8000001b: 0x0,
|
||||
0x8000001c: 0x20800,
|
||||
0x8000001d: 0x820,
|
||||
0x8000001e: 0x20020,
|
||||
0x8000001f: 0x8020800
|
||||
}
|
||||
];
|
||||
|
||||
// Masks that select the SBOX input
|
||||
var SBOX_MASK = [
|
||||
0xf8000001, 0x1f800000, 0x01f80000, 0x001f8000,
|
||||
0x0001f800, 0x00001f80, 0x000001f8, 0x8000001f
|
||||
];
|
||||
|
||||
/**
|
||||
* DES block cipher algorithm.
|
||||
*/
|
||||
var DES = C_algo.DES = BlockCipher.extend({
|
||||
_doReset: function () {
|
||||
// Shortcuts
|
||||
var key = this._key;
|
||||
var keyWords = key.words;
|
||||
|
||||
// Select 56 bits according to PC1
|
||||
var keyBits = [];
|
||||
for (var i = 0; i < 56; i++) {
|
||||
var keyBitPos = PC1[i] - 1;
|
||||
keyBits[i] = (keyWords[keyBitPos >>> 5] >>> (31 - keyBitPos % 32)) & 1;
|
||||
}
|
||||
|
||||
// Assemble 16 subkeys
|
||||
var subKeys = this._subKeys = [];
|
||||
for (var nSubKey = 0; nSubKey < 16; nSubKey++) {
|
||||
// Create subkey
|
||||
var subKey = subKeys[nSubKey] = [];
|
||||
|
||||
// Shortcut
|
||||
var bitShift = BIT_SHIFTS[nSubKey];
|
||||
|
||||
// Select 48 bits according to PC2
|
||||
for (var i = 0; i < 24; i++) {
|
||||
// Select from the left 28 key bits
|
||||
subKey[(i / 6) | 0] |= keyBits[((PC2[i] - 1) + bitShift) % 28] << (31 - i % 6);
|
||||
|
||||
// Select from the right 28 key bits
|
||||
subKey[4 + ((i / 6) | 0)] |= keyBits[28 + (((PC2[i + 24] - 1) + bitShift) % 28)] << (31 - i % 6);
|
||||
}
|
||||
|
||||
// Since each subkey is applied to an expanded 32-bit input,
|
||||
// the subkey can be broken into 8 values scaled to 32-bits,
|
||||
// which allows the key to be used without expansion
|
||||
subKey[0] = (subKey[0] << 1) | (subKey[0] >>> 31);
|
||||
for (var i = 1; i < 7; i++) {
|
||||
subKey[i] = subKey[i] >>> ((i - 1) * 4 + 3);
|
||||
}
|
||||
subKey[7] = (subKey[7] << 5) | (subKey[7] >>> 27);
|
||||
}
|
||||
|
||||
// Compute inverse subkeys
|
||||
var invSubKeys = this._invSubKeys = [];
|
||||
for (var i = 0; i < 16; i++) {
|
||||
invSubKeys[i] = subKeys[15 - i];
|
||||
}
|
||||
},
|
||||
|
||||
encryptBlock: function (M, offset) {
|
||||
this._doCryptBlock(M, offset, this._subKeys);
|
||||
},
|
||||
|
||||
decryptBlock: function (M, offset) {
|
||||
this._doCryptBlock(M, offset, this._invSubKeys);
|
||||
},
|
||||
|
||||
_doCryptBlock: function (M, offset, subKeys) {
|
||||
// Get input
|
||||
this._lBlock = M[offset];
|
||||
this._rBlock = M[offset + 1];
|
||||
|
||||
// Initial permutation
|
||||
exchangeLR.call(this, 4, 0x0f0f0f0f);
|
||||
exchangeLR.call(this, 16, 0x0000ffff);
|
||||
exchangeRL.call(this, 2, 0x33333333);
|
||||
exchangeRL.call(this, 8, 0x00ff00ff);
|
||||
exchangeLR.call(this, 1, 0x55555555);
|
||||
|
||||
// Rounds
|
||||
for (var round = 0; round < 16; round++) {
|
||||
// Shortcuts
|
||||
var subKey = subKeys[round];
|
||||
var lBlock = this._lBlock;
|
||||
var rBlock = this._rBlock;
|
||||
|
||||
// Feistel function
|
||||
var f = 0;
|
||||
for (var i = 0; i < 8; i++) {
|
||||
f |= SBOX_P[i][((rBlock ^ subKey[i]) & SBOX_MASK[i]) >>> 0];
|
||||
}
|
||||
this._lBlock = rBlock;
|
||||
this._rBlock = lBlock ^ f;
|
||||
}
|
||||
|
||||
// Undo swap from last round
|
||||
var t = this._lBlock;
|
||||
this._lBlock = this._rBlock;
|
||||
this._rBlock = t;
|
||||
|
||||
// Final permutation
|
||||
exchangeLR.call(this, 1, 0x55555555);
|
||||
exchangeRL.call(this, 8, 0x00ff00ff);
|
||||
exchangeRL.call(this, 2, 0x33333333);
|
||||
exchangeLR.call(this, 16, 0x0000ffff);
|
||||
exchangeLR.call(this, 4, 0x0f0f0f0f);
|
||||
|
||||
// Set output
|
||||
M[offset] = this._lBlock;
|
||||
M[offset + 1] = this._rBlock;
|
||||
},
|
||||
|
||||
keySize: 64/32,
|
||||
|
||||
ivSize: 64/32,
|
||||
|
||||
blockSize: 64/32
|
||||
});
|
||||
|
||||
// Swap bits across the left and right words
|
||||
function exchangeLR(offset, mask) {
|
||||
var t = ((this._lBlock >>> offset) ^ this._rBlock) & mask;
|
||||
this._rBlock ^= t;
|
||||
this._lBlock ^= t << offset;
|
||||
}
|
||||
|
||||
function exchangeRL(offset, mask) {
|
||||
var t = ((this._rBlock >>> offset) ^ this._lBlock) & mask;
|
||||
this._lBlock ^= t;
|
||||
this._rBlock ^= t << offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut functions to the cipher's object interface.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var ciphertext = CryptoJS.DES.encrypt(message, key, cfg);
|
||||
* var plaintext = CryptoJS.DES.decrypt(ciphertext, key, cfg);
|
||||
*/
|
||||
C.DES = BlockCipher._createHelper(DES);
|
||||
|
||||
/**
|
||||
* Triple-DES block cipher algorithm.
|
||||
*/
|
||||
var TripleDES = C_algo.TripleDES = BlockCipher.extend({
|
||||
_doReset: function () {
|
||||
// Shortcuts
|
||||
var key = this._key;
|
||||
var keyWords = key.words;
|
||||
// Make sure the key length is valid (64, 128 or >= 192 bit)
|
||||
if (keyWords.length !== 2 && keyWords.length !== 4 && keyWords.length < 6) {
|
||||
throw new Error('Invalid key length - 3DES requires the key length to be 64, 128, 192 or >192.');
|
||||
}
|
||||
|
||||
// Extend the key according to the keying options defined in 3DES standard
|
||||
var key1 = keyWords.slice(0, 2);
|
||||
var key2 = keyWords.length < 4 ? keyWords.slice(0, 2) : keyWords.slice(2, 4);
|
||||
var key3 = keyWords.length < 6 ? keyWords.slice(0, 2) : keyWords.slice(4, 6);
|
||||
|
||||
// Create DES instances
|
||||
this._des1 = DES.createEncryptor(WordArray.create(key1));
|
||||
this._des2 = DES.createEncryptor(WordArray.create(key2));
|
||||
this._des3 = DES.createEncryptor(WordArray.create(key3));
|
||||
},
|
||||
|
||||
encryptBlock: function (M, offset) {
|
||||
this._des1.encryptBlock(M, offset);
|
||||
this._des2.decryptBlock(M, offset);
|
||||
this._des3.encryptBlock(M, offset);
|
||||
},
|
||||
|
||||
decryptBlock: function (M, offset) {
|
||||
this._des3.decryptBlock(M, offset);
|
||||
this._des2.encryptBlock(M, offset);
|
||||
this._des1.decryptBlock(M, offset);
|
||||
},
|
||||
|
||||
keySize: 192/32,
|
||||
|
||||
ivSize: 64/32,
|
||||
|
||||
blockSize: 64/32
|
||||
});
|
||||
|
||||
/**
|
||||
* Shortcut functions to the cipher's object interface.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var ciphertext = CryptoJS.TripleDES.encrypt(message, key, cfg);
|
||||
* var plaintext = CryptoJS.TripleDES.decrypt(ciphertext, key, cfg);
|
||||
*/
|
||||
C.TripleDES = BlockCipher._createHelper(TripleDES);
|
||||
}());
|
||||
|
||||
|
||||
return CryptoJS.TripleDES;
|
||||
|
||||
}));
|
@ -0,0 +1,304 @@
|
||||
;(function (root, factory) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
(function (undefined) {
|
||||
// Shortcuts
|
||||
var C = CryptoJS;
|
||||
var C_lib = C.lib;
|
||||
var Base = C_lib.Base;
|
||||
var X32WordArray = C_lib.WordArray;
|
||||
|
||||
/**
|
||||
* x64 namespace.
|
||||
*/
|
||||
var C_x64 = C.x64 = {};
|
||||
|
||||
/**
|
||||
* A 64-bit word.
|
||||
*/
|
||||
var X64Word = C_x64.Word = Base.extend({
|
||||
/**
|
||||
* Initializes a newly created 64-bit word.
|
||||
*
|
||||
* @param {number} high The high 32 bits.
|
||||
* @param {number} low The low 32 bits.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var x64Word = CryptoJS.x64.Word.create(0x00010203, 0x04050607);
|
||||
*/
|
||||
init: function (high, low) {
|
||||
this.high = high;
|
||||
this.low = low;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bitwise NOTs this word.
|
||||
*
|
||||
* @return {X64Word} A new x64-Word object after negating.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var negated = x64Word.not();
|
||||
*/
|
||||
// not: function () {
|
||||
// var high = ~this.high;
|
||||
// var low = ~this.low;
|
||||
|
||||
// return X64Word.create(high, low);
|
||||
// },
|
||||
|
||||
/**
|
||||
* Bitwise ANDs this word with the passed word.
|
||||
*
|
||||
* @param {X64Word} word The x64-Word to AND with this word.
|
||||
*
|
||||
* @return {X64Word} A new x64-Word object after ANDing.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var anded = x64Word.and(anotherX64Word);
|
||||
*/
|
||||
// and: function (word) {
|
||||
// var high = this.high & word.high;
|
||||
// var low = this.low & word.low;
|
||||
|
||||
// return X64Word.create(high, low);
|
||||
// },
|
||||
|
||||
/**
|
||||
* Bitwise ORs this word with the passed word.
|
||||
*
|
||||
* @param {X64Word} word The x64-Word to OR with this word.
|
||||
*
|
||||
* @return {X64Word} A new x64-Word object after ORing.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var ored = x64Word.or(anotherX64Word);
|
||||
*/
|
||||
// or: function (word) {
|
||||
// var high = this.high | word.high;
|
||||
// var low = this.low | word.low;
|
||||
|
||||
// return X64Word.create(high, low);
|
||||
// },
|
||||
|
||||
/**
|
||||
* Bitwise XORs this word with the passed word.
|
||||
*
|
||||
* @param {X64Word} word The x64-Word to XOR with this word.
|
||||
*
|
||||
* @return {X64Word} A new x64-Word object after XORing.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var xored = x64Word.xor(anotherX64Word);
|
||||
*/
|
||||
// xor: function (word) {
|
||||
// var high = this.high ^ word.high;
|
||||
// var low = this.low ^ word.low;
|
||||
|
||||
// return X64Word.create(high, low);
|
||||
// },
|
||||
|
||||
/**
|
||||
* Shifts this word n bits to the left.
|
||||
*
|
||||
* @param {number} n The number of bits to shift.
|
||||
*
|
||||
* @return {X64Word} A new x64-Word object after shifting.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var shifted = x64Word.shiftL(25);
|
||||
*/
|
||||
// shiftL: function (n) {
|
||||
// if (n < 32) {
|
||||
// var high = (this.high << n) | (this.low >>> (32 - n));
|
||||
// var low = this.low << n;
|
||||
// } else {
|
||||
// var high = this.low << (n - 32);
|
||||
// var low = 0;
|
||||
// }
|
||||
|
||||
// return X64Word.create(high, low);
|
||||
// },
|
||||
|
||||
/**
|
||||
* Shifts this word n bits to the right.
|
||||
*
|
||||
* @param {number} n The number of bits to shift.
|
||||
*
|
||||
* @return {X64Word} A new x64-Word object after shifting.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var shifted = x64Word.shiftR(7);
|
||||
*/
|
||||
// shiftR: function (n) {
|
||||
// if (n < 32) {
|
||||
// var low = (this.low >>> n) | (this.high << (32 - n));
|
||||
// var high = this.high >>> n;
|
||||
// } else {
|
||||
// var low = this.high >>> (n - 32);
|
||||
// var high = 0;
|
||||
// }
|
||||
|
||||
// return X64Word.create(high, low);
|
||||
// },
|
||||
|
||||
/**
|
||||
* Rotates this word n bits to the left.
|
||||
*
|
||||
* @param {number} n The number of bits to rotate.
|
||||
*
|
||||
* @return {X64Word} A new x64-Word object after rotating.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var rotated = x64Word.rotL(25);
|
||||
*/
|
||||
// rotL: function (n) {
|
||||
// return this.shiftL(n).or(this.shiftR(64 - n));
|
||||
// },
|
||||
|
||||
/**
|
||||
* Rotates this word n bits to the right.
|
||||
*
|
||||
* @param {number} n The number of bits to rotate.
|
||||
*
|
||||
* @return {X64Word} A new x64-Word object after rotating.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var rotated = x64Word.rotR(7);
|
||||
*/
|
||||
// rotR: function (n) {
|
||||
// return this.shiftR(n).or(this.shiftL(64 - n));
|
||||
// },
|
||||
|
||||
/**
|
||||
* Adds this word with the passed word.
|
||||
*
|
||||
* @param {X64Word} word The x64-Word to add with this word.
|
||||
*
|
||||
* @return {X64Word} A new x64-Word object after adding.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var added = x64Word.add(anotherX64Word);
|
||||
*/
|
||||
// add: function (word) {
|
||||
// var low = (this.low + word.low) | 0;
|
||||
// var carry = (low >>> 0) < (this.low >>> 0) ? 1 : 0;
|
||||
// var high = (this.high + word.high + carry) | 0;
|
||||
|
||||
// return X64Word.create(high, low);
|
||||
// }
|
||||
});
|
||||
|
||||
/**
|
||||
* An array of 64-bit words.
|
||||
*
|
||||
* @property {Array} words The array of CryptoJS.x64.Word objects.
|
||||
* @property {number} sigBytes The number of significant bytes in this word array.
|
||||
*/
|
||||
var X64WordArray = C_x64.WordArray = Base.extend({
|
||||
/**
|
||||
* Initializes a newly created word array.
|
||||
*
|
||||
* @param {Array} words (Optional) An array of CryptoJS.x64.Word objects.
|
||||
* @param {number} sigBytes (Optional) The number of significant bytes in the words.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var wordArray = CryptoJS.x64.WordArray.create();
|
||||
*
|
||||
* var wordArray = CryptoJS.x64.WordArray.create([
|
||||
* CryptoJS.x64.Word.create(0x00010203, 0x04050607),
|
||||
* CryptoJS.x64.Word.create(0x18191a1b, 0x1c1d1e1f)
|
||||
* ]);
|
||||
*
|
||||
* var wordArray = CryptoJS.x64.WordArray.create([
|
||||
* CryptoJS.x64.Word.create(0x00010203, 0x04050607),
|
||||
* CryptoJS.x64.Word.create(0x18191a1b, 0x1c1d1e1f)
|
||||
* ], 10);
|
||||
*/
|
||||
init: function (words, sigBytes) {
|
||||
words = this.words = words || [];
|
||||
|
||||
if (sigBytes != undefined) {
|
||||
this.sigBytes = sigBytes;
|
||||
} else {
|
||||
this.sigBytes = words.length * 8;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts this 64-bit word array to a 32-bit word array.
|
||||
*
|
||||
* @return {CryptoJS.lib.WordArray} This word array's data as a 32-bit word array.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var x32WordArray = x64WordArray.toX32();
|
||||
*/
|
||||
toX32: function () {
|
||||
// Shortcuts
|
||||
var x64Words = this.words;
|
||||
var x64WordsLength = x64Words.length;
|
||||
|
||||
// Convert
|
||||
var x32Words = [];
|
||||
for (var i = 0; i < x64WordsLength; i++) {
|
||||
var x64Word = x64Words[i];
|
||||
x32Words.push(x64Word.high);
|
||||
x32Words.push(x64Word.low);
|
||||
}
|
||||
|
||||
return X32WordArray.create(x32Words, this.sigBytes);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a copy of this word array.
|
||||
*
|
||||
* @return {X64WordArray} The clone.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var clone = x64WordArray.clone();
|
||||
*/
|
||||
clone: function () {
|
||||
var clone = Base.clone.call(this);
|
||||
|
||||
// Clone "words" array
|
||||
var words = clone.words = this.words.slice(0);
|
||||
|
||||
// Clone each X64Word object
|
||||
var wordsLength = words.length;
|
||||
for (var i = 0; i < wordsLength; i++) {
|
||||
words[i] = words[i].clone();
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
});
|
||||
}());
|
||||
|
||||
|
||||
return CryptoJS;
|
||||
|
||||
}));
|
@ -0,0 +1,16 @@
|
||||
.header-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
.header-title .stop-btn {
|
||||
padding: 8px;
|
||||
border: 1px solid #009688;
|
||||
border-radius: 4px;
|
||||
font-size: 16px;
|
||||
color: #009688;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=index.css.map */
|
@ -0,0 +1 @@
|
||||
html #layuicss-skincodecss{display:none;position:absolute;width:1989px}.layui-code-view{display:block;position:relative;margin:10px 0;padding:0;border:1px solid #eee;border-left-width:6px;background-color:#fafafa;color:#333;font-family:Courier New;font-size:13px}.layui-code-title{position:relative;padding:0 10px;height:40px;line-height:40px;border-bottom:1px solid #eee;font-size:12px}.layui-code-title>.layui-code-about{position:absolute;right:10px;top:0;color:#b7b7b7}.layui-code-about>a{padding-left:10px}.layui-code-view>.layui-code-ol,.layui-code-view>.layui-code-ul{position:relative;overflow:auto}.layui-code-view>.layui-code-ol>li{position:relative;margin-left:45px;line-height:20px;padding:0 10px;border-left:1px solid #e2e2e2;list-style-type:decimal-leading-zero;*list-style-type:decimal;background-color:#fff}.layui-code-view>.layui-code-ol>li:first-child,.layui-code-view>.layui-code-ul>li:first-child{padding-top:10px}.layui-code-view>.layui-code-ol>li:last-child,.layui-code-view>.layui-code-ul>li:last-child{padding-bottom:10px}.layui-code-view>.layui-code-ul>li{position:relative;line-height:20px;padding:0 10px;list-style-type:none;*list-style-type:none;background-color:#fff}.layui-code-view pre{margin:0}.layui-code-dark{border:1px solid #0c0c0c;border-left-color:#3f3f3f;background-color:#0c0c0c;color:#c2be9e}.layui-code-dark>.layui-code-title{border-bottom:none}.layui-code-dark>.layui-code-ol>li,.layui-code-dark>.layui-code-ul>li{background-color:#3f3f3f;border-left:none}.layui-code-dark>.layui-code-ul>li{margin-left:6px}.layui-code-demo .layui-code{visibility:visible!important;margin:-15px;border-top:none;border-right:none;border-bottom:none}.layui-code-demo .layui-tab-content{padding:15px;border-top:none}
|
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 5.7 KiB |
After Width: | Height: | Size: 701 B |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 299 KiB |
@ -0,0 +1,97 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.main {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.main .content {
|
||||
position: absolute;
|
||||
top: 150px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
.main .content .logo {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
.main .content .logo img {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
}
|
||||
.main .content .logo .title {
|
||||
margin-top: 40px;
|
||||
font-size: 24px;
|
||||
}
|
||||
.main .content .logo .tips {
|
||||
margin-top: 8px;
|
||||
font-size: 16px;
|
||||
color: #707579;
|
||||
}
|
||||
.main .content .password-input {
|
||||
position: relative;
|
||||
width: 408px;
|
||||
margin-top: 48px;
|
||||
}
|
||||
.main .content .password-input:hover .tip {
|
||||
color: #3390ec !important;
|
||||
}
|
||||
.main .content .password-input .input-p {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 54px;
|
||||
padding: 11px 13px;
|
||||
border: 1px solid #dadce0;
|
||||
border-radius: 12px;
|
||||
font-size: 16px;
|
||||
transition: transform 0.15s ease-out, color 0.15s ease-out;
|
||||
outline: none;
|
||||
}
|
||||
.main .content .password-input .input-p:focus {
|
||||
outline: 2px solid #3390ec;
|
||||
}
|
||||
.main .content .password-input .tip {
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
top: 15px;
|
||||
padding: 0 4px;
|
||||
background-color: #fff;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
transition: transform 0.15s ease-out, color 0.15s ease-out;
|
||||
transform-origin: left center;
|
||||
color: #707579;
|
||||
transform: scale(0.75) translate(-8px, -36px);
|
||||
}
|
||||
.main .content .confirm-btn {
|
||||
margin-top: 44px;
|
||||
outline: none !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 56px;
|
||||
border: 0;
|
||||
border-radius: 12px;
|
||||
background-color: rgba(74, 149, 214, 0.08);
|
||||
background-size: cover;
|
||||
padding: 10px;
|
||||
color: #3390ec;
|
||||
line-height: 1.2;
|
||||
cursor: pointer;
|
||||
text-transform: uppercase;
|
||||
flex-shrink: 0;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: background-color 0.15s, color 0.15s;
|
||||
text-decoration: none !important;
|
||||
--premium-gradient: linear-gradient(88.39deg, #6C93FF -2.56%, #976FFF 51.27%, #DF69D1 107.39%);
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=index.css.map */
|
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120"><defs><linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="a"><stop stop-color="#38AEEB" offset="0%"/><stop stop-color="#279AD1" offset="100%"/></linearGradient></defs><g fill="none"><circle fill="url(#a)" cx="60" cy="60" r="60"/><path d="M23.775 58.77a3278.85 3278.85 0 0 1 39.27-16.223c18.698-7.454 21.3-8.542 23.828-8.58a4.995 4.995 0 0 1 2.977 1.103c1.058.9 1.38 1.47 1.47 1.972.083.503.075 2.07-.015 2.963-1.013 10.207-4.86 33.78-7.088 45.225-.945 4.837-2.805 6.457-4.605 6.615-3.907.345-6.877-2.475-10.664-4.86-5.925-3.728-7.905-5.1-13.65-8.737-6.653-4.2-3.916-5.663-.128-9.436.99-.982 17.415-15.974 17.662-17.34.21-1.2.286-1.357-.254-1.897-.548-.54-1.2-.473-1.62-.383-.6.128-9.645 5.85-27.15 17.176-2.685 1.777-5.115 2.64-7.298 2.595-2.4-.053-7.027-1.305-10.462-2.378-4.223-1.32-7.575-2.01-7.275-4.245.15-1.163 1.814-2.355 5.002-3.57Z" fill="#FFF"/></g></svg>
|
After Width: | Height: | Size: 932 B |