├── entrypoint.sh ├── requirements.txt ├── ClickToRun.cmd ├── .deepsource.toml ├── configexample.yml ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug-report.md └── workflows │ ├── checkin.yml │ └── repo_sync.yml ├── encrypt.py ├── .all-contributorsrc ├── docker └── dockerfile ├── .gitignore ├── doc └── analysis.md ├── ConfigureOnce.ps1 ├── README.md ├── model.py ├── LICENSE ├── json ├── model.json └── new_model.json ├── sduhealth.py ├── js └── des.js └── des.py /entrypoint.sh: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | beautifulsoup4==4.9.1 2 | demjson==2.2.4 3 | pytz==2020.5 4 | PyYAML==5.4 5 | requests==2.23.0 6 | colorclass==2.2.0 7 | -------------------------------------------------------------------------------- /ClickToRun.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | @REM set CONFIG= // BE AWARE OF WHAT YOU ARE DOING 3 | python sduhealth.py 4 | echo Press any key to exit. 5 | pause>nul -------------------------------------------------------------------------------- /.deepsource.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | 3 | [[analyzers]] 4 | name = "javascript" 5 | enabled = true 6 | 7 | [[analyzers]] 8 | name = "python" 9 | enabled = true 10 | 11 | [analyzers.meta] 12 | runtime_version = "3.x.x" -------------------------------------------------------------------------------- /configexample.yml: -------------------------------------------------------------------------------- 1 | name: Scripts for Health Sign 2 | version: v1.0 3 | 4 | # 脚本配置信息 5 | 6 | # 手动配置方法: 7 | # 将{STUID}替换为学号,{PSWD}替换为自己的密码。 8 | # 注意,手动配置时请连同前后的大括号一同删除替换。 9 | 10 | # 支持多个账户打卡,编辑时请保持yaml的格式不变。去掉"#"号即可 11 | 12 | jobs: 13 | studentID: 14 | - "{STUID}" 15 | #- "第二个账号" 16 | #- "第三个账号" 17 | studentPassword: 18 | - "{PSWD}" 19 | #- "第二个密码" 20 | #- "第三个密码" 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: viewv 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | 请说明你遇到的问题。 14 | 15 | **Operating mode** 16 | Operating mode, local run or GitHub Actions. 17 | 18 | 说明你的运行模式,本地运行还是 Github Actions 模式。 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | 期望的结果。 24 | 25 | **Screenshots** 26 | If applicable, add screenshots to help explain your problem. 27 | 28 | 请务必将运行结果截图或者复制相关文字附加在反馈中。 29 | 30 | 如果有其他相关内容截图,也请附加于此。 31 | 32 | **Environment:** 33 | 34 | 如果你是在本地运行的,请完成本节内容 35 | 36 | - OS: 37 | - Python version: 38 | - Node.js version: 39 | 40 | **Additional context** 41 | Add any other context about the problem here. 42 | 43 | 任何其他相关内容。 44 | -------------------------------------------------------------------------------- /.github/workflows/checkin.yml: -------------------------------------------------------------------------------- 1 | name: checkin 2 | 3 | on: 4 | schedule: 5 | - cron: "0 0,8 * * *" # scheduled at 08:00 (UTC+8) and 16:00(UTC+8) everyday 6 | workflow_dispatch: 7 | push: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | checkin: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Set up Python 3.8 18 | uses: actions/setup-python@v2 19 | with: 20 | python-version: 3.8 21 | - name: Install dependencies 22 | run: | 23 | python -m pip install --upgrade pip 24 | pip install -r requirements.txt 25 | 26 | - name: Random sleep 27 | if: github.event_name == 'schedule' 28 | run: sleep $(shuf -i 10-300 -n 1) 29 | 30 | - name: 运行打卡脚本 31 | env: 32 | CONFIG: ${{ secrets.CONFIG }} 33 | run: | 34 | python sduhealth.py 35 | -------------------------------------------------------------------------------- /.github/workflows/repo_sync.yml: -------------------------------------------------------------------------------- 1 | # File: .github/workflows/repo-sync.yml 2 | name: sync-healsign-scripts 3 | on: 4 | schedule: 5 | - cron: '1 0,12,22 * * *' 6 | workflow_dispatch: 7 | watch: 8 | types: started 9 | repository_dispatch: 10 | types: sync-healsign-scripts 11 | jobs: 12 | repo-sync: 13 | env: 14 | PAT: ${{ secrets.PAT }} #此处PAT需要申请,教程详见:https://www.jianshu.com/p/bb82b3ad1d11 15 | runs-on: ubuntu-latest 16 | if: github.event.repository.owner.id == github.event.sender.id 17 | steps: 18 | - uses: actions/checkout@v2 19 | with: 20 | persist-credentials: false 21 | 22 | - name: sync healsign-scripts 23 | uses: repo-sync/github-sync@v2 24 | if: env.PAT 25 | with: 26 | source_repo: "https://github.com/viewv/sduhealth.git" 27 | source_branch: "main" 28 | destination_branch: "main" 29 | github_token: ${{ secrets.PAT }} -------------------------------------------------------------------------------- /encrypt.py: -------------------------------------------------------------------------------- 1 | from des import * 2 | 3 | 4 | def strenc(data, firstkey, secondkey, thirdkey): 5 | bts_data = extend_to_16bits(data) 6 | bts_firstkey = extend_to_16bits(firstkey) 7 | bts_secondkey = extend_to_16bits(secondkey) 8 | bts_thirdkey = extend_to_16bits(thirdkey) 9 | i = 0 10 | bts_result = [] 11 | while i < len(bts_data): 12 | bts_temp = bts_data[i: i + 8] 13 | j, k, l = 0, 0, 0 14 | while j < len(bts_firstkey): 15 | des_k = des(bts_firstkey[j: j + 8], ECB) 16 | bts_temp = list(des_k.encrypt(bts_temp)) 17 | j += 8 18 | while k < len(bts_secondkey): 19 | des_k = des(bts_secondkey[k: k + 8], ECB) 20 | bts_temp = list(des_k.encrypt(bts_temp)) 21 | k += 8 22 | while l < len(bts_thirdkey): 23 | des_k = des(bts_secondkey[l: l + 8], ECB) 24 | bts_temp = list(des_k.encrypt(bts_temp)) 25 | l += 8 26 | bts_result.extend(bts_temp) 27 | i += 8 28 | str_result = '' 29 | for each in bts_result: 30 | str_result += '%02X' % each 31 | return str_result 32 | 33 | 34 | def extend_to_16bits(data): 35 | bts = data.encode() 36 | filled_bts = [] 37 | for each in bts: 38 | filled_bts.extend([0, each]) 39 | while len(filled_bts) % 8 != 0: 40 | filled_bts.append(0) 41 | return filled_bts 42 | -------------------------------------------------------------------------------- /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "contributors": [ 8 | { 9 | "login": "viewv", 10 | "name": "Zhang XueNan", 11 | "avatar_url": "https://avatars3.githubusercontent.com/u/32566594?v=4", 12 | "profile": "https://tech.viewv.top", 13 | "contributions": [ 14 | "code", 15 | "ideas" 16 | ] 17 | }, 18 | { 19 | "login": "nekomiao123", 20 | "name": "menma", 21 | "avatar_url": "https://avatars3.githubusercontent.com/u/34064940?v=4", 22 | "profile": "https://nekokiku.cn/", 23 | "contributions": [ 24 | "code" 25 | ] 26 | }, 27 | { 28 | "login": "Singularity0909", 29 | "name": "2U", 30 | "avatar_url": "https://avatars.githubusercontent.com/u/44798266?v=4", 31 | "profile": "https://www.macrohard.cn", 32 | "contributions": [ 33 | "code" 34 | ] 35 | }, 36 | { 37 | "login": "ryuujo1573", 38 | "name": "Ryuujo Zhang", 39 | "avatar_url": "https://avatars.githubusercontent.com/u/16525512?v=4", 40 | "profile": "https://github.com/ryuujo1573", 41 | "contributions": [ 42 | "code" 43 | ] 44 | }, 45 | { 46 | "login": "superzhaoyang", 47 | "name": "superzhaoyang", 48 | "avatar_url": "https://avatars.githubusercontent.com/u/49988452?v=4", 49 | "profile": "http://www.superzhaoyang.top", 50 | "contributions": [ 51 | "code" 52 | ] 53 | } 54 | ], 55 | "contributorsPerLine": 7, 56 | "projectName": "sduhealth", 57 | "projectOwner": "viewv", 58 | "repoType": "github", 59 | "repoHost": "https://github.com", 60 | "skipCi": true 61 | } 62 | -------------------------------------------------------------------------------- /docker/dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:buster-slim 2 | 3 | LABEL version="3.1.0" 4 | LABEL repository="https://github.com/viewv/sduhealth" 5 | LABEL maintainer="nekokiku " 6 | 7 | ARG HEALTH_BASE_URL=https://github.com/viewv/sduhealth.git 8 | ARG HEALTH_BASE_BRANCH=main 9 | 10 | ENV LANG=zh_CN.UTF-8 \ 11 | HEALTH_REPO=/sduhealth 12 | 13 | RUN apk update -f \ 14 | && apk upgrade \ 15 | && apk --no-cache add -f bash \ 16 | tzdata \ 17 | coreutils \ 18 | moreutils \ 19 | git \ 20 | wget \ 21 | curl \ 22 | nano \ 23 | tzdata \ 24 | perl \ 25 | openssl \ 26 | make \ 27 | gcc \ 28 | g++ \ 29 | libc-dev \ 30 | musl-dev \ 31 | musl \ 32 | autoconf \ 33 | libtool \ 34 | openssh \ 35 | && rm -rf /tmp/* \ 36 | && rm -rf /var/cache/apk/* \ 37 | && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ 38 | && echo "Asia/Shanghai" > /etc/timezone \ 39 | && apt-get update \ 40 | && apt-get install -y python \ 41 | python-dev \ 42 | python-pip \ 43 | && apt-get clean \ 44 | && apt-get autoclean \ 45 | && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \ 46 | && git clone -b ${HEALTH_BASE_BRANCH} ${HEALTH_BASE_URL} ${HEALTH_REPO}\ 47 | && cd ${HEALTH_REPO} \ 48 | && pip install --no-cache-dir -r /requirements.txt 49 | 50 | WORKDIR ${HEALTH_REPO}} 51 | 52 | ENTRYPOINT ["/entrypoint.sh"] 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Project ignore 2 | tempCodeRunnerFile.py 3 | example.json 4 | config.yml 5 | 6 | # macOS 7 | .DS_Store 8 | 9 | # editor 10 | .vscode/ 11 | .vscode/* 12 | !.vscode/settings.json 13 | !.vscode/tasks.json 14 | !.vscode/launch.json 15 | !.vscode/extensions.json 16 | *.code-workspace 17 | 18 | # Local History for Visual Studio Code 19 | .history/ 20 | 21 | # Byte-compiled / optimized / DLL files 22 | __pycache__/ 23 | *.py[cod] 24 | *$py.class 25 | *.pyc 26 | 27 | # C extensions 28 | *.so 29 | 30 | # Distribution / packaging 31 | .Python 32 | build/ 33 | develop-eggs/ 34 | dist/ 35 | downloads/ 36 | eggs/ 37 | .eggs/ 38 | lib/ 39 | lib64/ 40 | parts/ 41 | sdist/ 42 | var/ 43 | wheels/ 44 | *.egg-info/ 45 | .installed.cfg 46 | *.egg 47 | MANIFEST 48 | 49 | # PyInstaller 50 | # Usually these files are written by a python script from a template 51 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 52 | *.manifest 53 | *.spec 54 | 55 | # Installer logs 56 | pip-log.txt 57 | pip-delete-this-directory.txt 58 | 59 | # Unit test / coverage reports 60 | htmlcov/ 61 | .tox/ 62 | .nox/ 63 | .coverage 64 | .coverage.* 65 | .cache 66 | nosetests.xml 67 | coverage.xml 68 | *.cover 69 | .hypothesis/ 70 | .pytest_cache/ 71 | 72 | # Translations 73 | *.mo 74 | *.pot 75 | 76 | # Django stuff: 77 | *.log 78 | local_settings.py 79 | db.sqlite3 80 | 81 | # Flask stuff: 82 | instance/ 83 | .webassets-cache 84 | 85 | # Scrapy stuff: 86 | .scrapy 87 | 88 | # Sphinx documentation 89 | docs/_build/ 90 | 91 | # PyBuilder 92 | target/ 93 | 94 | # Jupyter Notebook 95 | .ipynb_checkpoints 96 | 97 | # IPython 98 | profile_default/ 99 | ipython_config.py 100 | 101 | # pyenv 102 | .python-version 103 | 104 | # celery beat schedule file 105 | celerybeat-schedule 106 | 107 | # SageMath parsed files 108 | *.sage.py 109 | 110 | # Environments 111 | .env 112 | .venv 113 | env/ 114 | venv/ 115 | ENV/ 116 | env.bak/ 117 | venv.bak/ 118 | 119 | # Spyder project settings 120 | .spyderproject 121 | .spyproject 122 | 123 | # Rope project settings 124 | .ropeproject 125 | 126 | # mkdocs documentation 127 | /site 128 | 129 | # mypy 130 | .mypy_cache/ 131 | .dmypy.json 132 | dmypy.json 133 | real.json 134 | test.json 135 | userinfo.txt 136 | 137 | # PyCharm 138 | .idea 139 | -------------------------------------------------------------------------------- /doc/analysis.md: -------------------------------------------------------------------------------- 1 | # 山东大学健康打卡系统分析 2 | 3 | 这个脚本主要使用 Request 来模拟一次打卡,与其他版本的区别大概是不是使用 selenium 来模拟浏览器,这样更加轻量而且方便服务器运行,下面来分析整个流程。 4 | 5 | ## 登陆 6 | 7 | 登陆部分是非常有意思的一部分,首先大家都注意到了,本程序为什么会需要 JS 的运行环境,这就和登陆有密切的关系了,整个山大系统的登陆是进行了如下几步。 8 | 9 | 首先是打开登陆网址,这时候服务器会给客户端一个网页,这个网页的 HTML 中,有两个非常关键的参数隐藏其中,LT 与 execution 这两个值,也就是登陆第一步,要 get 到这个网页,之后从中提取 LT 与 execution 这两个值。 10 | 11 | ![Screen Shot 2021-01-19 at 17.51.01](https://cdn.jsdelivr.net/gh/viewv/Pico@master/uPic/Screen%20Shot%202021-01-19%20at%2017.51.01.png) 12 | 13 | 之后便开始登陆流程,也就是用户名和密码,这样构成了登陆系统所需要的必要因素。 14 | 15 | 接下来我们来观察一个登陆样例 POST 包来看看是怎么登陆的: 16 | 17 | ``` 18 | POST /cas/login HTTP/1.1 19 | Host: pass.sdu.edu.cn 20 | Connection: close 21 | Content-Length: 383 22 | Cache-Control: max-age=0 23 | Upgrade-Insecure-Requests: 1 24 | Origin: https://pass.sdu.edu.cn 25 | Content-Type: application/x-www-form-urlencoded 26 | User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 27 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 28 | Sec-Fetch-Site: same-origin 29 | Sec-Fetch-Mode: navigate 30 | Sec-Fetch-User: ?1 31 | Sec-Fetch-Dest: document 32 | Referer: https://pass.sdu.edu.cn/cas/login 33 | Accept-Encoding: gzip, deflate 34 | Accept-Language: en-US,en;q=0.9 35 | Cookie: JSESSIONID=CDDADFE350C42BDEC44B59F94D326EE6; Language=zh_CN 36 | 37 | rsa=***&ul=***&pl=***<=***&execution=***&_eventId=submit 38 | ``` 39 | 40 | 其中注意内容,这里因为隐私信息,用***替代,这里 ul 代表用户名长度,这里是学号长度,之后 pl 代表密码长度,之后 lt 便是上面获取到的 lt,execution 就是上面获取带的 execution,之后后面固定_eventId=submit。 41 | 42 | 那么这个所谓的 rsa 呢?难道是非对称加密,lt 是公钥,非也非也,这里是本系统最大的一个笑话。 43 | 44 | 我们来看登陆了部分的 js 代码: 45 | 46 | ![IMAGE 2021-01-19 17:58:45](https://cdn.jsdelivr.net/gh/viewv/Pico@master/uPic/IMAGE%202021-01-19%2017:58:45.jpg) 47 | 48 | 这里可以看到这个的 rsa 是由 u-用户名,p-密码,lt-LT,拼接起来,之后加三个密钥,对真的三个字符串密钥`1 2 3` 那么这个 `strEnc` 是啥函数呢,这里我们只需简单搜索发现了这个脚本: 49 | 50 | ![IMAGE 2021-01-19 18:00:56](https://cdn.jsdelivr.net/gh/viewv/Pico@master/uPic/IMAGE%202021-01-19%2018:00:56.jpg) 51 | 52 | DES ?这和 RSA 完全没有关系,对,到这里我想大家就明白了,这个所谓的 RSA 这里是使用了 3DES 算法加密三个部分的拼接字符串,而三个密钥是简单的 `1 2 3` 有点可笑,不过我感觉还是安全的,因为毕竟学校的服务器开了 SSL,CA 证书也是正常的,只是这样折腾来的所谓加密,感觉还不如直接 Base 64,浪费服务器资源,还起名 RSA,挂羊头卖狗肉。 53 | 54 | ~~因为不知道,也不想分析,更不想写这个 3DES,所以这里选择了一个意外简单偷懒的方式,直接调用他的 js 加密,加上见代码中生成的随机 Cookies 这样我们就得到了登陆所需要的所有部分。~~ 55 | 56 | 最新版本的代码使用了原生 Python 的 DES 加密,可以不依赖 js 运行环境了。 57 | 58 | ## 打卡 59 | 60 | 打卡部分非常繁琐,因为涉及到了数据的操作,从顶倒下的来介绍本部分的实现。 61 | 62 | 首先在本项目中,model.json 便是抓包后分析的一个标准的打卡后提交的 json 内容,所以思路便是通过服务器的请求获取到前一天的打卡数据,这也就是为什么运行需要你先打一天的卡,这样服务器就有了你的过去的数据,这样就可以实现获取到前一天的数据之后填充到新一天的数据中,之后只需要对 Model 进行简单的时间修改,之后 post 出去就可以实现打卡。 63 | 64 | 但是获取前一天的数据是比较复杂的,需要好几个步骤,这部分的实现可以在 `health_checkin` 中查看,首先需要进行 `gethealth` 之后 `checkService` 之后 `serveInfo` 之后 `getServeApply` 之后 `getContinueService` 之后 `getSignData` 这一整套流程下来,其中的最后一步 `getSignData` 的到了服务器返回的包,在这个包中,会有上次打卡的数据或者是暂存的数据,之后将这个数据,传到 model 里面进行处理,之后返回处理后的数据。 65 | 66 | 相当于 model 是支架,我们将获取到的前一天的数据附加到 model 上,之后增加修补,post 到服务器上就可以完成这个打卡流程。 67 | 68 | -------------------------------------------------------------------------------- /ConfigureOnce.ps1: -------------------------------------------------------------------------------- 1 | function Initialize { 2 | Get-Content -Path 'configexample.yml' -Encoding UTF8 | Out-String | Set-Variable 'config' 3 | Write-Host '[Info] ' -ForegroundColor Cyan -NoNewline 4 | Write-Host '请输入需要自动打卡的信息化门户账号:' 5 | Read-Host | Set-Variable id 6 | Write-Host '[Info] ' -ForegroundColor Cyan -NoNewline 7 | Write-Host '请输入密码:' 8 | Read-Host -AsSecureString | Set-Variable pass 9 | 10 | $pass = [System.Net.NetworkCredential]::new('', $pass).Password # this looks stupid :( 11 | 12 | $config -creplace '"\{STUID\}"', "`"$id`"" -creplace '"\{PSWD\}"', "`"$pass`"" | ` 13 | Out-File -FilePath 'config.yml' -Encoding UTF8 -Force 14 | 15 | Write-Host '[Info] ' -ForegroundColor Cyan -NoNewline 16 | Write-Host '已将配置写入config.yml文件.' 17 | Pause 18 | } 19 | 20 | #### 21 | # Start from here: 22 | #### 23 | $step = 1 24 | 25 | Write-Host "[Step $step] " -ForegroundColor Red -NoNewLine 26 | Write-Host "检查Python环境..." -ForegroundColor White 27 | $step++ 28 | $justInstalled=0 29 | Invoke-Expression 'python -V' | Set-Variable v 30 | if (-not ($v -match '3\.\w\.\w')) { 31 | Write-Error "未正确配置Python环境." 32 | try { 33 | Write-Host "[Step $step] " -ForegroundColor Red -NoNewLine 34 | Write-Host "正在尝试安装Python环境, 这取决于目前网络环境速度. (Source: npm.taobao.org 淘宝镜像源)" 35 | $step++ 36 | if(!(Test-Path 'python-installer.exe')) { 37 | Invoke-WebRequest -Uri 'http://npm.taobao.org/mirrors/python/3.7.8/python-3.7.8-amd64.exe' -OutFile 'python-installer.exe' 38 | } 39 | pwd | set "cwd" 40 | .\python-installer.exe /quiet PrependPath=1 Include_test=0 ` 41 | TargetDir="$cwd\python3" Shortcuts=0 Include_doc=0 Include_dev=0 Include_launcher=0 42 | 43 | $justInstalled = 1 44 | while (!(Test-Path "$cwd\python3\Scripts\pip.exe")) { 45 | Start-Sleep -Seconds 1 46 | } 47 | Start-Sleep -Seconds 3 48 | } 49 | catch { Write-Host $error[0]; break } 50 | } 51 | 52 | Write-Host "[Step $step] " -ForegroundColor Red -NoNewLine 53 | Write-Host "正在检查pip指令是否可用." 54 | $step++ 55 | Invoke-Expression 'pip -V' | Set-Variable pv 56 | if ($pv -match '^pip\s\w{2}\.\w\.\w') { 57 | Write-Host "[Step $step] " -ForegroundColor Red -NoNewLine 58 | Write-Host "正在安装运行所需的Python Package." 59 | $step++ 60 | iex 'pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt' 61 | } 62 | elseif($justInstalled -eq 1) { 63 | iex "$cwd\python3\Scripts\pip.exe install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt" 64 | } 65 | else { 66 | Write-Host "[Step $step] " -ForegroundColor Red -NoNewLine 67 | Write-Host "正在安装运行所需的Python Package." 68 | $step++ 69 | @(iex 'cmd /U/C where python')[0].ToString().Replace('python.exe', 'Scripts') | set ppath 70 | iex "$ppath\pip.exe install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt" 71 | } 72 | Write-Host "[Step $step] " -ForegroundColor Red -NoNewLine 73 | Write-Host "记录账号信息(信息仅在本地存储)" 74 | Initialize # this performs the function defined most above. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SDUHealth 2 | 3 | [![All Contributors](https://img.shields.io/badge/all_contributors-5-orange.svg?style=flat-square)](#contributors-) 4 | 5 | 6 | 山东大学学生每日健康状况填报自动打卡。 7 | 8 | **注意:本程序的适用范围为一个健康的有理智的学生,暂时只实现了离校生的打卡,未来也许会实现在校生的打卡,如果你身体不适,请勿使用本程序打卡,因本程序会自动上传健康的数据。** 9 | 10 | **已经结束历史使命,如果学校的官网没变化的话,可能唯一有价值的是登陆部分的代码供其他服务参考。** 11 | 12 | ## 环境&运行 13 | 14 | 本程序依赖于: 15 | 16 | **[Python3](https://www.python.org)** 17 | 18 | 推荐使用 GitHub Actions 的运行方式,可以方便的实现每日自动打卡。 19 | 20 | 注意需要你使用本程序打卡前一天手动打过卡,且当天没有打过卡,之后每天就可以都只使用自动打卡了。 21 | 22 | ### Github Actions 运行 23 | 24 | 首先复制 configexample.yml 文件里面的所有内容,然后在本地进行编辑,根据文件的指示填入账号和密码,之后可以去 [yaml检测](https://www.bejson.com/validators/yaml_editor/) 检测一下文件的正确性,支持多账户,当然前提是你知道别人的密码。 25 | 26 | 之后 fork 本仓库到自己的 GitHub 账号,之后打开仓库的 Settings,在仓库设置界面在打开 Secrets 选项: 27 | 28 | ![Github Repo Secrets Settings](https://cdn.jsdelivr.net/gh/viewv/Pico@master/uPic/Screen%20Shot%202021-01-19%20at%2019.06.10.png) 29 | 30 | 之后新建一个 secret,Name 为 CONFIG,Value 就是你编辑的包含你的学号密码的 yaml 文件内容全部复制进去即可: 31 | 32 | ![New Github Repo Secret](https://cdn.jsdelivr.net/gh/viewv/Pico@master/uPic/Screen%20Shot%202021-01-19%20at%2019.08.02.png) 33 | 34 | 保存即可,之后转到仓库的 Actions 选项卡: 35 | 36 | ![Enable Github Actions workflow](https://cdn.jsdelivr.net/gh/viewv/Pico@master/uPic/Screen%20Shot%202021-01-19%20at%2019.09.06.png) 37 | 38 | Enable 本仓库已经配置好的 GitHub Actions workflow,之后点击 checkin 打开配置选项: 39 | 40 | ![Enable checkin workflow](https://cdn.jsdelivr.net/gh/viewv/Pico@master/uPic/Screen%20Shot%202021-01-21%20at%2015.11.44.png) 41 | 42 | Enable workflow,之后得到下面的界面: 43 | 44 | ![Checkin workflow page](https://cdn.jsdelivr.net/gh/viewv/Pico@master/uPic/Screen%20Shot%202021-01-21%20at%2015.12.33.png) 45 | 46 | 你现在可以选择 Run workflow 手动触发运行,本仓库也配置了每天的自动运行,你可以打开 GitHub Actions 的运行结果提示,这样就可以简单的实现每天的打卡任务。 47 | 48 | > ⚠️ 注意 49 | > 50 | > 最近我们发现 GitHub Actions workflow 有延时问题, 有可能您的实际运行时间与设置的不同。 51 | > 52 | > 又由于 GitHub 可能的设计问题,您有可能需要先手动运行一次打卡 workflow 才可以激活定时运行。 53 | 54 |
55 | 关于 repo_sync.yml 的使用说明 56 | 57 | 这个文件创建了一个定时自动同步源仓库内所有代码的 Github Actions workflow。 58 | 59 | > ⚠️ 注意 60 | > 61 | > 该 workflow 执行后会自动覆盖原仓库内的所有代码,并更新为源仓库的代码,所有自定义的修改将不会得到保留(一般来说,也没什么需要自定义的,实在是需要自定义,可以手动执行该 workflow 之后,将其设置为 disable )。 62 | 63 | 在 fork 了代码之后,首先需要去申请一个 GitHub Personal Access Token,该 Token 让此 workflow 拥有了更改指定仓库代码的权限。具体操作如下: 64 | 65 | 先选择账户 Settings 66 | 67 | ![Github account Settings](https://cdn.jsdelivr.net/gh/nekomiao123/pic/img/image-20210121121329851.png) 68 | 69 | 然后选择 Developer Settings 70 | 71 | ![Github Developer Settings](https://cdn.jsdelivr.net/gh/nekomiao123/pic/img/image-20210121121525838.png) 72 | 73 | 之后再选择 Personal access tokens 点击里面的 Generate new token 74 | 75 | ![Generate new token](https://cdn.jsdelivr.net/gh/nekomiao123/pic/img/image-20210121121640260.png) 76 | 77 | 之后先随便取个好记的名字,然后勾选前两项 78 | 79 | ![New personal access token](https://cdn.jsdelivr.net/gh/nekomiao123/pic/img/image-20210121121747207.png) 80 | 81 | 最后点击下面的 Generate token 即可,这样就能获得一串类似这样的字符,赶紧复制下来,它只会显示这一次 82 | 83 | ![Personal token info](https://cdn.jsdelivr.net/gh/nekomiao123/pic/img/image-20210121121953685.png) 84 | 85 | 然后去新建一个 secret(跟上面建立 secret 的流程一样) 86 | 87 | Name 是 PAT 88 | 89 | Value 是刚才你复制的这一串字符 90 | 91 |
92 | 93 | 到这里,该 Github Actions workflow 就可以使用了,可以实现每日自动打卡,如果你也开启了 repo_sync workflow 那么也将定时自动同步本仓库以保持代码更新。 94 | 95 | ### 本地运行 96 | 97 | 在本地安装好环境后,下载或者 clone 本仓库后,到项目文件夹下运行如下命令安装 Python 所需依赖: 98 | 99 | ```bash 100 | pip install -r requirements.txt 101 | ``` 102 | 103 | 安装好本程序所需依赖之后,需要把 configexample.yml 文件改名为 config.yml,然后根据文件里面的内容指示填入账号和密码。 104 | 105 | 之后运行如下命令运行 sduhealth.py 便会运行程序进行打卡。 106 | 107 | ```bash 108 | python sduhealth.py 109 | ``` 110 | 111 | ## 图形界面版本 112 | 113 | - 见 [gui 分支](https://github.com/viewv/sduhealth/tree/gui) 114 | 115 | ## 程序分析 116 | 117 | 想知道这个程序如何工作的,出现了 bug 想修改代码?欢迎阅读 [山东大学健康打卡系统分析](https://github.com/viewv/sduhealth/blob/main/doc/analysis.md) ! 118 | 119 | ## Contributors ✨ 120 | 121 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 |

Zhang XueNan

💻 🤔

menma

💻

2U

💻

Ryuujo Zhang

💻

superzhaoyang

💻
135 | 136 | 137 | 138 | 139 | 140 | 141 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! 142 | -------------------------------------------------------------------------------- /model.py: -------------------------------------------------------------------------------- 1 | import time 2 | import pytz 3 | import datetime 4 | import demjson as json 5 | import random as rand 6 | 7 | TIME_ZONE = 'Asia/Shanghai' 8 | 9 | 10 | def test(data): 11 | print("model") 12 | print(data) 13 | 14 | def get_random_temp(): 15 | temp = 36.2 16 | if(rand.random() > 0.8): 17 | temp += 0.1 18 | return temp 19 | 20 | 21 | def get_current_date(timezone): 22 | tz = pytz.timezone(timezone) 23 | current_time = datetime.datetime.now(tz).strftime("%Y-%m-%d") 24 | return current_time 25 | 26 | 27 | def get_yesterday_date(timezone): 28 | tz = pytz.timezone(timezone) 29 | yesterday_time = datetime.datetime.now( 30 | tz) - datetime.timedelta(days=1) 31 | yesterday_time = yesterday_time.strftime("%Y-%m-%d") 32 | return yesterday_time 33 | 34 | 35 | def get_current_stamp(): 36 | now = time.time() 37 | now = (int(now * 1000)) 38 | current_stamp = str(now) 39 | return current_stamp 40 | 41 | 42 | def generate_post_data(source_data): 43 | new_key = {} 44 | for code in source_data["body"]["dataStores"]: 45 | new_key[code] = len(code) 46 | 47 | sorted_key = sorted(new_key.items(), key = lambda x:x[1]) 48 | # print("sorted_key",sorted_key) 49 | # print("sorted_key[1][0]",sorted_key[1][0]) 50 | 51 | # This code may change aperiodically, we don't know how it works. 52 | unknown_code = sorted_key[1][0] 53 | 54 | rowSetName = source_data["body"]["dataStores"][unknown_code]["rowSetName"] 55 | 56 | whether_signed = False 57 | json_file = open("./json/model.json", encoding='utf-8') 58 | model_txt = json_file.read(-1) 59 | json_file.close() 60 | 61 | # add {var} feature 62 | model_txt = model_txt.replace(r"{Temp}", "%.1f"%get_random_temp()) 63 | model_data = json.decode(model_txt) 64 | # model_data = json.decode_file("./json/model.json") 65 | 66 | current_date = get_current_date(TIME_ZONE) 67 | # current_date_time = current_date + ' 00:00:00' 68 | 69 | yesterday_date = get_yesterday_date(TIME_ZONE) 70 | yesterday_date_time = yesterday_date + ' 09:00:00' 71 | 72 | # current_timestamp = get_current_stamp() 73 | 74 | unknown_code_use = unknown_code 75 | unknown_code_record = unknown_code + "_record" 76 | # print("unknown_code_use",unknown_code_use) 77 | new_model_data = {} 78 | new_model_data["header"] = model_data["header"] 79 | new_model_data["body"] = {} 80 | new_model_data["body"]["parameters"] = model_data["body"]["parameters"] 81 | new_model_data["body"]["dataStores"] = {} 82 | new_model_data["body"]["dataStores"]["variable"] = model_data["body"]["dataStores"]["variable"] 83 | new_model_data["body"]["dataStores"][unknown_code_use] = model_data["body"]["dataStores"]['535b1ef6-bf51-4d4c-9ae4-5a90cdc4'] 84 | new_model_data["body"]["dataStores"][unknown_code_record] = model_data["body"]["dataStores"]['535b1ef6-bf51-4d4c-9ae4-5a90cdc4_record'] 85 | new_model_data["body"]["dataStores"][unknown_code_use]["name"] = unknown_code_use 86 | new_model_data["body"]["dataStores"][unknown_code_use]["parameters"]["queryds"] = unknown_code_use 87 | new_model_data["body"]["dataStores"][unknown_code_record]["name"] = unknown_code_record 88 | new_model_data["body"]["dataStores"][unknown_code_record]["parameters"]["queryds"] = unknown_code_use 89 | 90 | new_model_data["body"]["dataStores"][unknown_code_use]["rowSetName"] = rowSetName 91 | new_model_data["body"]["dataStores"][unknown_code_record]["rowSetName"] = rowSetName 92 | 93 | 94 | json.encode_to_file("./json/new_model.json", new_model_data, overwrite=True) 95 | 96 | model_data = new_model_data 97 | # if you didn't click the "暂存" button 98 | if unknown_code_record in source_data["body"]["dataStores"]: 99 | source_record = source_data["body"]["dataStores"][unknown_code_record]["rowSet"]["primary"][0] 100 | model_data["body"]["dataStores"][unknown_code_record] = source_data["body"]["dataStores"][unknown_code_record] 101 | print("today is " + current_date) 102 | if source_record['SBSJ_STR'][0:10] == current_date: 103 | whether_signed = True 104 | # model_data["body"]["dataStores"][unknown_code_record]["rowSet"]["primary"][0]["CLSJ"] = current_timestamp 105 | # model_data["body"]["dataStores"][unknown_code_record]["rowSet"]["primary"][0]["SBSJ"] = current_timestamp 106 | else: 107 | source_record = source_data["body"]["dataStores"][unknown_code_use]["rowSet"]["primary"][0] 108 | del model_data["body"]["dataStores"][unknown_code_record] 109 | del model_data["body"]["dataStores"][unknown_code_use]["rowSet"]["primary"][0]["_o"] 110 | 111 | zh = source_record["ZH"] # student id 112 | xm = source_record["XM"] # student name 113 | xsxb = source_record["XSXB"] # student sex 114 | nl = source_record["NL"] # student age 115 | szdw = source_record["SZDW"] # student school 116 | zymc = source_record["ZYMC"] # student major 117 | xslx = source_record["XSLX"] # student type 118 | zxsj = source_record["ZXSJ"] # student phone number 119 | sbsj = current_date # date 120 | fdyxmx = source_record["FDYXMX"] # teacher name 121 | jjlxrxm = source_record["JJLXRXM"] # parent name 122 | jjlxrdh = source_record["JJLXRDH"] # parent phone number 123 | jjlxrybrgx = source_record["JJLXRYBRGX"] # parent rel. 124 | lxzt = source_record["LXZT"] # current city 125 | dqsfjjia = source_record["DQSFJJIA"] # at home or not? 126 | sheng_text = source_record["sheng_TEXT"] # provience text 127 | sheng = source_record["sheng"] # provience 128 | shi_text = source_record["shi_TEXT"] # city text 129 | shi = source_record["shi"] # city 130 | quxian_text = source_record["quxian_TEXT"] # tone text 131 | quxian = source_record["quxian"] # tone 132 | dqjzdz = source_record["DQJZDZ"] # location 133 | clsj = yesterday_date_time # temp. time 134 | 135 | # SYS_USER = source_vars[0]["value"] # student name 136 | # SYS_UNIT = source_vars[1]["value"] # student unit 137 | # SYS_DATE = current_timestamp # current timestamp 138 | # ID_NUMBER = source_vars[3]["value"] # student id 139 | # USER_NAME = source_vars[4]["value"] # student name 140 | # XB = source_vars[5]["value"] # student sex 141 | # SZYX = source_vars[6]["value"] # student school 142 | # ZYMC = source_vars[7]["value"] # student major 143 | # MOBILE = source_vars[8]["value"] # mobile 144 | 145 | model_data["body"]["dataStores"][unknown_code_use]["rowSet"]["primary"][0]["ZH"] = zh 146 | model_data["body"]["dataStores"][unknown_code_use]["rowSet"]["primary"][0]["XM"] = xm 147 | model_data["body"]["dataStores"][unknown_code_use]["rowSet"]["primary"][0]["XSXB"] = xsxb 148 | model_data["body"]["dataStores"][unknown_code_use]["rowSet"]["primary"][0]["NL"] = nl 149 | model_data["body"]["dataStores"][unknown_code_use]["rowSet"]["primary"][0]["SZDW"] = szdw 150 | model_data["body"]["dataStores"][unknown_code_use]["rowSet"]["primary"][0]["ZYMC"] = zymc 151 | model_data["body"]["dataStores"][unknown_code_use]["rowSet"]["primary"][0]["XSLX"] = xslx 152 | model_data["body"]["dataStores"][unknown_code_use]["rowSet"]["primary"][0]["ZXSJ"] = zxsj 153 | model_data["body"]["dataStores"][unknown_code_use]["rowSet"]["primary"][0]["SBSJ"] = sbsj 154 | model_data["body"]["dataStores"][unknown_code_use]["rowSet"]["primary"][0]["FDYXMX"] = fdyxmx 155 | model_data["body"]["dataStores"][unknown_code_use]["rowSet"]["primary"][0]["JJLXRXM"] = jjlxrxm 156 | model_data["body"]["dataStores"][unknown_code_use]["rowSet"]["primary"][0]["JJLXRDH"] = jjlxrdh 157 | model_data["body"]["dataStores"][unknown_code_use]["rowSet"]["primary"][0]["JJLXRYBRGX"] = jjlxrybrgx 158 | model_data["body"]["dataStores"][unknown_code_use]["rowSet"]["primary"][0]["LXZT"] = lxzt 159 | model_data["body"]["dataStores"][unknown_code_use]["rowSet"]["primary"][0]["DQSFJJIA"] = dqsfjjia 160 | model_data["body"]["dataStores"][unknown_code_use]["rowSet"]["primary"][0]["sheng_TEXT"] = sheng_text 161 | model_data["body"]["dataStores"][unknown_code_use]["rowSet"]["primary"][0]["sheng"] = sheng 162 | model_data["body"]["dataStores"][unknown_code_use]["rowSet"]["primary"][0]["shi_TEXT"] = shi_text 163 | model_data["body"]["dataStores"][unknown_code_use]["rowSet"]["primary"][0]["shi"] = shi 164 | model_data["body"]["dataStores"][unknown_code_use]["rowSet"]["primary"][0]["quxian_TEXT"] = quxian_text 165 | model_data["body"]["dataStores"][unknown_code_use]["rowSet"]["primary"][0]["quxian"] = quxian 166 | model_data["body"]["dataStores"][unknown_code_use]["rowSet"]["primary"][0]["DQJZDZ"] = dqjzdz 167 | model_data["body"]["dataStores"][unknown_code_use]["rowSet"]["primary"][0]["CLSJ"] = clsj 168 | 169 | model_data["body"]["dataStores"]["variable"] = source_data["body"]["dataStores"]["variable"] 170 | model_data["body"]["parameters"] = source_data["body"]["parameters"] 171 | 172 | json.encode_to_file("./json/example.json", model_data, overwrite=True) 173 | 174 | return model_data, whether_signed 175 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /json/model.json: -------------------------------------------------------------------------------- 1 | { 2 | "header": { 3 | "code": 0, 4 | "message": { 5 | "title": "", 6 | "detail": "" 7 | } 8 | }, 9 | "body": { 10 | "dataStores": { 11 | "535b1ef6-bf51-4d4c-9ae4-5a90cdc4": { 12 | "rowSet": { 13 | "primary": [{ 14 | "ZH": "", 15 | "_t": 3, 16 | "XM": "", 17 | "XSXB": "", 18 | "NL": "", 19 | "SZDW": "", 20 | "ZYMC": "", 21 | "XSLX": "", 22 | "ZXSJ": "", 23 | "SBSJ": "", 24 | "FDYXMX": "", 25 | "JJLXRXM": "", 26 | "JJLXRDH": "", 27 | "JJLXRYBRGX": "", 28 | "LXZT": "", 29 | "DQSFJJIA": "", 30 | "sheng_TEXT": "", 31 | "sheng": "", 32 | "shi_TEXT": "", 33 | "shi": "", 34 | "quxian_TEXT": "", 35 | "quxian": "", 36 | "DQJZDZ": "", 37 | "XQSFYQRBL": "否No", 38 | "DRTW": "{Temp}", 39 | "CLSJ": "", 40 | "BRSFFS": "否No", 41 | "YWZZ": "无症状No symptoms", 42 | "ZZLX": "", 43 | "JJGDZ": "否No", 44 | "JZGDZ": "否No", 45 | "YSGDZ": "否No", 46 | "YWZZGDZ": "无症状No symptoms", 47 | "ZZLXGDZ": "", 48 | "QTZZMS": "", 49 | "ZKGDZ": "", 50 | "SFJJYXGL": "否No", 51 | "JJGLZT": "", 52 | "YXGCX": "否No", 53 | "JZYXGCZT": "", 54 | "SFYSHQZ": "否No", 55 | "QZQK": "", 56 | "SFDGYQSQ": "否No", 57 | "SFJCGYQ": "否No", 58 | "SFJQZYQHZ": "否No", 59 | "JRSFYJCS": "否No", 60 | "EXCASE": "否No", 61 | "EXCASECONTACT": "否No", 62 | "EXFOURTEENFEVER": "否No", 63 | "EXTEST": "否No", 64 | "EXTESTRESULT": "否No", 65 | "sflxs": "否 No", 66 | "sqsfyyq": "否 No", 67 | "FXQKGDZ": "", 68 | "FXLYD": "", 69 | "FXSJ": "", 70 | "LXMDD": "", 71 | "LXSJ": "", 72 | "GRCN": "我承诺以上信息真实准确,并愿意承担相应法律责任。I promise that the above information is true and accurate, and I am willing to bear the corresponding legal responsibility.", 73 | "FYZKGDZ": "", 74 | "JZGC": "", 75 | "JZGLDD": "", 76 | "JJYXGC": "", 77 | "JJYXGCDD": "", 78 | "_o": { 79 | "ZH": null, 80 | "XM": null, 81 | "XSXB": null, 82 | "NL": null, 83 | "SZDW": null, 84 | "ZYMC": null, 85 | "XSLX": null, 86 | "ZXSJ": null, 87 | "SBSJ": null, 88 | "FDYXMX": null, 89 | "JJLXRXM": null, 90 | "JJLXRDH": null, 91 | "JJLXRYBRGX": null, 92 | "LXZT": null, 93 | "DQSFJJIA": null, 94 | "sheng_TEXT": null, 95 | "sheng": null, 96 | "shi_TEXT": null, 97 | "shi": null, 98 | "quxian_TEXT": null, 99 | "quxian": null, 100 | "DQJZDZ": null, 101 | "XQSFYQRBL": null, 102 | "DRTW": null, 103 | "CLSJ": null, 104 | "BRSFFS": null, 105 | "YWZZ": null, 106 | "ZZLX": null, 107 | "JJGDZ": null, 108 | "JZGDZ": null, 109 | "YSGDZ": null, 110 | "YWZZGDZ": null, 111 | "ZZLXGDZ": null, 112 | "QTZZMS": null, 113 | "ZKGDZ": null, 114 | "SFJJYXGL": null, 115 | "JJGLZT": null, 116 | "YXGCX": null, 117 | "JZYXGCZT": null, 118 | "SFYSHQZ": null, 119 | "QZQK": null, 120 | "SFDGYQSQ": null, 121 | "SFJCGYQ": null, 122 | "SFJQZYQHZ": null, 123 | "JRSFYJCS": null, 124 | "EXCASE": null, 125 | "EXCASECONTACT": null, 126 | "EXFOURTEENFEVER": null, 127 | "EXTEST": null, 128 | "EXTESTRESULT": null, 129 | "sflxs": null, 130 | "sqsfyyq": null, 131 | "FXQKGDZ": null, 132 | "FXLYD": null, 133 | "FXSJ": null, 134 | "LXMDD": null, 135 | "LXSJ": null, 136 | "GRCN": null, 137 | "FYZKGDZ": null, 138 | "JZGC": null, 139 | "JZGLDD": null, 140 | "JJYXGC": null, 141 | "JJYXGCDD": null 142 | } 143 | }], 144 | "filter": [], 145 | "delete": [] 146 | }, 147 | "name": "535b1ef6-bf51-4d4c-9ae4-5a90cdc4", 148 | "pageNumber": 1, 149 | "pageSize": 2147483647, 150 | "recordCount": 1, 151 | "rowSetName": "3586b9e6-f964-4a3b-a693-bb23da84", 152 | "parameters": { 153 | "relatedcontrols": "body_0", 154 | "primarykey": "pk_id", 155 | "queryds": "535b1ef6-bf51-4d4c-9ae4-5a90cdc4" 156 | } 157 | }, 158 | "535b1ef6-bf51-4d4c-9ae4-5a90cdc4_record": { 159 | "rowSet": { 160 | "primary": [{ 161 | "ZZLX": "", 162 | "LXZT": "", 163 | "DRTW": "36.3", 164 | "JJGLZT": "", 165 | "GRCN": "我承诺以上信息真实准确,并愿意承担相应法律责任。I promise that the above information is true and accurate, and I am willing to bear the corresponding legal responsibility.", 166 | "ZXSJ": "", 167 | "pk_id": "", 168 | "ZH": "", 169 | "YWZZ": "无症状No symptoms", 170 | "JZGDZ": "否No", 171 | "ZYMC": "", 172 | "LXMDD": "", 173 | "quxian_TEXT": "", 174 | "DQSFJJIA": "", 175 | "SZDW": "", 176 | "FXQKGDZ": "", 177 | "CLSJ": "", 178 | "SFJCGYQ": "否No", 179 | "YWZZGDZ": "无症状No symptoms", 180 | "JZYXGCZT": "", 181 | "SBSJ": "", 182 | "YSGDZ": "否No", 183 | "shi": "", 184 | "BRSFFS": "否No", 185 | "FDYXMX": "", 186 | "EXFOURTEENFEVER": "否No", 187 | "XSXB": "", 188 | "JJYXGCDD": "", 189 | "JJLXRDH": "", 190 | "SFJJYXGL": "否No", 191 | "DQJZDZ": "", 192 | "NL": "", 193 | "LXSJ": "", 194 | "JZGC": "", 195 | "FYZKGDZ": "", 196 | "JRSFYJCS": "否No", 197 | "FXSJ": "", 198 | "JJLXRXM": "", 199 | "XQSFYQRBL": "否No", 200 | "fk_id": "", 201 | "JJLXRYBRGX": "", 202 | "XM": "", 203 | "JJYXGC": "", 204 | "SFJQZYQHZ": "否No", 205 | "SFYSHQZ": "否No", 206 | "sheng": "", 207 | "XSLX": "", 208 | "ZZLXGDZ": "", 209 | "SFDGYQSQ": "否No", 210 | "ZKGDZ": "", 211 | "EXTESTRESULT": "否No", 212 | "EXCASECONTACT": "否No", 213 | "SBSJ_STR": "", 214 | "JZGLDD": "", 215 | "JJGDZ": "否No", 216 | "quxian": "", 217 | "shi_TEXT": "", 218 | "sqsfyyq": "否 No", 219 | "EXTEST": "否No", 220 | "EXCASE": "否No", 221 | "FXLYD": "", 222 | "sflxs": "否 No", 223 | "sheng_TEXT": "", 224 | "YXGCX": "否No", 225 | "QZQK": "", 226 | "CLSJ_STR": "", 227 | "QTZZMS": "" 228 | }], 229 | "filter": [], 230 | "delete": [] 231 | }, 232 | "name": "535b1ef6-bf51-4d4c-9ae4-5a90cdc4_record", 233 | "pageNumber": 1, 234 | "pageSize": 2147483647, 235 | "recordCount": 0, 236 | "rowSetName": "3586b9e6-f964-4a3b-a693-bb23da84", 237 | "parameters": { 238 | "relatedcontrols": "body_0", 239 | "primarykey": "pk_id", 240 | "queryds": "535b1ef6-bf51-4d4c-9ae4-5a90cdc4" 241 | } 242 | }, 243 | "variable": { 244 | "rowSet": { 245 | "primary": [{ 246 | "source": "interface", 247 | "name": "SYS_USER", 248 | "value": "", 249 | "type": "string" 250 | }, 251 | { 252 | "source": "interface", 253 | "name": "SYS_UNIT", 254 | "value": "", 255 | "type": "string" 256 | }, 257 | { 258 | "source": "interface", 259 | "name": "SYS_DATE", 260 | "value": "", 261 | "type": "date" 262 | }, 263 | { 264 | "name": "1189060d-4465-4b53-89c2-c9f88457.ID_NUMBER", 265 | "value": "" 266 | }, 267 | { 268 | "name": "1189060d-4465-4b53-89c2-c9f88457.USER_NAME", 269 | "value": "" 270 | }, 271 | { 272 | "name": "666d8a60-7108-4681-829d-545841c3.XB", 273 | "value": "" 274 | }, 275 | { 276 | "name": "666d8a60-7108-4681-829d-545841c3.SZYX", 277 | "value": "" 278 | }, 279 | { 280 | "name": "666d8a60-7108-4681-829d-545841c3.ZYMC", 281 | "value": "" 282 | }, 283 | { 284 | "name": "1189060d-4465-4b53-89c2-c9f88457.MOBILE", 285 | "value": "" 286 | } 287 | ], 288 | "filter": [], 289 | "delete": [] 290 | }, 291 | "name": "variable", 292 | "pageNumber": 1, 293 | "pageSize": 2147483647, 294 | "recordCount": 0, 295 | "parameters": {} 296 | } 297 | }, 298 | "parameters": { 299 | "formid": "", 300 | "status": "select", 301 | "privilegeId": "", 302 | "seqId": "", 303 | "service_id": "41d9ad4a-f681-4872-a400-20a3b606d399", 304 | "process": "", 305 | "strUserId": "", 306 | "strUserIdCC": "", 307 | "nextActId": "" 308 | } 309 | } 310 | } -------------------------------------------------------------------------------- /json/new_model.json: -------------------------------------------------------------------------------- 1 | { 2 | "body": { 3 | "dataStores": { 4 | "2d26dfad-56ae-4cf3-ab26-87112f1e": { 5 | "name": "2d26dfad-56ae-4cf3-ab26-87112f1e", 6 | "pageNumber": 1, 7 | "pageSize": 2147483647, 8 | "parameters": { 9 | "primarykey": "pk_id", 10 | "queryds": "2d26dfad-56ae-4cf3-ab26-87112f1e", 11 | "relatedcontrols": "body_0" 12 | }, 13 | "recordCount": 1, 14 | "rowSet": { 15 | "delete": [], 16 | "filter": [], 17 | "primary": [ 18 | { 19 | "BRSFFS": "否No", 20 | "CLSJ": "", 21 | "DQJZDZ": "", 22 | "DQSFJJIA": "", 23 | "DRTW": "36.2", 24 | "EXCASE": "否No", 25 | "EXCASECONTACT": "否No", 26 | "EXFOURTEENFEVER": "否No", 27 | "EXTEST": "否No", 28 | "EXTESTRESULT": "否No", 29 | "FDYXMX": "", 30 | "FXLYD": "", 31 | "FXQKGDZ": "", 32 | "FXSJ": "", 33 | "FYZKGDZ": "", 34 | "GRCN": "我承诺以上信息真实准确,并愿意承担相应法律责任。I promise that the above information is true and accurate, and I am willing to bear the corresponding legal responsibility.", 35 | "JJGDZ": "否No", 36 | "JJGLZT": "", 37 | "JJLXRDH": "", 38 | "JJLXRXM": "", 39 | "JJLXRYBRGX": "", 40 | "JJYXGC": "", 41 | "JJYXGCDD": "", 42 | "JRSFYJCS": "否No", 43 | "JZGC": "", 44 | "JZGDZ": "否No", 45 | "JZGLDD": "", 46 | "JZYXGCZT": "", 47 | "LXMDD": "", 48 | "LXSJ": "", 49 | "LXZT": "", 50 | "NL": "", 51 | "QTZZMS": "", 52 | "QZQK": "", 53 | "SBSJ": "", 54 | "SFDGYQSQ": "否No", 55 | "SFJCGYQ": "否No", 56 | "SFJJYXGL": "否No", 57 | "SFJQZYQHZ": "否No", 58 | "SFYSHQZ": "否No", 59 | "SZDW": "", 60 | "XM": "", 61 | "XQSFYQRBL": "否No", 62 | "XSLX": "", 63 | "XSXB": "", 64 | "YSGDZ": "否No", 65 | "YWZZ": "无症状No symptoms", 66 | "YWZZGDZ": "无症状No symptoms", 67 | "YXGCX": "否No", 68 | "ZH": "", 69 | "ZKGDZ": "", 70 | "ZXSJ": "", 71 | "ZYMC": "", 72 | "ZZLX": "", 73 | "ZZLXGDZ": "", 74 | "_o": { 75 | "BRSFFS": null, 76 | "CLSJ": null, 77 | "DQJZDZ": null, 78 | "DQSFJJIA": null, 79 | "DRTW": null, 80 | "EXCASE": null, 81 | "EXCASECONTACT": null, 82 | "EXFOURTEENFEVER": null, 83 | "EXTEST": null, 84 | "EXTESTRESULT": null, 85 | "FDYXMX": null, 86 | "FXLYD": null, 87 | "FXQKGDZ": null, 88 | "FXSJ": null, 89 | "FYZKGDZ": null, 90 | "GRCN": null, 91 | "JJGDZ": null, 92 | "JJGLZT": null, 93 | "JJLXRDH": null, 94 | "JJLXRXM": null, 95 | "JJLXRYBRGX": null, 96 | "JJYXGC": null, 97 | "JJYXGCDD": null, 98 | "JRSFYJCS": null, 99 | "JZGC": null, 100 | "JZGDZ": null, 101 | "JZGLDD": null, 102 | "JZYXGCZT": null, 103 | "LXMDD": null, 104 | "LXSJ": null, 105 | "LXZT": null, 106 | "NL": null, 107 | "QTZZMS": null, 108 | "QZQK": null, 109 | "SBSJ": null, 110 | "SFDGYQSQ": null, 111 | "SFJCGYQ": null, 112 | "SFJJYXGL": null, 113 | "SFJQZYQHZ": null, 114 | "SFYSHQZ": null, 115 | "SZDW": null, 116 | "XM": null, 117 | "XQSFYQRBL": null, 118 | "XSLX": null, 119 | "XSXB": null, 120 | "YSGDZ": null, 121 | "YWZZ": null, 122 | "YWZZGDZ": null, 123 | "YXGCX": null, 124 | "ZH": null, 125 | "ZKGDZ": null, 126 | "ZXSJ": null, 127 | "ZYMC": null, 128 | "ZZLX": null, 129 | "ZZLXGDZ": null, 130 | "quxian": null, 131 | "quxian_TEXT": null, 132 | "sflxs": null, 133 | "sheng": null, 134 | "sheng_TEXT": null, 135 | "shi": null, 136 | "shi_TEXT": null, 137 | "sqsfyyq": null 138 | }, 139 | "_t": 3, 140 | "quxian": "", 141 | "quxian_TEXT": "", 142 | "sflxs": "否 No", 143 | "sheng": "", 144 | "sheng_TEXT": "", 145 | "shi": "", 146 | "shi_TEXT": "", 147 | "sqsfyyq": "否 No" 148 | } 149 | ] 150 | }, 151 | "rowSetName": "f4575f2f-e527-419d-8378-235497f9" 152 | }, 153 | "2d26dfad-56ae-4cf3-ab26-87112f1e_record": { 154 | "name": "2d26dfad-56ae-4cf3-ab26-87112f1e_record", 155 | "pageNumber": 1, 156 | "pageSize": 2147483647, 157 | "parameters": { 158 | "primarykey": "pk_id", 159 | "queryds": "2d26dfad-56ae-4cf3-ab26-87112f1e", 160 | "relatedcontrols": "body_0" 161 | }, 162 | "recordCount": 0, 163 | "rowSet": { 164 | "delete": [], 165 | "filter": [], 166 | "primary": [ 167 | { 168 | "BRSFFS": "否No", 169 | "CLSJ": "", 170 | "CLSJ_STR": "", 171 | "DQJZDZ": "", 172 | "DQSFJJIA": "", 173 | "DRTW": "36.3", 174 | "EXCASE": "否No", 175 | "EXCASECONTACT": "否No", 176 | "EXFOURTEENFEVER": "否No", 177 | "EXTEST": "否No", 178 | "EXTESTRESULT": "否No", 179 | "FDYXMX": "", 180 | "FXLYD": "", 181 | "FXQKGDZ": "", 182 | "FXSJ": "", 183 | "FYZKGDZ": "", 184 | "GRCN": "我承诺以上信息真实准确,并愿意承担相应法律责任。I promise that the above information is true and accurate, and I am willing to bear the corresponding legal responsibility.", 185 | "JJGDZ": "否No", 186 | "JJGLZT": "", 187 | "JJLXRDH": "", 188 | "JJLXRXM": "", 189 | "JJLXRYBRGX": "", 190 | "JJYXGC": "", 191 | "JJYXGCDD": "", 192 | "JRSFYJCS": "否No", 193 | "JZGC": "", 194 | "JZGDZ": "否No", 195 | "JZGLDD": "", 196 | "JZYXGCZT": "", 197 | "LXMDD": "", 198 | "LXSJ": "", 199 | "LXZT": "", 200 | "NL": "", 201 | "QTZZMS": "", 202 | "QZQK": "", 203 | "SBSJ": "", 204 | "SBSJ_STR": "", 205 | "SFDGYQSQ": "否No", 206 | "SFJCGYQ": "否No", 207 | "SFJJYXGL": "否No", 208 | "SFJQZYQHZ": "否No", 209 | "SFYSHQZ": "否No", 210 | "SZDW": "", 211 | "XM": "", 212 | "XQSFYQRBL": "否No", 213 | "XSLX": "", 214 | "XSXB": "", 215 | "YSGDZ": "否No", 216 | "YWZZ": "无症状No symptoms", 217 | "YWZZGDZ": "无症状No symptoms", 218 | "YXGCX": "否No", 219 | "ZH": "", 220 | "ZKGDZ": "", 221 | "ZXSJ": "", 222 | "ZYMC": "", 223 | "ZZLX": "", 224 | "ZZLXGDZ": "", 225 | "fk_id": "", 226 | "pk_id": "", 227 | "quxian": "", 228 | "quxian_TEXT": "", 229 | "sflxs": "否 No", 230 | "sheng": "", 231 | "sheng_TEXT": "", 232 | "shi": "", 233 | "shi_TEXT": "", 234 | "sqsfyyq": "否 No" 235 | } 236 | ] 237 | }, 238 | "rowSetName": "f4575f2f-e527-419d-8378-235497f9" 239 | }, 240 | "variable": { 241 | "name": "variable", 242 | "pageNumber": 1, 243 | "pageSize": 2147483647, 244 | "parameters": {}, 245 | "recordCount": 0, 246 | "rowSet": { 247 | "delete": [], 248 | "filter": [], 249 | "primary": [ 250 | { 251 | "name": "SYS_USER", 252 | "source": "interface", 253 | "type": "string", 254 | "value": "" 255 | }, 256 | { 257 | "name": "SYS_UNIT", 258 | "source": "interface", 259 | "type": "string", 260 | "value": "" 261 | }, 262 | { 263 | "name": "SYS_DATE", 264 | "source": "interface", 265 | "type": "date", 266 | "value": "" 267 | }, 268 | { 269 | "name": "1189060d-4465-4b53-89c2-c9f88457.ID_NUMBER", 270 | "value": "" 271 | }, 272 | { 273 | "name": "1189060d-4465-4b53-89c2-c9f88457.USER_NAME", 274 | "value": "" 275 | }, 276 | { 277 | "name": "666d8a60-7108-4681-829d-545841c3.XB", 278 | "value": "" 279 | }, 280 | { 281 | "name": "666d8a60-7108-4681-829d-545841c3.SZYX", 282 | "value": "" 283 | }, 284 | { 285 | "name": "666d8a60-7108-4681-829d-545841c3.ZYMC", 286 | "value": "" 287 | }, 288 | { 289 | "name": "1189060d-4465-4b53-89c2-c9f88457.MOBILE", 290 | "value": "" 291 | } 292 | ] 293 | } 294 | } 295 | }, 296 | "parameters": { 297 | "formid": "", 298 | "nextActId": "", 299 | "privilegeId": "", 300 | "process": "", 301 | "seqId": "", 302 | "service_id": "41d9ad4a-f681-4872-a400-20a3b606d399", 303 | "status": "select", 304 | "strUserId": "", 305 | "strUserIdCC": "" 306 | } 307 | }, 308 | "header": { 309 | "code": 0, 310 | "message": { 311 | "detail": "", 312 | "title": "" 313 | } 314 | } 315 | } -------------------------------------------------------------------------------- /sduhealth.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | import platform 3 | import requests 4 | import secrets 5 | import demjson as json 6 | import os 7 | import yaml 8 | 9 | import model 10 | import encrypt 11 | import colorclass as c 12 | 13 | from bs4 import BeautifulSoup 14 | 15 | 16 | 17 | def js_from_file(filename): 18 | # Read Javascript file 19 | # so you need NodeJs installed on your computer 20 | with open(filename, 'r', encoding='UTF-8') as f: 21 | result = f.read() 22 | return result 23 | 24 | 25 | def generate_their_RSA(username, password, lt): 26 | # return they called 'RSA' string, note here just return 'RSA' string! 27 | return encrypt.strenc(username + password + lt, "1", "2", "3") 28 | 29 | 30 | def get_lt_And_execution(result): 31 | soup = BeautifulSoup(result.content, "html.parser") 32 | lt = soup.find(id="lt").get("value") 33 | execution = soup.find('input', {'name': 'execution'}).get("value") 34 | return {'lt': lt, 'execution': execution} 35 | 36 | 37 | def get_page_title(result): 38 | soup = BeautifulSoup(result.content, "html.parser") 39 | title = soup.find('title').string 40 | return title 41 | 42 | 43 | def get_frame(result): 44 | soup = BeautifulSoup(result.content, "html.parser") 45 | frame = soup.find(id="dcstr") 46 | return frame 47 | 48 | 49 | class SduHealth(object): 50 | def __init__(self, username, password) -> None: 51 | super().__init__() 52 | self.username = username 53 | self.password = password 54 | 55 | self.home_title = "山东大学信息化公共服务平台" 56 | self.serviceID = '41d9ad4a-f681-4872-a400-20a3b606d399' 57 | self.privilege_id = '' 58 | self.process_id = '' 59 | self.SYS_FK = '' 60 | self.form_id = '' 61 | 62 | self.login_url = "https://pass.sdu.edu.cn/cas/login" 63 | self.service_url = "https://service.sdu.edu.cn/tp_up" 64 | self.home_url = "https://service.sdu.edu.cn/tp_up/view?m=up#act=portal/viewhome" 65 | self.health_url = "https://scenter.sdu.edu.cn/tp_fp/view?m=fp#from=hall&serveID=41d9ad4a-f681-4872-a400-20a3b606d399&act=fp/serveapply" 66 | 67 | self.check_service_url = "https://scenter.sdu.edu.cn/tp_fp/fp/serveapply/checkService" 68 | self.serve_info_url = "https://scenter.sdu.edu.cn/tp_fp/fp/serveapply/serveInfo" 69 | self.get_serve_apply_url = "https://scenter.sdu.edu.cn/tp_fp/fp/serveapply/getServeApply" 70 | self.get_continue_service_url = "https://scenter.sdu.edu.cn/tp_fp/fp/serveapply/getContinueService" 71 | 72 | self.logout_1_url = "https://service.sdu.edu.cn/tp_up/logout" 73 | self.logout_2_url = "https://pass.sdu.edu.cn/cas/logout?service=https://service.sdu.edu.cn/tp_up/" 74 | self.logout_3_url = "https://pass.sdu.edu.cn/portal/logout.jsp?service=https://service.sdu.edu.cn/tp_up/" 75 | self.logout_4_url = "https://service.sdu.edu.cn/tp_up/" 76 | 77 | self.session = requests.session() 78 | self.login_header = { 79 | 'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Mobile Safari/537.36', 80 | 'content-type': "application/x-www-form-urlencoded", 81 | } 82 | self.service_header = { 83 | 'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Mobile Safari/537.36', 84 | 'Content-Type': 'application/json;charset=UTF-8', 85 | 'Origin': 'https://scenter.sdu.edu.cn', 86 | 'X-Requested-With': 'XMLHttpRequest', 87 | 'Referer': 'https://scenter.sdu.edu.cn/tp_fp/view?m=fp' 88 | } 89 | self.checkin_header = { 90 | 'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Mobile Safari/537.36', 91 | 'Content-Type': 'text/plain;charset=UTF-8', 92 | 'X-Requested-With': 'XMLHttpRequest', 93 | } 94 | self.login_cookie = { 95 | 'JSESSIONID': secrets.token_hex(16).upper() 96 | } 97 | self.frame_json = {} 98 | 99 | self.check_login = True 100 | self.check_getSignData = True 101 | self.check_checkin = True 102 | self.whether_signed = False 103 | 104 | def health_login(self): 105 | try: 106 | ul = str(len(self.username)) 107 | pl = str(len(self.password)) 108 | 109 | r = self.session.get(self.login_url) 110 | if r.status_code != 200: 111 | self.check_login = False 112 | error('Network Error!') 113 | 114 | lt_execution = get_lt_And_execution(r) 115 | lt = lt_execution['lt'] 116 | execution = lt_execution['execution'] 117 | 118 | rsa = generate_their_RSA( 119 | username=self.username, password=self.password, lt=lt) 120 | 121 | login_body = { 122 | 'rsa': rsa, 123 | 'ul': ul, 124 | 'pl': pl, 125 | 'lt': lt, 126 | 'execution': execution, 127 | '_eventId': 'submit' 128 | } 129 | 130 | result = self.session.post(self.login_url, headers=self.login_header, 131 | cookies=self.login_cookie, data=login_body) 132 | 133 | result = self.session.get(self.home_url) 134 | 135 | title = get_page_title(result=result) 136 | if title == self.home_title: 137 | info("login successful " + strong(result)) 138 | self.check_login = True 139 | else: 140 | info("login failed " + strong(result)) 141 | self.check_login = False 142 | except Exception as e: 143 | self.check_login = False 144 | error(e) 145 | 146 | def getHealthUrl(self): 147 | getHealth = self.session.get(self.health_url) 148 | print(strong(getHealth)) 149 | 150 | def check_service(self): 151 | try: 152 | check_body = { 153 | "serveID": self.serviceID 154 | } 155 | check_result = self.session.post( 156 | self.check_service_url, json=check_body, headers=self.service_header) 157 | print(strong(check_result)) 158 | except: 159 | error("check_service error") 160 | 161 | def serve_info(self): 162 | try: 163 | serve_body = { 164 | "serviceID": self.serviceID 165 | } 166 | serve_result = self.session.post( 167 | self.serve_info_url, json=serve_body, headers=self.service_header) 168 | print(strong(serve_result)) 169 | except Exception as e: 170 | error(e) 171 | 172 | def get_serve_apply(self): 173 | get_serve_apply_body = { 174 | "serveID": self.serviceID, 175 | "from": "hall" 176 | } 177 | try: 178 | get_serve_apply_result = self.session.post( 179 | self.get_serve_apply_url, json=get_serve_apply_body, headers=self.service_header) 180 | print(strong(get_serve_apply_result)) 181 | get_serve_apply_json = get_serve_apply_result.json() 182 | self.form_id = get_serve_apply_json['formID'] 183 | self.process_id = get_serve_apply_json['procID'] 184 | self.privilege_id = get_serve_apply_json['privilegeId'] 185 | except: 186 | error("get_serve_apply error") 187 | 188 | def get_continue_service(self): 189 | get_continue_service_body = { 190 | "serviceID": self.serviceID, 191 | } 192 | try: 193 | get_continue_service_result = self.session.post( 194 | self.get_continue_service_url, json=get_continue_service_body, headers=self.service_header) 195 | print(strong(get_continue_service_result)) 196 | get_continue_service_json = get_continue_service_result.json() 197 | self.SYS_FK = get_continue_service_json[0]['proc_inst_id'] 198 | except: 199 | warning("get_continue_service error") 200 | 201 | def get_sign_data(self): 202 | get_sign_data_url = "https://scenter.sdu.edu.cn/tp_fp/formParser?status=select&formid=" + self.form_id + "&service_id=" + \ 203 | self.serviceID + "&process=" + self.process_id + "&seqId=&SYS_FK=" + \ 204 | self.SYS_FK + "&privilegeId=" + self.privilege_id 205 | 206 | try: 207 | get_sign_data_result = self.session.get(get_sign_data_url) 208 | print(strong(get_sign_data_result)) 209 | 210 | if get_sign_data_result.status_code != 200: 211 | self.check_getSignData = False 212 | raise RuntimeError('Get Signin Data Network Error!') 213 | 214 | frame = get_frame(get_sign_data_result).string 215 | # print(frame) 216 | frame_json = json.decode(frame) 217 | source_json = frame_json 218 | # json.encode_to_file("./test.json", source_json, overwrite=True) 219 | frame_json, self.whether_signed = model.generate_post_data( 220 | source_data=source_json) 221 | self.frame_json = frame_json 222 | except: 223 | self.check_getSignData = False 224 | info("get_sign_data error") 225 | 226 | def health_checkin(self): 227 | info("gethealth ", end='') 228 | self.getHealthUrl() 229 | 230 | info("checkService ", end='') 231 | self.check_service() 232 | 233 | info("serveInfo ", end='') 234 | self.serve_info() 235 | 236 | info("getServeApply ", end='') 237 | self.get_serve_apply() 238 | 239 | info("getContinueService ", end='') 240 | self.get_continue_service() 241 | 242 | info("getSignData ", end='') 243 | self.get_sign_data() 244 | 245 | if self.whether_signed: 246 | print("\033[1;32;40mYou have signed today\033[0m") 247 | return 248 | 249 | checkin_url = "https://scenter.sdu.edu.cn/tp_fp/formParser?status=update&formid=" + \ 250 | self.form_id + "&workflowAction=startProcess&seqId=&workitemid=&process=" + self.process_id 251 | checkin_body = json.encode(self.frame_json) 252 | 253 | info("Start Checkin!") 254 | if self.check_getSignData == True: 255 | try: 256 | info("Checkin ", end='') 257 | result = self.session.post(checkin_url, data=checkin_body, 258 | headers=self.checkin_header) 259 | print(strong(result)) 260 | 261 | if result.status_code != 200: 262 | self.check_checkin = False 263 | raise RuntimeError("Checkin Network Error") 264 | except Exception as e: 265 | self.check_checkin = False 266 | error(e) 267 | else: 268 | error("Network Error!") 269 | 270 | def health_logout(self): 271 | try: 272 | info("logout (1/4): ", end='') 273 | print(strong('done' if self.session.get(self.logout_1_url).status_code == 200 else 'error')) 274 | info("logout (2/4): ", end='') 275 | print(strong('done' if self.session.get(self.logout_2_url).status_code == 200 else 'error')) 276 | info("logout (3/4): ", end='') 277 | print(strong('done' if self.session.get(self.logout_3_url).status_code == 200 else 'error')) 278 | info("logout (4/4): ", end='') 279 | print(strong('done' if self.session.get(self.logout_4_url).status_code == 200 else 'error')) 280 | except Exception as e: 281 | warning("logout ?") 282 | error(e) 283 | self.session.close() 284 | 285 | 286 | def read(): 287 | studentIDs = [] 288 | studentPasswords = [] 289 | if 'CONFIG' in os.environ: 290 | try: 291 | config_current = yaml.load( 292 | os.environ['CONFIG'], Loader=yaml.FullLoader) 293 | studentIDs = config_current['jobs']['studentID'] 294 | studentPasswords = config_current['jobs']['studentPassword'] 295 | return studentIDs, studentPasswords 296 | except Exception as e: 297 | warning("Error in reading config.yml from ENV:\033[94m$CONFIG\033[0m, please check.") 298 | error(e) 299 | else: 300 | with open("./config.yml", mode='r', encoding='utf-8') as f: 301 | config_current = yaml.load(f, Loader=yaml.FullLoader) 302 | 303 | studentIDs = config_current['jobs']['studentID'] 304 | studentPasswords = config_current['jobs']['studentPassword'] 305 | return studentIDs, studentPasswords 306 | 307 | def _timestamp(): 308 | print("[{}]".format(datetime.strftime(datetime.now(), "%Y-%m-%d %H:%M:%S")), end=' ') 309 | 310 | def error(cause): 311 | _timestamp() 312 | print("\033[91m[ERROR]\033[0m Program exited due to:", cause) 313 | exit() 314 | 315 | 316 | def info(log, end=None): 317 | _timestamp() 318 | print("\033[96m[INFO]\033[0m", log, end=end) 319 | return 320 | 321 | 322 | def warning(warning): 323 | _timestamp() 324 | print("\033[93m[WARN]\033[0m", warning) 325 | return 326 | 327 | 328 | def strong(string): 329 | return "\033[42;97m " + str(string) + " \033[0m" 330 | 331 | 332 | def main(): 333 | users, passwords = read() 334 | for i in range(0, len(users)): 335 | info("Sign in for " + users[i] + (", count: " + i if i > 0 else "")) 336 | 337 | user = users[i] 338 | password = passwords[i] 339 | sdu = SduHealth(username=user, password=password) 340 | 341 | info("-------------------") 342 | 343 | sdu.health_login() 344 | if sdu.check_login == False: 345 | error("Login Error") 346 | 347 | sdu.health_checkin() 348 | if sdu.check_getSignData == False: 349 | error("Get Sign Data Error") 350 | 351 | if sdu.check_checkin == False: 352 | error("Checkin Error") 353 | 354 | if not sdu.whether_signed: 355 | info("Checkin Successful") 356 | 357 | sdu.health_logout() 358 | info("Logout Successful") 359 | info("-------------------") 360 | 361 | 362 | if __name__ == "__main__": 363 | if(platform.system() == "Windows"): 364 | c.Windows.enable() 365 | main() 366 | -------------------------------------------------------------------------------- /js/des.js: -------------------------------------------------------------------------------- 1 | /** 2 | * DES加密解密 3 | * @Copyright Copyright (c) 2006 4 | * @author Guapo 5 | * @see DESCore 6 | */ 7 | 8 | /* 9 | * encrypt the string to string made up of hex 10 | * return the encrypted string 11 | */ 12 | function strEnc(data, firstKey, secondKey, thirdKey) { 13 | 14 | var leng = data.length; 15 | var encData = ""; 16 | var firstKeyBt, secondKeyBt, thirdKeyBt, firstLength, secondLength, thirdLength; 17 | if (firstKey != null && firstKey != "") { 18 | firstKeyBt = getKeyBytes(firstKey); 19 | firstLength = firstKeyBt.length; 20 | } 21 | if (secondKey != null && secondKey != "") { 22 | secondKeyBt = getKeyBytes(secondKey); 23 | secondLength = secondKeyBt.length; 24 | } 25 | if (thirdKey != null && thirdKey != "") { 26 | thirdKeyBt = getKeyBytes(thirdKey); 27 | thirdLength = thirdKeyBt.length; 28 | } 29 | 30 | if (leng > 0) { 31 | if (leng < 4) { 32 | var bt = strToBt(data); 33 | var encByte; 34 | if (firstKey != null && firstKey != "" && secondKey != null && secondKey != "" && thirdKey != null && thirdKey != "") { 35 | var tempBt; 36 | var x, y, z; 37 | tempBt = bt; 38 | for (x = 0; x < firstLength; x++) { 39 | tempBt = enc(tempBt, firstKeyBt[x]); 40 | } 41 | for (y = 0; y < secondLength; y++) { 42 | tempBt = enc(tempBt, secondKeyBt[y]); 43 | } 44 | for (z = 0; z < thirdLength; z++) { 45 | tempBt = enc(tempBt, thirdKeyBt[z]); 46 | } 47 | encByte = tempBt; 48 | } else { 49 | if (firstKey != null && firstKey != "" && secondKey != null && secondKey != "") { 50 | var tempBt; 51 | var x, y; 52 | tempBt = bt; 53 | for (x = 0; x < firstLength; x++) { 54 | tempBt = enc(tempBt, firstKeyBt[x]); 55 | } 56 | for (y = 0; y < secondLength; y++) { 57 | tempBt = enc(tempBt, secondKeyBt[y]); 58 | } 59 | encByte = tempBt; 60 | } else { 61 | if (firstKey != null && firstKey != "") { 62 | var tempBt; 63 | var x = 0; 64 | tempBt = bt; 65 | for (x = 0; x < firstLength; x++) { 66 | tempBt = enc(tempBt, firstKeyBt[x]); 67 | } 68 | encByte = tempBt; 69 | } 70 | } 71 | } 72 | encData = bt64ToHex(encByte); 73 | } else { 74 | var iterator = parseInt(leng / 4); 75 | var remainder = leng % 4; 76 | var i = 0; 77 | for (i = 0; i < iterator; i++) { 78 | var tempData = data.substring(i * 4 + 0, i * 4 + 4); 79 | var tempByte = strToBt(tempData); 80 | var encByte; 81 | if (firstKey != null && firstKey != "" && secondKey != null && secondKey != "" && thirdKey != null && thirdKey != "") { 82 | var tempBt; 83 | var x, y, z; 84 | tempBt = tempByte; 85 | for (x = 0; x < firstLength; x++) { 86 | tempBt = enc(tempBt, firstKeyBt[x]); 87 | } 88 | for (y = 0; y < secondLength; y++) { 89 | tempBt = enc(tempBt, secondKeyBt[y]); 90 | } 91 | for (z = 0; z < thirdLength; z++) { 92 | tempBt = enc(tempBt, thirdKeyBt[z]); 93 | } 94 | encByte = tempBt; 95 | } else { 96 | if (firstKey != null && firstKey != "" && secondKey != null && secondKey != "") { 97 | var tempBt; 98 | var x, y; 99 | tempBt = tempByte; 100 | for (x = 0; x < firstLength; x++) { 101 | tempBt = enc(tempBt, firstKeyBt[x]); 102 | } 103 | for (y = 0; y < secondLength; y++) { 104 | tempBt = enc(tempBt, secondKeyBt[y]); 105 | } 106 | encByte = tempBt; 107 | } else { 108 | if (firstKey != null && firstKey != "") { 109 | var tempBt; 110 | var x; 111 | tempBt = tempByte; 112 | for (x = 0; x < firstLength; x++) { 113 | tempBt = enc(tempBt, firstKeyBt[x]); 114 | } 115 | encByte = tempBt; 116 | } 117 | } 118 | } 119 | encData += bt64ToHex(encByte); 120 | } 121 | if (remainder > 0) { 122 | var remainderData = data.substring(iterator * 4 + 0, leng); 123 | var tempByte = strToBt(remainderData); 124 | var encByte; 125 | if (firstKey != null && firstKey != "" && secondKey != null && secondKey != "" && thirdKey != null && thirdKey != "") { 126 | var tempBt; 127 | var x, y, z; 128 | tempBt = tempByte; 129 | for (x = 0; x < firstLength; x++) { 130 | tempBt = enc(tempBt, firstKeyBt[x]); 131 | } 132 | for (y = 0; y < secondLength; y++) { 133 | tempBt = enc(tempBt, secondKeyBt[y]); 134 | } 135 | for (z = 0; z < thirdLength; z++) { 136 | tempBt = enc(tempBt, thirdKeyBt[z]); 137 | } 138 | encByte = tempBt; 139 | } else { 140 | if (firstKey != null && firstKey != "" && secondKey != null && secondKey != "") { 141 | var tempBt; 142 | var x, y; 143 | tempBt = tempByte; 144 | for (x = 0; x < firstLength; x++) { 145 | tempBt = enc(tempBt, firstKeyBt[x]); 146 | } 147 | for (y = 0; y < secondLength; y++) { 148 | tempBt = enc(tempBt, secondKeyBt[y]); 149 | } 150 | encByte = tempBt; 151 | } else { 152 | if (firstKey != null && firstKey != "") { 153 | var tempBt; 154 | var x; 155 | tempBt = tempByte; 156 | for (x = 0; x < firstLength; x++) { 157 | tempBt = enc(tempBt, firstKeyBt[x]); 158 | } 159 | encByte = tempBt; 160 | } 161 | } 162 | } 163 | encData += bt64ToHex(encByte); 164 | } 165 | } 166 | } 167 | return encData; 168 | } 169 | 170 | /* 171 | * decrypt the encrypted string to the original string 172 | * 173 | * return the original string 174 | */ 175 | function strDec(data, firstKey, secondKey, thirdKey) { 176 | var leng = data.length; 177 | var decStr = ""; 178 | var firstKeyBt, secondKeyBt, thirdKeyBt, firstLength, secondLength, thirdLength; 179 | if (firstKey != null && firstKey != "") { 180 | firstKeyBt = getKeyBytes(firstKey); 181 | firstLength = firstKeyBt.length; 182 | } 183 | if (secondKey != null && secondKey != "") { 184 | secondKeyBt = getKeyBytes(secondKey); 185 | secondLength = secondKeyBt.length; 186 | } 187 | if (thirdKey != null && thirdKey != "") { 188 | thirdKeyBt = getKeyBytes(thirdKey); 189 | thirdLength = thirdKeyBt.length; 190 | } 191 | 192 | var iterator = parseInt(leng / 16); 193 | var i = 0; 194 | for (i = 0; i < iterator; i++) { 195 | var tempData = data.substring(i * 16 + 0, i * 16 + 16); 196 | var strByte = hexToBt64(tempData); 197 | var intByte = new Array(64); 198 | var j = 0; 199 | for (j = 0; j < 64; j++) { 200 | intByte[j] = parseInt(strByte.substring(j, j + 1)); 201 | } 202 | var decByte; 203 | if (firstKey != null && firstKey != "" && secondKey != null && secondKey != "" && thirdKey != null && thirdKey != "") { 204 | var tempBt; 205 | var x, y, z; 206 | tempBt = intByte; 207 | for (x = thirdLength - 1; x >= 0; x--) { 208 | tempBt = dec(tempBt, thirdKeyBt[x]); 209 | } 210 | for (y = secondLength - 1; y >= 0; y--) { 211 | tempBt = dec(tempBt, secondKeyBt[y]); 212 | } 213 | for (z = firstLength - 1; z >= 0; z--) { 214 | tempBt = dec(tempBt, firstKeyBt[z]); 215 | } 216 | decByte = tempBt; 217 | } else { 218 | if (firstKey != null && firstKey != "" && secondKey != null && secondKey != "") { 219 | var tempBt; 220 | var x, y, z; 221 | tempBt = intByte; 222 | for (x = secondLength - 1; x >= 0; x--) { 223 | tempBt = dec(tempBt, secondKeyBt[x]); 224 | } 225 | for (y = firstLength - 1; y >= 0; y--) { 226 | tempBt = dec(tempBt, firstKeyBt[y]); 227 | } 228 | decByte = tempBt; 229 | } else { 230 | if (firstKey != null && firstKey != "") { 231 | var tempBt; 232 | var x, y, z; 233 | tempBt = intByte; 234 | for (x = firstLength - 1; x >= 0; x--) { 235 | tempBt = dec(tempBt, firstKeyBt[x]); 236 | } 237 | decByte = tempBt; 238 | } 239 | } 240 | } 241 | decStr += byteToString(decByte); 242 | } 243 | return decStr; 244 | } 245 | /* 246 | * chang the string into the bit array 247 | * 248 | * return bit array(it's length % 64 = 0) 249 | */ 250 | function getKeyBytes(key) { 251 | var keyBytes = new Array(); 252 | var leng = key.length; 253 | var iterator = parseInt(leng / 4); 254 | var remainder = leng % 4; 255 | var i = 0; 256 | for (i = 0; i < iterator; i++) { 257 | keyBytes[i] = strToBt(key.substring(i * 4 + 0, i * 4 + 4)); 258 | } 259 | if (remainder > 0) { 260 | keyBytes[i] = strToBt(key.substring(i * 4 + 0, leng)); 261 | } 262 | return keyBytes; 263 | } 264 | 265 | /* 266 | * chang the string(it's length <= 4) into the bit array 267 | * 268 | * return bit array(it's length = 64) 269 | */ 270 | function strToBt(str) { 271 | var leng = str.length; 272 | var bt = new Array(64); 273 | if (leng < 4) { 274 | var i = 0, 275 | j = 0, 276 | p = 0, 277 | q = 0; 278 | for (i = 0; i < leng; i++) { 279 | var k = str.charCodeAt(i); 280 | for (j = 0; j < 16; j++) { 281 | var pow = 1, 282 | m = 0; 283 | for (m = 15; m > j; m--) { 284 | pow *= 2; 285 | } 286 | bt[16 * i + j] = parseInt(k / pow) % 2; 287 | } 288 | } 289 | for (p = leng; p < 4; p++) { 290 | var k = 0; 291 | for (q = 0; q < 16; q++) { 292 | var pow = 1, 293 | m = 0; 294 | for (m = 15; m > q; m--) { 295 | pow *= 2; 296 | } 297 | bt[16 * p + q] = parseInt(k / pow) % 2; 298 | } 299 | } 300 | } else { 301 | for (i = 0; i < 4; i++) { 302 | var k = str.charCodeAt(i); 303 | for (j = 0; j < 16; j++) { 304 | var pow = 1; 305 | for (m = 15; m > j; m--) { 306 | pow *= 2; 307 | } 308 | bt[16 * i + j] = parseInt(k / pow) % 2; 309 | } 310 | } 311 | } 312 | return bt; 313 | } 314 | 315 | /* 316 | * chang the bit(it's length = 4) into the hex 317 | * 318 | * return hex 319 | */ 320 | function bt4ToHex(binary) { 321 | var hex; 322 | switch (binary) { 323 | case "0000": 324 | hex = "0"; 325 | break; 326 | case "0001": 327 | hex = "1"; 328 | break; 329 | case "0010": 330 | hex = "2"; 331 | break; 332 | case "0011": 333 | hex = "3"; 334 | break; 335 | case "0100": 336 | hex = "4"; 337 | break; 338 | case "0101": 339 | hex = "5"; 340 | break; 341 | case "0110": 342 | hex = "6"; 343 | break; 344 | case "0111": 345 | hex = "7"; 346 | break; 347 | case "1000": 348 | hex = "8"; 349 | break; 350 | case "1001": 351 | hex = "9"; 352 | break; 353 | case "1010": 354 | hex = "A"; 355 | break; 356 | case "1011": 357 | hex = "B"; 358 | break; 359 | case "1100": 360 | hex = "C"; 361 | break; 362 | case "1101": 363 | hex = "D"; 364 | break; 365 | case "1110": 366 | hex = "E"; 367 | break; 368 | case "1111": 369 | hex = "F"; 370 | break; 371 | } 372 | return hex; 373 | } 374 | 375 | /* 376 | * chang the hex into the bit(it's length = 4) 377 | * 378 | * return the bit(it's length = 4) 379 | */ 380 | function hexToBt4(hex) { 381 | var binary; 382 | switch (hex) { 383 | case "0": 384 | binary = "0000"; 385 | break; 386 | case "1": 387 | binary = "0001"; 388 | break; 389 | case "2": 390 | binary = "0010"; 391 | break; 392 | case "3": 393 | binary = "0011"; 394 | break; 395 | case "4": 396 | binary = "0100"; 397 | break; 398 | case "5": 399 | binary = "0101"; 400 | break; 401 | case "6": 402 | binary = "0110"; 403 | break; 404 | case "7": 405 | binary = "0111"; 406 | break; 407 | case "8": 408 | binary = "1000"; 409 | break; 410 | case "9": 411 | binary = "1001"; 412 | break; 413 | case "A": 414 | binary = "1010"; 415 | break; 416 | case "B": 417 | binary = "1011"; 418 | break; 419 | case "C": 420 | binary = "1100"; 421 | break; 422 | case "D": 423 | binary = "1101"; 424 | break; 425 | case "E": 426 | binary = "1110"; 427 | break; 428 | case "F": 429 | binary = "1111"; 430 | break; 431 | } 432 | return binary; 433 | } 434 | 435 | /* 436 | * chang the bit(it's length = 64) into the string 437 | * 438 | * return string 439 | */ 440 | function byteToString(byteData) { 441 | var str = ""; 442 | for (i = 0; i < 4; i++) { 443 | var count = 0; 444 | for (j = 0; j < 16; j++) { 445 | var pow = 1; 446 | for (m = 15; m > j; m--) { 447 | pow *= 2; 448 | } 449 | count += byteData[16 * i + j] * pow; 450 | } 451 | if (count != 0) { 452 | str += String.fromCharCode(count); 453 | } 454 | } 455 | return str; 456 | } 457 | 458 | function bt64ToHex(byteData) { 459 | var hex = ""; 460 | for (i = 0; i < 16; i++) { 461 | var bt = ""; 462 | for (j = 0; j < 4; j++) { 463 | bt += byteData[i * 4 + j]; 464 | } 465 | hex += bt4ToHex(bt); 466 | } 467 | return hex; 468 | } 469 | 470 | function hexToBt64(hex) { 471 | var binary = ""; 472 | for (i = 0; i < 16; i++) { 473 | binary += hexToBt4(hex.substring(i, i + 1)); 474 | } 475 | return binary; 476 | } 477 | 478 | /* 479 | * the 64 bit des core arithmetic 480 | */ 481 | 482 | function enc(dataByte, keyByte) { 483 | var keys = generateKeys(keyByte); 484 | var ipByte = initPermute(dataByte); 485 | var ipLeft = new Array(32); 486 | var ipRight = new Array(32); 487 | var tempLeft = new Array(32); 488 | var i = 0, 489 | j = 0, 490 | k = 0, 491 | m = 0, 492 | n = 0; 493 | for (k = 0; k < 32; k++) { 494 | ipLeft[k] = ipByte[k]; 495 | ipRight[k] = ipByte[32 + k]; 496 | } 497 | for (i = 0; i < 16; i++) { 498 | for (j = 0; j < 32; j++) { 499 | tempLeft[j] = ipLeft[j]; 500 | ipLeft[j] = ipRight[j]; 501 | } 502 | var key = new Array(48); 503 | for (m = 0; m < 48; m++) { 504 | key[m] = keys[i][m]; 505 | } 506 | var tempRight = xor(pPermute(sBoxPermute(xor(expandPermute(ipRight), key))), tempLeft); 507 | for (n = 0; n < 32; n++) { 508 | ipRight[n] = tempRight[n]; 509 | } 510 | 511 | } 512 | 513 | 514 | var finalData = new Array(64); 515 | for (i = 0; i < 32; i++) { 516 | finalData[i] = ipRight[i]; 517 | finalData[32 + i] = ipLeft[i]; 518 | } 519 | return finallyPermute(finalData); 520 | } 521 | 522 | function dec(dataByte, keyByte) { 523 | var keys = generateKeys(keyByte); 524 | var ipByte = initPermute(dataByte); 525 | var ipLeft = new Array(32); 526 | var ipRight = new Array(32); 527 | var tempLeft = new Array(32); 528 | var i = 0, 529 | j = 0, 530 | k = 0, 531 | m = 0, 532 | n = 0; 533 | for (k = 0; k < 32; k++) { 534 | ipLeft[k] = ipByte[k]; 535 | ipRight[k] = ipByte[32 + k]; 536 | } 537 | for (i = 15; i >= 0; i--) { 538 | for (j = 0; j < 32; j++) { 539 | tempLeft[j] = ipLeft[j]; 540 | ipLeft[j] = ipRight[j]; 541 | } 542 | var key = new Array(48); 543 | for (m = 0; m < 48; m++) { 544 | key[m] = keys[i][m]; 545 | } 546 | 547 | var tempRight = xor(pPermute(sBoxPermute(xor(expandPermute(ipRight), key))), tempLeft); 548 | for (n = 0; n < 32; n++) { 549 | ipRight[n] = tempRight[n]; 550 | } 551 | } 552 | 553 | 554 | var finalData = new Array(64); 555 | for (i = 0; i < 32; i++) { 556 | finalData[i] = ipRight[i]; 557 | finalData[32 + i] = ipLeft[i]; 558 | } 559 | return finallyPermute(finalData); 560 | } 561 | 562 | function initPermute(originalData) { 563 | var ipByte = new Array(64); 564 | for (i = 0, m = 1, n = 0; i < 4; i++, m += 2, n += 2) { 565 | for (j = 7, k = 0; j >= 0; j--, k++) { 566 | ipByte[i * 8 + k] = originalData[j * 8 + m]; 567 | ipByte[i * 8 + k + 32] = originalData[j * 8 + n]; 568 | } 569 | } 570 | return ipByte; 571 | } 572 | 573 | function expandPermute(rightData) { 574 | var epByte = new Array(48); 575 | for (i = 0; i < 8; i++) { 576 | if (i == 0) { 577 | epByte[i * 6 + 0] = rightData[31]; 578 | } else { 579 | epByte[i * 6 + 0] = rightData[i * 4 - 1]; 580 | } 581 | epByte[i * 6 + 1] = rightData[i * 4 + 0]; 582 | epByte[i * 6 + 2] = rightData[i * 4 + 1]; 583 | epByte[i * 6 + 3] = rightData[i * 4 + 2]; 584 | epByte[i * 6 + 4] = rightData[i * 4 + 3]; 585 | if (i == 7) { 586 | epByte[i * 6 + 5] = rightData[0]; 587 | } else { 588 | epByte[i * 6 + 5] = rightData[i * 4 + 4]; 589 | } 590 | } 591 | return epByte; 592 | } 593 | 594 | function xor(byteOne, byteTwo) { 595 | var xorByte = new Array(byteOne.length); 596 | for (i = 0; i < byteOne.length; i++) { 597 | xorByte[i] = byteOne[i] ^ byteTwo[i]; 598 | } 599 | return xorByte; 600 | } 601 | 602 | function sBoxPermute(expandByte) { 603 | 604 | var sBoxByte = new Array(32); 605 | var binary = ""; 606 | var s1 = [ 607 | [14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7], 608 | [0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8], 609 | [4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0], 610 | [15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13] 611 | ]; 612 | 613 | /* Table - s2 */ 614 | var s2 = [ 615 | [15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10], 616 | [3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5], 617 | [0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15], 618 | [13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9] 619 | ]; 620 | 621 | /* Table - s3 */ 622 | var s3 = [ 623 | [10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8], 624 | [13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1], 625 | [13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7], 626 | [1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12] 627 | ]; 628 | /* Table - s4 */ 629 | var s4 = [ 630 | [7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15], 631 | [13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9], 632 | [10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4], 633 | [3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14] 634 | ]; 635 | 636 | /* Table - s5 */ 637 | var s5 = [ 638 | [2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9], 639 | [14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6], 640 | [4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14], 641 | [11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3] 642 | ]; 643 | 644 | /* Table - s6 */ 645 | var s6 = [ 646 | [12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11], 647 | [10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8], 648 | [9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6], 649 | [4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13] 650 | ]; 651 | 652 | /* Table - s7 */ 653 | var s7 = [ 654 | [4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1], 655 | [13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6], 656 | [1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2], 657 | [6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12] 658 | ]; 659 | 660 | /* Table - s8 */ 661 | var s8 = [ 662 | [13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7], 663 | [1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2], 664 | [7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8], 665 | [2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11] 666 | ]; 667 | 668 | for (m = 0; m < 8; m++) { 669 | var i = 0, 670 | j = 0; 671 | i = expandByte[m * 6 + 0] * 2 + expandByte[m * 6 + 5]; 672 | j = expandByte[m * 6 + 1] * 2 * 2 * 2 + 673 | expandByte[m * 6 + 2] * 2 * 2 + 674 | expandByte[m * 6 + 3] * 2 + 675 | expandByte[m * 6 + 4]; 676 | switch (m) { 677 | case 0: 678 | binary = getBoxBinary(s1[i][j]); 679 | break; 680 | case 1: 681 | binary = getBoxBinary(s2[i][j]); 682 | break; 683 | case 2: 684 | binary = getBoxBinary(s3[i][j]); 685 | break; 686 | case 3: 687 | binary = getBoxBinary(s4[i][j]); 688 | break; 689 | case 4: 690 | binary = getBoxBinary(s5[i][j]); 691 | break; 692 | case 5: 693 | binary = getBoxBinary(s6[i][j]); 694 | break; 695 | case 6: 696 | binary = getBoxBinary(s7[i][j]); 697 | break; 698 | case 7: 699 | binary = getBoxBinary(s8[i][j]); 700 | break; 701 | } 702 | sBoxByte[m * 4 + 0] = parseInt(binary.substring(0, 1)); 703 | sBoxByte[m * 4 + 1] = parseInt(binary.substring(1, 2)); 704 | sBoxByte[m * 4 + 2] = parseInt(binary.substring(2, 3)); 705 | sBoxByte[m * 4 + 3] = parseInt(binary.substring(3, 4)); 706 | } 707 | return sBoxByte; 708 | } 709 | 710 | function pPermute(sBoxByte) { 711 | var pBoxPermute = new Array(32); 712 | pBoxPermute[0] = sBoxByte[15]; 713 | pBoxPermute[1] = sBoxByte[6]; 714 | pBoxPermute[2] = sBoxByte[19]; 715 | pBoxPermute[3] = sBoxByte[20]; 716 | pBoxPermute[4] = sBoxByte[28]; 717 | pBoxPermute[5] = sBoxByte[11]; 718 | pBoxPermute[6] = sBoxByte[27]; 719 | pBoxPermute[7] = sBoxByte[16]; 720 | pBoxPermute[8] = sBoxByte[0]; 721 | pBoxPermute[9] = sBoxByte[14]; 722 | pBoxPermute[10] = sBoxByte[22]; 723 | pBoxPermute[11] = sBoxByte[25]; 724 | pBoxPermute[12] = sBoxByte[4]; 725 | pBoxPermute[13] = sBoxByte[17]; 726 | pBoxPermute[14] = sBoxByte[30]; 727 | pBoxPermute[15] = sBoxByte[9]; 728 | pBoxPermute[16] = sBoxByte[1]; 729 | pBoxPermute[17] = sBoxByte[7]; 730 | pBoxPermute[18] = sBoxByte[23]; 731 | pBoxPermute[19] = sBoxByte[13]; 732 | pBoxPermute[20] = sBoxByte[31]; 733 | pBoxPermute[21] = sBoxByte[26]; 734 | pBoxPermute[22] = sBoxByte[2]; 735 | pBoxPermute[23] = sBoxByte[8]; 736 | pBoxPermute[24] = sBoxByte[18]; 737 | pBoxPermute[25] = sBoxByte[12]; 738 | pBoxPermute[26] = sBoxByte[29]; 739 | pBoxPermute[27] = sBoxByte[5]; 740 | pBoxPermute[28] = sBoxByte[21]; 741 | pBoxPermute[29] = sBoxByte[10]; 742 | pBoxPermute[30] = sBoxByte[3]; 743 | pBoxPermute[31] = sBoxByte[24]; 744 | return pBoxPermute; 745 | } 746 | 747 | function finallyPermute(endByte) { 748 | var fpByte = new Array(64); 749 | fpByte[0] = endByte[39]; 750 | fpByte[1] = endByte[7]; 751 | fpByte[2] = endByte[47]; 752 | fpByte[3] = endByte[15]; 753 | fpByte[4] = endByte[55]; 754 | fpByte[5] = endByte[23]; 755 | fpByte[6] = endByte[63]; 756 | fpByte[7] = endByte[31]; 757 | fpByte[8] = endByte[38]; 758 | fpByte[9] = endByte[6]; 759 | fpByte[10] = endByte[46]; 760 | fpByte[11] = endByte[14]; 761 | fpByte[12] = endByte[54]; 762 | fpByte[13] = endByte[22]; 763 | fpByte[14] = endByte[62]; 764 | fpByte[15] = endByte[30]; 765 | fpByte[16] = endByte[37]; 766 | fpByte[17] = endByte[5]; 767 | fpByte[18] = endByte[45]; 768 | fpByte[19] = endByte[13]; 769 | fpByte[20] = endByte[53]; 770 | fpByte[21] = endByte[21]; 771 | fpByte[22] = endByte[61]; 772 | fpByte[23] = endByte[29]; 773 | fpByte[24] = endByte[36]; 774 | fpByte[25] = endByte[4]; 775 | fpByte[26] = endByte[44]; 776 | fpByte[27] = endByte[12]; 777 | fpByte[28] = endByte[52]; 778 | fpByte[29] = endByte[20]; 779 | fpByte[30] = endByte[60]; 780 | fpByte[31] = endByte[28]; 781 | fpByte[32] = endByte[35]; 782 | fpByte[33] = endByte[3]; 783 | fpByte[34] = endByte[43]; 784 | fpByte[35] = endByte[11]; 785 | fpByte[36] = endByte[51]; 786 | fpByte[37] = endByte[19]; 787 | fpByte[38] = endByte[59]; 788 | fpByte[39] = endByte[27]; 789 | fpByte[40] = endByte[34]; 790 | fpByte[41] = endByte[2]; 791 | fpByte[42] = endByte[42]; 792 | fpByte[43] = endByte[10]; 793 | fpByte[44] = endByte[50]; 794 | fpByte[45] = endByte[18]; 795 | fpByte[46] = endByte[58]; 796 | fpByte[47] = endByte[26]; 797 | fpByte[48] = endByte[33]; 798 | fpByte[49] = endByte[1]; 799 | fpByte[50] = endByte[41]; 800 | fpByte[51] = endByte[9]; 801 | fpByte[52] = endByte[49]; 802 | fpByte[53] = endByte[17]; 803 | fpByte[54] = endByte[57]; 804 | fpByte[55] = endByte[25]; 805 | fpByte[56] = endByte[32]; 806 | fpByte[57] = endByte[0]; 807 | fpByte[58] = endByte[40]; 808 | fpByte[59] = endByte[8]; 809 | fpByte[60] = endByte[48]; 810 | fpByte[61] = endByte[16]; 811 | fpByte[62] = endByte[56]; 812 | fpByte[63] = endByte[24]; 813 | return fpByte; 814 | } 815 | 816 | function getBoxBinary(i) { 817 | var binary = ""; 818 | switch (i) { 819 | case 0: 820 | binary = "0000"; 821 | break; 822 | case 1: 823 | binary = "0001"; 824 | break; 825 | case 2: 826 | binary = "0010"; 827 | break; 828 | case 3: 829 | binary = "0011"; 830 | break; 831 | case 4: 832 | binary = "0100"; 833 | break; 834 | case 5: 835 | binary = "0101"; 836 | break; 837 | case 6: 838 | binary = "0110"; 839 | break; 840 | case 7: 841 | binary = "0111"; 842 | break; 843 | case 8: 844 | binary = "1000"; 845 | break; 846 | case 9: 847 | binary = "1001"; 848 | break; 849 | case 10: 850 | binary = "1010"; 851 | break; 852 | case 11: 853 | binary = "1011"; 854 | break; 855 | case 12: 856 | binary = "1100"; 857 | break; 858 | case 13: 859 | binary = "1101"; 860 | break; 861 | case 14: 862 | binary = "1110"; 863 | break; 864 | case 15: 865 | binary = "1111"; 866 | break; 867 | } 868 | return binary; 869 | } 870 | /* 871 | * generate 16 keys for xor 872 | * 873 | */ 874 | function generateKeys(keyByte) { 875 | var key = new Array(56); 876 | var keys = new Array(); 877 | 878 | keys[0] = new Array(); 879 | keys[1] = new Array(); 880 | keys[2] = new Array(); 881 | keys[3] = new Array(); 882 | keys[4] = new Array(); 883 | keys[5] = new Array(); 884 | keys[6] = new Array(); 885 | keys[7] = new Array(); 886 | keys[8] = new Array(); 887 | keys[9] = new Array(); 888 | keys[10] = new Array(); 889 | keys[11] = new Array(); 890 | keys[12] = new Array(); 891 | keys[13] = new Array(); 892 | keys[14] = new Array(); 893 | keys[15] = new Array(); 894 | var loop = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1]; 895 | 896 | for (i = 0; i < 7; i++) { 897 | for (j = 0, k = 7; j < 8; j++, k--) { 898 | key[i * 8 + j] = keyByte[8 * k + i]; 899 | } 900 | } 901 | 902 | var i = 0; 903 | for (i = 0; i < 16; i++) { 904 | var tempLeft = 0; 905 | var tempRight = 0; 906 | for (j = 0; j < loop[i]; j++) { 907 | tempLeft = key[0]; 908 | tempRight = key[28]; 909 | for (k = 0; k < 27; k++) { 910 | key[k] = key[k + 1]; 911 | key[28 + k] = key[29 + k]; 912 | } 913 | key[27] = tempLeft; 914 | key[55] = tempRight; 915 | } 916 | var tempKey = new Array(48); 917 | tempKey[0] = key[13]; 918 | tempKey[1] = key[16]; 919 | tempKey[2] = key[10]; 920 | tempKey[3] = key[23]; 921 | tempKey[4] = key[0]; 922 | tempKey[5] = key[4]; 923 | tempKey[6] = key[2]; 924 | tempKey[7] = key[27]; 925 | tempKey[8] = key[14]; 926 | tempKey[9] = key[5]; 927 | tempKey[10] = key[20]; 928 | tempKey[11] = key[9]; 929 | tempKey[12] = key[22]; 930 | tempKey[13] = key[18]; 931 | tempKey[14] = key[11]; 932 | tempKey[15] = key[3]; 933 | tempKey[16] = key[25]; 934 | tempKey[17] = key[7]; 935 | tempKey[18] = key[15]; 936 | tempKey[19] = key[6]; 937 | tempKey[20] = key[26]; 938 | tempKey[21] = key[19]; 939 | tempKey[22] = key[12]; 940 | tempKey[23] = key[1]; 941 | tempKey[24] = key[40]; 942 | tempKey[25] = key[51]; 943 | tempKey[26] = key[30]; 944 | tempKey[27] = key[36]; 945 | tempKey[28] = key[46]; 946 | tempKey[29] = key[54]; 947 | tempKey[30] = key[29]; 948 | tempKey[31] = key[39]; 949 | tempKey[32] = key[50]; 950 | tempKey[33] = key[44]; 951 | tempKey[34] = key[32]; 952 | tempKey[35] = key[47]; 953 | tempKey[36] = key[43]; 954 | tempKey[37] = key[48]; 955 | tempKey[38] = key[38]; 956 | tempKey[39] = key[55]; 957 | tempKey[40] = key[33]; 958 | tempKey[41] = key[52]; 959 | tempKey[42] = key[45]; 960 | tempKey[43] = key[41]; 961 | tempKey[44] = key[49]; 962 | tempKey[45] = key[35]; 963 | tempKey[46] = key[28]; 964 | tempKey[47] = key[31]; 965 | switch (i) { 966 | case 0: 967 | for (m = 0; m < 48; m++) { 968 | keys[0][m] = tempKey[m]; 969 | } 970 | break; 971 | case 1: 972 | for (m = 0; m < 48; m++) { 973 | keys[1][m] = tempKey[m]; 974 | } 975 | break; 976 | case 2: 977 | for (m = 0; m < 48; m++) { 978 | keys[2][m] = tempKey[m]; 979 | } 980 | break; 981 | case 3: 982 | for (m = 0; m < 48; m++) { 983 | keys[3][m] = tempKey[m]; 984 | } 985 | break; 986 | case 4: 987 | for (m = 0; m < 48; m++) { 988 | keys[4][m] = tempKey[m]; 989 | } 990 | break; 991 | case 5: 992 | for (m = 0; m < 48; m++) { 993 | keys[5][m] = tempKey[m]; 994 | } 995 | break; 996 | case 6: 997 | for (m = 0; m < 48; m++) { 998 | keys[6][m] = tempKey[m]; 999 | } 1000 | break; 1001 | case 7: 1002 | for (m = 0; m < 48; m++) { 1003 | keys[7][m] = tempKey[m]; 1004 | } 1005 | break; 1006 | case 8: 1007 | for (m = 0; m < 48; m++) { 1008 | keys[8][m] = tempKey[m]; 1009 | } 1010 | break; 1011 | case 9: 1012 | for (m = 0; m < 48; m++) { 1013 | keys[9][m] = tempKey[m]; 1014 | } 1015 | break; 1016 | case 10: 1017 | for (m = 0; m < 48; m++) { 1018 | keys[10][m] = tempKey[m]; 1019 | } 1020 | break; 1021 | case 11: 1022 | for (m = 0; m < 48; m++) { 1023 | keys[11][m] = tempKey[m]; 1024 | } 1025 | break; 1026 | case 12: 1027 | for (m = 0; m < 48; m++) { 1028 | keys[12][m] = tempKey[m]; 1029 | } 1030 | break; 1031 | case 13: 1032 | for (m = 0; m < 48; m++) { 1033 | keys[13][m] = tempKey[m]; 1034 | } 1035 | break; 1036 | case 14: 1037 | for (m = 0; m < 48; m++) { 1038 | keys[14][m] = tempKey[m]; 1039 | } 1040 | break; 1041 | case 15: 1042 | for (m = 0; m < 48; m++) { 1043 | keys[15][m] = tempKey[m]; 1044 | } 1045 | break; 1046 | } 1047 | } 1048 | return keys; 1049 | } -------------------------------------------------------------------------------- /des.py: -------------------------------------------------------------------------------- 1 | ############################################################################# 2 | # Documentation # 3 | ############################################################################# 4 | 5 | # Author: Todd Whiteman 6 | # Date: 16th March, 2009 7 | # Verion: 2.0.0 8 | # License: MIT 9 | # Homepage: http://twhiteman.netfirms.com/des.html 10 | # 11 | # This is a pure python implementation of the DES encryption algorithm. 12 | # It's pure python to avoid portability issues, since most DES 13 | # implementations are programmed in C (for performance reasons). 14 | # 15 | # Triple DES class is also implemented, utilising the DES base. Triple DES 16 | # is either DES-EDE3 with a 24 byte key, or DES-EDE2 with a 16 byte key. 17 | # 18 | # See the README.txt that should come with this python module for the 19 | # implementation methods used. 20 | # 21 | # Thanks to: 22 | # * David Broadwell for ideas, comments and suggestions. 23 | # * Mario Wolff for pointing out and debugging some triple des CBC errors. 24 | # * Santiago Palladino for providing the PKCS5 padding technique. 25 | # * Shaya for correcting the PAD_PKCS5 triple des CBC errors. 26 | # 27 | """A pure python implementation of the DES and TRIPLE DES encryption algorithms. 28 | 29 | Class initialization 30 | -------------------- 31 | pyDes.des(key, [mode], [IV], [pad], [padmode]) 32 | pyDes.triple_des(key, [mode], [IV], [pad], [padmode]) 33 | 34 | key -> Bytes containing the encryption key. 8 bytes for DES, 16 or 24 bytes 35 | for Triple DES 36 | mode -> Optional argument for encryption type, can be either 37 | pyDes.ECB (Electronic Code Book) or pyDes.CBC (Cypher Block Chaining) 38 | IV -> Optional Initial Value bytes, must be supplied if using CBC mode. 39 | Length must be 8 bytes. 40 | pad -> Optional argument, set the pad character (PAD_NORMAL) to use during 41 | all encrypt/decrpt operations done with this instance. 42 | padmode -> Optional argument, set the padding mode (PAD_NORMAL or PAD_PKCS5) 43 | to use during all encrypt/decrpt operations done with this instance. 44 | 45 | I recommend to use PAD_PKCS5 padding, as then you never need to worry about any 46 | padding issues, as the padding can be removed unambiguously upon decrypting 47 | data that was encrypted using PAD_PKCS5 padmode. 48 | 49 | Common methods 50 | -------------- 51 | encrypt(data, [pad], [padmode]) 52 | decrypt(data, [pad], [padmode]) 53 | 54 | data -> Bytes to be encrypted/decrypted 55 | pad -> Optional argument. Only when using padmode of PAD_NORMAL. For 56 | encryption, adds this characters to the end of the data block when 57 | data is not a multiple of 8 bytes. For decryption, will remove the 58 | trailing characters that match this pad character from the last 8 59 | bytes of the unencrypted data block. 60 | padmode -> Optional argument, set the padding mode, must be one of PAD_NORMAL 61 | or PAD_PKCS5). Defaults to PAD_NORMAL. 62 | 63 | 64 | Example 65 | ------- 66 | from pyDes import * 67 | 68 | data = "Please encrypt my data" 69 | k = des("DESCRYPT", CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5) 70 | # For Python3, you'll need to use bytes, i.e.: 71 | # data = b"Please encrypt my data" 72 | # k = des(b"DESCRYPT", CBC, b"\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5) 73 | d = k.encrypt(data) 74 | print "Encrypted: %r" % d 75 | print "Decrypted: %r" % k.decrypt(d) 76 | assert k.decrypt(d, padmode=PAD_PKCS5) == data 77 | 78 | 79 | See the module source (pyDes.py) for more examples of use. 80 | You can also run the pyDes.py file without and arguments to see a simple test. 81 | 82 | Note: This code was not written for high-end systems needing a fast 83 | implementation, but rather a handy portable solution with small usage. 84 | 85 | """ 86 | 87 | import sys 88 | 89 | # _pythonMajorVersion is used to handle Python2 and Python3 differences. 90 | _pythonMajorVersion = sys.version_info[0] 91 | 92 | # Modes of crypting / cyphering 93 | ECB = 0 94 | CBC = 1 95 | 96 | # Modes of padding 97 | PAD_NORMAL = 1 98 | PAD_PKCS5 = 2 99 | 100 | # PAD_PKCS5: is a method that will unambiguously remove all padding 101 | # characters after decryption, when originally encrypted with 102 | # this padding mode. 103 | # For a good description of the PKCS5 padding technique, see: 104 | # http://www.faqs.org/rfcs/rfc1423.html 105 | 106 | # The base class shared by des and triple des. 107 | 108 | 109 | class _baseDes(object): 110 | def __init__(self, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL): 111 | if IV: 112 | IV = self._guardAgainstUnicode(IV) 113 | if pad: 114 | pad = self._guardAgainstUnicode(pad) 115 | self.block_size = 8 116 | # Sanity checking of arguments. 117 | if pad and padmode == PAD_PKCS5: 118 | raise ValueError("Cannot use a pad character with PAD_PKCS5") 119 | if IV and len(IV) != self.block_size: 120 | raise ValueError( 121 | "Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes") 122 | 123 | # Set the passed in variables 124 | self._mode = mode 125 | self._iv = IV 126 | self._padding = pad 127 | self._padmode = padmode 128 | 129 | def getKey(self): 130 | """getKey() -> bytes""" 131 | return self.__key 132 | 133 | def setKey(self, key): 134 | """Will set the crypting key for this object.""" 135 | key = self._guardAgainstUnicode(key) 136 | self.__key = key 137 | 138 | def getMode(self): 139 | """getMode() -> pyDes.ECB or pyDes.CBC""" 140 | return self._mode 141 | 142 | def setMode(self, mode): 143 | """Sets the type of crypting mode, pyDes.ECB or pyDes.CBC""" 144 | self._mode = mode 145 | 146 | def getPadding(self): 147 | """getPadding() -> bytes of length 1. Padding character.""" 148 | return self._padding 149 | 150 | def setPadding(self, pad): 151 | """setPadding() -> bytes of length 1. Padding character.""" 152 | if pad is not None: 153 | pad = self._guardAgainstUnicode(pad) 154 | self._padding = pad 155 | 156 | def getPadMode(self): 157 | """getPadMode() -> pyDes.PAD_NORMAL or pyDes.PAD_PKCS5""" 158 | return self._padmode 159 | 160 | def setPadMode(self, mode): 161 | """Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5""" 162 | self._padmode = mode 163 | 164 | def getIV(self): 165 | """getIV() -> bytes""" 166 | return self._iv 167 | 168 | def setIV(self, IV): 169 | """Will set the Initial Value, used in conjunction with CBC mode""" 170 | if not IV or len(IV) != self.block_size: 171 | raise ValueError( 172 | "Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes") 173 | IV = self._guardAgainstUnicode(IV) 174 | self._iv = IV 175 | 176 | def _padData(self, data, pad, padmode): 177 | # Pad data depending on the mode 178 | if padmode is None: 179 | # Get the default padding mode. 180 | padmode = self.getPadMode() 181 | if pad and padmode == PAD_PKCS5: 182 | raise ValueError("Cannot use a pad character with PAD_PKCS5") 183 | 184 | if padmode == PAD_NORMAL: 185 | if len(data) % self.block_size == 0: 186 | # No padding required. 187 | return data 188 | 189 | if not pad: 190 | # Get the default padding. 191 | pad = self.getPadding() 192 | if not pad: 193 | raise ValueError("Data must be a multiple of " + str(self.block_size) + 194 | " bytes in length. Use padmode=PAD_PKCS5 or set the pad character.") 195 | data += (self.block_size - (len(data) % self.block_size)) * pad 196 | 197 | elif padmode == PAD_PKCS5: 198 | pad_len = 8 - (len(data) % self.block_size) 199 | if _pythonMajorVersion < 3: 200 | data += pad_len * chr(pad_len) 201 | else: 202 | data += bytes([pad_len] * pad_len) 203 | 204 | return data 205 | 206 | def _unpadData(self, data, pad, padmode): 207 | # Unpad data depending on the mode. 208 | if not data: 209 | return data 210 | if pad and padmode == PAD_PKCS5: 211 | raise ValueError("Cannot use a pad character with PAD_PKCS5") 212 | if padmode is None: 213 | # Get the default padding mode. 214 | padmode = self.getPadMode() 215 | 216 | if padmode == PAD_NORMAL: 217 | if not pad: 218 | # Get the default padding. 219 | pad = self.getPadding() 220 | if pad: 221 | data = data[:-self.block_size] + \ 222 | data[-self.block_size:].rstrip(pad) 223 | 224 | elif padmode == PAD_PKCS5: 225 | if _pythonMajorVersion < 3: 226 | pad_len = ord(data[-1]) 227 | else: 228 | pad_len = data[-1] 229 | data = data[:-pad_len] 230 | 231 | return data 232 | 233 | def _guardAgainstUnicode(self, data): 234 | # Only accept byte strings or ascii unicode values, otherwise 235 | # there is no way to correctly decode the data into bytes. 236 | if _pythonMajorVersion < 3: 237 | if isinstance(data, unicode): 238 | raise ValueError( 239 | "pyDes can only work with bytes, not Unicode strings.") 240 | else: 241 | if isinstance(data, str): 242 | # Only accept ascii unicode values. 243 | try: 244 | return data.encode('ascii') 245 | except UnicodeEncodeError: 246 | pass 247 | raise ValueError( 248 | "pyDes can only work with encoded strings, not Unicode.") 249 | return data 250 | 251 | ############################################################################# 252 | # DES # 253 | ############################################################################# 254 | 255 | 256 | class des(_baseDes): 257 | """DES encryption/decrytpion class 258 | 259 | Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes. 260 | 261 | pyDes.des(key,[mode], [IV]) 262 | 263 | key -> Bytes containing the encryption key, must be exactly 8 bytes 264 | mode -> Optional argument for encryption type, can be either pyDes.ECB 265 | (Electronic Code Book), pyDes.CBC (Cypher Block Chaining) 266 | IV -> Optional Initial Value bytes, must be supplied if using CBC mode. 267 | Must be 8 bytes in length. 268 | pad -> Optional argument, set the pad character (PAD_NORMAL) to use 269 | during all encrypt/decrpt operations done with this instance. 270 | padmode -> Optional argument, set the padding mode (PAD_NORMAL or 271 | PAD_PKCS5) to use during all encrypt/decrpt operations done 272 | with this instance. 273 | """ 274 | 275 | # Permutation and translation tables for DES 276 | # __pc1 = [56, 48, 40, 32, 24, 16, 8, 277 | # 0, 57, 49, 41, 33, 25, 17, 278 | # 9, 1, 58, 50, 42, 34, 26, 279 | # 18, 10, 2, 59, 51, 43, 35, 280 | # 62, 54, 46, 38, 30, 22, 14, 281 | # 6, 61, 53, 45, 37, 29, 21, 282 | # 13, 5, 60, 52, 44, 36, 28, 283 | # 20, 12, 4, 27, 19, 11, 3 284 | # ] 285 | __pc1 = [ 286 | 56, 48, 40, 32, 24, 16, 8, 0, 287 | 57, 49, 41, 33, 25, 17, 9, 1, 288 | 58, 50, 42, 34, 26, 18, 10, 2, 289 | 59, 51, 43, 35, 27, 19, 11, 3, 290 | 60, 52, 44, 36, 28, 20, 12, 4, 291 | 61, 53, 45, 37, 29, 21, 13, 5, 292 | 62, 54, 46, 38, 30, 22, 14, 6 293 | ] 294 | 295 | # number left rotations of pc1 296 | __left_rotations = [ 297 | 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 298 | ] 299 | 300 | # permuted choice key (table 2) 301 | __pc2 = [ 302 | 13, 16, 10, 23, 0, 4, 303 | 2, 27, 14, 5, 20, 9, 304 | 22, 18, 11, 3, 25, 7, 305 | 15, 6, 26, 19, 12, 1, 306 | 40, 51, 30, 36, 46, 54, 307 | 29, 39, 50, 44, 32, 47, 308 | 43, 48, 38, 55, 33, 52, 309 | 45, 41, 49, 35, 28, 31 310 | ] 311 | 312 | # initial permutation IP 313 | __ip = [57, 49, 41, 33, 25, 17, 9, 1, 314 | 59, 51, 43, 35, 27, 19, 11, 3, 315 | 61, 53, 45, 37, 29, 21, 13, 5, 316 | 63, 55, 47, 39, 31, 23, 15, 7, 317 | 56, 48, 40, 32, 24, 16, 8, 0, 318 | 58, 50, 42, 34, 26, 18, 10, 2, 319 | 60, 52, 44, 36, 28, 20, 12, 4, 320 | 62, 54, 46, 38, 30, 22, 14, 6 321 | ] 322 | 323 | # Expansion table for turning 32 bit blocks into 48 bits 324 | __expansion_table = [ 325 | 31, 0, 1, 2, 3, 4, 326 | 3, 4, 5, 6, 7, 8, 327 | 7, 8, 9, 10, 11, 12, 328 | 11, 12, 13, 14, 15, 16, 329 | 15, 16, 17, 18, 19, 20, 330 | 19, 20, 21, 22, 23, 24, 331 | 23, 24, 25, 26, 27, 28, 332 | 27, 28, 29, 30, 31, 0 333 | ] 334 | 335 | # The (in)famous S-boxes 336 | __sbox = [ 337 | # S1 338 | [14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, 339 | 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, 340 | 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, 341 | 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13], 342 | 343 | # S2 344 | [15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, 345 | 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, 346 | 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, 347 | 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9], 348 | 349 | # S3 350 | [10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, 351 | 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, 352 | 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, 353 | 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12], 354 | 355 | # S4 356 | [7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, 357 | 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, 358 | 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, 359 | 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14], 360 | 361 | # S5 362 | [2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, 363 | 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, 364 | 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, 365 | 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3], 366 | 367 | # S6 368 | [12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, 369 | 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, 370 | 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, 371 | 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13], 372 | 373 | # S7 374 | [4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, 375 | 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, 376 | 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, 377 | 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12], 378 | 379 | # S8 380 | [13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, 381 | 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, 382 | 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, 383 | 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11], 384 | ] 385 | 386 | # 32-bit permutation function P used on the output of the S-boxes 387 | __p = [ 388 | 15, 6, 19, 20, 28, 11, 389 | 27, 16, 0, 14, 22, 25, 390 | 4, 17, 30, 9, 1, 7, 391 | 23, 13, 31, 26, 2, 8, 392 | 18, 12, 29, 5, 21, 10, 393 | 3, 24 394 | ] 395 | 396 | # final permutation IP^-1 397 | __fp = [ 398 | 39, 7, 47, 15, 55, 23, 63, 31, 399 | 38, 6, 46, 14, 54, 22, 62, 30, 400 | 37, 5, 45, 13, 53, 21, 61, 29, 401 | 36, 4, 44, 12, 52, 20, 60, 28, 402 | 35, 3, 43, 11, 51, 19, 59, 27, 403 | 34, 2, 42, 10, 50, 18, 58, 26, 404 | 33, 1, 41, 9, 49, 17, 57, 25, 405 | 32, 0, 40, 8, 48, 16, 56, 24 406 | ] 407 | 408 | # Type of crypting being done 409 | ENCRYPT = 0x00 410 | DECRYPT = 0x01 411 | 412 | # Initialisation 413 | def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL): 414 | # Sanity checking of arguments. 415 | if len(key) != 8: 416 | raise ValueError( 417 | "Invalid DES key size. Key must be exactly 8 bytes long.") 418 | _baseDes.__init__(self, mode, IV, pad, padmode) 419 | self.key_size = 8 420 | 421 | self.L = [] 422 | self.R = [] 423 | self.Kn = [[0] * 48] * 16 # 16 48-bit keys (K1 - K16) 424 | self.final = [] 425 | 426 | self.setKey(key) 427 | 428 | def setKey(self, key): 429 | """Will set the crypting key for this object. Must be 8 bytes.""" 430 | _baseDes.setKey(self, key) 431 | self.__create_sub_keys() 432 | 433 | def __String_to_BitList(self, data): 434 | """Turn the string data, into a list of bits (1, 0)'s""" 435 | if _pythonMajorVersion < 3: 436 | # Turn the strings into integers. Python 3 uses a bytes 437 | # class, which already has this behaviour. 438 | data = [ord(c) for c in data] 439 | l = len(data) * 8 440 | result = [0] * l 441 | pos = 0 442 | for ch in data: 443 | i = 7 444 | while i >= 0: 445 | if ch & (1 << i) != 0: 446 | result[pos] = 1 447 | else: 448 | result[pos] = 0 449 | pos += 1 450 | i -= 1 451 | 452 | return result 453 | 454 | def __BitList_to_String(self, data): 455 | """Turn the list of bits -> data, into a string""" 456 | result = [] 457 | pos = 0 458 | c = 0 459 | while pos < len(data): 460 | c += data[pos] << (7 - (pos % 8)) 461 | if (pos % 8) == 7: 462 | result.append(c) 463 | c = 0 464 | pos += 1 465 | 466 | if _pythonMajorVersion < 3: 467 | return ''.join([chr(c) for c in result]) 468 | else: 469 | return bytes(result) 470 | 471 | def __permutate(self, table, block): 472 | """Permutate this block with the specified table""" 473 | return list(map(lambda x: block[x], table)) 474 | 475 | # Transform the secret key, so that it is ready for data processing 476 | # Create the 16 subkeys, K[1] - K[16] 477 | def __create_sub_keys(self): 478 | """Create the 16 subkeys K[1] to K[16] from the given key""" 479 | key = self.__permutate( 480 | des.__pc1, self.__String_to_BitList(self.getKey())) 481 | i = 0 482 | # Split into Left and Right sections 483 | self.L = key[:28] 484 | self.R = key[28:] 485 | while i < 16: 486 | j = 0 487 | # Perform circular left shifts 488 | while j < des.__left_rotations[i]: 489 | self.L.append(self.L[0]) 490 | del self.L[0] 491 | 492 | self.R.append(self.R[0]) 493 | del self.R[0] 494 | 495 | j += 1 496 | 497 | # Create one of the 16 subkeys through pc2 permutation 498 | self.Kn[i] = self.__permutate(des.__pc2, self.L + self.R) 499 | 500 | i += 1 501 | 502 | # Main part of the encryption algorithm, the number cruncher :) 503 | def __des_crypt(self, block, crypt_type): 504 | """Crypt the block of data through DES bit-manipulation""" 505 | block = self.__permutate(des.__ip, block) 506 | self.L = block[:32] 507 | self.R = block[32:] 508 | 509 | # Encryption starts from Kn[1] through to Kn[16] 510 | if crypt_type == des.ENCRYPT: 511 | iteration = 0 512 | iteration_adjustment = 1 513 | # Decryption starts from Kn[16] down to Kn[1] 514 | else: 515 | iteration = 15 516 | iteration_adjustment = -1 517 | 518 | i = 0 519 | while i < 16: 520 | # Make a copy of R[i-1], this will later become L[i] 521 | tempR = self.R[:] 522 | 523 | # Permutate R[i - 1] to start creating R[i] 524 | self.R = self.__permutate(des.__expansion_table, self.R) 525 | 526 | # Exclusive or R[i - 1] with K[i], create B[1] to B[8] whilst here 527 | self.R = list(map(lambda x, y: x ^ y, self.R, self.Kn[iteration])) 528 | B = [self.R[:6], self.R[6:12], self.R[12:18], self.R[18:24], 529 | self.R[24:30], self.R[30:36], self.R[36:42], self.R[42:]] 530 | # Optimization: Replaced below commented code with above 531 | #j = 0 532 | #B = [] 533 | # while j < len(self.R): 534 | # self.R[j] = self.R[j] ^ self.Kn[iteration][j] 535 | # j += 1 536 | # if j % 6 == 0: 537 | # B.append(self.R[j-6:j]) 538 | 539 | # Permutate B[1] to B[8] using the S-Boxes 540 | j = 0 541 | Bn = [0] * 32 542 | pos = 0 543 | while j < 8: 544 | # Work out the offsets 545 | m = (B[j][0] << 1) + B[j][5] 546 | n = (B[j][1] << 3) + (B[j][2] << 2) + (B[j][3] << 1) + B[j][4] 547 | 548 | # Find the permutation value 549 | v = des.__sbox[j][(m << 4) + n] 550 | 551 | # Turn value into bits, add it to result: Bn 552 | Bn[pos] = (v & 8) >> 3 553 | Bn[pos + 1] = (v & 4) >> 2 554 | Bn[pos + 2] = (v & 2) >> 1 555 | Bn[pos + 3] = v & 1 556 | 557 | pos += 4 558 | j += 1 559 | 560 | # Permutate the concatination of B[1] to B[8] (Bn) 561 | self.R = self.__permutate(des.__p, Bn) 562 | 563 | # Xor with L[i - 1] 564 | self.R = list(map(lambda x, y: x ^ y, self.R, self.L)) 565 | # Optimization: This now replaces the below commented code 566 | #j = 0 567 | # while j < len(self.R): 568 | # self.R[j] = self.R[j] ^ self.L[j] 569 | # j += 1 570 | 571 | # L[i] becomes R[i - 1] 572 | self.L = tempR 573 | 574 | i += 1 575 | iteration += iteration_adjustment 576 | 577 | # Final permutation of R[16]L[16] 578 | self.final = self.__permutate(des.__fp, self.R + self.L) 579 | return self.final 580 | 581 | # Data to be encrypted/decrypted 582 | 583 | def crypt(self, data, crypt_type): 584 | """Crypt the data in blocks, running it through des_crypt()""" 585 | 586 | # Error check the data 587 | if not data: 588 | return '' 589 | if len(data) % self.block_size != 0: 590 | if crypt_type == des.DECRYPT: # Decryption must work on 8 byte blocks 591 | raise ValueError( 592 | "Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n.") 593 | if not self.getPadding(): 594 | raise ValueError("Invalid data length, data must be a multiple of " + str( 595 | self.block_size) + " bytes\n. Try setting the optional padding character") 596 | else: 597 | data += (self.block_size - (len(data) % 598 | self.block_size)) * self.getPadding() 599 | # print "Len of data: %f" % (len(data) / self.block_size) 600 | 601 | if self.getMode() == CBC: 602 | if self.getIV(): 603 | iv = self.__String_to_BitList(self.getIV()) 604 | else: 605 | raise ValueError( 606 | "For CBC mode, you must supply the Initial Value (IV) for ciphering") 607 | 608 | # Split the data into blocks, crypting each one seperately 609 | i = 0 610 | dict = {} 611 | result = [] 612 | #cached = 0 613 | #lines = 0 614 | while i < len(data): 615 | # Test code for caching encryption results 616 | #lines += 1 617 | # if dict.has_key(data[i:i+8]): 618 | # print "Cached result for: %s" % data[i:i+8] 619 | # cached += 1 620 | # result.append(dict[data[i:i+8]]) 621 | # i += 8 622 | # continue 623 | 624 | block = self.__String_to_BitList(data[i:i+8]) 625 | 626 | # Xor with IV if using CBC mode 627 | if self.getMode() == CBC: 628 | if crypt_type == des.ENCRYPT: 629 | block = list(map(lambda x, y: x ^ y, block, iv)) 630 | #j = 0 631 | # while j < len(block): 632 | # block[j] = block[j] ^ iv[j] 633 | # j += 1 634 | 635 | processed_block = self.__des_crypt(block, crypt_type) 636 | 637 | if crypt_type == des.DECRYPT: 638 | processed_block = list( 639 | map(lambda x, y: x ^ y, processed_block, iv)) 640 | #j = 0 641 | # while j < len(processed_block): 642 | # processed_block[j] = processed_block[j] ^ iv[j] 643 | # j += 1 644 | iv = block 645 | else: 646 | iv = processed_block 647 | else: 648 | processed_block = self.__des_crypt(block, crypt_type) 649 | 650 | # Add the resulting crypted block to our list 651 | #d = self.__BitList_to_String(processed_block) 652 | # result.append(d) 653 | result.append(self.__BitList_to_String(processed_block)) 654 | #dict[data[i:i+8]] = d 655 | i += 8 656 | 657 | # print "Lines: %d, cached: %d" % (lines, cached) 658 | 659 | # Return the full crypted string 660 | if _pythonMajorVersion < 3: 661 | return ''.join(result) 662 | else: 663 | return bytes.fromhex('').join(result) 664 | 665 | def encrypt(self, data, pad=None, padmode=None): 666 | """encrypt(data, [pad], [padmode]) -> bytes 667 | 668 | data : Bytes to be encrypted 669 | pad : Optional argument for encryption padding. Must only be one byte 670 | padmode : Optional argument for overriding the padding mode. 671 | 672 | The data must be a multiple of 8 bytes and will be encrypted 673 | with the already specified key. Data does not have to be a 674 | multiple of 8 bytes if the padding character is supplied, or 675 | the padmode is set to PAD_PKCS5, as bytes will then added to 676 | ensure the be padded data is a multiple of 8 bytes. 677 | """ 678 | data = self._guardAgainstUnicode(data) 679 | if pad is not None: 680 | pad = self._guardAgainstUnicode(pad) 681 | data = self._padData(data, pad, padmode) 682 | return self.crypt(data, des.ENCRYPT) 683 | 684 | def decrypt(self, data, pad=None, padmode=None): 685 | """decrypt(data, [pad], [padmode]) -> bytes 686 | 687 | data : Bytes to be encrypted 688 | pad : Optional argument for decryption padding. Must only be one byte 689 | padmode : Optional argument for overriding the padding mode. 690 | 691 | The data must be a multiple of 8 bytes and will be decrypted 692 | with the already specified key. In PAD_NORMAL mode, if the 693 | optional padding character is supplied, then the un-encrypted 694 | data will have the padding characters removed from the end of 695 | the bytes. This pad removal only occurs on the last 8 bytes of 696 | the data (last data block). In PAD_PKCS5 mode, the special 697 | padding end markers will be removed from the data after decrypting. 698 | """ 699 | data = self._guardAgainstUnicode(data) 700 | if pad is not None: 701 | pad = self._guardAgainstUnicode(pad) 702 | data = self.crypt(data, des.DECRYPT) 703 | return self._unpadData(data, pad, padmode) 704 | 705 | 706 | ############################################################################# 707 | # Triple DES # 708 | ############################################################################# 709 | class triple_des(_baseDes): 710 | """Triple DES encryption/decrytpion class 711 | 712 | This algorithm uses the DES-EDE3 (when a 24 byte key is supplied) or 713 | the DES-EDE2 (when a 16 byte key is supplied) encryption methods. 714 | Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes. 715 | 716 | pyDes.des(key, [mode], [IV]) 717 | 718 | key -> Bytes containing the encryption key, must be either 16 or 719 | 24 bytes long 720 | mode -> Optional argument for encryption type, can be either pyDes.ECB 721 | (Electronic Code Book), pyDes.CBC (Cypher Block Chaining) 722 | IV -> Optional Initial Value bytes, must be supplied if using CBC mode. 723 | Must be 8 bytes in length. 724 | pad -> Optional argument, set the pad character (PAD_NORMAL) to use 725 | during all encrypt/decrpt operations done with this instance. 726 | padmode -> Optional argument, set the padding mode (PAD_NORMAL or 727 | PAD_PKCS5) to use during all encrypt/decrpt operations done 728 | with this instance. 729 | """ 730 | 731 | def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL): 732 | _baseDes.__init__(self, mode, IV, pad, padmode) 733 | self.setKey(key) 734 | 735 | def setKey(self, key): 736 | """Will set the crypting key for this object. Either 16 or 24 bytes long.""" 737 | self.key_size = 24 # Use DES-EDE3 mode 738 | if len(key) != self.key_size: 739 | if len(key) == 16: # Use DES-EDE2 mode 740 | self.key_size = 16 741 | else: 742 | raise ValueError( 743 | "Invalid triple DES key size. Key must be either 16 or 24 bytes long") 744 | if self.getMode() == CBC: 745 | if not self.getIV(): 746 | # Use the first 8 bytes of the key 747 | self._iv = key[:self.block_size] 748 | if len(self.getIV()) != self.block_size: 749 | raise ValueError("Invalid IV, must be 8 bytes in length") 750 | self.__key1 = des(key[:8], self._mode, self._iv, 751 | self._padding, self._padmode) 752 | self.__key2 = des(key[8:16], self._mode, self._iv, 753 | self._padding, self._padmode) 754 | if self.key_size == 16: 755 | self.__key3 = self.__key1 756 | else: 757 | self.__key3 = des(key[16:], self._mode, self._iv, 758 | self._padding, self._padmode) 759 | _baseDes.setKey(self, key) 760 | 761 | # Override setter methods to work on all 3 keys. 762 | 763 | def setMode(self, mode): 764 | """Sets the type of crypting mode, pyDes.ECB or pyDes.CBC""" 765 | _baseDes.setMode(self, mode) 766 | for key in (self.__key1, self.__key2, self.__key3): 767 | key.setMode(mode) 768 | 769 | def setPadding(self, pad): 770 | """setPadding() -> bytes of length 1. Padding character.""" 771 | _baseDes.setPadding(self, pad) 772 | for key in (self.__key1, self.__key2, self.__key3): 773 | key.setPadding(pad) 774 | 775 | def setPadMode(self, mode): 776 | """Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5""" 777 | _baseDes.setPadMode(self, mode) 778 | for key in (self.__key1, self.__key2, self.__key3): 779 | key.setPadMode(mode) 780 | 781 | def setIV(self, IV): 782 | """Will set the Initial Value, used in conjunction with CBC mode""" 783 | _baseDes.setIV(self, IV) 784 | for key in (self.__key1, self.__key2, self.__key3): 785 | key.setIV(IV) 786 | 787 | def encrypt(self, data, pad=None, padmode=None): 788 | """encrypt(data, [pad], [padmode]) -> bytes 789 | 790 | data : bytes to be encrypted 791 | pad : Optional argument for encryption padding. Must only be one byte 792 | padmode : Optional argument for overriding the padding mode. 793 | 794 | The data must be a multiple of 8 bytes and will be encrypted 795 | with the already specified key. Data does not have to be a 796 | multiple of 8 bytes if the padding character is supplied, or 797 | the padmode is set to PAD_PKCS5, as bytes will then added to 798 | ensure the be padded data is a multiple of 8 bytes. 799 | """ 800 | ENCRYPT = des.ENCRYPT 801 | DECRYPT = des.DECRYPT 802 | data = self._guardAgainstUnicode(data) 803 | if pad is not None: 804 | pad = self._guardAgainstUnicode(pad) 805 | # Pad the data accordingly. 806 | data = self._padData(data, pad, padmode) 807 | if self.getMode() == CBC: 808 | self.__key1.setIV(self.getIV()) 809 | self.__key2.setIV(self.getIV()) 810 | self.__key3.setIV(self.getIV()) 811 | i = 0 812 | result = [] 813 | while i < len(data): 814 | block = self.__key1.crypt(data[i:i+8], ENCRYPT) 815 | block = self.__key2.crypt(block, DECRYPT) 816 | block = self.__key3.crypt(block, ENCRYPT) 817 | self.__key1.setIV(block) 818 | self.__key2.setIV(block) 819 | self.__key3.setIV(block) 820 | result.append(block) 821 | i += 8 822 | if _pythonMajorVersion < 3: 823 | return ''.join(result) 824 | else: 825 | return bytes.fromhex('').join(result) 826 | else: 827 | data = self.__key1.crypt(data, ENCRYPT) 828 | data = self.__key2.crypt(data, DECRYPT) 829 | return self.__key3.crypt(data, ENCRYPT) 830 | 831 | def decrypt(self, data, pad=None, padmode=None): 832 | """decrypt(data, [pad], [padmode]) -> bytes 833 | 834 | data : bytes to be encrypted 835 | pad : Optional argument for decryption padding. Must only be one byte 836 | padmode : Optional argument for overriding the padding mode. 837 | 838 | The data must be a multiple of 8 bytes and will be decrypted 839 | with the already specified key. In PAD_NORMAL mode, if the 840 | optional padding character is supplied, then the un-encrypted 841 | data will have the padding characters removed from the end of 842 | the bytes. This pad removal only occurs on the last 8 bytes of 843 | the data (last data block). In PAD_PKCS5 mode, the special 844 | padding end markers will be removed from the data after 845 | decrypting, no pad character is required for PAD_PKCS5. 846 | """ 847 | ENCRYPT = des.ENCRYPT 848 | DECRYPT = des.DECRYPT 849 | data = self._guardAgainstUnicode(data) 850 | if pad is not None: 851 | pad = self._guardAgainstUnicode(pad) 852 | if self.getMode() == CBC: 853 | self.__key1.setIV(self.getIV()) 854 | self.__key2.setIV(self.getIV()) 855 | self.__key3.setIV(self.getIV()) 856 | i = 0 857 | result = [] 858 | while i < len(data): 859 | iv = data[i:i+8] 860 | block = self.__key3.crypt(iv, DECRYPT) 861 | block = self.__key2.crypt(block, ENCRYPT) 862 | block = self.__key1.crypt(block, DECRYPT) 863 | self.__key1.setIV(iv) 864 | self.__key2.setIV(iv) 865 | self.__key3.setIV(iv) 866 | result.append(block) 867 | i += 8 868 | if _pythonMajorVersion < 3: 869 | data = ''.join(result) 870 | else: 871 | data = bytes.fromhex('').join(result) 872 | else: 873 | data = self.__key3.crypt(data, DECRYPT) 874 | data = self.__key2.crypt(data, ENCRYPT) 875 | data = self.__key1.crypt(data, DECRYPT) 876 | return self._unpadData(data, pad, padmode) 877 | --------------------------------------------------------------------------------