Compare commits

..

No commits in common. "main" and "v0.15.6" have entirely different histories.

561 changed files with 32617 additions and 102526 deletions

View File

@ -5,4 +5,3 @@ ignore:
- "sm2/gen_p256_table.go"
- "sm4/sm4ni_gcm_asm.go"
- "sm4/cipher_ni.go"
- "docs"

View File

@ -9,13 +9,3 @@ updates:
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
- package-ecosystem: github-actions
directory: /
schedule:
interval: daily
- package-ecosystem: docker
directory: /internal/sm2ec/fiat
schedule:
interval: daily

View File

@ -6,69 +6,42 @@ on:
pull_request:
branches: [ main ]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
goVer: ['1.23', '1.24']
goVer: ['1.16', '1.17', '1.18']
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
- name: Checkout Repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.goVer }}
- name: Test with Coverage
if: ${{ matrix.goVer == '1.24' }}
run: go test -coverpkg=./... -v -short -race -coverprofile=coverage1.txt -covermode=atomic ./...
env:
GODEBUG: x509sha1=1
- name: Test Generic with Coverage
if: ${{ matrix.goVer == '1.24' }}
run: go test -coverpkg=./... -v -short -tags purego -coverprofile=coverage2.txt -covermode=atomic ./...
- name: Setup Environment
run: |
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
- name: Module cache
uses: actions/cache@v3
env:
GODEBUG: x509sha1=1
cache-name: go-mod-cache
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/go.sum') }}
- name: Test with Coverage
run: go test -coverpkg=./... -v -short -coverprofile=coverage1.txt -covermode=atomic ./...
- name: Test Generic
run: go test -coverpkg=./... -v -short -tags purego -coverprofile=coverage2.txt -covermode=atomic ./...
- name: Upload coverage to Codecov
if: ${{ matrix.goVer == '1.24' }}
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
uses: codecov/codecov-action@v3
with:
files: ./coverage1.txt,./coverage2.txt
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
- name: Test Force SM4 Single Block with AES-NI
run: go test -short ./sm4/...
env:
FORCE_SM4BLOCK_AESNI: 1
- name: Test only
if: ${{ matrix.goVer != '1.24' }}
run: go test -short ./...
env:
GODEBUG: x509sha1=1
- name: Test Generic only
if: ${{ matrix.goVer != '1.24' }}
run: go test -short -tags purego ./...
env:
GODEBUG: x509sha1=1
- name: Test Plugin only
if: ${{ matrix.goVer == '1.24' }}
run: go test -short -tags plugin ./...
env:
GODEBUG: x509sha1=1
files: ./coverage1.txt,./coverage2.txt

View File

@ -1,5 +1,5 @@
name: "CodeQL"
name: "CodeQL Analysis"
on:
workflow_dispatch:
@ -15,34 +15,22 @@ on:
# * * * * *
- cron: '30 1 * * *'
permissions:
contents: read
jobs:
CodeQL-Build:
permissions:
actions: read # for github/codeql-action/init to get workflow details
contents: read # for actions/checkout to fetch code
security-events: write # for github/codeql-action/autobuild to send a status report
runs-on: ubuntu-latest
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
uses: github/codeql-action/init@v2
with:
languages: go
- name: Autobuild
uses: github/codeql-action/autobuild@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
uses: github/codeql-action/analyze@v2

View File

@ -1,29 +0,0 @@
name: Update License File
on:
push:
branches: [ main ]
paths:
- 'go.mod'
- 'go.sum'
permissions:
contents: read
jobs:
update-licenses:
runs-on: ubuntu-latest
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with:
go-version: '1.23'
- name: Install go-licenses
run: go install github.com/google/go-licenses@latest
- name: Generate license files
run: |
go-licenses report github.com/emmansun/gmsm > third-party-licenses.md

View File

@ -6,31 +6,20 @@ on:
pull_request:
branches: [ "main" ]
permissions:
contents: read
jobs:
build:
runs-on: macos-latest
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
uses: actions/setup-go@v3
with:
go-version: 1.23
go-version: 1.15
- name: Build
run: go build -v ./...
- name: Test
run: go test -v ./...
env:
GODEBUG: x509sha1=1
GOARCH: ${{ matrix.arch }}

View File

@ -1,83 +0,0 @@
# This workflow uses actions that are not certified by GitHub. They are provided
# by a third-party and are governed by separate terms of service, privacy
# policy, and support documentation.
name: Scorecard supply-chain security
on:
# For Branch-Protection check. Only the default branch is supported. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
branch_protection_rule:
# To guarantee Maintained check is occasionally updated. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
schedule:
- cron: '32 2 * * 2'
push:
branches: [ "main" ]
# Declare default permissions as read only.
permissions: read-all
jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest
# `publish_results: true` only works when run from the default branch. conditional can be removed if disabled.
if: github.event.repository.default_branch == github.ref_name || github.event_name == 'pull_request'
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
# Needed to publish results and get a badge (see publish_results below).
id-token: write
# Uncomment the permissions below if installing in a private repository.
# contents: read
# actions: read
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
- name: "Checkout code"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
with:
results_file: results.sarif
results_format: sarif
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
# - you want to enable the Branch-Protection check on a *public* repository, or
# - you are installing Scorecard on a *private* repository
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional.
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
# Public repositories:
# - Publish results to OpenSSF REST API for easy access by consumers
# - Allows the repository to include the Scorecard badge.
# - See https://github.com/ossf/scorecard-action#publishing-results.
# For private repositories:
# - `publish_results` will always be set to `false`, regardless
# of the value entered here.
publish_results: true
# (Optional) Uncomment file_mode if you have a .gitattributes with files marked export-ignore
# file_mode: git
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: SARIF file
path: results.sarif
retention-days: 5
# Upload the results to GitHub's code scanning dashboard (optional).
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
with:
sarif_file: results.sarif

View File

@ -1,51 +0,0 @@
# This workflow will build a golang project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go
name: ppc64le-qemu
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
permissions:
contents: read
jobs:
test:
strategy:
matrix:
go-version: [1.23.x]
arch: [ppc64le]
ppc64: [power8]
runs-on: ubuntu-latest
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
- name: Set up Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with:
go-version: ${{ matrix.go-version }}
- name: Set up QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- name: Check out code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Test internal
run: go test -v ./internal/...
env:
GOARCH: ${{ matrix.arch }}
GOPPC64: ${{ matrix.ppc64 }}
- name: Test Cipher
run: go test -v -short ./cipher/...
env:
GOARCH: ${{ matrix.arch }}
GOPPC64: ${{ matrix.ppc64 }}

View File

@ -1,53 +0,0 @@
# This workflow will build a golang project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go
name: arm64-qemu
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
permissions:
contents: read
jobs:
test:
strategy:
matrix:
go-version: [1.23.x]
arch: [arm64]
runs-on: ubuntu-latest
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
- name: Set up Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with:
go-version: ${{ matrix.go-version }}
- name: Set up QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- name: Check out code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Test
run: go test -v -short ./...
env:
DISABLE_SM3NI: 1
DISABLE_SM4NI: 1
GODEBUG: x509sha1=1
GOARCH: ${{ matrix.arch }}
- name: Test Force SM4 Single Block with AES-NI
run: go test -v -short ./sm4/...
env:
DISABLE_SM4NI: 1
FORCE_SM4BLOCK_AESNI: 1
GOARCH: ${{ matrix.arch }}

View File

@ -1,44 +0,0 @@
# This workflow will build a golang project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go
name: riscv64-qemu
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
permissions:
contents: read
jobs:
test:
strategy:
matrix:
go-version: [1.23.x]
arch: [riscv64]
runs-on: ubuntu-latest
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
- name: Set up Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with:
go-version: ${{ matrix.go-version }}
- name: Set up QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- name: Check out code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Test
run: go test -v -short ./internal/...
env:
GODEBUG: x509sha1=1
GOARCH: ${{ matrix.arch }}

View File

@ -1,57 +0,0 @@
# This workflow will build a golang project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go
name: s390x-qemu
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
permissions:
contents: read
jobs:
test:
strategy:
matrix:
go-version: [1.23.x]
arch: [s390x]
runs-on: ubuntu-latest
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
- name: Set up Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with:
go-version: ${{ matrix.go-version }}
- name: Set up QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- name: Check out code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Test internal
run: go test -v ./internal/...
env:
GOARCH: ${{ matrix.arch }}
- name: Test Cipher
run: go test -v -short ./cipher/...
env:
GOARCH: ${{ matrix.arch }}
# - name: Test
# run: go test -v -short ./...
# env:
# GODEBUG: x509sha1=1
# GOARCH: ${{ matrix.arch }}

View File

@ -1,44 +0,0 @@
# This workflow will build a golang project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go
name: sm3-sm4-ni-qemu
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
permissions:
contents: read
jobs:
test:
strategy:
matrix:
go-version: [1.23.x]
arch: [arm64]
runs-on: ubuntu-latest
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
- name: Set up Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with:
go-version: ${{ matrix.go-version }}
- name: Set up QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- name: Check out code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Test
run: go test -v -short ./...
env:
GODEBUG: x509sha1=1
GOARCH: ${{ matrix.arch }}

20
.travis.yml Normal file
View File

@ -0,0 +1,20 @@
language: go
jobs:
include:
- arch: arm64-graviton2
virt: vm
os: linux
dist: focal
go: 1.16.x
group: edge
install:
- go mod tidy
- go mod download
script:
- go test -v -short ./...
#after_success:
# - go test -v -short -bench . -run=^$ ./...

View File

@ -1,8 +0,0 @@
cff-version: 1.2.0
message: "If you use this software, please cite it as below."
authors:
- family-names: "Sun"
given-names: "Yimin"
title: "gmsm"
date-released: 2021-1-20
url: "https://github.com/emmansun/gmsm"

View File

@ -1,27 +1,23 @@
# 免责
# 免责
## 版权声明
本项目(gmsm)代码的版权归[Sun Yimin](mailto:emman.sun@foxmail.com)所有,并按照[MIT](https://github.com/emmansun/gmsm/blob/main/LICENSE)进行分发。使用本项目的代码时,必须遵守相应的许可证条款。
## 声明
## 免责条款
任何直接或间接因使用本项目(gmsm)的任何内容所导致的全部后果与本项目(gmsm)的开发者无关,若使用者无法对使用本项目(gmsm)后的任何后果负责,请不要使用本项目(gmsm)的任何内容。若该项目(gmsm)的任何内容侵犯了您的法律权益,请联系 emman.sun@foxmail.com作者会第一时间删除侵权内容。
1. **无担保声明**
本项目以“原样”提供,不提供任何形式的明示或暗示担保,包括但不限于对适销性、特定用途适用性和非侵权性的担保。
## 认证
2. **责任限制**
对于因使用或无法使用本项目代码而导致的任何直接、间接、偶然、特殊、典型或惩罚性损害(包括但不限于数据丢失、利润损失、业务中断等),项目作者或贡献者概不负责。
本项目(gmsm)**没有**经过相关机构资质认证。
3. **使用风险**
使用本项目代码的风险由使用者自行承担。使用者在使用本项目时,应自行评估代码的适用性和安全性,并根据实际需求进行测试和调整。
## 保证
4. **第三方依赖**
本项目可能依赖于第三方库或服务,这些库或服务的使用应遵守其各自的许可证条款。项目作者或贡献者不对第三方库或服务的稳定性、安全性或合法性负责。
本项目(gmsm)不提供任何形式的保证。所有与使用本项目(gmsm)软件库及源代码相关的直接或间接风险均由用户自行承担。本项目(gmsm)提供的所有代码,并不对性能、适用性、适销性及其他方面提供任何保证。
5. **法律合规**
使用本项目代码时,使用者应确保遵守所在国家或地区的法律法规。如因使用本项目代码而违反相关法律,使用者需自行承担法律责任。
本项目(gmsm)实现的内容可能包含不准确性或错误,我们不对内容的准确性进行保证。如果您发现本项目(gmsm)内容包含错误,请直接提交错误至[issue tracker](https://github.com/emmansun/gmsm/issues),或者提交代码至[pull requests](https://github.com/emmansun/gmsm/pulls)。
6. **产品认证**
本项目(gmsm)**没有**经过相关机构(包括但不限于**国家密码管理局商用密码检测中心**)认证,也不承诺会进行相关认证工作。
## 您的行为
7. **修改与分发**
如果您修改本项目代码并重新分发,请确保保留上述版权声明和免责条款,并明确标注您的修改内容。
当您以任何方式使用本项目(gmsm)软件库及源代码时,说明您已经同意并接受本页面的所有信息。
## 联系方式
联系邮箱emman.sun@foxmail.com相关事务请发函至该邮箱

View File

@ -1,73 +0,0 @@
# GM-Standards SM2/SM3/SM4/SM9/ZUC for Go
[![Github CI](https://github.com/emmansun/gmsm/actions/workflows/ci.yml/badge.svg)](https://github.com/emmansun/gmsm/actions/workflows/ci.yml)
[![arm64-qemu](https://github.com/emmansun/gmsm/actions/workflows/test_qemu.yml/badge.svg)](https://github.com/emmansun/gmsm/actions/workflows/test_qemu.yml)
[![sm3-sm4-ni-qemu](https://github.com/emmansun/gmsm/actions/workflows/test_sm_ni.yml/badge.svg)](https://github.com/emmansun/gmsm/actions/workflows/test_sm_ni.yml)
[![codecov](https://codecov.io/gh/emmansun/gmsm/branch/main/graph/badge.svg?token=Otdi8m8sFj)](https://codecov.io/gh/emmansun/gmsm)
[![Go Report Card](https://goreportcard.com/badge/github.com/emmansun/gmsm)](https://goreportcard.com/report/github.com/emmansun/gmsm)
[![Documentation](https://godoc.org/github.com/emmansun/gmsm?status.svg)](https://godoc.org/github.com/emmansun/gmsm)
![GitHub go.mod Go version (branch)](https://img.shields.io/github/go-mod/go-version/emmansun/gmsm)
[![Release](https://img.shields.io/github/release/emmansun/gmsm/all.svg)](https://github.com/emmansun/gmsm/releases)
English | [简体中文](README.md)
ShangMi (SM) cipher suites for Golang, referred to as **GMSM**, is a secure, high-performance, easy-to-use Golang ShangMi (SM) cipher suites library, covering public algorithms SM2/SM3/SM4/SM9/ZUC.
## Packages
- **SM2** - This is a SM2 sm2p256v1 implementation whose performance is similar like golang native NIST P256 under **amd64**, **arm64**, **s390x** and **ppc64le**, for implementation detail, please refer [SM2实现细节](https://github.com/emmansun/gmsm/wiki/SM2%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96). It supports ShangMi sm2 digital signature, public key encryption algorithm and also key exchange.
- **SM3** - This is also a SM3 implementation whose performance is similar like golang native SHA 256 with SIMD under **amd64**, **arm64**, **s390x**, **ppc64x**, for implementation detail, please refer [SM3性能优化](https://github.com/emmansun/gmsm/wiki/SM3%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96). It also provides A64 cryptographic instructions SM3 tested with QEMU.
- **SM4** - For SM4 implementation, SIMD & AES-NI are used under **amd64**, **arm64** and **ppc64x**, for detail please refer [SM4性能优化](https://github.com/emmansun/gmsm/wiki/SM4%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96). It is optimized for **ECB/CBC/GCM/XTS** operation modes. It also provides A64 cryptographic instructions SM4 tested with QEMU.
- **SM9** - For SM9 implementation, please reference [SM9实现及优化](https://github.com/emmansun/gmsm/wiki/SM9%E5%AE%9E%E7%8E%B0%E5%8F%8A%E4%BC%98%E5%8C%96)
- **ZUC** - For ZUC implementation, SIMD, AES-NI and CLMUL are used under **amd64**, **arm64** and **ppc64x**, for detail please refer [Efficient Software Implementations of ZUC](https://github.com/emmansun/gmsm/wiki/Efficient-Software-Implementations-of-ZUC)
- **CBCMAC** - CBC-MAC and its variants (EMAC/ANSI retail MAC/MacDES/CMAC/LMAC/TrCBC/CBCR).
- **CFCA** - some cfca specific implementations.
- **CIPHER** - ECB/CCM/XTS/HCTR/BC/OFBNLF operation modes, XTS mode also supports **GB/T 17964-2021**. Current XTS mode implementation is **NOT** concurrent safe! **BC** and **OFBNLF** are legacy operation modes, **HCTR** is new operation mode in **GB/T 17964-2021**. **BC** operation mode is similar like **CBC**, there is no room for performance optimization in **OFBNLF** operation mode.
- **SMX509** - a fork of golang X509 that supports ShangMi.
- **PKCS7** - a fork of [mozilla-services/pkcs7](https://github.com/mozilla-services/pkcs7) that supports ShangMi.
- **PKCS8** - a fork of [youmark/pkcs8](https://github.com/youmark/pkcs8) that supports ShangMi.
- **ECDH** - a similar implementation of golang ECDH that supports SM2 ECDH & SM2MQV without usage of **big.Int**, a replacement of SM2 key exchange. For detail, pleaes refer [is my code constant time?](https://github.com/emmansun/gmsm/wiki/is-my-code-constant-time%3F)
- **DRBG** - Random Number Generation Using Deterministic Random Bit Generators, for detail, please reference **NIST Special Publication 800-90A** and **GM/T 0105-2021**: CTR-DRBG using derivation function and HASH-DRBG. NIST related implementations are tested with part of NIST provided test vectors. It's **NOT** concurrent safe! You can also use [randomness](https://github.com/Trisia/randomness) tool to check the generated random bits.
- **MLDSA** - NIST FIPS 204 Module-Lattice-Based Digital Signature Standard.
- **SLHDSA** - NIST FIPS 205 Stateless Hash-Based Digital Signature Standard
## Some Related Projects
- **[TLCP](https://github.com/Trisia/gotlcp)** - An implementation of **GB/T 38636-2020 Information security technology Transport Layer Cryptography Protocol (TLCP)**.
- **[Trisia/Randomness](https://github.com/Trisia/randomness)** - An implementation of **GM/T 0005-2021 Randomness test specification**.
- **[PKCS12](https://github.com/emmansun/go-pkcs12)** - pkcs12 supports ShangMi, a fork of [SSLMate/go-pkcs12](https://github.com/SSLMate/go-pkcs12).
- **[MKSMCERT](https://github.com/emmansun/mksmcert)** - A simple tool for making locally-trusted development ShangMi certificates, a fork of [FiloSottile/mkcert](https://github.com/FiloSottile/mkcert).
## License
This work is licensed under a MIT License. See the [LICENSE](./LICENSE) file for details.
## Acknowledgements
The basic architecture, design and some codes are from [golang crypto](https://github.com/golang/go/commits/master/src/crypto).
The SM4 amd64 SIMD AES-NI implementation is inspired by code from [mjosaarinen/sm4ni](https://github.com/mjosaarinen/sm4ni).
The original SM9/BN256 version is based on code from [cloudflare/bn256](https://github.com/cloudflare/bn256).
The ZUC amd64 SIMD AES-NI, CLMUL implementation is inspired by code from [Intel(R) Multi-Buffer Crypto for IPsec Library](https://github.com/intel/intel-ipsec-mb/).
The pkcs7 is based on code from [mozilla-services/pkcs7](https://github.com/mozilla-services/pkcs7), which has been archived by the owner on Feb 10, 2024.
The pkcs8 is based on code from [youmark/pkcs8](https://github.com/youmark/pkcs8).
## Disclaimer
This library is not fully audited and is offered as-is, and without a guarantee. Therefore, it is expected that changes in the code, repository, and API occur in the future. We recommend to take caution before using this library in a production application.
## Stargazers over time
[![Stargazers over time](https://starchart.cc/emmansun/gmsm.svg?variant=adaptive)](https://starchart.cc/emmansun/gmsm)

131
README.md
View File

@ -1,90 +1,41 @@
# Go语言商用密码软件
[![Github CI](https://github.com/emmansun/gmsm/actions/workflows/ci.yml/badge.svg)](https://github.com/emmansun/gmsm/actions/workflows/ci.yml)
[![arm64-qemu](https://github.com/emmansun/gmsm/actions/workflows/test_qemu.yml/badge.svg)](https://github.com/emmansun/gmsm/actions/workflows/test_qemu.yml)
[![sm3-sm4-ni-qemu](https://github.com/emmansun/gmsm/actions/workflows/test_sm_ni.yml/badge.svg)](https://github.com/emmansun/gmsm/actions/workflows/test_sm_ni.yml)
[![codecov](https://codecov.io/gh/emmansun/gmsm/branch/main/graph/badge.svg?token=Otdi8m8sFj)](https://codecov.io/gh/emmansun/gmsm)
[![Go Report Card](https://goreportcard.com/badge/github.com/emmansun/gmsm)](https://goreportcard.com/report/github.com/emmansun/gmsm)
[![Documentation](https://godoc.org/github.com/emmansun/gmsm?status.svg)](https://godoc.org/github.com/emmansun/gmsm)
![GitHub go.mod Go version (branch)](https://img.shields.io/github/go-mod/go-version/emmansun/gmsm)
[![Release](https://img.shields.io/github/release/emmansun/gmsm/all.svg)](https://github.com/emmansun/gmsm/releases)
[English](README-EN.md) | 简体中文
Go语言商用密码软件简称**GMSM**一个安全、高性能、易于使用的Go语言商用密码软件库涵盖商用密码公开算法SM2/SM3/SM4/SM9/ZUC。
## 用户文档
- [SM2椭圆曲线公钥密码算法应用指南](./docs/sm2.md)
- [SM3密码杂凑算法应用指南](./docs/sm3.md)
- [SM4分组密码算法应用指南](./docs/sm4.md)
- [SM9标识密码算法应用指南](./docs/sm9.md)
- [ZUC祖冲之序列密码算法应用指南](./docs/zuc.md)
- [CFCA互操作性指南](./docs/cfca.md)
- [PKCS7应用指南](./docs/pkcs7.md)
- [PKCS12应用指南](./docs/pkcs12.md)
如果你想提问题,建议你阅读[提问的智慧](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md)。
## 包结构
- **SM2** - SM2椭圆曲线公钥密码算法曲线的具体实现位于[internal/sm2ec](https://github.com/emmansun/gmsm/tree/main/internal/sm2ec) package中。SM2曲线实现性能和Golang标准库中的NIST P256椭圆曲线原生实现非BoringCrypto类似也对**amd64****arm64****s390x**和**ppc64le**架构做了专门汇编优化实现,您也可以参考[SM2实现细节](https://github.com/emmansun/gmsm/wiki/SM2%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96)及相关Wiki和代码以获得更多实现细节。SM2包实现了SM2椭圆曲线公钥密码算法的数字签名算法、公钥加密算法、密钥交换算法以及《GB/T 35276-2017信息安全技术 SM2密码算法使用规范》中的密钥对保护数据格式。
- **SM3** - SM3密码杂凑算法实现。**amd64**下分别针对**AVX2+BMI2、AVX、SSE2+SSSE3**做了消息扩展部分的SIMD实现 **arm64**下使用NEON指令做了消息扩展部分的SIMD实现同时也提供了基于**A64扩展密码指令**的汇编实现;**s390x**和**ppc64x**通过向量指令做了消息扩展部分的优化实现。您也可以参考[SM3性能优化](https://github.com/emmansun/gmsm/wiki/SM3%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96)及相关Wiki和代码以获得更多实现细节。
- **SM4** - SM4分组密码算法实现。**amd64**下使用**AES**指令加上**AVX2、AVX、SSE2+SSSE3**实现了比较好的性能。**arm64**下使用**AES**指令加上NEON指令实现了比较好的性能同时也提供了基于**A64扩展密码指令**的汇编实现。**ppc64x**下使用**vsbox**指令加上向量指令进行了并行优化。针对**ECB/CBC/GCM/XTS**加密模式做了和SM4分组密码算法的融合汇编优化实现。您也可以参考[SM4性能优化](https://github.com/emmansun/gmsm/wiki/SM4%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96)及相关Wiki和代码以获得更多实现细节。
- **SM9** - SM9标识密码算法实现。基础的素域、扩域、椭圆曲线运算以及双线性对运算位于[bn256](https://github.com/emmansun/gmsm/tree/main/sm9/bn256)包中,分别对**amd64**、**arm64**、**ppc64x**架构做了优化实现。您也可以参考[SM9实现及优化](https://github.com/emmansun/gmsm/wiki/SM9%E5%AE%9E%E7%8E%B0%E5%8F%8A%E4%BC%98%E5%8C%96)及相关讨论和代码以获得更多实现细节。SM9包实现了SM9标识密码算法的密钥生成、数字签名算法、密钥封装机制和公钥加密算法、密钥交换协议。
- **ZUC** - 祖冲之序列密码算法实现。使用SIMD、AES指令以及无进位乘法指令分别对**amd64**、**arm64**和**ppc64x**架构做了优化实现, 您也可以参考[ZUC实现及优化](https://github.com/emmansun/gmsm/wiki/Efficient-Software-Implementations-of-ZUC)和相关代码以获得更多实现细节。ZUC包实现了基于祖冲之序列密码算法的机密性算法、128/256位完整性算法。
- **CBCMAC** - 符合《GB/T 15852.1-2020 采用分组密码的机制》的消息鉴别码。
- **CFCA** - CFCA中金特定实现目前实现的是SM2私钥、证书封装处理对应SADK中的**PKCS12_SM2**信封加密、签名CSR生成及返回私钥解密、解析等功能。
- **CIPHER** - ECB/CCM/XTS/HCTR/BC/OFBNLF加密模式实现。XTS模式同时支持NIST规范和国标 **GB/T 17964-2021**。当前的XTS模式由于实现了BlockMode其结构包含一个tweak数组所以其**不支持并发使用**。**分组链接BC模式**和**带非线性函数的输出反馈OFBNLF模式**为分组密码算法的工作模式标准**GB/T 17964**的遗留模式,**带泛杂凑函数的计数器HCTR模式**是**GB/T 17964-2021**中的新增模式。分组链接BC模式和CBC模式类似而带非线性函数的输出反馈OFBNLF模式的话从软件实现的角度来看基本没有性能优化的空间。
- **SMX509** - Go语言X509包的分支加入了商用密码支持。
- **PADDING** - 一些填充方法实现(非常量时间运行):**pkcs7**,这是当前主要使用的填充方式,对应**GB/T 17964-2021**的附录C.2 填充方法 1**iso9797m2**,对应**GB/T 17964-2021**的附录C.3 填充方法 2**ansix923**对应ANSI X9.23标准。**GB/T 17964-2021**的附录C.4 填充方法 3对应ISO/IEC_9797-1 padding method 3。
- **PKCS7** - [mozilla-services/pkcs7](https://github.com/mozilla-services/pkcs7) 项目该项目已于2024年2月10日被归档的分支加入了商用密码支持。
- **PKCS8** - [youmark/pkcs8](https://github.com/youmark/pkcs8)项目的分支,加入了商用密码支持。
- **ECDH** - 一个类似Go语言中ECDH包的实现支持SM2椭圆曲线密码算法的ECDH & SM2MQV协议该实现没有使用 **big.Int**也是一个SM2包中密钥交换协议实现的替换实现推荐使用
- **DRBG** - 《GM/T 0105-2021软件随机数发生器设计指南》实现。本实现同时支持**NIST Special Publication 800-90A**(部分) 和 **GM/T 0105-2021**NIST相关实现使用了NIST提供的测试数据进行测试。本实现**不支持并发使用**。
- **MLDSA** - NIST FIPS 204 Module-Lattice-Based Digital Signature Standard实现。
- **SLHDSA** - NIST FIPS 205 Stateless Hash-Based Digital Signature Standard实现。
## 相关项目
- **[Trisia/TLCP](https://github.com/Trisia/gotlcp)** - 一个《GB/T 38636-2020 信息安全技术 传输层密码协议》Go语言实现项目。
- **[Trisia/Randomness](https://github.com/Trisia/randomness)** - 一个Go语言随机性检测规范实现。
- **[PKCS12](https://github.com/emmansun/go-pkcs12)** - [SSLMate/go-pkcs12](https://github.com/SSLMate/go-pkcs12)项目的一个分支加入了商用密码支持由于PKCS12标准比较老安全性不高所以以独立项目进行维护。
- **[MKSMCERT](https://github.com/emmansun/mksmcert)** - 一个用于生成SM2私钥和证书的工具主要用于开发测试它是[FiloSottile/mkcert](https://github.com/FiloSottile/mkcert)项目的一个分支,加入了商用密码支持。
- **JavaScript实现**
- [jsrsasign-sm](https://github.com/emmansun/sm2js) 扩展[jsrsasign](https://github.com/kjur/jsrsasign)实现的优势在于充分利用jsrsasign的PKIXCSRCERTPKCS8等处理能力。
- [sjcl-sm](https://github.com/emmansun/sm4js) 扩展[sjcl](https://github.com/bitwiseshiftleft/sjcl)实现的优势在于其丰富的对称加密模式实现,以及其简洁的代码、较好的性能。
## 软件许可
本软件使用MIT许可证详情请参考[软件许可](./LICENSE)。如果不熟悉MIT许可证条款请参考[MIT许可证](https://zh.wikipedia.org/zh-cn/MIT%E8%A8%B1%E5%8F%AF%E8%AD%89)。请知晓和遵守**被许可人义务**
## 致谢
本项目的基础架构、设计和部分代码源自[golang crypto](https://github.com/golang/go/commits/master/src/crypto).
SM4分组密码算法**amd64** SIMD AES-NI实现SSE部分的算法源自[mjosaarinen/sm4ni](https://github.com/mjosaarinen/sm4ni)。
SM9/BN256最初版本的代码复制自[cloudflare/bn256](https://github.com/cloudflare/bn256)项目,后期对基础的素域、扩域、椭圆曲线运算等进行了重写。
祖冲之序列密码算法实现**amd64** SIMD AES-NI, CLMUL实现算法源自[Intel(R) Multi-Buffer Crypto for IPsec Library](https://github.com/intel/intel-ipsec-mb/)项目。
PKCS7包代码是[mozilla-services/pkcs7](https://github.com/mozilla-services/pkcs7)项目该项目已于2024年2月10日被归档的一个分支加入了商用密码扩展。
PKCS8包代码是[youmark/pkcs8](https://github.com/youmark/pkcs8)项目的一个分支,加入了商用密码扩展。
## 免责声明
使用本项目前,请务必仔细阅读[GMSM软件免责声明](DISCLAIMER.md)
## 项目星标趋势
[![Stargazers over time](https://starchart.cc/emmansun/gmsm.svg?variant=adaptive)](https://starchart.cc/emmansun/gmsm)
# GM-Standards SM2/SM3/SM4/SM9/ZUC for Go
![Travis (.org)](https://img.shields.io/travis/emmansun/gmsm?label=arm64)
[![Github CI](https://github.com/emmansun/gmsm/actions/workflows/ci.yml/badge.svg)](https://github.com/emmansun/gmsm/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/emmansun/gmsm/branch/main/graph/badge.svg?token=Otdi8m8sFj)](https://codecov.io/gh/emmansun/gmsm)
[![Go Report Card](https://goreportcard.com/badge/github.com/emmansun/gmsm)](https://goreportcard.com/report/github.com/emmansun/gmsm)
[![Documentation](https://godoc.org/github.com/emmansun/gmsm?status.svg)](https://godoc.org/github.com/emmansun/gmsm)
![GitHub go.mod Go version (branch)](https://img.shields.io/github/go-mod/go-version/emmansun/gmsm)
[![Release](https://img.shields.io/github/release/emmansun/gmsm/all.svg)](https://github.com/emmansun/gmsm/releases)
## Packages
* **SM2** - This is a SM2 sm2p256v1 implementation whose performance is similar like golang native NIST P256 under **amd64** and **arm64**, for implementation detail, please refer [SM2实现细节](https://github.com/emmansun/gmsm/wiki/SM2%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96). It supports ShangMi sm2 digital signature, public key encryption algorithm and also key exchange.
* **SM3** - This is also a SM3 implementation whose performance is similar like golang native SHA 256 with SIMD under **amd64**, for implementation detail, please refer [SM3性能优化](https://github.com/emmansun/gmsm/wiki/SM3%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96). It also provides A64 cryptographic instructions SM3 POC without test.
* **SM4** - For SM4 implementation, SIMD & AES-NI are used under **amd64** and **arm64**, for detail please refer [SM4性能优化](https://github.com/emmansun/gmsm/wiki/SM4%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96), it supports CBC/CFB/OFB/CTR/GCM/CCM/XTS modes. It also provides A64 cryptographic instructions SM4 POC without test.
* **SM9** - For SM9 implementation, please reference [sm9/bn256 README.md](https://github.com/emmansun/gmsm/tree/main/sm9/bn256).
* **ZUC** - For ZUC implementation, SIMD, AES-NI and CLMUL are used under **amd64** and **arm64**, for detail please refer [Efficient Software Implementations of ZUC](https://github.com/emmansun/gmsm/wiki/Efficient-Software-Implementations-of-ZUC)
* **CIPHER** - CCM/XTS cipher modes.
* **SMX509** - a fork of golang X509 that supports ShangMi.
* **PKCS8** - a fork of [youmark/pkcs8](https://github.com/youmark/pkcs8) that supports ShangMi.
* **ECDH** - a similar implementation of golang ECDH that supports SM2 ECDH & SM2MQV without usage of **big.Int**, a replacement of SM2 key exchange. For detail, pleaes refer [is my code constant time?](https://github.com/emmansun/gmsm/wiki/is-my-code-constant-time%3F)
* **DRBG** - Random Number Generation Using Deterministic Random Bit Generators, for detail, please reference **NIST Special Publication 800-90A** and **GM/T 0105-2021**: CTR-DRBG using derivation function and HASH-DRBG. NIST related implementations are tested with part of NIST provided test vectors. You can also use [randomness](https://github.com/Trisia/randomness) tool to check the generated random bits.
## Some Related Projects
* **[PKCS12](https://github.com/emmansun/go-pkcs12)** - pkcs12 supports ShangMi, a fork of [SSLMate/go-pkcs12](https://github.com/SSLMate/go-pkcs12).
* **[PKCS7](https://github.com/emmansun/pkcs7)** - pkcs7 supports ShangMi (not supports SM9 yet), a fork of [mozilla-services/pkcs7](https://github.com/mozilla-services/pkcs7).
* **[TLCP](https://github.com/Trisia/gotlcp)** - An implementation of GB/T 38636-2020 Information security technology Transport Layer Cryptography Protocol (TLCP).
* **[MKSMCERT](https://github.com/emmansun/mksmcert)** - A simple tool for making locally-trusted development ShangMi certificates, a fork of [FiloSottile/mkcert](https://github.com/FiloSottile/mkcert).
## Disclaimer
Please read [disclaimer](DISCLAIMER.md) carefully!

View File

@ -1,7 +0,0 @@
# Security Policy
If you find a significant vulnerability, or evidence of one,
please report it privately.
We prefer that you use the [GitHub mechanism for privately reporting a vulnerability](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability#privately-reporting-a-security-vulnerability). Under the
[main repository's security tab](https://github.com/emmansun/gmsm/security), click "Report a vulnerability" to open the advisory form.

View File

@ -1,498 +0,0 @@
// Copyright 2024 Sun Yimin. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
// Package cbcmac implements the Message Authentication Code with the block chipher mechanisms.
package cbcmac
import (
"crypto/cipher"
"crypto/subtle"
"github.com/emmansun/gmsm/padding"
)
// Reference: GB/T 15821.1-2020 Security techniques
// Message authentication codes - Part 1: Mechanisms using block ciphers
// BlockCipherMAC is the interface that wraps the basic MAC method.
type BlockCipherMAC interface {
// Size returns the MAC value's number of bytes.
Size() int
// MAC calculates the MAC of the given data.
// The MAC value's number of bytes is returned by Size.
// Intercept message authentication code as needed.
MAC(src []byte) []byte
}
// cbcmac implements the basic CBC-MAC mode of operation for block ciphers.
type cbcmac struct {
b cipher.Block
pad padding.Padding
size int
}
// NewCBCMAC returns a CBC-MAC (GB/T 15821.1-2020 MAC scheme 1) instance that
// implements the MAC with the given block cipher. The padding scheme is ISO/IEC 9797-1 method 2.
func NewCBCMAC(b cipher.Block, size int) BlockCipherMAC {
return NewCBCMACWithPadding(b, size, padding.NewISO9797M2Padding)
}
// NewCBCMACWithPadding creates a new CBC-MAC (Cipher Block Chaining Message Authentication Code)
// with the specified block cipher, MAC size, and padding function. The MAC size must be greater
// than 0 and less than or equal to the block size of the cipher. If the size is invalid, the
// function will panic. The padding function is used to pad the input to the block size of the cipher.
func NewCBCMACWithPadding(b cipher.Block, size int, newPaddingFunc padding.NewPaddingFunc) BlockCipherMAC {
if size <= 0 || size > b.BlockSize() {
panic("cbcmac: invalid size")
}
return &cbcmac{b: b, pad: newPaddingFunc(uint(b.BlockSize())), size: size}
}
func (c *cbcmac) Size() int {
return c.size
}
// MAC calculates the MAC of the given data.
// The data is padded with the padding scheme of the block cipher before processing.
func (c *cbcmac) MAC(src []byte) []byte {
src = c.pad.Pad(src)
blockSize := c.b.BlockSize()
tag := make([]byte, blockSize)
for len(src) > 0 {
subtle.XORBytes(tag, tag, src[:blockSize])
c.b.Encrypt(tag, tag)
src = src[blockSize:]
}
return tag[:c.size]
}
// emac implements the EMAC mode of operation for block ciphers.
type emac struct {
pad padding.Padding
b1, b2 cipher.Block
size int
}
// NewEMAC returns an EMAC (GB/T 15821.1-2020 MAC scheme 2) instance that
// implements MAC with the given block cipher. The padding scheme is ISO/IEC 9797-1 method 2.
func NewEMAC(creator func(key []byte) (cipher.Block, error), key1, key2 []byte, size int) BlockCipherMAC {
return NewEMACWithPadding(creator, key1, key2, size, padding.NewISO9797M2Padding)
}
// NewEMACWithPadding creates a new instance of EMAC (Encrypted Message Authentication Code) with padding.
func NewEMACWithPadding(creator func(key []byte) (cipher.Block, error), key1, key2 []byte, size int, newPaddingFunc padding.NewPaddingFunc) BlockCipherMAC {
var b1, b2 cipher.Block
var err error
if b1, err = creator(key1); err != nil {
panic(err)
}
if size <= 0 || size > b1.BlockSize() {
panic("cbcmac: invalid size")
}
if b2, err = creator(key2); err != nil {
panic(err)
}
return &emac{pad: newPaddingFunc(uint(b1.BlockSize())), b1: b1, b2: b2, size: size}
}
func (e *emac) Size() int {
return e.size
}
func (e *emac) MAC(src []byte) []byte {
src = e.pad.Pad(src)
blockSize := e.b1.BlockSize()
tag := make([]byte, blockSize)
for len(src) > 0 {
subtle.XORBytes(tag, tag, src[:blockSize])
e.b1.Encrypt(tag, tag)
src = src[blockSize:]
}
e.b2.Encrypt(tag, tag)
return tag[:e.size]
}
type ansiRetailMAC emac
// NewANSIRetailMAC returns an ANSI Retail MAC (GB/T 15821.1-2020 MAC scheme 3) instance that
// implements MAC with the given block cipher. The padding scheme is ISO/IEC 9797-1 method 2.
func NewANSIRetailMAC(creator func(key []byte) (cipher.Block, error), key1, key2 []byte, size int) BlockCipherMAC {
return NewANSIRetailMACWithPadding(creator, key1, key2, size, padding.NewISO9797M2Padding)
}
// NewANSIRetailMACWithPadding creates a new ANSI Retail MAC with padding.
func NewANSIRetailMACWithPadding(creator func(key []byte) (cipher.Block, error), key1, key2 []byte, size int, newPaddingFunc padding.NewPaddingFunc) BlockCipherMAC {
return (*ansiRetailMAC)(NewEMACWithPadding(creator, key1, key2, size, newPaddingFunc).(*emac))
}
func (e *ansiRetailMAC) Size() int {
return e.size
}
func (e *ansiRetailMAC) MAC(src []byte) []byte {
src = e.pad.Pad(src)
blockSize := e.b1.BlockSize()
tag := make([]byte, blockSize)
for len(src) > 0 {
subtle.XORBytes(tag, tag, src[:blockSize])
e.b1.Encrypt(tag, tag)
src = src[blockSize:]
}
e.b2.Decrypt(tag, tag)
e.b1.Encrypt(tag, tag)
return tag[:e.size]
}
type macDES struct {
pad padding.Padding
b1, b2, b3 cipher.Block
size int
}
// NewMACDES returns a MAC-DES (GB/T 15821.1-2020 MAC scheme 4) instance that
// implements MAC with the given block cipher. The padding scheme is ISO/IEC 9797-1 method 2.
func NewMACDES(creator func(key []byte) (cipher.Block, error), key1, key2 []byte, size int) BlockCipherMAC {
return NewMACDESWithPadding(creator, key1, key2, size, padding.NewISO9797M2Padding)
}
// NewMACDESWithPadding creates a new BlockCipherMAC using DES encryption with padding.
func NewMACDESWithPadding(creator func(key []byte) (cipher.Block, error), key1, key2 []byte, size int, newPaddingFunc padding.NewPaddingFunc) BlockCipherMAC {
var b1, b2, b3 cipher.Block
var err error
if b1, err = creator(key1); err != nil {
panic(err)
}
if size <= 0 || size > b1.BlockSize() {
panic("cbcmac: invalid size")
}
if b2, err = creator(key2); err != nil {
panic(err)
}
key3 := make([]byte, len(key2))
copy(key3, key2)
for i := range key3 {
key3[i] ^= 0xF0
}
if b3, err = creator(key3); err != nil {
panic(err)
}
return &macDES{pad: newPaddingFunc(uint(b1.BlockSize())), b1: b1, b2: b2, b3: b3, size: size}
}
func (m *macDES) Size() int {
return m.size
}
func (m *macDES) MAC(src []byte) []byte {
src = m.pad.Pad(src)
blockSize := m.b1.BlockSize()
tag := make([]byte, blockSize)
copy(tag, src[:blockSize])
m.b1.Encrypt(tag, tag)
m.b3.Encrypt(tag, tag)
src = src[blockSize:]
for len(src) > 0 {
subtle.XORBytes(tag, tag, src[:blockSize])
m.b1.Encrypt(tag, tag)
src = src[blockSize:]
}
m.b2.Encrypt(tag, tag)
return tag[:m.size]
}
type cmac struct {
b cipher.Block
k1, k2 []byte
size int
blockSize int
tag []byte
x []byte
nx int
len uint64
}
// NewCMAC returns a CMAC (GB/T 15821.1-2020 MAC scheme 5) instance that implements MAC with the given block cipher.
//
// Reference: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38B.pdf
func NewCMAC(b cipher.Block, size int) *cmac {
if size <= 0 || size > b.BlockSize() {
panic("cbcmac: invalid size")
}
blockSize := b.BlockSize()
k1 := make([]byte, blockSize)
k2 := make([]byte, blockSize)
b.Encrypt(k1, k1)
msb := shiftLeft(k1)
k1[len(k1)-1] ^= msb * 0b10000111
copy(k2, k1)
msb = shiftLeft(k2)
k2[len(k2)-1] ^= msb * 0b10000111
d := &cmac{b: b, k1: k1, k2: k2, size: size}
d.blockSize = blockSize
d.tag = make([]byte, blockSize)
d.x = make([]byte, blockSize)
return d
}
func (c *cmac) Reset() {
for i := range c.tag {
c.tag[i] = 0
}
c.nx = 0
c.len = 0
}
func (c *cmac) BlockSize() int {
return c.blockSize
}
func (c *cmac) Size() int {
return c.size
}
func (d *cmac) Write(p []byte) (nn int, err error) {
nn = len(p)
if nn == 0 {
// nothing to do
return
}
d.len += uint64(nn)
if d.nx == d.blockSize {
// handle remaining full block
d.block(d.x)
d.nx = 0
} else if d.nx > 0 {
// handle remaining incomplete block
n := copy(d.x[d.nx:], p)
d.nx += n
p = p[n:]
if len(p) > 0 {
d.block(d.x)
d.nx = 0
}
}
lenP := len(p)
if lenP > d.blockSize {
n := lenP &^ (d.blockSize - 1)
if n == lenP {
n -= d.blockSize
}
d.block(p[:n])
p = p[n:]
}
// save remaining partial/full block
if len(p) > 0 {
d.nx = copy(d.x, p)
}
return
}
func (c *cmac) block(p []byte) {
for len(p) >= c.blockSize {
subtle.XORBytes(c.tag, p[:c.blockSize], c.tag)
c.b.Encrypt(c.tag, c.tag)
p = p[c.blockSize:]
}
}
// Sum appends the current hash to in and returns the resulting slice.
// It does not change the underlying hash state.
func (d *cmac) Sum(in []byte) []byte {
// Make a copy of d so that caller can keep writing and summing.
// shared block cipher and k1, k2, x
d0 := *d
// use slices.Clone() later
d0.tag = make([]byte, d.blockSize)
copy(d0.tag, d.tag)
hash := d0.checkSum()
return append(in, hash...)
}
func (c *cmac) checkSum() []byte {
tag := make([]byte, c.size)
switch c.nx {
case 0:
// Special-cased as a single empty partial final block.
copy(c.tag, c.k2)
c.tag[0] ^= 0b10000000
case c.blockSize:
subtle.XORBytes(c.tag, c.x, c.tag)
subtle.XORBytes(c.tag, c.k1, c.tag)
default:
subtle.XORBytes(c.tag, c.x, c.tag)
c.tag[c.nx] ^= 0b10000000
subtle.XORBytes(c.tag, c.k2, c.tag)
}
c.b.Encrypt(c.tag, c.tag)
copy(tag, c.tag[:c.size])
return tag
}
func (c *cmac) MAC(src []byte) []byte {
c.Reset()
c.Write(src)
return c.Sum(nil)
}
// shiftLeft sets x to x << 1, and returns MSB₁(x).
func shiftLeft(x []byte) byte {
var msb byte
for i := len(x) - 1; i >= 0; i-- {
msb, x[i] = x[i]>>7, x[i]<<1|msb
}
return msb
}
type lmac struct {
b1, b2 cipher.Block
pad padding.Padding
size int
}
// NewLMAC returns an LMAC (GB/T 15821.1-2020 MAC scheme 6) instance that
// implements MAC with the given block cipher. The padding scheme is ISO/IEC 9797-1 method 2.
func NewLMAC(creator func(key []byte) (cipher.Block, error), key []byte, size int) BlockCipherMAC {
return NewLMACWithPadding(creator, key, size, padding.NewISO9797M2Padding)
}
// NewLMACWithPadding creates a new LMAC (Length-based Message Authentication Code) with padding.
func NewLMACWithPadding(creator func(key []byte) (cipher.Block, error), key []byte, size int, newPaddingFunc padding.NewPaddingFunc) BlockCipherMAC {
var b, b1, b2 cipher.Block
var err error
if b, err = creator(key); err != nil {
panic(err)
}
if size <= 0 || size > b.BlockSize() {
panic("cbcmac: invalid size")
}
blockSize := b.BlockSize()
key1 := make([]byte, blockSize)
key1[blockSize-1] = 0x01
key2 := make([]byte, blockSize)
key2[blockSize-1] = 0x02
b.Encrypt(key1, key1)
b.Encrypt(key2, key2)
if b1, err = creator(key1); err != nil {
panic(err)
}
if b2, err = creator(key2); err != nil {
panic(err)
}
return &lmac{b1: b1, b2: b2, pad: newPaddingFunc(uint(blockSize)), size: size}
}
func (l *lmac) Size() int {
return l.b1.BlockSize()
}
func (l *lmac) MAC(src []byte) []byte {
src = l.pad.Pad(src)
blockSize := l.b1.BlockSize()
tag := make([]byte, blockSize)
for len(src) > blockSize {
subtle.XORBytes(tag, tag, src[:blockSize])
l.b1.Encrypt(tag, tag)
src = src[blockSize:]
}
subtle.XORBytes(tag, tag, src[:blockSize])
l.b2.Encrypt(tag, tag)
return tag
}
type trCBCMAC struct {
b cipher.Block
size int
}
// NewTRCBCMAC returns a TR-CBC-MAC (GB/T 15821.1-2020 MAC scheme 7) instance that
// implements MAC with the given block cipher.
//
// Reference: TrCBC: Another look at CBC-MAC.
func NewTRCBCMAC(b cipher.Block, size int) BlockCipherMAC {
if size <= 0 || size > b.BlockSize() {
panic("cbcmac: invalid size")
}
return &trCBCMAC{b: b, size: size}
}
func (t *trCBCMAC) Size() int {
return t.size
}
func (t *trCBCMAC) MAC(src []byte) []byte {
blockSize := t.b.BlockSize()
tag := make([]byte, blockSize)
padded := false
if len(src) == 0 || len(src)%blockSize != 0 {
pad := padding.NewISO9797M2Padding(uint(blockSize))
src = pad.Pad(src)
padded = true
}
for len(src) > 0 {
subtle.XORBytes(tag, tag, src[:blockSize])
t.b.Encrypt(tag, tag)
src = src[blockSize:]
}
if padded {
return tag[blockSize-t.size:]
}
return tag[:t.size]
}
type cbcrMAC struct {
b cipher.Block
size int
}
// NewCBCRMAC returns a CBCRMAC (GB/T 15821.1-2020 MAC scheme 8) instance that implements MAC with the given block cipher.
//
// Reference: CBCR: CBC MAC with rotating transformations.
func NewCBCRMAC(b cipher.Block, size int) BlockCipherMAC {
if size <= 0 || size > b.BlockSize() {
panic("cbcmac: invalid size")
}
return &cbcrMAC{b: b, size: size}
}
func (c *cbcrMAC) Size() int {
return c.size
}
func (c *cbcrMAC) MAC(src []byte) []byte {
blockSize := c.b.BlockSize()
tag := make([]byte, blockSize)
c.b.Encrypt(tag, tag)
padded := false
if len(src) == 0 || len(src)%blockSize != 0 {
pad := padding.NewISO9797M2Padding(uint(blockSize))
src = pad.Pad(src)
padded = true
}
for len(src) > blockSize {
subtle.XORBytes(tag, tag, src[:blockSize])
c.b.Encrypt(tag, tag)
src = src[blockSize:]
}
subtle.XORBytes(tag, tag, src[:blockSize])
if padded {
shiftLeft(tag)
} else {
shiftRight(tag)
}
c.b.Encrypt(tag, tag)
return tag[:c.size]
}
func shiftRight(x []byte) {
var lsb byte
for i := 0; i < len(x); i++ {
lsb, x[i] = x[i]<<7, x[i]>>1|lsb
}
x[0] ^= lsb
}

View File

@ -1,791 +0,0 @@
package cbcmac_test
import (
"bytes"
"crypto/aes"
"encoding/hex"
"hash"
"testing"
"github.com/emmansun/gmsm/cbcmac"
"github.com/emmansun/gmsm/internal/cryptotest"
"github.com/emmansun/gmsm/padding"
"github.com/emmansun/gmsm/sm4"
)
func TestCBCMAC(t *testing.T) {
// Test vectors from GB/T 15821.1-2020 Appendix B.
cases := []struct {
key []byte
src []byte
tag []byte
}{
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
nil,
[]byte{0x8c, 0x33, 0x8e, 0x5a, 0x27, 0xe3, 0x49, 0xbe, 0xae, 0x39, 0x21, 0x4f, 0xed, 0xa9, 0x70, 0x99},
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte("This is the test message for mac"),
[]byte{0x4b, 0x65, 0x53, 0xaf, 0x3c, 0x4e, 0x27, 0x44, 0x84, 0x12, 0x31, 0x5a, 0xc7, 0x84, 0x95, 0x35},
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte("This is the test message "),
[]byte{0x42, 0x1a, 0xd1, 0x69, 0x0a, 0xa1, 0x52, 0xe2, 0x84, 0x6f, 0xa2, 0xa5, 0xd8, 0x34, 0x45, 0xa9},
},
}
for i, c := range cases {
block, err := sm4.NewCipher(c.key)
if err != nil {
t.Errorf("#%d: failed to create cipher: %v", i, err)
}
mac := cbcmac.NewCBCMAC(block, len(c.tag))
tag := mac.MAC(c.src)
if !bytes.Equal(tag, c.tag) {
t.Errorf("#%d: expect tag %x, got %x", i, c.tag, tag)
}
}
}
func TestCBCMACWithPadding(t *testing.T) {
// Test vectors from GB/T 15821.1-2020 Appendix B.
cases := []struct {
key []byte
src []byte
tag []byte
newPaddingFunc padding.NewPaddingFunc
}{
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
nil,
[]byte{0x8c, 0x33, 0x8e, 0x5a, 0x27, 0xe3, 0x49, 0xbe, 0xae, 0x39, 0x21, 0x4f, 0xed, 0xa9, 0x70, 0x99},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte("This is the test message for mac"),
[]byte{0x4b, 0x65, 0x53, 0xaf, 0x3c, 0x4e, 0x27, 0x44, 0x84, 0x12, 0x31, 0x5a, 0xc7, 0x84, 0x95, 0x35},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte("This is the test message "),
[]byte{0x42, 0x1a, 0xd1, 0x69, 0x0a, 0xa1, 0x52, 0xe2, 0x84, 0x6f, 0xa2, 0xa5, 0xd8, 0x34, 0x45, 0xa9},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte("This is the test message for mac"),
[]byte{0x71, 0xaf, 0x7e, 0x45, 0x53, 0x40, 0x4c, 0xbc, 0xc4, 0xf2, 0x97, 0x3c, 0xdb, 0xd0, 0xf0, 0x63},
padding.NewISO9797M3Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte("This is the test message "),
[]byte{0x6a, 0x4a, 0x86, 0xf5, 0xb5, 0xe4, 0x68, 0xda, 0xd2, 0x7d, 0xf2, 0x5f, 0xb9, 0xd9, 0xbe, 0x16},
padding.NewISO9797M3Padding,
},
}
for i, c := range cases {
block, err := sm4.NewCipher(c.key)
if err != nil {
t.Errorf("#%d: failed to create cipher: %v", i, err)
}
mac := cbcmac.NewCBCMACWithPadding(block, len(c.tag), c.newPaddingFunc)
tag := mac.MAC(c.src)
if !bytes.Equal(tag, c.tag) {
t.Errorf("#%d: expect tag %x, got %x", i, c.tag, tag)
}
}
}
func TestEMACWithPadding(t *testing.T) {
// Test vectors from GB/T 15821.1-2020 Appendix B.
cases := []struct {
key1 []byte
key2 []byte
src []byte
tag []byte
newPaddingFunc padding.NewPaddingFunc
}{
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
nil,
[]byte{0x2c, 0xf6, 0xed, 0xf6, 0x3c, 0xce, 0x14, 0x44, 0x89, 0xea, 0xdd, 0xf0, 0x7b, 0x49, 0x38, 0xdb},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message for mac"),
[]byte{0xe4, 0x23, 0xe3, 0x55, 0x99, 0xaf, 0xd9, 0x48, 0xae, 0xc5, 0x0b, 0xde, 0xe8, 0x38, 0xe9, 0xea},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message "),
[]byte{0xf0, 0x26, 0x25, 0xce, 0xad, 0x00, 0x8d, 0x4e, 0xfb, 0xf3, 0xf0, 0xb2, 0xb0, 0xc2, 0xa7, 0x5b},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message for mac"),
[]byte{0x40, 0x03, 0xba, 0x1b, 0x6a, 0xdc, 0x53, 0xa8, 0x26, 0xe8, 0x2f, 0xce, 0xa1, 0x6a, 0xfa, 0xac},
padding.NewISO9797M3Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message "),
[]byte{0xff, 0xd5, 0xf1, 0xf2, 0xe5, 0xed, 0xa5, 0xcb, 0xf4, 0x02, 0xd6, 0x5a, 0x5b, 0x0b, 0x19, 0x53},
padding.NewISO9797M3Padding,
},
}
for i, c := range cases {
mac := cbcmac.NewEMACWithPadding(sm4.NewCipher, c.key1, c.key2, len(c.tag), c.newPaddingFunc)
tag := mac.MAC(c.src)
if !bytes.Equal(tag, c.tag) {
t.Errorf("#%d: expect tag %x, got %x", i, c.tag, tag)
}
}
}
func TestEMAC(t *testing.T) {
// Test vectors from GB/T 15821.1-2020 Appendix B.
cases := []struct {
key1 []byte
key2 []byte
src []byte
tag []byte
}{
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
nil,
[]byte{0x2c, 0xf6, 0xed, 0xf6, 0x3c, 0xce, 0x14, 0x44, 0x89, 0xea, 0xdd, 0xf0, 0x7b, 0x49, 0x38, 0xdb},
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message for mac"),
[]byte{0xe4, 0x23, 0xe3, 0x55, 0x99, 0xaf, 0xd9, 0x48, 0xae, 0xc5, 0x0b, 0xde, 0xe8, 0x38, 0xe9, 0xea},
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message "),
[]byte{0xf0, 0x26, 0x25, 0xce, 0xad, 0x00, 0x8d, 0x4e, 0xfb, 0xf3, 0xf0, 0xb2, 0xb0, 0xc2, 0xa7, 0x5b},
},
}
for i, c := range cases {
mac := cbcmac.NewEMAC(sm4.NewCipher, c.key1, c.key2, len(c.tag))
tag := mac.MAC(c.src)
if !bytes.Equal(tag, c.tag) {
t.Errorf("#%d: expect tag %x, got %x", i, c.tag, tag)
}
}
}
func TestANSIRetailMAC(t *testing.T) {
// Test vectors from GB/T 15821.1-2020 Appendix B.
cases := []struct {
key1 []byte
key2 []byte
src []byte
tag []byte
}{
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
nil,
[]byte{0xb4, 0x73, 0x6b, 0xe9, 0xa1, 0x74, 0xfa, 0xa3, 0x4d, 0xb1, 0xe9, 0xf1, 0xda, 0xcd, 0x5d, 0x62},
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message for mac"),
[]byte{0x51, 0xe9, 0x92, 0x8c, 0x22, 0x38, 0x33, 0x0c, 0x32, 0x31, 0xb8, 0x75, 0x2a, 0x9a, 0xfd, 0x7f},
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message "),
[]byte{0x19, 0x72, 0x47, 0x22, 0x9c, 0xe9, 0xd7, 0xb6, 0xae, 0x40, 0x5b, 0xf8, 0x85, 0xb2, 0x70, 0x57},
},
}
for i, c := range cases {
mac := cbcmac.NewANSIRetailMAC(sm4.NewCipher, c.key1, c.key2, len(c.tag))
tag := mac.MAC(c.src)
if !bytes.Equal(tag, c.tag) {
t.Errorf("#%d: expect tag %x, got %x", i, c.tag, tag)
}
}
}
func TestANSIRetailMACWithPadding(t *testing.T) {
// Test vectors from GB/T 15821.1-2020 Appendix B.
cases := []struct {
key1 []byte
key2 []byte
src []byte
tag []byte
newPaddingFunc padding.NewPaddingFunc
}{
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
nil,
[]byte{0xb4, 0x73, 0x6b, 0xe9, 0xa1, 0x74, 0xfa, 0xa3, 0x4d, 0xb1, 0xe9, 0xf1, 0xda, 0xcd, 0x5d, 0x62},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message for mac"),
[]byte{0x51, 0xe9, 0x92, 0x8c, 0x22, 0x38, 0x33, 0x0c, 0x32, 0x31, 0xb8, 0x75, 0x2a, 0x9a, 0xfd, 0x7f},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message "),
[]byte{0x19, 0x72, 0x47, 0x22, 0x9c, 0xe9, 0xd7, 0xb6, 0xae, 0x40, 0x5b, 0xf8, 0x85, 0xb2, 0x70, 0x57},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message for mac"),
[]byte{0x7c, 0xd4, 0x8c, 0x42, 0x42, 0xe4, 0x55, 0x75, 0xe5, 0x1a, 0xaf, 0x0d, 0xcc, 0x7a, 0x20, 0x8c},
padding.NewISO9797M3Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message "),
[]byte{0x3c, 0x43, 0x0f, 0x1e, 0xa4, 0x3b, 0x54, 0x0c, 0x68, 0x45, 0x7e, 0x24, 0x9c, 0x46, 0xf1, 0xdb},
padding.NewISO9797M3Padding,
},
}
for i, c := range cases {
mac := cbcmac.NewANSIRetailMACWithPadding(sm4.NewCipher, c.key1, c.key2, len(c.tag), c.newPaddingFunc)
tag := mac.MAC(c.src)
if !bytes.Equal(tag, c.tag) {
t.Errorf("#%d: expect tag %x, got %x", i, c.tag, tag)
}
}
}
func TestMACDES(t *testing.T) {
// Test vectors from GB/T 15821.1-2020 Appendix B.
cases := []struct {
key1 []byte
key2 []byte
src []byte
tag []byte
}{
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
nil,
[]byte{0x0c, 0x56, 0x00, 0x96, 0xb6, 0x09, 0xed, 0x0e, 0xaa, 0x39, 0xaf, 0xd6, 0xe2, 0x66, 0x65, 0x11},
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message for mac"),
[]byte{0x7e, 0x1a, 0x9a, 0x5e, 0x0e, 0xf0, 0x94, 0x7f, 0x25, 0xcb, 0x94, 0x85, 0x26, 0x1c, 0x98, 0x5c},
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message "),
[]byte{0x94, 0x94, 0x76, 0xd3, 0x5f, 0x17, 0x26, 0x1e, 0x1f, 0xb8, 0xc4, 0x39, 0x6d, 0x62, 0xdc, 0x05},
},
}
for i, c := range cases {
mac := cbcmac.NewMACDES(sm4.NewCipher, c.key1, c.key2, 16)
tag := mac.MAC(c.src)
if !bytes.Equal(tag, c.tag) {
t.Errorf("#%d: expect tag %x, got %x", i, c.tag, tag)
}
}
}
func TestMACDESWithPadding(t *testing.T) {
// Test vectors from GB/T 15821.1-2020 Appendix B.
cases := []struct {
key1 []byte
key2 []byte
src []byte
tag []byte
newPaddingFunc padding.NewPaddingFunc
}{
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
nil,
[]byte{0x0c, 0x56, 0x00, 0x96, 0xb6, 0x09, 0xed, 0x0e, 0xaa, 0x39, 0xaf, 0xd6, 0xe2, 0x66, 0x65, 0x11},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message for mac"),
[]byte{0x7e, 0x1a, 0x9a, 0x5e, 0x0e, 0xf0, 0x94, 0x7f, 0x25, 0xcb, 0x94, 0x85, 0x26, 0x1c, 0x98, 0x5c},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message "),
[]byte{0x94, 0x94, 0x76, 0xd3, 0x5f, 0x17, 0x26, 0x1e, 0x1f, 0xb8, 0xc4, 0x39, 0x6d, 0x62, 0xdc, 0x05},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message for mac"),
[]byte{0x28, 0xa7, 0x0d, 0x6b, 0xcc, 0xf7, 0x44, 0x22, 0x46, 0x20, 0x58, 0xab, 0xbc, 0x27, 0xf6, 0xae},
padding.NewISO9797M3Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message "),
[]byte{0xc9, 0xd3, 0x4e, 0x16, 0xc4, 0x9a, 0xb6, 0x43, 0x57, 0xa2, 0x61, 0x8d, 0xeb, 0xd1, 0x03, 0x2f},
padding.NewISO9797M3Padding,
},
}
for i, c := range cases {
mac := cbcmac.NewMACDESWithPadding(sm4.NewCipher, c.key1, c.key2, 16, c.newPaddingFunc)
tag := mac.MAC(c.src)
if !bytes.Equal(tag, c.tag) {
t.Errorf("#%d: expect tag %x, got %x", i, c.tag, tag)
}
}
}
func TestCMAC(t *testing.T) {
// Test vectors from GB/T 15821.1-2020 Appendix B.
cases := []struct {
key []byte
src []byte
tag []byte
}{
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
nil,
[]byte{0x29, 0xe1, 0x54, 0x32, 0x2e, 0x5c, 0x7b, 0xd8, 0xee, 0x6a, 0x25, 0xba, 0x54, 0x9b, 0x24, 0xbc},
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte("This is the test message for mac"),
[]byte{0x69, 0x2c, 0x43, 0x71, 0x00, 0xf3, 0xb5, 0xee, 0x2b, 0x8a, 0xbc, 0xef, 0x37, 0x3d, 0x99, 0x0c},
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte("This is the test message "),
[]byte{0x47, 0x38, 0xa6, 0xc7, 0x60, 0xb2, 0x80, 0xfc, 0x0c, 0x8a, 0x8a, 0xf3, 0x88, 0x6e, 0x9f, 0x5d},
},
}
for i, c := range cases {
block, err := sm4.NewCipher(c.key)
if err != nil {
t.Errorf("#%d: failed to create cipher: %v", i, err)
}
mac := cbcmac.NewCMAC(block, 16)
tag := mac.MAC(c.src)
if !bytes.Equal(tag, c.tag) {
t.Errorf("#%d: expect tag %x, got %x", i, c.tag, tag)
}
}
}
func TestLMAC(t *testing.T) {
// Test vectors from GB/T 15821.1-2020 Appendix B.
cases := []struct {
key []byte
src []byte
tag []byte
}{
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
nil,
[]byte{0xcd, 0x7e, 0xd2, 0x79, 0x64, 0xe2, 0x57, 0xc0, 0x77, 0xf0, 0x55, 0xf8, 0xee, 0x38, 0x3c, 0x3f},
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte("This is the test message for mac"),
[]byte{0xa0, 0xc4, 0x65, 0xee, 0x58, 0x96, 0x97, 0x2f, 0x83, 0x37, 0xaa, 0x1f, 0x92, 0xc9, 0x9d, 0x10},
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte("This is the test message "),
[]byte{0x60, 0xdd, 0x95, 0x5e, 0xd0, 0xca, 0x3d, 0x7a, 0x64, 0x22, 0x71, 0x74, 0xdd, 0x98, 0xdd, 0x81},
},
}
for i, c := range cases {
mac := cbcmac.NewLMAC(sm4.NewCipher, c.key, 16)
tag := mac.MAC(c.src)
if !bytes.Equal(tag, c.tag) {
t.Errorf("#%d: expect tag %x, got %x", i, c.tag, tag)
}
}
}
func TestLMACWithPadding(t *testing.T) {
// Test vectors from GB/T 15821.1-2020 Appendix B.
cases := []struct {
key []byte
src []byte
tag []byte
newPaddingFunc padding.NewPaddingFunc
}{
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
nil,
[]byte{0xcd, 0x7e, 0xd2, 0x79, 0x64, 0xe2, 0x57, 0xc0, 0x77, 0xf0, 0x55, 0xf8, 0xee, 0x38, 0x3c, 0x3f},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte("This is the test message for mac"),
[]byte{0xa0, 0xc4, 0x65, 0xee, 0x58, 0x96, 0x97, 0x2f, 0x83, 0x37, 0xaa, 0x1f, 0x92, 0xc9, 0x9d, 0x10},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte("This is the test message "),
[]byte{0x60, 0xdd, 0x95, 0x5e, 0xd0, 0xca, 0x3d, 0x7a, 0x64, 0x22, 0x71, 0x74, 0xdd, 0x98, 0xdd, 0x81},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte("This is the test message for mac"),
[]byte{0x43, 0x05, 0x0d, 0x51, 0xc6, 0x56, 0xae, 0x60, 0xbe, 0x27, 0x3f, 0xbe, 0xa4, 0x87, 0x0e, 0xf1},
padding.NewISO9797M3Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte("This is the test message "),
[]byte{0x61, 0xe0, 0x00, 0x49, 0xe2, 0x69, 0x62, 0xa3, 0x6f, 0xed, 0xba, 0x8d, 0x4f, 0x52, 0xf0, 0xad},
padding.NewISO9797M3Padding,
},
}
for i, c := range cases {
mac := cbcmac.NewLMACWithPadding(sm4.NewCipher, c.key, 16, c.newPaddingFunc)
tag := mac.MAC(c.src)
if !bytes.Equal(tag, c.tag) {
t.Errorf("#%d: expect tag %x, got %x", i, c.tag, tag)
}
}
}
func TestTRCBCMAC(t *testing.T) {
// Test vectors from GB/T 15821.1-2020 Appendix B.
cases := []struct {
key []byte
src []byte
tag []byte
}{
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
nil,
[]byte{0xae, 0x39, 0x21, 0x4f, 0xed, 0xa9, 0x70, 0x99},
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte("This is the test message for mac"),
[]byte{0x16, 0xe0, 0x29, 0x04, 0xef, 0xb7, 0x65, 0xb7},
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte("This is the test message "),
[]byte{0x84, 0x6f, 0xa2, 0xa5, 0xd8, 0x34, 0x45, 0xa9},
},
}
for i, c := range cases {
block, err := sm4.NewCipher(c.key)
if err != nil {
t.Errorf("#%d: failed to create cipher: %v", i, err)
}
mac := cbcmac.NewTRCBCMAC(block, len(c.tag))
tag := mac.MAC(c.src)
if !bytes.Equal(tag, c.tag) {
t.Errorf("#%d: expect tag %x, got %x", i, c.tag, tag)
}
}
}
func TestCBCRMAC(t *testing.T) {
// Test vectors from GB/T 15821.1-2020 Appendix B.
cases := []struct {
key []byte
src []byte
tag []byte
}{
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
nil,
[]byte{0x90, 0x9f, 0x5e, 0x6e, 0xd1, 0x55, 0x18, 0xc0, 0x12, 0x52, 0x30, 0x23, 0x83, 0xc6, 0x3e, 0x8c},
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte("This is the test message for mac"),
[]byte{0xe4, 0x0e, 0xd7, 0x9c, 0x31, 0x49, 0xa1, 0xc9, 0xd4, 0x2f, 0x04, 0xc4, 0x23, 0x04, 0x99, 0x35},
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte("This is the test message "),
[]byte{0xa9, 0x9d, 0x13, 0x01, 0x3e, 0x89, 0x2e, 0xe2, 0xc2, 0x5b, 0xe2, 0xda, 0xaa, 0x6c, 0x82, 0xe8},
},
}
for i, c := range cases {
block, err := sm4.NewCipher(c.key)
if err != nil {
t.Errorf("#%d: failed to create cipher: %v", i, err)
}
mac := cbcmac.NewCBCRMAC(block, 16)
tag := mac.MAC(c.src)
if !bytes.Equal(tag, c.tag) {
t.Errorf("#%d: expect tag %x, got %x", i, c.tag, tag)
}
}
}
func TestMustPanic(t *testing.T) {
t.Run("invalid size", func(t *testing.T) {
key := make([]byte, 16)
block, _ := sm4.NewCipher(key)
cryptotest.MustPanic(t, "cbcmac: invalid size", func() {
cbcmac.NewCBCMAC(block, 0)
cbcmac.NewCBCMAC(block, 17)
cbcmac.NewEMAC(sm4.NewCipher, key, key, 0)
cbcmac.NewEMAC(sm4.NewCipher, key, key, 17)
cbcmac.NewANSIRetailMAC(sm4.NewCipher, key, key, 0)
cbcmac.NewANSIRetailMAC(sm4.NewCipher, key, key, 17)
cbcmac.NewMACDES(sm4.NewCipher, key, key, 0)
cbcmac.NewMACDES(sm4.NewCipher, key, key, 17)
cbcmac.NewCMAC(block, 0)
cbcmac.NewCMAC(block, 17)
cbcmac.NewLMAC(sm4.NewCipher, key, 0)
cbcmac.NewLMAC(sm4.NewCipher, key, 17)
cbcmac.NewTRCBCMAC(block, 0)
cbcmac.NewTRCBCMAC(block, 17)
cbcmac.NewCBCRMAC(block, 0)
cbcmac.NewCBCRMAC(block, 17)
})
})
t.Run("invalid key size", func(t *testing.T) {
key := make([]byte, 16)
cryptotest.MustPanic(t, "cbcmac: invalid size", func() {
cbcmac.NewEMAC(sm4.NewCipher, key[:15], key, 8)
cbcmac.NewEMAC(sm4.NewCipher, key, key[:15], 8)
cbcmac.NewANSIRetailMAC(sm4.NewCipher, key[:15], key, 8)
cbcmac.NewANSIRetailMAC(sm4.NewCipher, key, key[:15], 8)
cbcmac.NewMACDES(sm4.NewCipher, key[:15], key, 8)
cbcmac.NewMACDES(sm4.NewCipher, key, key[:15], 8)
cbcmac.NewLMAC(sm4.NewCipher, key[:15], 8)
})
})
}
func fromHex(s string) []byte {
b, err := hex.DecodeString(s)
if err != nil {
panic(err)
}
return b
}
// Test vectors for CMAC-AES from NIST
// http://csrc.nist.gov/publications/nistpubs/800-38B/SP_800-38B.pdf
// Appendix D
var testVectors = []struct {
key, msg, hash string
tagsize int
}{
// AES-128 vectors
{
key: "2b7e151628aed2a6abf7158809cf4f3c",
msg: "",
hash: "bb1d6929e95937287fa37d129b756746",
tagsize: 16,
},
{
key: "2b7e151628aed2a6abf7158809cf4f3c",
msg: "6bc1bee22e409f96e93d7e117393172a",
hash: "070a16b46b4d4144f79bdd9dd04a287c",
tagsize: 16,
},
{
key: "2b7e151628aed2a6abf7158809cf4f3c",
msg: "6bc1bee22e409f96e93d7e117393172aae2d8a57",
hash: "7d85449ea6ea19c823a7bf78837dfade",
tagsize: 16,
},
{
key: "2b7e151628aed2a6abf7158809cf4f3c",
msg: "6bc1bee22e409f96e93d7e117393172a" +
"ae2d8a571e03ac9c9eb76fac45af8e51" +
"30c81c46a35ce411e5fbc1191a0a52ef" +
"f69f2445df4f9b17ad2b417be66c3710",
hash: "51f0bebf7e3b9d92fc49741779363cfe",
tagsize: 16,
},
{
key: "2b7e151628aed2a6abf7158809cf4f3c",
msg: "6bc1bee22e409f96e93d7e117393172a" +
"ae2d8a571e03ac9c9eb76fac45af8e51" +
"30c81c46a35ce411",
hash: "dfa66747de9ae63030ca32611497c827",
tagsize: 16,
},
{
key: "2b7e151628aed2a6abf7158809cf4f3c",
msg: "6bc1bee22e409f96e93d7e117393172a" +
"ae2d8a571e03ac9c9eb76fac45af8e51" +
"30c81c46a35ce411",
hash: "dfa66747de9ae63030ca32611497c827",
tagsize: 16,
},
{
key: "2b7e151628aed2a6abf7158809cf4f3c",
msg: "6bc1bee22e409f96e93d7e117393172a" +
"ae2d8a571e03ac9c9eb76fac45af8e51" +
"30c81c46a35ce411",
hash: "dfa66747de9ae63030ca3261",
tagsize: 12,
},
// AES-256 vectors
{
key: "603deb1015ca71be2b73aef0857d7781" +
"1f352c073b6108d72d9810a30914dff4",
msg: "",
hash: "028962f61b7bf89efc6b551f4667d983",
tagsize: 16,
},
{
key: "603deb1015ca71be2b73aef0857d7781" +
"1f352c073b6108d72d9810a30914dff4",
msg: "6bc1bee22e409f96e93d7e117393172a",
hash: "28a7023f452e8f82bd4bf28d8c37c35c",
tagsize: 16,
},
{
key: "603deb1015ca71be2b73aef0857d7781" +
"1f352c073b6108d72d9810a30914dff4",
msg: "6bc1bee22e409f96e93d7e117393172aae2d8a57",
hash: "156727dc0878944a023c1fe03bad6d93",
tagsize: 16,
},
{
key: "603deb1015ca71be2b73aef0857d7781" +
"1f352c073b6108d72d9810a30914dff4",
msg: "6bc1bee22e409f96e93d7e117393172a" +
"ae2d8a571e03ac9c9eb76fac45af8e51" +
"30c81c46a35ce411e5fbc1191a0a52ef" +
"f69f2445df4f9b17ad2b417be66c3710",
hash: "e1992190549f6ed5696a2c056c315410",
tagsize: 16,
},
{
key: "603deb1015ca71be2b73aef0857d7781" +
"1f352c073b6108d72d9810a30914dff4",
msg: "6bc1bee22e409f96e93d7e117393172a" +
"ae2d8a571e03ac9c9eb76fac45af8e51" +
"30c81c46a35ce411",
hash: "aaf3d8f1de5640c232f5b169b9c911e6",
tagsize: 16,
},
{
key: "603deb1015ca71be2b73aef0857d7781" +
"1f352c073b6108d72d9810a30914dff4",
msg: "6bc1bee22e409f96e93d7e117393172a" +
"ae2d8a571e03ac9c9eb76fac45af8e51" +
"30c81c46a35ce411",
hash: "aaf3d8f1de5640c232f5b169",
tagsize: 12,
},
}
func TestCMACAES(t *testing.T) {
for i, v := range testVectors {
key := fromHex(v.key)
msg := fromHex(v.msg)
hash := fromHex(v.hash)
block, err := aes.NewCipher(key)
if err != nil {
t.Errorf("#%d: failed to create cipher: %v", i, err)
}
mac := cbcmac.NewCMAC(block, v.tagsize)
tag := mac.MAC(msg)
if !bytes.Equal(tag, hash) {
t.Errorf("#%d: expect tag %x, got %x", i, hash, tag)
}
}
}
func TestCMACHash(t *testing.T) {
t.Run("CMAC Hash", func(t *testing.T) {
cryptotest.TestHash(t, func() hash.Hash {
key := []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10}
block, err := aes.NewCipher(key)
if err != nil {
t.Fatal(err)
}
return cbcmac.NewCMAC(block, 16)
})
})
}
var buf = make([]byte, 8192)
func benchmarkSize(hash hash.Hash, b *testing.B, size int) {
b.SetBytes(int64(size))
sum := make([]byte, hash.Size())
for i := 0; i < b.N; i++ {
hash.Reset()
hash.Write(buf[:size])
hash.Sum(sum[:0])
}
}
func BenchmarkAESCMAC1K(b *testing.B) {
key := []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10}
block, err := aes.NewCipher(key)
if err != nil {
b.Fatal(err)
}
benchmarkSize(cbcmac.NewCMAC(block, 16), b, 1024)
}
func BenchmarkSM4CMAC1K(b *testing.B) {
key := []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10}
block, err := sm4.NewCipher(key)
if err != nil {
b.Fatal(err)
}
benchmarkSize(cbcmac.NewCMAC(block, 16), b, 1024)
}

View File

@ -1,57 +0,0 @@
// Copyright 2024 Sun Yimin. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package cfca
import (
"crypto/cipher"
"errors"
"github.com/emmansun/gmsm/padding"
"github.com/emmansun/gmsm/sm3"
"github.com/emmansun/gmsm/sm4"
)
// NewSM4CBCBlockMode creates a new SM4-CBC block mode with the password.
func NewSM4CBCBlockMode(password []byte, isEncrypter bool) (cipher.BlockMode, error) {
if len(password) == 0 {
return nil, errors.New("cfca: invalid password")
}
ivkey := sm3.Kdf(password, 32)
block, err := sm4.NewCipher(ivkey[16:])
if err != nil {
return nil, err
}
if isEncrypter {
return cipher.NewCBCEncrypter(block, ivkey[:16]), nil
}
return cipher.NewCBCDecrypter(block, ivkey[:16]), nil
}
// EncryptBySM4CBC encrypts the data with the password using SM4-CBC algorithm.
// Corresponds to the cfca.sadk.util.encryptMessageBySM4 method.
func EncryptBySM4CBC(plaintext, password []byte) ([]byte, error) {
mode, err := NewSM4CBCBlockMode(password, true)
if err != nil {
return nil, err
}
pkcs7 := padding.NewPKCS7Padding(uint(mode.BlockSize()))
plaintext = pkcs7.Pad(plaintext)
ciphertext := make([]byte, len(plaintext))
mode.CryptBlocks(ciphertext, plaintext)
return ciphertext, nil
}
// DecryptBySM4CBC decrypts the data with the password using SM4-CBC algorithm.
// Corresponds to the cfca.sadk.util.decryptMessageBySM4 method.
func DecryptBySM4CBC(ciphertext, password []byte) ([]byte, error) {
mode, err := NewSM4CBCBlockMode(password, false)
if err != nil {
return nil, err
}
plaintext := make([]byte, len(ciphertext))
mode.CryptBlocks(plaintext, ciphertext)
pkcs7 := padding.NewPKCS7Padding(uint(mode.BlockSize()))
return pkcs7.Unpad(plaintext)
}

View File

@ -1,180 +0,0 @@
package cfca_test
import (
"crypto/ecdsa"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/base64"
"encoding/hex"
"encoding/pem"
"fmt"
"github.com/emmansun/gmsm/cfca"
"github.com/emmansun/gmsm/sm2"
)
func ExampleParseSM2() {
base64data := `
MIIDSQIBATBHBgoqgRzPVQYBBAIBBgcqgRzPVQFoBDDkLvKllj9ZWhaKU6MSnxBBV5yaF3tEcOk1
vQniWyVzyaQA4F3j/YvDJwEoE8gOF/swggL5BgoqgRzPVQYBBAIBBIIC6TCCAuUwggKJoAMCAQIC
BRBAmQgJMAwGCCqBHM9VAYN1BQAwXDELMAkGA1UEBhMCQ04xMDAuBgNVBAoMJ0NoaW5hIEZpbmFu
Y2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEbMBkGA1UEAwwSQ0ZDQSBURVNUIFNNMiBPQ0Ex
MB4XDTIwMTExOTA4MzExOFoXDTI1MTExOTA4MzExOFowgYkxCzAJBgNVBAYTAkNOMRcwFQYDVQQK
DA5DRkNBIFRFU1QgT0NBMTENMAsGA1UECwwEUFNCQzEZMBcGA1UECwwQT3JnYW5pemF0aW9uYWwt
MjE3MDUGA1UEAwwuMDUxQOmCruWCqOe6v+S4iuaUtuWNleWVhuaIt0BONTEwMTEzMDAwMTg4NzhA
MTBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABJVRC63OKfcL4H324rDOdb4SSlbAjoJDXnK0qmwX
Z59FWmiSqt3ipreljKew4QynjTgR/yfp9yjNgNU8G5pkYdujggEGMIIBAjAfBgNVHSMEGDAWgBRr
/hjaj0I6prhtsy6Igzo0osEw4TAMBgNVHRMBAf8EAjAAMEgGA1UdIARBMD8wPQYIYIEchu8qAQEw
MTAvBggrBgEFBQcCARYjaHR0cDovL3d3dy5jZmNhLmNvbS5jbi91cy91cy0xNC5odG0wOQYDVR0f
BDIwMDAuoCygKoYoaHR0cDovL3VjcmwuY2ZjYS5jb20uY24vU00yL2NybDE0MzU2LmNybDAOBgNV
HQ8BAf8EBAMCBsAwHQYDVR0OBBYEFPiGPZT0oTuRXvkyGoOgviNEWnc1MB0GA1UdJQQWMBQGCCsG
AQUFBwMCBggrBgEFBQcDBDAMBggqgRzPVQGDdQUAA0gAMEUCIQCJDSsVPfhr+gnDASMj5Syt+hxs
amHygPecjCLbcdFQQgIgSXC4musF5Fnj/CpNTqvk9+56FuINkATGS8xRh7kzKBE=`
password := []byte("123456")
data, err := base64.StdEncoding.DecodeString(base64data)
if err != nil {
panic(err)
}
priv, cert, err := cfca.ParseSM2(password, data)
if err != nil {
panic(err)
}
fmt.Printf("%x\n", priv.D.Bytes())
fmt.Printf("%v\n", cert.Issuer)
// Output: d3f24d61bb2816882b8474b778dd7c3166d665f9455dc9d551c989c161e76ab0
// CN=CFCA TEST SM2 OCA1,O=China Financial Certification Authority,C=CN
}
func ExampleParseSM2_pemEncoded() {
pemdata := `
-----BEGIN CFCA KEY-----
MIIDSQIBATBHBgoqgRzPVQYBBAIBBgcqgRzPVQFoBDDkLvKllj9ZWhaKU6MSnxBBV5yaF3tEcOk1
vQniWyVzyaQA4F3j/YvDJwEoE8gOF/swggL5BgoqgRzPVQYBBAIBBIIC6TCCAuUwggKJoAMCAQIC
BRBAmQgJMAwGCCqBHM9VAYN1BQAwXDELMAkGA1UEBhMCQ04xMDAuBgNVBAoMJ0NoaW5hIEZpbmFu
Y2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEbMBkGA1UEAwwSQ0ZDQSBURVNUIFNNMiBPQ0Ex
MB4XDTIwMTExOTA4MzExOFoXDTI1MTExOTA4MzExOFowgYkxCzAJBgNVBAYTAkNOMRcwFQYDVQQK
DA5DRkNBIFRFU1QgT0NBMTENMAsGA1UECwwEUFNCQzEZMBcGA1UECwwQT3JnYW5pemF0aW9uYWwt
MjE3MDUGA1UEAwwuMDUxQOmCruWCqOe6v+S4iuaUtuWNleWVhuaIt0BONTEwMTEzMDAwMTg4NzhA
MTBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABJVRC63OKfcL4H324rDOdb4SSlbAjoJDXnK0qmwX
Z59FWmiSqt3ipreljKew4QynjTgR/yfp9yjNgNU8G5pkYdujggEGMIIBAjAfBgNVHSMEGDAWgBRr
/hjaj0I6prhtsy6Igzo0osEw4TAMBgNVHRMBAf8EAjAAMEgGA1UdIARBMD8wPQYIYIEchu8qAQEw
MTAvBggrBgEFBQcCARYjaHR0cDovL3d3dy5jZmNhLmNvbS5jbi91cy91cy0xNC5odG0wOQYDVR0f
BDIwMDAuoCygKoYoaHR0cDovL3VjcmwuY2ZjYS5jb20uY24vU00yL2NybDE0MzU2LmNybDAOBgNV
HQ8BAf8EBAMCBsAwHQYDVR0OBBYEFPiGPZT0oTuRXvkyGoOgviNEWnc1MB0GA1UdJQQWMBQGCCsG
AQUFBwMCBggrBgEFBQcDBDAMBggqgRzPVQGDdQUAA0gAMEUCIQCJDSsVPfhr+gnDASMj5Syt+hxs
amHygPecjCLbcdFQQgIgSXC4musF5Fnj/CpNTqvk9+56FuINkATGS8xRh7kzKBE=
-----END CFCA KEY-----`
password := []byte("123456")
block, _ := pem.Decode([]byte(pemdata))
if block == nil {
panic("failed to decode PEM block")
}
priv, cert, err := cfca.ParseSM2(password, block.Bytes)
if err != nil {
panic(err)
}
fmt.Printf("%x\n", priv.D.Bytes())
fmt.Printf("%v\n", cert.Issuer)
// Output: d3f24d61bb2816882b8474b778dd7c3166d665f9455dc9d551c989c161e76ab0
// CN=CFCA TEST SM2 OCA1,O=China Financial Certification Authority,C=CN
}
func ExampleMarshalSM2_changePassword() {
pemdata := `
-----BEGIN CFCA KEY-----
MIIDSQIBATBHBgoqgRzPVQYBBAIBBgcqgRzPVQFoBDDkLvKllj9ZWhaKU6MSnxBBV5yaF3tEcOk1
vQniWyVzyaQA4F3j/YvDJwEoE8gOF/swggL5BgoqgRzPVQYBBAIBBIIC6TCCAuUwggKJoAMCAQIC
BRBAmQgJMAwGCCqBHM9VAYN1BQAwXDELMAkGA1UEBhMCQ04xMDAuBgNVBAoMJ0NoaW5hIEZpbmFu
Y2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEbMBkGA1UEAwwSQ0ZDQSBURVNUIFNNMiBPQ0Ex
MB4XDTIwMTExOTA4MzExOFoXDTI1MTExOTA4MzExOFowgYkxCzAJBgNVBAYTAkNOMRcwFQYDVQQK
DA5DRkNBIFRFU1QgT0NBMTENMAsGA1UECwwEUFNCQzEZMBcGA1UECwwQT3JnYW5pemF0aW9uYWwt
MjE3MDUGA1UEAwwuMDUxQOmCruWCqOe6v+S4iuaUtuWNleWVhuaIt0BONTEwMTEzMDAwMTg4NzhA
MTBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABJVRC63OKfcL4H324rDOdb4SSlbAjoJDXnK0qmwX
Z59FWmiSqt3ipreljKew4QynjTgR/yfp9yjNgNU8G5pkYdujggEGMIIBAjAfBgNVHSMEGDAWgBRr
/hjaj0I6prhtsy6Igzo0osEw4TAMBgNVHRMBAf8EAjAAMEgGA1UdIARBMD8wPQYIYIEchu8qAQEw
MTAvBggrBgEFBQcCARYjaHR0cDovL3d3dy5jZmNhLmNvbS5jbi91cy91cy0xNC5odG0wOQYDVR0f
BDIwMDAuoCygKoYoaHR0cDovL3VjcmwuY2ZjYS5jb20uY24vU00yL2NybDE0MzU2LmNybDAOBgNV
HQ8BAf8EBAMCBsAwHQYDVR0OBBYEFPiGPZT0oTuRXvkyGoOgviNEWnc1MB0GA1UdJQQWMBQGCCsG
AQUFBwMCBggrBgEFBQcDBDAMBggqgRzPVQGDdQUAA0gAMEUCIQCJDSsVPfhr+gnDASMj5Syt+hxs
amHygPecjCLbcdFQQgIgSXC4musF5Fnj/CpNTqvk9+56FuINkATGS8xRh7kzKBE=
-----END CFCA KEY-----`
password := []byte("123456")
block, _ := pem.Decode([]byte(pemdata))
if block == nil {
panic("failed to decode PEM block")
}
priv, cert, err := cfca.ParseSM2(password, block.Bytes)
if err != nil {
panic(err)
}
newpassword := []byte("654321")
data, err := cfca.MarshalSM2(newpassword, priv, cert)
if err != nil {
panic(err)
}
priv2, cert2, err := cfca.ParseSM2(newpassword, data)
if err != nil {
panic(err)
}
fmt.Printf("%x\n", priv2.D.Bytes())
fmt.Printf("%v\n", cert2.Issuer)
// Output: d3f24d61bb2816882b8474b778dd7c3166d665f9455dc9d551c989c161e76ab0
// CN=CFCA TEST SM2 OCA1,O=China Financial Certification Authority,C=CN
}
func ExampleCreateCertificateRequest() {
keyBytes, _ := hex.DecodeString("6c5a0a0b2eed3cbec3e4f1252bfe0e28c504a1c6bf1999eebb0af9ef0f8e6c85")
priv, err := sm2.NewPrivateKey(keyBytes)
if err != nil {
panic(err)
}
random := rand.Reader
// tmpKey used to decrypt the returned escrow PrivateKey
keyBytes, _ = hex.DecodeString("cacece36cac24aab94e52bcd5c0f552c95028f2856053135a1e47510b4c307ba")
tmpKey, err := sm2.NewPrivateKey(keyBytes)
if err != nil {
panic(err)
}
template := &x509.CertificateRequest{
Subject: pkix.Name{
CommonName: "certRequisition",
Organization: []string{"CFCA TEST CA"},
Country: []string{"CN"},
},
}
csrder, err := cfca.CreateCertificateRequest(random, template, priv, &tmpKey.PublicKey, "123456")
if err != nil {
panic(err)
}
// you can encode the csrder to PEM format or base64 format
csr, err := cfca.ParseCertificateRequest(csrder)
if err != nil {
panic(err)
}
fmt.Printf("%v\n", csr.Subject)
fmt.Printf("%v\n", csr.ChallengePassword)
fmt.Printf("%x\n", csr.TmpPublicKey.(*ecdsa.PublicKey).X)
// Output: CN=certRequisition,O=CFCA TEST CA,C=CN
// 123456
// c7c4d0945ebdfc2111ad64b0e92e04582b0725fea172968c6c40162c810f8882
}
func ExampleParseEscrowPrivateKey() {
// a sample method to parse the escrow private key
keydata := `00000000000000010000000000000001000000000000000000000000000000000000000000000268MIHGAgEBBIHArhtKwTVT8dPEkykVRpvQNMxHv/yeqtaKZiSp2MbjcqMZtPfKW8IatiIPPitNhQtU5C7gMbsUxgf5Yo16vDSXdoWqoOOaes2pEJwmXWZI55lMMWc168WgzQ82fmMi05Vhlw9HNjGI3azE6MS5/ujSNGLZ0qAAmLnBiHlXFAXXAWRiy9MxZKwF4xKn6qMaKmkqbYmTbBbEJEhzJBmu0IJ1kNDcTFirAyapghHSw267erSUwsHjkQis9mKYpzGied0E`
keyBytes, _ := hex.DecodeString("cacece36cac24aab94e52bcd5c0f552c95028f2856053135a1e47510b4c307ba")
tmpKey, err := sm2.NewPrivateKey(keyBytes)
if err != nil {
panic(err)
}
encKey, err := cfca.ParseEscrowPrivateKey(tmpKey, []byte(keydata))
if err != nil {
panic(err)
}
fmt.Printf("%x\n", encKey.D.Bytes())
// Output: f6e02c941a0dfdac58d8b3b1bc1bd136f179741b7465ebc7b0b25bb381840a3b
}

View File

@ -1,92 +0,0 @@
// Copyright 2024 Sun Yimin. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package cfca
import (
"bytes"
"crypto/ecdsa"
"crypto/x509"
"encoding/asn1"
"encoding/base64"
"errors"
"io"
"strconv"
"github.com/emmansun/gmsm/sm2"
"github.com/emmansun/gmsm/smx509"
)
type CertificateRequest = smx509.CertificateRequestCFCA
// CreateCertificateRequest creates a new certificate request based on a template.
// The following members of template are used: Subject.
// The certPriv is the private key for the certificate, and the tmpPub is the temporary public key for returning encryption key decryption.
// The challenge password is basically a shared-secret nonce between you and CFCA, embedded in the CSR.
func CreateCertificateRequest(rand io.Reader, template *x509.CertificateRequest, certPriv, tmpPub any, challengePassword string) ([]byte, error) {
return smx509.CreateCFCACertificateRequest(rand, template, certPriv, tmpPub, challengePassword)
}
// ParseCertificateRequest parses a certificate request from the given DER data.
// This method corresponds to CFCA SADK's cfca.sadk.asn1.pkcs.PKCS10.load.
func ParseCertificateRequest(der []byte) (*CertificateRequest, error) {
return smx509.ParseCFCACertificateRequest(der)
}
const encryptedEncKeyPrefix = "0000000000000001000000000000000100000000000000000000000000000000"
type encryptedPrivateKeyInfo struct {
Version int `asn1:"default:1"`
EncryptedKey []byte
}
// ParseEscrowPrivateKey parses an CFCA generated and returned SM2 private key from the given data.
// The data is expected to be in the format of "0000000000000001000000000000000100000000000000000000000000000000...".
// If the data is not in this format, it will be treated as base64 encoded data directly.
func ParseEscrowPrivateKey(tmpPriv *sm2.PrivateKey, data []byte) (*sm2.PrivateKey, error) {
if len(data) < 268 {
return nil, errors.New("cfca: invalid encrypted private key data")
}
encodedKeyPart := data
if bytes.HasPrefix(data, []byte(encryptedEncKeyPrefix)) {
retLen, err := strconv.Atoi(string(data[64:80]))
if err != nil {
return nil, err
}
if retLen != len(data[80:]) {
return nil, errors.New("cfca: invalid encrypted private key data")
}
encodedKeyPart = data[80:]
}
// remove all commas ONLY now. If there are other non-base64 characters, the base64 decoder will fail.
encodedKeyPart = bytes.ReplaceAll(encodedKeyPart, []byte{44}, []byte{})
der, err := base64.StdEncoding.DecodeString(string(encodedKeyPart))
if err != nil {
return nil, err
}
var keyInfo encryptedPrivateKeyInfo
if _, err := asn1.Unmarshal(der, &keyInfo); err != nil {
return nil, err
}
var ret []byte
if ret, err = tmpPriv.Decrypt(nil, append([]byte{0x04}, keyInfo.EncryptedKey...), nil); err != nil {
return nil, errors.New("cfca: failed to decrypt the private key, possibly due to incorrect key data")
}
// X || Y || D
if len(ret) != 96 {
return nil, errors.New("cfca: invalid decrypted private key data")
}
var priv *sm2.PrivateKey
if priv, err = sm2.NewPrivateKey(ret[64:]); err != nil {
return nil, err
}
var pub *ecdsa.PublicKey
if pub, err = sm2.NewPublicKey(append([]byte{0x04}, ret[:64]...)); err != nil {
return nil, err
}
if !pub.Equal(&priv.PublicKey) {
return nil, errors.New("cfca: key pair mismatch, possibly due to incorrect key data or corruption")
}
return priv, nil
}

View File

@ -1,281 +0,0 @@
// Copyright 2024 Sun Yimin. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package cfca
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/hex"
"testing"
"github.com/emmansun/gmsm/sm2"
)
func TestCreateCertificateRequest(t *testing.T) {
random := rand.Reader
certKey, err := sm2.GenerateKey(random)
if err != nil {
t.Fatal(err)
}
certRSAKey, err := rsa.GenerateKey(random, 2048)
if err != nil {
t.Fatal(err)
}
tmpKey, err := sm2.GenerateKey(random)
if err != nil {
t.Fatal(err)
}
p256Key, err := ecdsa.GenerateKey(elliptic.P256(), random)
if err != nil {
t.Fatal(err)
}
rsaKey, err := rsa.GenerateKey(random, 2048)
if err != nil {
t.Fatal(err)
}
template := &x509.CertificateRequest{
Subject: pkix.Name{
CommonName: "certRequisition",
Organization: []string{"CFCA TEST CA"},
Country: []string{"CN"},
},
}
testCases := []struct {
template *x509.CertificateRequest
priv interface{}
tmpPub interface{}
challengePassword string
wantErr bool
errormsg string
}{
{
template: template,
priv: certKey,
tmpPub: tmpKey.Public(),
challengePassword: "111111",
wantErr: false,
errormsg: "",
},
{
template: template,
priv: certRSAKey,
tmpPub: rsaKey.Public(),
challengePassword: "111111",
wantErr: false,
errormsg: "",
},
{
template: template,
priv: p256Key,
tmpPub: nil,
challengePassword: "",
wantErr: false,
errormsg: "",
},
{
template: template,
priv: "",
tmpPub: "",
challengePassword: "",
wantErr: true,
errormsg: "x509: certificate private key does not implement crypto.Signer",
},
{
template: template,
priv: certKey,
tmpPub: "",
challengePassword: "",
wantErr: true,
errormsg: "x509: SM2 temp public key is required",
},
{
template: template,
priv: certKey,
tmpPub: rsaKey.Public(),
challengePassword: "",
wantErr: true,
errormsg: "x509: SM2 temp public key is required",
},
{
template: template,
priv: certRSAKey,
tmpPub: tmpKey.Public(),
challengePassword: "",
wantErr: true,
errormsg: "x509: RSA temp public key is required",
},
{
template: template,
priv: certKey,
tmpPub: p256Key.Public(),
challengePassword: "",
wantErr: true,
errormsg: "x509: SM2 temp public key is required",
},
{
template: template,
priv: p256Key,
tmpPub: certKey.Public(),
challengePassword: "111111",
wantErr: true,
errormsg: "x509: only RSA or SM2 key is supported",
},
{
template: template,
priv: certKey,
tmpPub: tmpKey.Public(),
challengePassword: "",
wantErr: true,
errormsg: "x509: challenge password is required",
},
}
for _, tc := range testCases {
_, err := CreateCertificateRequest(random, tc.template, tc.priv, tc.tmpPub, tc.challengePassword)
if tc.wantErr {
if err == nil {
t.Fatal("expected error, got nil")
}
if err.Error() != tc.errormsg {
t.Fatalf("expected error %s, got %s", tc.errormsg, err.Error())
}
} else if err != nil {
t.Fatal(err)
}
}
}
func TestParseEscrowPrivateKey(t *testing.T) {
cases := []struct {
encKeyHex string
tmpKeyHex string
encryptedKey string
wantError bool
errorMsg string
}{
{ // with prefix, without delimiter
"f6e02c941a0dfdac58d8b3b1bc1bd136f179741b7465ebc7b0b25bb381840a3b",
"cacece36cac24aab94e52bcd5c0f552c95028f2856053135a1e47510b4c307ba",
"00000000000000010000000000000001000000000000000000000000000000000000000000000268MIHGAgEBBIHArhtKwTVT8dPEkykVRpvQNMxHv/yeqtaKZiSp2MbjcqMZtPfKW8IatiIPPitNhQtU5C7gMbsUxgf5Yo16vDSXdoWqoOOaes2pEJwmXWZI55lMMWc168WgzQ82fmMi05Vhlw9HNjGI3azE6MS5/ujSNGLZ0qAAmLnBiHlXFAXXAWRiy9MxZKwF4xKn6qMaKmkqbYmTbBbEJEhzJBmu0IJ1kNDcTFirAyapghHSw267erSUwsHjkQis9mKYpzGied0E",
false,
"",
},
{ // without prefix, without delimiter
"f6e02c941a0dfdac58d8b3b1bc1bd136f179741b7465ebc7b0b25bb381840a3b",
"cacece36cac24aab94e52bcd5c0f552c95028f2856053135a1e47510b4c307ba",
"MIHGAgEBBIHArhtKwTVT8dPEkykVRpvQNMxHv/yeqtaKZiSp2MbjcqMZtPfKW8IatiIPPitNhQtU5C7gMbsUxgf5Yo16vDSXdoWqoOOaes2pEJwmXWZI55lMMWc168WgzQ82fmMi05Vhlw9HNjGI3azE6MS5/ujSNGLZ0qAAmLnBiHlXFAXXAWRiy9MxZKwF4xKn6qMaKmkqbYmTbBbEJEhzJBmu0IJ1kNDcTFirAyapghHSw267erSUwsHjkQis9mKYpzGied0E",
false,
"",
},
{ // with prefix, with delimiter
"f6e02c941a0dfdac58d8b3b1bc1bd136f179741b7465ebc7b0b25bb381840a3b",
"cacece36cac24aab94e52bcd5c0f552c95028f2856053135a1e47510b4c307ba",
"00000000000000010000000000000001000000000000000000000000000000000000000000000273MIHGAgEBBIHArhtKwTVT8dPEkykVRpvQNMxHv/yeqtaKZiSp2MbjcqMZtPfKW8Ia,tiIPPitNhQtU5C7gMbsUxgf5Yo16vDSXdoWqoOOaes2pEJwmXWZI55lMMWc168Wg,zQ82fmMi05Vhlw9HNjGI3azE6MS5/ujSNGLZ0qAAmLnBiHlXFAXXAWRiy9MxZKwF,4xKn6qMaKmkqbYmTbBbEJEhzJBmu0IJ1kNDcTFirAyapghHSw267erSUwsHjkQis,9mKYpzGied0E,",
false,
"",
},
{ // too short
"f6e02c941a0dfdac58d8b3b1bc1bd136f179741b7465ebc7b0b25bb381840a3b",
"cacece36cac24aab94e52bcd5c0f552c95028f2856053135a1e47510b4c307ba",
"000000000000000100000000000000010",
true,
"cfca: invalid encrypted private key data",
},
{ // length not match
"f6e02c941a0dfdac58d8b3b1bc1bd136f179741b7465ebc7b0b25bb381840a3b",
"cacece36cac24aab94e52bcd5c0f552c95028f2856053135a1e47510b4c307ba",
"00000000000000010000000000000001000000000000000000000000000000000000000000000273MIHGAgEBBIHArhtKwTVT8dPEkykVRpvQNMxHv/yeqtaKZiSp2MbjcqMZtPfKW8IatiIPPitNhQtU5C7gMbsUxgf5Yo16vDSXdoWqoOOaes2pEJwmXWZI55lMMWc168WgzQ82fmMi05Vhlw9HNjGI3azE6MS5/ujSNGLZ0qAAmLnBiHlXFAXXAWRiy9MxZKwF4xKn6qMaKmkqbYmTbBbEJEhzJBmu0IJ1kNDcTFirAyapghHSw267erSUwsHjkQis9mKYpzGied0E",
true,
"cfca: invalid encrypted private key data",
},
{ // with prefix, with invalid delimiter
"f6e02c941a0dfdac58d8b3b1bc1bd136f179741b7465ebc7b0b25bb381840a3b",
"cacece36cac24aab94e52bcd5c0f552c95028f2856053135a1e47510b4c307ba",
"00000000000000010000000000000001000000000000000000000000000000000000000000000274MIHGAgEBBIHArhtKwTVT8dPEkykVRpvQNMxHv/yeqtaKZiSp2MbjcqMZtPfKW8Ia, tiIPPitNhQtU5C7gMbsUxgf5Yo16vDSXdoWqoOOaes2pEJwmXWZI55lMMWc168Wg,zQ82fmMi05Vhlw9HNjGI3azE6MS5/ujSNGLZ0qAAmLnBiHlXFAXXAWRiy9MxZKwF,4xKn6qMaKmkqbYmTbBbEJEhzJBmu0IJ1kNDcTFirAyapghHSw267erSUwsHjkQis,9mKYpzGied0E,",
true,
"illegal base64 data at input byte 64",
},
{ // invalid base64
"f6e02c941a0dfdac58d8b3b1bc1bd136f179741b7465ebc7b0b25bb381840a3b",
"cacece36cac24aab94e52bcd5c0f552c95028f2856053135a1e47510b4c307ba",
"NIHGAgEBBIHArhtKwTVT8dPEkykVRpvQNMxHv/yeqtaKZiSp2MbjcqMZtPfKW8IatiIPPitNhQtU5C7gMbsUxgf5Yo16vDSXdoWqoOOaes2pEJwmXWZI55lMMWc168WgzQ82fmMi05Vhlw9HNjGI3azE6MS5/ujSNGLZ0qAAmLnBiHlXFAXXAWRiy9MxZKwF4xKn6qMaKmkqbYmTbBbEJEhzJBmu0IJ1kNDcTFirAyapghHSw267erSUwsHjkQis9mKYpzGied0E",
true,
"asn1: structure error: tags don't match (16 vs {class:0 tag:20 length:198 isCompound:true}) {optional:false explicit:false application:false private:false defaultValue:<nil> tag:<nil> stringType:0 timeType:0 set:false omitEmpty:false} encryptedPrivateKeyInfo @3",
},
{ // with prefix, with delimiter, invalid length string
"f6e02c941a0dfdac58d8b3b1bc1bd136f179741b7465ebc7b0b25bb381840a3b",
"cacece36cac24aab94e52bcd5c0f552c95028f2856053135a1e47510b4c307ba",
"00000000000000010000000000000001000000000000000000000000000000000000000000000/73MIHGAgEBBIHArhtKwTVT8dPEkykVRpvQNMxHv/yeqtaKZiSp2MbjcqMZtPfKW8Ia,tiIPPitNhQtU5C7gMbsUxgf5Yo16vDSXdoWqoOOaes2pEJwmXWZI55lMMWc168Wg,zQ82fmMi05Vhlw9HNjGI3azE6MS5/ujSNGLZ0qAAmLnBiHlXFAXXAWRiy9MxZKwF,4xKn6qMaKmkqbYmTbBbEJEhzJBmu0IJ1kNDcTFirAyapghHSw267erSUwsHjkQis,9mKYpzGied0E,",
true,
"strconv.Atoi: parsing \"0000000000000/73\": invalid syntax",
},
{
"f6e02c941a0dfdac58d8b3b1bc1bd136f179741b7465ebc7b0b25bb381840a3b",
"cacece36cac24aab94e52bcd5c0f552c95028f2856053135a1e47510b4c307ba",
"MIHLAgEBBIHFBNMWBxk04B00wJQC1fQsida/0ZZEAMh/ggaC006oQUFFQKJp18YgC/9xkkBLa75DxPy85+n21gZaXUs3s628SaQiKejqH7yx3Pr0onRepDED5O/grQoyxdHL3LpuC4jp7MrOeVDqC6PAWIhZanDhdN4617QJeBmKbkZSqo/SNXfh9+QDDwBBNMLV27LR53ShpAUYbJwqQoW2Od4+MGkzUK3jy+T9HbPcaAZMedAuhXhQgRf69x8CNSHjmOVVFQQZe7OHYY8=",
true,
"cfca: failed to decrypt the private key, possibly due to incorrect key data",
},
{
"f6e02c941a0dfdac58d8b3b1bc1bd136f179741b7465ebc7b0b25bb381840a3b",
"cacece36cac24aab94e52bcd5c0f552c95028f2856053135a1e47510b4c307ba",
"MIHKAgEBBIHEMEiAGc8dn+9mKnIlaesNqV2h53FxzNm1O4Bl5P16t6QT4JcJvTcTsh9DiHZF1Z0b+z/PrAT2r8aST2aKwRBPLrkWHKKDLZnCtAuz3Al1sV5ZMb5dCVX/Gy3LWMhVNwmzgkV6hfuFokTc2qL7p297XG4nnT11jz7iI1sRJ2E7bn52tF6W6ApICJuDKyFiLVKmMayn3PSsd8+I5IXNNtIer+GYKabAkNHwao4cuK1tuhy1uiSlwfzWq1CSHFD+LIRbXpijQA==",
true,
"cfca: invalid decrypted private key data",
},
{
"f6e02c941a0dfdac58d8b3b1bc1bd136f179741b7465ebc7b0b25bb381840a3b",
"cacece36cac24aab94e52bcd5c0f552c95028f2856053135a1e47510b4c307ba",
"MIHGAgEBBIHANZyM9KF7qqyUDzh6wZmLU6czep9FxJfojSpxrAYbNN2j/Jad5cOaNmhO4tL+tfk42O8y9+jUebPWCUOuSXZADJZOEyRo2tehvrT2CxEEA9cJ0pK87uXiRsd9vLyjYeEzbngO8tpFrSrpF8G/KYbJ1QiI3W+QLQnofwtChNVwOjyjLxoFO9gx3jvfVH79ECoYC11UL0o0YASx9niiGkqT/q8tqbr7DwIDu0tbXVfwhjJJ2zNZIdECDkV3o7as9ika",
true,
"sm2: invalid private key",
},
{
"f6e02c941a0dfdac58d8b3b1bc1bd136f179741b7465ebc7b0b25bb381840a3b",
"cacece36cac24aab94e52bcd5c0f552c95028f2856053135a1e47510b4c307ba",
"MIHGAgEBBIHAaxudEYQXAT4n+s2fhHJlPVvY2+TNRAS96F7vskiENVLHahIWxtDeU6BeJ5SFTEXTz5vdYp4as66DU69xCWNYl4kDCy3gfT2iIDEp6NcbPHkAp/rKIFXMUZyBq9wGCkeAZwvpK09JMLffvGWTFU7MzepyFtYTsRjwZ5tBX+8GaSDHaCD0CtVtz5k3bFRLPE2ru4XZW787BiEBrxUG9Zn5pnkNLlnVmUNSI01qKXJxK/hAJ+B82DtXdZgSUaspW5ro",
true,
"point not on SM2 P256 curve",
},
{
"f6e02c941a0dfdac58d8b3b1bc1bd136f179741b7465ebc7b0b25bb381840a3b",
"cacece36cac24aab94e52bcd5c0f552c95028f2856053135a1e47510b4c307ba",
"MIHGAgEBBIHA7pcowvNdY6kHesm6Ni1rM+iFNSXOXyET+gstbxQ0Vq1+W+YmZUTNQs8CpNuU6fpjZt8azXvKwdrUKEMaadZR4vTBwl+UcvjdpwlBmI8o9UxYkWNSGeI0CWHCgml57xHbhAl3xlRzCi2qOakvEcwTRmzvB73Pt/DgahSPGSmdOy3CrAyMkhcrHiiR9aIWXEKbOnwST+wcRJ65Mr+5ZDOaN8wg6NzLttnWg93CA3k1AsziCGe/sRW6Qd2FrcvMZQc2",
true,
"cfca: key pair mismatch, possibly due to incorrect key data or corruption",
},
}
for _, c := range cases {
encKey, _ := hex.DecodeString(c.encKeyHex)
tmpKey, _ := hex.DecodeString(c.tmpKeyHex)
tmpSM2Key, err := sm2.NewPrivateKey(tmpKey)
if err != nil {
t.Fatal(err)
}
targetEncKey, err := sm2.NewPrivateKey(encKey)
if err != nil {
t.Fatal(err)
}
gotKey, err := ParseEscrowPrivateKey(tmpSM2Key, []byte(c.encryptedKey))
if c.wantError {
if err == nil || err.Error() != c.errorMsg {
t.Fatalf("expected error %v, got %v", c.errorMsg, err)
}
continue
}
if err != nil {
t.Fatal(err)
}
if !gotKey.Equal(targetEncKey) {
t.Fatalf("decrypted key not match")
}
}
}

View File

@ -1,105 +0,0 @@
// Package cfca supports part of CFCA SADK's functions, provides interoperability with CFCA SADK.
package cfca
import (
"encoding/asn1"
"errors"
"fmt"
"math/big"
"github.com/emmansun/gmsm/pkcs"
"github.com/emmansun/gmsm/pkcs7"
"github.com/emmansun/gmsm/sm2"
"github.com/emmansun/gmsm/smx509"
)
// cfcaKeyPairData represents a key pair data structure used
// in CFCA (China Financial Certification Authority)
// for both parsing and marshaling SM2 keys and certificates.
type cfcaKeyPairData struct {
Version int `asn1:"default:1"`
EncryptedKey keyData
Certificate certData
}
// Encrypted private key data
type keyData struct {
ContentType asn1.ObjectIdentifier
Algorithm asn1.ObjectIdentifier
EncryptedContent asn1.RawValue
}
// Corresponding certificate
type certData struct {
ContentType asn1.ObjectIdentifier
Content asn1.RawContent
}
var (
oidSM2Data = pkcs7.SM2OIDData
oidSM4 = pkcs.SM4.OID()
oidSM4CBC = pkcs.SM4CBC.OID()
)
// ParseSM2 parses the der data, returns private key and related certificate, it's CFCA private structure.
// This method is corresponding to CFCA SADK's cfca.sadk.asn1.pkcs.load.
func ParseSM2(password, data []byte) (*sm2.PrivateKey, *smx509.Certificate, error) {
var keys cfcaKeyPairData
if _, err := asn1.Unmarshal(data, &keys); err != nil {
return nil, nil, err
}
if !keys.Certificate.ContentType.Equal(oidSM2Data) {
return nil, nil, fmt.Errorf("cfca: unsupported content type oid <%v>", keys.Certificate.ContentType)
}
if !keys.EncryptedKey.ContentType.Equal(oidSM2Data) {
return nil, nil, fmt.Errorf("cfca: unsupported content type oid <%v>", keys.EncryptedKey.ContentType)
}
if !keys.EncryptedKey.Algorithm.Equal(oidSM4) && !keys.EncryptedKey.Algorithm.Equal(oidSM4CBC) {
return nil, nil, fmt.Errorf("cfca: unsupported algorithm <%v>", keys.EncryptedKey.Algorithm)
}
pk, err := DecryptBySM4CBC(keys.EncryptedKey.EncryptedContent.Bytes, password)
if err != nil {
return nil, nil, fmt.Errorf("cfca: failed to decrypt by SM4-CBC, please ensure the password is correct: %v", err)
}
prvKey, err := sm2.NewPrivateKeyFromInt(new(big.Int).SetBytes(pk))
if err != nil {
return nil, nil, err
}
cert, err := smx509.ParseCertificate(keys.Certificate.Content)
if err != nil {
return nil, nil, err
}
if !prvKey.PublicKey.Equal(cert.PublicKey) {
return nil, nil, errors.New("cfca: public key and private key do not match")
}
return prvKey, cert, nil
}
// MarshalSM2 encodes sm2 private key and related certificate to cfca defined format.
// This method is corresponding to CFCA SADK's cfca.sadk.asn1.pkcs.CombineSM2Data.
func MarshalSM2(password []byte, key *sm2.PrivateKey, cert *smx509.Certificate) ([]byte, error) {
var err error
var ciphertext []byte
if ciphertext, err = EncryptBySM4CBC(key.D.Bytes(), password); err != nil {
return nil, err
}
if ciphertext, err = asn1.Marshal(ciphertext); err != nil {
return nil, err
}
keys := cfcaKeyPairData{
Version: 1,
EncryptedKey: keyData{
ContentType: oidSM2Data,
Algorithm: oidSM4,
EncryptedContent: asn1.RawValue{FullBytes: ciphertext},
},
Certificate: certData{
ContentType: oidSM2Data,
Content: cert.Raw,
},
}
return asn1.Marshal(keys)
}

View File

@ -1,205 +0,0 @@
package cfca
import (
"encoding/hex"
"encoding/pem"
"errors"
"math/big"
"testing"
"github.com/emmansun/gmsm/sm2"
"github.com/emmansun/gmsm/smx509"
)
var v2exKeyPem = `-----BEGIN CFCA KEY-----
MIIDSQIBATBHBgoqgRzPVQYBBAIBBgcqgRzPVQFoBDDkLvKllj9ZWhaKU6MSnxBBV5yaF3tEcOk1
vQniWyVzyaQA4F3j/YvDJwEoE8gOF/swggL5BgoqgRzPVQYBBAIBBIIC6TCCAuUwggKJoAMCAQIC
BRBAmQgJMAwGCCqBHM9VAYN1BQAwXDELMAkGA1UEBhMCQ04xMDAuBgNVBAoMJ0NoaW5hIEZpbmFu
Y2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEbMBkGA1UEAwwSQ0ZDQSBURVNUIFNNMiBPQ0Ex
MB4XDTIwMTExOTA4MzExOFoXDTI1MTExOTA4MzExOFowgYkxCzAJBgNVBAYTAkNOMRcwFQYDVQQK
DA5DRkNBIFRFU1QgT0NBMTENMAsGA1UECwwEUFNCQzEZMBcGA1UECwwQT3JnYW5pemF0aW9uYWwt
MjE3MDUGA1UEAwwuMDUxQOmCruWCqOe6v+S4iuaUtuWNleWVhuaIt0BONTEwMTEzMDAwMTg4NzhA
MTBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABJVRC63OKfcL4H324rDOdb4SSlbAjoJDXnK0qmwX
Z59FWmiSqt3ipreljKew4QynjTgR/yfp9yjNgNU8G5pkYdujggEGMIIBAjAfBgNVHSMEGDAWgBRr
/hjaj0I6prhtsy6Igzo0osEw4TAMBgNVHRMBAf8EAjAAMEgGA1UdIARBMD8wPQYIYIEchu8qAQEw
MTAvBggrBgEFBQcCARYjaHR0cDovL3d3dy5jZmNhLmNvbS5jbi91cy91cy0xNC5odG0wOQYDVR0f
BDIwMDAuoCygKoYoaHR0cDovL3VjcmwuY2ZjYS5jb20uY24vU00yL2NybDE0MzU2LmNybDAOBgNV
HQ8BAf8EBAMCBsAwHQYDVR0OBBYEFPiGPZT0oTuRXvkyGoOgviNEWnc1MB0GA1UdJQQWMBQGCCsG
AQUFBwMCBggrBgEFBQcDBDAMBggqgRzPVQGDdQUAA0gAMEUCIQCJDSsVPfhr+gnDASMj5Syt+hxs
amHygPecjCLbcdFQQgIgSXC4musF5Fnj/CpNTqvk9+56FuINkATGS8xRh7kzKBE=
-----END CFCA KEY-----
`
var cfcasm2oca1 = `-----BEGIN CERTIFICATE-----
MIICTTCCAfKgAwIBAgIKZCTXgL0MKPOtBzAMBggqgRzPVQGDdQUAMF0xCzAJBgNV
BAYTAkNOMTAwLgYDVQQKDCdDaGluYSBGaW5hbmNpYWwgQ2VydGlmaWNhdGlvbiBB
dXRob3JpdHkxHDAaBgNVBAMME0NGQ0EgVEVTVCBDUyBTTTIgQ0EwHhcNMTIxMjI1
MTIyNTA2WhcNMzIwNzIzMTIyNTA2WjBcMQswCQYDVQQGEwJDTjEwMC4GA1UECgwn
Q2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRswGQYDVQQD
DBJDRkNBIFRFU1QgU00yIE9DQTEwWTATBgcqhkjOPQIBBggqgRzPVQGCLQNCAAQz
uFgJbedY55u6NToJElGWzPT+9UF1dxcopnerNO3fqRd4C1lDzz9LJZSfmMyNYaky
YC+6zh9G6/aPXW1Od/RFo4GYMIGVMB8GA1UdIwQYMBaAFLXYkG9c8Ngz0mO9frLD
jcZPEnphMAwGA1UdEwQFMAMBAf8wOAYDVR0fBDEwLzAtoCugKYYnaHR0cDovLzIx
MC43NC40Mi4zL3Rlc3RyY2EvU00yL2NybDEuY3JsMAsGA1UdDwQEAwIBBjAdBgNV
HQ4EFgQUa/4Y2o9COqa4bbMuiIM6NKLBMOEwDAYIKoEcz1UBg3UFAANHADBEAiAR
kDmkQ0Clio48994IUs63nA8k652O2C4+7EQs1SSbuAIgcwNUrHJyEYX8xT5BKl9T
lJOefzCNNJW5Z0f3Y/SjaG0=
-----END CERTIFICATE-----
`
func parseTestKeyAndCert() (*sm2.PrivateKey, *smx509.Certificate, error) {
password := []byte("123456")
var block *pem.Block
block, rest := pem.Decode([]byte(v2exKeyPem))
if len(rest) != 0 {
return nil, nil, errors.New("unexpected remaining PEM block during decode")
}
return ParseSM2(password, block.Bytes)
}
func TestParseSM2(t *testing.T) {
cases := []struct {
pem string
password []byte
}{
{
v2exKeyPem,
[]byte("123456"),
},
{
`-----BEGIN CFCA KEY-----
MIIDmwIBATBHBgoqgRzPVQYBBAIBBgcqgRzPVQFoBDAjEsMB1LZrH4B5zBJQLh/S3vLTegY5twIU
lKu80vkB3XLfImABwhYVzFkjfJY1lWEwggNLBgoqgRzPVQYBBAIBBIIDOzCCAzcwggLaoAMCAQIC
BUQmAVGGMAwGCCqBHM9VAYN1BQAwXDELMAkGA1UEBhMCQ04xMDAuBgNVBAoMJ0NoaW5hIEZpbmFu
Y2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEbMBkGA1UEAwwSQ0ZDQSBBQ1MgU00yIE9DQTMx
MB4XDTIxMDcyMTA5NTMxMloXDTIxMDkyMTA5NTMxMlowbDELMAkGA1UEBhMCQ04xEzARBgNVBAoM
CkNGQ0EgT0NBMzExDzANBgNVBAsMBnlzZXBheTEVMBMGA1UECwwMSW5kaXZpZHVhbC0xMSAwHgYD
VQQDDBcwNTFAdGVzdF9zbTJAdGVzdF9zbTJAMjBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABIex
X8bD+NRAEyP9mKl8/OKHHfogP82NobcifE9zyFlH0MPyMyXnjMT4FBQ1HPGRTExIUvnnS1GnuG0E
gtF58oCjggF1MIIBcTBsBggrBgEFBQcBAQRgMF4wKAYIKwYBBQUHMAGGHGh0dHA6Ly9vY3NwLmNm
Y2EuY29tLmNuL29jc3AwMgYIKwYBBQUHMAKGJmh0dHA6Ly9jcmwuY2ZjYS5jb20uY24vb2NhMzEv
b2NhMzEuY2VyMB8GA1UdIwQYMBaAFAjY0SbESH2c7KyY6fF/YrmAzqlFMAkGA1UdEwQCMAAwSAYD
VR0gBEEwPzA9BghggRyG7yoBBDAxMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmNmY2EuY29tLmNu
L3VzL3VzLTE0Lmh0bTA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3JsLmNmY2EuY29tLmNuL29j
YTMxL1NNMi9jcmwxNDQwLmNybDAOBgNVHQ8BAf8EBAMCBsAwHQYDVR0OBBYEFJDoMEr89lXvtODi
obIvu3LOpoiFMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDBDAMBggqgRzPVQGDdQUAA0kA
MEYCIQCV2YwNr90ad1E5mZqzmdkU0E1CWie9K0lsml012slavgIhAM/++u/l1x5cCIPZsCOYrIy2
0N8+aiLInpgEnkw3wQMt
-----END CFCA KEY-----
`,
[]byte("ys123456"),
},
}
for _, c := range cases {
block, _ := pem.Decode([]byte(c.pem))
if block == nil {
t.Fatal("failed to decode PEM block")
}
priv, cert, err := ParseSM2(c.password, block.Bytes)
if err != nil {
t.Fatal(err)
}
if priv == nil {
t.Fatal("failed to parse private key")
}
if cert == nil {
t.Fatal("failed to parse certificate")
}
if !priv.PublicKey.Equal(cert.PublicKey) {
t.Fatal("public key mismatch")
}
}
}
func TestMarshalSM2(t *testing.T) {
password := []byte("changeit")
priv, cert, err := parseTestKeyAndCert()
if err != nil {
t.Fatal(err)
}
rootca1, err := smx509.ParseCertificatePEM([]byte(cfcasm2oca1))
if err != nil {
t.Fatal(err)
}
err = rootca1.CheckSignature(cert.SignatureAlgorithm, cert.RawTBSCertificate, cert.Signature)
if err != nil {
t.Fatal(err)
}
result, err := MarshalSM2(password, priv, cert)
if err != nil {
t.Fatal(err)
}
priv1, cert1, err := ParseSM2(password, result)
if err != nil {
t.Fatal(err)
}
if !priv.Equal(priv1) {
t.Fatal("not same private key")
}
if !cert.Equal(cert1) {
t.Fatal("not same certficate")
}
}
func TestParseSM2WithInvalidOID(t *testing.T) {
password := []byte("changeit")
testcases := []string{
"308203490201013047060a2a811ccf55060104020906072a811ccf5501680430016ca58c339f32f5bbe2b0491d5087c9b5de0f5aef4b4d0726941a0c21d9795f51e802f2a1596cc909d1638067ca28cc308202f9060a2a811ccf550601040201048202e9308202e530820289a00302010202051040990809300c06082a811ccf550183750500305c310b300906035504061302434e3130302e060355040a0c274368696e612046696e616e6369616c2043657274696669636174696f6e20417574686f72697479311b301906035504030c1243464341205445535420534d32204f434131301e170d3230313131393038333131385a170d3235313131393038333131385a308189310b300906035504061302434e31173015060355040a0c0e434643412054455354204f434131310d300b060355040b0c045053424331193017060355040b0c104f7267616e697a6174696f6e616c2d323137303506035504030c2e30353140e982aee582a8e7babfe4b88ae694b6e58d95e59586e688b7404e353130313133303030313838373840313059301306072a8648ce3d020106082a811ccf5501822d0342000495510badce29f70be07df6e2b0ce75be124a56c08e82435e72b4aa6c17679f455a6892aadde2a6b7a58ca7b0e10ca78d3811ff27e9f728cd80d53c1b9a6461dba382010630820102301f0603551d230418301680146bfe18da8f423aa6b86db32e88833a34a2c130e1300c0603551d130101ff0402300030480603551d200441303f303d060860811c86ef2a01013031302f06082b060105050702011623687474703a2f2f7777772e636663612e636f6d2e636e2f75732f75732d31342e68746d30390603551d1f04323030302ea02ca02a8628687474703a2f2f7563726c2e636663612e636f6d2e636e2f534d322f63726c31343335362e63726c300e0603551d0f0101ff0404030206c0301d0603551d0e04160414f8863d94f4a13b915ef9321a83a0be23445a7735301d0603551d250416301406082b0601050507030206082b06010505070304300c06082a811ccf5501837505000348003045022100890d2b153df86bfa09c3012323e52cadfa1c6c6a61f280f79c8c22db71d1504202204970b89aeb05e459e3fc2a4d4eabe4f7ee7a16e20d9004c64bcc5187b9332811",
"308203490201013047060a2a811ccf55060104020106072a811ccf5501680430016ca58c339f32f5bbe2b0491d5087c9b5de0f5aef4b4d0726941a0c21d9795f51e802f2a1596cc909d1638067ca28cc308202f9060a2a811ccf550601040209048202e9308202e530820289a00302010202051040990809300c06082a811ccf550183750500305c310b300906035504061302434e3130302e060355040a0c274368696e612046696e616e6369616c2043657274696669636174696f6e20417574686f72697479311b301906035504030c1243464341205445535420534d32204f434131301e170d3230313131393038333131385a170d3235313131393038333131385a308189310b300906035504061302434e31173015060355040a0c0e434643412054455354204f434131310d300b060355040b0c045053424331193017060355040b0c104f7267616e697a6174696f6e616c2d323137303506035504030c2e30353140e982aee582a8e7babfe4b88ae694b6e58d95e59586e688b7404e353130313133303030313838373840313059301306072a8648ce3d020106082a811ccf5501822d0342000495510badce29f70be07df6e2b0ce75be124a56c08e82435e72b4aa6c17679f455a6892aadde2a6b7a58ca7b0e10ca78d3811ff27e9f728cd80d53c1b9a6461dba382010630820102301f0603551d230418301680146bfe18da8f423aa6b86db32e88833a34a2c130e1300c0603551d130101ff0402300030480603551d200441303f303d060860811c86ef2a01013031302f06082b060105050702011623687474703a2f2f7777772e636663612e636f6d2e636e2f75732f75732d31342e68746d30390603551d1f04323030302ea02ca02a8628687474703a2f2f7563726c2e636663612e636f6d2e636e2f534d322f63726c31343335362e63726c300e0603551d0f0101ff0404030206c0301d0603551d0e04160414f8863d94f4a13b915ef9321a83a0be23445a7735301d0603551d250416301406082b0601050507030206082b06010505070304300c06082a811ccf5501837505000348003045022100890d2b153df86bfa09c3012323e52cadfa1c6c6a61f280f79c8c22db71d1504202204970b89aeb05e459e3fc2a4d4eabe4f7ee7a16e20d9004c64bcc5187b9332811",
"3082034a0201013048060a2a811ccf55060104020106082a811ccf550168010430016ca58c339f32f5bbe2b0491d5087c9b5de0f5aef4b4d0726941a0c21d9795f51e802f2a1596cc909d1638067ca28cc308202f9060a2a811ccf550601040201048202e9308202e530820289a00302010202051040990809300c06082a811ccf550183750500305c310b300906035504061302434e3130302e060355040a0c274368696e612046696e616e6369616c2043657274696669636174696f6e20417574686f72697479311b301906035504030c1243464341205445535420534d32204f434131301e170d3230313131393038333131385a170d3235313131393038333131385a308189310b300906035504061302434e31173015060355040a0c0e434643412054455354204f434131310d300b060355040b0c045053424331193017060355040b0c104f7267616e697a6174696f6e616c2d323137303506035504030c2e30353140e982aee582a8e7babfe4b88ae694b6e58d95e59586e688b7404e353130313133303030313838373840313059301306072a8648ce3d020106082a811ccf5501822d0342000495510badce29f70be07df6e2b0ce75be124a56c08e82435e72b4aa6c17679f455a6892aadde2a6b7a58ca7b0e10ca78d3811ff27e9f728cd80d53c1b9a6461dba382010630820102301f0603551d230418301680146bfe18da8f423aa6b86db32e88833a34a2c130e1300c0603551d130101ff0402300030480603551d200441303f303d060860811c86ef2a01013031302f06082b060105050702011623687474703a2f2f7777772e636663612e636f6d2e636e2f75732f75732d31342e68746d30390603551d1f04323030302ea02ca02a8628687474703a2f2f7563726c2e636663612e636f6d2e636e2f534d322f63726c31343335362e63726c300e0603551d0f0101ff0404030206c0301d0603551d0e04160414f8863d94f4a13b915ef9321a83a0be23445a7735301d0603551d250416301406082b0601050507030206082b06010505070304300c06082a811ccf5501837505000348003045022100890d2b153df86bfa09c3012323e52cadfa1c6c6a61f280f79c8c22db71d1504202204970b89aeb05e459e3fc2a4d4eabe4f7ee7a16e20d9004c64bcc5187b9332811",
}
for _, testcase := range testcases {
der, _ := hex.DecodeString(testcase)
_, _, err := ParseSM2(password, der)
if err == nil {
t.Fatal("expected error")
}
}
}
func TestParseSM2WithInvalidPwd(t *testing.T) {
password := []byte("wrongpwd")
der, _ := hex.DecodeString("308203490201013047060a2a811ccf55060104020106072a811ccf5501680430016ca58c339f32f5bbe2b0491d5087c9b5de0f5aef4b4d0726941a0c21d9795f51e802f2a1596cc909d1638067ca28cc308202f9060a2a811ccf550601040201048202e9308202e530820289a00302010202051040990809300c06082a811ccf550183750500305c310b300906035504061302434e3130302e060355040a0c274368696e612046696e616e6369616c2043657274696669636174696f6e20417574686f72697479311b301906035504030c1243464341205445535420534d32204f434131301e170d3230313131393038333131385a170d3235313131393038333131385a308189310b300906035504061302434e31173015060355040a0c0e434643412054455354204f434131310d300b060355040b0c045053424331193017060355040b0c104f7267616e697a6174696f6e616c2d323137303506035504030c2e30353140e982aee582a8e7babfe4b88ae694b6e58d95e59586e688b7404e353130313133303030313838373840313059301306072a8648ce3d020106082a811ccf5501822d0342000495510badce29f70be07df6e2b0ce75be124a56c08e82435e72b4aa6c17679f455a6892aadde2a6b7a58ca7b0e10ca78d3811ff27e9f728cd80d53c1b9a6461dba382010630820102301f0603551d230418301680146bfe18da8f423aa6b86db32e88833a34a2c130e1300c0603551d130101ff0402300030480603551d200441303f303d060860811c86ef2a01013031302f06082b060105050702011623687474703a2f2f7777772e636663612e636f6d2e636e2f75732f75732d31342e68746d30390603551d1f04323030302ea02ca02a8628687474703a2f2f7563726c2e636663612e636f6d2e636e2f534d322f63726c31343335362e63726c300e0603551d0f0101ff0404030206c0301d0603551d0e04160414f8863d94f4a13b915ef9321a83a0be23445a7735301d0603551d250416301406082b0601050507030206082b06010505070304300c06082a811ccf5501837505000348003045022100890d2b153df86bfa09c3012323e52cadfa1c6c6a61f280f79c8c22db71d1504202204970b89aeb05e459e3fc2a4d4eabe4f7ee7a16e20d9004c64bcc5187b9332811")
_, _, err := ParseSM2(password, der)
if err == nil || err.Error() != "cfca: failed to decrypt by SM4-CBC, please ensure the password is correct: padding: invalid padding byte/length" {
t.Fatal("cfca: failed to decrypt by SM4-CBC, please ensure the password is correct: padding: invalid padding byte/length")
}
}
func TestMarshalSM2WithoutPwd(t *testing.T) {
priv, cert, err := parseTestKeyAndCert()
if err != nil {
t.Fatal(err)
}
_, err = MarshalSM2(nil, priv, cert)
if err == nil || err.Error() != "cfca: invalid password" {
t.Fatal(err)
}
}
func TestParseSM2Mismatch(t *testing.T) {
password := []byte("changeit")
privKey, _ := hex.DecodeString("6c5a0a0b2eed3cbec3e4f1252bfe0e28c504a1c6bf1999eebb0af9ef0f8e6c85")
d := new(big.Int).SetBytes(privKey)
testkey := new(sm2.PrivateKey)
testkey.Curve = sm2.P256()
testkey.D = d
testkey.PublicKey.X, testkey.PublicKey.Y = testkey.ScalarBaseMult(testkey.D.Bytes())
_, cert, err := parseTestKeyAndCert()
if err != nil {
t.Fatal(err)
}
result, err := MarshalSM2(password, testkey, cert)
if err != nil {
t.Fatal(err)
}
_, _, err = ParseSM2(password, result)
if err == nil || err.Error() != "cfca: public key and private key do not match" {
t.Fatal("expected cfca: public key and private key do not match")
}
}

View File

@ -1,53 +0,0 @@
// Copyright 2024 Sun Yimin. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package cfca
import (
"crypto"
"github.com/emmansun/gmsm/pkcs"
"github.com/emmansun/gmsm/pkcs7"
"github.com/emmansun/gmsm/smx509"
)
// EnvelopeMessage creates and returns an envelope data PKCS7 structure (DER encoded) with encrypted
// recipient keys for each recipient public key.
//
// The OIDs use GM/T 0010 - 2012 set and the encrypted key uses ASN.1 format.
// This function uses recipient's SubjectKeyIdentifier to identify the recipient.
func EnvelopeMessage(cipher pkcs.Cipher, content []byte, recipients []*smx509.Certificate) ([]byte, error) {
return pkcs7.EnvelopeMessageCFCA(cipher, content, recipients)
}
// OpenEnvelopedMessage decrypts the enveloped message (DER encoded) using the provided certificate and private key.
// The certificate is used to identify the recipient and the private key is used to decrypt the encrypted key.
func OpenEnvelopedMessage(data []byte, recipientCert *smx509.Certificate, key crypto.PrivateKey) ([]byte, error) {
p7, err := pkcs7.Parse(data)
if err != nil {
return nil, err
}
return p7.Decrypt(recipientCert, key)
}
// EnvelopeMessageLegacy creates and returns an envelope data PKCS7 structure (DER encoded) with encrypted
// recipient keys for each recipient public key. This method is used for CFCA SADK verion less than 3.2 compatibility.
//
// The OIDs use GM/T 0010 - 2012 set and the encrypted key use C1C2C3 format and without 0x4 prefix.
// This function uses recipient's IssuerAndSerialNumber to identify the recipient.
func EnvelopeMessageLegacy(cipher pkcs.Cipher, content []byte, recipients []*smx509.Certificate) ([]byte, error) {
return pkcs7.EncryptCFCA(cipher, content, recipients)
}
// OpenEnvelopedMessageLegacy decrypts the enveloped message (DER encoded) using the provided certificate and private key.
// The certificate is used to identify the recipient and the private key is used to decrypt the encrypted key.
//
// This method is used for CFCA SADK verion less than 3.2 compatibility.
func OpenEnvelopedMessageLegacy(data []byte, recipientCert *smx509.Certificate, key crypto.PrivateKey) ([]byte, error) {
p7, err := pkcs7.Parse(data)
if err != nil {
return nil, err
}
return p7.DecryptCFCA(recipientCert, key)
}

View File

@ -1,171 +0,0 @@
// Copyright 2024 Sun Yimin. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package cfca
import (
"bytes"
"crypto"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"fmt"
"math/big"
"testing"
"time"
"github.com/emmansun/gmsm/pkcs"
"github.com/emmansun/gmsm/sm2"
"github.com/emmansun/gmsm/smx509"
)
type certKeyPair struct {
Certificate *smx509.Certificate
PrivateKey crypto.PrivateKey
}
func createTestSM2Certificate(allCA bool) (certKeyPair, error) {
signer, err := createTestSM2CertificateByIssuer("Eddard Stark", nil, smx509.SM2WithSM3, true)
if err != nil {
return certKeyPair{}, err
}
pair, err := createTestSM2CertificateByIssuer("Jon Snow", signer, smx509.SM2WithSM3, allCA)
if err != nil {
return certKeyPair{}, err
}
return *pair, nil
}
func createTestSM2CertificateByIssuer(name string, issuer *certKeyPair, sigAlg x509.SignatureAlgorithm, isCA bool) (*certKeyPair, error) {
var (
err error
priv crypto.PrivateKey
derCert []byte
issuerCert *smx509.Certificate
issuerKey crypto.PrivateKey
)
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 32)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
return nil, err
}
template := x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
CommonName: name,
Organization: []string{"Acme Co"},
},
NotBefore: time.Now().Add(-1 * time.Second),
NotAfter: time.Now().AddDate(1, 0, 0),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageEmailProtection},
}
if issuer != nil {
issuerCert = issuer.Certificate
issuerKey = issuer.PrivateKey
}
switch sigAlg {
case smx509.SM2WithSM3:
priv, err = sm2.GenerateKey(rand.Reader)
if err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("unsupported signature algorithm %v", sigAlg)
}
if isCA {
template.IsCA = true
template.KeyUsage |= x509.KeyUsageCertSign
template.BasicConstraintsValid = true
}
if issuer == nil {
// no issuer given,make this a self-signed root cert
issuerCert = (*smx509.Certificate)(&template)
issuerKey = priv
}
switch pkey := priv.(type) {
case *sm2.PrivateKey:
derCert, err = smx509.CreateCertificate(rand.Reader, &template, (*x509.Certificate)(issuerCert), pkey.Public(), issuerKey)
default:
return nil, fmt.Errorf("unsupported private key type %T", pkey)
}
if err != nil {
return nil, err
}
if len(derCert) == 0 {
return nil, fmt.Errorf("no certificate created, probably due to wrong keys. types were %T and %T", priv, issuerKey)
}
cert, err := smx509.ParseCertificate(derCert)
if err != nil {
return nil, err
}
// pem.Encode(os.Stdout, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})
return &certKeyPair{
Certificate: cert,
PrivateKey: priv,
}, nil
}
func TestEnvelopeMessage(t *testing.T) {
ciphers := []pkcs.Cipher{
pkcs.SM4,
pkcs.SM4CBC,
}
for _, cipher := range ciphers {
plaintext := []byte("Hello Secret World!")
cert, err := createTestSM2Certificate(true)
if err != nil {
t.Fatal(err)
}
encrypted, err := EnvelopeMessage(cipher, plaintext, []*smx509.Certificate{cert.Certificate})
if err != nil {
t.Fatal(err)
}
_, err = OpenEnvelopedMessage(encrypted[:len(encrypted)-1], cert.Certificate, cert.PrivateKey)
if err == nil {
t.Fatalf("expected error when decrypting with wrong key, got nil")
}
// pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: encrypted})
result, err := OpenEnvelopedMessage(encrypted, cert.Certificate, cert.PrivateKey)
if err != nil {
t.Fatalf("cannot Decrypt encrypted result: %v", err)
}
if !bytes.Equal(plaintext, result) {
t.Errorf("encrypted data does not match plaintext:\n\tExpected: %s\n\tActual: %s", plaintext, result)
}
}
}
func TestEnvelopeMessageLegacy(t *testing.T) {
ciphers := []pkcs.Cipher{
pkcs.SM4,
pkcs.SM4CBC,
}
for _, cipher := range ciphers {
plaintext := []byte("Hello Secret World!")
cert, err := createTestSM2Certificate(false)
if err != nil {
t.Fatal(err)
}
encrypted, err := EnvelopeMessageLegacy(cipher, plaintext, []*smx509.Certificate{cert.Certificate})
if err != nil {
t.Fatal(err)
}
_, err = OpenEnvelopedMessage(encrypted[:len(encrypted)-1], cert.Certificate, cert.PrivateKey)
if err == nil {
t.Fatalf("expected error when decrypting with wrong key, got nil")
}
// pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: encrypted})
result, err := OpenEnvelopedMessageLegacy(encrypted, cert.Certificate, cert.PrivateKey)
if err != nil {
t.Fatalf("cannot Decrypt encrypted result: %v", err)
}
if !bytes.Equal(plaintext, result) {
t.Errorf("encrypted data does not match plaintext:\n\tExpected: %s\n\tActual: %s", plaintext, result)
}
}
}

View File

@ -1,83 +0,0 @@
// Copyright 2024 Sun Yimin. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package cfca
import (
"crypto"
"github.com/emmansun/gmsm/pkcs7"
"github.com/emmansun/gmsm/smx509"
)
func signMessage(data []byte, cert *smx509.Certificate, key crypto.PrivateKey, detached bool) ([]byte, error) {
signData, _ := pkcs7.NewSMSignedData(data)
if err := signData.SignWithoutAttr(cert, key, pkcs7.SignerInfoConfig{}); err != nil {
return nil, err
}
if detached {
signData.Detach()
}
return signData.Finish()
}
// SignMessageAttach signs the data with the certificate and private key, returns the signed data in PKCS7 (DER) format.
// This method corresponds to CFCA SADK's cfca.sadk.util.p7SignMessageAttach.
func SignMessageAttach(data []byte, cert *smx509.Certificate, key crypto.PrivateKey) ([]byte, error) {
return signMessage(data, cert, key, false)
}
// VerifyMessageAttach verifies the signed data in PKCS7 (DER) format.
// This method corresponds to CFCA SADK's cfca.sadk.util.p7VerifyMessageAttach.
// If verification fails, an error is returned. otherwise, nil is returned.
func VerifyMessageAttach(p7Der []byte) error {
p7, err := pkcs7.Parse(p7Der)
if err != nil {
return err
}
return p7.Verify()
}
// SignMessageDetach signs the data with the certificate and private key, returns the signed data in PKCS7 (DER) format.
// This method corresponds to CFCA SADK's cfca.sadk.util.p7SignMessageDetach.
func SignMessageDetach(data []byte, cert *smx509.Certificate, key crypto.PrivateKey) ([]byte, error) {
return signMessage(data, cert, key, true)
}
// VerifyMessageDetach verifies the signed data in PKCS7 (DER) format with the given source data.
// This method corresponds to CFCA SADK's cfca.sadk.util.p7VerifyMessageDetach.
// If verification fails, an error is returned. otherwise, nil is returned.
func VerifyMessageDetach(p7Der, sourceData []byte) error {
p7, err := pkcs7.Parse(p7Der)
if err != nil {
return err
}
p7.Content = sourceData
return p7.Verify()
}
// SignDigestDetach signs a given digest using the provided certificate and private key,
// and returns the detached PKCS7 signature.
//
// This method corresponds to CFCA SADK's cfca.sadk.util.p7SignByHash.
func SignDigestDetach(digest []byte, cert *smx509.Certificate, key crypto.PrivateKey) ([]byte, error) {
signData, _ := pkcs7.NewSMSignedDataWithDigest(digest)
if err := signData.SignWithoutAttr(cert, key, pkcs7.SignerInfoConfig{}); err != nil {
return nil, err
}
return signData.Finish()
}
// VerifyDigestDetach verifies a detached PKCS7 signature against a given digest.
// It parses the p7Der, assigns the provided digest to the parsed PKCS7 content, and then verifies it.
//
// This method corresponds to CFCA SADK's cfca.sadk.util.p7VerifyByHash.
func VerifyDigestDetach(p7Der, digest []byte) error {
p7, err := pkcs7.Parse(p7Der)
if err != nil {
return err
}
p7.Content = digest
return p7.VerifyAsDigest()
}

View File

@ -1,122 +0,0 @@
// Copyright 2024 Sun Yimin. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package cfca
import (
"crypto/ecdsa"
"encoding/base64"
"testing"
"github.com/emmansun/gmsm/sm2"
)
func TestSignMessageAttach(t *testing.T) {
_, err := SignMessageAttach(nil, nil, nil)
if err == nil {
t.Fatalf("SignMessageAttach() error = %v, wantErr %v", err, true)
}
pair, err := createTestSM2Certificate(false)
if err != nil {
t.Fatal(err)
}
_, err = SignMessageAttach([]byte("test"), pair.Certificate, nil)
if err == nil {
t.Fatalf("SignMessageAttach() error = %v, wantErr %v", err, true)
}
p7, err := SignMessageAttach([]byte("test"), pair.Certificate, pair.PrivateKey)
if err != nil {
t.Fatal(err)
}
p7[0] = 0x20
err = VerifyMessageAttach(p7)
if err == nil {
t.Fatalf("VerifyMessageAttach() error = %v, wantErr %v", err, true)
}
p7[0] = 0x30
err = VerifyMessageAttach(p7)
if err != nil {
t.Fatal(err)
}
p7, _ = base64.StdEncoding.DecodeString(sadkSignedData)
err = VerifyMessageAttach(p7)
if err != nil {
t.Fatal(err)
}
}
func TestSignMessageDetach(t *testing.T) {
_, err := SignMessageDetach(nil, nil, nil)
if err == nil {
t.Fatalf("SignMessageAttach() error = %v, wantErr %v", err, true)
}
pair, err := createTestSM2Certificate(false)
if err != nil {
t.Fatal(err)
}
_, err = SignMessageDetach([]byte("test"), pair.Certificate, nil)
if err == nil {
t.Fatalf("SignMessageAttach() error = %v, wantErr %v", err, true)
}
p7, err := SignMessageDetach([]byte("test"), pair.Certificate, pair.PrivateKey)
if err != nil {
t.Fatal(err)
}
p7[0] = 0x20
err = VerifyMessageDetach(p7, []byte("test"))
if err == nil {
t.Fatalf("VerifyMessageAttach() error = %v, wantErr %v", err, true)
}
p7[0] = 0x30
err = VerifyMessageDetach(p7, []byte("test"))
if err != nil {
t.Fatal(err)
}
err = VerifyMessageDetach(p7, []byte("test 1"))
if err == nil || err.Error() != "x509: SM2 verification failure" {
t.Fatalf("VerifyMessageAttach() error = %v, wantErr %v", err, true)
}
err = VerifyMessageDetach(p7, nil)
if err == nil || err.Error() != "x509: SM2 verification failure" {
t.Fatalf("VerifyMessageAttach() error = %v, wantErr %v", err, true)
}
p7, _ = base64.StdEncoding.DecodeString(sadkSignedDataDetach)
err = VerifyMessageDetach(p7, []byte("Hello Secret World!"))
if err != nil {
t.Fatal(err)
}
}
var sadkSignedData = "MIICgAYKKoEcz1UGAQQCAqCCAnAwggJsAgEBMQ4wDAYIKoEcz1UBgxEFADAjBgoqgRzPVQYBBAIBoBUEE0hlbGxvIFNlY3JldCBXb3JsZCGgggGNMIIBiTCCAS+gAwIBAgIFAKncGpAwCgYIKoEcz1UBg3UwKTEQMA4GA1UEChMHQWNtZSBDbzEVMBMGA1UEAxMMRWRkYXJkIFN0YXJrMB4XDTI0MTExOTAwMTIyNVoXDTI1MTExOTAwMTIyNlowJTEQMA4GA1UEChMHQWNtZSBDbzERMA8GA1UEAxMISm9uIFNub3cwWTATBgcqhkjOPQIBBggqgRzPVQGCLQNCAATYcgrHXJmFO1/t/9WQ6GkCW6D0yDyd2ya5wRXjVAU08I9Oo6k99jB2MPauCn64W81APRCPHLlwWOtuIsmSmQhjo0gwRjAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwQwHwYDVR0jBBgwFoAUyBfYaeGJxaf9ST9aCRgotC+MwvwwCgYIKoEcz1UBg3UDSAAwRQIgRaF0PA74cCYKeu8pZ4VDQti+rE283Hq/tGXzXUzOWKUCIQDl3z1boZxtRscbnOGOXg1NY+yoY2lz5b63kGOTkn/SxzGBoDCBnQIBATAyMCkxEDAOBgNVBAoTB0FjbWUgQ28xFTATBgNVBAMTDEVkZGFyZCBTdGFyawIFAKncGpAwDAYIKoEcz1UBgxEFADANBgkqgRzPVQGCLQEFAARHMEUCIQCl145xtYc7QWTymATxUGbLfF1mlPlyMoIKSp9alu14UQIgQSV/Ll3yYCyXSNxhPelz8Nsbxopky+Pt56Al54rv3p0="
var sadkSignedDataDetach = "MIICaQYKKoEcz1UGAQQCAqCCAlkwggJVAgEBMQ4wDAYIKoEcz1UBgxEFADAMBgoqgRzPVQYBBAIBoIIBjTCCAYkwggEvoAMCAQICBQCp3BqQMAoGCCqBHM9VAYN1MCkxEDAOBgNVBAoTB0FjbWUgQ28xFTATBgNVBAMTDEVkZGFyZCBTdGFyazAeFw0yNDExMTkwMDEyMjVaFw0yNTExMTkwMDEyMjZaMCUxEDAOBgNVBAoTB0FjbWUgQ28xETAPBgNVBAMTCEpvbiBTbm93MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE2HIKx1yZhTtf7f/VkOhpAlug9Mg8ndsmucEV41QFNPCPTqOpPfYwdjD2rgp+uFvNQD0Qjxy5cFjrbiLJkpkIY6NIMEYwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMEMB8GA1UdIwQYMBaAFMgX2GnhicWn/Uk/WgkYKLQvjML8MAoGCCqBHM9VAYN1A0gAMEUCIEWhdDwO+HAmCnrvKWeFQ0LYvqxNvNx6v7Rl811MzlilAiEA5d89W6GcbUbHG5zhjl4NTWPsqGNpc+W+t5Bjk5J/0scxgaAwgZ0CAQEwMjApMRAwDgYDVQQKEwdBY21lIENvMRUwEwYDVQQDEwxFZGRhcmQgU3RhcmsCBQCp3BqQMAwGCCqBHM9VAYMRBQAwDQYJKoEcz1UBgi0BBQAERzBFAiEA4ylCl8qQDfNDfBw7VkxVN0bUs4N56TZDqZhAdEv01N8CIDtOG5VbmWNZeagC8VRfzEhu+ratFCo3fTu2liV8kH5h"
func TestSignDigestDetach(t *testing.T) {
_, err := SignDigestDetach(nil, nil, nil)
if err == nil {
t.Fatalf("SignDigestDetach() error = %v, wantErr %v", err, true)
}
pair, err := createTestSM2Certificate(false)
if err != nil {
t.Fatal(err)
}
rawMessage := []byte("test")
digest, err := sm2.CalculateSM2Hash(pair.Certificate.PublicKey.(*ecdsa.PublicKey), rawMessage, nil)
if err != nil {
t.Fatal(err)
}
p7, err := SignDigestDetach(digest, pair.Certificate, pair.PrivateKey)
if err != nil {
t.Fatal(err)
}
err = VerifyDigestDetach(p7, digest)
if err != nil {
t.Fatal(err)
}
err = VerifyMessageDetach(p7, rawMessage)
if err != nil {
t.Fatal(err)
}
}

View File

@ -1,132 +0,0 @@
// Block Chaining operation mode (BC mode) in Chinese national standard GB/T 17964-2021.
// See GB/T 17964-2021 Chapter 12.
package cipher
import (
"bytes"
"crypto/cipher"
"crypto/subtle"
)
type bc struct {
b cipher.Block
blockSize int
iv []byte
}
func newBC(b cipher.Block, iv []byte) *bc {
return &bc{
b: b,
blockSize: b.BlockSize(),
iv: bytes.Clone(iv),
}
}
type bcEncrypter bc
// bcEncAble is an interface implemented by ciphers that have a specific
// optimized implementation of BC encryption.
// NewBCEncrypter will check for this interface and return the specific
// BlockMode if found.
type bcEncAble interface {
NewBCEncrypter(iv []byte) cipher.BlockMode
}
// NewBCEncrypter returns a BlockMode which encrypts in block chaining
// mode, using the given Block. The length of iv must be the same as the
// Block's block size.
func NewBCEncrypter(b cipher.Block, iv []byte) cipher.BlockMode {
if len(iv) != b.BlockSize() {
panic("cipher.NewBCEncrypter: IV length must equal block size")
}
if bc, ok := b.(bcEncAble); ok {
return bc.NewBCEncrypter(iv)
}
return (*bcEncrypter)(newBC(b, iv))
}
func (x *bcEncrypter) BlockSize() int { return x.blockSize }
func (x *bcEncrypter) CryptBlocks(dst, src []byte) {
validate(x.blockSize, dst, src)
iv := x.iv
for len(src) > 0 {
// Write the xor to dst, then encrypt in place.
subtle.XORBytes(dst[:x.blockSize], src[:x.blockSize], iv)
x.b.Encrypt(dst[:x.blockSize], dst[:x.blockSize])
subtle.XORBytes(iv, iv, dst[:x.blockSize])
src = src[x.blockSize:]
dst = dst[x.blockSize:]
}
// Save the iv for the next CryptBlocks call.
copy(x.iv, iv)
}
func (x *bcEncrypter) SetIV(iv []byte) {
if len(iv) != len(x.iv) {
panic("cipher: incorrect length IV")
}
copy(x.iv, iv)
}
type bcDecrypter bc
// bcDecAble is an interface implemented by ciphers that have a specific
// optimized implementation of BC decryption.
// NewBCDecrypter will check for this interface and return the specific
// BlockMode if found.
type bcDecAble interface {
NewBCDecrypter(iv []byte) cipher.BlockMode
}
// NewBCDecrypter returns a BlockMode which decrypts in block chaining
// mode, using the given Block. The length of iv must be the same as the
// Block's block size and must match the iv used to encrypt the data.
func NewBCDecrypter(b cipher.Block, iv []byte) cipher.BlockMode {
if len(iv) != b.BlockSize() {
panic("cipher.NewBCDecrypter: IV length must equal block size")
}
if bc, ok := b.(bcDecAble); ok {
return bc.NewBCDecrypter(iv)
}
return (*bcDecrypter)(newBC(b, iv))
}
func (x *bcDecrypter) BlockSize() int { return x.blockSize }
func (x *bcDecrypter) CryptBlocks(dst, src []byte) {
validate(x.blockSize, dst, src)
if len(src) == 0 {
return
}
iv := x.iv
nextIV := make([]byte, x.blockSize)
for len(src) > 0 {
// Get F(i+1)
subtle.XORBytes(nextIV, iv, src[:x.blockSize])
// Get plaintext P(i)
x.b.Decrypt(dst[:x.blockSize], src[:x.blockSize])
subtle.XORBytes(dst[:x.blockSize], dst[:x.blockSize], iv)
copy(iv, nextIV)
src = src[x.blockSize:]
dst = dst[x.blockSize:]
}
// Save the iv for the next CryptBlocks call.
copy(x.iv, iv)
}
func (x *bcDecrypter) SetIV(iv []byte) {
if len(iv) != len(x.iv) {
panic("cipher: incorrect length IV")
}
copy(x.iv, iv)
}

View File

@ -1,100 +0,0 @@
package cipher_test
import (
"bytes"
"crypto/rand"
"encoding/hex"
"io"
mrand "math/rand"
"testing"
"time"
"github.com/emmansun/gmsm/cipher"
"github.com/emmansun/gmsm/internal/cryptotest"
"github.com/emmansun/gmsm/sm4"
)
var bcSM4TestVectors = []struct {
key string
iv string
plaintext string
ciphertext string
}{
{
"2B7E151628AED2A6ABF7158809CF4F3C",
"000102030405060708090A0B0C0D0E0F",
"6BC1BEE22E409F96E93D7E117393172AAE2D8A571E03AC9C9EB76FAC45AF8E5130C81C46A35CE411E5FBC1191A0A52EFF69F2445DF4F9B17AD2B417BE66C3710",
"AC529AF989A62FCE9CDDC5FFB84125CAFB8CDE77339FFE481D113C40BBD5B6786FFC9916F98F94FF12D78319707E240428718707605BC1EAC503153EBAA0FB1D",
},
}
func TestBC(t *testing.T) {
for i, test := range bcSM4TestVectors {
key, _ := hex.DecodeString(test.key)
iv, _ := hex.DecodeString(test.iv)
plaintext, _ := hex.DecodeString(test.plaintext)
ciphertext, _ := hex.DecodeString(test.ciphertext)
got := make([]byte, len(plaintext))
c, err := sm4.NewCipher(key)
if err != nil {
t.Fatal(err)
}
encrypter := cipher.NewBCEncrypter(c, iv)
encrypter.CryptBlocks(got, plaintext)
if !bytes.Equal(got, ciphertext) {
t.Fatalf("%v case encrypt failed, got %x\n", i+1, got)
}
decrypter := cipher.NewBCDecrypter(c, iv)
decrypter.CryptBlocks(got, ciphertext)
if !bytes.Equal(got, plaintext) {
t.Fatalf("%v case decrypt failed, got %x\n", i+1, got)
}
}
}
func TestSM4BCRandom(t *testing.T) {
key, _ := hex.DecodeString(bcSM4TestVectors[0].key)
iv := []byte("0123456789ABCDEF")
c, err := sm4.NewCipher(key)
if err != nil {
t.Fatal(err)
}
encrypter := cipher.NewBCEncrypter(c, iv)
decrypter := cipher.NewBCDecrypter(c, iv)
for i := 1; i <= 50; i++ {
plaintext := make([]byte, i*16)
ciphertext := make([]byte, i*16)
got := make([]byte, i*16)
io.ReadFull(rand.Reader, plaintext)
encrypter.CryptBlocks(ciphertext, plaintext)
decrypter.CryptBlocks(got, ciphertext)
if !bytes.Equal(got, plaintext) {
t.Errorf("test %v blocks failed", i)
}
}
}
// Test BC Blockmode against the general cipher.BlockMode interface tester
func TestBCBlockMode(t *testing.T) {
t.Run("SM4", func(t *testing.T) {
rng := newRandReader(t)
key := make([]byte, 16)
rng.Read(key)
block, err := sm4.NewCipher(key)
if err != nil {
panic(err)
}
cryptotest.TestBlockMode(t, block, cipher.NewBCEncrypter, cipher.NewBCDecrypter)
})
}
func newRandReader(t *testing.T) io.Reader {
seed := time.Now().UnixNano()
t.Logf("Deterministic RNG seed: 0x%x", seed)
return mrand.New(mrand.NewSource(seed))
}

View File

@ -3,83 +3,15 @@ package cipher_test
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"io"
"testing"
smcipher "github.com/emmansun/gmsm/cipher"
"github.com/emmansun/gmsm/sm4"
)
func BenchmarkSM4BCEncrypt1K(b *testing.B) {
var key [16]byte
c, _ := sm4.NewCipher(key[:])
benchmarkBCEncrypt(b, c, make([]byte, 1024))
}
func benchmarkBCEncrypt(b *testing.B, block cipher.Block, buf []byte) {
b.SetBytes(int64(len(buf)))
var iv [16]byte
bc := smcipher.NewBCEncrypter(block, iv[:])
for i := 0; i < b.N; i++ {
bc.CryptBlocks(buf, buf)
}
}
func BenchmarkSM4BCDecrypt1K(b *testing.B) {
var key [16]byte
c, _ := sm4.NewCipher(key[:])
benchmarkBCDecrypt(b, c, make([]byte, 1024))
}
func benchmarkBCDecrypt(b *testing.B, block cipher.Block, buf []byte) {
b.SetBytes(int64(len(buf)))
var iv [16]byte
bc := smcipher.NewBCDecrypter(block, iv[:])
for i := 0; i < b.N; i++ {
bc.CryptBlocks(buf, buf)
}
}
func BenchmarkSM4HCTREncrypt1K(b *testing.B) {
var key [16]byte
var tweak [32]byte
c, _ := sm4.NewCipher(key[:])
io.ReadFull(rand.Reader, tweak[:])
hctr, _ := smcipher.NewHCTR(c, tweak[:16], tweak[16:])
func benchmarkCBCEncrypt1K(b *testing.B, block cipher.Block) {
buf := make([]byte, 1024)
b.SetBytes(int64(len(buf)))
b.ResetTimer()
for i := 0; i < b.N; i++ {
hctr.EncryptBytes(buf, buf)
}
}
func benchmarkECBEncrypt(b *testing.B, block cipher.Block, buf []byte) {
b.SetBytes(int64(len(buf)))
ecb := smcipher.NewECBEncrypter(block)
for i := 0; i < b.N; i++ {
ecb.CryptBlocks(buf, buf)
}
}
func BenchmarkSM4ECBEncrypt1K(b *testing.B) {
var key [16]byte
c, _ := sm4.NewCipher(key[:])
benchmarkECBEncrypt(b, c, make([]byte, 1024))
}
func BenchmarkAES128ECBEncrypt1K(b *testing.B) {
var key [16]byte
c, _ := aes.NewCipher(key[:])
benchmarkECBEncrypt(b, c, make([]byte, 1024))
}
func benchmarkCBCEncrypt(b *testing.B, block cipher.Block, buf []byte) {
b.SetBytes(int64(len(buf)))
var iv [16]byte
cbc := cipher.NewCBCEncrypter(block, iv[:])
@ -91,22 +23,17 @@ func benchmarkCBCEncrypt(b *testing.B, block cipher.Block, buf []byte) {
func BenchmarkAESCBCEncrypt1K(b *testing.B) {
var key [16]byte
c, _ := aes.NewCipher(key[:])
benchmarkCBCEncrypt(b, c, make([]byte, 1024))
benchmarkCBCEncrypt1K(b, c)
}
func BenchmarkSM4CBCEncrypt1K(b *testing.B) {
var key [16]byte
c, _ := sm4.NewCipher(key[:])
benchmarkCBCEncrypt(b, c, make([]byte, 1024))
benchmarkCBCEncrypt1K(b, c)
}
func BenchmarkSM4CBCEncrypt8K(b *testing.B) {
var key [16]byte
c, _ := sm4.NewCipher(key[:])
benchmarkCBCEncrypt(b, c, make([]byte, 8*1024))
}
func benchmarkCBCDecrypt(b *testing.B, block cipher.Block, buf []byte) {
func benchmarkSM4CBCDecrypt1K(b *testing.B, block cipher.Block) {
buf := make([]byte, 1024)
b.SetBytes(int64(len(buf)))
var iv [16]byte
@ -119,19 +46,21 @@ func benchmarkCBCDecrypt(b *testing.B, block cipher.Block, buf []byte) {
func BenchmarkAESCBCDecrypt1K(b *testing.B) {
var key [16]byte
c, _ := aes.NewCipher(key[:])
benchmarkCBCDecrypt(b, c, make([]byte, 1024))
benchmarkSM4CBCDecrypt1K(b, c)
}
func BenchmarkSM4CBCDecrypt1K(b *testing.B) {
var key [16]byte
c, _ := sm4.NewCipher(key[:])
benchmarkCBCDecrypt(b, c, make([]byte, 1024))
benchmarkSM4CBCDecrypt1K(b, c)
}
func benchmarkStream(b *testing.B, block cipher.Block, mode func(cipher.Block, []byte) cipher.Stream, buf []byte) {
b.SetBytes(int64(len(buf)))
//var key [16]byte
var iv [16]byte
//c, _ := sm4.NewCipher(key[:])
stream := mode(block, iv[:])
b.ResetTimer()
@ -433,146 +362,54 @@ func benchmarkSM4CCMOpen(b *testing.B, buf []byte) {
benchmarkGCMOpen(b, sm4gcm, buf)
}
func benchmarkXTS(b *testing.B, isGB bool, cipherFunc func([]byte) (cipher.Block, error), length, keylen int64) {
func benchmarkXTS(b *testing.B, cipherFunc func([]byte) (cipher.Block, error), length, keylen int64) {
c, err := smcipher.NewXTS(cipherFunc, make([]byte, keylen))
if err != nil {
b.Fatalf("NewCipher failed: %s", err)
}
plaintext := make([]byte, length)
encrypted := make([]byte, length)
var c cipher.BlockMode
var err error
if !isGB {
c, err = smcipher.NewXTSEncrypterWithSector(cipherFunc, make([]byte, keylen), make([]byte, keylen), 0)
if err != nil {
b.Fatalf("NewCipher failed: %s", err)
}
} else {
c, err = smcipher.NewGBXTSEncrypterWithSector(cipherFunc, make([]byte, keylen), make([]byte, keylen), 0)
if err != nil {
b.Fatalf("NewCipher failed: %s", err)
}
}
//decrypted := make([]byte, length)
b.SetBytes(int64(len(plaintext)))
b.ResetTimer()
for i := 0; i < b.N; i++ {
c.CryptBlocks(encrypted, plaintext)
c.Encrypt(encrypted, plaintext, 0)
//c.Decrypt(decrypted, encrypted[:len(plaintext)], 0)
}
}
func BenchmarkAES128XTSEncrypt512(b *testing.B) {
benchmarkXTS(b, false, aes.NewCipher, 512, 16)
benchmarkXTS(b, aes.NewCipher, 512, 32)
}
func BenchmarkAES128XTSEncrypt1K(b *testing.B) {
benchmarkXTS(b, false, aes.NewCipher, 1024, 16)
benchmarkXTS(b, aes.NewCipher, 1024, 32)
}
func BenchmarkAES128XTSEncrypt4K(b *testing.B) {
benchmarkXTS(b, false, aes.NewCipher, 4096, 16)
benchmarkXTS(b, aes.NewCipher, 4096, 32)
}
func BenchmarkAES256XTSEncrypt512(b *testing.B) {
benchmarkXTS(b, false, aes.NewCipher, 512, 32)
benchmarkXTS(b, aes.NewCipher, 512, 64)
}
func BenchmarkAES256XTSEncrypt1K(b *testing.B) {
benchmarkXTS(b, false, aes.NewCipher, 1024, 32)
benchmarkXTS(b, aes.NewCipher, 1024, 64)
}
func BenchmarkAES256XTSEncrypt4K(b *testing.B) {
benchmarkXTS(b, false, aes.NewCipher, 4096, 32)
benchmarkXTS(b, aes.NewCipher, 4096, 64)
}
func BenchmarkSM4XTSEncrypt512(b *testing.B) {
benchmarkXTS(b, false, sm4.NewCipher, 512, 16)
benchmarkXTS(b, sm4.NewCipher, 512, 32)
}
func BenchmarkSM4XTSEncrypt1K(b *testing.B) {
benchmarkXTS(b, false, sm4.NewCipher, 1024, 16)
benchmarkXTS(b, sm4.NewCipher, 1024, 32)
}
func BenchmarkSM4XTSEncrypt4K(b *testing.B) {
benchmarkXTS(b, false, sm4.NewCipher, 4096, 16)
}
func BenchmarkSM4XTSEncrypt512_GB(b *testing.B) {
benchmarkXTS(b, true, sm4.NewCipher, 512, 16)
}
func BenchmarkSM4XTSEncrypt1K_GB(b *testing.B) {
benchmarkXTS(b, true, sm4.NewCipher, 1024, 16)
}
func BenchmarkSM4XTSEncrypt4K_GB(b *testing.B) {
benchmarkXTS(b, true, sm4.NewCipher, 4096, 16)
}
func benchmarkXTS_Decrypt(b *testing.B, isGB bool, cipherFunc func([]byte) (cipher.Block, error), length, keylen int64) {
plaintext := make([]byte, length)
encrypted := make([]byte, length)
var c cipher.BlockMode
var err error
if !isGB {
c, err = smcipher.NewXTSDecrypterWithSector(cipherFunc, make([]byte, keylen), make([]byte, keylen), 0)
if err != nil {
b.Fatalf("NewCipher failed: %s", err)
}
} else {
c, err = smcipher.NewGBXTSDecrypterWithSector(cipherFunc, make([]byte, keylen), make([]byte, keylen), 0)
if err != nil {
b.Fatalf("NewCipher failed: %s", err)
}
}
b.SetBytes(int64(len(plaintext)))
b.ResetTimer()
for i := 0; i < b.N; i++ {
c.CryptBlocks(plaintext, encrypted)
}
}
func BenchmarkAES128XTSDecrypt512(b *testing.B) {
benchmarkXTS_Decrypt(b, false, aes.NewCipher, 512, 16)
}
func BenchmarkAES128XTSDecrypt1K(b *testing.B) {
benchmarkXTS_Decrypt(b, false, aes.NewCipher, 1024, 16)
}
func BenchmarkAES128XTSDecrypt4K(b *testing.B) {
benchmarkXTS_Decrypt(b, false, aes.NewCipher, 4096, 16)
}
func BenchmarkAES256XTSDecrypt512(b *testing.B) {
benchmarkXTS_Decrypt(b, false, aes.NewCipher, 512, 32)
}
func BenchmarkAES256XTSDecrypt1K(b *testing.B) {
benchmarkXTS_Decrypt(b, false, aes.NewCipher, 1024, 32)
}
func BenchmarkAES256XTSDecrypt4K(b *testing.B) {
benchmarkXTS_Decrypt(b, false, aes.NewCipher, 4096, 32)
}
func BenchmarkSM4XTSDecrypt512(b *testing.B) {
benchmarkXTS_Decrypt(b, false, sm4.NewCipher, 512, 16)
}
func BenchmarkSM4XTSDecrypt1K(b *testing.B) {
benchmarkXTS_Decrypt(b, false, sm4.NewCipher, 1024, 16)
}
func BenchmarkSM4XTSDecrypt4K(b *testing.B) {
benchmarkXTS_Decrypt(b, false, sm4.NewCipher, 4096, 16)
}
func BenchmarkSM4XTSDecrypt512_GB(b *testing.B) {
benchmarkXTS_Decrypt(b, true, sm4.NewCipher, 512, 16)
}
func BenchmarkSM4XTSDecrypt1K_GB(b *testing.B) {
benchmarkXTS_Decrypt(b, true, sm4.NewCipher, 1024, 16)
}
func BenchmarkSM4XTSDecrypt4K_GB(b *testing.B) {
benchmarkXTS_Decrypt(b, true, sm4.NewCipher, 4096, 16)
benchmarkXTS(b, sm4.NewCipher, 4096, 32)
}

View File

@ -3,10 +3,8 @@ package cipher_test
import (
"bytes"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"fmt"
"io"
"testing"
"github.com/emmansun/gmsm/padding"
@ -99,153 +97,6 @@ var cbcSM4Tests = []struct {
0xf7, 0x90, 0x47, 0x74, 0xaf, 0x40, 0xfd, 0x72, 0xc6, 0x17, 0xeb, 0xc0, 0x8b, 0x01, 0x71, 0x5c,
},
},
{
"10 blocks",
[]byte("0123456789ABCDEF"),
[]byte("0123456789ABCDEF"),
[]byte("Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World"),
[]byte{
0xd3, 0x1e, 0x36, 0x83, 0xe4, 0xfc, 0x9b, 0x51, 0x6a, 0x2c, 0x0f, 0x98, 0x36, 0x76, 0xa9, 0xeb,
0x1f, 0xdc, 0xc3, 0x2a, 0xf3, 0x84, 0x08, 0x97, 0x81, 0x57, 0xa2, 0x06, 0x5d, 0xe3, 0x4c, 0x6a,
0xe0, 0x02, 0xd6, 0xe4, 0xf5, 0x66, 0x87, 0xc4, 0xcc, 0x54, 0x1d, 0x1f, 0x1c, 0xc4, 0x2f, 0xe6,
0xe5, 0x1d, 0xea, 0x52, 0xb8, 0x0c, 0xc8, 0xbe, 0xae, 0xcc, 0x44, 0xa8, 0x51, 0x81, 0x08, 0x60,
0xb6, 0x09, 0x7b, 0xb8, 0x7e, 0xdb, 0x53, 0x4b, 0xea, 0x2a, 0xc6, 0xa1, 0xe5, 0xa0, 0x2a, 0xe9,
0x22, 0x65, 0x5b, 0xa3, 0xb9, 0xcc, 0x63, 0x92, 0x16, 0x0e, 0x2f, 0xf4, 0x3b, 0x93, 0x06, 0x82,
0xb3, 0x8c, 0x26, 0x2e, 0x06, 0x51, 0x34, 0x2c, 0xe4, 0x3d, 0xd0, 0xc7, 0x2b, 0x8f, 0x31, 0x15,
0x30, 0xa8, 0x96, 0x1c, 0xbc, 0x8e, 0xf7, 0x4f, 0x6b, 0x69, 0x9d, 0xc9, 0x40, 0x89, 0xd7, 0xe8,
0x2a, 0xe8, 0xc3, 0x3d, 0xcb, 0x8a, 0x1c, 0xb3, 0x70, 0x7d, 0xe9, 0xe6, 0x88, 0x36, 0x65, 0x21,
0x97, 0x0d, 0x27, 0xc6, 0x74, 0x49, 0x05, 0xa8, 0x28, 0x73, 0x65, 0x48, 0xb8, 0x81, 0x37, 0x6b,
},
},
{
"11 blocks",
[]byte("0123456789ABCDEF"),
[]byte("0123456789ABCDEF"),
[]byte("Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World"),
[]byte{
0xd3, 0x1e, 0x36, 0x83, 0xe4, 0xfc, 0x9b, 0x51, 0x6a, 0x2c, 0x0f, 0x98, 0x36, 0x76, 0xa9, 0xeb,
0x1f, 0xdc, 0xc3, 0x2a, 0xf3, 0x84, 0x08, 0x97, 0x81, 0x57, 0xa2, 0x06, 0x5d, 0xe3, 0x4c, 0x6a,
0xe0, 0x02, 0xd6, 0xe4, 0xf5, 0x66, 0x87, 0xc4, 0xcc, 0x54, 0x1d, 0x1f, 0x1c, 0xc4, 0x2f, 0xe6,
0xe5, 0x1d, 0xea, 0x52, 0xb8, 0x0c, 0xc8, 0xbe, 0xae, 0xcc, 0x44, 0xa8, 0x51, 0x81, 0x08, 0x60,
0xb6, 0x09, 0x7b, 0xb8, 0x7e, 0xdb, 0x53, 0x4b, 0xea, 0x2a, 0xc6, 0xa1, 0xe5, 0xa0, 0x2a, 0xe9,
0x22, 0x65, 0x5b, 0xa3, 0xb9, 0xcc, 0x63, 0x92, 0x16, 0x0e, 0x2f, 0xf4, 0x3b, 0x93, 0x06, 0x82,
0xb3, 0x8c, 0x26, 0x2e, 0x06, 0x51, 0x34, 0x2c, 0xe4, 0x3d, 0xd0, 0xc7, 0x2b, 0x8f, 0x31, 0x15,
0x30, 0xa8, 0x96, 0x1c, 0xbc, 0x8e, 0xf7, 0x4f, 0x6b, 0x69, 0x9d, 0xc9, 0x40, 0x89, 0xd7, 0xe8,
0x2a, 0xe8, 0xc3, 0x3d, 0xcb, 0x8a, 0x1c, 0xb3, 0x70, 0x7d, 0xe9, 0xe6, 0x88, 0x36, 0x65, 0x21,
0x7b, 0x34, 0xac, 0x73, 0x8d, 0x4f, 0x11, 0xde, 0xd4, 0x21, 0x45, 0x9f, 0x1f, 0x3e, 0xe8, 0xcf,
0x73, 0x8e, 0x70, 0xa7, 0x00, 0xc4, 0x27, 0x67, 0xa0, 0x9f, 0x91, 0xf4, 0xe3, 0x9e, 0x76, 0x15,
},
},
{
"12 blocks",
[]byte("0123456789ABCDEF"),
[]byte("0123456789ABCDEF"),
[]byte("Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello"),
[]byte{
0xd3, 0x1e, 0x36, 0x83, 0xe4, 0xfc, 0x9b, 0x51, 0x6a, 0x2c, 0x0f, 0x98, 0x36, 0x76, 0xa9, 0xeb,
0x1f, 0xdc, 0xc3, 0x2a, 0xf3, 0x84, 0x08, 0x97, 0x81, 0x57, 0xa2, 0x06, 0x5d, 0xe3, 0x4c, 0x6a,
0xe0, 0x02, 0xd6, 0xe4, 0xf5, 0x66, 0x87, 0xc4, 0xcc, 0x54, 0x1d, 0x1f, 0x1c, 0xc4, 0x2f, 0xe6,
0xe5, 0x1d, 0xea, 0x52, 0xb8, 0x0c, 0xc8, 0xbe, 0xae, 0xcc, 0x44, 0xa8, 0x51, 0x81, 0x08, 0x60,
0xb6, 0x09, 0x7b, 0xb8, 0x7e, 0xdb, 0x53, 0x4b, 0xea, 0x2a, 0xc6, 0xa1, 0xe5, 0xa0, 0x2a, 0xe9,
0x22, 0x65, 0x5b, 0xa3, 0xb9, 0xcc, 0x63, 0x92, 0x16, 0x0e, 0x2f, 0xf4, 0x3b, 0x93, 0x06, 0x82,
0xb3, 0x8c, 0x26, 0x2e, 0x06, 0x51, 0x34, 0x2c, 0xe4, 0x3d, 0xd0, 0xc7, 0x2b, 0x8f, 0x31, 0x15,
0x30, 0xa8, 0x96, 0x1c, 0xbc, 0x8e, 0xf7, 0x4f, 0x6b, 0x69, 0x9d, 0xc9, 0x40, 0x89, 0xd7, 0xe8,
0x2a, 0xe8, 0xc3, 0x3d, 0xcb, 0x8a, 0x1c, 0xb3, 0x70, 0x7d, 0xe9, 0xe6, 0x88, 0x36, 0x65, 0x21,
0x7b, 0x34, 0xac, 0x73, 0x8d, 0x4f, 0x11, 0xde, 0xd4, 0x21, 0x45, 0x9f, 0x1f, 0x3e, 0xe8, 0xcf,
0x50, 0x92, 0x8c, 0xa4, 0x79, 0x58, 0x3a, 0x26, 0x01, 0x7b, 0x99, 0x5c, 0xff, 0x8d, 0x66, 0x5b,
0x2e, 0x7d, 0x4e, 0xda, 0x3b, 0xe0, 0xef, 0x9d, 0x33, 0x2d, 0x2f, 0x6d, 0x47, 0x3f, 0x56, 0x8d,
},
},
{
"13 blocks",
[]byte("0123456789ABCDEF"),
[]byte("0123456789ABCDEF"),
[]byte("Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World"),
[]byte{
0xd3, 0x1e, 0x36, 0x83, 0xe4, 0xfc, 0x9b, 0x51, 0x6a, 0x2c, 0x0f, 0x98, 0x36, 0x76, 0xa9, 0xeb,
0x1f, 0xdc, 0xc3, 0x2a, 0xf3, 0x84, 0x08, 0x97, 0x81, 0x57, 0xa2, 0x06, 0x5d, 0xe3, 0x4c, 0x6a,
0xe0, 0x02, 0xd6, 0xe4, 0xf5, 0x66, 0x87, 0xc4, 0xcc, 0x54, 0x1d, 0x1f, 0x1c, 0xc4, 0x2f, 0xe6,
0xe5, 0x1d, 0xea, 0x52, 0xb8, 0x0c, 0xc8, 0xbe, 0xae, 0xcc, 0x44, 0xa8, 0x51, 0x81, 0x08, 0x60,
0xb6, 0x09, 0x7b, 0xb8, 0x7e, 0xdb, 0x53, 0x4b, 0xea, 0x2a, 0xc6, 0xa1, 0xe5, 0xa0, 0x2a, 0xe9,
0x22, 0x65, 0x5b, 0xa3, 0xb9, 0xcc, 0x63, 0x92, 0x16, 0x0e, 0x2f, 0xf4, 0x3b, 0x93, 0x06, 0x82,
0xb3, 0x8c, 0x26, 0x2e, 0x06, 0x51, 0x34, 0x2c, 0xe4, 0x3d, 0xd0, 0xc7, 0x2b, 0x8f, 0x31, 0x15,
0x30, 0xa8, 0x96, 0x1c, 0xbc, 0x8e, 0xf7, 0x4f, 0x6b, 0x69, 0x9d, 0xc9, 0x40, 0x89, 0xd7, 0xe8,
0x2a, 0xe8, 0xc3, 0x3d, 0xcb, 0x8a, 0x1c, 0xb3, 0x70, 0x7d, 0xe9, 0xe6, 0x88, 0x36, 0x65, 0x21,
0x7b, 0x34, 0xac, 0x73, 0x8d, 0x4f, 0x11, 0xde, 0xd4, 0x21, 0x45, 0x9f, 0x1f, 0x3e, 0xe8, 0xcf,
0x50, 0x92, 0x8c, 0xa4, 0x79, 0x58, 0x3a, 0x26, 0x01, 0x7b, 0x99, 0x5c, 0xff, 0x8d, 0x66, 0x5b,
0x07, 0x86, 0x0e, 0x22, 0xb4, 0xb4, 0x83, 0x74, 0x33, 0x79, 0xd0, 0x54, 0x9f, 0x03, 0x6b, 0x60,
0x4f, 0xae, 0xbd, 0x43, 0x34, 0x43, 0xb4, 0xee, 0x34, 0x22, 0xcc, 0xc5, 0xcd, 0xbd, 0x2d, 0x63,
},
},
{
"14 blocks",
[]byte("0123456789ABCDEF"),
[]byte("0123456789ABCDEF"),
[]byte("Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World"),
[]byte{
0xd3, 0x1e, 0x36, 0x83, 0xe4, 0xfc, 0x9b, 0x51, 0x6a, 0x2c, 0x0f, 0x98, 0x36, 0x76, 0xa9, 0xeb,
0x1f, 0xdc, 0xc3, 0x2a, 0xf3, 0x84, 0x08, 0x97, 0x81, 0x57, 0xa2, 0x06, 0x5d, 0xe3, 0x4c, 0x6a,
0xe0, 0x02, 0xd6, 0xe4, 0xf5, 0x66, 0x87, 0xc4, 0xcc, 0x54, 0x1d, 0x1f, 0x1c, 0xc4, 0x2f, 0xe6,
0xe5, 0x1d, 0xea, 0x52, 0xb8, 0x0c, 0xc8, 0xbe, 0xae, 0xcc, 0x44, 0xa8, 0x51, 0x81, 0x08, 0x60,
0xb6, 0x09, 0x7b, 0xb8, 0x7e, 0xdb, 0x53, 0x4b, 0xea, 0x2a, 0xc6, 0xa1, 0xe5, 0xa0, 0x2a, 0xe9,
0x22, 0x65, 0x5b, 0xa3, 0xb9, 0xcc, 0x63, 0x92, 0x16, 0x0e, 0x2f, 0xf4, 0x3b, 0x93, 0x06, 0x82,
0xb3, 0x8c, 0x26, 0x2e, 0x06, 0x51, 0x34, 0x2c, 0xe4, 0x3d, 0xd0, 0xc7, 0x2b, 0x8f, 0x31, 0x15,
0x30, 0xa8, 0x96, 0x1c, 0xbc, 0x8e, 0xf7, 0x4f, 0x6b, 0x69, 0x9d, 0xc9, 0x40, 0x89, 0xd7, 0xe8,
0x2a, 0xe8, 0xc3, 0x3d, 0xcb, 0x8a, 0x1c, 0xb3, 0x70, 0x7d, 0xe9, 0xe6, 0x88, 0x36, 0x65, 0x21,
0x7b, 0x34, 0xac, 0x73, 0x8d, 0x4f, 0x11, 0xde, 0xd4, 0x21, 0x45, 0x9f, 0x1f, 0x3e, 0xe8, 0xcf,
0x50, 0x92, 0x8c, 0xa4, 0x79, 0x58, 0x3a, 0x26, 0x01, 0x7b, 0x99, 0x5c, 0xff, 0x8d, 0x66, 0x5b,
0x07, 0x86, 0x0e, 0x22, 0xb4, 0xb4, 0x83, 0x74, 0x33, 0x79, 0xd0, 0x54, 0x9f, 0x03, 0x6b, 0x60,
0xa1, 0x52, 0x3c, 0x61, 0x1d, 0x91, 0xbf, 0x50, 0x00, 0xfb, 0x62, 0x58, 0xfa, 0xd3, 0xbd, 0x17,
0x28, 0x95, 0x7b, 0x51, 0x7e, 0x07, 0x6f, 0xfb, 0x8f, 0x9a, 0x4c, 0xbf, 0x33, 0xd5, 0xd7, 0xb6,
},
},
{
"15 blocks",
[]byte("0123456789ABCDEF"),
[]byte("0123456789ABCDEF"),
[]byte("Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello"),
[]byte{
0xd3, 0x1e, 0x36, 0x83, 0xe4, 0xfc, 0x9b, 0x51, 0x6a, 0x2c, 0x0f, 0x98, 0x36, 0x76, 0xa9, 0xeb,
0x1f, 0xdc, 0xc3, 0x2a, 0xf3, 0x84, 0x08, 0x97, 0x81, 0x57, 0xa2, 0x06, 0x5d, 0xe3, 0x4c, 0x6a,
0xe0, 0x02, 0xd6, 0xe4, 0xf5, 0x66, 0x87, 0xc4, 0xcc, 0x54, 0x1d, 0x1f, 0x1c, 0xc4, 0x2f, 0xe6,
0xe5, 0x1d, 0xea, 0x52, 0xb8, 0x0c, 0xc8, 0xbe, 0xae, 0xcc, 0x44, 0xa8, 0x51, 0x81, 0x08, 0x60,
0xb6, 0x09, 0x7b, 0xb8, 0x7e, 0xdb, 0x53, 0x4b, 0xea, 0x2a, 0xc6, 0xa1, 0xe5, 0xa0, 0x2a, 0xe9,
0x22, 0x65, 0x5b, 0xa3, 0xb9, 0xcc, 0x63, 0x92, 0x16, 0x0e, 0x2f, 0xf4, 0x3b, 0x93, 0x06, 0x82,
0xb3, 0x8c, 0x26, 0x2e, 0x06, 0x51, 0x34, 0x2c, 0xe4, 0x3d, 0xd0, 0xc7, 0x2b, 0x8f, 0x31, 0x15,
0x30, 0xa8, 0x96, 0x1c, 0xbc, 0x8e, 0xf7, 0x4f, 0x6b, 0x69, 0x9d, 0xc9, 0x40, 0x89, 0xd7, 0xe8,
0x2a, 0xe8, 0xc3, 0x3d, 0xcb, 0x8a, 0x1c, 0xb3, 0x70, 0x7d, 0xe9, 0xe6, 0x88, 0x36, 0x65, 0x21,
0x7b, 0x34, 0xac, 0x73, 0x8d, 0x4f, 0x11, 0xde, 0xd4, 0x21, 0x45, 0x9f, 0x1f, 0x3e, 0xe8, 0xcf,
0x50, 0x92, 0x8c, 0xa4, 0x79, 0x58, 0x3a, 0x26, 0x01, 0x7b, 0x99, 0x5c, 0xff, 0x8d, 0x66, 0x5b,
0x07, 0x86, 0x0e, 0x22, 0xb4, 0xb4, 0x83, 0x74, 0x33, 0x79, 0xd0, 0x54, 0x9f, 0x03, 0x6b, 0x60,
0xa1, 0x52, 0x3c, 0x61, 0x1d, 0x91, 0xbf, 0x50, 0x00, 0xfb, 0x62, 0x58, 0xfa, 0xd3, 0xbd, 0x17,
0x7d, 0x6f, 0xda, 0x76, 0x9a, 0xdb, 0x01, 0x96, 0x97, 0xc9, 0x5f, 0x64, 0x20, 0x3c, 0x70, 0x7a,
0xa3, 0xae, 0x6c, 0xce, 0x49, 0x31, 0x7a, 0xd3, 0x98, 0x01, 0xb0, 0x96, 0x2b, 0x4e, 0x81, 0x98,
},
},
{
"16 blocks",
[]byte("0123456789ABCDEF"),
[]byte("0123456789ABCDEF"),
[]byte("Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World"),
[]byte{
0xd3, 0x1e, 0x36, 0x83, 0xe4, 0xfc, 0x9b, 0x51, 0x6a, 0x2c, 0x0f, 0x98, 0x36, 0x76, 0xa9, 0xeb,
0x1f, 0xdc, 0xc3, 0x2a, 0xf3, 0x84, 0x08, 0x97, 0x81, 0x57, 0xa2, 0x06, 0x5d, 0xe3, 0x4c, 0x6a,
0xe0, 0x02, 0xd6, 0xe4, 0xf5, 0x66, 0x87, 0xc4, 0xcc, 0x54, 0x1d, 0x1f, 0x1c, 0xc4, 0x2f, 0xe6,
0xe5, 0x1d, 0xea, 0x52, 0xb8, 0x0c, 0xc8, 0xbe, 0xae, 0xcc, 0x44, 0xa8, 0x51, 0x81, 0x08, 0x60,
0xb6, 0x09, 0x7b, 0xb8, 0x7e, 0xdb, 0x53, 0x4b, 0xea, 0x2a, 0xc6, 0xa1, 0xe5, 0xa0, 0x2a, 0xe9,
0x22, 0x65, 0x5b, 0xa3, 0xb9, 0xcc, 0x63, 0x92, 0x16, 0x0e, 0x2f, 0xf4, 0x3b, 0x93, 0x06, 0x82,
0xb3, 0x8c, 0x26, 0x2e, 0x06, 0x51, 0x34, 0x2c, 0xe4, 0x3d, 0xd0, 0xc7, 0x2b, 0x8f, 0x31, 0x15,
0x30, 0xa8, 0x96, 0x1c, 0xbc, 0x8e, 0xf7, 0x4f, 0x6b, 0x69, 0x9d, 0xc9, 0x40, 0x89, 0xd7, 0xe8,
0x2a, 0xe8, 0xc3, 0x3d, 0xcb, 0x8a, 0x1c, 0xb3, 0x70, 0x7d, 0xe9, 0xe6, 0x88, 0x36, 0x65, 0x21,
0x7b, 0x34, 0xac, 0x73, 0x8d, 0x4f, 0x11, 0xde, 0xd4, 0x21, 0x45, 0x9f, 0x1f, 0x3e, 0xe8, 0xcf,
0x50, 0x92, 0x8c, 0xa4, 0x79, 0x58, 0x3a, 0x26, 0x01, 0x7b, 0x99, 0x5c, 0xff, 0x8d, 0x66, 0x5b,
0x07, 0x86, 0x0e, 0x22, 0xb4, 0xb4, 0x83, 0x74, 0x33, 0x79, 0xd0, 0x54, 0x9f, 0x03, 0x6b, 0x60,
0xa1, 0x52, 0x3c, 0x61, 0x1d, 0x91, 0xbf, 0x50, 0x00, 0xfb, 0x62, 0x58, 0xfa, 0xd3, 0xbd, 0x17,
0x7d, 0x6f, 0xda, 0x76, 0x9a, 0xdb, 0x01, 0x96, 0x97, 0xc9, 0x5f, 0x64, 0x20, 0x3c, 0x70, 0x7a,
0x40, 0x1f, 0x35, 0xc8, 0x22, 0xf2, 0x76, 0x6d, 0x8e, 0x4a, 0x78, 0xd7, 0x8d, 0x52, 0x51, 0x60,
0x71, 0xa7, 0x42, 0x07, 0xb2, 0x32, 0x3b, 0xa8, 0x5b, 0x15, 0x8f, 0x4e, 0x56, 0xef, 0xe0, 0x0a,
},
},
{
"17 blocks",
[]byte("0123456789ABCDEF"),
@ -334,24 +185,3 @@ func TestCBCDecrypterSM4(t *testing.T) {
}
}
}
func TestSM4CBCRandom(t *testing.T) {
key := []byte("0123456789ABCDEF")
c, err := sm4.NewCipher(key)
if err != nil {
t.Fatal(err)
}
encrypter := cipher.NewCBCEncrypter(c, key)
decrypter := cipher.NewCBCDecrypter(c, key)
for i:=1; i<=50; i++ {
plaintext := make([]byte, i*16)
ciphertext := make([]byte, i*16)
got := make([]byte, i*16)
io.ReadFull(rand.Reader, plaintext)
encrypter.CryptBlocks(ciphertext, plaintext)
decrypter.CryptBlocks(got, ciphertext)
if !bytes.Equal(got, plaintext) {
t.Errorf("test %v blocks failed", i)
}
}
}

View File

@ -2,14 +2,15 @@
package cipher
import (
"crypto/cipher"
"crypto/subtle"
goCipher "crypto/cipher"
goSubtle "crypto/subtle"
"encoding/binary"
"math"
"errors"
"github.com/emmansun/gmsm/internal/alias"
"github.com/emmansun/gmsm/internal/byteorder"
"github.com/emmansun/gmsm/internal/subtle"
)
const (
@ -22,11 +23,11 @@ const (
// ccmAble is an interface implemented by ciphers that have a specific optimized
// implementation of CCM.
type ccmAble interface {
NewCCM(nonceSize, tagSize int) (cipher.AEAD, error)
NewCCM(nonceSize, tagSize int) (goCipher.AEAD, error)
}
type ccm struct {
cipher cipher.Block
cipher goCipher.Block
nonceSize int
tagSize int
}
@ -56,14 +57,14 @@ func maxlen(L, tagsize int) int {
// NewCCM returns the given 128-bit, block cipher wrapped in CCM
// with the standard nonce length.
func NewCCM(cipher cipher.Block) (cipher.AEAD, error) {
func NewCCM(cipher goCipher.Block) (goCipher.AEAD, error) {
return NewCCMWithNonceAndTagSize(cipher, ccmStandardNonceSize, ccmTagSize)
}
// NewCCMWithNonceSize returns the given 128-bit, block cipher wrapped in CCM,
// which accepts nonces of the given length. The length must not
// be zero.
func NewCCMWithNonceSize(cipher cipher.Block, size int) (cipher.AEAD, error) {
func NewCCMWithNonceSize(cipher goCipher.Block, size int) (goCipher.AEAD, error) {
return NewCCMWithNonceAndTagSize(cipher, size, ccmTagSize)
}
@ -71,13 +72,13 @@ func NewCCMWithNonceSize(cipher cipher.Block, size int) (cipher.AEAD, error) {
// which generates tags with the given length.
//
// Tag sizes between 8 and 16 bytes are allowed.
func NewCCMWithTagSize(cipher cipher.Block, tagSize int) (cipher.AEAD, error) {
//
func NewCCMWithTagSize(cipher goCipher.Block, tagSize int) (goCipher.AEAD, error) {
return NewCCMWithNonceAndTagSize(cipher, ccmStandardNonceSize, tagSize)
}
// NewCCMWithNonceAndTagSize creates a new Counter with CBC-MAC (CCM) mode AEAD
// with the given nonce size and tag size.
func NewCCMWithNonceAndTagSize(cipher cipher.Block, nonceSize, tagSize int) (cipher.AEAD, error) {
// https://tools.ietf.org/html/rfc3610
func NewCCMWithNonceAndTagSize(cipher goCipher.Block, nonceSize, tagSize int) (goCipher.AEAD, error) {
if tagSize < ccmMinimumTagSize || tagSize > ccmBlockSize || tagSize&1 != 0 {
return nil, errors.New("cipher: incorrect tag size given to CCM")
}
@ -132,7 +133,7 @@ func (c *ccm) auth(nonce, plaintext, additionalData []byte, tagMask *[ccmBlockSi
}
out[0] |= byte(c.tagSize-2) << 2 // M' = ((tagSize - 2) / 2)*8
out[0] |= byte(14 - c.nonceSize) // L'
byteorder.BEPutUint64(out[ccmBlockSize-8:], uint64(len(plaintext)))
binary.BigEndian.PutUint64(out[ccmBlockSize-8:], uint64(len(plaintext)))
copy(out[1:], nonce)
// B0
c.cipher.Encrypt(out[:], out[:])
@ -142,7 +143,7 @@ func (c *ccm) auth(nonce, plaintext, additionalData []byte, tagMask *[ccmBlockSi
// First adata block includes adata length
i := 2
if n <= 0xfeff { // l(a) < (2^16 - 2^8)
byteorder.BEPutUint16(block[:i], uint16(n))
binary.BigEndian.PutUint16(block[:i], uint16(n))
} else {
block[0] = 0xff
// If (2^16 - 2^8) <= l(a) < 2^32, then the length field is encoded as
@ -151,14 +152,14 @@ func (c *ccm) auth(nonce, plaintext, additionalData []byte, tagMask *[ccmBlockSi
if n < uint64(1<<32) {
block[1] = 0xfe
i = 2 + 4
byteorder.BEPutUint32(block[2:i], uint32(n))
binary.BigEndian.PutUint32(block[2:i], uint32(n))
} else {
block[1] = 0xff
// If 2^32 <= l(a) < 2^64, then the length field is encoded as ten
// octets consisting of the octets 0xff, 0xff, and eight octets encoding
// l(a) in most-significant-byte-first order.
i = 2 + 8
byteorder.BEPutUint64(block[2:i], uint64(n))
binary.BigEndian.PutUint64(block[2:i], uint64(n))
}
}
i = copy(block[i:], additionalData) // first block start with additional data length
@ -189,7 +190,7 @@ func (c *ccm) Seal(dst, nonce, plaintext, data []byte) []byte {
c.cipher.Encrypt(tagMask[:], counter[:])
counter[len(counter)-1] |= 1
ctr := cipher.NewCTR(c.cipher, counter[:])
ctr := goCipher.NewCTR(c.cipher, counter[:])
ctr.XORKeyStream(out, plaintext)
tag := c.auth(nonce, plaintext, data, &tagMask)
@ -231,15 +232,17 @@ func (c *ccm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
}
counter[len(counter)-1] |= 1
ctr := cipher.NewCTR(c.cipher, counter[:])
ctr := goCipher.NewCTR(c.cipher, counter[:])
ctr.XORKeyStream(out, ciphertext)
expectedTag := c.auth(nonce, out, data, &tagMask)
if subtle.ConstantTimeCompare(expectedTag, tag) != 1 {
if goSubtle.ConstantTimeCompare(expectedTag, tag) != 1 {
// The AESNI code decrypts and authenticates concurrently, and
// so overwrites dst in the event of a tag mismatch. That
// behavior is mimicked here in order to be consistent across
// platforms.
clear(out)
for i := range out {
out[i] = 0
}
return nil, errOpen
}
return ret, nil

View File

@ -1,31 +0,0 @@
//go:build !(arm || mips)
package cipher_test
import (
"crypto/aes"
"encoding/hex"
"testing"
"github.com/emmansun/gmsm/cipher"
)
func TestCCMLongAd(t *testing.T) {
key, _ := hex.DecodeString("ab72c77b97cb5fe9a382d9fe81ffdbed")
nonce, _ := hex.DecodeString("54cc7dc2c37ec006bcc6d1db")
c, _ := aes.NewCipher(key)
aesccm, _ := cipher.NewCCM(c)
ad := make([]byte, 0x10000)
ct := aesccm.Seal(nil, nonce, nil, ad)
if hex.EncodeToString(ct) != "e1ad65c3bfaba94b1085aff8c6ea2698" {
t.Errorf("got %s, want e1ad65c3bfaba94b1085aff8c6ea2698", hex.EncodeToString(ct))
}
ad = make([]byte, 1<<32+1)
ct = aesccm.Seal(nil, nonce, nil, ad)
if hex.EncodeToString(ct) != "c1949a661c605ff5640a29dd3e285ddb" {
t.Errorf("got %s, want c1949a661c605ff5640a29dd3e285ddb", hex.EncodeToString(ct))
}
}

View File

@ -41,6 +41,7 @@ func TestCCM(t *testing.T) {
continue
}
//func (c *ccm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error)
pt, err := sm4ccm.Open(nil, nonce, ct, ad)
if err != nil {
t.Fatal(err)

View File

@ -277,3 +277,23 @@ func TestCCMTagFailureOverwrite(t *testing.T) {
}
}
}
func TestCCMLongAd(t *testing.T) {
key, _ := hex.DecodeString("ab72c77b97cb5fe9a382d9fe81ffdbed")
nonce, _ := hex.DecodeString("54cc7dc2c37ec006bcc6d1db")
c, _ := aes.NewCipher(key)
aesccm, _ := cipher.NewCCM(c)
ad := make([]byte, 0x10000)
ct := aesccm.Seal(nil, nonce, nil, ad)
if hex.EncodeToString(ct) != "e1ad65c3bfaba94b1085aff8c6ea2698" {
t.Errorf("got %s, want e1ad65c3bfaba94b1085aff8c6ea2698", hex.EncodeToString(ct))
}
ad = make([]byte, 1<<32+1)
ct = aesccm.Seal(nil, nonce, nil, ad)
if hex.EncodeToString(ct) != "c1949a661c605ff5640a29dd3e285ddb" {
t.Errorf("got %s, want c1949a661c605ff5640a29dd3e285ddb", hex.EncodeToString(ct))
}
}

View File

@ -7,7 +7,6 @@ import (
"encoding/hex"
"testing"
"github.com/emmansun/gmsm/internal/cryptotest"
"github.com/emmansun/gmsm/sm4"
)
@ -106,24 +105,3 @@ func TestCFBInverse(t *testing.T) {
t.Errorf("got: %x, want: %x", plaintextCopy, plaintext)
}
}
func TestCFBStream(t *testing.T) {
t.Run("SM4", func(t *testing.T) {
rng := newRandReader(t)
key := make([]byte, 16)
rng.Read(key)
block, err := sm4.NewCipher(key)
if err != nil {
panic(err)
}
t.Run("Encrypter", func(t *testing.T) {
cryptotest.TestStreamFromBlock(t, block, cipher.NewCFBEncrypter)
})
t.Run("Decrypter", func(t *testing.T) {
cryptotest.TestStreamFromBlock(t, block, cipher.NewCFBDecrypter)
})
})
}

View File

@ -1,14 +0,0 @@
package cipher
import "crypto/cipher"
// blockSize is the block size that the underlying cipher must have.
const blockSize = 16
type concurrentBlocks interface {
Concurrency() int
EncryptBlocks(dst, src []byte)
DecryptBlocks(dst, src []byte)
}
type CipherCreator func([]byte) (cipher.Block, error)

View File

@ -5,10 +5,11 @@ import (
"crypto/cipher"
"testing"
"github.com/emmansun/gmsm/internal/cryptotest"
"github.com/emmansun/gmsm/sm4"
)
var commonCounter = []byte{0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff}
var ctrSM4Tests = []struct {
name string
key []byte
@ -27,110 +28,6 @@ var ctrSM4Tests = []struct {
0xbc, 0x71, 0x0d, 0x76, 0x2d, 0x07, 0x0b, 0x26, 0x36, 0x1d, 0xa8, 0x2b, 0x54, 0x56, 0x5e, 0x46,
0xb0, 0x2b, 0x3d, 0xbd, 0xdd, 0x50, 0xd5, 0xb4, 0x58, 0xae, 0xcc, 0xb2, 0x5d, 0xa1, 0x05, 0xe1},
},
{
"4 blocks",
[]byte{0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c},
[]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f},
[]byte{
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10,
},
[]byte{
0xbc, 0x71, 0x0d, 0x76, 0x2d, 0x07, 0x0b, 0x26, 0x36, 0x1d, 0xa8, 0x2b, 0x54, 0x56, 0x5e, 0x46,
0xb0, 0x2b, 0x3d, 0xbd, 0xdd, 0x50, 0xd5, 0xb4, 0x58, 0xae, 0xcc, 0xb2, 0x5d, 0xa1, 0x05, 0xe1,
0x6a, 0xd7, 0x0b, 0xc0, 0x11, 0x75, 0xad, 0x43, 0xb0, 0x80, 0x6a, 0x2e, 0x7b, 0x9c, 0xa5, 0x45,
0x60, 0x24, 0x59, 0xa0, 0x6b, 0x7d, 0x13, 0x0d, 0xde, 0x42, 0xa3, 0xe0, 0x47, 0x68, 0x18, 0xd2,
},
},
{
"6 blocks",
[]byte{0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c},
[]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f},
[]byte{
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10,
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
},
[]byte{
0xbc, 0x71, 0x0d, 0x76, 0x2d, 0x07, 0x0b, 0x26, 0x36, 0x1d, 0xa8, 0x2b, 0x54, 0x56, 0x5e, 0x46,
0xb0, 0x2b, 0x3d, 0xbd, 0xdd, 0x50, 0xd5, 0xb4, 0x58, 0xae, 0xcc, 0xb2, 0x5d, 0xa1, 0x05, 0xe1,
0x6a, 0xd7, 0x0b, 0xc0, 0x11, 0x75, 0xad, 0x43, 0xb0, 0x80, 0x6a, 0x2e, 0x7b, 0x9c, 0xa5, 0x45,
0x60, 0x24, 0x59, 0xa0, 0x6b, 0x7d, 0x13, 0x0d, 0xde, 0x42, 0xa3, 0xe0, 0x47, 0x68, 0x18, 0xd2,
0x00, 0xb8, 0x33, 0x1a, 0x66, 0x57, 0xd6, 0xbe, 0xb8, 0x5b, 0x72, 0x4f, 0x55, 0x0c, 0xd5, 0x2d,
0x96, 0xf3, 0xe4, 0x12, 0x37, 0xa2, 0x07, 0x44, 0x43, 0xa5, 0x43, 0x3a, 0x41, 0x33, 0x0d, 0xca,
},
},
{
"8 blocks",
[]byte{0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c},
[]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f},
[]byte{
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10,
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10,
},
[]byte{
0xbc, 0x71, 0x0d, 0x76, 0x2d, 0x07, 0x0b, 0x26, 0x36, 0x1d, 0xa8, 0x2b, 0x54, 0x56, 0x5e, 0x46,
0xb0, 0x2b, 0x3d, 0xbd, 0xdd, 0x50, 0xd5, 0xb4, 0x58, 0xae, 0xcc, 0xb2, 0x5d, 0xa1, 0x05, 0xe1,
0x6a, 0xd7, 0x0b, 0xc0, 0x11, 0x75, 0xad, 0x43, 0xb0, 0x80, 0x6a, 0x2e, 0x7b, 0x9c, 0xa5, 0x45,
0x60, 0x24, 0x59, 0xa0, 0x6b, 0x7d, 0x13, 0x0d, 0xde, 0x42, 0xa3, 0xe0, 0x47, 0x68, 0x18, 0xd2,
0x00, 0xb8, 0x33, 0x1a, 0x66, 0x57, 0xd6, 0xbe, 0xb8, 0x5b, 0x72, 0x4f, 0x55, 0x0c, 0xd5, 0x2d,
0x96, 0xf3, 0xe4, 0x12, 0x37, 0xa2, 0x07, 0x44, 0x43, 0xa5, 0x43, 0x3a, 0x41, 0x33, 0x0d, 0xca,
0x41, 0xb7, 0x3a, 0x57, 0x17, 0x65, 0xef, 0x3d, 0xbb, 0xd1, 0x04, 0x5d, 0xb7, 0xf8, 0x71, 0x39,
0xff, 0x82, 0x01, 0x19, 0x75, 0xaf, 0x8a, 0x01, 0x8a, 0x0a, 0x26, 0x93, 0x85, 0xfd, 0x04, 0x5f,
},
},
{
"16 blocks",
[]byte{0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c},
[]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f},
[]byte{
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10,
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10,
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10,
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10,
},
[]byte{
0xbc, 0x71, 0x0d, 0x76, 0x2d, 0x07, 0x0b, 0x26, 0x36, 0x1d, 0xa8, 0x2b, 0x54, 0x56, 0x5e, 0x46,
0xb0, 0x2b, 0x3d, 0xbd, 0xdd, 0x50, 0xd5, 0xb4, 0x58, 0xae, 0xcc, 0xb2, 0x5d, 0xa1, 0x05, 0xe1,
0x6a, 0xd7, 0x0b, 0xc0, 0x11, 0x75, 0xad, 0x43, 0xb0, 0x80, 0x6a, 0x2e, 0x7b, 0x9c, 0xa5, 0x45,
0x60, 0x24, 0x59, 0xa0, 0x6b, 0x7d, 0x13, 0x0d, 0xde, 0x42, 0xa3, 0xe0, 0x47, 0x68, 0x18, 0xd2,
0x00, 0xb8, 0x33, 0x1a, 0x66, 0x57, 0xd6, 0xbe, 0xb8, 0x5b, 0x72, 0x4f, 0x55, 0x0c, 0xd5, 0x2d,
0x96, 0xf3, 0xe4, 0x12, 0x37, 0xa2, 0x07, 0x44, 0x43, 0xa5, 0x43, 0x3a, 0x41, 0x33, 0x0d, 0xca,
0x41, 0xb7, 0x3a, 0x57, 0x17, 0x65, 0xef, 0x3d, 0xbb, 0xd1, 0x04, 0x5d, 0xb7, 0xf8, 0x71, 0x39,
0xff, 0x82, 0x01, 0x19, 0x75, 0xaf, 0x8a, 0x01, 0x8a, 0x0a, 0x26, 0x93, 0x85, 0xfd, 0x04, 0x5f,
0x73, 0xd4, 0xc6, 0x3a, 0x81, 0x0a, 0x91, 0xe3, 0xb9, 0x17, 0x89, 0xdf, 0x4c, 0xcd, 0xe8, 0xe3,
0x4e, 0xe7, 0x8d, 0x52, 0x89, 0x93, 0xb9, 0xef, 0x42, 0xe7, 0x5d, 0x67, 0xa8, 0x25, 0xad, 0xf0,
0xe2, 0x45, 0x9d, 0x8c, 0x30, 0x61, 0x8a, 0x26, 0x90, 0x4f, 0x52, 0x61, 0xa0, 0x61, 0x62, 0xfb,
0x36, 0xc8, 0x95, 0xe2, 0x8d, 0x75, 0x86, 0xf5, 0xbf, 0x22, 0x1c, 0xdd, 0xc9, 0x52, 0x71, 0x5a,
0x7e, 0xb0, 0x56, 0xd6, 0x8a, 0x7e, 0xfa, 0x4f, 0xda, 0x6b, 0x97, 0x95, 0x23, 0xa7, 0xa8, 0x39,
0x76, 0x31, 0x10, 0x79, 0x47, 0x98, 0x5b, 0x71, 0xbf, 0xc9, 0x4c, 0xce, 0xb7, 0xd4, 0x19, 0x86,
0x04, 0x87, 0xc0, 0xba, 0xe8, 0xa5, 0x4c, 0xc8, 0x48, 0x9c, 0x28, 0xd3, 0x4b, 0x4d, 0xfc, 0x3f,
0x9b, 0xbc, 0xf3, 0xd1, 0x9d, 0x25, 0x43, 0x47, 0x37, 0xea, 0xb7, 0x5e, 0x0d, 0xdb, 0x58, 0xf1,
},
},
}
func TestCTR_SM4(t *testing.T) {
@ -168,19 +65,3 @@ func TestCTR_SM4(t *testing.T) {
}
}
}
func TestCTRStream(t *testing.T) {
t.Run("SM4", func(t *testing.T) {
rng := newRandReader(t)
key := make([]byte, 16)
rng.Read(key)
block, err := sm4.NewCipher(key)
if err != nil {
panic(err)
}
cryptotest.TestStreamFromBlock(t, block, cipher.NewCTR)
})
}

View File

@ -1,100 +0,0 @@
// Electronic Code Book (ECB) mode.
// Please do NOT use this mode alone.
package cipher
import (
goCipher "crypto/cipher"
"github.com/emmansun/gmsm/internal/alias"
)
type ecb struct {
b goCipher.Block
blockSize int
}
func newECB(b goCipher.Block) *ecb {
return &ecb{
b: b,
blockSize: b.BlockSize(),
}
}
func validate(size int, dst, src []byte) {
if len(src)%size != 0 {
panic("cipher: input not full blocks")
}
if len(dst) < len(src) {
panic("cipher: output smaller than input")
}
if alias.InexactOverlap(dst[:len(src)], src) {
panic("cipher: invalid buffer overlap")
}
}
type ecbEncrypter ecb
// ecbEncAble is an interface implemented by ciphers that have a specific
// optimized implementation of ECB encryption, like sm4.
// NewECBEncrypter will check for this interface and return the specific
// BlockMode if found.
type ecbEncAble interface {
NewECBEncrypter() goCipher.BlockMode
}
// NewECBEncrypter returns a BlockMode which encrypts in electronic code book
// mode, using the given Block.
func NewECBEncrypter(b goCipher.Block) goCipher.BlockMode {
if ecb, ok := b.(ecbEncAble); ok {
return ecb.NewECBEncrypter()
}
return (*ecbEncrypter)(newECB(b))
}
func (x *ecbEncrypter) BlockSize() int { return x.blockSize }
func (x *ecbEncrypter) CryptBlocks(dst, src []byte) {
validate(x.blockSize, dst, src)
for len(src) > 0 {
x.b.Encrypt(dst[:x.blockSize], src[:x.blockSize])
src = src[x.blockSize:]
dst = dst[x.blockSize:]
}
}
type ecbDecrypter ecb
// ecbDecAble is an interface implemented by ciphers that have a specific
// optimized implementation of ECB decryption, like sm4.
// NewECBDecrypter will check for this interface and return the specific
// BlockMode if found.
type ecbDecAble interface {
NewECBDecrypter() goCipher.BlockMode
}
// NewECBDecrypter returns a BlockMode which decrypts in electronic code book
// mode, using the given Block.
func NewECBDecrypter(b goCipher.Block) goCipher.BlockMode {
if ecb, ok := b.(ecbDecAble); ok {
return ecb.NewECBDecrypter()
}
return (*ecbDecrypter)(newECB(b))
}
func (x *ecbDecrypter) BlockSize() int { return x.blockSize }
func (x *ecbDecrypter) CryptBlocks(dst, src []byte) {
validate(x.blockSize, dst, src)
if len(src) == 0 {
return
}
for len(src) > 0 {
x.b.Decrypt(dst[:x.blockSize], src[:x.blockSize])
src = src[x.blockSize:]
dst = dst[x.blockSize:]
}
}

View File

@ -1,145 +0,0 @@
package cipher_test
import (
"bytes"
"crypto/rand"
"io"
"testing"
"github.com/emmansun/gmsm/cipher"
"github.com/emmansun/gmsm/sm4"
)
var ecbSM4Tests = []struct {
name string
key []byte
in []byte
}{
{
"1 block",
[]byte("0123456789ABCDEF"),
[]byte("exampleplaintext"),
},
{
"2 same blocks",
[]byte("0123456789ABCDEF"),
[]byte("exampleplaintextexampleplaintext"),
},
{
"2 different blocks",
[]byte("0123456789ABCDEF"),
[]byte("exampleplaintextfedcba9876543210"),
},
{
"3 same blocks",
[]byte("0123456789ABCDEF"),
[]byte("exampleplaintextexampleplaintextexampleplaintext"),
},
{
"4 same blocks",
[]byte("0123456789ABCDEF"),
[]byte("exampleplaintextexampleplaintextexampleplaintextexampleplaintext"),
},
{
"5 same blocks",
[]byte("0123456789ABCDEF"),
[]byte("exampleplaintextexampleplaintextexampleplaintextexampleplaintextexampleplaintext"),
},
{
"6 same blocks",
[]byte("0123456789ABCDEF"),
[]byte("exampleplaintextexampleplaintextexampleplaintextexampleplaintextexampleplaintextexampleplaintext"),
},
{
"7 same blocks",
[]byte("0123456789ABCDEF"),
[]byte("exampleplaintextexampleplaintextexampleplaintextexampleplaintextexampleplaintextexampleplaintextexampleplaintext"),
},
{
"8 same blocks",
[]byte("0123456789ABCDEF"),
[]byte("exampleplaintextexampleplaintextexampleplaintextexampleplaintextexampleplaintextexampleplaintextexampleplaintextexampleplaintext"),
},
{
"9 same blocks",
[]byte("0123456789ABCDEF"),
[]byte("exampleplaintextexampleplaintextexampleplaintextexampleplaintextexampleplaintextexampleplaintextexampleplaintextexampleplaintextexampleplaintext"),
},
{
"18 same blocks",
[]byte("0123456789ABCDEF"),
[]byte("exampleplaintextexampleplaintextexampleplaintextexampleplaintextexampleplaintextexampleplaintextexampleplaintextexampleplaintextexampleplaintextexampleplaintextexampleplaintextexampleplaintextexampleplaintextexampleplaintextexampleplaintextexampleplaintextexampleplaintextexampleplaintext"),
},
}
func TestECBBasic(t *testing.T) {
for _, test := range ecbSM4Tests {
c, err := sm4.NewCipher(test.key)
if err != nil {
t.Errorf("%s: NewCipher(%d bytes) = %s", test.name, len(test.key), err)
continue
}
encrypter := cipher.NewECBEncrypter(c)
ciphertext := make([]byte, len(test.in))
encrypter.CryptBlocks(ciphertext, test.in)
plaintext := make([]byte, len(test.in))
decrypter := cipher.NewECBDecrypter(c)
decrypter.CryptBlocks(plaintext, ciphertext)
if !bytes.Equal(test.in, plaintext) {
t.Errorf("%s: ECB encrypt/decrypt failed, %s", test.name, string(plaintext))
}
}
}
func TestECBRandom(t *testing.T) {
key := []byte("0123456789ABCDEF")
plaintext := make([]byte, 464)
ciphertext := make([]byte, 464)
io.ReadFull(rand.Reader, plaintext)
c, err := sm4.NewCipher(key)
if err != nil {
t.Fatal(err)
}
encrypter := cipher.NewECBEncrypter(c)
encrypter.CryptBlocks(ciphertext, plaintext)
result := make([]byte, 464)
decrypter := cipher.NewECBDecrypter(c)
decrypter.CryptBlocks(result, ciphertext)
if !bytes.Equal(result, plaintext) {
t.Error("ECB encrypt/decrypt failed")
}
}
func shouldPanic(t *testing.T, f func()) {
t.Helper()
defer func() { _ = recover() }()
f()
t.Errorf("should have panicked")
}
func TestECBValidate(t *testing.T) {
key := make([]byte, 16)
src := make([]byte, 32)
c, err := sm4.NewCipher(key)
if err != nil {
t.Fatal(err)
}
decrypter := cipher.NewECBDecrypter(c)
// test len(src) == 0
decrypter.CryptBlocks(nil, nil)
// cipher: input not full blocks
shouldPanic(t, func() {
decrypter.CryptBlocks(src, src[1:])
})
// cipher: output smaller than input
shouldPanic(t, func() {
decrypter.CryptBlocks(src[1:], src)
})
// cipher: invalid buffer overlap
shouldPanic(t, func() {
decrypter.CryptBlocks(src[1:17], src[2:18])
})
}

View File

@ -1,76 +0,0 @@
package cipher_test
import (
"crypto/aes"
"encoding/hex"
"fmt"
"github.com/emmansun/gmsm/cipher"
)
func ExampleNewECBEncrypter() {
// Load your secret key from a safe place and reuse it across multiple
// NewCipher calls. (Obviously don't use this example key for anything
// real.) If you want to convert a passphrase to a key, use a suitable
// package like bcrypt or scrypt.
key, _ := hex.DecodeString("6368616e676520746869732070617373")
plaintext := []byte("exampleplaintextexampleplaintext")
// ECB mode works on blocks so plaintexts may need to be padded to the
// next whole block. For an example of such padding, see
// https://tools.ietf.org/html/rfc5246#section-6.2.3.2. Here we'll
// assume that the plaintext is already of the correct length.
if len(plaintext)%aes.BlockSize != 0 {
panic("plaintext is not a multiple of the block size")
}
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
ciphertext := make([]byte, len(plaintext))
mode := cipher.NewECBEncrypter(block)
mode.CryptBlocks(ciphertext, plaintext)
// It's important to remember that ciphertexts must be authenticated
// (i.e. by using crypto/hmac) as well as being encrypted in order to
// be secure.
fmt.Printf("%x\n", ciphertext)
}
func ExampleNewECBDecrypter() {
// Load your secret key from a safe place and reuse it across multiple
// NewCipher calls. (Obviously don't use this example key for anything
// real.) If you want to convert a passphrase to a key, use a suitable
// package like bcrypt or scrypt.
key, _ := hex.DecodeString("6368616e676520746869732070617373")
ciphertext, _ := hex.DecodeString("f42512e1e4039213bd449ba47faa1b74f42512e1e4039213bd449ba47faa1b74")
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
// ECB mode always works in whole blocks.
if len(ciphertext)%aes.BlockSize != 0 {
panic("ciphertext is not a multiple of the block size")
}
mode := cipher.NewECBDecrypter(block)
// CryptBlocks can work in-place if the two arguments are the same.
mode.CryptBlocks(ciphertext, ciphertext)
// If the original plaintext lengths are not a multiple of the block
// size, padding would have to be added when encrypting, which would be
// removed at this point. For an example, see
// https://tools.ietf.org/html/rfc5246#section-6.2.3.2. However, it's
// critical to note that ciphertexts must be authenticated (i.e. by
// using crypto/hmac) before being decrypted in order to avoid creating
// a padding oracle.
fmt.Printf("%s\n", ciphertext)
// Output: exampleplaintextexampleplaintext
}

View File

@ -3,13 +3,9 @@ package cipher_test
import (
"bytes"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"fmt"
"io"
"testing"
"github.com/emmansun/gmsm/internal/cryptotest"
"github.com/emmansun/gmsm/sm4"
)
@ -23,20 +19,6 @@ var sm4GCMTests = []struct {
"",
"232f0cfe308b49ea6fc88229b5dc858d",
},
{ // GB/T 15852.3-2019 A.4 GMAC
"feffe9928665731c6d6a8f9467308308",
"cafebabefacedbaddecaf888",
"",
"feedfacedeadbeeffeedfacedeadbeef",
"9d632570f93064264a20918e3081b4cd",
},
{ // GB/T 15852.3-2019 A.4 GMAC
"feffe9928665731c6d6a8f9467308308",
"cafebabefacedbaddecaf888",
"",
"feedfacedeadbeeffeedfacedeadbeefabaddad242831ec2217774244b7221b7",
"1eeaeb669e96bd059bd9929123030e78",
},
{ // GB/T 36624-2018 C.5 2
"00000000000000000000000000000000",
"000000000000000000000000",
@ -409,65 +391,3 @@ func TestGCMCounterWrap(t *testing.T) {
}
}
}
func TestSM4GCMRandom(t *testing.T) {
key := []byte("0123456789ABCDEF")
nonce := []byte("0123456789AB")
plaintext := make([]byte, 0x198)
io.ReadFull(rand.Reader, plaintext)
c, err := sm4.NewCipher(key)
if err != nil {
t.Fatal(err)
}
aead, err := cipher.NewGCMWithNonceSize(c, len(nonce))
if err != nil {
t.Fatal(err)
}
got := aead.Seal(nil, nonce, plaintext, nil)
result, err := aead.Open(got[:0], nonce, got, nil)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(result, plaintext) {
t.Error("gcm seal/open 408 bytes fail")
}
}
// Test GCM against the general cipher.AEAD interface tester.
func TestGCMAEAD(t *testing.T) {
minTagSize := 12
for _, keySize := range []int{128} {
// Use AES as underlying block cipher at different key sizes for GCM.
t.Run(fmt.Sprintf("SM4-%d", keySize), func(t *testing.T) {
rng := newRandReader(t)
key := make([]byte, keySize/8)
rng.Read(key)
block, err := sm4.NewCipher(key)
if err != nil {
panic(err)
}
// Test GCM with the current AES block with the standard nonce and tag
// sizes.
cryptotest.TestAEAD(t, func() (cipher.AEAD, error) { return cipher.NewGCM(block) })
// Test non-standard tag sizes.
t.Run("MinTagSize", func(t *testing.T) {
cryptotest.TestAEAD(t, func() (cipher.AEAD, error) { return cipher.NewGCMWithTagSize(block, minTagSize) })
})
// Test non-standard nonce sizes.
for _, nonceSize := range []int{1, 16, 100} {
t.Run(fmt.Sprintf("NonceSize-%d", nonceSize), func(t *testing.T) {
cryptotest.TestAEAD(t, func() (cipher.AEAD, error) { return cipher.NewGCMWithNonceSize(block, nonceSize) })
})
}
})
}
}

View File

@ -1,306 +0,0 @@
package cipher
import (
"crypto/cipher"
"crypto/subtle"
"errors"
"github.com/emmansun/gmsm/internal/alias"
"github.com/emmansun/gmsm/internal/byteorder"
)
// A LengthPreservingMode represents a block cipher running in a length preserving mode (HCTR,
// HCTR2 etc).
type LengthPreservingMode interface {
// EncryptBytes encrypts a number of plaintext bytes. The length of
// src must be NOT smaller than block size. Dst and src must overlap
// entirely or not at all.
//
// If len(dst) < len(src), EncryptBytes should panic. It is acceptable
// to pass a dst bigger than src, and in that case, Encrypt will
// only update dst[:len(src)] and will not touch the rest of dst.
//
// Multiple calls to EncryptBytes behave NOT same as if the concatenation of
// the src buffers was passed in a single run.
EncryptBytes(dst, src []byte)
// DecryptBytes decrypts a number of ciphertext bytes. The length of
// src must be NOT smaller than block size. Dst and src must overlap
// entirely or not at all.
//
// If len(dst) < len(src), DecryptBytes should panic. It is acceptable
// to pass a dst bigger than src, and in that case, DecryptBytes will
// only update dst[:len(src)] and will not touch the rest of dst.
//
// Multiple calls to DecryptBytes behave NOT same as if the concatenation of
// the src buffers was passed in a single run.
DecryptBytes(dst, src []byte)
// BlockSize returns the mode's block size.
BlockSize() int
}
// hctrFieldElement represents a value in GF(2¹²⁸). In order to reflect the HCTR
// standard and make binary.BigEndian suitable for marshaling these values, the
// bits are stored in big endian order. For example:
//
// the coefficient of x⁰ can be obtained by v.low >> 63.
// the coefficient of x⁶³ can be obtained by v.low & 1.
// the coefficient of x⁶⁴ can be obtained by v.high >> 63.
// the coefficient of x¹²⁷ can be obtained by v.high & 1.
type hctrFieldElement struct {
low, high uint64
}
// reverseBits reverses the order of the bits of 4-bit number in i.
func reverseBits(i int) int {
i = ((i << 2) & 0xc) | ((i >> 2) & 0x3)
i = ((i << 1) & 0xa) | ((i >> 1) & 0x5)
return i
}
// hctrAdd adds two elements of GF(2¹²⁸) and returns the sum.
func hctrAdd(x, y *hctrFieldElement) hctrFieldElement {
// Addition in a characteristic 2 field is just XOR.
return hctrFieldElement{x.low ^ y.low, x.high ^ y.high}
}
// hctrDouble returns the result of doubling an element of GF(2¹²⁸).
func hctrDouble(x *hctrFieldElement) (double hctrFieldElement) {
msbSet := x.high&1 == 1
// Because of the bit-ordering, doubling is actually a right shift.
double.high = x.high >> 1
double.high |= x.low << 63
double.low = x.low >> 1
// If the most-significant bit was set before shifting then it,
// conceptually, becomes a term of x^128. This is greater than the
// irreducible polynomial so the result has to be reduced. The
// irreducible polynomial is 1+x+x^2+x^7+x^128. We can subtract that to
// eliminate the term at x^128 which also means subtracting the other
// four terms. In characteristic 2 fields, subtraction == addition ==
// XOR.
if msbSet {
double.low ^= 0xe100000000000000
}
return
}
// hctrReductionTable is stored irreducible polynomial's double & add precomputed results.
// 0000 - 0
// 0001 - irreducible polynomial >> 3
// 0010 - irreducible polynomial >> 2
// 0011 - (irreducible polynomial >> 3 xor irreducible polynomial >> 2)
// ...
// 1000 - just the irreducible polynomial
var hctrReductionTable = []uint16{
0x0000, 0x1c20, 0x3840, 0x2460, 0x7080, 0x6ca0, 0x48c0, 0x54e0,
0xe100, 0xfd20, 0xd940, 0xc560, 0x9180, 0x8da0, 0xa9c0, 0xb5e0,
}
// hctr represents a Variable-Input-Length enciphering mode with a specific block cipher,
// and specific tweak and a hash key. See
// https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.470.5288
// GB/T 17964-2021 第11章 带泛杂凑函数的计数器工作模式
type hctr struct {
cipher cipher.Block
tweak [blockSize]byte
// productTable contains the first sixteen powers of the hash key.
// However, they are in bit reversed order.
productTable [16]hctrFieldElement
}
func (h *hctr) BlockSize() int {
return blockSize
}
// NewHCTR returns a [LengthPreservingMode] which encrypts/decrypts using the given [Block]
// in HCTR mode. The length of tweak and hash key must be the same as the [Block]'s block size.
func NewHCTR(cipher cipher.Block, tweak, hkey []byte) (LengthPreservingMode, error) {
if len(tweak) != blockSize || len(hkey) != blockSize {
return nil, errors.New("cipher: invalid tweak and/or hash key length")
}
c := &hctr{}
c.cipher = cipher
copy(c.tweak[:], tweak)
// We precompute 16 multiples of |key|. However, when we do lookups
// into this table we'll be using bits from a field element and
// therefore the bits will be in the reverse order. So normally one
// would expect, say, 4*key to be in index 4 of the table but due to
// this bit ordering it will actually be in index 0010 (base 2) = 2.
x := hctrFieldElement{
byteorder.BEUint64(hkey[:8]),
byteorder.BEUint64(hkey[8:blockSize]),
}
c.productTable[reverseBits(1)] = x
for i := 2; i < 16; i += 2 {
c.productTable[reverseBits(i)] = hctrDouble(&c.productTable[reverseBits(i/2)])
c.productTable[reverseBits(i+1)] = hctrAdd(&c.productTable[reverseBits(i)], &x)
}
return c, nil
}
// mul sets y to y*H, where H is the GCM key, fixed during NewHCTR.
func (h *hctr) mul(y *hctrFieldElement) {
var z hctrFieldElement
// Eliminate bounds checks in the loop.
_ = hctrReductionTable[0xf]
for i := 0; i < 2; i++ {
word := y.high
if i == 1 {
word = y.low
}
// Multiplication works by multiplying z by 16 and adding in
// one of the precomputed multiples of hash key.
for j := 0; j < 64; j += 4 {
msw := z.high & 0xf
z.high >>= 4
z.high |= z.low << 60
z.low >>= 4
z.low ^= uint64(hctrReductionTable[msw]) << 48
// the values in |table| are ordered for
// little-endian bit positions. See the comment
// in NewHCTR.
t := &h.productTable[word&0xf]
z.low ^= t.low
z.high ^= t.high
word >>= 4
}
}
*y = z
}
func (h *hctr) updateBlock(block []byte, y *hctrFieldElement) {
y.low ^= byteorder.BEUint64(block)
y.high ^= byteorder.BEUint64(block[8:])
h.mul(y)
}
// Universal Hash Function.
// Chapter 3.3 in https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.470.5288.
func (h *hctr) uhash(m []byte, out *[blockSize]byte) {
var y hctrFieldElement
msg := m
// update blocks
for len(msg) >= blockSize {
h.updateBlock(msg, &y)
msg = msg[blockSize:]
}
// update partial block & tweak
if len(msg) > 0 {
var partialBlock [blockSize]byte
copy(partialBlock[:], msg)
copy(partialBlock[len(msg):], h.tweak[:])
h.updateBlock(partialBlock[:], &y)
copy(partialBlock[:], h.tweak[len(msg):])
for i := len(msg); i < blockSize; i++ {
partialBlock[i] = 0
}
h.updateBlock(partialBlock[:], &y)
} else {
h.updateBlock(h.tweak[:], &y)
}
// update bit string length (|M|)₂
y.high ^= uint64(len(m)+blockSize) * 8
h.mul(&y)
// output result
byteorder.BEPutUint64(out[:], y.low)
byteorder.BEPutUint64(out[8:], y.high)
}
func (h *hctr) EncryptBytes(ciphertext, plaintext []byte) {
if len(ciphertext) < len(plaintext) {
panic("cipher: ciphertext is smaller than plaintext")
}
if len(plaintext) < blockSize {
panic("cipher: plaintext length is smaller than the block size")
}
if alias.InexactOverlap(ciphertext[:len(plaintext)], plaintext) {
panic("cipher: invalid buffer overlap")
}
var z1, z2 [blockSize]byte
// a) z1 generation
h.uhash(plaintext[blockSize:], &z1)
subtle.XORBytes(z1[:], z1[:], plaintext[:blockSize])
// b) z2 generation
h.cipher.Encrypt(z2[:], z1[:])
// c) CTR
subtle.XORBytes(z1[:], z1[:], z2[:])
h.ctr(ciphertext[blockSize:], plaintext[blockSize:], &z1)
// d) first ciphertext block generation
h.uhash(ciphertext[blockSize:], &z1)
subtle.XORBytes(ciphertext, z2[:], z1[:])
}
func (h *hctr) DecryptBytes(plaintext, ciphertext []byte) {
if len(plaintext) < len(ciphertext) {
panic("cipher: plaintext is smaller than cihpertext")
}
if len(ciphertext) < blockSize {
panic("cipher: ciphertext length is smaller than the block size")
}
if alias.InexactOverlap(plaintext[:len(ciphertext)], ciphertext) {
panic("cipher: invalid buffer overlap")
}
var z1, z2 [blockSize]byte
// a) z2 generation
h.uhash(ciphertext[blockSize:], &z2)
subtle.XORBytes(z2[:], z2[:], ciphertext[:blockSize])
// b) z1 generation
h.cipher.Decrypt(z1[:], z2[:])
// c) CTR
subtle.XORBytes(z2[:], z2[:], z1[:])
h.ctr(plaintext[blockSize:], ciphertext[blockSize:], &z2)
// d) first plaintext block generation
h.uhash(plaintext[blockSize:], &z2)
subtle.XORBytes(plaintext, z2[:], z1[:])
}
func (h *hctr) ctr(dst, src []byte, baseCtr *[blockSize]byte) {
ctr := make([]byte, blockSize)
num := make([]byte, blockSize)
i := uint64(1)
if concCipher, ok := h.cipher.(concurrentBlocks); ok {
batchSize := concCipher.Concurrency() * blockSize
if len(src) >= batchSize {
var ctrs = make([]byte, batchSize)
for len(src) >= batchSize {
for j := 0; j < concCipher.Concurrency(); j++ {
// (i)₂
byteorder.BEPutUint64(num[blockSize-8:], i)
subtle.XORBytes(ctrs[j*blockSize:], baseCtr[:], num)
i++
}
concCipher.EncryptBlocks(ctrs, ctrs)
subtle.XORBytes(dst, src, ctrs)
src = src[batchSize:]
dst = dst[batchSize:]
}
}
}
for len(src) > 0 {
// (i)₂
byteorder.BEPutUint64(num[blockSize-8:], i)
subtle.XORBytes(ctr, baseCtr[:], num)
h.cipher.Encrypt(ctr, ctr)
n := subtle.XORBytes(dst, src, ctr)
src = src[n:]
dst = dst[n:]
i++
}
}

View File

@ -1,75 +0,0 @@
package cipher_test
import (
"bytes"
"encoding/hex"
"testing"
"github.com/emmansun/gmsm/cipher"
"github.com/emmansun/gmsm/sm4"
)
var hctrSM4TestVectors = []struct {
key string
hashKey string
tweak string
plaintext string
ciphertext string
}{
{
"2B7E151628AED2A6ABF7158809CF4F3C",
"000102030405060708090A0B0C0D0E0F",
"F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF",
"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c37106bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c37106bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
"8858dda3034233e377936b76ce7edeb6a245075a37800b0b996e8e974c9032ac8de40d90ee4ee5fb58bc10cbc95779485ab38ffb0b4f961d85f086db705ff723edbeaec649b3b406b11b96a418a9c2c51ef41cdd24e472c18336e9efcd07b7e264a1e2d46615198eb74938d72104fa89294a6360cdb6b032a704cf07a087bb2283598552701b2f710d6528d9c3f4dab529afef4413f25169b6cbf8168ccbfa02a2f507513d0cb3802da34dbd928b67e6afc30ca91011070cfd40c2ef3d4ac041",
},
{
"2B7E151628AED2A6ABF7158809CF4F3C",
"000102030405060708090A0B0C0D0E0F",
"F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF",
"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
"9cd7481d3b7ca904b14b4084d9d4c83ed39eac8e16747895fc2ae1eecd220276af3d0d2f21cb3807561347c81ad138117dd85c652afe16a47dc68eb884068ae3",
},
{
"2B7E151628AED2A6ABF7158809CF4F3C",
"000102030405060708090A0B0C0D0E0F",
"F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF",
"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417b",
"f7505aff357ac13107cdb2848c6bb2dcdda473f7a6ea939d44f52c986c11ca9341042f2b0091a1ca5c8f708cae8ca6a5c59e2228b3616c4455627722",
},
{
"2B7E151628AED2A6ABF7158809CF4F3C",
"000102030405060708090A0B0C0D0E0F",
"F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF",
"6bc1bee22e409f96e93d7e117393172a",
"b7b1dd75f608012dc69621d4ea720a60",
},
}
func TestHCTR(t *testing.T) {
for i, test := range hctrSM4TestVectors {
key1, _ := hex.DecodeString(test.key)
key2, _ := hex.DecodeString(test.hashKey)
tw, _ := hex.DecodeString(test.tweak)
plaintext, _ := hex.DecodeString(test.plaintext)
ciphertext, _ := hex.DecodeString(test.ciphertext)
got := make([]byte, len(plaintext))
c, err := sm4.NewCipher(key1)
if err != nil {
t.Fatal(err)
}
hctr, err := cipher.NewHCTR(c, tw, key2)
if err != nil {
t.Fatal(err)
}
hctr.EncryptBytes(got, plaintext)
if !bytes.Equal(got, ciphertext) {
t.Fatalf("%v case encrypt failed, got %x\n", i+1, got)
}
hctr.DecryptBytes(got, ciphertext)
if !bytes.Equal(got, plaintext) {
t.Fatalf("%v case decrypt failed, got %x\n", i+1, got)
}
}
}

View File

@ -5,7 +5,6 @@ import (
"crypto/cipher"
"testing"
"github.com/emmansun/gmsm/internal/cryptotest"
"github.com/emmansun/gmsm/sm4"
)
@ -72,19 +71,3 @@ func TestOFB(t *testing.T) {
}
}
}
func TestOFBStream(t *testing.T) {
t.Run("SM4", func(t *testing.T) {
rng := newRandReader(t)
key := make([]byte, 16)
rng.Read(key)
block, err := sm4.NewCipher(key)
if err != nil {
panic(err)
}
cryptotest.TestStreamFromBlock(t, block, cipher.NewOFB)
})
}

View File

@ -1,128 +0,0 @@
// Output feedback with a nonlinear function operation mode (OFBNLF mode) in Chinese national standard GB/T 17964-2021.
// See GB/T 17964-2021 Chapter 13.
package cipher
import (
"bytes"
_cipher "crypto/cipher"
"errors"
)
type ofbnlf struct {
cipherFunc CipherCreator
b _cipher.Block
blockSize int
iv []byte
}
func newOFBNLF(cipherFunc CipherCreator, key, iv []byte) (*ofbnlf, error) {
c := &ofbnlf{
cipherFunc: cipherFunc,
}
var err error
c.b, err = cipherFunc(key)
if err != nil {
return nil, err
}
c.blockSize = c.b.BlockSize()
if len(iv) != c.blockSize {
return nil, errors.New("cipher: IV length must equal block size")
}
c.iv = bytes.Clone(iv)
return c, nil
}
type ofbnlfEncrypter ofbnlf
// NewOFBNLFEncrypter returns a BlockMode which encrypts in Output feedback
// with a nonlinear function operation mode, using the given Block.
// The length of iv must be the same as the Block's block size.
func NewOFBNLFEncrypter(cipherFunc CipherCreator, key, iv []byte) (_cipher.BlockMode, error) {
c, err := newOFBNLF(cipherFunc, key, iv)
if err != nil {
return nil, err
}
return (*ofbnlfEncrypter)(c), nil
}
func (x *ofbnlfEncrypter) BlockSize() int { return x.blockSize }
func (x *ofbnlfEncrypter) CryptBlocks(dst, src []byte) {
validate(x.blockSize, dst, src)
iv := x.iv
k := make([]byte, x.blockSize)
for len(src) > 0 {
x.b.Encrypt(k, iv)
c, err := x.cipherFunc(k)
if err != nil {
panic(err)
}
c.Encrypt(dst, src)
src = src[x.blockSize:]
dst = dst[x.blockSize:]
copy(iv, k)
}
// Save the iv for the next CryptBlocks call.
copy(x.iv, iv)
}
func (x *ofbnlfEncrypter) SetIV(iv []byte) {
if len(iv) != len(x.iv) {
panic("cipher: incorrect length IV")
}
copy(x.iv, iv)
}
type ofbnlfDecrypter ofbnlf
// NewOFBNLFDecrypter returns a BlockMode which decrypts in Output feedback
// with a nonlinear function operation mode, using the given Block.
// The length of iv must be the same as the Block's block size and must match
// the iv used to encrypt the data.
func NewOFBNLFDecrypter(cipherFunc CipherCreator, key, iv []byte) (_cipher.BlockMode, error) {
c, err := newOFBNLF(cipherFunc, key, iv)
if err != nil {
return nil, err
}
return (*ofbnlfDecrypter)(c), nil
}
func (x *ofbnlfDecrypter) BlockSize() int { return x.blockSize }
func (x *ofbnlfDecrypter) CryptBlocks(dst, src []byte) {
validate(x.blockSize, dst, src)
if len(src) == 0 {
return
}
iv := x.iv
k := make([]byte, x.blockSize)
for len(src) > 0 {
x.b.Encrypt(k, iv)
c, err := x.cipherFunc(k)
if err != nil {
panic(err)
}
c.Decrypt(dst, src)
src = src[x.blockSize:]
dst = dst[x.blockSize:]
copy(iv, k)
}
// Save the iv for the next CryptBlocks call.
copy(x.iv, iv)
}
func (x *ofbnlfDecrypter) SetIV(iv []byte) {
if len(iv) != len(x.iv) {
panic("cipher: incorrect length IV")
}
copy(x.iv, iv)
}

View File

@ -1,71 +0,0 @@
package cipher_test
import (
"bytes"
"crypto/rand"
"encoding/hex"
"io"
"testing"
"github.com/emmansun/gmsm/cipher"
"github.com/emmansun/gmsm/sm4"
)
var ofbnlfSM4TestVectors = []struct {
key string
iv string
plaintext string
ciphertext string
}{
{
"2B7E151628AED2A6ABF7158809CF4F3C",
"000102030405060708090A0B0C0D0E0F",
"6BC1BEE22E409F96E93D7E117393172AAE2D8A571E03AC9C9EB76FAC45AF8E5130C81C46A35CE411E5FBC1191A0A52EFF69F2445DF4F9B17AD2B417BE66C3710",
"00A5B5C9E645557C20CE7F267736F308A18037828850B9D78883CA622851F86CB7CAEFDFB6D4CABA6AE2D2FCE369CEB31001DD71FDDA9341F8D221CB720FF27B",
},
}
func TestOFBNLF(t *testing.T) {
for i, test := range ofbnlfSM4TestVectors {
key, _ := hex.DecodeString(test.key)
iv, _ := hex.DecodeString(test.iv)
plaintext, _ := hex.DecodeString(test.plaintext)
ciphertext, _ := hex.DecodeString(test.ciphertext)
got := make([]byte, len(plaintext))
encrypter, err := cipher.NewOFBNLFEncrypter(sm4.NewCipher, key, iv)
if err != nil {
t.Fatal(err)
}
encrypter.CryptBlocks(got, plaintext)
if !bytes.Equal(got, ciphertext) {
t.Fatalf("%v case encrypt failed, got %x\n", i+1, got)
}
decrypter, err := cipher.NewOFBNLFDecrypter(sm4.NewCipher, key, iv)
if err != nil {
t.Fatal(err)
}
decrypter.CryptBlocks(got, ciphertext)
if !bytes.Equal(got, plaintext) {
t.Fatalf("%v case decrypt failed, got %x\n", i+1, got)
}
}
}
func TestSM4OFBNLFRandom(t *testing.T) {
key, _ := hex.DecodeString(ofbnlfSM4TestVectors[0].key)
iv := []byte("0123456789ABCDEF")
encrypter, _ := cipher.NewOFBNLFEncrypter(sm4.NewCipher, key, iv)
decrypter, _ := cipher.NewOFBNLFDecrypter(sm4.NewCipher, key, iv)
for i := 1; i <= 50; i++ {
plaintext := make([]byte, i*16)
ciphertext := make([]byte, i*16)
got := make([]byte, i*16)
io.ReadFull(rand.Reader, plaintext)
encrypter.CryptBlocks(ciphertext, plaintext)
decrypter.CryptBlocks(got, ciphertext)
if !bytes.Equal(got, plaintext) {
t.Errorf("test %v blocks failed", i)
}
}
}

View File

@ -1,13 +0,0 @@
// Copyright 2024 Sun Yimin. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package cipher
import "crypto/cipher"
type SeekableStream interface {
cipher.Stream
// XORKeyStreamAt XORs the given src with the key stream at the given offset and writes the result to dst.
XORKeyStreamAt(dst, src []byte, offset uint64)
}

View File

@ -1,68 +1,249 @@
package cipher
import (
"crypto/cipher"
_cipher "crypto/cipher"
"encoding/binary"
"errors"
"sync"
"github.com/emmansun/gmsm/internal/byteorder"
"github.com/emmansun/gmsm/internal/cipher/xts"
"github.com/emmansun/gmsm/internal/alias"
"github.com/emmansun/gmsm/internal/subtle"
)
// NewXTSEncrypter creates a Cipher given a function for creating the underlying
// block cipher (which must have a block size of 16 bytes).
func NewXTSEncrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte) (cipher.BlockMode, error) {
return xts.NewXTSEncrypter(cipherFunc, key, tweakKey, tweak, false)
const GF128_FDBK byte = 0x87
type CipherCreator func([]byte) (_cipher.Block, error)
type concurrentBlocks interface {
Concurrency() int
EncryptBlocks(dst, src []byte)
DecryptBlocks(dst, src []byte)
}
// NewXTSEncrypterWithSector creates a Cipher given a function for creating the underlying
// block cipher (which must have a block size of 16 bytes) with sector number.
func NewXTSEncrypterWithSector(cipherFunc CipherCreator, key, tweakKey []byte, sectorNum uint64) (cipher.BlockMode, error) {
tweak := make([]byte, blockSize)
byteorder.LEPutUint64(tweak[:8], sectorNum)
return NewXTSEncrypter(cipherFunc, key, tweakKey, tweak)
// A XTSBlockMode represents a block cipher running in a XTS mode
type XTSBlockMode interface {
// BlockSize returns the mode's block size.
BlockSize() int
// Encrypt encrypts or decrypts a number of blocks. The length of
// src must be a multiple of the block size. Dst and src must overlap
// entirely or not at all.
//
Encrypt(dst, src []byte, sectorNum uint64)
// Decrypt decrypts a number of blocks. The length of
// src must be a multiple of the block size. Dst and src must overlap
// entirely or not at all.
//
Decrypt(dst, src []byte, sectorNum uint64)
}
// NewGBXTSEncrypter creates a Cipher given a function for creating the underlying
// block cipher (which must have a block size of 16 bytes).
// It follows GB/T 17964-2021.
func NewGBXTSEncrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte) (cipher.BlockMode, error) {
return xts.NewXTSEncrypter(cipherFunc, key, tweakKey, tweak, true)
// Cipher contains an expanded key structure. It is safe for concurrent use if
// the underlying block cipher is safe for concurrent use.
type xts struct {
k1, k2 _cipher.Block
}
// NewGBXTSEncrypterWithSector creates a Cipher given a function for creating the underlying
// block cipher (which must have a block size of 16 bytes) with sector number.
// It follows GB/T 17964-2021.
func NewGBXTSEncrypterWithSector(cipherFunc CipherCreator, key, tweakKey []byte, sectorNum uint64) (cipher.BlockMode, error) {
tweak := make([]byte, blockSize)
byteorder.LEPutUint64(tweak[:8], sectorNum)
return NewGBXTSEncrypter(cipherFunc, key, tweakKey, tweak)
// blockSize is the block size that the underlying cipher must have. XTS is
// only defined for 16-byte ciphers.
const blockSize = 16
var tweakPool = sync.Pool{
New: func() interface{} {
return new([blockSize]byte)
},
}
// NewXTSDecrypter creates a Cipher given a function for creating the underlying
// block cipher (which must have a block size of 16 bytes) for decryption.
func NewXTSDecrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte) (cipher.BlockMode, error) {
return xts.NewXTSDecrypter(cipherFunc, key, tweakKey, tweak, false)
// NewXTS creates a Cipher given a function for creating the underlying
// block cipher (which must have a block size of 16 bytes). The key must be
// twice the length of the underlying cipher's key.
func NewXTS(cipherFunc CipherCreator, key []byte) (XTSBlockMode, error) {
k1, err := cipherFunc(key[:len(key)/2])
if err != nil {
return nil, err
}
k2, err := cipherFunc(key[len(key)/2:])
c := &xts{
k1,
k2,
}
if c.k1.BlockSize() != blockSize {
err = errors.New("xts: cipher does not have a block size of 16")
return nil, err
}
return c, nil
}
// NewXTSDecrypterWithSector creates a Cipher given a function for creating the underlying
// block cipher (which must have a block size of 16 bytes) with sector number for decryption.
func NewXTSDecrypterWithSector(cipherFunc CipherCreator, key, tweakKey []byte, sectorNum uint64) (cipher.BlockMode, error) {
tweak := make([]byte, blockSize)
byteorder.LEPutUint64(tweak[:8], sectorNum)
return NewXTSDecrypter(cipherFunc, key, tweakKey, tweak)
func (c *xts) BlockSize() int {
return blockSize
}
// NewGBXTSDecrypter creates a Cipher given a function for creating the underlying
// block cipher (which must have a block size of 16 bytes) for decryption.
// It follows GB/T 17964-2021.
func NewGBXTSDecrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte) (cipher.BlockMode, error) {
return xts.NewXTSDecrypter(cipherFunc, key, tweakKey, tweak, true)
// Encrypt encrypts a sector of plaintext and puts the result into ciphertext.
// Plaintext and ciphertext must overlap entirely or not at all.
// Sectors must be a multiple of 16 bytes and less than 2²⁴ bytes.
func (c *xts) Encrypt(ciphertext, plaintext []byte, sectorNum uint64) {
if len(ciphertext) < len(plaintext) {
panic("xts: ciphertext is smaller than plaintext")
}
if len(plaintext) < blockSize {
panic("xts: plaintext length is smaller than the block size")
}
if alias.InexactOverlap(ciphertext[:len(plaintext)], plaintext) {
panic("xts: invalid buffer overlap")
}
tweak := tweakPool.Get().(*[blockSize]byte)
for i := range tweak {
tweak[i] = 0
}
binary.LittleEndian.PutUint64(tweak[:8], sectorNum)
c.k2.Encrypt(tweak[:], tweak[:])
lastCiphertext := ciphertext
if concCipher, ok := c.k1.(concurrentBlocks); ok {
batchSize := concCipher.Concurrency() * blockSize
var tweaks []byte = make([]byte, batchSize)
for len(plaintext) >= batchSize {
for i := 0; i < concCipher.Concurrency(); i++ {
copy(tweaks[blockSize*i:], tweak[:])
mul2(tweak)
}
subtle.XORBytes(ciphertext, plaintext, tweaks)
concCipher.EncryptBlocks(ciphertext, ciphertext)
subtle.XORBytes(ciphertext, ciphertext, tweaks)
plaintext = plaintext[batchSize:]
lastCiphertext = ciphertext[batchSize-blockSize:]
ciphertext = ciphertext[batchSize:]
}
}
for len(plaintext) >= blockSize {
subtle.XORBytes(ciphertext, plaintext, tweak[:])
c.k1.Encrypt(ciphertext, ciphertext)
subtle.XORBytes(ciphertext, ciphertext, tweak[:])
plaintext = plaintext[blockSize:]
lastCiphertext = ciphertext
ciphertext = ciphertext[blockSize:]
mul2(tweak)
}
// is there a final partial block to handle?
if remain := len(plaintext); remain > 0 {
var x [blockSize]byte
//Copy the final ciphertext bytes
copy(ciphertext, lastCiphertext[:remain])
//Copy the final plaintext bytes
copy(x[:], plaintext)
//Steal ciphertext to complete the block
copy(x[remain:], lastCiphertext[remain:blockSize])
//Merge the tweak into the input block
subtle.XORBytes(x[:], x[:], tweak[:])
//Encrypt the final block using K1
c.k1.Encrypt(x[:], x[:])
//Merge the tweak into the output block
subtle.XORBytes(lastCiphertext, x[:], tweak[:])
}
tweakPool.Put(tweak)
}
// NewGBXTSDecrypterWithSector creates a Cipher given a function for creating the underlying
// block cipher (which must have a block size of 16 bytes) with sector number for decryption.
// It follows GB/T 17964-2021.
func NewGBXTSDecrypterWithSector(cipherFunc CipherCreator, key, tweakKey []byte, sectorNum uint64) (cipher.BlockMode, error) {
tweak := make([]byte, blockSize)
byteorder.LEPutUint64(tweak[:8], sectorNum)
return NewGBXTSDecrypter(cipherFunc, key, tweakKey, tweak)
// Decrypt decrypts a sector of ciphertext and puts the result into plaintext.
// Plaintext and ciphertext must overlap entirely or not at all.
// Sectors must be a multiple of 16 bytes and less than 2²⁴ bytes.
func (c *xts) Decrypt(plaintext, ciphertext []byte, sectorNum uint64) {
if len(plaintext) < len(ciphertext) {
panic("xts: plaintext is smaller than ciphertext")
}
if len(ciphertext) < blockSize {
panic("xts: ciphertext length is smaller than the block size")
}
if alias.InexactOverlap(plaintext[:len(ciphertext)], ciphertext) {
panic("xts: invalid buffer overlap")
}
tweak := tweakPool.Get().(*[blockSize]byte)
for i := range tweak {
tweak[i] = 0
}
binary.LittleEndian.PutUint64(tweak[:8], sectorNum)
c.k2.Encrypt(tweak[:], tweak[:])
if concCipher, ok := c.k1.(concurrentBlocks); ok {
batchSize := concCipher.Concurrency() * blockSize
var tweaks []byte = make([]byte, batchSize)
for len(ciphertext) >= batchSize {
for i := 0; i < concCipher.Concurrency(); i++ {
copy(tweaks[blockSize*i:], tweak[:])
mul2(tweak)
}
subtle.XORBytes(plaintext, ciphertext, tweaks)
concCipher.DecryptBlocks(plaintext, plaintext)
subtle.XORBytes(plaintext, plaintext, tweaks)
plaintext = plaintext[batchSize:]
ciphertext = ciphertext[batchSize:]
}
}
for len(ciphertext) >= 2*blockSize {
subtle.XORBytes(plaintext, ciphertext, tweak[:])
c.k1.Decrypt(plaintext, plaintext)
subtle.XORBytes(plaintext, plaintext, tweak[:])
plaintext = plaintext[blockSize:]
ciphertext = ciphertext[blockSize:]
mul2(tweak)
}
if remain := len(ciphertext); remain >= blockSize {
var x [blockSize]byte
if remain > blockSize {
var tt [blockSize]byte
copy(tt[:], tweak[:])
mul2(&tt)
subtle.XORBytes(x[:], ciphertext, tt[:])
c.k1.Decrypt(x[:], x[:])
subtle.XORBytes(plaintext, x[:], tt[:])
//Retrieve the length of the final block
remain -= blockSize
//Copy the final plaintext bytes
copy(plaintext[blockSize:], plaintext)
//Copy the final ciphertext bytes
copy(x[:], ciphertext[blockSize:])
//Steal ciphertext to complete the block
copy(x[remain:], plaintext[remain:blockSize])
} else {
//The last block contains exactly 128 bits
copy(x[:], ciphertext)
}
subtle.XORBytes(x[:], x[:], tweak[:])
c.k1.Decrypt(x[:], x[:])
subtle.XORBytes(plaintext, x[:], tweak[:])
}
tweakPool.Put(tweak)
}
// mul2 multiplies tweak by 2 in GF(2¹²⁸) with an irreducible polynomial of
// x¹²⁸ + x⁷ + x² + x + 1.
func mul2(tweak *[blockSize]byte) {
var carryIn byte
for j := range tweak {
carryOut := tweak[j] >> 7
tweak[j] = (tweak[j] << 1) + carryIn
carryIn = carryOut
}
if carryIn != 0 {
// If we have a carry bit then we need to subtract a multiple
// of the irreducible polynomial (x¹²⁸ + x⁷ + x² + x + 1).
// By dropping the carry bit, we're subtracting the x^128 term
// so all that remains is to subtract x⁷ + x² + x + 1.
// Subtraction (and addition) in this representation is just
// XOR.
tweak[0] ^= GF128_FDBK // 1<<7 | 1<<2 | 1<<1 | 1
}
}

View File

@ -69,22 +69,15 @@ func fromHex(s string) []byte {
func TestXTS(t *testing.T) {
for i, test := range xtsTestVectors {
key := fromHex(test.key)
encrypter, err := cipher.NewXTSEncrypterWithSector(sm4.NewCipher, key[:len(key)/2], key[len(key)/2:], test.sector)
c, err := cipher.NewXTS(sm4.NewCipher, fromHex(test.key))
if err != nil {
t.Errorf("#%d: failed to create encrypter: %s", i, err)
continue
}
decrypter, err := cipher.NewXTSDecrypterWithSector(sm4.NewCipher, key[:len(key)/2], key[len(key)/2:], test.sector)
if err != nil {
t.Errorf("#%d: failed to create decrypter: %s", i, err)
t.Errorf("#%d: failed to create cipher: %s", i, err)
continue
}
plaintext := fromHex(test.plaintext)
ciphertext := make([]byte, len(plaintext))
encrypter.CryptBlocks(ciphertext, plaintext)
c.Encrypt(ciphertext, plaintext, test.sector)
expectedCiphertext := fromHex(test.ciphertext)
if !bytes.Equal(ciphertext, expectedCiphertext) {
t.Errorf("#%d: encrypted failed, got: %x, want: %x", i, ciphertext, expectedCiphertext)
@ -92,136 +85,7 @@ func TestXTS(t *testing.T) {
}
decrypted := make([]byte, len(ciphertext))
decrypter.CryptBlocks(decrypted, ciphertext)
if !bytes.Equal(decrypted, plaintext) {
t.Errorf("#%d: decryption failed, got: %x, want: %x", i, decrypted, plaintext)
}
}
}
// Test data is from GB/T 17964-2021 B.7
var xtsGBTestVectors = []struct {
key string
tweak string
plaintext string
ciphertext string
}{
{
"2B7E151628AED2A6ABF7158809CF4F3C000102030405060708090A0B0C0D0E0F",
"F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF",
"6BC1BEE22E409F96E93D7E117393172AAE2D8A571E03AC9C9EB76FAC45AF8E5130C81C46A35CE411E5FBC1191A0A52EFF69F2445DF4F9B17",
"E9538251C71D7B80BBE4483FEF497BD12C5C581BD6242FC51E08964FB4F60FDB0BA42F63499279213D318D2C11F6886E903BE7F93A1B3479",
},
}
func TestGBXTSSample(t *testing.T) {
for i, test := range xtsGBTestVectors {
key := fromHex(test.key)
tweak := fromHex(test.tweak)
encrypter, err := cipher.NewGBXTSEncrypter(sm4.NewCipher, key[:len(key)/2], key[len(key)/2:], tweak)
if err != nil {
t.Errorf("#%d: failed to create encrypter: %s", i, err)
continue
}
decrypter, err := cipher.NewGBXTSDecrypter(sm4.NewCipher, key[:len(key)/2], key[len(key)/2:], tweak)
if err != nil {
t.Errorf("#%d: failed to create decrypter: %s", i, err)
continue
}
plaintext := fromHex(test.plaintext)
ciphertext := make([]byte, len(plaintext))
encrypter.CryptBlocks(ciphertext, plaintext)
expectedCiphertext := fromHex(test.ciphertext)
if !bytes.Equal(ciphertext, expectedCiphertext) {
t.Errorf("#%d: encrypted failed, got: %x, want: %x", i, ciphertext, expectedCiphertext)
continue
}
decrypted := make([]byte, len(ciphertext))
decrypter.CryptBlocks(decrypted, ciphertext)
if !bytes.Equal(decrypted, plaintext) {
t.Errorf("#%d: decryption failed, got: %x, want: %x", i, decrypted, plaintext)
}
}
}
var gbXtsTestVectors = []struct {
key string
sector uint64
plaintext string
ciphertext string
}{
{ // XTS-SM4-128 applied for a data unit of 32 bytes
"0000000000000000000000000000000000000000000000000000000000000000",
0,
"0000000000000000000000000000000000000000000000000000000000000000",
"d9b421f731c894fdc35b77291fe4e3b0e58e55e613a862b4d2b0f1073b4b4fd0",
}, {
"1111111111111111111111111111111122222222222222222222222222222222",
0x3333333333,
"4444444444444444444444444444444444444444444444444444444444444444",
"a74d726c11196a32be04e001ff29d0c7724feef81d666ae5afdfe4649544fcf5",
}, {
"fffefdfcfbfaf9f8f7f6f5f4f3f2f1f022222222222222222222222222222222",
0x3333333333,
"4444444444444444444444444444444444444444444444444444444444444444",
"7f76088effadf70c02ea9f95da0628d3ef2d6a77004beaa9016001d6789dd5a0",
}, { // XTS-SM4-128 applied for a data unit of 512 bytes
"2718281828459045235360287471352631415926535897932384626433832795",
0,
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
"54dd65b6326faea8fad1a83c63614af398bddb6824735dab93ec4e75734215d463f67daf53742fb2a2847d5fde3984f8882cfd5fa9d6642e1e871c155202440044251465211628ba86f8d2998387a685edde23c07610b7388aab17f205aa5dada33c0a8a4225bc114254c796f800c638e016d199cd21dc28e92dc2b8587545509a8e1d659c596d3f6c8c225a27bdb2b02fe5a0c0183a592b396d32765fe733afb438a6ffb305ae1377c56d872badcebbd37812ff79f0571b3f977537570a1f76b9a50c49ab8d867fa024ea4483a25f7947b07885bb839e777abe76af11adf3108d1195933f96b7949b0664bdb89beb3bc48fb5f5d2109d32332f17c9a6ddea55441d1bbf43280ec7e75791e234d651a0716209eb21ae06061e33a72b0c530cb15fe0b55016b188dad75c4c50232dce1f5df61911c79bee60397b64bb914c0f26efcfb6ffab2bb33bdfd8db98c44debbd4ca865d41cbe1d0801b01aba2603cbea599b32c836789deeb9a3c18f3cae977b42ec81f1dfef6e098dd9e9dd6c1822bb938b08641bb72461f8d38c1724a43ae1254b9223e2270cf9f7d71a6bf093df2079fd2cc2fe87e846d799de30483f80164c31e65d8aae5f72d6dc71118932a008df547c712bee45ddebcdce098d673ef5ede91edfd45d17cb90963d3e2e2e2508a376a7b1af4d69e756ea5df52ac440791d57d56b5e057ad00e077d2df5009416",
}, { // Vector 5
"2718281828459045235360287471352631415926535897932384626433832795",
1,
"27a7479befa1d476489f308cd4cfa6e2a96e4bbe3208ff25287dd3819616e89cc78cf7f5e543445f8333d8fa7f56000005279fa5d8b5e4ad40e736ddb4d35412328063fd2aab53e5ea1e0a9f332500a5df9487d07a5c92cc512c8866c7e860ce93fdf166a24912b422976146ae20ce846bb7dc9ba94a767aaef20c0d61ad02655ea92dc4c4e41a8952c651d33174be51a10c421110e6d81588ede82103a252d8a750e8768defffed9122810aaeb99f9172af82b604dc4b8e51bcb08235a6f4341332e4ca60482a4ba1a03b3e65008fc5da76b70bf1690db4eae29c5f1badd03c5ccf2a55d705ddcd86d449511ceb7ec30bf12b1fa35b913f9f747a8afd1b130e94bff94effd01a91735ca1726acd0b197c4e5b03393697e126826fb6bbde8ecc1e08298516e2c9ed03ff3c1b7860f6de76d4cecd94c8119855ef5297ca67e9f3e7ff72b1e99785ca0a7e7720c5b36dc6d72cac9574c8cbbc2f801e23e56fd344b07f22154beba0f08ce8891e643ed995c94d9a69c9f1b5f499027a78572aeebd74d20cc39881c213ee770b1010e4bea718846977ae119f7a023ab58cca0ad752afe656bb3c17256a9f6e9bf19fdd5a38fc82bbe872c5539edb609ef4f79c203ebb140f2e583cb2ad15b4aa5b655016a8449277dbd477ef2c8d6c017db738b18deb4a427d1923ce3ff262735779a418f20a282df920147beabe421ee5319d0568",
"0e36ba273dd121afc77e0d8c00aa4a665f801f3607af61b61058b2f5d007310822200eaaeef759d515ebd032dad4235f5cd2dc735b57b56e003bce3f56890618877db69aa4519edcf681c6fc19c9c4a5655372d1549148c759efba00140275b46b6a5f6522de1702c48ff209a1dd7d1f56e775252796a09c20f903bfb3935bc79c0cdcaa9d2f30e616160e0662fb35311676e86e18d7d90d4203bc6862a9187b8657162143ce914750a86f984cf660311917e00fcf450ee188f088b4222522276bf3391e94de4fdad4134dfc7d08113c65e1b103bd3ad75fb13bba7f842451f9023ed21f1d23bc1c57d593932e021548bbff61ea9a24f359b4f7a8f2a998587495b726411f84734b189f65c4e79f09c7875f9c924b32e5bf2785a9935854e08ce86f5a4a399af6731099a13e10db0b32b888865b4416d69014a8cdb28b3912ec0b832835df7b59637d0687747815ba7cf9efae862dd6e80763acb50898fe1b3ba13a39d81b20d6d50613fbb5fbdcae2a7a87b9377eec455a8bae5102d5e6a7bea9b6b77d3f9895b277a55a524721cd0e59ce35e915de622480c5e0d31d153282dd832278fd2b795933f5dc591c17bd6d7f38fcd6afce551e8485109673881519d2845395ce9ceaea6306e38a73f9bb990931323a3136d18ee76c3e727cfb07cf386519313e1c44adcc50ae79bfac6952e3b98948206fb3dc3ebaed556bf27f16",
}, { // XTS-SM4-128 applied for a data unit that is not a multiple of 16 bytes, but should be a complte byte
"c46acc2e7e013cb71cdbf750cf76b000249fbf4fb6cd17607773c23ffa2c4330",
94,
"7e9c2289cba460e470222953439cdaa892a5433d4dab2a3f67",
"4d5501ea41cf6b6532b4b7129c6f6ee74605d9fb66f1f12c0c",
}, {
"56ffcc9bbbdf413f0fc0f888f44b7493bb1925a39b8adf02d9009bb16db0a887",
144,
"9a839cc14363bafcfc0cc93b14f8e769d35b94cc98267438e3",
"f04f3f16b354cccdc39fc664ec7f8db010a83bcacbc5c96353",
},
{
"7454a43b87b1cf0dec95032c22873be3cace3bb795568854c1a008c07c5813f3",
108,
"41088fa15195b2733fe824d2c1fdc8306080863945fb2a73cf",
"791a9469ed5a22d8195ac37c43c1b0377dc15126349bed1465",
},
}
func TestGBXTS(t *testing.T) {
for i, test := range gbXtsTestVectors {
key := fromHex(test.key)
encrypter, err := cipher.NewGBXTSEncrypterWithSector(sm4.NewCipher, key[:len(key)/2], key[len(key)/2:], test.sector)
if err != nil {
t.Errorf("#%d: failed to create encrypter: %s", i, err)
continue
}
decrypter, err := cipher.NewGBXTSDecrypterWithSector(sm4.NewCipher, key[:len(key)/2], key[len(key)/2:], test.sector)
if err != nil {
t.Errorf("#%d: failed to create decrypter: %s", i, err)
continue
}
plaintext := fromHex(test.plaintext)
ciphertext := make([]byte, len(plaintext))
encrypter.CryptBlocks(ciphertext, plaintext)
expectedCiphertext := fromHex(test.ciphertext)
if !bytes.Equal(ciphertext, expectedCiphertext) {
t.Errorf("#%d: encrypted failed, got: %x, want: %x", i, ciphertext, expectedCiphertext)
continue
}
decrypted := make([]byte, len(ciphertext))
decrypter.CryptBlocks(decrypted, ciphertext)
c.Decrypt(decrypted, ciphertext, test.sector)
if !bytes.Equal(decrypted, plaintext) {
t.Errorf("#%d: decryption failed, got: %x, want: %x", i, decrypted, plaintext)
}

View File

@ -66,54 +66,41 @@ var xtsAesTestVectors = []struct {
func TestXTSWithAES(t *testing.T) {
for i, test := range xtsAesTestVectors {
key := fromHex(test.key)
encrypter, err := cipher.NewXTSEncrypterWithSector(aes.NewCipher, key[:len(key)/2], key[len(key)/2:], test.sector)
c, err := cipher.NewXTS(aes.NewCipher, fromHex(test.key))
if err != nil {
t.Errorf("#%d: failed to create encrypter: %s", i, err)
continue
}
decrypter, err := cipher.NewXTSDecrypterWithSector(aes.NewCipher, key[:len(key)/2], key[len(key)/2:], test.sector)
if err != nil {
t.Errorf("#%d: failed to create decrypter: %s", i, err)
t.Errorf("#%d: failed to create cipher: %s", i, err)
continue
}
plaintext := fromHex(test.plaintext)
ciphertext := make([]byte, len(plaintext))
copy(ciphertext, plaintext)
encrypter.CryptBlocks(ciphertext, ciphertext)
c.Encrypt(ciphertext, plaintext, test.sector)
expectedCiphertext := fromHex(test.ciphertext)
if !bytes.Equal(ciphertext, expectedCiphertext) {
t.Errorf("#%d: encrypted failed, got: %x, want: %x", i, ciphertext, expectedCiphertext)
continue
}
decrypter.CryptBlocks(ciphertext, ciphertext)
if !bytes.Equal(ciphertext, plaintext) {
t.Errorf("#%d: decryption failed, got: %x, want: %x", i, ciphertext, plaintext)
decrypted := make([]byte, len(ciphertext))
c.Decrypt(decrypted, ciphertext, test.sector)
if !bytes.Equal(decrypted, plaintext) {
t.Errorf("#%d: decryption failed, got: %x, want: %x", i, decrypted, plaintext)
}
}
}
func TestShorterCiphertext(t *testing.T) {
encrypter, err := cipher.NewXTSEncrypterWithSector(aes.NewCipher, make([]byte, 16), make([]byte, 16), 0)
c, err := cipher.NewXTS(aes.NewCipher, make([]byte, 32))
if err != nil {
t.Fatalf("NewXTSEncrypterWithSector failed: %s", err)
}
decrypter, err := cipher.NewXTSDecrypterWithSector(aes.NewCipher, make([]byte, 16), make([]byte, 16), 0)
if err != nil {
t.Fatalf("NewXTSDecrypterWithSector failed: %s", err)
t.Fatalf("NewCipher failed: %s", err)
}
plaintext := make([]byte, 32)
encrypted := make([]byte, 48)
decrypted := make([]byte, 48)
encrypter.CryptBlocks(encrypted, plaintext)
decrypter.CryptBlocks(decrypted, encrypted[:len(plaintext)])
c.Encrypt(encrypted, plaintext, 0)
c.Decrypt(decrypted, encrypted[:len(plaintext)], 0)
if !bytes.Equal(plaintext, decrypted[:len(plaintext)]) {
t.Errorf("En/Decryption is not inverse")

View File

@ -1,112 +0,0 @@
# CFCA互操作性指南
这是一份**不完整**的CFCA互操作性指南。
## 什么是CFCA
CFCA是中国金融认证中心的英文缩写。
## 什么是SADK
SADKSecurity Application Development Kit是CFCA推出的一套支持全平台、全浏览器、多语言的安全应用开发套件。实现了证书下载、证书应用过程中的全业务功能覆盖可与客户业务系统、CFCA RA、CA、统一下载平台无缝对接为用户提供证书下载、证书更新、签名验签、加密解密等全方位的数字证书安全服务。具体说明请参考[数字证书安全应用开发套件SADK](https://www.cfca.com.cn/20150807/101228565.html)。
其JAVA版本(其它语言版本未知)基本上是一个基于[Bouncy Castle](https://www.bouncycastle.org/)的实现当然它看起来也支持JNI接入OpenSSL、U盾
## 为什么会有互操作性问题
* CFCA有一些实现没有相关标准。
* SADK存在较早可能有些实现早于标准发布。
* SADK版本较多不同版本也会有互操作性兼容问题。
* 其它未知原因。
因为我也找不到SADK的版本历史或者Change Log这里只是根据有限资料的一些判断。
## 容易出现互操作性问题的功能
相对而言互操作性问题主要出现在SM2椭圆曲线公钥密码算法应用上特别是加解密、数字信封加解密上。
### SM2加解密
加解密算法实现部分没有什么互操作性问题,主要是密文格式问题。
#### SADK 3.2之前版本
由于没有版本历史,所以这里只是大致推测(如果有不准确之处,敬请指出)。
SADK 3.2之前的版本只支持C1C2C3密文格式而且C1只支持非压缩点格式且输出忽略0x04这个点非压缩标识。
| 随机点 | 密文 | SM3哈希值 |
| :--- | :--- | :--- |
| C1 (64 bytes) | C2 | C3 (32 bytes) |
所以如果和SADK 3.2之前的应用交互加密输出格式只能选C1C2C3且密文通过切片操作忽略首字节0x04这个点非压缩标识反之如果是解密SADK 3.2之前的应用提供的密文则要指定C1C2C3格式同时自己在密文前添加0x04这个点非压缩标识。**互操作的重要前提是知道对方的密文格式**。
#### SADK 3.2+版本
SADK 3.2之后的版本支持下列SM2密文格式(encryptedType)
| encryptedType | 输出格式 | 用本软件库如何解密 |
| :--- | :--- | :--- |
| 0 | ASN.1编码格式 ```EncryptUtil.encrypt``` 方法默认 | 正常解密 |
| 2 | C1C3C2 格式带0x04这个点非压缩标识 | 正常解密 |
| 4 | C1C3C2 格式不带0x04这个点非压缩标识 ```EncryptUtil.encryptMessageBySM2 / EncryptUtil.encryptFileBySM2``` 方法默认) | 添加0x04前缀后解密 |
| 8 | C1C2C3 格式带0x04这个点非压缩标识 | 指定解密Opts后解密 |
| 16 | C1C2C3 格式不带0x04这个点非压缩标识 | 添加0x04前缀同时指定解密Opts后解密 |
**SADK 3.2之后的版本,解密过程**
1. 先尝试是否ASN.1格式,如果是,就解密;否则,
2. 当**C1C3C2不带0x04这个点非压缩标识**的格式处理,如果解密成功,则结束;否则,
3. 当**C1C2C3不带0x04这个点非压缩标识**的格式处理。
从这个解密流程来看SADK 3.2+可以解密 SADK 3.2之前的SM2密文反之不行。
所以如果和SADK 3.2之后的应用交互加密输出格式可以是ASN.1编码格式或者是不带0x04这个点非压缩标识的C1C3C2/C1C2C3格式反之如果是解密使用SADK 3.2+的应用提供的密文则要先区分是否是ASN.1格式是的话就比较简单不是的话则要指定C1C3C2格式同时自己在密文前添加0x04这个点非压缩标识。**互操作的重要前提是知道对方的密文格式**。
### SM2数字信封加解密
互操作性问题主要出在:
1. 数据对称加密所用密钥的SM2密文格式。
2. 对称加密算法的OID。```public static final ASN1ObjectIdentifier id_sm4_CBC = new ASN1ObjectIdentifier("1.2.156.10197.1.104");```。
3. 如果需要用本软件库去解密CFCA生成的SM2数字信封目前会有问题从**v0.29.3**开始可以解密。CFCA实现不符合《GB/T 35275-2017信息安全技术 SM2密码算法加密签名消息语法规范》它的**RecipientInfo**默认使用SubjectKeyIdentifier而不是IssuerAndSerialNumber。在SADK 3.7.1.0中需要指定recipientPolicyType=20从证书扩展中获取SubjectKeyID找不到抛异常1根据公钥数据直接计算SubjectKeyID2使用证书的IssuerAndSerialNumber才会使用IssuerAndSerialNumber。正常情况下只有CA证书才一定会在证书扩展中有SubjectKeyID信息。如果要产生和CFCA一样的加密信封请使用```pkcs7.EnvelopeMessageCFCA```方法。
**v0.29.6**之后,请直接使用
* `cfca.EnvelopeMessage`
* `cfca.OpenEnvelopedMessage`
* `cfca.EnvelopeMessageLegacy`
* `cfca.OpenEnvelopedMessageLegacy`
#### SADK 3.2之前版本
1. 数据对称加密密钥的密文格式为**C1C2C3 格式不带0x04这个点非压缩标识**。这个不符合《GM/T 0010-2012 SM2密码算法加密签名消息语法规范》以及《GB/T 35275-2017 信息安全技术 SM2密码算法加密签名消息语法规范》。
2. SM4-CBC的OID使用了["SM4" block cipher](https://oid-rep.orange-labs.fr/get/1.2.156.10197.1.104),而不是["SMS4-CBC"](https://oid-rep.orange-labs.fr/get/1.2.156.10197.1.104.2)。
本软件库的```pkcs7.EncryptCFCA```方法```DecryptCFCA```方法提供了SADK 3.2之前版本的信封加解密兼容性记得cipher参数选择```pkcs.SM4```。但是```pkcs7.EncryptCFCA```方法产生的加密信封依然使用IssuerAndSerialNumber作为RecipientInfo。
#### SADK 3.2+版本
1. 数据对称加密密钥的密文格式为**ASN.1编码格式**这个符合《GB/T 35275-2017 信息安全技术 SM2密码算法加密签名消息语法规范》。
2. SM4-CBC的OID使用了["SM4" block cipher](https://oid-rep.orange-labs.fr/get/1.2.156.10197.1.104),而不是["SMS4-CBC"](https://oid-rep.orange-labs.fr/get/1.2.156.10197.1.104.2)。
本软件库的```pkcs7.EncryptSM```方法```Decrypt```方法提供了SADK 3.2+版本的信封加解密兼容性。使用时,请确保`cipher`参数选择```pkcs.SM4```。```pkcs7.EncryptSM```方法符合《GB/T 35275-2017 信息安全技术 SM2密码算法加密签名消息语法规范》CFCA的SADK可实现相应数据的解密。
本软件库的```pkcs7.EnvelopeMessageCFCA```方法提供了CFCA SADK更兼容的实现也就是recipientPolicyType=0。
从SADK 的向下兼容性来看SADK 3.2+能够解密SADK 3.2之前版本的数字信封加密数据,反之不行。
### SM2 PKCS7签名数据
```cfca.sadk.util.p7SignMessageAttach / cfca.sadk.util.p7SignMessageDetach```,对应```pkcs7.SignWithoutAttr```如果要Detach签名调用```Finish```之前调用```Detach```就行。
```cfca.sadk.util.p7SignFileAttach / cfca.sadk.util.p7SignFileDetach```类似,只是本软件库不提供对应方法,您可以通过```pkcs7.SignWithoutAttr```自己实现。
参考[cfca sadk 3.0.2.0](https://github.com/emmansun/gmsm/issues/260)
**v0.29.6**之后,请直接使用
* `cfca.SignMessageAttach`
* `cfca.VerifyMessageAttach`
* `cfca.SignMessageDetach`
* `cfca.VerifyMessageDetach`
* `cfca.SignDigestDetach`
* `cfca.VerifyDigestDetach`
### 解密时自动检测?
要穷举、尝试所有可能的密文格式不是不可以但这会或多或少地影响解密的性能。你要和对方集成还是知己知彼比较好对于加解密来说对用户透明不代表是好事。本软件库的SM2解密也实现了一定的自动检测通过首字节判断基于首字节只有固定那几个的假设
* 0x30 - ASN.1格式。
* 0x04 - C1为非压缩点格式具体是C1C3C2还是C1C2C3取决于解密时的选项参数默认为C1C3C2。
* 0x02/0x03 - C1为压缩点格式具体是C1C3C2还是C1C2C3取决于解密时的选项参数默认为C1C3C2。
### 生成双密钥CSR v0.29.6+
`cfca.CreateCertificateRequest`和CFCA SADK不同调用者需要自行先生成两对密钥对一对用于签名证书一对用于加解密CFCA生成的加密用私钥文件CFCA加密申请者解密。这个方法对应CFCA的`cfca.sadk.util.P10Request.generateDoublePKCS10Request`方法。按我的理解非国密RSA应该不需要支持这种双密钥对机制不过既然**CFCA SADK**支持,本软件库从**v0.30.0**开始也支持。
使用`cfca.ParseEscrowPrivateKey`解析CFCA返回的加密用私钥。
### SM2私钥、证书的解析
这个是CFCA自定义的未见相关标准可以通过```cfca.ParseSM2```来解析。```cfca.ParseSM2```函数只接受**DER**编码的二进制数据,如果你的数据是**base64**编码的,请先自行解码。

View File

@ -1,186 +0,0 @@
# [go-pkcs12](https://github.com/emmansun/go-pkcs12)应用指南
[PKCS #12: Personal Information Exchange Syntax v1.1](https://datatracker.ietf.org/doc/html/rfc7292)PKCS12目前似乎没有相应的国密标准。
定制PKCS12的目的是
1. 可以处理**SM2**私钥和证书。
2. 可以替代、使用一些商密算法,主要是**SM3**和**SM4**。
## PKCS#12的解析
[go-pkcs12](https://github.com/emmansun/go-pkcs12)提供三个方法:
| 方法 | 适用 | 具体说明 |
| :--- | :--- | :--- |
| ```DecodeChain``` | 抽取出一个私钥、一个相应证书以及证书链 | 私钥和相应证书必须存在,否则报错 |
| ```Decode``` | 抽取出一个私钥、一个相应证书 | 私钥和相应证书必须存在,否则报错;并且**不能有证书链存在**。 |
| ```DecodeTrustStore``` | 抽取出证书链 | 只支持java的TrustStore, [Difference Between a Java Keystore and a Truststore](https://www.baeldung.com/java-keystore-truststore-difference) |
### 解码能处理的算法
#### 证书及私钥加密算法
这里主要是**PBES(Password-Based Encryption Scheme)**, 它主要涉及几方面:
1. 密码处理
2. 从密码派生出加密密钥
3. 具体对称加密算法
**PBES-PKCS12**
* pbeWithSHAAnd3-KeyTripleDES-CBC OBJECT IDENTIFIER ::= {pkcs-12PbeIds 3}
* pbeWithSHAAnd128BitRC2-CBC OBJECT IDENTIFIER ::= {pkcs-12PbeIds 5}
* pbewithSHAAnd40BitRC2-CBC OBJECT IDENTIFIER ::= {pkcs-12PbeIds 6}
不同于**PKCS#5 v1.5**中的**PBES1**,上述这些是**PKCS#12**的独有算法,特别是它的**KDF**和**密码处理**。
**PBES1**
PBES1属于老旧遗留算法目前版本未实现。
**PBES2**
由两部分组成,分别为**KDF**和加密算法。目前KDF只支持**KDF2**, KDF2中支持的**PRF**方法有:
* id-hmacWithSHA1
* id-hmacWithSHA256
* id-hmacWithSM3
具体可参考[PKCS #5: Password-Based Cryptography Specification Version 2.1](https://datatracker.ietf.org/doc/html/rfc8018)
加密算法有:
* AES-CBC-Pad密钥长度支持16/24/32字节
* SM4-CBC-Pad密钥长度支持16字节
#### 数据完整性保护(PBMAC)
这里只支持基于密码的完整性保护:**PKCS12-KDF + HMAC**。支持的HASH算法有
* SHA1
* SHA256
* SM3
**PBMAC**目前的实现还是基于**PKCS12-KDF**,将来看情况是否要实现**PBMAC1**,主要看**OpenSSL**的支持进度:
* [Support FIPS-compliant PKCS#12 files and create them by default in FIPS mode](https://github.com/openssl/openssl/issues/24546)
* [RFC 9579 implementation: add PBMAC1 with PBKDF2 to PKCS#12](https://github.com/openssl/openssl/pull/24577)
从**v0.4.1**开始支持**PBMAC1**。
## PKCS#12的生成
目前只支持下列几种,不支持自由定义:
* ```LegacyRC2```加密使用PKCS12特有算法对证书使用RC2加密对私钥使用3DES加密一致性保证使用HMAC-SHA1。
* ```LegacyDES```加密使用PKCS12特有算法对证书和私钥都是用3DES加密一致性保证使用HMAC-SHA1。
* ```Passwordless```,无加密、一致性保证模式。
* ```Modern2023```对应OpenSSL 3+ 默认加密使用AES-256-CBC with PBKDF2一致性保证使用HMAC-SHA256。
* ```ShangMi2024```,这个估计目前没什么互操作性。
目前的全局函数```Encode``` / ```EncodeTrustStore```使用**LegacyRC2**编码器。
```go
// LegacyRC2 encodes PKCS#12 files using weak algorithms that were
// traditionally used in PKCS#12 files, including those produced
// by OpenSSL before 3.0.0, go-pkcs12 before 0.3.0, and Java when
// keystore.pkcs12.legacy is defined. Specifically, certificates
// are encrypted using PBE with RC2, and keys are encrypted using PBE
// with 3DES, using keys derived with 2048 iterations of HMAC-SHA-1.
// MACs use HMAC-SHA-1 with keys derived with 1 iteration of HMAC-SHA-1.
//
// Due to the weak encryption, it is STRONGLY RECOMMENDED that you use [DefaultPassword]
// when encoding PKCS#12 files using this encoder, and protect the PKCS#12 files
// using other means.
//
// By default, OpenSSL 3 can't decode PKCS#12 files created using this encoder.
// For better compatibility, use [LegacyDES]. For better security, use
// [Modern2023].
var LegacyRC2 = &Encoder{
macAlgorithm: oidSHA1,
certAlgorithm: oidPBEWithSHAAnd40BitRC2CBC,
keyAlgorithm: oidPBEWithSHAAnd3KeyTripleDESCBC,
kdfPrf: nil,
encryptionScheme: nil,
macIterations: 1,
encryptionIterations: 2048,
saltLen: 8,
rand: rand.Reader,
}
// LegacyDES encodes PKCS#12 files using weak algorithms that are
// supported by a wide variety of software. Certificates and keys
// are encrypted using PBE with 3DES using keys derived with 2048
// iterations of HMAC-SHA-1. MACs use HMAC-SHA-1 with keys derived
// with 1 iteration of HMAC-SHA-1. These are the same parameters
// used by OpenSSL's -descert option. As of 2023, this encoder is
// likely to produce files that can be read by the most software.
//
// Due to the weak encryption, it is STRONGLY RECOMMENDED that you use [DefaultPassword]
// when encoding PKCS#12 files using this encoder, and protect the PKCS#12 files
// using other means. To create more secure PKCS#12 files, use [Modern2023].
var LegacyDES = &Encoder{
macAlgorithm: oidSHA1,
certAlgorithm: oidPBEWithSHAAnd3KeyTripleDESCBC,
keyAlgorithm: oidPBEWithSHAAnd3KeyTripleDESCBC,
kdfPrf: nil,
encryptionScheme: nil,
macIterations: 1,
encryptionIterations: 2048,
saltLen: 8,
rand: rand.Reader,
}
// Passwordless encodes PKCS#12 files without any encryption or MACs.
// A lot of software has trouble reading such files, so it's probably only
// useful for creating Java trust stores using [Encoder.EncodeTrustStore]
// or [Encoder.EncodeTrustStoreEntries].
//
// When using this encoder, you MUST specify an empty password.
var Passwordless = &Encoder{
macAlgorithm: nil,
certAlgorithm: nil,
keyAlgorithm: nil,
kdfPrf: nil,
encryptionScheme: nil,
rand: rand.Reader,
}
// Modern2023 encodes PKCS#12 files using algorithms that are considered modern
// as of 2023. Private keys and certificates are encrypted using PBES2 with
// PBKDF2-HMAC-SHA-256 and AES-256-CBC. The MAC algorithm is HMAC-SHA-2. These
// are the same algorithms used by OpenSSL 3 (by default), Java 20 (by default),
// and Windows Server 2019 (when "stronger" is used).
//
// Files produced with this encoder can be read by OpenSSL 1.1.1 and higher,
// Java 12 and higher, and Windows Server 2019 and higher.
//
// For passwords, it is RECOMMENDED that you do one of the following:
// 1) Use [DefaultPassword] and protect the file using other means, or
// 2) Use a high-entropy password, such as one generated with `openssl rand -hex 16`.
//
// You SHOULD NOT use a lower-entropy password with this encoder because the number of KDF
// iterations is only 2048 and doesn't provide meaningful protection against
// brute-forcing. You can increase the number of iterations using [Encoder.WithIterations],
// but as https://neilmadden.blog/2023/01/09/on-pbkdf2-iterations/ explains, this doesn't
// help as much as you think.
var Modern2023 = &Encoder{
macAlgorithm: oidSHA256,
certAlgorithm: oidPBES2,
keyAlgorithm: oidPBES2,
kdfPrf: oidHmacWithSHA256,
encryptionScheme: oidAES256CBC,
macIterations: 2048,
encryptionIterations: 2048,
saltLen: 16,
rand: rand.Reader,
}
// ShangMi2024 encodes PKCS#12 files using algorithms that are all ShangMi.
// Private keys and certificates are encrypted using PBES2 with PBKDF2-HMAC-SM3 and SM4-CBC.
// The MAC algorithm is PBMAC1-HMAC-SM3.
var ShangMi2024 = &Encoder{
macAlgorithm: oidPBMAC1,
certAlgorithm: oidPBES2,
keyAlgorithm: oidPBES2,
kdfPrf: oidHmacWithSM3,
encryptionScheme: oidSM4CBC,
messageAuthScheme: oidHmacWithSM3,
macIterations: 2048,
encryptionIterations: 2048,
saltLen: 16,
rand: rand.Reader,
}
```
## 解析加密的PKCS#8私钥
[go-pkcs12](https://github.com/emmansun/go-pkcs12) 也提供了```ParsePKCS8PrivateKey```方法,相比**pkcs8**的类似方法,这里特别支持**PBES-PKCS12**加密算法。
* PBE-SHA1-RC2-128
* PBE-SHA1-RC2-40
* PBE-SHA1-3DES

View File

@ -1,123 +0,0 @@
# PKCS7应用指南
本项目实现 PKCS#7/加密消息语法的子集[RFC2315](https://www.rfc-editor.org/rfc/rfc2315.html)、[RFC5652](https://www.rfc-editor.org/rfc/rfc5652.html)以及相应国密支持《GB/T 35275-2017 信息安全技术 SM2密码算法加密签名消息语法规范》。这是 [mozilla-services/pkcs7](https://github.com/mozilla-services/pkcs7) 的一个分支,目前[mozilla-services/pkcs7](https://github.com/mozilla-services/pkcs7)已经是弃用状态,代码仓库也已经进入存档、只读状态。
## 支持的功能
### 数字信封数据Enveloped Data
数字信封数据,使用对称加密算法加密数据,使用非对称加密加密数据密钥。支持的对称加密算法(以及模式)有
* AES128-CBC
* AES192-CBC
* AES256-CBC
* AES128-GCM
* AES192-GCM
* AES256-GCM
* DES-CBC
* 3DES-CBC
* SM4-CBC
* SM4-GCM
支持的非对称加密算法为:
* RSAPKCS1v15目前尚不支持RSAOAEP
* SM2
#### 主要方法
是否国密是指OID也使用国密体系
| 是否国密 | 加密 | 解密(先调用```Parse``` |
| :--- | :--- | :--- |
| 否 | Encrypt | Decrypt |
| 否 | EncryptUsingPSK | DecryptUsingPSK |
| 是 | EncryptSM | Decrypt |
| 是 | EncryptCFCA | DecryptCFCA |
| 是 | EncryptSMUsingPSK | DecryptUsingPSK |
关于```EncryptSM / EncryptCFCA```的区别,请参考**CFCA互操作性指南**。
带PSKPre-shared key后缀的方法其对称加密密钥由调用者提供而非随机生成。
### 加密数据Encrypted Data
加密:对应本项目的```pkcs7.EncryptUsingPSK```和```pkcs7.EncryptSMUsingPSK```方法。
解密:对应本项目的```pkcs7.DecryptUsingPSK```方法(当然要先调用```pkcs7.Parse```)。
### 签名数据Signed Data
签名数据,使用证书对应的私钥进行签名,理论上支持多个签名者,但通常使用场景都是单签。和数字信封数据类似,也分国密和非国密。
#### 签名流程
1. 创建SignedData
是否国密是指OID也使用国密体系
| 是否国密 | 数据是否是哈希值 | 方法 | 默认签名算法 |
| :--- | :--- | :--- | :--- |
| 否 | 否 | ```NewSignedData``` | SHA1 |
| 否 | 是 | ```NewSignedDataWithDigest``` | SHA1 |
| 是 | 否 | ```NewSMSignedData``` | SM3 |
| 是 | 是 | ```NewSMSignedDataWithDigest``` | SM3 |
2. 可选步骤:调用```SetDigestAlgorithm```设置想要的签名算法,通常国密**不需要**修改。
3. 接着调用```AddSigner```或```AddSignerChain```方法,进行签名;可以通过```SignerInfoConfig.SkipCertificates```指定忽略证书项(最终签名数据中不包含证书项);
4. 如果进行Detach签名则调用```Detach```方法;
5. 最后调用```Finish```方法,序列化输出结果。
**注意**
1. 如果是直接对哈希值签名一定是Detach签名。
2. 国密签名如果要传入哈希值在有Attribute的情况下则哈希值只是标准的SM3哈希值否则必须是符合SM2签名标准的哈希值含SM2公钥信息
#### Detach签名
就是外部签名,**被签名数据**不包含在SignedData中也就是其ContentInfo.Content为空
In PKCS#7 SignedData, attached and detached formats are supported… In detached format, data that is signed is not embedded inside the SignedData package instead it is placed at some external location…
可以参考[RFC2315](https://www.rfc-editor.org/rfc/rfc2315.html)的第7章 注3
The optional omission of the content field makes it possible to construct "external signatures," for example, without modification to or replication of the content to which the signatures apply. In the case of external signatures, the content being signed would be omitted from the "inner" encapsulated ContentInfo value included in the signed-data content type.
这种外部签名要验签的话,需要先提供**被签名数据**。以下代码片段来自**sign_test.go**中的**testSign**方法:
```golang
p7, err := Parse(signed)
if err != nil {
t.Fatalf("test %s/%s/%s: cannot parse signed data: %s", sigalgroot, sigalginter, sigalgsigner, err)
}
if testDetach {
// Detached signature should not contain the content
// So we should not be able to find the content in the parsed data
// We should suppliment the content to the parsed data before verifying
p7.Content = content
}
if !bytes.Equal(content, p7.Content) {
t.Errorf("test %s/%s/%s: content was not found in the parsed data:\n\tExpected: %s\n\tActual: %s", sigalgroot, sigalginter, sigalgsigner, content, p7.Content)
}
if err := p7.VerifyWithChain(truststore); err != nil {
t.Errorf("test %s/%s/%s: cannot verify signed data: %s", sigalgroot, sigalginter, sigalgsigner, err)
}
```
#### 验证签名
而验证的话,流程如下:
1. 调用```Parse```方法;
2. 如果是Detach签名数据则手动设置原始数据参考```testSign```方法);
3. 如果签名数据中不包含证书项,则手动设置验签证书(参考```TestSkipCertificates```
4. 如果Content是原始数据调用```Verify```或```VerifyWithChain```方法如果Content是哈希值调用```VerifyAsDigest```或```VerifyAsDigestWithChain```方法。
#### 特殊方法
```DegenerateCertificate```退化成签名数据中只包含证书目前没有使用SM2 OID的方法如果需要可以请求添加。可以参考```TestDegenerateCertificate```和```TestParseSM2CertificateChain```。
### 签名及数字信封数据Signed and Enveloped Data
签名和数字信封数据使用场景较少有些实现用它来传输私钥譬如www.gmcert.org。具体请参考```sign_enveloped_test.go```。
The "signed and enveloped data" content type is a part of the Cryptographic Message Syntax (CMS), which is used in various Internet Standards. However, it's not recommended for use due to several reasons:
1. **Complexity**: The "signed and enveloped data" content type combines two operations - signing and enveloping (encryption). This increases the complexity of the implementation and can lead to potential security vulnerabilities if not handled correctly.
2. **Order of Operations**: The "signed and enveloped data" content type first signs the data and then encrypts it. This means that to verify the signature, the data must first be decrypted. This could potentially expose sensitive data to unauthorized parties before the signature is verified.
3. **Lack of Flexibility**: Combining signing and enveloping into a single operation reduces flexibility. It's often more useful to be able to perform these operations separately, as it allows for more varied use cases.
Instead of using the "signed and enveloped data" content type, it's generally recommended to use separate "signed data" and "enveloped data" content types. This allows the operations to be performed in the order that best suits the application's needs, and also simplifies the implementation.
#### 加密签名流程
1. 调用```NewSignedAndEnvelopedData```或者```NewSMSignedAndEnvelopedData```创建```SignedAndEnvelopedData```数据结构,此过程包含了数据加密过程;
2. 调用```AddSigner```或```AddSignerChain```方法,进行签名;
3. 调用```AddRecipient```方法用Recipient的公钥加密数据密钥
4. 最后调用```Finish```方法,序列化输出结果。
#### 解密验签流程
1. 调用```Parse```方法;
2. 调用```DecryptAndVerify```或者```DecryptAndVerifyOnlyOne```进行解密和验签。

View File

@ -1,368 +0,0 @@
# SM2椭圆曲线公钥密码算法应用指南
## 参考标准
* 《GB/T 32918.1-2016 信息安全技术 SM2椭圆曲线公钥密码算法 第1部分总则》
* 《GB/T 32918.2-2016 信息安全技术 SM2椭圆曲线公钥密码算法 第2部分数字签名算法》
* 《GB/T 32918.3-2016 信息安全技术 SM2椭圆曲线公钥密码算法 第3部分密钥交换协议》
* 《GB/T 32918.4-2016 信息安全技术 SM2椭圆曲线公钥密码算法 第4部分公钥加密算法》
* 《GB/T 32918.5-2017 信息安全技术 SM2椭圆曲线公钥密码算法 第5部分参数定义》
* 《GB/T 35276-2017 信息安全技术 SM2密码算法使用规范》
* 《GB/T 33560-2017 信息安全技术 密码应用标识规范》
* 《GB/T 35275-2017 信息安全技术 SM2密码算法加密签名消息语法规范》(对应PKCS#7)
您可以从[国家标准全文公开系统](https://openstd.samr.gov.cn/)在线阅读这些标准。
## 概述
SM2既然是椭圆曲线公钥密码算法它就和NIST P系列椭圆曲线公钥密码算法类似特别是P-256。NIST P 系列椭圆曲线公钥密码算法主要用于数字签名和密钥交换NIST没有定义基于椭圆曲线的公钥加密算法标准[SEC 1: Elliptic Curve Cryptography](https://www.secg.org/sec1-v2.pdf)第五章定义了“Elliptic Curve Integrated Encryption Scheme (ECIES)”不过应用不广。SM2公钥加密算法与其相似只是MAC不同。感兴趣的同学可以进一步对比一下
| SM2 | SEC 1 |
| :--- | :--- |
| 数字签名算法 | ECDSA |
| 密钥交换协议 | ECMQV |
| 公钥加密算法 | ECIES |
**注**最新的阿里KMS支持ECIES难道客户有这个需求
ECIES_DH_SHA_1_XOR_HMAC遵循[SEC 1: Elliptic Curve Cryptography, Version 2.0](https://www.secg.org/sec1-v2.pdf)标准密钥协商算法采用ECDH密钥派生算法采用 KDF2 with SHA-1MAC算法采用HMAC-SHA-1对称加密算法采用XOR。
**业界对RSA非对称加密的安全性担忧与日俱增**
* [The Marvin Attack](https://people.redhat.com/~hkario/marvin/)
* [CVE-2023-45287 Detail](https://nvd.nist.gov/vuln/detail/CVE-2023-45287)
* [Vulnerability Report: GO-2023-2375](https://pkg.go.dev/vuln/GO-2023-2375)
* [Seriously, stop using RSA](https://blog.trailofbits.com/2019/07/08/fuck-rsa/)
## SM2公私钥对
SM2公私钥对的话要么是自己产生要么是别的系统产生后通过某种方式传输给您的。
### SM2公私钥对的生成
您可以通过调用```sm2.GenerateKey```方法产生SM2公私钥对SM2的私钥通过组合方式扩展了```ecdsa.PrivateKey```用于定义一些SM2特定的方法
```go
// PrivateKey represents an ECDSA SM2 private key.
// It implemented both crypto.Decrypter and crypto.Signer interfaces.
type PrivateKey struct {
ecdsa.PrivateKey
...
}
```
SM2的公钥类型沿用了```ecdsa.PublicKey```结构。注意Go从v1.20开始,```ecdsa.PublicKey```增加了```func (k *PublicKey) ECDH() (*ecdh.PublicKey, error)```方法这个方法对SM2的公钥不适用SM2公钥请使用```func PublicKeyToECDH(k *ecdsa.PublicKey) (*ecdh.PublicKey, error)```。
### SM2公钥的解析、构造
通常情况下公钥是通过PEM编码的文本传输的您可以通过两步获得公钥
* 获得PEM中的block
* 解析block中的公钥
```go
func getPublicKey(pemContent []byte) (any, error) {
block, _ := pem.Decode(pemContent)
if block == nil {
return nil, errors.New("Failed to parse PEM block")
}
return smx509.ParsePKIXPublicKey(block.Bytes)
}
```
由于```smx509.ParsePKIXPublicKey```返回any类型您需要通过```pub, ok := publicKey.(*ecdsa.PublicKey)```转型。
有些应用可能会直接存储公钥的曲线点X, Y 坐标值,这时候,您可以通过以下类似方法构造公钥(假设输入的是点的非压缩序列化字节数组):
```go
func ExampleNewPublicKey() {
keypoints, _ := hex.DecodeString("048356e642a40ebd18d29ba3532fbd9f3bbee8f027c3f6f39a5ba2f870369f9988981f5efe55d1c5cdf6c0ef2b070847a14f7fdf4272a8df09c442f3058af94ba1")
pub, err := sm2.NewPublicKey(keypoints)
if err != nil {
log.Fatalf("fail to new public key %v", err)
}
fmt.Printf("%x\n", elliptic.Marshal(sm2.P256(), pub.X, pub.Y))
// Output: 048356e642a40ebd18d29ba3532fbd9f3bbee8f027c3f6f39a5ba2f870369f9988981f5efe55d1c5cdf6c0ef2b070847a14f7fdf4272a8df09c442f3058af94ba1
}
```
当然您也可以使用ecdh包下的方法```ecdh.P256().NewPublicKey```来构造,目前只支持非压缩方式。
### SM2私钥的解析、构造
私钥的封装格式主要有以下几种,[相关讨论](https://github.com/emmansun/gmsm/issues/104)
* RFC 5915 / SEC1 - http://www.secg.org/sec1-v2.pdf
* PKCS#12
* PKCS#8
* PKCS#7《GB/T 35275-2017 信息安全技术 SM2密码算法加密签名消息语法规范》
* CFCA自定义封装
* 《GB/T 35276-2017 信息安全技术 SM2密码算法使用规范》
存在于智能密码钥匙中符合《GB/T 35291-2017 信息安全技术 智能密码钥匙应用接口规范》的,不在这里说明。)
所以当您拿到一个密钥文件您需要知道它的封装格式然后选用合适的方法。PEM编码的密钥文本通常第一行会有相关信息。如果您得到的是一个ASN.1编码那可能需要通过ASN.1结构和一些其中的OID来判断了。私钥信息是非常关键的信息通常密钥文件被加密保护。可能是标准落后于应用的原因目前这一块的互操作性可能差一点。
| 封装格式 | 解析方法 |
| :--- | :--- |
| RFC 5915 / SEC1 | ```smx509.ParseSM2PrivateKey``` |
| PKCS#12 | 使用 github.com/emmansun/go-pkcs12 解析 |
| PKCS#8 | ```smx509.ParsePKCS8PrivateKey```可以处理未加密的;```pkcs8.ParsePKCS8PrivateKeySM2```可以处理未加密的,也可以处理加密的 |
| PKCS#7 | Cryptographic Message Syntax, 可以参考github.com/emmansun/pkcs7/sign_enveloped_test.go中的```TestParseSignedEvnvelopedData```,测试数据来自 https://www.gmcert.org/ |
| CFCA自定义封装 | 顾名思义这个封装是CFCA特定的修改自PKCS#12,使用```cfca.ParseSM2```方法来解析 |
|《GB/T 35276-2017 信息安全技术 SM2密码算法使用规范》| 这个规范还比较新,使用```sm2.ParseEnvelopedPrivateKey```解析。典型的应用场景是CA机构返回CSRResponse, 里面包含签名证书、CA生成的SM2加密私钥以及相应的SM2加密证书其中SM2加密私钥就用该规范定义的方式加密封装。请参考《GM/T 0092-2020 基于SM2算法的证书申请语法规范》 |
有些系统可能会直接存储、得到私钥的字节数组,那么您可以使用如下方法来构造私钥:
```go
func ExampleNewPrivateKey() {
keyBytes, _ := hex.DecodeString("6c5a0a0b2eed3cbec3e4f1252bfe0e28c504a1c6bf1999eebb0af9ef0f8e6c85")
priv, err := sm2.NewPrivateKey(keyBytes)
if err != nil {
log.Fatalf("fail to new private key %v", err)
}
fmt.Printf("%x\n", priv.D.Bytes())
// Output: 6c5a0a0b2eed3cbec3e4f1252bfe0e28c504a1c6bf1999eebb0af9ef0f8e6c85
}
func ExampleNewPrivateKeyFromInt() {
key := big.NewInt(0x123456)
priv, err := sm2.NewPrivateKeyFromInt(key)
if err != nil {
log.Fatalf("fail to new private key %v", err)
}
fmt.Printf("%x\n", priv.D.Bytes())
// Output: 123456
}
```
当然你也可以使用ecdh包的方法```ecdh.P256().NewPrivateKey```来构造私钥您要确保输入的字节数组是256位32字节如果不是请先自行处理。
### 关于《GM/T 0091-2020 基于口令的密钥派生规范》
这个规范就是[RFC8018 PKCS#5](https://datatracker.ietf.org/doc/html/rfc8018) 国密定制版其中PBES/PBKDF/PBMAC使用了不同的OID但是这些OID似乎没有注册过。而且表A.1 中**id-hmacWithSM3**的OID为没有注册过的**1.2.156.10197.1.401.3.1**,和我们常用的**1.2.156.10197.1.401.2**不一致也与该文档本身附录C不一致。不知道哪个产品遵从了这个行业规范。
| 对象标识符OID | 对象标识符定义 |
| :--- | :--- |
| 1.2.156.10197.6.1.4.1.5 | 基于口令的密钥派生规范 |
| 1.2.156.10197.6.1.4.1.5.1 | 基于口令的密钥派生函数 PBKDF (其实就是PBKDF2) |
| 1.2.156.10197.6.1.4.1.5.2 | 基于口令的加密方案PBES (其实就是PBES2) |
| 1.2.156.10197.6.1.4.1.5.3 | 基于口令的消息鉴别码PBMAC |
规范中让人困惑的地方:
1. 附录 **A.2 伪随机函数** 引入了这个新的**id-hmacWithSM3**的OID**1.2.156.10197.1.401.3.1**
2. 附录 **A.4 基础消息鉴别方案**给出的实例片段中的OID为**1.2.156.10197.1.401**,让人怀疑这个实例抄的是**PKCS12-MAC**,而不是**PBMAC1**。
3. 附录 **B.2 PBES结构****PBES-Encs**竟然给出了**pbeWithSM3AndSM4-CBC OJBECTIDENTIFIER ::= {1.2.156.10197.6.1.4.1.12.1.1}**,难不成又要抄**PBES1**?
4. 附录 **C. ASN.1 结构定义****id-hmacWithSM3**的OID又是**1.2.156.10197.1.401.2**。
## 数字签名算法
您可以直接使用sm2私钥的签名方法```Sign```
```go
// This is a reference method to force SM2 standard with SDK [crypto.Signer].
func ExamplePrivateKey_Sign_forceSM2() {
toSign := []byte("ShangMi SM2 Sign Standard")
// real private key should be from secret storage
privKey, _ := hex.DecodeString("6c5a0a0b2eed3cbec3e4f1252bfe0e28c504a1c6bf1999eebb0af9ef0f8e6c85")
testkey, err := sm2.NewPrivateKey(privKey)
if err != nil {
log.Fatalf("fail to new private key %v", err)
}
// force SM2 sign standard and use default UID
sig, err := testkey.Sign(rand.Reader, toSign, sm2.DefaultSM2SignerOpts)
if err != nil {
fmt.Fprintf(os.Stderr, "Error from sign: %s\n", err)
return
}
// Since sign is a randomized function, signature will be
// different each time.
fmt.Printf("%x\n", sig)
}
```
我们通过```SignerOpts```参数来指示```toSign```已经是hash值还是需要进行处理的原始信息。通常情况下```toSign```传入原始信息、```SignerOpts```传入```sm2.DefaultSM2SignerOpts```。如果将来标准支持自定义的uid那么您可以通过调用```sm2.NewSM2SignerOption```来构造一个自定义的```SignerOpts```。
当然您也可以通过调用SM2私钥的```SignWithSM2```方法,区别在于,```Sign```方法是```crypto.Singer```接口中定义的方法,而```SignWithSM2```方法是```sm2.Signer```接口中定义的方法。
您可以使用```sm2.VerifyASN1WithSM2```来校验SM2签名
```go
func ExampleVerifyASN1WithSM2() {
// real public key should be from cert or public key pem file
keypoints, _ := hex.DecodeString("048356e642a40ebd18d29ba3532fbd9f3bbee8f027c3f6f39a5ba2f870369f9988981f5efe55d1c5cdf6c0ef2b070847a14f7fdf4272a8df09c442f3058af94ba1")
testkey, err := sm2.NewPublicKey(keypoints)
if err != nil {
log.Fatalf("fail to new public key %v", err)
}
toSign := []byte("ShangMi SM2 Sign Standard")
signature, _ := hex.DecodeString("304402205b3a799bd94c9063120d7286769220af6b0fa127009af3e873c0e8742edc5f890220097968a4c8b040fd548d1456b33f470cabd8456bfea53e8a828f92f6d4bdcd77")
ok := sm2.VerifyASN1WithSM2(testkey, nil, toSign, signature)
fmt.Printf("%v\n", ok)
// Output: true
}
```
### 如何处理不用Z的签名、验签
所谓**Z**就是用户可识别标识符和用户公钥、SM2椭圆曲线参数的杂凑值。其它签名算法如ECDSA是没有这个**Z**的这也是SM2签名算法难以融入以ECDSA签名算法为主的体系的主因。
#### 签名
也是使用sm2私钥的`Sign`方法,只是```SignerOpts```传入`nil`或者其它非`SM2SignerOption`即可,那么,你自己负责预先计算杂凑值,当然如何计算杂凑值,由你自己说了算了。
#### 验签
调用`sm2.VerifyASN1`方法,同样,你自己负责预先计算杂凑值,确保杂凑算法和签名时使用的杂凑算法保持一致。
### 如何对大文件签名、验签?
解决方案就是对杂凑值进行签名、验签。`sm2.CalculateSM2Hash`并不适合对大文件进行杂凑计算,请使用专门的`hash.Hash`接口实现。
## 密钥交换协议
这里有两个实现一个是传统实现位于sm2包中另外一个参考最新go语言的实现在ecdh包中。在这里不详细介绍使用方法一般只有tls/tlcp才会用到普通应用通常不会涉及这一块感兴趣的话可以参考github.com/Trisia/gotlcp中的应用。
## 公钥加密算法
请牢记,非对称加密算法通常不用于加密大量数据,而是用来加密对称加密密钥,我们在**tlcp**以及**信封加密**机制中能找到这种用法。
SM2公钥加密算法支持的密文编码格式有两种
* 简单串接方式: C1C3C2曾经老的标准为 C1C2C3
* ASN.1格式
SM2公钥加密示例
```go
func ExampleEncryptASN1() {
// real public key should be from cert or public key pem file
keypoints, _ := hex.DecodeString("048356e642a40ebd18d29ba3532fbd9f3bbee8f027c3f6f39a5ba2f870369f9988981f5efe55d1c5cdf6c0ef2b070847a14f7fdf4272a8df09c442f3058af94ba1")
testkey, err := sm2.NewPublicKey(keypoints)
if err != nil {
log.Fatalf("fail to new public key %v", err)
}
secretMessage := []byte("send reinforcements, we're going to advance")
// crypto/rand.Reader is a good source of entropy for randomizing the
// encryption function.
rng := rand.Reader
ciphertext, err := sm2.EncryptASN1(rng, testkey, secretMessage)
if err != nil {
fmt.Fprintf(os.Stderr, "Error from encryption: %s\n", err)
return
}
// Since encryption is a randomized function, ciphertext will be
// different each time.
fmt.Printf("Ciphertext: %x\n", ciphertext)
}
```
如果您需要普通拼接编码输出,您可以调用```sm2.Encrypt```方法,其中```EncrypterOpts```类型参数可以传入nil表示默认C1C3C2。
sm2包也提供了辅助方法用于密文输出编码格式转换您可以通过```sm2.ASN1Ciphertext2Plain```方法把ASN.1密文转换为简单拼接输出;反过来,您也可以通过```sm2.PlainCiphertext2ASN1```将简单拼接密文输出转换为ASN.1密文。你还可以通过```sm2.AdjustCiphertextSplicingOrder```方法来改变串接顺序。
SM2公钥加密算法解密示例
```go
func ExamplePrivateKey_Decrypt() {
ciphertext, _ := hex.DecodeString("308194022100bd31001ce8d39a4a0119ff96d71334cd12d8b75bbc780f5bfc6e1efab535e85a02201839c075ff8bf761dcbe185c9750816410517001d6a130f6ab97fb23337cce150420ea82bd58d6a5394eb468a769ab48b6a26870ca075377eb06663780c920ea5ee0042be22abcf48e56ae9d29ac770d9de0d6b7094a874a2f8d26c26e0b1daaf4ff50a484b88163d04785b04585bb")
// real private key should be from secret storage
privKey, _ := hex.DecodeString("6c5a0a0b2eed3cbec3e4f1252bfe0e28c504a1c6bf1999eebb0af9ef0f8e6c85")
testkey, err := sm2.NewPrivateKey(privKey)
if err != nil {
log.Fatalf("fail to new private key %v", err)
}
plaintext, err := testkey.Decrypt(nil, ciphertext, nil)
if err != nil {
fmt.Fprintf(os.Stderr, "Error from decryption: %s\n", err)
return
}
fmt.Printf("Plaintext: %s\n", string(plaintext))
// Output: Plaintext: send reinforcements, we're going to advance
}
```
这个SM2私钥的解密方法```Decrypt```,通常情况下,对```crypto.DecrypterOpts```类型参数您只需传入nil系统会自己检测输入密文是ASN.1还是普通拼接但是如果密文是老旧的C1||C2||C3拼接请传入相应的```crypto.DecrypterOpts```类型参数,或者您可以先通过上面介绍的辅助函数转换一下。
具体API文档请参考[API Document](https://godoc.org/github.com/emmansun/gmsm)
### 关于C1C2C3 和 C1C3C2
目前有据可查的是国家密码管理局2010版SM2标准还是用C1C2C3格式到了2012年标准就改用了C1C3C2并延续至今。
其实C1C2C3是符合《SEC 1: Elliptic Curve Cryptography》May 21, 2009 Version 2.0Elliptic Curve Integrated Encryption Scheme 5.1.3中的密文输出描述9. Output C = ($\overline{\text{R}}$, EM, D). Optionally, the ciphertext maybe output as C = $\overline{\text{R}}$ || EM || D. 这里 $\overline{\text{R}}$ 相对于C1, EM相对于C2, D相对于C3。
### 关于点到字节串的转换
我没有找到到哪个文档写有固定64字节转换的。从国家密码管理局2010年版的《SM2椭圆曲线公钥密码算法》到2012年的GM/T 0003-2012 SM2椭圆曲线公钥密码算法再到GB/T 32918-2016 信息安全技术 SM2椭圆曲线公钥密码算法都在第一部分第四章的“点到字节串的转换”有详细说明。这也符合《SEC 1: Elliptic Curve Cryptography》May 21, 2009 Version 2.0中2.3.3 Elliptic-Curve-Point-to-Octet-String Conversion的描述。
#### 来自AI的问答
**问: 为什么一些crypto实现中椭圆曲线点转换为八位字节串时没有使用格式指示符**
**答:** 在椭圆曲线密码学ECC椭圆曲线点转换为八位字节串的方法有多种通常可以是压缩形式或未压缩形式。格式指示符也称为前缀用于表示该点是以压缩形式还是未压缩形式表示。
然而,一些实现可能会出于几个原因选择省略格式指示符:
1. **简洁性**:省略格式指示符可以简化实现过程,因为不需要处理不同形式的点表示。
2. **预设知识**:如果事先知道所有点都将处于某种特定形式(例如,所有点总是未压缩的),则可能会认为格式指示符是不必要的。
3. **兼容性**:并非所有系统或库都支持两种形式的点表示。为了确保最大兼容性,实现可能会选择始终使用一种形式,从而省略格式指示符。
4. **安全性**:在某些情况下,为了防止依赖于点形式的某些类型的攻击,可能会省略格式指示符。
请注意,这些是通用原因,具体原因可能会根据确切的上下文以及使用的特定密码协议或系统而有所不同。
**问:这不会引起互操作性问题吗?**
**答:** 是的,你说得对。在将椭圆曲线点转换为八位字节串时省略格式指示符,确实可能导致互操作性问题。
当系统需要互操作时,它们必须就数据交换的通用格式达成一致。如果一个系统在编码椭圆曲线点时没有使用格式指示符,而另一个系统期望格式指示符存在,它们将无法正确解释彼此的数据。
这就是为什么像SEC高效密码学标准这样的标准会指定使用格式指示符。例如SEC 1标准指定未压缩点的格式指示符应为0x04压缩点的格式指示符应为0x02或0x03。
因此,虽然在封闭系统中省略格式指示符可能会简化实现或提供其他好处,但它可能会阻碍与其他系统的互操作性。在编码和解码密码数据时,通常最好遵循已建立的标准,以确保最大的兼容性。
### 性能
从**v0.27.0**开始对大数据量的加解密做了优化处理尤其是KDF并行计算。详情请参考[SM2加解密性能](https://github.com/emmansun/gmsm/wiki/SM2%E5%8A%A0%E8%A7%A3%E5%AF%86%E6%80%A7%E8%83%BD)。
## 与KMS集成
国内云服务商的KMS服务大都提供SM2密钥我们一般调用其API进行签名和解密而验签和加密操作一般在本地用公钥即可完成。不过需要注意的是KMS提供的签名通常需要您在本地进行hash操作而sm2签名的hash又比较特殊下面示例供参考自版本**v0.24.0**开始,您可以直接使用函数```sm2.CalculateSM2Hash```
```go
func calculateSM2Hash(pub *ecdsa.PublicKey, data, uid []byte) ([]byte, error) {
if len(uid) == 0 {
uid = defaultUID
}
za, err := sm2.CalculateZA(pub, uid)
if err != nil {
return nil, err
}
md := sm3.New()
md.Write(za)
md.Write(data)
return md.Sum(nil), nil
}
```
公钥加密就没啥特殊只要确保输出密文的编码格式和KMS一致即可。
## 基于密码硬件定制SM2私钥
密码硬件SDF/SKF中的用户密钥私钥通常是无法导出的但都提供了签名、解密APIs供调用为了和本软件库集成需要实现以下接口
1. `crypto.Signer`,这个接口的实现通常把传入的数据作为哈希值。
2. `crypto.Decrypter`,这个接口用于解密操作。
通常需要实现四个方法:
1. `Public() crypto.PublicKey`
2. `Sign(rand io.Reader, digest []byte, opts SignerOpts) (signature []byte, err error)`
3. `Decrypt(rand io.Reader, msg []byte, opts DecrypterOpts) (plaintext []byte, err error)`
第一个返回公钥的方法是必须要实现的后面的方法取决于这个KEY的用途。
**注意**
1. `Sign(rand io.Reader, digest []byte, opts SignerOpts) (signature []byte, err error)`方法通常用于对哈希值作签名,最好遵从以下实现逻辑:检查`opts`是否是`*sm2.SM2SignerOption`类型,如果是,则把传入的`digest`作为原始数据进行处理,具体实现可以参考`sm2.SignASN1`函数。当然,在大多数情况下,直接将数据视为原始数据是可行的。实施者可以根据具体的应用场景灵活处理。
2. 如果密码硬件有自己的随机数源,可以忽略传入的`rand`
3. 很多设备签名函数通常只接收哈希值,需要调用```sm2.CalculateSM2Hash```或者**SDF**提供的哈希函数计算哈希值。
SDF API请参考《GB/T 36322-2018 密码设备应用接口规范》
## SM2扩展应用
SM2的一些扩展应用譬如从签名中恢复公钥、半同态加密、环签名等大多尚处于POC状态也无相关标准。其它扩展应用但凡椭圆曲线公钥密码算法能用到的场合包括但不限于
* [确定性签名](https://datatracker.ietf.org/doc/html/rfc6979)
* [可验证随机函数ECVRF](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-04)
* 盲签名
* 群签名
* 门限签名
* [Pederson承诺](https://crypto.stackexchange.com/questions/64437/what-is-a-pedersen-commitment)
### 从签名中恢复公钥
ECDSA 签名由两个数字整数组成r 和 s。以太坊还引入了额外的变量 v恢复标识符。签名可以表示成 {r, s, v}。SM2 签名也由两个数字整数组成r 和 s。签名算法中都只取随机点的X坐标并对N取模所以只有签名r和s的情况下可以恢复出多个公钥。
```go
// RecoverPublicKeysFromSM2Signature recovers two or four SM2 public keys from a given signature and hash.
// It takes the hash and signature as input and returns the recovered public keys as []*ecdsa.PublicKey.
// If the signature or hash is invalid, it returns an error.
// The function follows the SM2 algorithm to recover the public keys.
func RecoverPublicKeysFromSM2Signature(hash, sig []byte) ([]*ecdsa.PublicKey, error)
```
返回的结果:
* 公钥0 - Rx = (r - e) mod N; Ry是偶数compressFlag = 2
* 公钥1 - Rx = (r - e) mod N; Ry是奇数compressFlag = 3
* 公钥2 - Rx = ((r - e) mod N) + N; Ry是偶数compressFlag = 2
* 公钥3 - Rx = ((r - e) mod N) + N; Ry是奇数compressFlag = 3
Rx, Ry代表随机点R的X,Y坐标值。绝大多数情况下只会返回两个公钥后两者只有当(r - e) mod N的值小于P-1-N时才可能。
### 半同态加解密
EC-ElGamal with SM2的半同态加密Partially Homomorphic Encryption, PHE, 支持uint32 或者 int32类型。[Partially Homomorphic Encryption, EC-ElGamal with SM2](https://github.com/emmansun/sm2elgamal).
### 环签名
[Ring Signature Schemes Based on SM2 Digital Signature Algorithm](https://github.com/emmansun/sm2rsign).

View File

@ -1,46 +0,0 @@
# SM3密码杂凑算法
## 参考标准
* 《GB/T 32905-2016 信息安全技术 SM3密码杂凑算法》
您可以从[国家标准全文公开系统](https://openstd.samr.gov.cn/)在线阅读此标准。
## 概述
SM3密码杂凑算法或者叫SM3哈希算法它是一个输出结果为256位32字节的哈希算法。在本软件库中SM3的实现方法签名与Go语言中的哈希算法特别是SHA256保持一致所以用法也是一样的。具体API文档包括Example请参考[API Document](https://godoc.org/github.com/emmansun/gmsm)。
## 常用用法示例
```go
// 直接使用sm3.Sum方法
func ExampleSum() {
sum := sm3.Sum([]byte("hello world\n"))
fmt.Printf("%x", sum)
// Output: 4cc2036b86431b5d2685a04d289dfe140a36baa854b01cb39fcd6009638e4e7a
}
// 先创建sm3 hash实例再进行hash计算
func ExampleNew() {
h := sm3.New()
h.Write([]byte("hello world\n"))
fmt.Printf("%x", h.Sum(nil))
// Output: 4cc2036b86431b5d2685a04d289dfe140a36baa854b01cb39fcd6009638e4e7a
}
// 计算文件内容hash
func ExampleNew_file() {
f, err := os.Open("file.txt")
if err != nil {
log.Fatal(err)
}
defer f.Close()
h := sm3.New()
if _, err := io.Copy(h, f); err != nil {
log.Fatal(err)
}
fmt.Printf("%x", h.Sum(nil))
}
```
## 性能
请参考[SM3密码杂凑算法性能优化](https://github.com/emmansun/gmsm/wiki/SM3%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96)。

View File

@ -1,268 +0,0 @@
# SM4分组密码算法应用指南
## 参考标准
* 《GB/T 32907-2016 信息安全技术 SM4分组密码算法》
* 《GB/T 17964-2021 信息安全技术 分组密码算法的工作模式》
您可以从[国家标准全文公开系统](https://openstd.samr.gov.cn/)在线阅读这些标准。
## 概述
SM4分组密码算法其地位类似NIST中的AES分组密码算法密钥长度128位16字节分组大小也是128位16字节。在本软件库中SM4的实现与Go语言中的AES实现一致也实现了```cipher.Block```接口所以所有Go语言中实现的工作模式CBC/GCM/CFB/OFB/CTR都能与SM4组合使用。
## [工作模式](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation)
Go语言实现的工作模式主要有三类
* 基于分组的工作模式 ```cipher.BlockMode```譬如CBC。
* 带有关联数据的认证加密工作模式```cipher.AEAD```譬如GCM。
* 流加密工作模式```cipher.Stream```譬如CTR、CFB、OFB。
在实际加解密操作中,我们一般不会直接使用```cipher.Block```必须结合分组密码算法的工作模式使用。除了Go语言自带的工作模式CBC/GCM/CFB/OFB/CTR本软件库也实现了下列工作模式
* ECB - 电码本模式
* BC - 分组链接模式
* HCTR - 带泛杂凑函数的计数器模式
* XTS - 带密文挪用的XEX可调分组密码模式
* OFBNLF - 带非线性函数的输出反馈模式
* CCM - 分组密码链接-消息认证码组合模式
其中ECB/BC/HCTR/XTS/OFBNLF是《GB/T 17964-2021 信息安全技术 分组密码算法的工作模式》列出的工作模式。BC/OFBNLF模式是商密中的遗留工作模式**不建议**在新的应用中使用。XTS/HCTR模式适用于对磁盘加密其中HCTR模式是《GB/T 17964-2021 信息安全技术 分组密码算法的工作模式》最新引入的HCTR模式最近业界研究比较多也指出了原论文中的BugsOn modern processors HCTR [WFW05](https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.470.5288) is one of the most efficient constructions for building a tweakable super-pseudorandom permutation. However, a bug in the specification and another in Chakraborty and Nandis security proof [CN08](https://www.iacr.org/cryptodb/archive/2008/FSE/paper/15611.pdf) invalidate the claimed security bound.
不知道这个不足是否会影响到这个工作模式的采用。很奇怪《GB/T 17964-2021 信息安全技术 分组密码算法的工作模式》为何没有纳入GCM工作模式难道是版权问题
本软件库引入CCM模式只是为了有些标准还用到该模式。ECB模式也不建议单独使用。
目前本软件库的SM4针对ECB/CBC/GCM/XTS工作模式进行了绑定组合性能优化暂时没有计划使用汇编优化HCTR模式HCTR模式可以采用和GCM类似的方法进行汇编优化
### 使用建议
常用的对称加解密应用场合推荐优先使用GCM模式其次CBC模式一些安全扫描工具也会把CBC工作模式列为安全性不高的工作模式。我能想到的GCM模式的缺点是加解密的相关方不支持GCM模式或者实现性能不好。
#### 关于ECB模式
1. 请使用本软件库提供的`NewECBEncrypter/NewECBDecrypter`方法,否则大概率不会得到性能优化。
2. 基于安全考虑,最好不要使用该模式。
## 填充padding
有些分组密码算法的工作模式(譬如实现了```cipher.BlockMode```接口的模式的输入要求是其长度必须是分组大小的整数倍。《GB/T 17964-2021 信息安全技术 分组密码算法的工作模式》附录C中列出了以下几种填充模式
* 填充方式 1对应本软件库的```padding.NewPKCS7Padding```
* 填充方式 2对应本软件库的```padding.NewISO9797M2Padding```
* 填充方式 3目前没有实现它对应ISO/IEC_9797-1 padding method 3
本软件库也实现了ANSI X9.23标准中定义的填充方式```padding.NewANSIX923Padding```**用的最广的还是填充方式 1PKCS7填充**。
您如果使用实现了```cipher.BlockMode```接口的分组加密工作模式那您也必须与相关方协调好填充模式。JAVA库的对称加密算法字符串名就包含了所有信息譬如**AES/CBC/PKCS7Padding**。
## 密文及其相关参数的传输和存储
如果是自描述的那肯定有相关标准定义相关ASN.1结构并且给分组密码算法、工作模式、填充方式都赋予一个OID。或者如hashicorp vault一个对称密钥确定了分组密码算法、工作模式、填充方式最终输出密文是密钥ID和原始密文的组合。
如果是内部服务之间,可能是在应用/服务级别自定义所使用分组密码算法、工作模式、填充方式的标识作为应用的METADATA也就是加密用的METADATA和密文分离。
也可能是隐式使用一致的分组密码算法、工作模式、填充方式,也就是代码知道,还有文档知道?
具体使用哪种方式,取决于应用场景。
另外一个就是必须和密文一起存储/传输的参数譬如如果使用CBC工作模式那IV怎么办如果是GCM模式那Nonce、Nonce长度、Tag长度怎么办这通常也有两种方案
* 使用预定义的ASN.1结构
* 和密文简单拼接譬如CBC工作模式前面16字节IV后面ciphertextGCM模式使用默认Tag长度和Nonce长度前面12字节Nonce后面ciphertext。
至于要将二进制转为文本传输、存储编个码就行标准base64 / URL base64 / HEX事先协调、定义好就可以了。这里顺便推荐一下[性能更好的BASE64实现](https://github.com/emmansun/base64)。
## API文档及示例
这里只列出GCM/CBC的例子其余请参考[API Document](https://godoc.org/github.com/emmansun/gmsm)。
### GCM示例
```go
func Example_encryptGCM() {
// Load your secret key from a safe place and reuse it across multiple
// Seal/Open calls. (Obviously don't use this example key for anything
// real.) If you want to convert a passphrase to a key, use a suitable
// package like bcrypt or scrypt.
key, _ := hex.DecodeString("6368616e676520746869732070617373")
plaintext := []byte("exampleplaintext")
block, err := sm4.NewCipher(key)
if err != nil {
panic(err.Error())
}
// Never use more than 2^32 random nonces with a given key because of the risk of a repeat.
nonce := make([]byte, 12)
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
panic(err.Error())
}
sm4gcm, err := cipher.NewGCM(block)
if err != nil {
panic(err.Error())
}
// You can encode the nonce and ciphertext with your own scheme
ciphertext := sm4gcm.Seal(nil, nonce, plaintext, nil)
fmt.Printf("%x %x\n", nonce, ciphertext)
}
func Example_decryptGCM() {
// Load your secret key from a safe place and reuse it across multiple
// Seal/Open calls. (Obviously don't use this example key for anything
// real.) If you want to convert a passphrase to a key, use a suitable
// package like bcrypt or scrypt.
key, _ := hex.DecodeString("6368616e676520746869732070617373")
// You can decode the nonce and ciphertext with your encoding scheme
ciphertext, _ := hex.DecodeString("b7fdece1c6b3dce9cc386e8bc93df0ce496df789166229f14b973b694a4a23c3")
nonce, _ := hex.DecodeString("07d168e0517656ab7131f495")
block, err := sm4.NewCipher(key)
if err != nil {
panic(err.Error())
}
sm4gcm, err := cipher.NewGCM(block)
if err != nil {
panic(err.Error())
}
plaintext, err := sm4gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
panic(err.Error())
}
fmt.Printf("%s\n", plaintext)
// Output: exampleplaintext
}
```
### CBC示例
```go
func Example_encryptCBC() {
// Load your secret key from a safe place and reuse it across multiple
// NewCipher calls. (Obviously don't use this example key for anything
// real.) If you want to convert a passphrase to a key, use a suitable
// package like bcrypt or scrypt.
key, _ := hex.DecodeString("6368616e676520746869732070617373")
plaintext := []byte("sm4 exampleplaintext")
block, err := sm4.NewCipher(key)
if err != nil {
panic(err)
}
// CBC mode works on blocks so plaintexts may need to be padded to the
// next whole block. For an example of such padding, see
// https://tools.ietf.org/html/rfc5246#section-6.2.3.2.
pkcs7 := padding.NewPKCS7Padding(sm4.BlockSize)
paddedPlainText := pkcs7.Pad(plaintext)
// The IV needs to be unique, but not secure. Therefore it's common to
// include it at the beginning of the ciphertext.
ciphertext := make([]byte, sm4.BlockSize+len(paddedPlainText))
iv := ciphertext[:sm4.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
}
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext[sm4.BlockSize:], paddedPlainText)
fmt.Printf("%x\n", ciphertext)
}
func Example_decryptCBC() {
// Load your secret key from a safe place and reuse it across multiple
// NewCipher calls. (Obviously don't use this example key for anything
// real.) If you want to convert a passphrase to a key, use a suitable
// package like bcrypt or scrypt.
key, _ := hex.DecodeString("6368616e676520746869732070617373")
ciphertext, _ := hex.DecodeString("4d5a1486bfda1b34447afd5bb852e77a867cc6b726a8a0e0ef9b2c21fffc3a30b42acf504628f65cb3fba339101c98ff")
block, err := sm4.NewCipher(key)
if err != nil {
panic(err)
}
// The IV needs to be unique, but not secure. Therefore it's common to
// include it at the beginning of the ciphertext.
if len(ciphertext) < sm4.BlockSize {
panic("ciphertext too short")
}
iv := ciphertext[:sm4.BlockSize]
ciphertext = ciphertext[sm4.BlockSize:]
mode := cipher.NewCBCDecrypter(block, iv)
// CryptBlocks can work in-place if the two arguments are the same.
mode.CryptBlocks(ciphertext, ciphertext)
// Unpad plaintext
pkcs7 := padding.NewPKCS7Padding(sm4.BlockSize)
ciphertext, err = pkcs7.Unpad(ciphertext)
if err != nil {
panic(err)
}
fmt.Printf("%s\n", ciphertext)
// Output: sm4 exampleplaintext
}
```
需要注意一下,```cipher.AEAD```对```dst```参数的要求:
```cipher.AEAD```是**追加**结果,所以如果要重用切片,要注意一下。而且```Seal```的结果要比plaintext长加上tag所以只有```cap(plaintext)>=len(plaintext)+tagSize```时才会重用,否则还是会新建一个切片。
```go
// AEAD is a cipher mode providing authenticated encryption with associated
// data. For a description of the methodology, see
// https://en.wikipedia.org/wiki/Authenticated_encryption.
type AEAD interface {
// NonceSize returns the size of the nonce that must be passed to Seal
// and Open.
NonceSize() int
// Overhead returns the maximum difference between the lengths of a
// plaintext and its ciphertext.
Overhead() int
// Seal encrypts and authenticates plaintext, authenticates the
// additional data and appends the result to dst, returning the updated
// slice. The nonce must be NonceSize() bytes long and unique for all
// time, for a given key.
//
// To reuse plaintext's storage for the encrypted output, use plaintext[:0]
// as dst. Otherwise, the remaining capacity of dst must not overlap plaintext.
Seal(dst, nonce, plaintext, additionalData []byte) []byte
// Open decrypts and authenticates ciphertext, authenticates the
// additional data and, if successful, appends the resulting plaintext
// to dst, returning the updated slice. The nonce must be NonceSize()
// bytes long and both it and the additional data must match the
// value passed to Seal.
//
// To reuse ciphertext's storage for the decrypted output, use ciphertext[:0]
// as dst. Otherwise, the remaining capacity of dst must not overlap plaintext.
//
// Even if the function fails, the contents of dst, up to its capacity,
// may be overwritten.
Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error)
}
```
而```cipher.BlockMode```和```cipher.Stream```的话,则是直接覆盖。
## 性能
SM4分组密码算法的软件高效实现不算CPU指令支持的话已知有如下几种方法
* S盒和L转换预计算本软件库纯Go语言实现采用该方法
* SIMD并行处理并行查表
* SIMD并行处理借助CPU的AES指令本软件库采用该方法
* SIMD并行处理借助CPU的GFNI指令部分新AMD64 CPU架构支持该指令本软件库尚未实现[SM4 with GFNI](https://github.com/emmansun/gmsm/wiki/SM4-with-GFNI)
* SIMD并行处理位切片(bitslicing)[参考实现](https://github.com/emmansun/sm4bs)
当然这些与有CPU指令支持的AES算法相比性能差距依然偏大要是工作模式不支持并行差距就更巨大了。
### 混合方式
从**v0.25.0**开始AMD64/ARM64 支持AES-NI的CPU架构下**默认会使用混合方式**,即```cipher.Block```的方法会用纯Go语言实现而对于可以并行的加解密模式则还是会尽量采用AES-NI和SIMD并行处理。您可以通过环境变量```FORCE_SM4BLOCK_AESNI=1```来强制都使用AES-NI实现和v0.25.0之前版本的行为一样)。请参考[SM4: 单block的性能问题](https://github.com/emmansun/gmsm/discussions/172)。
**注意**目前的纯Golang SM4实现查表实现是以可变时间运行的
## 与KMS集成
可能您会说如果我在KMS中创建了一个SM4对称密钥就不需要本地加解密了这话很对不过有种场景会用到
* 在KMS中只创建非对称密钥KEK
* 对称加解密在本地进行;
* 对称加密密钥,或者称为数据密钥(DEK/CEK)可以在本地通过安全伪随机数函数生成也可以通过KMS的Data Key API生成如果有这类API的话用Data Key API的话会有DEK/CEK明文传输问题毕竟KMS需要把DEK/CEK的密文/明文同时返回。
这种加密方案有什么优点呢?
* KMS API通常都会限流譬如200次/秒通过把对称加解密放在本地进行可以有效减少KMS交互。
* 减少网络带宽占用。
* 避免明文数据的网络传输。
当然前提是用于本地对称加解密的SM4分组密码算法和选用的工作模式性能可以满足需求。

View File

@ -1,329 +0,0 @@
# SM9标识密码算法应用指南
## 参考标准
* 《GB/T 38635.1-2020 信息安全技术 SM9标识密码算法 第1部分总则》
* 《GB/T 38635.2-2020 信息安全技术 SM9标识密码算法 第2部分算法》
* 《GB/T 41389-2022 信息安全技术 SM9密码算法使用规范》
* 《GM/T 0086-2020 基于SM9标识密码算法的密钥管理系统技术规范》
您可以从[国家标准全文公开系统](https://openstd.samr.gov.cn/)在线阅读这些标准。
## 概述
SM9算法是一种基于双线性对的标识密码算法简称“IBC”由数字签名算法、标识加密算法、密钥协商协议三部分组成相比于传统密码体系SM9密码系统号称的**最大的优势就是无需证书、易于使用、易于管理、总体拥有成本低**,但这显然过于理想化:
* **KGC**中心的标准化与权威性。标志密码算法依然需要主密钥需要中心化的KGC私有系统可能自己搞个简单点的服务就行但作为公共、公开服务系统没有标准化与权威性是不行的。
* 用户私钥依然有被盗、遗失的风险,所以依然有用户标识作废、重新启用等需求。这也意味着客户端依然需要访问**KGC**的公开参数服务,查询用户标识状态。
* **《GM/T 0086-2020 基于SM9标识密码算法的密钥管理系统技术规范》** 定义了相关规范但不知道有没有建成相关系统。且这和传统的公钥体系PKI相比有何优势
同时SM9标识密码算法还有以下问题
* 基于双线性对的标识密码算法的实现复杂度和性能问题本软件库的SM9实现其签名、验签性能不到SM2的十分之一
* SM9标识密码算法选择的bn256曲线安全问题[128位安全性挑战](https://moderncrypto.org/mail-archive/curves/2016/000740.html)
上述只是简单的探讨没有贬低SM9标识密码算法的意思。
## 主公私钥对
SM9标识密码算法用于签名和加密的主公私钥对是分开的需要各自独立生成
* ```sm9.GenerateSignMasterKey```用于生成签名主密钥对。
* ```sm9.GenerateEncryptMasterKey```用于生成加密主密钥对。
其中签名主公钥是G2上的点加密主公钥是G1上的点而签名、加密主私钥都是一个随机大整数。
主公私钥的ASN.1数据格式定义请参考《GB/T 41389-2022 信息安全技术 SM9密码算法使用规范》和椭圆曲线的公私钥ASN.1数据格式类似。本软件实现了相应的Marshal/Unmarshal方法。
## 用户私钥
用户的签名私钥由签名主私钥、用户标识生成:```(master *SignMasterPrivateKey) GenerateUserKey(uid []byte, hid byte) (*SignPrivateKey, error)```它是G1上的点。
用户的加密私钥由加密主私钥、用户标识生成:```func (master *EncryptMasterPrivateKey) GenerateUserKey(uid []byte, hid byte) (*EncryptPrivateKey, error)```它是G2上的点。
《GB/T 41389-2022 信息安全技术 SM9密码算法使用规范》中 hid 定义如下:
* hid = 1签名
* hid = 3加密
本软件实现没有硬编码**hid**的值。
用户签名、加密私钥的ASN.1数据格式定义请参考《GB/T 41389-2022 信息安全技术 SM9密码算法使用规范》和椭圆曲线点的ASN.1数据格式类似。本软件实现了相应的Marshal/Unmarshal方法。
目前```smx509```中实现的```MarshalPKCS8PrivateKey/ParsePKCS8PrivateKey```没有相关标准,只是为了和[gmssl](https://github.com/guanzhi/GmSSL)互操作验证,请参考[sm9:【feature】是否考虑支持 pem 格式的公私钥输出](https://github.com/emmansun/gmsm/issues/86)。
```go
func TestMarshalPKCS8SM9SignPrivateKey(t *testing.T) {
masterKey, err := sm9.GenerateSignMasterKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
privateKey, err := masterKey.GenerateUserKey([]byte("emmansun"), 0x01)
if err != nil {
t.Fatal(err)
}
res, err := MarshalPKCS8PrivateKey(privateKey)
if err != nil {
t.Fatal(err)
}
privateKey1, err := ParsePKCS8PrivateKey(res)
if err != nil {
t.Fatal(err)
}
privateKey2, ok := privateKey1.(*sm9.SignPrivateKey)
if !ok {
t.Fatalf("not expected key")
}
if !privateKey.PrivateKey.Equal(privateKey2.PrivateKey) ||
!privateKey.MasterPublicKey.Equal(privateKey2.MasterPublicKey) {
t.Fatalf("not same key")
}
}
func TestMarshalPKCS8SM9EncPrivateKey(t *testing.T) {
masterKey, err := sm9.GenerateEncryptMasterKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
privateKey, err := masterKey.GenerateUserKey([]byte("emmansun"), 0x01)
if err != nil {
t.Fatal(err)
}
res, err := MarshalPKCS8PrivateKey(privateKey)
if err != nil {
t.Fatal(err)
}
privateKey1, err := ParsePKCS8PrivateKey(res)
if err != nil {
t.Fatal(err)
}
privateKey2, ok := privateKey1.(*sm9.EncryptPrivateKey)
if !ok {
t.Fatalf("not expected key")
}
if !privateKey.PrivateKey.Equal(privateKey2.PrivateKey) ||
!privateKey.MasterPublicKey.Equal(privateKey2.MasterPublicKey) {
t.Fatalf("not same key")
}
}
func TestMarshalPKCS8SM9SignMasterPrivateKey(t *testing.T) {
masterKey, err := sm9.GenerateSignMasterKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
res, err := MarshalPKCS8PrivateKey(masterKey)
if err != nil {
t.Fatal(err)
}
masterKey1, err := ParsePKCS8PrivateKey(res)
if err != nil {
t.Fatal(err)
}
masterKey2, ok := masterKey1.(*sm9.SignMasterPrivateKey)
if !ok {
t.Fatalf("not expected key")
}
masterKey2.MasterPublicKey.Marshal()
if !(masterKey.D.Cmp(masterKey2.D) == 0 && masterKey.MasterPublicKey.Equal(masterKey2.MasterPublicKey)) {
t.Fatalf("not same key")
}
}
func TestMarshalPKCS8SM9EncMasterPrivateKey(t *testing.T) {
masterKey, err := sm9.GenerateEncryptMasterKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
res, err := MarshalPKCS8PrivateKey(masterKey)
if err != nil {
t.Fatal(err)
}
masterKey1, err := ParsePKCS8PrivateKey(res)
if err != nil {
t.Fatal(err)
}
masterKey2, ok := masterKey1.(*sm9.EncryptMasterPrivateKey)
if !ok {
t.Fatalf("not expected key")
}
masterKey2.MasterPublicKey.Marshal()
if !(masterKey.D.Cmp(masterKey2.D) == 0 && masterKey.MasterPublicKey.Equal(masterKey2.MasterPublicKey)) {
t.Fatalf("not same key")
}
}
```
## 数字签名
使用用户签名私钥进行签名,使用签名主公钥和用户标识进行验签:
```go
func ExampleSignPrivateKey_Sign() {
// real user sign private key should be from secret storage.
kb, _ := hex.DecodeString("0130E78459D78545CB54C587E02CF480CE0B66340F319F348A1D5B1F2DC5F4")
var b cryptobyte.Builder
b.AddASN1BigInt(new(big.Int).SetBytes(kb))
kb, _ = b.Bytes()
masterkey := new(sm9.SignMasterPrivateKey)
err := masterkey.UnmarshalASN1(kb)
if err != nil {
fmt.Fprintf(os.Stderr, "Error from UnmarshalASN1: %s\n", err)
return
}
hid := byte(0x01)
uid := []byte("Alice")
userKey, err := masterkey.GenerateUserKey(uid, hid)
if err != nil {
fmt.Fprintf(os.Stderr, "Error from GenerateUserKey: %s\n", err)
return
}
// sm9 sign
hash := []byte("Chinese IBS standard")
sig, err := userKey.Sign(rand.Reader, hash, nil)
if err != nil {
fmt.Fprintf(os.Stderr, "Error from Sign: %s\n", err)
return
}
// Since sign is a randomized function, signature will be
// different each time.
fmt.Printf("%x\n", sig)
}
func ExampleVerifyASN1() {
// get master public key, can be from pem
masterPubKey := new(sm9.SignMasterPublicKey)
keyBytes, _ := hex.DecodeString("03818200049f64080b3084f733e48aff4b41b565011ce0711c5e392cfb0ab1b6791b94c40829dba116152d1f786ce843ed24a3b573414d2177386a92dd8f14d65696ea5e3269850938abea0112b57329f447e3a0cbad3e2fdb1a77f335e89e1408d0ef1c2541e00a53dda532da1a7ce027b7a46f741006e85f5cdff0730e75c05fb4e3216d")
err := masterPubKey.UnmarshalASN1(keyBytes)
if err != nil {
fmt.Fprintf(os.Stderr, "Error from UnmarshalASN1: %s\n", err)
return
}
hid := byte(0x01)
uid := []byte("Alice")
hash := []byte("Chinese IBS standard")
sig, _ := hex.DecodeString("30660420b0d0c0bb1b57ea0d5b51cb5c96be850b8c2eef6b0fff5fcccb524b972574e6eb03420004901819575c9211c7b4e6e137794d23d0095608bcdad5c82dbff05777c5b49c763e4425acea2aaedf9e48d4784b4e4a5621cc3663fe0aae44dcbeac183fee9b0f")
ok := sm9.VerifyASN1(masterPubKey, uid, hid, hash, sig)
fmt.Printf("%v\n", ok)
// Output: true
}
func ExampleSignMasterPublicKey_Verify() {
// get master public key, can be from pem
masterPubKey := new(sm9.SignMasterPublicKey)
keyBytes, _ := hex.DecodeString("03818200049f64080b3084f733e48aff4b41b565011ce0711c5e392cfb0ab1b6791b94c40829dba116152d1f786ce843ed24a3b573414d2177386a92dd8f14d65696ea5e3269850938abea0112b57329f447e3a0cbad3e2fdb1a77f335e89e1408d0ef1c2541e00a53dda532da1a7ce027b7a46f741006e85f5cdff0730e75c05fb4e3216d")
err := masterPubKey.UnmarshalASN1(keyBytes)
if err != nil {
fmt.Fprintf(os.Stderr, "Error from UnmarshalASN1: %s\n", err)
return
}
hid := byte(0x01)
uid := []byte("Alice")
hash := []byte("Chinese IBS standard")
sig, _ := hex.DecodeString("30660420b0d0c0bb1b57ea0d5b51cb5c96be850b8c2eef6b0fff5fcccb524b972574e6eb03420004901819575c9211c7b4e6e137794d23d0095608bcdad5c82dbff05777c5b49c763e4425acea2aaedf9e48d4784b4e4a5621cc3663fe0aae44dcbeac183fee9b0f")
ok := masterPubKey.Verify(uid, hid, hash, sig)
fmt.Printf("%v\n", ok)
// Output: true
}
```
签名结果ASN.1格式请参考参考《GB/T 41389-2022 信息安全技术 SM9密码算法使用规范》。
## 密钥封装
使用加密主公钥和目标用户标识进行密钥封装,使用用户加密私钥和用户标识进行解封:
```go
func ExampleEncryptMasterPublicKey_WrapKey() {
// get master public key, can be from pem
masterPubKey := new(sm9.EncryptMasterPublicKey)
keyBytes, _ := hex.DecodeString("03420004787ed7b8a51f3ab84e0a66003f32da5c720b17eca7137d39abc66e3c80a892ff769de61791e5adc4b9ff85a31354900b202871279a8c49dc3f220f644c57a7b1")
err := masterPubKey.UnmarshalASN1(keyBytes)
if err != nil {
fmt.Fprintf(os.Stderr, "Error from UnmarshalASN1: %s\n", err)
return
}
hid := byte(0x03)
uid := []byte("Bob")
key, cipherDer, err := masterPubKey.WrapKey(rand.Reader, uid, hid, 32)
if err != nil {
fmt.Fprintf(os.Stderr, "Error from WrapKeyASN1: %s\n", err)
return
}
// Since WrapKey is a randomized function, result will be
// different each time.
fmt.Printf("%s %s\n", hex.EncodeToString(key), hex.EncodeToString(cipherDer))
}
func ExampleEncryptPrivateKey_UnwrapKey() {
// real user encrypt private key should be from secret storage, e.g. password protected pkcs8 file
kb, _ := hex.DecodeString("038182000494736acd2c8c8796cc4785e938301a139a059d3537b6414140b2d31eecf41683115bae85f5d8bc6c3dbd9e5342979acccf3c2f4f28420b1cb4f8c0b59a19b1587aa5e47570da7600cd760a0cf7beaf71c447f3844753fe74fa7ba92ca7d3b55f27538a62e7f7bfb51dce08704796d94c9d56734f119ea44732b50e31cdeb75c1")
userKey := new(sm9.EncryptPrivateKey)
err := userKey.UnmarshalASN1(kb)
if err != nil {
fmt.Fprintf(os.Stderr, "Error from UnmarshalASN1: %s\n", err)
return
}
cipherDer, _ := hex.DecodeString("0342000447689629d1fa57e8def447f42b75e28518a1b692891528ca596f7bcbf581c7cf429ed01b114ce157ed4eadd0b2ded9a7e475e347f67b6affa3a6cf654573f978")
key, err := userKey.UnwrapKey([]byte("Bob"), cipherDer, 32)
if err != nil {
fmt.Fprintf(os.Stderr, "Error from UnwrapKey: %s\n", err)
return
}
fmt.Printf("%s\n", hex.EncodeToString(key))
// Output: 270c42505bca90a8084064ea8af279364405a8195f30664082ead3d6991ed70f
}
```
密钥封装结果ASN.1格式请参考参考《GB/T 41389-2022 信息安全技术 SM9密码算法使用规范》。
## 公钥加密算法
使用加密主公钥和目标用户标识进行加密,使用用户加密私钥和用户标识进行解密:
```go
func ExampleEncryptMasterPublicKey_Encrypt() {
// get master public key, can be from pem
masterPubKey := new(sm9.EncryptMasterPublicKey)
keyBytes, _ := hex.DecodeString("03420004787ed7b8a51f3ab84e0a66003f32da5c720b17eca7137d39abc66e3c80a892ff769de61791e5adc4b9ff85a31354900b202871279a8c49dc3f220f644c57a7b1")
err := masterPubKey.UnmarshalASN1(keyBytes)
if err != nil {
fmt.Fprintf(os.Stderr, "Error from UnmarshalASN1: %s\n", err)
return
}
hid := byte(0x03)
uid := []byte("Bob")
ciphertext, err := masterPubKey.Encrypt(rand.Reader, uid, hid, []byte("Chinese IBE standard"), sm9.DefaultEncrypterOpts)
if err != nil {
fmt.Fprintf(os.Stderr, "Error from Encrypt: %s\n", err)
return
}
// Since Encrypt is a randomized function, result will be
// different each time.
fmt.Printf("%s\n", hex.EncodeToString(ciphertext))
}
func ExampleEncryptPrivateKey_Decrypt() {
// real user encrypt private key should be from secret storage.
kb, _ := hex.DecodeString("038182000494736acd2c8c8796cc4785e938301a139a059d3537b6414140b2d31eecf41683115bae85f5d8bc6c3dbd9e5342979acccf3c2f4f28420b1cb4f8c0b59a19b1587aa5e47570da7600cd760a0cf7beaf71c447f3844753fe74fa7ba92ca7d3b55f27538a62e7f7bfb51dce08704796d94c9d56734f119ea44732b50e31cdeb75c1")
userKey := new(sm9.EncryptPrivateKey)
err := userKey.UnmarshalASN1(kb)
if err != nil {
fmt.Fprintf(os.Stderr, "Error from UnmarshalASN1: %s\n", err)
return
}
uid := []byte("Bob")
cipherDer, _ := hex.DecodeString("307f020100034200042cb3e90b0977211597652f26ee4abbe275ccb18dd7f431876ab5d40cc2fc563d9417791c75bc8909336a4e6562450836cc863f51002e31ecf0c4aae8d98641070420638ca5bfb35d25cff7cbd684f3ed75f2d919da86a921a2e3e2e2f4cbcf583f240414b7e776811774722a8720752fb1355ce45dc3d0df")
plaintext, err := userKey.DecryptASN1(uid, cipherDer)
if err != nil {
fmt.Fprintf(os.Stderr, "Error from Decrypt: %s\n", err)
return
}
fmt.Printf("%s\n", plaintext)
// Output: Chinese IBE standard
}
```
密文封装结果ASN.1格式请参考参考《GB/T 41389-2022 信息安全技术 SM9密码算法使用规范》。
SM9公钥加密算法支持多种对称加密算法不像SM2公钥加密算法只支持XOR。不过由于非XOR对称加密算法有几个需要IV而规范没有定义所以会有互操作问题
* [关于SM9 非XOR加密标准问题](https://github.com/emmansun/gmsm/discussions/112)。
* 《GB/T 41389-2022 信息安全技术 SM9密码算法使用规范》6.1.5 加密数据格式。
## 密钥交换
在这里不详细介绍使用方法一般只有tls/tlcp才会用到普通应用通常不会涉及这一块请参考[API Document](https://godoc.org/github.com/emmansun/gmsm)。
## 性能
参考[SM9实现及优化](https://github.com/emmansun/gmsm/wiki/SM9%E5%AE%9E%E7%8E%B0%E5%8F%8A%E4%BC%98%E5%8C%96)。

View File

@ -1,226 +0,0 @@
# 祖冲之序列密码算法应用指南
## 参考标准
* 《GB/T 33133.1-2016 信息安全技术 祖冲之序列密码算法 第1部分算法描述》
* 《GB/T 33133.2-2021 信息安全技术 祖冲之序列密码算法 第2部分保密性算法》
* 《GB/T 33133.3-2021 信息安全技术 祖冲之序列密码算法 第2部分完整性算法》
* [《祖冲之算法ZUC-256算法草案(中文)》](https://github.com/guanzhi/GM-Standards/blob/master/%E5%85%AC%E5%BC%80%E6%96%87%E6%A1%A3/%E7%A5%96%E5%86%B2%E4%B9%8B%E7%AE%97%E6%B3%95%EF%BC%9AZUC-256%E7%AE%97%E6%B3%95%E8%8D%89%E6%A1%88(%E4%B8%AD%E6%96%87).pdf)
您可以从[国家标准全文公开系统](https://openstd.samr.gov.cn/)在线阅读这些标准。
## 保密性算法
保密性算法EEA实现了```cipher.Stream```接口,所以和其它流密码算法使用类似,只是创建方法不同而已。
| | ZUC-128 | ZUC-256 |
| :--- | :--- | :--- |
| Key字节数 | 16 | 32 |
| IV字节数 | 16 | 23 |
```go
func ExampleNewCipher() {
// Load your secret key from a safe place and reuse it across multiple
// NewCipher calls. (Obviously don't use this example key for anything
// real.) If you want to convert a passphrase to a key, use a suitable
// package like bcrypt or scrypt.
key, _ := hex.DecodeString("6368616e676520746869732070617373")
plaintext := []byte("some plaintext")
const ivSize = zuc.IVSize128
// The IV needs to be unique, but not secure. Therefore it's common to
// include it at the beginning of the ciphertext.
ciphertext := make([]byte, ivSize+len(plaintext))
iv := ciphertext[:ivSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
}
stream, err := zuc.NewCipher(key, iv)
if err != nil {
panic(err)
}
stream.XORKeyStream(ciphertext[ivSize:], plaintext)
// It's important to remember that ciphertexts must be authenticated
// (i.e. by using crypto/hmac) as well as being encrypted in order to
// be secure.
// Stream cipher is the same for both encryption and decryption, so we can
// also decrypt that ciphertext with NewCTR.
plaintext2 := make([]byte, len(plaintext))
stream, err = zuc.NewCipher(key, iv)
if err != nil {
panic(err)
}
stream.XORKeyStream(plaintext2, ciphertext[ivSize:])
fmt.Printf("%s\n", plaintext2)
// Output: some plaintext
}
func ExampleNewCipher_zuc256() {
// Load your secret key from a safe place and reuse it across multiple
// NewCipher calls. (Obviously don't use this example key for anything
// real.) If you want to convert a passphrase to a key, use a suitable
// package like bcrypt or scrypt.
key, _ := hex.DecodeString("6368616e6765207468697320706173736368616e676520746869732070617373")
plaintext := []byte("some plaintext")
const ivSize = zuc.IVSize256
// The IV needs to be unique, but not secure. Therefore it's common to
// include it at the beginning of the ciphertext.
ciphertext := make([]byte, ivSize+len(plaintext))
iv := ciphertext[:ivSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
}
stream, err := zuc.NewCipher(key, iv)
if err != nil {
panic(err)
}
stream.XORKeyStream(ciphertext[ivSize:], plaintext)
// It's important to remember that ciphertexts must be authenticated
// (i.e. by using crypto/hmac) as well as being encrypted in order to
// be secure.
// Stream cipher is the same for both encryption and decryption, so we can
// also decrypt that ciphertext with NewCTR.
plaintext2 := make([]byte, len(plaintext))
stream, err = zuc.NewCipher(key, iv)
if err != nil {
panic(err)
}
stream.XORKeyStream(plaintext2, ciphertext[ivSize:])
fmt.Printf("%s\n", plaintext2)
// Output: some plaintext
}
```
### Seekable Stream
完整性算法支持Seekable Stream也就是随机定位到某点进行处理内部实现了分桶缓存状态每个状态的大小大概是88字节`bucketSize`的大小可以结合要处理的流大小以及内存占用来平衡考虑。同时,`bucketSize`内部会被处理成128字节的倍数以利于实现。
如果您没有对同一个流反复进行**前进**、**后退**加解密的需求,可以使用`NewCipher`或者`NewEEACipher`方法,避免内部状态缓存。
## 完整性算法
完整性算法实现了```hash.Hash```接口,所以其使用方法和其它哈希算法类似。
| | ZUC-128 | ZUC-256 |
| :--- | :--- | :--- |
| Key字节数 | 16 | 32 |
| IV字节数 | 16 | 23 |
| MAC字节数 | 4 | 4/8/16 |
```go
func ExampleNewHash() {
// Load your secret key from a safe place and reuse it across multiple
// NewCipher calls. (Obviously don't use this example key for anything
// real.) If you want to convert a passphrase to a key, use a suitable
// package like bcrypt or scrypt.
key, _ := hex.DecodeString("6368616e676520746869732070617373")
// iv should be generated randomly
iv, _ := hex.DecodeString("6368616e676520746869732070617373")
h, err := zuc.NewHash(key, iv)
if err != nil {
panic(err)
}
h.Write([]byte("hello world\n"))
fmt.Printf("%x", h.Sum(nil))
// Output: c43cd26a
}
func ExampleNewHash256_tagSize4() {
// Load your secret key from a safe place and reuse it across multiple
// NewCipher calls. (Obviously don't use this example key for anything
// real.) If you want to convert a passphrase to a key, use a suitable
// package like bcrypt or scrypt.
key, _ := hex.DecodeString("6368616e6765207468697320706173736368616e676520746869732070617373")
// iv should be generated randomly
iv, _ := hex.DecodeString("6368616e6765207468697320706173736368616e676520")
h, err := zuc.NewHash256(key, iv, 4)
if err != nil {
panic(err)
}
h.Write([]byte("hello world\n"))
fmt.Printf("%x", h.Sum(nil))
// Output: b76f96ed
}
func ExampleNewHash256_tagSize8() {
// Load your secret key from a safe place and reuse it across multiple
// NewCipher calls. (Obviously don't use this example key for anything
// real.) If you want to convert a passphrase to a key, use a suitable
// package like bcrypt or scrypt.
key, _ := hex.DecodeString("6368616e6765207468697320706173736368616e676520746869732070617373")
// iv should be generated randomly
iv, _ := hex.DecodeString("6368616e6765207468697320706173736368616e676520")
h, err := zuc.NewHash256(key, iv, 8)
if err != nil {
panic(err)
}
h.Write([]byte("hello world\n"))
fmt.Printf("%x", h.Sum(nil))
// Output: f28aea6c9db3dc69
}
func ExampleNewHash256_tagSize16() {
// Load your secret key from a safe place and reuse it across multiple
// NewCipher calls. (Obviously don't use this example key for anything
// real.) If you want to convert a passphrase to a key, use a suitable
// package like bcrypt or scrypt.
key, _ := hex.DecodeString("6368616e6765207468697320706173736368616e676520746869732070617373")
// iv should be generated randomly
iv, _ := hex.DecodeString("6368616e6765207468697320706173736368616e676520")
h, err := zuc.NewHash256(key, iv, 16)
if err != nil {
panic(err)
}
h.Write([]byte("hello world\n"))
fmt.Printf("%x", h.Sum(nil))
// Output: fd8d10ea65b6369cccc07d50b4657d84
}
```
要支持位为单位的话,可以调用```Finish```方法。
```go
func ExampleZUC128Mac_Finish() {
key := make([]byte, 16)
iv := make([]byte, 16)
h, err := zuc.NewHash(key, iv)
if err != nil {
panic(err)
}
fmt.Printf("%x", h.Finish([]byte{0}, 1))
// Output: c8a9595e
}
func ExampleZUC128Mac_Finish_mixed() {
key := []byte{
0xc9, 0xe6, 0xce, 0xc4, 0x60, 0x7c, 0x72, 0xdb,
0x00, 0x0a, 0xef, 0xa8, 0x83, 0x85, 0xab, 0x0a,
}
// iv should be generated randomly
iv, _ := hex.DecodeString("a94059da50000000294059da50008000")
h, err := zuc.NewHash(key, iv)
if err != nil {
panic(err)
}
in, _ := hex.DecodeString("983b41d47d780c9e1ad11d7eb70391b1de0b35da2dc62f83e7b78d6306ca0ea07e941b7be91348f9fcb170e2217fecd97f9f68adb16e5d7d21e569d280ed775cebde3f4093c53881")
h.Write(in)
fmt.Printf("%x", h.Finish([]byte{0}, 1))
// Output: fae8ff0b
}
```

View File

@ -1,294 +1,249 @@
// Package drbg implements Random Number Generation Using Deterministic Random Bit Generators.
package drbg
import (
"crypto/cipher"
"crypto/rand"
"errors"
"hash"
"io"
"time"
"github.com/emmansun/gmsm/sm3"
"github.com/emmansun/gmsm/sm4"
)
const (
reseedCounterIntervalLevelTest = uint64(8)
reseedCounterIntervalLevel2 = 1 << 10
reseedCounterIntervalLevel1 = 1 << 20
reseedTimeIntervalLevelTest = time.Duration(6) * time.Second
reseedTimeIntervalLevel2 = time.Duration(60) * time.Second
reseedTimeIntervalLevel1 = time.Duration(600) * time.Second
maxBytes = 1 << 27
maxBytesPerGenerate = 1 << 11
)
var ErrReseedRequired = errors.New("drbg: reseed reuqired")
type SecurityLevel byte
const (
SECURITY_LEVEL_ONE SecurityLevel = 0x01
SECURITY_LEVEL_TWO SecurityLevel = 0x02
SECURITY_LEVEL_TEST SecurityLevel = 0x99
)
// DrbgPrng sample pseudo random number generator base on DRBG
type DrbgPrng struct {
entropySource io.Reader
securityStrength int
impl DRBG
}
// NewCtrDrbgPrng create pseudo random number generator base on CTR DRBG
func NewCtrDrbgPrng(cipherProvider func(key []byte) (cipher.Block, error), keyLen int, entropySource io.Reader, securityStrength int, gm bool, securityLevel SecurityLevel, personalization []byte) (*DrbgPrng, error) {
prng := new(DrbgPrng)
if entropySource != nil {
prng.entropySource = entropySource
} else {
prng.entropySource = rand.Reader
}
prng.securityStrength = selectSecurityStrength(securityStrength)
if gm && securityStrength < 32 {
return nil, errors.New("drbg: invalid security strength")
}
// Get entropy input
entropyInput := make([]byte, prng.securityStrength)
err := prng.getEntropy(entropyInput)
if err != nil {
return nil, err
}
// Get nonce, reference to NIST SP 800-90A, 8.6.7
nonce := make([]byte, prng.securityStrength/2)
err = prng.getEntropy(nonce)
if err != nil {
return nil, err
}
// initial working state
prng.impl, err = NewCtrDrbg(cipherProvider, keyLen, securityLevel, gm, entropyInput, nonce, personalization)
if err != nil {
return nil, err
}
return prng, nil
}
// NewNistCtrDrbgPrng create pseudo random number generator base on CTR DRBG which follows NIST standard
func NewNistCtrDrbgPrng(cipherProvider func(key []byte) (cipher.Block, error), keyLen int, entropySource io.Reader, securityStrength int, securityLevel SecurityLevel, personalization []byte) (*DrbgPrng, error) {
return NewCtrDrbgPrng(cipherProvider, keyLen, entropySource, securityStrength, false, securityLevel, personalization)
}
// NewNistCtrDrbgPrng create pseudo random number generator base on CTR DRBG which follows GM/T 0105-2021 standard
func NewGmCtrDrbgPrng(entropySource io.Reader, securityStrength int, securityLevel SecurityLevel, personalization []byte) (*DrbgPrng, error) {
return NewCtrDrbgPrng(sm4.NewCipher, 16, entropySource, securityStrength, true, securityLevel, personalization)
}
// NewHashDrbgPrng create pseudo random number generator base on HASH DRBG
func NewHashDrbgPrng(newHash func() hash.Hash, entropySource io.Reader, securityStrength int, gm bool, securityLevel SecurityLevel, personalization []byte) (*DrbgPrng, error) {
prng := new(DrbgPrng)
if entropySource != nil {
prng.entropySource = entropySource
} else {
prng.entropySource = rand.Reader
}
prng.securityStrength = selectSecurityStrength(securityStrength)
if gm && securityStrength < 32 {
return nil, errors.New("drbg: invalid security strength")
}
// Get entropy input
entropyInput := make([]byte, prng.securityStrength)
err := prng.getEntropy(entropyInput)
if err != nil {
return nil, err
}
// Get nonce, reference to NIST SP 800-90A, 8.6.7
nonce := make([]byte, prng.securityStrength/2)
err = prng.getEntropy(nonce)
if err != nil {
return nil, err
}
// initial working state
prng.impl, err = NewHashDrbg(newHash, securityLevel, gm, entropyInput, nonce, personalization)
if err != nil {
return nil, err
}
return prng, nil
}
// NewNistHashDrbgPrng create pseudo random number generator base on hash DRBG which follows NIST standard
func NewNistHashDrbgPrng(newHash func() hash.Hash, entropySource io.Reader, securityStrength int, securityLevel SecurityLevel, personalization []byte) (*DrbgPrng, error) {
return NewHashDrbgPrng(newHash, entropySource, securityStrength, false, securityLevel, personalization)
}
// NewGmHashDrbgPrng create pseudo random number generator base on hash DRBG which follows GM/T 0105-2021 standard
func NewGmHashDrbgPrng(entropySource io.Reader, securityStrength int, securityLevel SecurityLevel, personalization []byte) (*DrbgPrng, error) {
return NewHashDrbgPrng(sm3.New, entropySource, securityStrength, true, securityLevel, personalization)
}
// NewHmacDrbgPrng create pseudo random number generator base on hash mac DRBG
func NewHmacDrbgPrng(newHash func() hash.Hash, entropySource io.Reader, securityStrength int, gm bool, securityLevel SecurityLevel, personalization []byte) (*DrbgPrng, error) {
prng := new(DrbgPrng)
if entropySource != nil {
prng.entropySource = entropySource
} else {
prng.entropySource = rand.Reader
}
prng.securityStrength = selectSecurityStrength(securityStrength)
// Get entropy input
entropyInput := make([]byte, prng.securityStrength)
err := prng.getEntropy(entropyInput)
if err != nil {
return nil, err
}
// Get nonce, reference to NIST SP 800-90A, 8.6.7
nonce := make([]byte, prng.securityStrength/2)
err = prng.getEntropy(nonce)
if err != nil {
return nil, err
}
// initial working state
prng.impl, err = NewHmacDrbg(newHash, securityLevel, gm, entropyInput, nonce, personalization)
if err != nil {
return nil, err
}
return prng, nil
}
// NewNistHmacDrbgPrng create pseudo random number generator base on hash mac DRBG which follows NIST standard
func NewNistHmacDrbgPrng(newHash func() hash.Hash, entropySource io.Reader, securityStrength int, securityLevel SecurityLevel, personalization []byte) (*DrbgPrng, error) {
return NewHmacDrbgPrng(newHash, entropySource, securityStrength, false, securityLevel, personalization)
}
func (prng *DrbgPrng) getEntropy(entropyInput []byte) error {
n, err := prng.entropySource.Read(entropyInput)
if err != nil {
return err
}
if n != len(entropyInput) {
return errors.New("drbg: fail to read enough entropy input")
}
return nil
}
func (prng *DrbgPrng) Read(data []byte) (int, error) {
maxBytesPerRequest := prng.impl.MaxBytesPerRequest()
total := 0
for len(data) > 0 {
b := data
if len(data) > maxBytesPerRequest {
b = data[:maxBytesPerRequest]
}
err := prng.impl.Generate(b, nil)
if err == ErrReseedRequired {
entropyInput := make([]byte, prng.securityStrength)
err := prng.getEntropy(entropyInput)
if err != nil {
return 0, err
}
err = prng.impl.Reseed(entropyInput, nil)
if err != nil {
return 0, err
}
} else if err != nil {
return 0, err
} else {
total += len(b)
data = data[len(b):]
}
}
return total, nil
}
// DRBG interface for both hash and ctr drbg implementations
type DRBG interface {
// check internal state, return if reseed required
NeedReseed() bool
// reseed process
Reseed(entropy, additional []byte) error
// generate requrested bytes to b
Generate(b, additional []byte) error
// MaxBytesPerRequest return max bytes per request
MaxBytesPerRequest() int
}
type BaseDrbg struct {
v []byte
seedLength int
reseedTime time.Time
reseedIntervalInTime time.Duration
reseedCounter uint64
reseedIntervalInCounter uint64
securityLevel SecurityLevel
gm bool
}
func (hd *BaseDrbg) NeedReseed() bool {
return (hd.reseedCounter > hd.reseedIntervalInCounter) || (hd.gm && time.Since(hd.reseedTime) > hd.reseedIntervalInTime)
}
func (hd *BaseDrbg) setSecurityLevel(securityLevel SecurityLevel) {
hd.securityLevel = securityLevel
switch securityLevel {
case SECURITY_LEVEL_TWO:
hd.reseedIntervalInCounter = reseedCounterIntervalLevel2
hd.reseedIntervalInTime = reseedTimeIntervalLevel2
case SECURITY_LEVEL_TEST:
hd.reseedIntervalInCounter = reseedCounterIntervalLevelTest
hd.reseedIntervalInTime = reseedTimeIntervalLevelTest
default:
hd.reseedIntervalInCounter = reseedCounterIntervalLevel1
hd.reseedIntervalInTime = reseedTimeIntervalLevel1
}
}
// Set security_strength to the lowest security strength greater than or equal to
// requested_instantiation_security_strength from the set {112, 128, 192, 256}.
func selectSecurityStrength(requested int) int {
switch {
case requested <= 14:
return 14
case requested <= 16:
return 16
case requested <= 24:
return 24
case requested <= 32:
return 32
default:
return requested
}
}
func add(left, right []byte, len int) {
var temp uint16 = 0
for i := len - 1; i >= 0; i-- {
temp += uint16(left[i]) + uint16(right[i])
right[i] = byte(temp & 0xff)
temp >>= 8
}
}
func addOne(data []byte, len int) {
var temp uint16 = 1
for i := len - 1; i >= 0; i-- {
temp += uint16(data[i])
data[i] = byte(temp & 0xff)
temp >>= 8
}
}
// Package drbg implements Random Number Generation Using Deterministic Random Bit Generators.
package drbg
import (
"crypto/cipher"
"crypto/rand"
"errors"
"hash"
"io"
"time"
"github.com/emmansun/gmsm/sm3"
"github.com/emmansun/gmsm/sm4"
)
const DRBG_RESEED_COUNTER_INTERVAL_LEVEL_TEST uint64 = 8
const DRBG_RESEED_COUNTER_INTERVAL_LEVEL2 uint64 = 1 << 10
const DRBG_RESEED_COUNTER_INTERVAL_LEVEL1 uint64 = 1 << 20
const DRBG_RESEED_TIME_INTERVAL_LEVEL_TEST = time.Duration(6) * time.Second
const DRBG_RESEED_TIME_INTERVAL_LEVEL2 = time.Duration(60) * time.Second
const DRBG_RESEED_TIME_INTERVAL_LEVEL1 = time.Duration(600) * time.Second
const MAX_BYTES = 1 << 27
const MAX_BYTES_PER_GENERATE = 1 << 11
var ErrReseedRequired = errors.New("reseed reuqired")
type SecurityLevel byte
const (
SECURITY_LEVEL_ONE SecurityLevel = 0x01
SECURITY_LEVEL_TWO SecurityLevel = 0x02
SECURITY_LEVEL_TEST SecurityLevel = 0x99
)
// DrbgPrng sample pseudo random number generator base on DRBG
type DrbgPrng struct {
entropySource io.Reader
securityStrength int
impl DRBG
}
// NewCtrDrbgPrng create pseudo random number generator base on CTR DRBG
func NewCtrDrbgPrng(cipherProvider func(key []byte) (cipher.Block, error), keyLen int, entropySource io.Reader, securityStrength int, gm bool, securityLevel SecurityLevel, personalization []byte) (*DrbgPrng, error) {
prng := new(DrbgPrng)
if entropySource != nil {
prng.entropySource = entropySource
} else {
prng.entropySource = rand.Reader
}
prng.securityStrength = selectSecurityStrength(securityStrength)
if gm && securityStrength < 32 {
return nil, errors.New("invalid security strength")
}
// Get entropy input
entropyInput := make([]byte, prng.securityStrength)
err := prng.getEntropy(entropyInput)
if err != nil {
return nil, err
}
// Get nonce
nonce := make([]byte, prng.securityStrength/2)
err = prng.getEntropy(nonce)
if err != nil {
return nil, err
}
prng.impl, err = NewCtrDrbg(cipherProvider, keyLen, securityLevel, gm, entropyInput, nonce, personalization)
if err != nil {
return nil, err
}
return prng, nil
}
// NewNistCtrDrbgPrng create pseudo random number generator base on CTR DRBG which follows NIST standard
func NewNistCtrDrbgPrng(cipherProvider func(key []byte) (cipher.Block, error), keyLen int, entropySource io.Reader, securityStrength int, securityLevel SecurityLevel, personalization []byte) (*DrbgPrng, error) {
return NewCtrDrbgPrng(cipherProvider, keyLen, entropySource, securityStrength, false, securityLevel, personalization)
}
// NewNistCtrDrbgPrng create pseudo random number generator base on CTR DRBG which follows GM/T 0105-2021 standard
func NewGmCtrDrbgPrng(entropySource io.Reader, securityStrength int, securityLevel SecurityLevel, personalization []byte) (*DrbgPrng, error) {
return NewCtrDrbgPrng(sm4.NewCipher, 16, entropySource, securityStrength, true, securityLevel, personalization)
}
// NewHashDrbgPrng create pseudo random number generator base on HASH DRBG
func NewHashDrbgPrng(md hash.Hash, entropySource io.Reader, securityStrength int, gm bool, securityLevel SecurityLevel, personalization []byte) (*DrbgPrng, error) {
prng := new(DrbgPrng)
if entropySource != nil {
prng.entropySource = entropySource
} else {
prng.entropySource = rand.Reader
}
prng.securityStrength = selectSecurityStrength(securityStrength)
if gm && securityStrength < 32 {
return nil, errors.New("invalid security strength")
}
// Get entropy input
entropyInput := make([]byte, prng.securityStrength)
err := prng.getEntropy(entropyInput)
if err != nil {
return nil, err
}
// Get nonce from entropy source here
nonce := make([]byte, prng.securityStrength/2)
err = prng.getEntropy(nonce)
if err != nil {
return nil, err
}
prng.impl, err = NewHashDrbg(md, securityLevel, gm, entropyInput, nonce, personalization)
if err != nil {
return nil, err
}
return prng, nil
}
// NewNistHashDrbgPrng create pseudo random number generator base on hash DRBG which follows NIST standard
func NewNistHashDrbgPrng(md hash.Hash, entropySource io.Reader, securityStrength int, securityLevel SecurityLevel, personalization []byte) (*DrbgPrng, error) {
return NewHashDrbgPrng(md, entropySource, securityStrength, false, securityLevel, personalization)
}
// NewGmHashDrbgPrng create pseudo random number generator base on hash DRBG which follows GM/T 0105-2021 standard
func NewGmHashDrbgPrng(entropySource io.Reader, securityStrength int, securityLevel SecurityLevel, personalization []byte) (*DrbgPrng, error) {
return NewHashDrbgPrng(sm3.New(), entropySource, securityStrength, true, securityLevel, personalization)
}
func (prng *DrbgPrng) getEntropy(entropyInput []byte) error {
n, err := prng.entropySource.Read(entropyInput)
if err != nil {
return err
}
if n != len(entropyInput) {
return errors.New("fail to read enough entropy input")
}
return nil
}
func (prng *DrbgPrng) Read(data []byte) (int, error) {
maxBytesPerRequest := prng.impl.MaxBytesPerRequest()
total := 0
for len(data) > 0 {
b := data
if len(data) > maxBytesPerRequest {
b = data[:maxBytesPerRequest]
}
err := prng.impl.Generate(b, nil)
if err == ErrReseedRequired {
entropyInput := make([]byte, prng.securityStrength)
err := prng.getEntropy(entropyInput)
if err != nil {
return 0, err
}
err = prng.impl.Reseed(entropyInput, nil)
if err != nil {
return 0, err
}
} else if err != nil {
return 0, err
}
total += len(b)
data = data[len(b):]
}
return total, nil
}
// DRBG interface for both hash and ctr drbg implementations
type DRBG interface {
// check internal state, return if reseed required
NeedReseed() bool
// reseed process
Reseed(entropy, additional []byte) error
// generate requrested bytes to b
Generate(b, additional []byte) error
// MaxBytesPerRequest return max bytes per request
MaxBytesPerRequest() int
}
type BaseDrbg struct {
v []byte
seedLength int
reseedTime time.Time
reseedIntervalInTime time.Duration
reseedCounter uint64
reseedIntervalInCounter uint64
securityLevel SecurityLevel
gm bool
}
func (hd *BaseDrbg) NeedReseed() bool {
return (hd.reseedCounter > hd.reseedIntervalInCounter) || (hd.gm && time.Since(hd.reseedTime) > hd.reseedIntervalInTime)
}
func (hd *BaseDrbg) setSecurityLevel(securityLevel SecurityLevel) {
hd.securityLevel = securityLevel
switch securityLevel {
case SECURITY_LEVEL_TWO:
hd.reseedIntervalInCounter = DRBG_RESEED_COUNTER_INTERVAL_LEVEL2
hd.reseedIntervalInTime = DRBG_RESEED_TIME_INTERVAL_LEVEL2
case SECURITY_LEVEL_TEST:
hd.reseedIntervalInCounter = DRBG_RESEED_COUNTER_INTERVAL_LEVEL_TEST
hd.reseedIntervalInTime = DRBG_RESEED_TIME_INTERVAL_LEVEL_TEST
default:
hd.reseedIntervalInCounter = DRBG_RESEED_COUNTER_INTERVAL_LEVEL1
hd.reseedIntervalInTime = DRBG_RESEED_TIME_INTERVAL_LEVEL1
}
}
func selectSecurityStrength(requested int) int {
switch {
case requested <= 14:
return 14
case requested <= 16:
return 16
case requested <= 24:
return 24
case requested <= 32:
return 32
default:
return requested
}
}
func add(left, right []byte, len int) {
var temp uint16 = 0
for i := len - 1; i >= 0; i-- {
temp += uint16(left[i]) + uint16(right[i])
right[i] = byte(temp & 0xff)
temp >>= 8
}
}
func addOne(data []byte, len int) {
var temp uint16 = 1
for i := len - 1; i >= 0; i-- {
temp += uint16(data[i])
data[i] = byte(temp & 0xff)
temp >>= 8
}
}

View File

@ -1,123 +1,82 @@
package drbg
import (
"bytes"
"crypto/aes"
"crypto/sha256"
"testing"
)
func TestGmCtrDrbgPrng(t *testing.T) {
prng, err := NewGmCtrDrbgPrng(nil, 32, SECURITY_LEVEL_TEST, nil)
if err != nil {
t.Fatal(err)
}
data := make([]byte, 33)
for i := 0; i < int(reseedCounterIntervalLevelTest+1); i++ {
n, err := prng.Read(data)
if err != nil {
t.Fatal(err)
}
if n != 33 {
t.Errorf("not got enough random bytes")
}
}
}
func TestGmCtrDrbgPrngReseedCase(t *testing.T) {
prng, err := NewGmCtrDrbgPrng(nil, 32, SECURITY_LEVEL_TEST, nil)
if err != nil {
t.Fatal(err)
}
data := make([]byte, 64)
for i := 0; i < int(reseedCounterIntervalLevelTest+1); i++ {
for j := 0; j < 64; j++ {
data[j] = 0
}
n, err := prng.Read(data)
if err != nil {
t.Fatal(err)
}
if n != 64 {
t.Errorf("not got enough random bytes")
}
if bytes.Contains(data, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) {
t.Fatal("failed, it's a bug")
}
}
}
func TestNistCtrDrbgPrng(t *testing.T) {
prng, err := NewNistCtrDrbgPrng(aes.NewCipher, 16, nil, 16, SECURITY_LEVEL_TEST, nil)
if err != nil {
t.Fatal(err)
}
data := make([]byte, maxBytesPerGenerate+1)
n, err := prng.Read(data)
if err != nil {
t.Fatal(err)
}
if n != maxBytesPerGenerate+1 {
t.Errorf("not got enough random bytes")
}
}
func TestGmHashDrbgPrng(t *testing.T) {
prng, err := NewGmHashDrbgPrng(nil, 32, SECURITY_LEVEL_TEST, nil)
if err != nil {
t.Fatal(err)
}
data := make([]byte, 33)
for i := 0; i < int(reseedCounterIntervalLevelTest+1); i++ {
n, err := prng.Read(data)
if err != nil {
t.Fatal(err)
}
if n != 33 {
t.Errorf("not got enough random bytes")
}
}
}
func TestNistHashDrbgPrng(t *testing.T) {
prng, err := NewNistHashDrbgPrng(sha256.New, nil, 32, SECURITY_LEVEL_TEST, nil)
if err != nil {
t.Fatal(err)
}
data := make([]byte, maxBytesPerGenerate+1)
n, err := prng.Read(data)
if err != nil {
t.Fatal(err)
}
if n != maxBytesPerGenerate+1 {
t.Errorf("not got enough random bytes")
}
}
func TestNistHmacDrbgPrng(t *testing.T) {
prng, err := NewNistHmacDrbgPrng(sha256.New, nil, 32, SECURITY_LEVEL_TEST, nil)
if err != nil {
t.Fatal(err)
}
data := make([]byte, maxBytesPerGenerate+1)
n, err := prng.Read(data)
if err != nil {
t.Fatal(err)
}
if n != maxBytesPerGenerate+1 {
t.Errorf("not got enough random bytes")
}
}
func TestGMSecurityStrengthValidation(t *testing.T) {
_, err := NewGmHashDrbgPrng(nil, 24, SECURITY_LEVEL_TEST, nil)
if err == nil {
t.Fatalf("expected error here")
}
_, err = NewGmCtrDrbgPrng(nil, 24, SECURITY_LEVEL_TEST, nil)
if err == nil {
t.Fatalf("expected error here")
}
}
package drbg
import (
"crypto/aes"
"crypto/sha256"
"testing"
)
func TestGmCtrDrbgPrng(t *testing.T) {
prng, err := NewGmCtrDrbgPrng(nil, 32, SECURITY_LEVEL_TEST, nil)
if err != nil {
t.Fatal(err)
}
data := make([]byte, 33)
for i := 0; i < int(DRBG_RESEED_COUNTER_INTERVAL_LEVEL_TEST+1); i++ {
n, err := prng.Read(data)
if err != nil {
t.Fatal(err)
}
if n != 33 {
t.Errorf("not got enough random bytes")
}
}
}
func TestNistCtrDrbgPrng(t *testing.T) {
prng, err := NewNistCtrDrbgPrng(aes.NewCipher, 16, nil, 16, SECURITY_LEVEL_TEST, nil)
if err != nil {
t.Fatal(err)
}
data := make([]byte, MAX_BYTES_PER_GENERATE+1)
n, err := prng.Read(data)
if err != nil {
t.Fatal(err)
}
if n != MAX_BYTES_PER_GENERATE+1 {
t.Errorf("not got enough random bytes")
}
}
func TestGmHashDrbgPrng(t *testing.T) {
prng, err := NewGmHashDrbgPrng(nil, 32, SECURITY_LEVEL_TEST, nil)
if err != nil {
t.Fatal(err)
}
data := make([]byte, 33)
for i := 0; i < int(DRBG_RESEED_COUNTER_INTERVAL_LEVEL_TEST+1); i++ {
n, err := prng.Read(data)
if err != nil {
t.Fatal(err)
}
if n != 33 {
t.Errorf("not got enough random bytes")
}
}
}
func TestNistHashDrbgPrng(t *testing.T) {
prng, err := NewNistHashDrbgPrng(sha256.New(), nil, 32, SECURITY_LEVEL_TEST, nil)
if err != nil {
t.Fatal(err)
}
data := make([]byte, MAX_BYTES_PER_GENERATE+1)
n, err := prng.Read(data)
if err != nil {
t.Fatal(err)
}
if n != MAX_BYTES_PER_GENERATE+1 {
t.Errorf("not got enough random bytes")
}
}
func TestGMSecurityStrengthValidation(t *testing.T) {
_, err := NewGmHashDrbgPrng(nil, 24, SECURITY_LEVEL_TEST, nil)
if err == nil {
t.Fatalf("expected error here")
}
_, err = NewGmCtrDrbgPrng(nil, 24, SECURITY_LEVEL_TEST, nil)
if err == nil {
t.Fatalf("expected error here")
}
}

View File

@ -1,224 +1,223 @@
package drbg
import (
"crypto/cipher"
"crypto/subtle"
"errors"
"time"
"github.com/emmansun/gmsm/internal/byteorder"
"github.com/emmansun/gmsm/sm4"
)
// CtrDrbg CTR DRBG structure, its instance is NOT goroutine safe!!!
type CtrDrbg struct {
BaseDrbg
cipherProvider func(key []byte) (cipher.Block, error)
key []byte
keyLen int
}
// NewCtrDrbg create one CTR DRBG instance
func NewCtrDrbg(cipherProvider func(key []byte) (cipher.Block, error), keyLen int, securityLevel SecurityLevel, gm bool, entropy, nonce, personalization []byte) (*CtrDrbg, error) {
hd := &CtrDrbg{}
hd.gm = gm
hd.setSecurityLevel(securityLevel)
// here for the min length, we just check <=0 now
if len(entropy) == 0 || (hd.gm && len(entropy) < 32) || len(entropy) >= maxBytes {
return nil, errors.New("drbg: invalid entropy length")
}
// here for the min length, we just check <=0 now
if len(nonce) == 0 || (hd.gm && len(nonce) < 16) || len(nonce) >= maxBytes>>1 {
return nil, errors.New("drbg: invalid nonce length")
}
if len(personalization) >= maxBytes {
return nil, errors.New("drbg: personalization is too long")
}
hd.cipherProvider = cipherProvider
hd.keyLen = keyLen
temp := make([]byte, hd.keyLen)
block, err := cipherProvider(temp)
if err != nil {
return nil, err
}
hd.seedLength = block.BlockSize() + keyLen
hd.v = make([]byte, block.BlockSize())
hd.key = make([]byte, hd.keyLen)
// seed_material = entropy_input || instantiation_nonce || personalization_string
seedMaterial := make([]byte, len(entropy)+len(nonce)+len(personalization))
copy(seedMaterial, entropy)
copy(seedMaterial[len(entropy):], nonce)
copy(seedMaterial[len(entropy)+len(nonce):], personalization)
// seed_material = Block_Cipher_df(seed_material, seed_length)
seedMaterial = hd.derive(seedMaterial, hd.seedLength)
// CTR_DRBG_Updae(seed_material, Key, V)
hd.update(seedMaterial)
hd.reseedCounter = 1
hd.reseedTime = time.Now()
return hd, nil
}
// NewNISTCtrDrbg create one CTR DRBG implementation which follows NIST standard
func NewNISTCtrDrbg(cipherProvider func(key []byte) (cipher.Block, error), keyLen int, securityLevel SecurityLevel, entropy, nonce, personalization []byte) (*CtrDrbg, error) {
return NewCtrDrbg(cipherProvider, keyLen, securityLevel, false, entropy, nonce, personalization)
}
// NewGMCtrDrbg create one CTR DRBG implementation which follows GM/T 0105-2021 standard
func NewGMCtrDrbg(securityLevel SecurityLevel, entropy, nonce, personalization []byte) (*CtrDrbg, error) {
return NewCtrDrbg(sm4.NewCipher, 16, securityLevel, true, entropy, nonce, personalization)
}
func (cd *CtrDrbg) Reseed(entropy, additional []byte) error {
// here for the min length, we just check <=0 now
if len(entropy) == 0 || (cd.gm && len(entropy) < 32) || len(entropy) >= maxBytes {
return errors.New("drbg: invalid entropy length")
}
if len(additional) >= maxBytes {
return errors.New("drbg: additional input too long")
}
// seed_material = entropy_input || additional_input
var seedMaterial []byte
if len(additional) == 0 {
seedMaterial = entropy
} else {
seedMaterial = make([]byte, len(entropy)+len(additional))
copy(seedMaterial, entropy)
copy(seedMaterial[len(entropy):], additional)
}
// seed_material = Block_Cipher_df(seed_material, seed_length)
seedMaterial = cd.derive(seedMaterial, cd.seedLength)
// CTR_DRBG_Updae(seed_material, Key, V)
cd.update(seedMaterial)
cd.reseedCounter = 1
cd.reseedTime = time.Now()
return nil
}
func (cd *CtrDrbg) newBlockCipher(key []byte) cipher.Block {
block, err := cd.cipherProvider(key)
if err != nil {
panic(err)
}
return block
}
func (cd *CtrDrbg) MaxBytesPerRequest() int {
if cd.gm {
return len(cd.v)
}
return maxBytesPerGenerate
}
// Generate CTR DRBG pseudorandom bits generate process.
func (cd *CtrDrbg) Generate(out, additional []byte) error {
if cd.NeedReseed() {
return ErrReseedRequired
}
outlen := len(cd.v)
if (cd.gm && len(out) > outlen) || (!cd.gm && len(out) > maxBytesPerGenerate) {
return errors.New("drbg: too many bytes requested")
}
// If len(additional_input) > 0, then
// additional_input = Block_Cipher_df(additional_input, seed_length)
// CTR_DRBG_Update(additional_input, Key, V)
if len(additional) > 0 {
additional = cd.derive(additional, cd.seedLength)
cd.update(additional)
}
block := cd.newBlockCipher(cd.key)
temp := make([]byte, outlen)
m := len(out)
limit := uint64(m+outlen-1) / uint64(outlen)
for i := range int(limit) {
// V = (V + 1) mod 2^outlen)
addOne(cd.v, outlen)
// output_block = Encrypt(Key, V)
block.Encrypt(temp, cd.v)
copy(out[i*outlen:], temp)
}
cd.update(additional)
cd.reseedCounter++
return nil
}
func (cd *CtrDrbg) update(seedMaterial []byte) {
temp := make([]byte, cd.seedLength)
block := cd.newBlockCipher(cd.key)
outlen := block.BlockSize()
v := make([]byte, outlen)
output := make([]byte, outlen)
copy(v, cd.v)
for i := range (cd.seedLength+outlen-1)/outlen {
// V = (V + 1) mod 2^outlen
addOne(v, outlen)
// output_block = Encrypt(Key, V)
block.Encrypt(output, v)
copy(temp[i*outlen:], output)
}
// temp = temp XOR seed_material
subtle.XORBytes(temp, temp, seedMaterial)
// Key = leftmost(temp, key_length)
copy(cd.key, temp)
// V = rightmost(temp, outlen)
copy(cd.v, temp[cd.keyLen:])
}
// derive Block_Cipher_df
func (cd *CtrDrbg) derive(seedMaterial []byte, returnBytes int) []byte {
outlen := cd.seedLength - cd.keyLen
lenS := ((4 + 4 + len(seedMaterial) + outlen) / outlen) * outlen
S := make([]byte, lenS+outlen)
// S = counter || len(seed_material) || len(return_bytes) || seed_material || 0x80
// len(S) = ((outlen + 4 + 4 + len(seed_material) + 1 + outlen - 1) / outlen) * outlen
byteorder.BEPutUint32(S[outlen:], uint32(len(seedMaterial)))
byteorder.BEPutUint32(S[outlen+4:], uint32(returnBytes))
copy(S[outlen+8:], seedMaterial)
S[outlen+8+len(seedMaterial)] = 0x80
key := make([]byte, cd.keyLen)
for i := range cd.keyLen {
key[i] = byte(i)
}
blocks := (cd.seedLength + outlen - 1) / outlen
temp := make([]byte, blocks*outlen)
block := cd.newBlockCipher(key)
for i := 0; i < blocks; i++ {
byteorder.BEPutUint32(S, uint32(i))
copy(temp[i*outlen:], cd.bcc(block, S))
}
key = temp[:cd.keyLen]
X := temp[cd.keyLen:cd.seedLength]
temp = make([]byte, returnBytes)
block = cd.newBlockCipher(key)
for i := 0; i < (returnBytes+outlen-1)/outlen; i++ {
block.Encrypt(X, X)
copy(temp[i*outlen:], X)
}
return temp
}
func (cd *CtrDrbg) bcc(block cipher.Block, data []byte) []byte {
chainingValue := make([]byte, block.BlockSize())
for i := 0; i < len(data)/block.BlockSize(); i++ {
subtle.XORBytes(chainingValue, chainingValue, data[i*block.BlockSize():])
block.Encrypt(chainingValue, chainingValue)
}
return chainingValue
}
package drbg
import (
"crypto/cipher"
"encoding/binary"
"errors"
"time"
"github.com/emmansun/gmsm/internal/subtle"
"github.com/emmansun/gmsm/sm4"
)
type CtrDrbg struct {
BaseDrbg
cipherProvider func(key []byte) (cipher.Block, error)
key []byte
keyLen int
}
// NewCtrDrbg create one CTR DRBG instance
func NewCtrDrbg(cipherProvider func(key []byte) (cipher.Block, error), keyLen int, securityLevel SecurityLevel, gm bool, entropy, nonce, personalization []byte) (*CtrDrbg, error) {
hd := &CtrDrbg{}
hd.gm = gm
hd.setSecurityLevel(securityLevel)
// here for the min length, we just check <=0 now
if len(entropy) == 0 || (hd.gm && len(entropy) < 32) || len(entropy) >= MAX_BYTES {
return nil, errors.New("invalid entropy length")
}
// here for the min length, we just check <=0 now
if len(nonce) == 0 || (hd.gm && len(nonce) < 16) || len(nonce) >= MAX_BYTES>>1 {
return nil, errors.New("invalid nonce length")
}
if len(personalization) >= MAX_BYTES {
return nil, errors.New("personalization is too long")
}
hd.cipherProvider = cipherProvider
hd.keyLen = keyLen
temp := make([]byte, hd.keyLen)
block, err := cipherProvider(temp)
if err != nil {
return nil, err
}
hd.seedLength = block.BlockSize() + keyLen
hd.v = make([]byte, block.BlockSize())
hd.key = make([]byte, hd.keyLen)
// seed_material = entropy_input || instantiation_nonce || personalization_string
seedMaterial := make([]byte, len(entropy)+len(nonce)+len(personalization))
copy(seedMaterial, entropy)
copy(seedMaterial[len(entropy):], nonce)
copy(seedMaterial[len(entropy)+len(nonce):], personalization)
// seed_material = Block_Cipher_df(seed_material, seed_length)
seedMaterial = hd.derive(seedMaterial, hd.seedLength)
// CTR_DRBG_Updae(seed_material, Key, V)
hd.update(seedMaterial)
hd.reseedCounter = 1
hd.reseedTime = time.Now()
return hd, nil
}
// NewNISTCtrDrbg create one CTR DRBG implementation which follows NIST standard
func NewNISTCtrDrbg(cipherProvider func(key []byte) (cipher.Block, error), keyLen int, securityLevel SecurityLevel, entropy, nonce, personalization []byte) (*CtrDrbg, error) {
return NewCtrDrbg(cipherProvider, keyLen, securityLevel, false, entropy, nonce, personalization)
}
// NewGMCtrDrbg create one CTR DRBG implementation which follows GM/T 0105-2021 standard
func NewGMCtrDrbg(securityLevel SecurityLevel, entropy, nonce, personalization []byte) (*CtrDrbg, error) {
return NewCtrDrbg(sm4.NewCipher, 16, securityLevel, true, entropy, nonce, personalization)
}
func (hd *CtrDrbg) Reseed(entropy, additional []byte) error {
// here for the min length, we just check <=0 now
if len(entropy) <= 0 || (hd.gm && len(entropy) < 32) || len(entropy) >= MAX_BYTES {
return errors.New("invalid entropy length")
}
if len(additional) >= MAX_BYTES {
return errors.New("additional input too long")
}
// seed_material = entropy_input || additional_input
var seedMaterial []byte
if len(additional) == 0 {
seedMaterial = entropy
} else {
seedMaterial = make([]byte, len(entropy)+len(additional))
copy(seedMaterial, entropy)
copy(seedMaterial[len(entropy):], additional)
}
// seed_material = Block_Cipher_df(seed_material, seed_length)
seedMaterial = hd.derive(seedMaterial, hd.seedLength)
// CTR_DRBG_Updae(seed_material, Key, V)
hd.update(seedMaterial)
hd.reseedCounter = 1
hd.reseedTime = time.Now()
return nil
}
func (hd *CtrDrbg) newBlockCipher(key []byte) cipher.Block {
block, err := hd.cipherProvider(key)
if err != nil {
panic(err)
}
return block
}
func (hd *CtrDrbg) MaxBytesPerRequest() int {
if hd.gm {
return len(hd.v)
}
return MAX_BYTES_PER_GENERATE
}
// Generate CTR DRBG pseudorandom bits generate process.
func (hd *CtrDrbg) Generate(b, additional []byte) error {
if hd.NeedReseed() {
return ErrReseedRequired
}
outlen := len(hd.v)
if (hd.gm && len(b) > outlen) || (!hd.gm && len(b) > MAX_BYTES_PER_GENERATE) {
return errors.New("too many bytes requested")
}
// If len(additional_input) > 0, then
// additional_input = Block_Cipher_df(additional_input, seed_length)
// CTR_DRBG_Update(additional_input, Key, V)
if len(additional) > 0 {
additional = hd.derive(additional, hd.seedLength)
hd.update(additional)
}
block := hd.newBlockCipher(hd.key)
temp := make([]byte, outlen)
m := len(b)
limit := uint64(m+outlen-1) / uint64(outlen)
for i := 0; i < int(limit); i++ {
// V = (V + 1) mod 2^outlen)
addOne(hd.v, outlen)
// output_block = Encrypt(Key, V)
block.Encrypt(temp, hd.v)
copy(b[i*outlen:], temp)
}
hd.update(additional)
hd.reseedCounter++
return nil
}
func (cd *CtrDrbg) update(seedMaterial []byte) {
temp := make([]byte, cd.seedLength)
block := cd.newBlockCipher(cd.key)
outlen := block.BlockSize()
v := make([]byte, outlen)
output := make([]byte, outlen)
copy(v, cd.v)
for i := 0; i < (cd.seedLength+outlen-1)/outlen; i++ {
// V = (V + 1) mod 2^outlen
addOne(v, outlen)
// output_block = Encrypt(Key, V)
block.Encrypt(output, v)
copy(temp[i*outlen:], output)
}
// temp = temp XOR seed_material
subtle.XORBytes(temp, temp, seedMaterial)
// Key = leftmost(temp, key_length)
copy(cd.key, temp)
// V = rightmost(temp, outlen)
copy(cd.v, temp[cd.keyLen:])
}
// derive Block_Cipher_df
func (cd *CtrDrbg) derive(seedMaterial []byte, returnBytes int) []byte {
outlen := cd.seedLength - cd.keyLen
lenS := ((4 + 4 + len(seedMaterial) + outlen) / outlen) * outlen
S := make([]byte, lenS+outlen)
// S = counter || len(seed_material) || len(return_bytes) || seed_material || 0x80
// len(S) = ((outlen + 4 + 4 + len(seed_material) + 1 + outlen - 1) / outlen) * outlen
binary.BigEndian.PutUint32(S[outlen:], uint32(len(seedMaterial)))
binary.BigEndian.PutUint32(S[outlen+4:], uint32(returnBytes))
copy(S[outlen+8:], seedMaterial)
S[outlen+8+len(seedMaterial)] = 0x80
key := make([]byte, cd.keyLen)
for i := 0; i < cd.keyLen; i++ {
key[i] = byte(i)
}
blocks := (cd.seedLength + outlen - 1) / outlen
temp := make([]byte, blocks*outlen)
block := cd.newBlockCipher(key)
for i := 0; i < blocks; i++ {
binary.BigEndian.PutUint32(S, uint32(i))
copy(temp[i*outlen:], cd.bcc(block, S))
}
key = temp[:cd.keyLen]
X := temp[cd.keyLen:cd.seedLength]
temp = make([]byte, returnBytes)
block = cd.newBlockCipher(key)
for i := 0; i < (returnBytes+outlen-1)/outlen; i++ {
block.Encrypt(X, X)
copy(temp[i*outlen:], X)
}
return temp
}
func (cd *CtrDrbg) bcc(block cipher.Block, data []byte) []byte {
chainingValue := make([]byte, block.BlockSize())
for i := 0; i < len(data)/block.BlockSize(); i++ {
subtle.XORBytes(chainingValue, chainingValue, data[i*block.BlockSize():])
block.Encrypt(chainingValue, chainingValue)
}
return chainingValue
}

View File

@ -1,305 +1,305 @@
package drbg
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/hex"
"testing"
"github.com/emmansun/gmsm/sm4"
)
var ctrtests = []struct {
gm bool
cipherProvider func(key []byte) (cipher.Block, error)
keyLen int
entropyInput string
nonce string
personalizationString string
v0 string
key0 string
entropyInputReseed string
additionalInputReseed string
v1 string
key1 string
additionalInput1 string
v2 string
key2 string
additionalInput2 string
returnbits1 string
v3 string
key3 string
}{
{ // AES-128, without additional input
false,
aes.NewCipher,
16,
"0f65da13dca407999d4773c2b4a11d85",
"5209e5b4ed82a234",
"",
"80941680713df715056fb2a3d2e998b2",
"0c42ea6804303954deb197a07e6dbdd2",
"1dea0a12c52bf64339dd291c80d8ca89",
"",
"f2bacbb233252fba35fb0582f9286179", // v1
"32fbfd0109f364ed21ef21a6e5c763e7", //key1
"",
"99003d630bba500fe17c37f8c7331bf6", // v2
"757c8eb766f9aaa4650d6500b58624a3", //key2
"",
"2859cc468a76b08661ffd23b28547ffd0997ad526a0f51261b99ed3a37bd407bf418dbe6c6c3e26ed0ddefcb7474d899bd99f3655427519fc5b4057bcaf306d4",
"5907ab447a88e5106753507cc97e0fd5",
"e421ff2445e04992faf36cf9a5eaf1f9",
},
{ // AES-128, without additional input
false,
aes.NewCipher,
16,
"c9b8d7eb0afa5889e7f9b78a50ed453c",
"3058ba347ecd11b1",
"",
"b4e0180e3af0d99592249db33a29cc4e",
"1621bebef7e9215078459ecc74baffbc",
"643686b86266d9111f29eb389e1184b4",
"",
"7574911eeb85d56d385f0c8c99965c4a", // v1
"a4c515266cb5986825a503b39d5f398c", //key1
"",
"abca3ea049e405d3826f43e54e08c8f7", // v2
"edcdf23f60d3988a4d235798aa0d33a2", //key2
"",
"0a8ccadc1c5cbd20b8ce32f942505e654b91a4e9410e0ea627c961d632d3be71d6a7dfd64b8f70d28ff91869b92ced908b454936b6d18fcddd7fb77216ccc404",
"1d57b4e09fd920d91877a0737559ee29",
"e83e07722d26779d0b76a52a629b211b",
},
{ // AES-128, with additional input
false,
aes.NewCipher,
16,
"285da6cf762552634636bfee3400b156",
"8f8bada74820cb43",
"",
"ad2af7e4c84337cfc3116d59f02c54a8", // v0
"c92780982442d348cc7363dfc96a999d", // key0
"b4699b33354a83bfed115f770f32db0b", // EntropyInputReseed
"38bfec9a10e6e40c106841dae48dc3b8", // AdditionalInputReseed
"923f37427a8e10bf945249a5b790769a", // v1
"57004c8a776f5c702e83ff56acc32dcc", // key1
"629ead5bacfac8235711ffeb22f57558", // AdditionalInput1
"7ade619ed91092987d8a1d244605f85f", // v2
"3b5f92f511c10fef2f640de2cd8c9049", // key2
"dd8a02ee668ca3e03949b38cb6e6b4df", // AdditionalInput2
"e555aa4432bde04dcf0f0b03ead187b31df06653d444234b5c1bfc11b224285f2fb2b6cdd5a9ae6f13d99bd02c3c9fe9c3c1be46a600f5f757ab4574af893501",
"f5dac2375e820f797c6f1258147d8ea7", // v3
"6bc01c1518fe9f9dfbbb08d97c34db1e", // key3
},
{ // AES-192, without additional input
false,
aes.NewCipher,
24,
"b11d8b104a7ced9b9f37e5d92ad3dfcbb817552b1ae88f6a",
"017510f270c66586a51313eadc32b07e",
"",
"9e5767ab537fe663c71e4054ba618c8d", // v0
"b9b3d73bc0c784a7d78db344109707c73abbff7dc2dfa864", // key0
"6d14cfb36f30c9c1a1ba0e0a32c2f99d1b47f219a3a8ac14", // EntropyInputReseed
"", // AdditionalInputReseed
"c8563c5a4adc3b579f79f898c4b69854", // v1
"3e18d4984d454e5f986e49bfa7a569dab3667ece8130cba1", // key1
"", // AdditionalInput1
"087a3112e191f60619acae2a556f333b", // v2
"b42a24cbb9e8c014bb65350afa28a67b273a41e599bde5b8", // key2
"", // AdditionalInput2
"53fbba563ae014ebc080767aab8452a9f36ce40bbf68f1a12dc0a6388c870c8dfa4250526cbc8c983fee6449903c6bd7c2c02e327680a66b464267edbc4e6797",
"84f344f8277841e920464ca475b10276", // v3
"1f5e987ac2259b7072867e4ae59167094d0162111062f6f8", // key3
},
{ // AES-192, with additional input
false,
aes.NewCipher,
24,
"3a09c9cc5e01f152ea2ed3021d49b4d6386aa6f04521ebde",
"490bd4ee628cf9615035543e70fce4e2",
"",
"59a45ccbc3864f79b896c30d4a231d46", // v0
"a4283dc9450ac97bf22c387082e3816728243473cedaa2af", // key0
"df06e5668d41a6fa7660aef477eff7a0ffc0542c1cd406d5", // EntropyInputReseed
"59b8c26626aab69e462752722f19450d12e2c0e959882d4d06ef4177e396855d", // AdditionalInputReseed
"5857d49a1552923931926dca1682fbc2", // v1
"9c4d7784fe341619e21f2535d404866df3b75e9a7940d471", // key1
"28e57a9128e479985cce391e98127fd126f37ad0f317fd5f97b8c18e762f360b", // AdditionalInput1
"bb8ed7bcbe1203be861b8e6570fe116b", // v2
"6a8fddde995255f89ea3c9454cc481045ff0e16ce5a34693", // key2
"d488672b52e867816178369f542190685bbe8672720c1943d8a4378cc9b9dd0c", // AdditionalInput2
"5c233e2850e4981bab0f6513a76ca2c9f9f97b89b7fedd3d9aaffecf305d89fd5306cf24715895ad9ba7dac8c389fd87f95b4973003150871fa281e962f270cb",
"1cf82a0638c421bb43401943498d0f88", // v3
"5dec9ad1f5f3d0e7bb59ae581097a3f616e443e4f5bd804a", // key3
},
{ // AES-256, without additional input
false,
aes.NewCipher,
32,
"2d4c9f46b981c6a0b2b5d8c69391e569ff13851437ebc0fc00d616340252fed5",
"0bf814b411f65ec4866be1abb59d3c32",
"",
"446ce986bd722ad1a514ebb7d274ec99", // v0
"d64160c3e965f377caef625c7eb21dd37728bcf84bfc23b92e267611feaffda8", // key0
"93500fae4fa32b86033b7a7bac9d37e710dcc67ca266bc8607d665937766d207", // EntropyInputReseed
"", // AdditionalInputReseed
"0b8e38a54036f1ba80a2880d4f17bb09", // v1
"50d9feb33fc77303b83232b7deded04f1bfa4afaa937712f88458d6b64c046c5", // key1
"", // AdditionalInput1
"84b0a849c5459e27fe7f8c5db26fa13d", // v2
"a2203a6f082ecdc0cd38f0b3b19f1a8cd6a5f110a13bb488c1e70f9f95a93024", // key2
"", // AdditionalInput2
"322dd28670e75c0ea638f3cb68d6a9d6e50ddfd052b772a7b1d78263a7b8978b6740c2b65a9550c3a76325866fa97e16d74006bc96f26249b9f0a90d076f08e5",
"de67dd5f9a431fc46dd1825cd1a2bff3", // v3
"de721178a341a85eb54a2f7e2b3cd4bcc201417e739eb183fa958f9af8535b2c", // key3
},
{ // AES-256, with additional input
false,
aes.NewCipher,
32,
"6f60f0f9d486bc23e1223b934e61c0c78ae9232fa2e9a87c6dacd447c3f10e9e",
"401e3f87762fa8a14ab232ccb8480a2f",
"",
"ee534dcfd9d2be3a3f9c65a6c5f599b0", // v0
"6d9aa2e029466438d3e4c22530bd071dbe57b549b87370957b28da8ae083f8d6", // key0
"350be52552a65a804a106543ebb7dd046cffae104e4e8b2f18936d564d3c1950", // EntropyInputReseed
"7a3688adb1cfb6c03264e2762ece96bfe4daf9558fabf74d7fff203c08b4dd9f", // AdditionalInputReseed
"433725f6c4b8c662c3b2db4b75f38d86", // v1
"b5953178a900b2fcf052b5cbc1d882ea944da2965e84fef59c4919bb4d5c892d", // key1
"67cf4a56d081c53670f257c25557014cd5e8b0e919aa58f23d6861b10b00ea80", // AdditionalInput1
"2c342b2ab12bd3484e4660b8dd5f85eb", // v2
"b2b9e9f1ffcfd84c050445f93dfad90d6ca240494bbed5d44a0deb38fbaeb751", // key2
"648d4a229198b43f33dd7dd8426650be11c5656adcdf913bb3ee5eb49a2a3892", // AdditionalInput2
"2d819fb9fee38bfc3f15a07ef0e183ff36db5d3184cea1d24e796ba103687415abe6d9f2c59a11931439a3d14f45fc3f4345f331a0675a3477eaf7cd89107e37",
"a9729f842063b9464e74018c0ab30df3", // v3
"770600434fe0af64e045f5530e2b9732da9e3b4c3af342994a4f1f7ee5c4144e", // key3
},
{ // SM4-128, without additional input
true,
sm4.NewCipher,
16,
"2d4c9f46b981c6a0b2b5d8c69391e569ff13851437ebc0fc00d616340252fed5",
"0bf814b411f65ec4866be1abb59d3c32",
"",
"044f9ff3b7e8ad2b60a7b2c05fe6b5b7",
"7fce60b97d8ceb60506bff1d37b1a936",
"93500fae4fa32b86033b7a7bac9d37e710dcc67ca266bc8607d665937766d207",
"",
"8bd44b2e39f8186497f889c73555797d", // v1
"02b9a8f88124bd9cec909e1fd7ec9971", //key1
"",
"fbc91ad876ba3a84588be2f358b9e13c", // v2
"4804b2a1a971ca729abff5bada051cf6", //key2
"",
"e732a524de8ad239aa293ac8ae588f9d",
"ce60250d77048bdbe48ade354b6869f6",
"6788e31ae27aae09a14aed967ce8b219",
},
{ // SM4-128, with additional input
false,
sm4.NewCipher,
16,
"6f60f0f9d486bc23e1223b934e61c0c78ae9232fa2e9a87c6dacd447c3f10e9e",
"401e3f87762fa8a14ab232ccb8480a2f",
"",
"5e8c10afe142dc9c8caf35411b38730a", // v0
"d72aefa9fd527383ad418f6158627feb", // key0
"350be52552a65a804a106543ebb7dd046cffae104e4e8b2f18936d564d3c1950", // EntropyInputReseed
"7a3688adb1cfb6c03264e2762ece96bfe4daf9558fabf74d7fff203c08b4dd9f", // AdditionalInputReseed
"c00836da0fd780cdc81dabec80e344ce", // v1
"f5f3abdeff30df22f4866d83cd96bc1b", // key1
"67cf4a56d081c53670f257c25557014cd5e8b0e919aa58f23d6861b10b00ea80", // AdditionalInput1
"6ddb205ec76567b31a07ee48437acebc", // v2
"5e23cbe8b97065102ca0d87bfd9ae0da", // key2
"648d4a229198b43f33dd7dd8426650be11c5656adcdf913bb3ee5eb49a2a3892", // AdditionalInput2
"b0ac91f148efbdc3570d7e434aba8d24",
"d1f029bb089613d836ddc6fe1d6fb96f", // v3
"8adfe65e9137b18f060ae91e7a6224c1", // key3
},
}
func TestCtrDRBG(t *testing.T) {
for i, test := range ctrtests {
entropyInput, _ := hex.DecodeString(test.entropyInput)
nonce, _ := hex.DecodeString(test.nonce)
personalizationString, _ := hex.DecodeString(test.personalizationString)
v0, _ := hex.DecodeString(test.v0)
key0, _ := hex.DecodeString(test.key0)
hd, err := NewCtrDrbg(test.cipherProvider, test.keyLen, SECURITY_LEVEL_ONE, test.gm, entropyInput, nonce, personalizationString)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(hd.v[:len(v0)], v0) {
t.Errorf("case %v, not same v0 %s", i+1, hex.EncodeToString(hd.v))
}
if !bytes.Equal(hd.key[:len(key0)], key0) {
t.Errorf("case %v, not same key0 %s", i+1, hex.EncodeToString(hd.key))
}
// Reseed
entropyInputReseed, _ := hex.DecodeString(test.entropyInputReseed)
additionalInputReseed, _ := hex.DecodeString(test.additionalInputReseed)
v1, _ := hex.DecodeString(test.v1)
key1, _ := hex.DecodeString(test.key1)
err = hd.Reseed(entropyInputReseed, additionalInputReseed)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(hd.v, v1) {
t.Errorf("case %v, not same v1 %s", i+1, hex.EncodeToString(hd.v))
}
if !bytes.Equal(hd.key, key1) {
t.Errorf("case %v, not same key1 %s", i+1, hex.EncodeToString(hd.key))
}
// Generate 1
returnbits1, _ := hex.DecodeString(test.returnbits1)
v2, _ := hex.DecodeString(test.v2)
key2, _ := hex.DecodeString(test.key2)
output := make([]byte, len(returnbits1))
additionalInput1, _ := hex.DecodeString(test.additionalInput1)
hd.Generate(output, additionalInput1)
if !bytes.Equal(hd.v, v2) {
t.Errorf("case %v, not same v2 %s", i+1, hex.EncodeToString(hd.v))
}
if !bytes.Equal(hd.key, key2) {
t.Errorf("case %v, not same key2 %s", i+1, hex.EncodeToString(hd.key))
}
// Generate 2
v3, _ := hex.DecodeString(test.v3)
key3, _ := hex.DecodeString(test.key3)
additionalInput2, _ := hex.DecodeString(test.additionalInput2)
hd.Generate(output, additionalInput2)
if !bytes.Equal(hd.v[:len(v0)], v3) {
t.Errorf("case %v, not same v3 %s", i+1, hex.EncodeToString(hd.v))
}
if !bytes.Equal(hd.key, key3) {
t.Errorf("case %v, not same key3 %s", i+1, hex.EncodeToString(hd.key))
}
if !bytes.Equal(returnbits1, output) {
t.Errorf("case %v, not expected return bits %s", i+1, hex.EncodeToString(output))
}
}
}
func TestGmCtrDRBG_Validation(t *testing.T) {
entropyInput := make([]byte, 64)
_, err := NewCtrDrbg(sm4.NewCipher, 16, SECURITY_LEVEL_ONE, true, entropyInput[:16], entropyInput[16:24], nil)
if err == nil {
t.Fatalf("expected error here")
}
_, err = NewCtrDrbg(sm4.NewCipher, 16, SECURITY_LEVEL_ONE, true, entropyInput[:32], entropyInput[32:40], nil)
if err == nil {
t.Fatalf("expected error here")
}
hd, err := NewCtrDrbg(sm4.NewCipher, 16, SECURITY_LEVEL_ONE, true, entropyInput[:32], entropyInput[32:48], nil)
if err != nil {
t.Fatal(err)
}
err = hd.Reseed(entropyInput[:16], nil)
if err == nil {
t.Fatalf("expected error here")
}
}
package drbg
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/hex"
"testing"
"github.com/emmansun/gmsm/sm4"
)
var ctrtests = []struct {
gm bool
cipherProvider func(key []byte) (cipher.Block, error)
keyLen int
entropyInput string
nonce string
personalizationString string
v0 string
key0 string
entropyInputReseed string
additionalInputReseed string
v1 string
key1 string
additionalInput1 string
v2 string
key2 string
additionalInput2 string
returnbits1 string
v3 string
key3 string
}{
{ // AES-128, without additional input
false,
aes.NewCipher,
16,
"0f65da13dca407999d4773c2b4a11d85",
"5209e5b4ed82a234",
"",
"80941680713df715056fb2a3d2e998b2",
"0c42ea6804303954deb197a07e6dbdd2",
"1dea0a12c52bf64339dd291c80d8ca89",
"",
"f2bacbb233252fba35fb0582f9286179", // v1
"32fbfd0109f364ed21ef21a6e5c763e7", //key1
"",
"99003d630bba500fe17c37f8c7331bf6", // v2
"757c8eb766f9aaa4650d6500b58624a3", //key2
"",
"2859cc468a76b08661ffd23b28547ffd0997ad526a0f51261b99ed3a37bd407bf418dbe6c6c3e26ed0ddefcb7474d899bd99f3655427519fc5b4057bcaf306d4",
"5907ab447a88e5106753507cc97e0fd5",
"e421ff2445e04992faf36cf9a5eaf1f9",
},
{ // AES-128, without additional input
false,
aes.NewCipher,
16,
"c9b8d7eb0afa5889e7f9b78a50ed453c",
"3058ba347ecd11b1",
"",
"b4e0180e3af0d99592249db33a29cc4e",
"1621bebef7e9215078459ecc74baffbc",
"643686b86266d9111f29eb389e1184b4",
"",
"7574911eeb85d56d385f0c8c99965c4a", // v1
"a4c515266cb5986825a503b39d5f398c", //key1
"",
"abca3ea049e405d3826f43e54e08c8f7", // v2
"edcdf23f60d3988a4d235798aa0d33a2", //key2
"",
"0a8ccadc1c5cbd20b8ce32f942505e654b91a4e9410e0ea627c961d632d3be71d6a7dfd64b8f70d28ff91869b92ced908b454936b6d18fcddd7fb77216ccc404",
"1d57b4e09fd920d91877a0737559ee29",
"e83e07722d26779d0b76a52a629b211b",
},
{ // AES-128, with additional input
false,
aes.NewCipher,
16,
"285da6cf762552634636bfee3400b156",
"8f8bada74820cb43",
"",
"ad2af7e4c84337cfc3116d59f02c54a8", // v0
"c92780982442d348cc7363dfc96a999d", // key0
"b4699b33354a83bfed115f770f32db0b", // EntropyInputReseed
"38bfec9a10e6e40c106841dae48dc3b8", // AdditionalInputReseed
"923f37427a8e10bf945249a5b790769a", // v1
"57004c8a776f5c702e83ff56acc32dcc", // key1
"629ead5bacfac8235711ffeb22f57558", // AdditionalInput1
"7ade619ed91092987d8a1d244605f85f", // v2
"3b5f92f511c10fef2f640de2cd8c9049", // key2
"dd8a02ee668ca3e03949b38cb6e6b4df", // AdditionalInput2
"e555aa4432bde04dcf0f0b03ead187b31df06653d444234b5c1bfc11b224285f2fb2b6cdd5a9ae6f13d99bd02c3c9fe9c3c1be46a600f5f757ab4574af893501",
"f5dac2375e820f797c6f1258147d8ea7", // v3
"6bc01c1518fe9f9dfbbb08d97c34db1e", // key3
},
{ // AES-192, without additional input
false,
aes.NewCipher,
24,
"b11d8b104a7ced9b9f37e5d92ad3dfcbb817552b1ae88f6a",
"017510f270c66586a51313eadc32b07e",
"",
"9e5767ab537fe663c71e4054ba618c8d", // v0
"b9b3d73bc0c784a7d78db344109707c73abbff7dc2dfa864", // key0
"6d14cfb36f30c9c1a1ba0e0a32c2f99d1b47f219a3a8ac14", // EntropyInputReseed
"", // AdditionalInputReseed
"c8563c5a4adc3b579f79f898c4b69854", // v1
"3e18d4984d454e5f986e49bfa7a569dab3667ece8130cba1", // key1
"", // AdditionalInput1
"087a3112e191f60619acae2a556f333b", // v2
"b42a24cbb9e8c014bb65350afa28a67b273a41e599bde5b8", // key2
"", // AdditionalInput2
"53fbba563ae014ebc080767aab8452a9f36ce40bbf68f1a12dc0a6388c870c8dfa4250526cbc8c983fee6449903c6bd7c2c02e327680a66b464267edbc4e6797",
"84f344f8277841e920464ca475b10276", // v3
"1f5e987ac2259b7072867e4ae59167094d0162111062f6f8", // key3
},
{ // AES-192, with additional input
false,
aes.NewCipher,
24,
"3a09c9cc5e01f152ea2ed3021d49b4d6386aa6f04521ebde",
"490bd4ee628cf9615035543e70fce4e2",
"",
"59a45ccbc3864f79b896c30d4a231d46", // v0
"a4283dc9450ac97bf22c387082e3816728243473cedaa2af", // key0
"df06e5668d41a6fa7660aef477eff7a0ffc0542c1cd406d5", // EntropyInputReseed
"59b8c26626aab69e462752722f19450d12e2c0e959882d4d06ef4177e396855d", // AdditionalInputReseed
"5857d49a1552923931926dca1682fbc2", // v1
"9c4d7784fe341619e21f2535d404866df3b75e9a7940d471", // key1
"28e57a9128e479985cce391e98127fd126f37ad0f317fd5f97b8c18e762f360b", // AdditionalInput1
"bb8ed7bcbe1203be861b8e6570fe116b", // v2
"6a8fddde995255f89ea3c9454cc481045ff0e16ce5a34693", // key2
"d488672b52e867816178369f542190685bbe8672720c1943d8a4378cc9b9dd0c", // AdditionalInput2
"5c233e2850e4981bab0f6513a76ca2c9f9f97b89b7fedd3d9aaffecf305d89fd5306cf24715895ad9ba7dac8c389fd87f95b4973003150871fa281e962f270cb",
"1cf82a0638c421bb43401943498d0f88", // v3
"5dec9ad1f5f3d0e7bb59ae581097a3f616e443e4f5bd804a", // key3
},
{ // AES-256, without additional input
false,
aes.NewCipher,
32,
"2d4c9f46b981c6a0b2b5d8c69391e569ff13851437ebc0fc00d616340252fed5",
"0bf814b411f65ec4866be1abb59d3c32",
"",
"446ce986bd722ad1a514ebb7d274ec99", // v0
"d64160c3e965f377caef625c7eb21dd37728bcf84bfc23b92e267611feaffda8", // key0
"93500fae4fa32b86033b7a7bac9d37e710dcc67ca266bc8607d665937766d207", // EntropyInputReseed
"", // AdditionalInputReseed
"0b8e38a54036f1ba80a2880d4f17bb09", // v1
"50d9feb33fc77303b83232b7deded04f1bfa4afaa937712f88458d6b64c046c5", // key1
"", // AdditionalInput1
"84b0a849c5459e27fe7f8c5db26fa13d", // v2
"a2203a6f082ecdc0cd38f0b3b19f1a8cd6a5f110a13bb488c1e70f9f95a93024", // key2
"", // AdditionalInput2
"322dd28670e75c0ea638f3cb68d6a9d6e50ddfd052b772a7b1d78263a7b8978b6740c2b65a9550c3a76325866fa97e16d74006bc96f26249b9f0a90d076f08e5",
"de67dd5f9a431fc46dd1825cd1a2bff3", // v3
"de721178a341a85eb54a2f7e2b3cd4bcc201417e739eb183fa958f9af8535b2c", // key3
},
{ // AES-256, with additional input
false,
aes.NewCipher,
32,
"6f60f0f9d486bc23e1223b934e61c0c78ae9232fa2e9a87c6dacd447c3f10e9e",
"401e3f87762fa8a14ab232ccb8480a2f",
"",
"ee534dcfd9d2be3a3f9c65a6c5f599b0", // v0
"6d9aa2e029466438d3e4c22530bd071dbe57b549b87370957b28da8ae083f8d6", // key0
"350be52552a65a804a106543ebb7dd046cffae104e4e8b2f18936d564d3c1950", // EntropyInputReseed
"7a3688adb1cfb6c03264e2762ece96bfe4daf9558fabf74d7fff203c08b4dd9f", // AdditionalInputReseed
"433725f6c4b8c662c3b2db4b75f38d86", // v1
"b5953178a900b2fcf052b5cbc1d882ea944da2965e84fef59c4919bb4d5c892d", // key1
"67cf4a56d081c53670f257c25557014cd5e8b0e919aa58f23d6861b10b00ea80", // AdditionalInput1
"2c342b2ab12bd3484e4660b8dd5f85eb", // v2
"b2b9e9f1ffcfd84c050445f93dfad90d6ca240494bbed5d44a0deb38fbaeb751", // key2
"648d4a229198b43f33dd7dd8426650be11c5656adcdf913bb3ee5eb49a2a3892", // AdditionalInput2
"2d819fb9fee38bfc3f15a07ef0e183ff36db5d3184cea1d24e796ba103687415abe6d9f2c59a11931439a3d14f45fc3f4345f331a0675a3477eaf7cd89107e37",
"a9729f842063b9464e74018c0ab30df3", // v3
"770600434fe0af64e045f5530e2b9732da9e3b4c3af342994a4f1f7ee5c4144e", // key3
},
{ // SM4-128, without additional input
true,
sm4.NewCipher,
16,
"2d4c9f46b981c6a0b2b5d8c69391e569ff13851437ebc0fc00d616340252fed5",
"0bf814b411f65ec4866be1abb59d3c32",
"",
"044f9ff3b7e8ad2b60a7b2c05fe6b5b7",
"7fce60b97d8ceb60506bff1d37b1a936",
"93500fae4fa32b86033b7a7bac9d37e710dcc67ca266bc8607d665937766d207",
"",
"8bd44b2e39f8186497f889c73555797d", // v1
"02b9a8f88124bd9cec909e1fd7ec9971", //key1
"",
"fbc91ad876ba3a84588be2f358b9e13c", // v2
"4804b2a1a971ca729abff5bada051cf6", //key2
"",
"e732a524de8ad239aa293ac8ae588f9d",
"ce60250d77048bdbe48ade354b6869f6",
"6788e31ae27aae09a14aed967ce8b219",
},
{ // SM4-128, with additional input
false,
sm4.NewCipher,
16,
"6f60f0f9d486bc23e1223b934e61c0c78ae9232fa2e9a87c6dacd447c3f10e9e",
"401e3f87762fa8a14ab232ccb8480a2f",
"",
"5e8c10afe142dc9c8caf35411b38730a", // v0
"d72aefa9fd527383ad418f6158627feb", // key0
"350be52552a65a804a106543ebb7dd046cffae104e4e8b2f18936d564d3c1950", // EntropyInputReseed
"7a3688adb1cfb6c03264e2762ece96bfe4daf9558fabf74d7fff203c08b4dd9f", // AdditionalInputReseed
"c00836da0fd780cdc81dabec80e344ce", // v1
"f5f3abdeff30df22f4866d83cd96bc1b", // key1
"67cf4a56d081c53670f257c25557014cd5e8b0e919aa58f23d6861b10b00ea80", // AdditionalInput1
"6ddb205ec76567b31a07ee48437acebc", // v2
"5e23cbe8b97065102ca0d87bfd9ae0da", // key2
"648d4a229198b43f33dd7dd8426650be11c5656adcdf913bb3ee5eb49a2a3892", // AdditionalInput2
"b0ac91f148efbdc3570d7e434aba8d24",
"d1f029bb089613d836ddc6fe1d6fb96f", // v3
"8adfe65e9137b18f060ae91e7a6224c1", // key3
},
}
func TestCtrDRBG(t *testing.T) {
for i, test := range ctrtests {
entropyInput, _ := hex.DecodeString(test.entropyInput)
nonce, _ := hex.DecodeString(test.nonce)
personalizationString, _ := hex.DecodeString(test.personalizationString)
v0, _ := hex.DecodeString(test.v0)
key0, _ := hex.DecodeString(test.key0)
hd, err := NewCtrDrbg(test.cipherProvider, test.keyLen, SECURITY_LEVEL_ONE, test.gm, entropyInput, nonce, personalizationString)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(hd.v[:len(v0)], v0) {
t.Errorf("case %v, not same v0 %s", i+1, hex.EncodeToString(hd.v))
}
if !bytes.Equal(hd.key[:len(key0)], key0) {
t.Errorf("case %v, not same key0 %s", i+1, hex.EncodeToString(hd.key))
}
// Reseed
entropyInputReseed, _ := hex.DecodeString(test.entropyInputReseed)
additionalInputReseed, _ := hex.DecodeString(test.additionalInputReseed)
v1, _ := hex.DecodeString(test.v1)
key1, _ := hex.DecodeString(test.key1)
err = hd.Reseed(entropyInputReseed, additionalInputReseed)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(hd.v, v1) {
t.Errorf("case %v, not same v1 %s", i+1, hex.EncodeToString(hd.v))
}
if !bytes.Equal(hd.key, key1) {
t.Errorf("case %v, not same key1 %s", i+1, hex.EncodeToString(hd.key))
}
// Generate 1
returnbits1, _ := hex.DecodeString(test.returnbits1)
v2, _ := hex.DecodeString(test.v2)
key2, _ := hex.DecodeString(test.key2)
output := make([]byte, len(returnbits1))
additionalInput1, _ := hex.DecodeString(test.additionalInput1)
hd.Generate(output, additionalInput1)
if !bytes.Equal(hd.v, v2) {
t.Errorf("case %v, not same v2 %s", i+1, hex.EncodeToString(hd.v))
}
if !bytes.Equal(hd.key, key2) {
t.Errorf("case %v, not same key2 %s", i+1, hex.EncodeToString(hd.key))
}
// Generate 2
v3, _ := hex.DecodeString(test.v3)
key3, _ := hex.DecodeString(test.key3)
additionalInput2, _ := hex.DecodeString(test.additionalInput2)
hd.Generate(output, additionalInput2)
if !bytes.Equal(hd.v[:len(v0)], v3) {
t.Errorf("case %v, not same v3 %s", i+1, hex.EncodeToString(hd.v))
}
if !bytes.Equal(hd.key, key3) {
t.Errorf("case %v, not same key3 %s", i+1, hex.EncodeToString(hd.key))
}
if !bytes.Equal(returnbits1, output) {
t.Errorf("case %v, not expected return bits %s", i+1, hex.EncodeToString(output))
}
}
}
func TestGmCtrDRBG_Validation(t *testing.T) {
entropyInput := make([]byte, 64)
_, err := NewCtrDrbg(sm4.NewCipher, 16, SECURITY_LEVEL_ONE, true, entropyInput[:16], entropyInput[16:24], nil)
if err == nil {
t.Fatalf("expected error here")
}
_, err = NewCtrDrbg(sm4.NewCipher, 16, SECURITY_LEVEL_ONE, true, entropyInput[:32], entropyInput[32:40], nil)
if err == nil {
t.Fatalf("expected error here")
}
hd, err := NewCtrDrbg(sm4.NewCipher, 16, SECURITY_LEVEL_ONE, true, entropyInput[:32], entropyInput[32:48], nil)
if err != nil {
t.Fatal(err)
}
err = hd.Reseed(entropyInput[:16], nil)
if err == nil {
t.Fatalf("expected error here")
}
}

View File

@ -1,46 +1,46 @@
package drbg_test
import (
"bytes"
"fmt"
"github.com/emmansun/gmsm/drbg"
)
func ExampleNewGmCtrDrbgPrng() {
prng, err := drbg.NewGmCtrDrbgPrng(nil, 32, drbg.SECURITY_LEVEL_TEST, nil)
if err != nil {
panic(err)
}
c := 10
b := make([]byte, c)
_, err = prng.Read(b)
if err != nil {
fmt.Println("error:", err)
return
}
// The slice should now contain random bytes instead of only zeroes.
fmt.Println(bytes.Equal(b, make([]byte, c)))
// Output:
// false
}
func ExampleNewGmHashDrbgPrng() {
prng, err := drbg.NewGmHashDrbgPrng(nil, 32, drbg.SECURITY_LEVEL_TEST, nil)
if err != nil {
panic(err)
}
c := 10
b := make([]byte, c)
_, err = prng.Read(b)
if err != nil {
fmt.Println("error:", err)
return
}
// The slice should now contain random bytes instead of only zeroes.
fmt.Println(bytes.Equal(b, make([]byte, c)))
// Output:
// false
}
package drbg_test
import (
"bytes"
"fmt"
"github.com/emmansun/gmsm/drbg"
)
func ExampleNewGmCtrDrbgPrng() {
prng, err := drbg.NewGmCtrDrbgPrng(nil, 32, drbg.SECURITY_LEVEL_TEST, nil)
if err != nil {
panic(err)
}
c := 10
b := make([]byte, c)
_, err = prng.Read(b)
if err != nil {
fmt.Println("error:", err)
return
}
// The slice should now contain random bytes instead of only zeroes.
fmt.Println(bytes.Equal(b, make([]byte, c)))
// Output:
// false
}
func ExampleNewGmHashDrbgPrng() {
prng, err := drbg.NewGmHashDrbgPrng(nil, 32, drbg.SECURITY_LEVEL_TEST, nil)
if err != nil {
panic(err)
}
c := 10
b := make([]byte, c)
_, err = prng.Read(b)
if err != nil {
fmt.Println("error:", err)
return
}
// The slice should now contain random bytes instead of only zeroes.
fmt.Println(bytes.Equal(b, make([]byte, c)))
// Output:
// false
}

View File

@ -1,224 +1,219 @@
package drbg
import (
"errors"
"hash"
"time"
"github.com/emmansun/gmsm/internal/byteorder"
"github.com/emmansun/gmsm/sm3"
)
const HASH_DRBG_SEED_SIZE = 55
const HASH_DRBG_MAX_SEED_SIZE = 111
// HashDrbg hash DRBG structure, its instance is NOT goroutine safe!!!
type HashDrbg struct {
BaseDrbg
newHash func() hash.Hash
c []byte
hashSize int
}
// NewHashDrbg create one hash DRBG instance
func NewHashDrbg(newHash func() hash.Hash, securityLevel SecurityLevel, gm bool, entropy, nonce, personalization []byte) (*HashDrbg, error) {
hd := &HashDrbg{}
hd.gm = gm
hd.newHash = newHash
hd.setSecurityLevel(securityLevel)
md := newHash()
hd.hashSize = md.Size()
// here for the min length, we just check <=0 now
if len(entropy) == 0 || (hd.gm && len(entropy) < hd.hashSize) || len(entropy) >= maxBytes {
return nil, errors.New("drbg: invalid entropy length")
}
// here for the min length, we just check <=0 now
if len(nonce) == 0 || (hd.gm && len(nonce) < hd.hashSize/2) || len(nonce) >= maxBytes>>1 {
return nil, errors.New("drbg: invalid nonce length")
}
if len(personalization) >= maxBytes {
return nil, errors.New("drbg: personalization is too long")
}
if hd.hashSize <= sm3.Size {
hd.v = make([]byte, HASH_DRBG_SEED_SIZE)
hd.c = make([]byte, HASH_DRBG_SEED_SIZE)
hd.seedLength = HASH_DRBG_SEED_SIZE
} else {
hd.v = make([]byte, HASH_DRBG_MAX_SEED_SIZE)
hd.c = make([]byte, HASH_DRBG_MAX_SEED_SIZE)
hd.seedLength = HASH_DRBG_MAX_SEED_SIZE
}
// seed_material = entropy_input || instantiation_nonce || personalization_string
seedMaterial := make([]byte, len(entropy)+len(nonce)+len(personalization))
copy(seedMaterial, entropy)
copy(seedMaterial[len(entropy):], nonce)
copy(seedMaterial[len(entropy)+len(nonce):], personalization)
// seed = Hash_df(seed_material, seed_length)
seed := hd.derive(seedMaterial, hd.seedLength)
// V = seed
copy(hd.v, seed)
// C = Hash_df(0x00 || V, seed_length)
temp := make([]byte, hd.seedLength+1)
temp[0] = 0
copy(temp[1:], seed)
seed = hd.derive(temp, hd.seedLength)
copy(hd.c, seed)
hd.reseedCounter = 1
hd.reseedTime = time.Now()
return hd, nil
}
// NewNISTHashDrbg return hash DRBG implementation which follows NIST standard
func NewNISTHashDrbg(newHash func() hash.Hash, securityLevel SecurityLevel, entropy, nonce, personalization []byte) (*HashDrbg, error) {
return NewHashDrbg(newHash, securityLevel, false, entropy, nonce, personalization)
}
// NewGMHashDrbg return hash DRBG implementation which follows GM/T 0105-2021 standard
func NewGMHashDrbg(securityLevel SecurityLevel, entropy, nonce, personalization []byte) (*HashDrbg, error) {
return NewHashDrbg(sm3.New, securityLevel, true, entropy, nonce, personalization)
}
// Reseed hash DRBG reseed process. GM/T 0105-2021 has a little different with NIST.
func (hd *HashDrbg) Reseed(entropy, additional []byte) error {
// here for the min length, we just check <=0 now
if len(entropy) == 0 || (hd.gm && len(entropy) < hd.hashSize) || len(entropy) >= maxBytes {
return errors.New("drbg: invalid entropy length")
}
if len(additional) >= maxBytes {
return errors.New("drbg: additional input too long")
}
seedMaterial := make([]byte, len(entropy)+hd.seedLength+len(additional)+1)
seedMaterial[0] = 1
if hd.gm { // seed_material = 0x01 || entropy_input || V || additional_input
copy(seedMaterial[1:], entropy)
copy(seedMaterial[len(entropy)+1:], hd.v)
} else { // seed_material = 0x01 || V || entropy_input || additional_input
copy(seedMaterial[1:], hd.v)
copy(seedMaterial[hd.seedLength+1:], entropy)
}
copy(seedMaterial[len(entropy)+hd.seedLength+1:], additional)
// seed = Hash_df(seed_material, seed_length)
seed := hd.derive(seedMaterial, hd.seedLength)
// V = seed
copy(hd.v, seed)
temp := make([]byte, hd.seedLength+1)
// C = Hash_df(0x01 || V, seed_length)
temp[0] = 0
copy(temp[1:], seed)
seed = hd.derive(temp, hd.seedLength)
copy(hd.c, seed)
hd.reseedCounter = 1
hd.reseedTime = time.Now()
return nil
}
func (hd *HashDrbg) addW(w []byte) {
t := make([]byte, hd.seedLength)
copy(t[hd.seedLength-len(w):], w)
add(t, hd.v, hd.seedLength)
}
func (hd *HashDrbg) addC() {
add(hd.c, hd.v, hd.seedLength)
}
func (hd *HashDrbg) addH() {
md := hd.newHash()
md.Write([]byte{0x03})
md.Write(hd.v)
hd.addW(md.Sum(nil))
}
func (hd *HashDrbg) addReseedCounter() {
t := make([]byte, hd.seedLength)
byteorder.BEPutUint64(t[hd.seedLength-8:], hd.reseedCounter)
add(t, hd.v, hd.seedLength)
}
func (hd *HashDrbg) MaxBytesPerRequest() int {
if hd.gm {
return hd.hashSize
}
return maxBytesPerGenerate
}
// Generate hash DRBG pseudorandom bits process. GM/T 0105-2021 has a little different with NIST.
// GM/T 0105-2021 can only generate no more than hash.Size bytes once.
func (hd *HashDrbg) Generate(b, additional []byte) error {
if hd.NeedReseed() {
return ErrReseedRequired
}
if (hd.gm && len(b) > hd.hashSize) || (!hd.gm && len(b) > maxBytesPerGenerate) {
return errors.New("drbg: too many bytes requested")
}
md := hd.newHash()
m := len(b)
// if len(additional_input) > 0, then
// w = Hash(0x02 || V || additional_input)
if len(additional) > 0 {
md.Write([]byte{0x02})
md.Write(hd.v)
md.Write(additional)
w := md.Sum(nil)
md.Reset()
hd.addW(w)
}
if hd.gm { // leftmost(Hash(V))
md.Write(hd.v)
copy(b, md.Sum(nil))
md.Reset()
} else {
limit := uint64(m+md.Size()-1) / uint64(md.Size())
data := make([]byte, hd.seedLength)
copy(data, hd.v)
for i := range int(limit) {
md.Write(data)
copy(b[i*md.Size():], md.Sum(nil))
addOne(data, hd.seedLength)
md.Reset()
}
}
// V = (V + H + C + reseed_counter) mode 2^seed_length
hd.addH()
hd.addC()
hd.addReseedCounter()
hd.reseedCounter++
return nil
}
// derive Hash_df
func (hd *HashDrbg) derive(seedMaterial []byte, len int) []byte {
md := hd.newHash()
limit := uint64(len+hd.hashSize-1) / uint64(hd.hashSize)
var requireBytes [4]byte
byteorder.BEPutUint32(requireBytes[:], uint32(len<<3))
var ct byte = 1
k := make([]byte, len)
for i := range int(limit) {
// Hash( counter_byte || return_bits || seed_material )
md.Write([]byte{ct})
md.Write(requireBytes[:])
md.Write(seedMaterial)
copy(k[i*md.Size():], md.Sum(nil))
ct++
md.Reset()
}
return k
}
package drbg
import (
"encoding/binary"
"errors"
"hash"
"time"
"github.com/emmansun/gmsm/sm3"
)
const HASH_DRBG_SEED_SIZE = 55
const HASH_DRBG_MAX_SEED_SIZE = 111
type HashDrbg struct {
BaseDrbg
md hash.Hash
c []byte
}
// NewHashDrbg create one hash DRBG instance
func NewHashDrbg(md hash.Hash, securityLevel SecurityLevel, gm bool, entropy, nonce, personalization []byte) (*HashDrbg, error) {
hd := &HashDrbg{}
hd.gm = gm
hd.md = md
hd.setSecurityLevel(securityLevel)
// here for the min length, we just check <=0 now
if len(entropy) == 0 || (hd.gm && len(entropy) < hd.md.Size()) || len(entropy) >= MAX_BYTES {
return nil, errors.New("invalid entropy length")
}
// here for the min length, we just check <=0 now
if len(nonce) == 0 || (hd.gm && len(nonce) < hd.md.Size()/2) || len(nonce) >= MAX_BYTES>>1 {
return nil, errors.New("invalid nonce length")
}
if len(personalization) >= MAX_BYTES {
return nil, errors.New("personalization is too long")
}
if md.Size() <= sm3.Size {
hd.v = make([]byte, HASH_DRBG_SEED_SIZE)
hd.c = make([]byte, HASH_DRBG_SEED_SIZE)
hd.seedLength = HASH_DRBG_SEED_SIZE
} else {
hd.v = make([]byte, HASH_DRBG_MAX_SEED_SIZE)
hd.c = make([]byte, HASH_DRBG_MAX_SEED_SIZE)
hd.seedLength = HASH_DRBG_MAX_SEED_SIZE
}
// seed_material = entropy_input || instantiation_nonce || personalization_string
seedMaterial := make([]byte, len(entropy)+len(nonce)+len(personalization))
copy(seedMaterial, entropy)
copy(seedMaterial[len(entropy):], nonce)
copy(seedMaterial[len(entropy)+len(nonce):], personalization)
// seed = Hash_df(seed_material, seed_length)
seed := hd.derive(seedMaterial, hd.seedLength)
// V = seed
copy(hd.v, seed)
// C = Hash_df(0x00 || V, seed_length)
temp := make([]byte, hd.seedLength+1)
temp[0] = 0
copy(temp[1:], seed)
seed = hd.derive(temp, hd.seedLength)
copy(hd.c, seed)
hd.reseedCounter = 1
hd.reseedTime = time.Now()
return hd, nil
}
// NewNISTHashDrbg return hash DRBG implementation which follows NIST standard
func NewNISTHashDrbg(md hash.Hash, securityLevel SecurityLevel, entropy, nonce, personalization []byte) (*HashDrbg, error) {
return NewHashDrbg(md, securityLevel, false, entropy, nonce, personalization)
}
// NewGMHashDrbg return hash DRBG implementation which follows GM/T 0105-2021 standard
func NewGMHashDrbg(securityLevel SecurityLevel, entropy, nonce, personalization []byte) (*HashDrbg, error) {
return NewHashDrbg(sm3.New(), securityLevel, true, entropy, nonce, personalization)
}
// Reseed hash DRBG reseed process. GM/T 0105-2021 has a little different with NIST.
func (hd *HashDrbg) Reseed(entropy, additional []byte) error {
// here for the min length, we just check <=0 now
if len(entropy) == 0 || (hd.gm && len(entropy) < hd.md.Size()) || len(entropy) >= MAX_BYTES {
return errors.New("invalid entropy length")
}
if len(additional) >= MAX_BYTES {
return errors.New("additional input too long")
}
seedMaterial := make([]byte, len(entropy)+hd.seedLength+len(additional)+1)
seedMaterial[0] = 1
if hd.gm { // seed_material = 0x01 || entropy_input || V || additional_input
copy(seedMaterial[1:], entropy)
copy(seedMaterial[len(entropy)+1:], hd.v)
} else { // seed_material = 0x01 || V || entropy_input || additional_input
copy(seedMaterial[1:], hd.v)
copy(seedMaterial[hd.seedLength+1:], entropy)
}
copy(seedMaterial[len(entropy)+hd.seedLength+1:], additional)
// seed = Hash_df(seed_material, seed_length)
seed := hd.derive(seedMaterial, hd.seedLength)
// V = seed
copy(hd.v, seed)
temp := make([]byte, hd.seedLength+1)
// C = Hash_df(0x01 || V, seed_length)
temp[0] = 0
copy(temp[1:], seed)
seed = hd.derive(temp, hd.seedLength)
copy(hd.c, seed)
hd.reseedCounter = 1
hd.reseedTime = time.Now()
return nil
}
func (hd *HashDrbg) addW(w []byte) {
t := make([]byte, hd.seedLength)
copy(t[hd.seedLength-len(w):], w)
add(t, hd.v, hd.seedLength)
}
func (hd *HashDrbg) addC() {
add(hd.c, hd.v, hd.seedLength)
}
func (hd *HashDrbg) addH() {
hd.md.Write([]byte{0x03})
hd.md.Write(hd.v)
hd.addW(hd.md.Sum(nil))
hd.md.Reset()
}
func (hd *HashDrbg) addReseedCounter() {
t := make([]byte, hd.seedLength)
binary.BigEndian.PutUint64(t[hd.seedLength-8:], hd.reseedCounter)
add(t, hd.v, hd.seedLength)
}
func (hd *HashDrbg) MaxBytesPerRequest() int {
if hd.gm {
return hd.md.Size()
}
return MAX_BYTES_PER_GENERATE
}
// Generate hash DRBG pseudorandom bits process. GM/T 0105-2021 has a little different with NIST.
// GM/T 0105-2021 can only generate no more than hash.Size bytes once.
func (hd *HashDrbg) Generate(b, additional []byte) error {
if hd.NeedReseed() {
return ErrReseedRequired
}
if (hd.gm && len(b) > hd.md.Size()) || (!hd.gm && len(b) > MAX_BYTES_PER_GENERATE) {
return errors.New("too many bytes requested")
}
md := hd.md
m := len(b)
// if len(additional_input) > 0, then
// w = Hash(0x02 || V || additional_input)
if len(additional) > 0 {
md.Write([]byte{0x02})
md.Write(hd.v)
md.Write(additional)
w := md.Sum(nil)
md.Reset()
hd.addW(w)
}
if hd.gm { // leftmost(Hash(V))
md.Write(hd.v)
copy(b, md.Sum(nil))
md.Reset()
} else {
limit := uint64(m+md.Size()-1) / uint64(md.Size())
data := make([]byte, hd.seedLength)
copy(data, hd.v)
for i := 0; i < int(limit); i++ {
md.Write(data)
copy(b[i*md.Size():], md.Sum(nil))
addOne(data, hd.seedLength)
md.Reset()
}
}
// V = (V + H + C + reseed_counter) mode 2^seed_length
hd.addH()
hd.addC()
hd.addReseedCounter()
hd.reseedCounter++
return nil
}
// derive Hash_df
func (hd *HashDrbg) derive(seedMaterial []byte, len int) []byte {
md := hd.md
limit := uint64(len+md.Size()-1) / uint64(md.Size())
var requireBytes [4]byte
binary.BigEndian.PutUint32(requireBytes[:], uint32(len<<3))
var ct byte = 1
k := make([]byte, len)
for i := 0; i < int(limit); i++ {
// Hash( counter_byte || return_bits || seed_material )
md.Write([]byte{ct})
md.Write(requireBytes[:])
md.Write(seedMaterial)
copy(k[i*md.Size():], md.Sum(nil))
ct++
md.Reset()
}
return k
}

View File

@ -1,251 +1,251 @@
package drbg
import (
"bytes"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
"hash"
"testing"
"github.com/emmansun/gmsm/sm3"
)
var tests = []struct {
gm bool
newHash func() hash.Hash
entropyInput string
nonce string
personalizationString string
v0 string
c0 string
entropyInputReseed string
additionalInputReseed string
v1 string
c1 string
additionalInput1 string
v2 string
additionalInput2 string
returnbits1 string
v3 string
}{
{
false,
sha1.New,
"1610b828ccd27de08ceea032a20e9208",
"492cf1709242f6b5",
"",
"9e8301725d5f133b4ab7d329fd2f87ae5f89d96a9dd7e2b98beee1c707b8c3fe412d1125b58bae5dc08a11dac3be4a3147347160fef218",
"e5e12450450efe5fdc777c95b8c23c938fcd592e2d788f12461936e4a16131b1f2d11ce7f0159ee1e635e62f3df8bda4fea077ad5f9d06",
"72d28c908edaf9a4d1e526d8f2ded544",
"",
"745c659f2944829ca6e209c8ca2dddecf9f1861383e34e94007a3a51b8444fd5ae738e7d9c0d5e69aa97ee16c49cfd2432eb32ba5738fa",
"a1fc40009357a024d878818cf6f979a88d4cc5d760b308ae1a5b9f067972e6f7cf92ddb129a8d3c1bb0005bcf3f8871fd65e794f1990b7",
"",
"1658a59fbc9c22c17f5a8b55c1275795873e4beae49657421ad5d95831b736cd7e066c738bcbb343933c411c7c17917593c03a77bed56b",
"",
"56f33d4fdbb9a5b64d26234497e9dcb87798c68d08f7c41199d4bddf97ebbf6cb5550e5d149ff4d5bd0f05f25a6988c17436396227184af84a564335658e2f8572bea333eee2abff22ffa6de3e22aca2",
"b854e5a04ff3c2e657d30ce2b820d13e148b11c245495ff03531785eab2a1dc54d994a5597b15c5b10001f49606c88b4ff0d61acb61820",
},
{
false,
sha1.New,
"d9bab5cedca96f6178d64509a0dfdc5e",
"dad8989414450e01",
"",
"5e07c6b72aaa5afcaab1cc3929239debde7f590886ba5bf558b90345f8518cb87a2bccdefa0c22283538e505efdaf2bd643059fd842106",
"362aafd121de087197466e77b9bf6924841c01bd5fa98d6bc0a75b95d91166ec80e1516a10fff3216a7ad0b0c6e4f4d9708ccd69677134",
"c6bad074c5906786f5e1f32099f5b491",
"3e6bf46f4daa3825d7194e694e7752f7",
"66165aed47c55d963e25aa856553e0a5a590ed06e3cec66254c6a3d8ac8b30da6b334145c466a025b445938d84151bbdbe1509e1cc7189",
"bca1bfd5a1c718d53cd73eb584eedc19d5a3396bf558f659ae673106d0abe1f194e695ca67c2e8ddc8ee95ace21e6b12751faa695ac727",
"04fa2895aa5a6f8c5743343b805e5ea4",
"22b81ac2e98c766b7afce93aea42bcbf7b342672d927bcbc032dd4df7d3712cc0019d750e811a157c71db2340f6d022bd498dbd4dd4669",
"df5dc459dff02aa2f052d721ec607230",
"c48b89f9da3f748245555d5d033b693dd71a4df5690205cefcd720113cc24e098936ff5e77b541535870b339468cdd8d6faf8c56163a700a75b23e599b5aecf16f3baf6d5f2419971f24f446720feabe",
"df59da988b538f40b7d427f06f3198d950d75fdece80b315b19505e64de2f4bd95006d7c6d774e39237115e40aca2d4a88ddec412b67ee",
},
{
false,
sha256.New,
"63363377e41e86468deb0ab4a8ed683f6a134e47e014c700454e81e95358a569",
"808aa38f2a72a62359915a9f8a04ca68",
"",
"32ab605ddc8d5651093b8a59bd9d3adea1249e21a69e2e4a3967515fa03ad41ccf5b126eb9f3b268080c952df88241fe4cc27bbcbbbed5",
"8ea2691d1915ebb4975593ca3fbad0ba137026d901a95950a207c41dc7773e15c1e85f4a5f91002866830bebe5c4ee1785b839323fbb44",
"e62b8a8ee8f141b6980566e3bfe3c04903dad4ac2cdf9f2280010a6739bc83d3",
"",
"59177d93843f0550f33933a51eb488168699ab9c85651536a61f7ec71e8b274a151f17e56becaf531dcfc955f2f1adb6536d51b256d53c",
"897c02699f4254e1f33c94f7bfa85da3826df6c2590ed0815cbced36d77aa3375a1582ffc1c887416afd1ba0f04b6ddff81a2b0e5b844d",
"",
"e2937ffd23815a32e675c89cde5ce5ba0907a25ede73e61c9ec76d67da582c94001fda32b60ec40202a164c6a4d66411cc6b99b1284617",
"",
"04eec63bb231df2c630a1afbe724949d005a587851e1aa795e477347c8b056621c18bddcdd8d99fc5fc2b92053d8cfacfb0bb8831205fad1ddd6c071318a6018f03b73f5ede4d4d071f9de03fd7aea105d9299b8af99aa075bdb4db9aa28c18d174b56ee2a014d098896ff2282c955a81969e069fa8ce007a180183a07dfae17",
"6c0f8266c2c3af14d9b25d949e05435d8b7599213782b6eac6cd90a10d48e1c96088f5dba20241b68cb64bb05028c35e5558ef8a6edca6",
},
{
false,
sha256.New,
"9cfb7ad03be487a3b42be06e9ae44f283c2b1458cec801da2ae6532fcb56cc4c",
"a20765538e8db31295747ec922c13a69",
"",
"8037eb9f243343f8af8c756475ea998f47a487c64dfad9945391004b08cf1a9102d4669492f554b543d820f18a90f453ad53acaf39f0c9",
"ed540b209e044dc2591923883c9a3b1b7c265bc053c40aa91971b09be4d3b3034b05f197a09c6339c7c16de14a20e29ea17bf11cbdb248",
"96bc8014f90ebdf690db0e171b59cc46c75e2e9b8e1dc699c65c03ceb2f4d7dc",
"6fea0894052dab3c44d503950c7c72bd7b87de87cb81d3bb51c32a62f742286d",
"cf9d4dd8a2c4fb507addbe849643acef2bcf6a4403082a026d50371bc7f2ea9d3975790238af78b750ef0334b7e42e0b1e71aeb97c6029",
"e16ed4378e0342deff3003334eae72709c31f5b4004ab9870ee73a6ab4c7eb6f18027c717bf8c94ccc1e06ce5a3afaacb431e2f860f7ed",
"d3467c78563b74c13db7af36c2a964820f2a9b1b167474906508fdac9b2049a6",
"b10c221030c83e2f7a0dc1b7e4f21f5fc8015ff80352e416298fcc88847c8d0ca970964fbaa83f411e07fb6d6ac42b95a2c1abce0fc285",
"5840a11cc9ebf77b963854726a826370ffdb2fc2b3d8479e1df5dcfa3dddd10b",
"71c1154a2a7a3552413970bf698aa02f14f8ea95e861f801f463be27868b1b14b1b4babd9eba5915a6414ab1104c8979b1918f3094925aeab0d07d2037e613b63cbd4f79d9f95c84b47ed9b77230a57515c211f48f4af6f5edb2c308b33905db308cf88f552c8912c49b34e66c026e67b302ca65b187928a1aba9a49edbfe190",
"927af647becb810e793dc4eb33a091d0643355ac039d9e1e4d60a2ac023dca791d46f5e560b237047371aa1d629988772af7b96c0d0a07",
},
{
false,
sha512.New,
"3144e17a10c856129764f58fd8e4231020546996c0bf6cff8e91c24ee09be333",
"b16fcb1cf0c010f31feab733588b8e04",
"",
"3a85ca10eac683d6a9270594d17f33a21dad7b9b259c2a174462a5e0c909a133db84b4ee2bdb0f72cdcef7d62854e535468452285dbe8e46bed3965dc9c66952defa48879493edc01bc07ed4973c115cfdd9947a708465351b78b804652ec7cbe7f6e2a09193fa352ff991d38c94ac",
"74ea437c49126ff361feab5639a8ad318d455c94b3f999ff1606f592c27f8bf0be562c7bffa297de8512ef44b0dfc8db5cb17c9692ac0d80f066961e6426084108089eee4a759d5309ec861668ddeb1c31ceef26edad678b6f36c3ebcb9c936cafcee3d9a96ae6554e22d42888ab07",
"a0b3584c2c8412f618406834404d1eb0ce999ba28966054d7e497e0db608b967",
"",
"b37f9aa39c5a80df56c040402407960ef6f8892d1a688ffc93bad6ebe6af44d55ccd66c1f44eb531e9dac1c9447681d7b27b2b703b490032696b32330b5edd123e5ece7c40efe70a29822ea8e4e454bb72085c6b037a8652ec227f899dd01455db8ee7b6b2e92114f6f9fb678e6332",
"908ad858db2c5d21fa1cd860217bd75ad0ba1df2fd24e303964c01113a0b024a1e53640d5ae339040b4357c1f3c0be2f14607b1385e968183c53ecd9a33ddb04b3ac36dfc1353d8571159a0b31b81b5d3de24b8ae6530c838fa8712ea5d4d58763f2be0ab1989987c56bfd315df521",
"",
"440a72fc7786de0150dd18a045836d69c7b2a720178d73002a06d7fd20ba471f7b20cacf4f31ee35f51e198b383740fb34724a0747e261c800fa0f744bdc842d37199f6acf5f4af041a6600878cf72a7ceaa750fa1c23546f962afe97c055683eaf5131d9f9c882edb93c50adba963",
"",
"efa35dd0362adb7626456b36fac74d3c28d01d926420275a28bea9c9dd7547c15e7931852ac1277076567535239c1f429c7f75cf74c2267deb6a3e596cf326156c796941283b8d583f171c2f6e3323f7555e1b181ffda30507210cb1f589b23cd71880fd44370cacf43375b0db7e336f12b309bfd4f610bb8f20e1a15e253a4fe511a027968df0b105a1d73aff7c7a826d39f640dfb8f522259ed402282e2c2e9d3a498f51725fe4141b06da5598a42ac1e0494e997d566a1a39b676b96a6003a4c5db84f246584ee65af70ff2160278166da16d91c9b8f2deb02751a1088ad6be4e80ef966eb73e66bc87cad87c77c0b34a21ba1da0ba6d16ca5046dc4abda0",
"d4954b5552b33b234af9f10066ff44c4986cc51314b25603c052d90e5ac5496999742edcaa15273a0061714d2bf7ffb32b7000bfdeb10605f36174eb33a48a4cc007c23bb03597b4d8a6373ca7037e8a8ff08f63779da9e61878b1886cb084ba68ceef8ad4e5ba7720acbd3b262822",
},
{
false,
sha512.New,
"c73a7820f0f53e8bbfc3b7b71d994143cf6e98642e9ea6d8df5dccbc43db8720",
"20cc9834b588adcb1bbde64f0d2a34cb",
"",
"852d1b4fdd41cd7e0f597c45c8e4b401a5fecf9229b6072451ca65b5289882c686e7919922ce82de2faac83cd4c4eddfa2cdcf6244a4d2acdd34c0232136409bb50ea24d0c33fcfd1aaf1cc110b5353d32e4e6df59ae25ec124000de62fcfa8bb4cb3f3b72e2da2066ef00cd66d9e9",
"f7b0c9cf2ccf58fd8c8b69daa4cf24a874c95b57a9f5be16aaa71ec30070ac8f222fe21788fec14b8a9ad7ad20912c05a6f94548646779a16c787b135ce8d08c49f7e234cbd2c7733571f5ad6479b5fc50403496581b4861ef8ec848affbd2077ab164fc6bb2dd7b008a650504bfd8",
"12dd2aca8879046d23165c60f8aedc20415783e156d42a94346826aaeb02eacf",
"9b59ff78a34eabe0060c2792ca9b49e9781e6b802badf7dbde27caaed3343706",
"181a302352d9ebf0b669730b2441a9f4c16a4b9d25ebc84ed01c460d293cd3e8b7bff1aca32b0ea8d281df0ef8d1ae09d4cf97690c944f4713adb9ede90763f3ed77081c37c0fc60f8b60b5108cf6276c80db14a82aaef1bf8da03781445cfcc7cdc02b1c7a2740874dd948118f7ef",
"28b638d631f054eba562320e9d151f905863dd6c04d8ba41167bcf3b0236d4e5dde1dc7bf690e61b4a65997bd9c67ff908fe7e2443d01c8eac15b2ea5c80ba89f09aa9b8a81d56124bb71586812827f463de90318727102dbd5e59ca5f1af78ab73844695eee0977b754854e525097",
"dc74a9e480a6ff6f6bce53ab9c7bdde4b13d70fb5196cdd5e3a0555ccf06fe91",
"40d068f984ca40dc5bcba519c156c98519ce29092ac4828fe69815482b73a8ce95a1ce2899bbf4c41ce7788ad2982e3cea3266f4cadc50ae528dc61aa7c521489869e3efc6c82ccefbbab45673e0f59d5654cf910fa146d984a42c5f17fb60340c86d0d07c7e2f2e6df3cffd722a0e",
"8f3f229011209b2f399096afb054bccca6bc46aaee98845838fb1fb78b66f3bd",
"e6c96442582811ec90e587525f36c555e2fd6361a0c5b0284917a4fa6f6e8ace83f11a1fb26cea6692b225ae7c5be286dd27471f323d7a2e4431722bb337b1ba0e648ea2e9f0918b50e9111f2377636ba69b0e1cb5295078d76c549c8656940eb15ca5aded7adc46e6fa4b86948f212fea3f3befdeece8b20e420ca84c760196ddf0b074df0a9f097a5db8f6125800f5fe746a62df1208042f1255b524465a17efcf6a537612968430e2adcff30f7407a51ed7305334384e512e003642cca175636819f021c76a2f44e89e6fe39cf164477910379cd314f735c357f9379de22495276b401c98ffb09a6dc03e484b355a9464511401eeaa05b4556e73b55227f8",
"6986a1cfb6ba95c8012dd7285e6be915723206752f9d3cd0fd13e4832daa7db47383aaa4904cdadf674d1206ac5eafa99de1304fc0b6a1b5e32e34a7f4141e89353878c0d3f6a0ba5b9ed452d61260de9e5acbf8134485b3b9e990f59f34d4d43307e40ad0d0a505efdb24b72f807b",
},
{
true,
sm3.New,
"63363377e41e86468deb0ab4a8ed683f6a134e47e014c700454e81e95358a569",
"808aa38f2a72a62359915a9f8a04ca68",
"",
"7a070cb0c9806fe6a74c0396825642690fc458e76397b75b5977e8a5e693687a703b08c8fbb9e9a600d181c530e889336390818ca2c271",
"218bcd7e3a836cc2e275e660a29fab9c1b3e5596275d8f3141f8c7fb3b285b9edda112e7e85e8492c9b4ed9c1ea466b00cce3be9c10a03",
"e62b8a8ee8f141b6980566e3bfe3c04903dad4ac2cdf9f2280010a6739bc83d3",
"",
"99ec25b65124082695c773d8ef03cc2b31a916956bb582752399c3951c9956d21a73a08091f47401e7133844ea6544361961c7cc4d7e6e",
"c7d8714c59bce8c8b033b99ce3577acd68a3b10151cc0a60c4b2790e56ce47592574d6909fab7fc551d33402f364aaa4d6fcc83989b9c7",
"",
"61c49702aae0f0ef45fb2d75d25b46f89a4cc796bd818d0c7bd0a23a3938e9c81783160d900852f33241399b1a69f7c80bc9865e182676",
"",
"00d98d35a2fab8df23e9e1fb9aad143d62c0759eb79e15c37e8f2bc5064e68da",
"299d084f049dd9b7f62ee712b5b2c1c602f078980f4d9816d8f2baf38765be984b6c493497af30f68a56072404f27e45af419d04eb9e35",
},
{
true,
sm3.New,
"9cfb7ad03be487a3b42be06e9ae44f283c2b1458cec801da2ae6532fcb56cc4c",
"a20765538e8db31295747ec922c13a69",
"",
"997cd31a7032c8643ca56de1d34ff4f930b13192e17c8947bcaf9b9d010cf79805511255c7ea18b41cde77e491ca943861ec29780f3f36", // v0
"4c5b167d27fa9d40cbc45d0d9f3c52504a1cb5aa2f37a3fa812037bd1e458412ecff0641dd5cb2785d0f8044151b42842777211547b457", // c0
"96bc8014f90ebdf690db0e171b59cc46c75e2e9b8e1dc699c65c03ceb2f4d7dc",
"6fea0894052dab3c44d503950c7c72bd7b87de87cb81d3bb51c32a62f742286d",
"e3d804eda66df62f425c41047c5812fca471c8236395c92d4bd834c2e52d606be6ad3da8973df16e8567bcb16e45f2842ace91bf6dfeb3", // v1
"1a1b2fffca26953626e0fa3afd377e14e63c5e81275b39f1436b707efb2c3059e6ced8fdb238a45bf05aae9f2417dbf5c4f89d3772f324", // c1
"d3467c78563b74c13db7af36c2a964820f2a9b1b167474906508fdac9b2049a6",
"fdf334ed70948b65693d3b3f798f91118aae26a48af1045be386984b75695902870479c3c53593d332d195ee7f1bed45fc5069cc4f9948", // v2
"5840a11cc9ebf77b963854726a826370ffdb2fc2b3d8479e1df5dcfa3dddd10b",
"48709db5509a03d6131775fbbfe74fe52611e760d22fde61e274a295f4354d67",
"180e64ed3abb209b901e357a76c70f2670ea8525b24c401d146b70b178e33d8fd10f7680c50bbe1f9773ba664dd14cd25d329c380bf399", // v3
},
}
func TestHashDRBG(t *testing.T) {
for _, test := range tests {
entropyInput, _ := hex.DecodeString(test.entropyInput)
nonce, _ := hex.DecodeString(test.nonce)
personalizationString, _ := hex.DecodeString(test.personalizationString)
v0, _ := hex.DecodeString(test.v0)
c0, _ := hex.DecodeString(test.c0)
hd, err := NewHashDrbg(test.newHash, SECURITY_LEVEL_ONE, test.gm, entropyInput, nonce, personalizationString)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(hd.v[:len(v0)], v0) {
t.Errorf("not same v0 %s", hex.EncodeToString(hd.v[:len(v0)]))
}
if !bytes.Equal(hd.c[:len(c0)], c0) {
t.Errorf("not same c0 %s", hex.EncodeToString(hd.c[:len(c0)]))
}
// Reseed
entropyInputReseed, _ := hex.DecodeString(test.entropyInputReseed)
additionalInputReseed, _ := hex.DecodeString(test.additionalInputReseed)
v1, _ := hex.DecodeString(test.v1)
c1, _ := hex.DecodeString(test.c1)
err = hd.Reseed(entropyInputReseed, additionalInputReseed)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(hd.v[:len(v0)], v1) {
t.Errorf("not same v1 %s", hex.EncodeToString(hd.v[:len(v0)]))
}
if !bytes.Equal(hd.c[:len(c0)], c1) {
t.Errorf("not same c1 %s", hex.EncodeToString(hd.c[:len(c0)]))
}
// Generate 1
returnbits1, _ := hex.DecodeString(test.returnbits1)
v2, _ := hex.DecodeString(test.v2)
output := make([]byte, len(returnbits1))
additionalInput1, _ := hex.DecodeString(test.additionalInput1)
hd.Generate(output, additionalInput1)
if !bytes.Equal(hd.v[:len(v0)], v2) {
t.Errorf("not same v2 %s", hex.EncodeToString(hd.v[:len(v0)]))
}
// Generate 2
v3, _ := hex.DecodeString(test.v3)
additionalInput2, _ := hex.DecodeString(test.additionalInput2)
hd.Generate(output, additionalInput2)
if !bytes.Equal(hd.v[:len(v0)], v3) {
t.Errorf("not same v3 %s", hex.EncodeToString(hd.v[:len(v0)]))
}
if !bytes.Equal(returnbits1, output) {
t.Errorf("not expected return bits %s", hex.EncodeToString(output))
}
}
}
func TestGmHashDRBG_Validation(t *testing.T) {
entropyInput := make([]byte, 64)
_, err := NewHashDrbg(sm3.New, SECURITY_LEVEL_ONE, true, entropyInput[:16], entropyInput[16:24], nil)
if err == nil {
t.Fatalf("expected error here")
}
_, err = NewHashDrbg(sm3.New, SECURITY_LEVEL_ONE, true, entropyInput[:32], entropyInput[32:40], nil)
if err == nil {
t.Fatalf("expected error here")
}
hd, err := NewHashDrbg(sm3.New, SECURITY_LEVEL_ONE, true, entropyInput[:32], entropyInput[32:48], nil)
if err != nil {
t.Fatal(err)
}
err = hd.Reseed(entropyInput[:16], nil)
if err == nil {
t.Fatalf("expected error here")
}
}
package drbg
import (
"bytes"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
"hash"
"testing"
"github.com/emmansun/gmsm/sm3"
)
var tests = []struct {
gm bool
md hash.Hash
entropyInput string
nonce string
personalizationString string
v0 string
c0 string
entropyInputReseed string
additionalInputReseed string
v1 string
c1 string
additionalInput1 string
v2 string
additionalInput2 string
returnbits1 string
v3 string
}{
{
false,
sha1.New(),
"1610b828ccd27de08ceea032a20e9208",
"492cf1709242f6b5",
"",
"9e8301725d5f133b4ab7d329fd2f87ae5f89d96a9dd7e2b98beee1c707b8c3fe412d1125b58bae5dc08a11dac3be4a3147347160fef218",
"e5e12450450efe5fdc777c95b8c23c938fcd592e2d788f12461936e4a16131b1f2d11ce7f0159ee1e635e62f3df8bda4fea077ad5f9d06",
"72d28c908edaf9a4d1e526d8f2ded544",
"",
"745c659f2944829ca6e209c8ca2dddecf9f1861383e34e94007a3a51b8444fd5ae738e7d9c0d5e69aa97ee16c49cfd2432eb32ba5738fa",
"a1fc40009357a024d878818cf6f979a88d4cc5d760b308ae1a5b9f067972e6f7cf92ddb129a8d3c1bb0005bcf3f8871fd65e794f1990b7",
"",
"1658a59fbc9c22c17f5a8b55c1275795873e4beae49657421ad5d95831b736cd7e066c738bcbb343933c411c7c17917593c03a77bed56b",
"",
"56f33d4fdbb9a5b64d26234497e9dcb87798c68d08f7c41199d4bddf97ebbf6cb5550e5d149ff4d5bd0f05f25a6988c17436396227184af84a564335658e2f8572bea333eee2abff22ffa6de3e22aca2",
"b854e5a04ff3c2e657d30ce2b820d13e148b11c245495ff03531785eab2a1dc54d994a5597b15c5b10001f49606c88b4ff0d61acb61820",
},
{
false,
sha1.New(),
"d9bab5cedca96f6178d64509a0dfdc5e",
"dad8989414450e01",
"",
"5e07c6b72aaa5afcaab1cc3929239debde7f590886ba5bf558b90345f8518cb87a2bccdefa0c22283538e505efdaf2bd643059fd842106",
"362aafd121de087197466e77b9bf6924841c01bd5fa98d6bc0a75b95d91166ec80e1516a10fff3216a7ad0b0c6e4f4d9708ccd69677134",
"c6bad074c5906786f5e1f32099f5b491",
"3e6bf46f4daa3825d7194e694e7752f7",
"66165aed47c55d963e25aa856553e0a5a590ed06e3cec66254c6a3d8ac8b30da6b334145c466a025b445938d84151bbdbe1509e1cc7189",
"bca1bfd5a1c718d53cd73eb584eedc19d5a3396bf558f659ae673106d0abe1f194e695ca67c2e8ddc8ee95ace21e6b12751faa695ac727",
"04fa2895aa5a6f8c5743343b805e5ea4",
"22b81ac2e98c766b7afce93aea42bcbf7b342672d927bcbc032dd4df7d3712cc0019d750e811a157c71db2340f6d022bd498dbd4dd4669",
"df5dc459dff02aa2f052d721ec607230",
"c48b89f9da3f748245555d5d033b693dd71a4df5690205cefcd720113cc24e098936ff5e77b541535870b339468cdd8d6faf8c56163a700a75b23e599b5aecf16f3baf6d5f2419971f24f446720feabe",
"df59da988b538f40b7d427f06f3198d950d75fdece80b315b19505e64de2f4bd95006d7c6d774e39237115e40aca2d4a88ddec412b67ee",
},
{
false,
sha256.New(),
"63363377e41e86468deb0ab4a8ed683f6a134e47e014c700454e81e95358a569",
"808aa38f2a72a62359915a9f8a04ca68",
"",
"32ab605ddc8d5651093b8a59bd9d3adea1249e21a69e2e4a3967515fa03ad41ccf5b126eb9f3b268080c952df88241fe4cc27bbcbbbed5",
"8ea2691d1915ebb4975593ca3fbad0ba137026d901a95950a207c41dc7773e15c1e85f4a5f91002866830bebe5c4ee1785b839323fbb44",
"e62b8a8ee8f141b6980566e3bfe3c04903dad4ac2cdf9f2280010a6739bc83d3",
"",
"59177d93843f0550f33933a51eb488168699ab9c85651536a61f7ec71e8b274a151f17e56becaf531dcfc955f2f1adb6536d51b256d53c",
"897c02699f4254e1f33c94f7bfa85da3826df6c2590ed0815cbced36d77aa3375a1582ffc1c887416afd1ba0f04b6ddff81a2b0e5b844d",
"",
"e2937ffd23815a32e675c89cde5ce5ba0907a25ede73e61c9ec76d67da582c94001fda32b60ec40202a164c6a4d66411cc6b99b1284617",
"",
"04eec63bb231df2c630a1afbe724949d005a587851e1aa795e477347c8b056621c18bddcdd8d99fc5fc2b92053d8cfacfb0bb8831205fad1ddd6c071318a6018f03b73f5ede4d4d071f9de03fd7aea105d9299b8af99aa075bdb4db9aa28c18d174b56ee2a014d098896ff2282c955a81969e069fa8ce007a180183a07dfae17",
"6c0f8266c2c3af14d9b25d949e05435d8b7599213782b6eac6cd90a10d48e1c96088f5dba20241b68cb64bb05028c35e5558ef8a6edca6",
},
{
false,
sha256.New(),
"9cfb7ad03be487a3b42be06e9ae44f283c2b1458cec801da2ae6532fcb56cc4c",
"a20765538e8db31295747ec922c13a69",
"",
"8037eb9f243343f8af8c756475ea998f47a487c64dfad9945391004b08cf1a9102d4669492f554b543d820f18a90f453ad53acaf39f0c9",
"ed540b209e044dc2591923883c9a3b1b7c265bc053c40aa91971b09be4d3b3034b05f197a09c6339c7c16de14a20e29ea17bf11cbdb248",
"96bc8014f90ebdf690db0e171b59cc46c75e2e9b8e1dc699c65c03ceb2f4d7dc",
"6fea0894052dab3c44d503950c7c72bd7b87de87cb81d3bb51c32a62f742286d",
"cf9d4dd8a2c4fb507addbe849643acef2bcf6a4403082a026d50371bc7f2ea9d3975790238af78b750ef0334b7e42e0b1e71aeb97c6029",
"e16ed4378e0342deff3003334eae72709c31f5b4004ab9870ee73a6ab4c7eb6f18027c717bf8c94ccc1e06ce5a3afaacb431e2f860f7ed",
"d3467c78563b74c13db7af36c2a964820f2a9b1b167474906508fdac9b2049a6",
"b10c221030c83e2f7a0dc1b7e4f21f5fc8015ff80352e416298fcc88847c8d0ca970964fbaa83f411e07fb6d6ac42b95a2c1abce0fc285",
"5840a11cc9ebf77b963854726a826370ffdb2fc2b3d8479e1df5dcfa3dddd10b",
"71c1154a2a7a3552413970bf698aa02f14f8ea95e861f801f463be27868b1b14b1b4babd9eba5915a6414ab1104c8979b1918f3094925aeab0d07d2037e613b63cbd4f79d9f95c84b47ed9b77230a57515c211f48f4af6f5edb2c308b33905db308cf88f552c8912c49b34e66c026e67b302ca65b187928a1aba9a49edbfe190",
"927af647becb810e793dc4eb33a091d0643355ac039d9e1e4d60a2ac023dca791d46f5e560b237047371aa1d629988772af7b96c0d0a07",
},
{
false,
sha512.New(),
"3144e17a10c856129764f58fd8e4231020546996c0bf6cff8e91c24ee09be333",
"b16fcb1cf0c010f31feab733588b8e04",
"",
"3a85ca10eac683d6a9270594d17f33a21dad7b9b259c2a174462a5e0c909a133db84b4ee2bdb0f72cdcef7d62854e535468452285dbe8e46bed3965dc9c66952defa48879493edc01bc07ed4973c115cfdd9947a708465351b78b804652ec7cbe7f6e2a09193fa352ff991d38c94ac",
"74ea437c49126ff361feab5639a8ad318d455c94b3f999ff1606f592c27f8bf0be562c7bffa297de8512ef44b0dfc8db5cb17c9692ac0d80f066961e6426084108089eee4a759d5309ec861668ddeb1c31ceef26edad678b6f36c3ebcb9c936cafcee3d9a96ae6554e22d42888ab07",
"a0b3584c2c8412f618406834404d1eb0ce999ba28966054d7e497e0db608b967",
"",
"b37f9aa39c5a80df56c040402407960ef6f8892d1a688ffc93bad6ebe6af44d55ccd66c1f44eb531e9dac1c9447681d7b27b2b703b490032696b32330b5edd123e5ece7c40efe70a29822ea8e4e454bb72085c6b037a8652ec227f899dd01455db8ee7b6b2e92114f6f9fb678e6332",
"908ad858db2c5d21fa1cd860217bd75ad0ba1df2fd24e303964c01113a0b024a1e53640d5ae339040b4357c1f3c0be2f14607b1385e968183c53ecd9a33ddb04b3ac36dfc1353d8571159a0b31b81b5d3de24b8ae6530c838fa8712ea5d4d58763f2be0ab1989987c56bfd315df521",
"",
"440a72fc7786de0150dd18a045836d69c7b2a720178d73002a06d7fd20ba471f7b20cacf4f31ee35f51e198b383740fb34724a0747e261c800fa0f744bdc842d37199f6acf5f4af041a6600878cf72a7ceaa750fa1c23546f962afe97c055683eaf5131d9f9c882edb93c50adba963",
"",
"efa35dd0362adb7626456b36fac74d3c28d01d926420275a28bea9c9dd7547c15e7931852ac1277076567535239c1f429c7f75cf74c2267deb6a3e596cf326156c796941283b8d583f171c2f6e3323f7555e1b181ffda30507210cb1f589b23cd71880fd44370cacf43375b0db7e336f12b309bfd4f610bb8f20e1a15e253a4fe511a027968df0b105a1d73aff7c7a826d39f640dfb8f522259ed402282e2c2e9d3a498f51725fe4141b06da5598a42ac1e0494e997d566a1a39b676b96a6003a4c5db84f246584ee65af70ff2160278166da16d91c9b8f2deb02751a1088ad6be4e80ef966eb73e66bc87cad87c77c0b34a21ba1da0ba6d16ca5046dc4abda0",
"d4954b5552b33b234af9f10066ff44c4986cc51314b25603c052d90e5ac5496999742edcaa15273a0061714d2bf7ffb32b7000bfdeb10605f36174eb33a48a4cc007c23bb03597b4d8a6373ca7037e8a8ff08f63779da9e61878b1886cb084ba68ceef8ad4e5ba7720acbd3b262822",
},
{
false,
sha512.New(),
"c73a7820f0f53e8bbfc3b7b71d994143cf6e98642e9ea6d8df5dccbc43db8720",
"20cc9834b588adcb1bbde64f0d2a34cb",
"",
"852d1b4fdd41cd7e0f597c45c8e4b401a5fecf9229b6072451ca65b5289882c686e7919922ce82de2faac83cd4c4eddfa2cdcf6244a4d2acdd34c0232136409bb50ea24d0c33fcfd1aaf1cc110b5353d32e4e6df59ae25ec124000de62fcfa8bb4cb3f3b72e2da2066ef00cd66d9e9",
"f7b0c9cf2ccf58fd8c8b69daa4cf24a874c95b57a9f5be16aaa71ec30070ac8f222fe21788fec14b8a9ad7ad20912c05a6f94548646779a16c787b135ce8d08c49f7e234cbd2c7733571f5ad6479b5fc50403496581b4861ef8ec848affbd2077ab164fc6bb2dd7b008a650504bfd8",
"12dd2aca8879046d23165c60f8aedc20415783e156d42a94346826aaeb02eacf",
"9b59ff78a34eabe0060c2792ca9b49e9781e6b802badf7dbde27caaed3343706",
"181a302352d9ebf0b669730b2441a9f4c16a4b9d25ebc84ed01c460d293cd3e8b7bff1aca32b0ea8d281df0ef8d1ae09d4cf97690c944f4713adb9ede90763f3ed77081c37c0fc60f8b60b5108cf6276c80db14a82aaef1bf8da03781445cfcc7cdc02b1c7a2740874dd948118f7ef",
"28b638d631f054eba562320e9d151f905863dd6c04d8ba41167bcf3b0236d4e5dde1dc7bf690e61b4a65997bd9c67ff908fe7e2443d01c8eac15b2ea5c80ba89f09aa9b8a81d56124bb71586812827f463de90318727102dbd5e59ca5f1af78ab73844695eee0977b754854e525097",
"dc74a9e480a6ff6f6bce53ab9c7bdde4b13d70fb5196cdd5e3a0555ccf06fe91",
"40d068f984ca40dc5bcba519c156c98519ce29092ac4828fe69815482b73a8ce95a1ce2899bbf4c41ce7788ad2982e3cea3266f4cadc50ae528dc61aa7c521489869e3efc6c82ccefbbab45673e0f59d5654cf910fa146d984a42c5f17fb60340c86d0d07c7e2f2e6df3cffd722a0e",
"8f3f229011209b2f399096afb054bccca6bc46aaee98845838fb1fb78b66f3bd",
"e6c96442582811ec90e587525f36c555e2fd6361a0c5b0284917a4fa6f6e8ace83f11a1fb26cea6692b225ae7c5be286dd27471f323d7a2e4431722bb337b1ba0e648ea2e9f0918b50e9111f2377636ba69b0e1cb5295078d76c549c8656940eb15ca5aded7adc46e6fa4b86948f212fea3f3befdeece8b20e420ca84c760196ddf0b074df0a9f097a5db8f6125800f5fe746a62df1208042f1255b524465a17efcf6a537612968430e2adcff30f7407a51ed7305334384e512e003642cca175636819f021c76a2f44e89e6fe39cf164477910379cd314f735c357f9379de22495276b401c98ffb09a6dc03e484b355a9464511401eeaa05b4556e73b55227f8",
"6986a1cfb6ba95c8012dd7285e6be915723206752f9d3cd0fd13e4832daa7db47383aaa4904cdadf674d1206ac5eafa99de1304fc0b6a1b5e32e34a7f4141e89353878c0d3f6a0ba5b9ed452d61260de9e5acbf8134485b3b9e990f59f34d4d43307e40ad0d0a505efdb24b72f807b",
},
{
true,
sm3.New(),
"63363377e41e86468deb0ab4a8ed683f6a134e47e014c700454e81e95358a569",
"808aa38f2a72a62359915a9f8a04ca68",
"",
"7a070cb0c9806fe6a74c0396825642690fc458e76397b75b5977e8a5e693687a703b08c8fbb9e9a600d181c530e889336390818ca2c271",
"218bcd7e3a836cc2e275e660a29fab9c1b3e5596275d8f3141f8c7fb3b285b9edda112e7e85e8492c9b4ed9c1ea466b00cce3be9c10a03",
"e62b8a8ee8f141b6980566e3bfe3c04903dad4ac2cdf9f2280010a6739bc83d3",
"",
"99ec25b65124082695c773d8ef03cc2b31a916956bb582752399c3951c9956d21a73a08091f47401e7133844ea6544361961c7cc4d7e6e",
"c7d8714c59bce8c8b033b99ce3577acd68a3b10151cc0a60c4b2790e56ce47592574d6909fab7fc551d33402f364aaa4d6fcc83989b9c7",
"",
"61c49702aae0f0ef45fb2d75d25b46f89a4cc796bd818d0c7bd0a23a3938e9c81783160d900852f33241399b1a69f7c80bc9865e182676",
"",
"00d98d35a2fab8df23e9e1fb9aad143d62c0759eb79e15c37e8f2bc5064e68da",
"299d084f049dd9b7f62ee712b5b2c1c602f078980f4d9816d8f2baf38765be984b6c493497af30f68a56072404f27e45af419d04eb9e35",
},
{
true,
sm3.New(),
"9cfb7ad03be487a3b42be06e9ae44f283c2b1458cec801da2ae6532fcb56cc4c",
"a20765538e8db31295747ec922c13a69",
"",
"997cd31a7032c8643ca56de1d34ff4f930b13192e17c8947bcaf9b9d010cf79805511255c7ea18b41cde77e491ca943861ec29780f3f36", // v0
"4c5b167d27fa9d40cbc45d0d9f3c52504a1cb5aa2f37a3fa812037bd1e458412ecff0641dd5cb2785d0f8044151b42842777211547b457", // c0
"96bc8014f90ebdf690db0e171b59cc46c75e2e9b8e1dc699c65c03ceb2f4d7dc",
"6fea0894052dab3c44d503950c7c72bd7b87de87cb81d3bb51c32a62f742286d",
"e3d804eda66df62f425c41047c5812fca471c8236395c92d4bd834c2e52d606be6ad3da8973df16e8567bcb16e45f2842ace91bf6dfeb3", // v1
"1a1b2fffca26953626e0fa3afd377e14e63c5e81275b39f1436b707efb2c3059e6ced8fdb238a45bf05aae9f2417dbf5c4f89d3772f324", // c1
"d3467c78563b74c13db7af36c2a964820f2a9b1b167474906508fdac9b2049a6",
"fdf334ed70948b65693d3b3f798f91118aae26a48af1045be386984b75695902870479c3c53593d332d195ee7f1bed45fc5069cc4f9948", // v2
"5840a11cc9ebf77b963854726a826370ffdb2fc2b3d8479e1df5dcfa3dddd10b",
"48709db5509a03d6131775fbbfe74fe52611e760d22fde61e274a295f4354d67",
"180e64ed3abb209b901e357a76c70f2670ea8525b24c401d146b70b178e33d8fd10f7680c50bbe1f9773ba664dd14cd25d329c380bf399", // v3
},
}
func TestHashDRBG(t *testing.T) {
for _, test := range tests {
entropyInput, _ := hex.DecodeString(test.entropyInput)
nonce, _ := hex.DecodeString(test.nonce)
personalizationString, _ := hex.DecodeString(test.personalizationString)
v0, _ := hex.DecodeString(test.v0)
c0, _ := hex.DecodeString(test.c0)
hd, err := NewHashDrbg(test.md, SECURITY_LEVEL_ONE, test.gm, entropyInput, nonce, personalizationString)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(hd.v[:len(v0)], v0) {
t.Errorf("not same v0 %s", hex.EncodeToString(hd.v[:len(v0)]))
}
if !bytes.Equal(hd.c[:len(c0)], c0) {
t.Errorf("not same c0 %s", hex.EncodeToString(hd.c[:len(c0)]))
}
// Reseed
entropyInputReseed, _ := hex.DecodeString(test.entropyInputReseed)
additionalInputReseed, _ := hex.DecodeString(test.additionalInputReseed)
v1, _ := hex.DecodeString(test.v1)
c1, _ := hex.DecodeString(test.c1)
err = hd.Reseed(entropyInputReseed, additionalInputReseed)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(hd.v[:len(v0)], v1) {
t.Errorf("not same v1 %s", hex.EncodeToString(hd.v[:len(v0)]))
}
if !bytes.Equal(hd.c[:len(c0)], c1) {
t.Errorf("not same c1 %s", hex.EncodeToString(hd.c[:len(c0)]))
}
// Generate 1
returnbits1, _ := hex.DecodeString(test.returnbits1)
v2, _ := hex.DecodeString(test.v2)
output := make([]byte, len(returnbits1))
additionalInput1, _ := hex.DecodeString(test.additionalInput1)
hd.Generate(output, additionalInput1)
if !bytes.Equal(hd.v[:len(v0)], v2) {
t.Errorf("not same v2 %s", hex.EncodeToString(hd.v[:len(v0)]))
}
// Generate 2
v3, _ := hex.DecodeString(test.v3)
additionalInput2, _ := hex.DecodeString(test.additionalInput2)
hd.Generate(output, additionalInput2)
if !bytes.Equal(hd.v[:len(v0)], v3) {
t.Errorf("not same v3 %s", hex.EncodeToString(hd.v[:len(v0)]))
}
if !bytes.Equal(returnbits1, output) {
t.Errorf("not expected return bits %s", hex.EncodeToString(output))
}
}
}
func TestGmHashDRBG_Validation(t *testing.T) {
entropyInput := make([]byte, 64)
_, err := NewHashDrbg(sm3.New(), SECURITY_LEVEL_ONE, true, entropyInput[:16], entropyInput[16:24], nil)
if err == nil {
t.Fatalf("expected error here")
}
_, err = NewHashDrbg(sm3.New(), SECURITY_LEVEL_ONE, true, entropyInput[:32], entropyInput[32:40], nil)
if err == nil {
t.Fatalf("expected error here")
}
hd, err := NewHashDrbg(sm3.New(), SECURITY_LEVEL_ONE, true, entropyInput[:32], entropyInput[32:48], nil)
if err != nil {
t.Fatal(err)
}
err = hd.Reseed(entropyInput[:16], nil)
if err == nil {
t.Fatalf("expected error here")
}
}

View File

@ -1,155 +0,0 @@
package drbg
import (
"crypto/hmac"
"errors"
"hash"
"time"
)
// HmacDrbg hmac DRBG structure, its instance is NOT goroutine safe!!!
// The instance should be used in one goroutine only.
// Thera are NO hmac DRBR definition in GM/T 0105-2021 yet.
type HmacDrbg struct {
BaseDrbg
newHash func() hash.Hash
key []byte
hashSize int
}
// NewHmacDrbg create one hmac DRBG instance
func NewHmacDrbg(newHash func() hash.Hash, securityLevel SecurityLevel, gm bool, entropy, nonce, personalization []byte) (*HmacDrbg, error) {
hd := &HmacDrbg{}
hd.gm = gm
hd.newHash = newHash
hd.setSecurityLevel(securityLevel)
md := newHash()
hd.hashSize = md.Size()
// here for the min length, we just check <=0 now
if len(entropy) == 0 || len(entropy) >= maxBytes {
return nil, errors.New("drbg: invalid entropy length")
}
// here for the min length, we just check <=0 now
if len(nonce) == 0 || len(nonce) >= maxBytes>>1 {
return nil, errors.New("drbg: invalid nonce length")
}
if len(personalization) >= maxBytes {
return nil, errors.New("drbg: personalization is too long")
}
// HMAC_DRBG_Instantiate_process
hd.key = make([]byte, hd.hashSize)
hd.v = make([]byte, hd.hashSize)
for i := range hd.hashSize {
hd.key[i] = 0x00
hd.v[i] = 0x01
}
hd.update(entropy, nonce, personalization)
hd.reseedCounter = 1
hd.reseedTime = time.Now()
return hd, nil
}
// NewNISTHmacDrbg return hmac DRBG implementation which follows NIST standard
func NewNISTHmacDrbg(newHash func() hash.Hash, securityLevel SecurityLevel, entropy, nonce, personalization []byte) (*HmacDrbg, error) {
return NewHmacDrbg(newHash, securityLevel, false, entropy, nonce, personalization)
}
// Generate generates pseudo random bytes usging HMAC_DRBG_Generate_process
func (hd *HmacDrbg) Generate(output, additional []byte) error {
// Step 1. If reseed_counter > reseed_interval, then return [ErrReseedRequired] that a reseed is required
if hd.NeedReseed() {
return ErrReseedRequired
}
// Step 2. If additional_input is provided, then do update
if len(additional) > 0 {
hd.update(additional)
}
requestedBytes := len(output)
md := hmac.New(hd.newHash, hd.key)
for ; requestedBytes > 0; requestedBytes -= hd.hashSize {
// 4.1. V = HMAC (Key, V)
md.Reset()
md.Write(hd.v)
hd.v = md.Sum(hd.v[:0])
// 4.2. copy V to output
copy(output, hd.v)
if requestedBytes > hd.hashSize {
output = output[hd.hashSize:]
}
}
// Step 6. (Key, V) = HMAC_DRBG_Update (additional_input, Key, V)
hd.update(additional)
// Step 7. reseed_counter = reseed_counter + 1
hd.reseedCounter++
return nil
}
// Reseed hash DRBG reseed process. GM/T 0105-2021 has a little different with NIST.
// reference to NIST.SP.800-90Ar1.pdf section 10.1.2.4
func (hd *HmacDrbg) Reseed(entropy, additional []byte) error {
// here for the min length, we just check <=0 now
if len(entropy) == 0 || (hd.gm && len(entropy) < hd.hashSize) || len(entropy) >= maxBytes {
return errors.New("drbg: invalid entropy length")
}
if len(additional) >= maxBytes {
return errors.New("drbg: additional input too long")
}
hd.update(entropy, additional)
hd.reseedCounter = 1
hd.reseedTime = time.Now()
return nil
}
func (hd *HmacDrbg) MaxBytesPerRequest() int {
return maxBytesPerGenerate
}
// The HMAC_DRBG_Update function updates the internal state of
// HMAC_DRBG using the provided_data. Note that for this DRBG mechanism, the
// HMAC_DRBG_Update function also serves as a derivation function for the
// instantiate and reseed functions.
func (hd *HmacDrbg) update(byteSlices ...[]byte) error {
// step 1. K = HMAC(K, V || 0x00 || provided_data)
md := hmac.New(hd.newHash, hd.key)
md.Write(hd.v)
md.Write([]byte{0x00})
length := 0
for _, bytes := range byteSlices {
length += len(bytes)
if len(bytes) > 0 {
md.Write(bytes)
}
}
hd.key = md.Sum(hd.key[:0])
// step 2. V = HMAC(K, V)
md = hmac.New(hd.newHash, hd.key)
md.Write(hd.v)
hd.v = md.Sum(hd.v[:0])
// step 3. If provided_data = null, then return
if length == 0 {
return nil
}
// step 4. K = HMAC(K, V || 0x01 || provided_data)
md.Reset()
md.Write(hd.v)
md.Write([]byte{0x01})
for _, bytes := range byteSlices {
if len(bytes) > 0 {
md.Write(bytes)
}
}
hd.key = md.Sum(hd.key[:0])
// step 5. V = HMAC(K, V)
md = hmac.New(hd.newHash, hd.key)
md.Write(hd.v)
hd.v = md.Sum(hd.v[:0])
return nil
}

View File

@ -1,804 +0,0 @@
package drbg
import (
"bytes"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
"hash"
"testing"
)
var hmactests = []struct {
gm bool
newHash func() hash.Hash
entropyInput string
nonce string
personalizationString string
v0 string
k0 string
entropyInputReseed string
additionalInputReseed string
v1 string
k1 string
additionalInput1 string
v2 string
k2 string
returnbits1 string
additionalInput2 string
v3 string
k3 string
}{
{
false,
sha1.New,
"79349bbf7cdda5799557866621c91383",
"1146733abf8c35c8",
"",
"4d9c202854ec825e28c032c0c1d8971f658a2b04",
"900918d4a6f2a3e54f62a6b03bcf372de6f8b2a2",
"c7215b5b96c48e9b338c74e3e99dfedf",
"",
"fa5315383a3e5e8e9591667982984353b7a0a8bf",
"45467ed1cc9435d5a1683cec9d8533864ec58202",
"",
"c0e51987773054629ee998cbab5e77478ba960c7",
"ea1ae579b6b071ffbda9d6fe2346c86a32b60a4c",
"c6a16ab8d420706f0f34ab7fec5adca9d8ca3a133e159ca6ac43c6f8a2be22834a4c0a0affb10d7194f1c1a5cf7322ec1ae0964ed4bf122746e087fdb5b3e91b3493d5bb98faed49e85f130fc8a459b7",
"",
"f26b962d0e43a5587031297607079c387fc111f3",
"2af498a9c9387c8cf02b552084cd3aafd23afd2f",
},
{
false,
sha1.New,
"ee57fc23600fb9029a9ec6c82e7b51e4",
"3e9721e4393ef9ad",
"",
"aaac207d2258bf970c58549e8f6e210ba211107f",
"8aade97aacb843b6256ccfa694514a79d4e37d4c",
"841d276ca9519061d92d7ddfa6628ca3",
"",
"56768fe7ba06c56c2c3ee1dff126b70f0e833304",
"c988036512bed2d33a34dc2394bfe37dfd6ff387",
"",
"1d6ada6bfba009849de2398cb55a6efb1ff6bf13",
"a7069625d1b3c57e515511494660bc0402fb64ed",
"ee26a5c8ef08a1ca8f14154d67c88f5e7ed8219d931b9842ac0039f2145539f2142b44117a998c22f590f6c9b38b465b783ecff13a7750201f7ecf1b8ab393604c73b2389336609af3440cde43298b84",
"",
"962cbd9a0e0d025d319f5c5cfe8bded24fe9a79a",
"c5940ea824e876ce9484448ab5672762e10a78c4",
},
{
false,
sha1.New,
"ebfdad13c8f941d279dbb4de8d7706dd",
"fdaa279f5e4428d6",
"",
"5cabbe2d80eed1ddf1b1e42c1264ba79eba1284c",
"dcdb91176e0ce05e3ae661269284b1b284f6997d",
"f785c5b2f833b69b09b71a57cf5701d4",
"",
"c209a646a0a790d5b7b4e1f1673ae5d056b334f9",
"c5d38ec73619b570dbc9c0f0a18961af10089209",
"",
"a957a52715a8d14d83509e7541224c2e8ada2fff",
"19ac7d4a6f57ab5cc32082f0c724072db630509c",
"66e35f9b8e05a861a0b3d01c66c416d5e8b77d4d21328c625cff9163ffc92e753015aa9d7f36ae3a961681d39f271d0b627787868cec3dedc520ecb303f96a43cec67369117af268a19f5284880cb3be",
"",
"d856adf8f9107ac94f100517407fdeb01e7c6f14",
"97ac5e50aa2519a630283677b71fd5480c84f3df",
},
{
false,
sha1.New,
"7d7052a776fd2fb3d7191f733304ee8b",
"be4a0ceedca80207",
"",
"8bd11c639be35d012b8fa9358b25b8996b100b0d",
"1b64cef03525dd4e7584bcb7c797e0c5542237ab",
"49047e879d610955eed916e4060e00c9",
"fd8bb33aab2f6cdfbc541811861d518d",
"5c754b7134f409d22552a30aa674b1b3a0a590a7",
"162373a8ebda0cf1bdc501cc38ef90b659becf52",
"99afe347540461ddf6abeb491e0715b4",
"c4ecfaadba781a1c10b7756470bbbc1eff813a08",
"2b2af03fbe60d589215434ebded5945d6a0f8782",
"a736343844fc92511391db0addd9064dbee24c8976aa259a9e3b6368aa6de4c9bf3a0effcda9cb0e9dc33652ab58ecb7650ed80467f76a849fb1cfc1ed0a09f7155086064db324b1e124f3fc9e614fcb",
"02f773482dd7ae66f76e381598a64ef0",
"15d2c57b07db598f83f07d67b23c6772c3ccf4b2",
"02658247d79451981931d4bc5d838d42065385bc",
},
{
false,
sha1.New,
"29c62afa3c52208a3fdecb43fa613f15",
"6c9eb59ac3c2d48b",
"",
"ca5a106831ab507cae0fccc9067bfbf0a553f9d0",
"1bd9518f24761b1131275ade489516db2c8724eb",
"bd87be99d184165412314140d4027141",
"433ddaf259d14bcf897630ccaa27338c",
"7060974f64c4c6dd6c2991a7712781b3710e6de2",
"322745157f68853ce481ce599d03dbd115af01cd",
"141146d404f284c2d02b6a10156e3382",
"16b255c1dd94882e34b1a5630dc1dca51bf93474",
"ca6715ddd201caa186e7c6bba1306b15c2b1e92a",
"8c730f0526694d5a9a45dbab057a1975357d65afd3eff303320bd14061f9ad38759102b6c60116f6db7a6e8e7ab94c05500b4d1e357df8e957ac8937b05fb3d080a0f90674d44de1bd6f94d295c4519d",
"edc343dbffe71ab4114ac3639d445b65",
"aaa83705d128b5e8b175da966c3dad1cb73a72ab",
"af019a6ba7dfee072efc9735e5288ac1431c209c",
},
{
false,
sha1.New,
"11c0a7e1472cec70fa8c1ca15759ac5b",
"b1c73c22db39cd7b",
"b24e392cb1f3c18af2cb50feac733e32",
"19a03422ae972aa8b3c6f9f06cd8d67cf32c1bde",
"bdd113dc2a898f2ee086769f85b9d510d10db72b",
"c6ab59ff708a5c1f598e75df060e1981",
"",
"dc831116739734241418a975dcac594eb13c6370",
"62a245070b0c8bf07570be54f1aa02756992e140",
"",
"db1b24a92bbb6d7638ce376f9ec0df515291a196",
"83dd36854e46c3b04e86ea0d8685ab054c924cad",
"070e603cd48d56430a5ab461a751ec2a4a6aa6fb6ee52efe9a41e4611eafdfc957184b47bbb017e484ac34c7de56cd7813feb301b5befce573ad0a254e6cfe35b77c30be6b7cb5e7efa72813c7546ba5",
"",
"d3ab4ccd28b9104be57eac8b6fa293497a1ba273",
"7882dba03b6522f7094f2de9eb7b47b2ebb004cc",
},
{
false,
sha1.New,
"e05141adb678c297eebd8136885b6734",
"5b9c0c54a0ff74d8",
"4814ea71a8e11845716b22085cc65f2b",
"c3a9671b90438f5d8cee8c323cc653d42737eedc",
"efcd5a168654b32131b5fa0d98017ff0787f36a6",
"26e26c9323a3da3af6e5a5a1f351cb54",
"",
"8c65b742f0fee1b4a632fed1f004efb22e2eb591",
"c12d25d75dbdaf325e6306b666836208b13ca6ed",
"",
"f8bb4e888520ff5caa66df0ee1ddc3546e467ebc",
"c0ccd43632a814890fc6a2c98cf57d896f502c66",
"5ef29a2e7e821d529d1928e6bab16fb80d6491a98dd53695473dadead4e5142c146f1e29b101c6b1a57d8315ce34db17040c02572c6455d902303dcfcb2ad3052166de790ce0c94af78a51864efd4b12",
"",
"11058e5edd748bbd315c89ebefb4e3b72d05841e",
"19679a8f39c1888208bef66a13dda0ffd96fb79e",
},
{
false,
sha1.New,
"03e7b41c95818eb0b667bfa8a175a824",
"66a1e417a9b6b92f",
"126dded5eb0bc81be37c10bcd9d5f793",
"9671884e9ddb848ea8dd2908b5deccc69b67e302",
"a354572eb8e7e09758fb4a084be292a7670ee014",
"d17e98c2e50ee0db00d25c3364451e95",
"dc596d188e2343802240bc7f5cc60516",
"cfac259ba4dbb8b30300438cb20f96a2ec588530",
"3ca61b3a111c37639973d655a5097196928ecb31",
"14c8ec10f5bdde6b9e75898d7f9f03d0",
"3d8c62b18f1f9150767a2ef3e6edd918ca4ed183",
"c22f4f421a53fd7f7449e873f6c0e0ca1b6ba74f",
"4739b1bcf87404a2290829bd7a61f0b391a794c71c055c7cc513b28dcb5fdc88645bc9cb490f41fab134c6b33ce9336571762754343961de671b02a47960b4b4e23c5bfb87dcc19b260b3bcb921ae325",
"31aa842afcc1daa94098241a87d6ddfc",
"0904e5dd8a54b62532e2d5eaea04f3d168af632a",
"3654b36c6618fe7a37de46ee21e7546039f96162",
},
{
false,
sha1.New,
"e5a81357b91215dda0a0986a3ff5123f",
"9c1838d7360e674b",
"",
"f6debe6572dd2754a1928efaadb1c4d79aa8603d",
"b79e1aac4cb1e531309518cb4ec6b42993b61238",
"592dd232ac83db36abbacfb8c640dc60",
"",
"acb23f384f6ab63b51355dfc4875d60b68c5d0d4",
"878895412d3bde709daf05406e6aeecf81e37b53",
"",
"2160f1b0ef75fffc2bc81621ea4035234ecf5244",
"1383125bc283e0d9fbd5f957fd57fde07edf8269",
"a4d1843c7af208f770f8b5acf64528866d51e731ed5ebc756e81efe8fb8f9c9af89d52e8e8d1c0141ebebc18b1ca78c78a2f21cb909d5bac3b6ae60c4a2ff4176b14905e4afa3ba9e458216d5720ce83",
"",
"e39a379bfb91f673476c171fcc179e1547c3a033",
"4cc81aa1ca4de9831295d86671594a78f5074b81",
},
{
false,
sha1.New,
"12bfb2bff5781128967e9ce4d429453c",
"5aab2be4e92ec855",
"129126c16d99a684f3cb47e7ffb207ac",
"c4520b5effa4fd7825a846cc49ca3941cd0675d8",
"68a71d4eeaa08771dd960e6fea10046f092649bb",
"fa8b0da2cd5031433d64310ba44d47e4",
"",
"9145bfe03440396ea954f1084e9a0d0047735243",
"52b6c07b6cf17d94a80d1e51fbed0d5ca71a74bc",
"",
"64f6d3aa23e7e8cb86ac115d68bbeef3efffe6e1",
"0c373021c29c578e6d25b0594814f8c9ddb9330b",
"766c4a1670dee63ee3aafb6039e86ec5ca2890531974e70fedb3c0218b08faca07ddfaaff0a77f0ae47762b0e1b31c5fb036dca413fb96f6e0ec6ab7dbcbb558aa5d94f429c38e0fd7b39f12721c9935",
"",
"6fd14126a142d1e5c03f0e5ec37c1a3eafd20e6d",
"c775929a2b39c80b63cf796133b8995ef1548c38",
},
{
false,
sha256.New,
"06032cd5eed33f39265f49ecb142c511da9aff2af71203bffaf34a9ca5bd9c0d",
"0e66f71edc43e42a45ad3c6fc6cdc4df",
"",
"81e0d8830ed2d16f9b288a1cb289c5fab3f3c5c28131be7cafedcc7734604d34",
"17dc11c2389f5eeb9d0f6a5148a1ea83ee8a828f4f140ac78272a0da435fa121",
"01920a4e669ed3a85ae8a33b35a74ad7fb2a6bb4cf395ce00334a9c9a5a5d552",
"",
"c246fa97570ba2b9d9e5b453fe4632366f146fbd8491146563eb463c9eafe50c",
"ca43e73325de43c41d7e0a7a3163fb04061b09fcee4c7b8884e969e3bdfdff9a",
"",
"df67d0816d6a8f3b73ba7638ea113bef0e33a1da451272ef1472211fb31c1cd6",
"8be4c7f9f249d5af2c6345a8f07af14be1d7adc2b9892286ffe37760d8aa5a1b",
"76fc79fe9b50beccc991a11b5635783a83536add03c157fb30645e611c2898bb2b1bc215000209208cd506cb28da2a51bdb03826aaf2bd2335d576d519160842e7158ad0949d1a9ec3e66ea1b1a064b005de914eac2e9d4f2d72a8616a80225422918250ff66a41bd2f864a6a38cc5b6499dc43f7f2bd09e1e0f8f5885935124",
"",
"80524881711e89a61e6fe7169581e50fb9ad642f3dff48fba5773352fa04cec3",
"5ed31bc06cc4f3a97f7f34929b0558b0c34de1f4bd1cef456a8364140e2d9f41",
},
{
false,
sha256.New,
"05ac9fc4c62a02e3f90840da5616218c6de5743d66b8e0fbf833759c5928b53d",
"2b89a17904922ed8f017a63044848545",
"",
"eaa29892ee1e46198ea68c07588ac12641fc901e484eda321c2f26a9ff328e3d",
"8d3006bd33b7d8b935a8484b786850f107b731a7efc51521848b875c2214d154",
"2791126b8b52ee1fd9392a0a13e0083bed4186dc649b739607ac70ec8dcecf9b",
"43bac13bae715092cf7eb280a2e10a962faf7233c41412f69bc74a35a584e54c",
"25d3b766cd9f8ad5c45efd7fa01cc08dbce8d0d3792ec2b59bfead7bce39ed01",
"3138df070c49f48b080004df669f386676b4cb92b40de4d021b2a9e4451e5013",
"3f2fed4b68d506ecefa21f3f5bb907beb0f17dbc30f6ffbba5e5861408c53a1e",
"06624fa590e1d63397b13a0e69081274434f793fdfc1e6298a7373834024da46",
"cac850b111de755bb8a5ed1ebc052ed53ab1ff1b9d0fab2946a3728c7e9f43e4",
"02ddff5173da2fcffa10215b030d660d61179e61ecc22609b1151a75f1cbcbb4363c3a89299b4b63aca5e581e73c860491010aa35de3337cc6c09ebec8c91a6287586f3a74d9694b462d2720ea2e11bbd02af33adefb4a16e6b370fa0effd57d607547bdcfbb7831f54de7073ad2a7da987a0016a82fa958779a168674b56524",
"529030df50f410985fde068df82b935ec23d839cb4b269414c0ede6cffea5b68",
"92971e96fc46608d4343821491990915cdb957ae983ab6cdab84fd094bce1380",
"b15ae269570790a8c6a81c5be7aef33f645abb161d218761ff8739cb7997eed8",
},
{
false,
sha256.New,
"fa0ee1fe39c7c390aa94159d0de97564342b591777f3e5f6a4ba2aea342ec840",
"dd0820655cb2ffdb0da9e9310a67c9e5",
"f2e58fe60a3afc59dad37595415ffd318ccf69d67780f6fa0797dc9aa43e144c",
"8ef5e5870a97c084d1755e84fd741309679c35fa9c7d35daf22209ac26428773",
"7f37fd4ce652ffbe367106d3b36e0111653e8cbe85004d92f18576c93586ca94",
"e0629b6d7975ddfa96a399648740e60f1f9557dc58b3d7415f9ba9d4dbb501f6",
"",
"ee34cedfaa282d1d55e0bb001aa5ae42c1f90b56c6b426ad47deccce83786f38",
"fd616afaa26dd2fc3c2e93cf84af86e6d948fa01c617758816d5ea689925b812",
"",
"12a5a939f3f229cb85a1d6fb72ca5e109959726dda4ff9d95c11d7129ad3c1f9",
"d4bbadb25daa6f76c18ad05c07e448f719f0af2f535e2f938e2dcc5dfa5525b7",
"f92d4cf99a535b20222a52a68db04c5af6f5ffc7b66a473a37a256bd8d298f9b4aa4af7e8d181e02367903f93bdb744c6c2f3f3472626b40ce9bd6a70e7b8f93992a16a76fab6b5f162568e08ee6c3e804aefd952ddd3acb791c50f2ad69e9a04028a06a9c01d3a62aca2aaf6efe69ed97a016213a2dd642b4886764072d9cbe",
"",
"53bc9a0420b02b4f6a60aacd8e0320bc440a2385e27887e6ceba60571b27aa47",
"eab97b2cf76bd1817dc5d6826361b51c4dc8776ef643254dae01f83b23c2d5c2",
},
{
false,
sha256.New,
"cdb0d9117cc6dbc9ef9dcb06a97579841d72dc18b2d46a1cb61e314012bdf416",
"d0c0d01d156016d0eb6b7e9c7c3c8da8",
"6f0fb9eab3f9ea7ab0a719bfa879bf0aaed683307fda0c6d73ce018b6e34faaa",
"6c02577c505aed360be7b1cecb61068d8765be1391bacb10f4180d91bd3915db",
"108a7674f3348216c91f5745dd87a919f552fc44373b84ad4b3b843a26b574cb",
"8ec6f7d5a8e2e88f43986f70b86e050d07c84b931bcf18e601c5a3eee3064c82",
"1ab4ca9014fa98a55938316de8ba5a68c629b0741bdd058c4d70c91cda5099b3",
"21a645aeca821899e7e733a10f64565deee5ced3cd5c0356b66c76dc8a906e69",
"e57f901d4bff2909f09467003096edfdb46c89af6bd82e904d11b6753d645c90",
"16e2d0721b58d839a122852abd3bf2c942a31c84d82fca74211871880d7162ff",
"490c0b7786c80f16ad5ee1cc0efd29618968dce14cccebecec8964ea8a41b439",
"648f92d385c3fbf61526deef48ca5ca4dfe4646d82fe8e73bc1705824e181dc9",
"dda04a2ca7b8147af1548f5d086591ca4fd951a345ce52b3cd49d47e84aa31a183e31fbc42a1ff1d95afec7143c8008c97bc2a9c091df0a763848391f68cb4a366ad89857ac725a53b303ddea767be8dc5f605b1b95f6d24c9f06be65a973a089320b3cc42569dcfd4b92b62a993785b0301b3fc452445656fce22664827b88f",
"53686f042a7b087d5d2eca0d2a96de131f275ed7151189f7ca52deaa78b79fb2",
"47390036d5cb308cf9592fdfe95bf19b8ed1a3db88ed8c3b2b2d77540dfb5470",
"db4853ca51700d43c5b6d63eb6cd20ea2dbe3dff512f2dc9531b5b3d9120121c",
},
{
false,
sha256.New,
"ff0cdd555c60464760b289b7bc1f811a41fff72de59083858c020a1053bdc74a",
"7bc099285ad5621993b639c4a94c376b",
"",
"b38a63e3481f97dd59c74511899e1f4cb77b778e3f8815fa6f2094b44d5a0654",
"29bd9df8847368493ce8d0e073aab128a67b41f93eedbeb0026a9692d97dd26d",
"14fc6c9b178db644a8cd7130a4cf051678c8f4fa8f24c27b0a531338a5ce8589",
"",
"548b3fd5d4d80f0fbcc9c97ae9558156867f3d4d484c6b18da5cb16e697cc6ed",
"34aa93c837e97411d0c95983f9b1d758a5068be88b144e64a1fa0bf68f69e8da",
"",
"ae75026c2254d2365d6cf7a163b4d3b2c77ac5f46a4332b5282ba6d8b7b536b3",
"509923309fac70e02cea78879e8b85bac27a6a697d21464626d8a6e2d7ce1fcf",
"2f2620347bddcaa2943685346bbf31c44081f8665f3ddb2b42ae1416a74c4b77fab3fa19aeecc547e76c8cbe6ad1f100a3fc8b2ce2a1ea3a3dd7cfad46c1b27830b940ba18d09e9b7fa902bb760669b1735cc7b7bd39052da7f2626fa87000cffada410019d053386ad808bd3c0cfcf56b91879eb8d3f932ee2d185e54f31b74",
"",
"f57c1fce260aeef586bc35e5f4fc4eb472119527f4904cfe68a82e05b43d5c31",
"7f7d67925b617a068e249b60b04c6febc093a83fad1b88df7cb98bf2f066f669",
},
{
false,
sha256.New,
"d5303207d58bffb97e0772dc848e7e32dfe2f517fcc9b82f256dcbbbe225a543",
"478f5d6ee7101835a177bd002ac75955",
"",
"a1d4aaed8499761fbc5a11242a416d7bffb9cab72ededa9055e43c7acf2a2c8e",
"3e8c47c5b200169ea78a59314b2c994cac510b7f4ffbf4bc3c1a597959fdd38d",
"e2b00122b868747633010cf2e505db7fe89b197f0847508ec385d2180c97b962",
"1c2a88e25d1711c7862a849eb9a217c2a4219031a0d2e0c2c2dfb5f160b2528b",
"0508e475308c870b27b7ed3ea896569bd5481d31e16070047c62615dd96b5256",
"e5cf473b8f30c10c5ba76c74329baef3f96c06b047ae23b0631f2c80ff364cb4",
"282e5c2989d4df5e1ce476bf05057b7560cab5447b15992951db78f7a92767d9",
"bbc4c4a0450848e704729689ff55d135fe6f80132590a0518de4836095397d7b",
"c4e05db3f077be5fb7d2e52f11fd9655f7635d33baafe66859b1bf1f9958dfab",
"623af8a786c2303f1248eda345d3a80df15a6be6cd34973d68c454ea1399390a41835266c27d0d2efc7bab2207022b2adbd8de654937b49bbf620a716fb0c69911c39b2f96ace53d81fd1bc015364dfdb4b226f216a2a129fd0d1a061d74f4aaf6cc8871e015a480e527aad612f40178ad40d4f790b6f81de9b4669b194b799f",
"3a5b9e896338713c7707aa03360a3027f76e2418bdced7d3e8062196e2721887",
"b191e0371078f06d71f458812e28f5f7c57ef8cf2fbfd764ec53a090372dd7ce",
"9af4e6cfc51dc5fca8658e64e515e20aaac94401c70b44bb88d8883a7da670b8",
},
{
false,
sha256.New,
"28ba1a661632efc8ecced5f51b791300fb3b55b05d041708638de4beb757a9e5",
"76828796aff07f55795cb54713c77ed4",
"40933fdcce4159b0955111f844471b0db85b73bdd2b78c468dd39e2a9b29aef2",
"37f4c974532bbf210847f1f05181ec855022c3b8acb0d34cf07e64b979849d27",
"986cf15107e33375fbc9388fe3d75235ce8e6776aeed4d221cbbe3b644ff972e",
"a5f542b04aaa5dbc931e47019feb38962616c57af09b7c1df83f2b860ff76586",
"",
"7efdf833e6dd9f5729d4acbc990fefbb4c30660bc1e84e8ccf06691946dae5e4",
"18a8a6433b6814350293e9806bf70e98644bacc8a7b7b5f71b51ed7d552dd7b2",
"",
"e17005e5a980016e84c3b000cba73dffa728013a7d7e4dbc5dd96443df087703",
"d8b48fa398ac7fc186a84a8f11ac585f482f54c420baa66b5ed1be2d0bafadb7",
"65e5aa47b385f1ea42b231b9fe744253b8598859d7011e525f5a2a1ad32a972a850802c60a2be19be270063a3cfbeaae954f10b122352de6a08ac410e0991653aab271b360fe9191cf5addcccced8c4acfb61457049992988fd7a9acca1f1bca35f1475813694a39988e5fac9f4ac0572286bc462582ad0af78ab3b85ec17a25",
"",
"ad4f354b7c48ff344821d0de7a250e4f26e6772e26d09f187af20ca466d4ba24",
"73d833d2f2ca0d420ae761dcfae629e40891570dbe59e86450d6f53b41446d15",
},
{
false,
sha256.New,
"40f4a4344082039f5452c3fd21aba9393d253c558728346b6954b00f0c64beb1",
"f8e297eedaf4384a12d91e5b3432c69f",
"11716a84a8b4e5717b61f0827cc9e56e21c0acb9b40372f68902576b50c46ba5",
"027ba7ca6c2cd795b794c517826243335452fea4727b2a09253c02b106261c9c",
"f65cf5100d3e62e80d2f836143c4b6b054acc2d563ae4b2ea7b713dcf02adcec",
"07402212d59681934d22d9bde4cb04d69d2fca68e184c526de933669bfa18f78",
"604e8c8e5fd3c9368288451929c9de0e7f537493d7adba56dc400961abebe501",
"c54e1f38a41b642740d7d917fe2116d6bf0eaad51d8ea595ec5254dafba3417d",
"6f25e235bdf69d4a4bcd1fbe857de81f09921571e0092e85d5370910a6aaef64",
"34cecc853e6e379bfd1e13b8eaed00db34602e33760c590e3f75f10ea2e26951",
"1a6bc09b053ca036d9a8ee8e4985172cc59c14bf69f090a4abe52c1d84e17ee9",
"8632c8a9393c4fac921b885d8f9a0057a19a2e63cae7e67610705625d76f0951",
"3ca3ebc8cdd8280c07213c5a5f1e937ba1294c9aa682ca1a3dee63842132b8da641b340861abe7512074f6d1b9745a541ccf19cc1224967a9faa285e21d7572b3e669d9a3647c30d43c27974fbdb461d468149fc1b340d4ee6c640cfe194669d85d4a595bd1b78808e0496d0e2e38a1b9bc1d0e5273fa03cb5f041bfea7f1c9e",
"82b7f37cb3ca1543927659fddb13d09507fe77c941c5d60d451a9103f1b0b5e4",
"d947854c922986cf1ae2238de3dd913b7cc4980b68e7f0cf30fbafce032fc523",
"bc666597b26ad183696cde941daa3b551e8bafd0df774343c9821904e52e2f8f",
},
{
false,
sha256.New,
"6ae80303292391335bf9c9387fbd3bf615756c9c27c3478c87e260cf97d47110",
"01e16247dd4cae6499337d82784ea57f",
"",
"e2f8032e3792b4dd0c3627ec47d174de10ba62f5d9b199de919082bdd8fd13f9",
"87e52aacf4ed425e22d33d74525c9ca07d07784779df5cd6a721de8cce6428bf",
"035702ef4e112b173112c5851d07b279309863740d38d0d0720223e24017bbc0",
"",
"61cd66e5243e1f1016e57157d492d663d912b53d79ab0b592a1f44a5d9a5405b",
"62f41f0b598858cd999cf3e562dfe6fe41b32a7a99a8148eb12dc257bc967459",
"",
"5852153685d511d0bf62977a544c6fc59d877a83fb5639efacc2630ab5feb68b",
"743b37c4f072f87705f9fd8142e0d5012674f7ba13004cc085d3442675f793c0",
"cf4315598fcd6af1315518c4bfbac0540c589635273548a7b507e7d2e685e5947b87ae257e58faf214f2b58ed10c3bd35f75f6c35dd6d441c93bcd42e71720102631b1a6a4ba247c175ed800cfca6e1e839b5aa907604ccfe6f984f6822e001ab02dd6634964f789cb107a977346693f3244c895e840dfa0edf7f14dc61d794f",
"",
"d606304db9bffaeea999ef797238cb680edbf2562e096f77359464bb777b01c8",
"1334aecfff15ed5ae3d02acbd58a72b63cbc6485d1a461398620ce6a0623bf3a",
},
{
false,
sha256.New,
"010935ec2370e1f0fedf365cd4e37666727cc33eb799f38b1cfd226b7037d9b1",
"e09e498a94860ac567ae911bb218f36e",
"",
"e341ac03662f309e62d69216408cd5ad77159238aaa18927485ed39b94d7dc48",
"6cf8613c8c8be636dc49fd8951f7c7aadd48345fd01a6db8bc9754815012db6e",
"d21a3b9a439bb26fa4b4009807c5cafed3ccff19b2b0608a29f0ef6289f27abb",
"204fa3482fe99b6670ec70b7f2558a5ce9caa98113bad174b85a719410010867",
"e4503fa75ff6f9de250c7167a67f3901af2e0698080d8c7325a020437e6ab788",
"9f5ac47eb6d7c6c9dd64b8f90e77d1c5698d5f15db14a93b16db590a61361687",
"4f61b22033e1cc256f5c677e91882914abca6a5a3f716af0ab2c652ff8bce5f8",
"eafdeb36ac2c280af0b3ca5aa8fc89bd35f28be936a226e9838df27257676f67",
"f1e06eb0a70d41b38f9f14ad29391991c7c1d63800ca4958bb704fb43a455646",
"503e08620268aff772e06603679a7509b4bd590787375a4319fd1f7c7ca7261aa1ef336d862096b4cb98ba97c5e96905e410e719fe2a2de1be621c5a537d1596c7e20db9b242523fc926e22e2826bddabdca1c0b8e2fdb32c87044eb6e7760c6634bd9b96d385f98fb0f8a273dc21bb7dbecc29ff69bd691688db2a4b013576c",
"ce1eb61f9af9727844666e283f2ae69eb7c91d098535fbf2ac7b0584ba81560c",
"08d033ceca9b9bb3f196b5ddbf8090fb794ca49d2024cfd2006bde9644132c03",
"54573b125c5e81f4b43d9c83097d1f9f17b97709587b0e27322283d29df8aa37",
},
{
false,
sha256.New,
"633d32e3005f78114723b3ea5ac121ba74aa00c52d939667e30c3351b38549f7",
"37afff504a2d8ac168c68e24d0fe66f6",
"9f1699c99d60b085bc61cb110ef8ab590d82a970021c3c6a5d48021c45de4956",
"f7c520ba361753f1df1c15e95182d4d4e323ab57226702a4f40480d3f5f02154",
"16a8cfadfd0a75b24b3f7ef16c0628c99e0c44117141caad5f6d191006bc9cc9",
"3e3347c547f17f4d0b9f46405a54eedd7e980d06a215ec15e89316ab743b7547",
"",
"2a647361226a2176a700b3c62d7f8c00d52d9ae2d11ea859f7bd009ef379c02b",
"71e8992a9d2d956c40d30e495fac5547b40e108a74e7afb26638e5a5aa411c92",
"",
"b1e011a27e4e68ac1925249aebad9814b8f8f92d4ea63fed97e441a7d49b0631",
"25b7d30f319c23b20d6cf2a25ec6aa79cb153b0bfa7168bb4d242e86d08c98b6",
"6e38e82962d707ce9a6ac383a738a748f975eb785611fad5e3f5a4fe44d7b59a98137a2bcdc35f9ee9a1e21bb17df1665cd1397625a177247e2e329a660140636141560610a368bfd499c2e25be318aa4da9e7a352d115db8282ed8d79ecf9cd820360d3d2d1a58a93e040f5554887ce6c9858bc2bb102249980a858498abcda",
"",
"9dffe821ef5928a8a274999ec13931c69e9db73321712275380ee933365afece",
"9884c09fe1dc5f85a3e3d21c59599bcdbf5139995b97d605dcc56b8a4446723d",
},
{
false,
sha256.New,
"d712e0448c7f07ffc32cb24d4a13980f63a95c676d332f3d96dff8faa867286b",
"eae6e959651245cc5420455b264bddc9",
"82e3cc515480a6d3fefc6687ac488ce04c0cb0361fb87098104355bfef4b41fe",
"7ab58eb84a6c028f8aed46449c6e4286a7b2906e694e87ccd6f2ed1dace0469f",
"5bd94103f2c143502267a5d1d607b58f54f9874b408697036f6d44fc1565ea7b",
"b34a0fdbd1082df4fb3404502cb99a2b564b7a7335e485a907693b8be256dd10",
"e500f6f8af3c4ff61f46f933f80e80e4d08376349511de5a2d3394855e5edf33",
"9f99111aab6e887cd0a94ea2e76c6934440ed13d235bbcb3a41a4606e283fbcb",
"922c584abb9871a16bcc28d967ab3e6055fedafb2fd25c0f504d54c17fe10732",
"87c571b98a05b372cbb53429a37d91295f6f8892813ba23c3c21327f83f9f55d",
"3b9feef739f8cd40ee1cb7ed6c5717e28ce590e38b81c6a6411b06bac60fe466",
"890dfdcbc4bf9d42c6102b147552d4fd89fcd129c8860dbd55fbcc0c2e12aa1a",
"ce50ca00cb699b6dfdf0b997b8a425d5d665150579c01232d51b6b296138fd39da0349c9fe1e1c47cb2589342cc9a9dd9d17671a68a4915be152826c3245d51d87b37275a5cf4be9b4c90bfe752065eb93bb99679b25ace57ac6bbd4c7a30ee489cff74f2d11a38782f05301db7c60c9659f08f237de354ff7a8ee10b8d93460",
"72d1895e1c4c3093452447cdb3519c8dbdcaf90fa0821ab5ced0079c03ab8dd0",
"6d82fd4c23194021e4b477fa3d4ebb502eb49965bed4bd3ff8941c22ae4378f0",
"aa11c583a988e28dc40358af31e6a3eed4326bb5833706417f50a8a93f47f50d",
},
{
false,
sha256.New,
"ea28926cd5df4fefd572c9103d87ffb04f599da95e1e6fecb84f53f73fd00d6c",
"cb40e16655b9a2c71e8e3677b9ea6c6f",
"",
"15415c709cd481e15f0bd111bb9e0b94e325bdce66fc651d48e4e0516d0bd7fa",
"9c7c9fa9df4dfb3fb69341a84596106f36e4a100cba6d4070eedb89f5430c941",
"3d2d1db88b8462787a5576c95fd660734fb681f894e8efc47e3be3bfc3098e40",
"",
"d591e3aa2e49fa8454f3d7702d65399eb0ae7a8c7ee2647037defc23f4087438",
"c31973f18e450f9b91397cec700d7ab843e99a65422a06e3aa16d2bb1b271f81",
"",
"e53be6df73127677e8b13133c5dcf047b0f0586079acc15eb1c0aaae447d25e8",
"bf8de52f6fba271ccf659401ed4f719e9acdfe678e64854de34f4b5ac3db76f2",
"e929c6e749c5175031dcc926bce8d529147b5e940f61d0ba1f02831c80c27a23cd4b5ffb507c7d09a77e4c8427e29010cf1c8021a80ca29504caa350a27d6ca4554fe4d8b0235554f251a59ec6729d802b473083b0bd6ca83f6d945b3d1de2b706bdcc3b50ddef57847fff88a4498586ca6afe65e76c2d97f87ddea66f5563e3",
"",
"5351bdb181f7f4d3055ffec5262423afa6489a18af7ddabeb4a3a568d3b0291e",
"c5aa9123a932d3e3e3bbf8b94cecda12c8ffac76ab37037610dc8d45ca6452c3",
},
{
false,
sha256.New,
"82d87758f16581b2bd8bd4374aba49a59b65cb953f753594240e69575d51b170",
"c12ea8a42dbef64b9b3abbae9222f94a",
"",
"957f42a1588fddd245955c3cdbea7323a3806f87ba2cd1e3c3a67f8bc36d6654",
"f3cff36fa5e85d23d10ace40ff1ad926bc6da4cdb8ab33cb09c32ef2028fafc9",
"f9bcb64209e8f68e6e3f996196ab445dba64568c7e572dcd2378e916449e39f7",
"987d93237e7e52d1ef3941407e8739f7d95991bad92a49ad6a681dcab1644049",
"25e551f9ca83b241de672369e37b430a2fe116c8f5e3eb63ebacc6314eb62316",
"3f6f557538af2c779b04f90a38e951a50741ca0d5ef13ff7864df9e5098683eb",
"8dd4dc1e62114a0c7e52537607ea8313fa4128f486d871a8bb8adbcd9bf46c42",
"64ab49c35fe6dcc4bd5bb3adefa5d7f9e24a1392933f27edc5566d0881acc60d",
"5fcdd5efd3ec9615df7091344b92837acebda1d5d579853ff22522109a8890bd",
"ba00a49886a6c34ebcd649bb93989cabb9c1d11f9c53f721c99ab125a6cf4726a79713c2683ddae6ae7939be465e9a2b95d008af76db42973a6b63a33bc662d99afd9bd4c7aaadc116da5d11db66f2fc27bfd471ff5130b40f81c0da8de5ba09661165371818ea61f936b4fbd511efc2dc5a7d24dd56326a0e10b03f1f94465d",
"e16721a200d3f18abfd154eb0dc2c9b7c245e8a2a644d59c2e888d4de11652de",
"e78d061da89c47e35de40f87980a6212088add1d770fb304ca9777dad23d3c40",
"35d25d84e870645b71b69a58dd2adc4cd6144e7a63549f9fac19d214a7573655",
},
{
false,
sha256.New,
"22d8c62bcddf5da1dbdb093d6b1f663dde337343c56ccfd40acbe3302309eae0",
"2a4b8e66deaac38b70d9ffc20c585da5",
"0a337038f4b4573ff43a4321a586ca777c30301267d82fdf937198ac56c7062c",
"6b2ddf937ea35aeff5985662edf9ba5e74c7226baf08d75154f93bb068ee3f4f",
"68f2b025b5d56fe2557a19195bed188c25f8fbe0899c5d8652e05983e3662136",
"0b6fd179e44f149f062da4f66f829c3c58c4a0a4f75ac2a9e0240d43bec30e44",
"",
"5c2b1bf10f2b5d59717c860ea225f6e80492948510099e45f2a6e3120064125e",
"1bb103384b405f8f8e52a1d60abd5e791ac19a1125452ce2950c28988c48a307",
"",
"17d2c28e23ef6b7132ceaaf41ce180a43a1d9ac451c0b22396f242163cd13b23",
"cb1d57115e74a19602379450f6f4c6fb77ac026899cc1f90789e8fd5b608c507",
"046e19b4d8ab38dd08defdd0d7c30c8c75d5689ca26f2bc994ac7fca4fdbee80677dfbdd851e7722835844dca79dec4a3fa82feb884df7d474bd972e12619bd5d6cb1b951eac47eec2581195b531534ede507af6f7417fca84532be7ef5da6738dbf7aadfcd7cb888862b52ec773cf3fd00e6d4efb3071af9b70a49935a3ba38",
"",
"fa3b5fbc00ad399c78d57d3fab7f3e16fd7d5e98d512164d5ab2476a84ffe0a8",
"7a4b87e1252d2937d1092873b54ac838e80a8aca4c58b16383c6826a6afd5102",
},
{
false,
sha256.New,
"00efb9c7f02719ff5c7030ffa897a308d36c11ce27526340728bcd487c80457b",
"09cebd489d363b5578ddf30534ee6a7f",
"27e38c624a8f934e931e195a0cbcf38e4e8d50108dc318743fb4b61cf78a7d14",
"65ee4461fd700036721800d2a71f5110cc8099fe447d4b68f317b821983a2063",
"ac0e155af29e01caf36f5f36944adf9cef01b18a4a7aa5af8d63cd56a1d03d43",
"4c87234a9bb529aebb7278daa089753bd2b501d30677edb6cc31e38788fe0e21",
"0e4dddbe0034180b59303d527a938a447bad9e4a91787d1072e6f41350ff11e5",
"1de6328fc0c04353018c24e671c0828229d2bdb2faf331a9cb6363a68baf0ad9",
"e21c915e4af5e628b7a45d2953dd61b552f256a215dd604a56067829bd4dfabe",
"cb25fccf929812b9fc66aea93e0cafb064e25b8c2989ae5078648ef529ecb487",
"a3d029a688de1596b7a214af9f547d26e0d03bf3f62a5755e5288923b851c02e",
"41b4ab280425b8e79e25011bcfeef18fbfa57b272100351a742afcabb5be78cf",
"7569ff1ad01a56ab283c1f2357bd519e15c0be84b80cfe8ec6e26cf903aa8a17f52311a2458e48468122ce1f4abff12920f7dffa86c46f06d744d198004bdd0b29b1b0f17712863df82406e2c2a2fb73ea99dc3969c7e52aeaea031e0112fbf8d785426ae7c106d876a900ba54c4e9a1f3656990571c6d1fb56131cd1cdb1e68",
"c1685a422e4a0673cea9948937a8fdaa77777066f501aa17493682a83d931e6a",
"53243974490ced473f3bc498415b40d75b08cd275bc596e5572aa14b9b51e054",
"8d5ed0295e15688a8f9d7556fd452d63c6b0dff43e653b607cc79958cb305921",
},
{
false,
sha512.New384,
"096349506f3a7653d54db7ec1d09e93413edd175b6ddbeb00e56752a520ac8ff",
"fc7983b918acadaa71a67e1624f1b502",
"",
"b3b502977d1a8483d98d92ee3b0d9b484a28aefb015edbfce57154ed7dc66814833825c9ec2677b14f5cd696f5c95982",
"6660ea0017ee93b79f448c81361c601fd02bbd8d3d81af2592ed077855b448708c0e0cee59459684c72822bd455f012a",
"4260a0495fdaba58aae41df82505012d480c8e4f751fd7ebc39f9becd694b2a3",
"",
"b9d842a55279faf4656fd858c2785ae7c4596d03543937bab29761f7ce3adeddbf20a1e0bd27d63bc2fe6f5161d12470",
"d4163efe4c2cda7bf426f7b42130abe16d090342b1195435fa67a9f315e7d7d5899ba5b7a52debfa2378fc12b06fbba0",
"",
"622b6ebac986b1a2d420d685dabf0ea169c2e3b75c42fda106d7963ddac536fababe374c1827a078def62e2ca7dc9628",
"4ac4a9fae8f6798c594278c1f5b017dab870e34f940f9912e5047f6da5be00c67157ffa07e90cae534e8673aa0082cbf",
"f4c7bec0c26cf3892d214549ac6f3d82f34c6966d4295099ee56166e879a70ecae130251facda351e903d877b6c5eab5153ce87ba6c7cf8bcc61cbd14cfbe34cf1ed43678aee69cd87b60e6bcb6ff48ebd44ce9e31982d8fe20aec34fa51d625f845f61056575969bf785c2ffab4dcc754f13de63423e94bad8d5e166d96a62a602d3ee4045df162028b89cac45e6207d9097f2b3ac0ab17729251985f276f1287f5c56cc9ba1a79fbdbb291f3a945fbfdbd63cf13b82ec91f7b1085b33279e3",
"",
"16d64f1dbb1e386f6126ee917f6d1a561c5fa3ae1d427c7a210e521cf5b878538c9d2fed5faf87c7f5d2cea0f1da642c",
"7914529cc8599e510f68c86b4c44004417eae219beddd5e4d6e0595f0c2ebcc9eeda6ca7e009c89439a1013bfef2aca4",
},
{
false,
sha512.New384,
"a0c341ddf73d9404177a5fde32cbe21319c318f35cc9afca9ad41a3b06e13491",
"e843cc6afdf2bcd00ce77ff06ce3d8a5",
"",
"004f625d33624306986abd1e424b2c651abfd42f60e58e24d8f6cc70d329b061868704137eff0ea756d42ce8086bd6ad",
"4cede2db1bbdc9fc3a4ba0951ec3f02e942c11d34fef1d25c32702efcf19b01e5651454c732311bdd91af77ace5c70ee",
"4772c46baf142e569ecd9131d6185af3575bb62a41cb646bdcae8a7a9fe60cc5",
"b83491ec1bd89f3fc84acf1aad6fbeb8ef6ab949f41adc6d0dedc53722c171fe",
"5c11272a6c0e7bf5a7328a2abf3f5a51fc53bcf6c7945a66d97fadfe760c63bc07815731be6582e5396ca0a751b9915c",
"c4f07fcc042077073d630e06eb2823beb05a878239048a78110aacf3b436cb2ed398a587a9cfddcbaa8955cf4f3ab0fc",
"b76cec3d6300ecc4a02e810296c7e70bd9b4e7121fc5e971cbb94337980fddbd",
"b42f1505e9cc3ebb10ab39cfaa1d5eba50e642e568d859e95a53b0f6dc5c3ed568e7c0f9347973a01f944afe279f88b8",
"1dc6a2b09d2cf6f4068e31707c997465b56a7139114ff997b4d32d83a3b90fbd56890a2625afaab0fcdfa05d3470cea8",
"98c01d4527fd131cc327e9632104d9eee10407cd73ab607228d37b9b72ca2c987aa794804d505d072561ccd5016bd4189ac9e3db9187822877dd533347b5d2071818bb7683312e1e8806e9b73b021777f7f878bb7d304ec58ce92e5e36d3d05a7383dc77f3fe6eb84b615f3f290bf8a43c34ef5478a30a6ad616157c9d7dd046aa66b522bcef61c9d19382c32425d38ed3fc049e73035af1e8b97388de22c4dcba0bdc09fd36ab7eb3f67659cbd92b8d7f6d74b56fc8daf17068c65fb016e29f",
"2a25cb0ecf913749ad46b585c76097739a14ca7b59f1f3ce4f79bc8a4afd1378",
"6022695c754ccc20b5a7f8ced36af219d58a1fc2d87162be87eec064a074ddc206b70ad6e569573c1ef89f8cdf27f62b",
"f00007567e5e9f16f998454da61493935d718476a8f423d728f8077435609cba1455531f305dd47e1acc7151b2735949",
},
{
false,
sha512.New384,
"4d95f31b9606a5f6d04dff1d89b50becfd0882e6cf51c1c5d24ad843bc12d977",
"eba4582c39d793a63eadb63f292568c7",
"43bf6f32b3b5f580b54179e4102d063536e7c47681d6de3cfe88fd8ec66e4873",
"3e0ada07e834a8751618a80c5a170d0a2b23fcd11724ba72da8df002ce7c442a5eaa49b899dd671ecebaccd3b5abf837",
"c02f8b20c9946489da8560ffd2198fa722d62d9a22414c9ab7a221eddc3cf7e0d3f323f83286f70fbb4d6b9dbb1d82e6",
"fc4270e6c9aec83186a20819a7d35e7f1155ea108794302d593c53ce9d25422b",
"",
"60a9edaabed8af69bc1cd20cc5ef322f3c66175057e3816b4e7cb56f79963a57375105c688e700dda6ee99293838e187",
"5b7048c6a6266aac485fee29839dd93bdaed1cb59b9405901526fda454c1d004c18e64965622f84ec205f6f44a33175a",
"",
"72c661a45691345b76a9d9c745a6113bc3accc930d255afc15cd68042e270abd2fa2bf86cd573a3cd75bbfe4055add12",
"e3016c5a315d26268a7482290d5408ffe9b41a6c8da4d02e9a23d9c5d7de8c932fe9235432c2f82cdbabf91308606827",
"e991d000b24ebdf838ba11f9849591b0029feff33604bc4d71acd94301f8d045eeb1f81f3a101a297403a35859113c099939638680d481c86067f54762892f82146f61cce7bc2c85d395348f3ea2aba6bb3e59dbcf8e41a81918b6cab304d44ea1e32573cd6936f38cdc11d3c2f96290cc27b0dfa3bbbafa9394acdf2f4435170b428563427c4b02ed25924226edf8d5a5eca4eec4aecf98ef2e6f75caa70bdd84877df2e637b7fad621c6170ca5bd86e21d0bb01cc90fe2e76353a9d5687bea",
"",
"0dd623ca0606fd5082825b31fb59dd130151f912a64e340c23d76f78ec1d77da3a3e196dd18138c83fa19dae60a6dd50",
"bb45802d2d5ea3c759314805a0db4d7ce8bda8ea465bcb058486045c773d17ffe2471b9aa1bd79d867c5f3d4721e8705",
},
{
false,
sha512.New384,
"c4868db5c46fde0a10008838b5be62c349209fded42fab461b01e11723c8242a",
"618faba54acba1e0afd4b27cbd731ed9",
"135132cf2b8a57554bdc13c68e90dc434353e4f65a4d5ca07c3e0a13c62e7265",
"d930565481cdc8f47df5f1a60c2c6b1c9c93d8890192a15f6f3f0b2d6190b4705eea8c1996081b17c0d46bfb84d99f9b",
"d655b88f171cc0ee23c178309a999bdbe5a24e03291326bb22cff71246de2da18a165bac2b1f62f71c2963a3287501d3",
"d30016b5827dc2bfe4034c6654d69775fe98432b19e3da373213d939d391f54a",
"a0bbd02f6aa71a06d1642ca2cc7cdc5e8857e431b176bcf1ecd20f041467bd2d",
"96d9e60925d82c869f4f902eaf70518c2a3a3b354cc3cb220141b856ae28a5451b0287e2f3efffaf3f3394de0d3e2e6c",
"b3e0f0eacd32cdb4396a4249977b716b211d9b5ab55955d12df4d226e18dbdd78fc1b5efecc7dff1c8812a68db36e16a",
"93ee30a9e7a0e244aa91da62f2215c7233bdfc415740d2770780cbbad61b9ba2",
"ba136597b9daa78365422ccf2037bdf871a0e617d250678c0db6dd68b463d02cc2238a5768a33ed36e3ded5ed7674404",
"4df2f0c9aebfb6c382ee30e154cc2090766e72fc5355380bcd5f0fe9576247b729e7b5c973d2614ffa056e37622dacb6",
"2aac4cebed080c68ef0dcff348506eca568180f7370c020deda1a4c9050ce94d4db90fd827165846d6dd6cb2031eec1634b0e7f3e0e89504e34d248e23a8fb31cd32ff39a486946b2940f54c968f96cfc508cd871c84e68458ca7dccabc6dcfb1e9fbef9a47caae14c5239c28686e0fc0942b0c847c9d8d987970c1c5f5f06eaa8385575dacb1e925c0ed85e13edbb9922083f9bbbb79405411ff5dfe70615685df1f1e49867d0b6ed69afe8ac5e76ffab6ff3d71b4dae998faf8c7d5bc6ae4d",
"36d922cacca00ae89db8f0c1cae5a47d2de8e61ae09357ca431c28a07907fce1",
"955b79910f09ed836e1a41d204a9678f2696fce5ba26e6ba366eec4198461d20eee5b77377e6ad67fbe950cf010137ff",
"0fa2e4a250d0e2c55706a7302f58dae496c10548023240bfcdb93f07809896633980cddecf8a39b9efc4766e7673f272",
},
{
false,
sha512.New,
"48c121b18733af15c27e1dd9ba66a9a81a5579cdba0f5b657ec53c2b9e90bbf6",
"bbb7c777428068fad9970891f879b1af",
"",
"aa25644df73d1bd2f9082c64a196ecfc23f4782324b724b1d7f99073d6e47da735fa8f6d4bc7eb08cedf56ebf0b1c0a39cb75e95d589468a64ee6cb15c90349c",
"8191bfa36fa063c30f4d9c0bb54d9be4b831c9eb16f4fd34c56ed8db831cd27003081bd50ef75d578af9abdb5eaf384da45963b84d9c49275682350c4c3415de",
"e0ffefdadb9ccf990504d568bdb4d862cbe17ccce6e22dfcab8b4804fd21421a",
"",
"53d265c9981d347fea85e3ae38567f4c118a5592d984fda24dc44e888f7037063d89d83019525080ee3b20d3ec223243cf45d4ea468ae5234ac2a14970985e0b",
"0f3eb26c2be623fc36330be73ce25ccb9a6195a13324a429b9fd205ccd30122aa521184fd324110bf484d8c80f2a71dedbc949668d023b6f5cdff72f69f3b033",
"",
"d1e27c212508713dcce84e7ce6714864b9bf5c48b5f817495fc10ec11b39e5d43b826a7d44acc0ed90f801901108462b12b631a31b8ae0938da4b678daa3d844",
"05271c404b99bf9f96e57c6e2737f2eaba656878173b1b5e82dc1fe54ebb4450f76b3efa00913371eaba8e94ac5f1c23eb781c83448d49b76f36a9d265fddb68",
"05da6aac7d980da038f65f392841476d37fe70fbd3e369d1f80196e66e54b8fadb1d60e1a0f3d4dc173769d75fc3410549d7a843270a54a068b4fe767d7d9a59604510a875ad1e9731c8afd0fd50b825e2c50d062576175106a9981be37e02ec7c5cd0a69aa0ca65bddaee1b0de532e10cfa1f5bf6a026e47379736a099d6750ab121dbe3622b841baf8bdcbe875c85ba4b586b8b5b57b0fecbec08c12ff2a9453c47c6e32a52103d972c62ab9affb8e728a31fcefbbccc556c0f0a35f4b10ace2d96b906e36cbb72233201e536d3e13b045187b417d2449cad1edd192e061f12d22147b0a176ea8d9c4c35404395b6502ef333a813b6586037479e0fa3c6a23",
"",
"3a9703e8d52c68286e9af1fb2841411f53b785ca1fa01d447b4afcb7fd9a83485d7394bb28558d49d55902b0c282edc417bcb0a9505b74a1ad8f71da14e44640",
"94d65383dbdebd813534f1713f0392431ae12e82d3fb6a50c5890152e53ac0c7c0e53912deb35d925ea9e661b6de7cce6dea139bb6361019b0fc96362739827a",
},
{
false,
sha512.New,
"4686a959e17dfb96c294b09c0f7a60efb386416cfb4c8972bcc55e44a151607a",
"5226543b4c89321bbfb0f11f18ee3462",
"",
"2c727eb88ad430a0a7914ef55b0e3547212b8821fa14775f58e256a45d960a61fd6a66b228845551742a31ae973849762f583fe6cd0a665e4490e25ff783f1e4",
"9d0896a238dd283a933ab4d585d2b10f2e91c855e9b49d9985fc8d47aabe75646436fb3fd53a0fdcd7c172455a51d02231c40211c9a2ff06bbd40c6baec10d4e",
"5ef50daaf29929047870235c17762f5df5d9ab1af656e0e215fcc6fd9fc0d85d",
"d2383c3e528492269e6c3b3aaa2b54fbf48731f5aa52150ce7fc644679a5e7c6",
"97be60b6742e6af6cd7692cb5cf65977b71518d4eee93df68bb7373b57b7163c5a123b9ab74b9e9d797d9e617b250ac0e707e0a7d3173302c712cef46cf1a67d",
"e18cf3770e3ff4ccd50739b9acac479e8a83f121d0107c4ad03b182784208e82a26d6c348c60a1b1ce9790c3767f649ffda451efd14d0ae1eeb771c380bee8c5",
"c841e7a2d9d13bdb8644cd7f5d91d241a369e12dc6c9c2be50d1ed29484bff98",
"17c52eff18f2b07d2e62a58da815ad17b11fe62eec41ac84dc779dab44810957213ba1a6fa2ea9ef6f1503b1e5db724142b6f20f471f3f2584f5c8b250193e33",
"43d4bdc01bfe767a12f7248137db68d5f872451b620119ea539397cf8b219673b1f35547cdf62f62a22a39cd77ce9bd0f052687088d71878217602c6735cc28f",
"b60d8803531b2b8583d17bdf3ac7c01f3c65cf9b069862b2d39b9024b34c172b712db0704acb078a1ab1aec0390dbaee2dec9be7b234e63da481fd469a92c77bc7bb2cfca586855520e0f9e9d47dcb9bdf2a2fdfa9f2b4342ef0ea582616b55477717cfd516d46d6383257743656f7cf8b38402ba795a8c9d35a4aa88bec623313dad6ead689d152b54074f183b2fee556f554db343626cea853718f18d386bc8bebb0c07b3c5e96ceb391ffceece88864dbd3be83a613562c5c417a24807d5f9332974f045e79a9ade36994af6cf9bbeeb71d0025fcb4ad50f121cbc2df7cd12ff5a50cddfd9a4bbc6d942d743c8b8fbebe00eeccea3d14e07ff8454fa715da",
"9054cf9216af66a788d3bf6757b8987e42d4e49b325e728dc645d5e107048245",
"1dba36d15b1a3bb892155c3ce14f62f40c7f06e496240aa9947d4c26b1b6add01b583a124f6e5e6a8c1abd6bca963045611189c9cbcc982ee4862449316f1f76",
"e1bdf89b316cf3030ebb9278fe8ba1d46199459f171b3a225e41137755691dfbd041cc4d00744f095f6933d3ffc2dd3351e7a81c10e41fe8f68f41500b877954",
},
{
false,
sha512.New,
"97aef935ea33717e8e8644bb8c4789f375c48a945ded08771149e828a22dc866",
"82580f51070ba1e991d9803f51fd9a6f",
"212300f93899ff7cb144f20426028b976380a348253bcc3ff42b528cd1972549",
"369c1220d1c4e2fa7093eea955c133259fca33f0611be5b244a37b06a0e4fd5795e93f43cc11fee03a619c99b2956c5759241cf2e00212faf00c5ee6b3ac73ac",
"01a1b73d723b0083929bfe8402817da32bf0e210275a2432a7b2f52b5ff927d40123b686c04c4973c5783f3edddc77558d4b679ceb3a99ce521574cd62b1bab3",
"63cd91c1ebb2caa15f2837df8f35cbb6fe96df2674a136990a5976cbbab63bc1",
"",
"a11c607cbb8772a490e9268120d1c41a67941e0d629396a494ae3ca90dfd45b1ad726e23c9a100478b6c9a50be8ebf72dd1f1aa7d77217edcdad2ac275a1b5af",
"0503fc92a158a6846fcb5e180e7b4c9a46e9c8a1dd55cff8b6d3dec035610e593f14cf6c290168637861fe5c4dee3b9e6d837e779bcbdce2268edaa69aacd653",
"",
"c92961a387245d8aa3afa2b95ed1cddec892aa1fde32ee24951efe1ea5987fe7cf2c10229ecde1c48d62860bfa3fca39890d316ba2da1d3c1c24e2ee91738f3c",
"c42b36968f282b8bd7bc3277ccb9fd23f2fd1f2b3c1d9487583040711d3a21c119d89fbc8e2c3c16c976f1ac8c57a107aa4dbfb1315ececbca8ed0d429f5a128",
"0e8533f64b60c23a2655827037db218c2fe9ce430fa4ed6ed9be349c4bdc6f40018b42f486fa04288b3b0c62a12812e76e08c76062a510cc60841f165869efaceef90805bdde2fd66c36c38a2ac9c3cb86bfd30406569e0afd245102f2ea2d49e4ee5f69187227a3f0edfbc1259cb6564a2d4e829b3fc3b6996e37546f1d8a16fcd8201d1ad28661bbb0012daad55d5403e833d8a0068d216c879bcebc054df0c9cba14dad4863ee1f75b78bc488662cb0c91ca4fdfce7df5916b4e62580902c601be706dcc7903858e6b9920735bdaa635add5c06080d82265345b49037a32fcf0a7c9ea6069e3369f9b4aa45493efd7318da2ae9b4fc300498248afaad8d49",
"",
"bb3e2d10d3cf2158822abd8ba82ed7d773e908d51ae7de16c833cccfe47a3f2b77fe8ebcadbb68900666b8e27d6f7f44e481e61b6ff5c330e7865e30c50bc762",
"a2ab1e3e87adddeba12949f4faa6746b697ba877e1cefb1d7c69c27fc2a45d03f80dd05fa652a329c8e95009cdb5f643561fd94db89a13d70d9a26ea288039b9",
},
{
false,
sha512.New,
"da740cbc36057a8e282ae717fe7dfbb245e9e5d49908a0119c5dbcf0a1f2d5ab",
"46561ff612217ba3ff91baa06d4b5440",
"fc227293523ecb5b1e28c87863626627d958acc558a672b148ce19e2abd2dde4",
"ee0af8405fc2d21b1e28284d18e9100abaa7841e89d92bd8ecc68687c378e667f0ed517ff6eb991088068a2823739f166f0eef1d442a85df094dc7690f0b3246",
"3d40b40ab107cdb5c0719ee03e31cb32f6d237396303c6842edcb6a56e412d6036dd6185eb4542fc4ecb1dc73927091f948527cb42ef1c20304100d496cbcc10",
"1d61d4d8a41c3254b92104fd555adae0569d1835bb52657ec7fbba0fe03579c5",
"b9ed8e35ad018a375b61189c8d365b00507cb1b4510d21cac212356b5bbaa8b2",
"b2b05a95a8b4c2c91309508725ec6304282184d26e9df6a650c0fc413902c2cc7bb1ef9490ec46504d7a4d9f6758778e145368aebab1c63d162f5a23aa6a6060",
"b6fd4b1d54d114ae9145dd6241abaa633844382036977ac558ada8cd345863253df1c13a0638f1605ff86b1e3496ea4e97ac53c486c7a8bd760d7566581bc1ec",
"b7998998eaf9e5d34e64ff7f03de765b31f407899d20535573e670c1b402c26a",
"1f58ce2f986917745fab282551b17b824f332483d76a32c1345877e3a7973ab180d96433245abe920e7a0d08e230c25cdd46cb14e202b1a4346c1e538c797d0d",
"eb5bfa832a8b2c7211043465313e7ca1ac3c414c3e9ed4ccc02e2786777e85aecdab88f4b9a6cd62a1e5b2af87990cce57e34c5d5db38f96509a09acb8c1910a",
"5b70f3e4da95264233efbab155b828d4e231b67cc92757feca407cc9615a660871cb07ad1a2e9a99412feda8ee34dc9c57fa08d3f8225b30d29887d20907d12330fffd14d1697ba0756d37491b0a8814106e46c8677d49d9157109c402ad0c247a2f50cd5d99e538c850b906937a05dbb8888d984bc77f6ca00b0e3bc97b16d6d25814a54aa12143afddd8b2263690565d545f4137e593bb3ca88a37b0aadf79726b95c61906257e6dc47acd5b6b7e4b534243b13c16ad5a0a1163c0099fce43f428cd27c3e6463cf5e9a9621f4b3d0b3d4654316f4707675df39278d5783823049477dcce8c57fdbd576711c91301e9bd6bb0d3e72dc46d480ed8f61fd63811",
"2089d49d63e0c4df58879d0cb1ba998e5b3d1a7786b785e7cf13ca5ea5e33cfd",
"30615b8b8d10f81026274dddd95ddd2da1b50de88c02e286693dee4dd3f8e2d4c605e6fa4d4518630b86f6d1f82f24b661aa7d9e3a720f269d87599ebb8ac12f",
"9e2ef438065edf51cdd7b718a11b1a2ced6e7779140f1e5829edff98b6f61d24e774e027284aa44b942469db9054a056455b05b77ff5c1238c4b8e9e6f51b18e",
},
{
false,
sha512.New512_224,
"f7d44498e0d7cfe749833c7bdf3a16809cb467b22df30f7f",
"b5a7763e69b64ec67eaa3806",
"",
"2904c74438042227d60363614f7fafba0c6713e1af8f9e28b50ce7df",
"99908e0abde34f367fac54655d216a1297e689cd21a14f48b367afb6",
"8268be026354c36a66c492fbdfe701ff1c41cc960b0431d9",
"",
"ec4012d7804cdee678c4ccbd830e070a4dbbd79b8da1f5e637d8f4e8",
"568a44312a03a6e517a0c4fd303f9f527b057be617a6542526e532a2",
"",
"ccb2838b2c04d4ae1620b9c23ec643610cc5b44d8ab7b3b0bd5cf508",
"50fa697921521bbbf3b767cf227ee5e9f48f59cdf150435ad7c4420c",
"3685116cd406fdc0dad3fc66ffbc1404db38897d488acc3046bcb13bb23061837c4af3d744d6cfba9c9ecdc9cbfe7fe3398d8d4927d6a7de519203d0787f7618478c0b27af9564839c81801a9e6e49cf64cfb87027bb78183bba9e2873db327c99b149afe6f1e8e65e5026e822fde377",
"",
"65f82f4ed47fce03578d32dca707ad892d3c7fa024be7037f3aa3c16",
"159c00b2b65ac310007e3d0906906315a023a469088836f1b6549d41",
},
{
false,
sha512.New512_256,
"eb314860cd71525c8a406511e899d4125a8041ce9dcbe3d496866982ac4f8090",
"adf81bd208b66e8560d72f076dd4b8e3",
"683537ae09e0073c39e73be9defa838a6e767b908d84a14645913048ceb6eaf0",
"3ab613508a2ba85b63f7a2a4cfddcba3fe23be87ad4e40fa5254e7742e8489bc",
"89301629bc88954597ab966c4aaaba647e86c0ca9ebcc051bf6322e39e08260c",
"80764cf816353078637ee46b0b657eb9eb67bb307a75fbf81c17ad1b9d2c27c4",
"a1bdabfcd7447bfc69d8038f3e19453b09b9a1771cd847aeb8a0debe9fe262d1",
"3542b0e9893fb4eb5184c5b65c110424037e02f7293cf4b3559bfafff0f01c66",
"9350056d9e5f08286e5fb34b27a74db09ff60a750dedff4a295ce99c18bbce5f",
"8e2fc1be2b64970bb822c5a0eb4c02563997de76a0c6a13ffbcc8e6cde2230fa",
"419ee68fe489cf190c740fee49b93dab9d0e7c0b86203cc2dde9bb54b85c5fec",
"f6116be7581ad7f0815863150437db14a884f8248ccac38534a10e069c8c5400",
"c39f34a610c96d995c2ab7bdfafc511737e8cf031cb13af70ca1fb3413a74893f90ae22591c534ddd82da64c998d4fc6be2a4df5129890a00376d97308c418387ca481a35062479dd9a788be4565a1f4b4cdefac6045da934e69b3b7131c95f8d14352a65b68c9bd6c6f6f5cbfbbd7fc51d02d61da8bac9cc89c44e04978e511",
"a9a1a518d95bbac9329f373f8a65957df4d882fe793765db030b07122ff8d549",
"8851d039d2975c933d743b4ad58b50163cfb09079273924280746182fde68e60",
"f3bc690d6d87ac8066202fe7ad34c8235e5123cca199caf0749a869f45c52643",
},
}
func hexDecode(s string) []byte {
result, _ := hex.DecodeString(s)
return result
}
func TestHmacDRBG(t *testing.T) {
for i, tt := range hmactests {
hd, err := NewHmacDrbg(tt.newHash, SECURITY_LEVEL_ONE, tt.gm, hexDecode(tt.entropyInput), hexDecode(tt.nonce), hexDecode(tt.personalizationString))
if err != nil {
t.Errorf("NewHmacDrbg case %v failed: %v", i, err)
}
if !bytes.Equal(hd.v, hexDecode(tt.v0)) {
t.Errorf("NewHmacDrbg case %v failed: v0 does not match", i)
}
if !bytes.Equal(hd.key, hexDecode(tt.k0)) {
t.Errorf("NewHmacDrbg case %v failed: k0 does not match", i)
}
hd.Reseed(hexDecode(tt.entropyInputReseed), hexDecode(tt.additionalInputReseed))
if !bytes.Equal(hd.v, hexDecode(tt.v1)) {
t.Errorf("Reseed case %v failed: v1 does not match", i)
}
if !bytes.Equal(hd.key, hexDecode(tt.k1)) {
t.Errorf("Reseed case %v failed: k1 does not match", i)
}
output := make([]byte, len(tt.returnbits1)/2)
err = hd.Generate(output, hexDecode(tt.additionalInput1))
if err != nil {
t.Errorf("Generate case %v failed: %v", i, err)
}
if !bytes.Equal(hd.v, hexDecode(tt.v2)) {
t.Errorf("Generate case %v failed: v2 does not match", i)
}
if !bytes.Equal(hd.key, hexDecode(tt.k2)) {
t.Errorf("Generate case %v failed: k2 does not match", i)
}
err = hd.Generate(output, hexDecode(tt.additionalInput2))
if err != nil {
t.Errorf("Generate case %v failed: %v", i, err)
}
if !bytes.Equal(output, hexDecode(tt.returnbits1)) {
t.Errorf("Generate case %v failed: returnbits1 does not match, got %x", i, output)
}
if !bytes.Equal(hd.v, hexDecode(tt.v3)) {
t.Errorf("Generate case %v failed: v3 does not match", i)
}
if !bytes.Equal(hd.key, hexDecode(tt.k3)) {
t.Errorf("Generate case %v failed: k3 does not match", i)
}
}
}

View File

@ -1,215 +1,205 @@
// Package ecdh implements Elliptic Curve Diffie-Hellman / SM2-MQV over
// SM2 curve.
package ecdh
import (
"crypto"
"crypto/subtle"
"errors"
"hash"
"io"
"sync"
"github.com/emmansun/gmsm/sm3"
)
type Curve interface {
// GenerateKey generates a random PrivateKey.
//
// Most applications should use [crypto/rand.Reader] as rand. Note that the
// returned key does not depend deterministically on the bytes read from rand,
// and may change between calls and/or between versions.
GenerateKey(rand io.Reader) (*PrivateKey, error)
// NewPrivateKey checks that key is valid and returns a PrivateKey.
//
// For NIST curves, this follows SEC 1, Version 2.0, Section 2.3.6, which
// amounts to decoding the bytes as a fixed length big endian integer and
// checking that the result is lower than the order of the curve. The zero
// private key is also rejected, as the encoding of the corresponding public
// key would be irregular.
//
// For X25519, this only checks the scalar length. Adversarially selected
// private keys can cause ECDH to return an error.
NewPrivateKey(key []byte) (*PrivateKey, error)
// NewPublicKey checks that key is valid and returns a PublicKey.
//
// For NIST curves, this decodes an uncompressed point according to SEC 1,
// Version 2.0, Section 2.3.4. Compressed encodings and the point at
// infinity are rejected.
//
// For X25519, this only checks the u-coordinate length. Adversarially
// selected public keys can cause ECDH to return an error.
NewPublicKey(key []byte) (*PublicKey, error)
// ecdh performs a ECDH exchange and returns the shared secret. It's exposed
// as the PrivateKey.ECDH method.
//
// The private method also allow us to expand the ECDH interface with more
// methods in the future without breaking backwards compatibility.
ecdh(local *PrivateKey, remote *PublicKey) ([]byte, error)
// sm2mqv performs a SM2 specific style ECMQV exchange and return the shared secret.
sm2mqv(sLocal, eLocal *PrivateKey, sRemote, eRemote *PublicKey) (*PublicKey, error)
// sm2za ZA = H256(ENTLA || IDA || a || b || xG || yG || xA || yA).
// Compliance with GB/T 32918.2-2016 5.5
sm2za(md hash.Hash, pub *PublicKey, uid []byte) ([]byte, error)
// privateKeyToPublicKey converts a PrivateKey to a PublicKey. It's exposed
// as the PrivateKey.PublicKey method.
//
// This method always succeeds: for X25519, it might output the all-zeroes
// value (unlike the ECDH method); for NIST curves, it would only fail for
// the zero private key, which is rejected by NewPrivateKey.
//
// The private method also allow us to expand the ECDH interface with more
// methods in the future without breaking backwards compatibility.
privateKeyToPublicKey(*PrivateKey) *PublicKey
}
// PublicKey is an ECDH public key, usually a peer's ECDH share sent over the wire.
//
// These keys can be parsed with [smx509.ParsePKIXPublicKey] and encoded
// with [smx509.MarshalPKIXPublicKey]. For SM2 curve, it then needs to
// be converted with [sm2.PublicKeyToECDH] after parsing.
type PublicKey struct {
curve Curve
publicKey []byte
}
// Bytes returns a copy of the encoding of the public key.
func (k *PublicKey) Bytes() []byte {
// Copy the public key to a fixed size buffer that can get allocated on the
// caller's stack after inlining.
var buf [133]byte
return append(buf[:0], k.publicKey...)
}
// Equal returns whether x represents the same public key as k.
//
// Note that there can be equivalent public keys with different encodings which
// would return false from this check but behave the same way as inputs to ECDH.
//
// This check is performed in constant time as long as the key types and their
// curve match.
func (k *PublicKey) Equal(x crypto.PublicKey) bool {
xx, ok := x.(*PublicKey)
if !ok {
return false
}
return k.curve == xx.curve &&
subtle.ConstantTimeCompare(k.publicKey, xx.publicKey) == 1
}
func (k *PublicKey) Curve() Curve {
return k.curve
}
// SM2ZA ZA = H256(ENTLA || IDA || a || b || xG || yG || xA || yA).
// Compliance with GB/T 32918.2-2016 5.5
func (k *PublicKey) SM2ZA(md hash.Hash, uid []byte) ([]byte, error) {
return k.curve.sm2za(md, k, uid)
}
// SM2SharedKey performs SM2 key derivation to generate shared keying data, the uv was generated by [SM2MQV].
func (uv *PublicKey) SM2SharedKey(isResponder bool, kenLen int, sPub, sRemote *PublicKey, uid []byte, remoteUID []byte) ([]byte, error) {
var buffer [128]byte
copy(buffer[:], uv.publicKey[1:])
peerZ, err := sRemote.SM2ZA(sm3.New(), remoteUID)
if err != nil {
return nil, err
}
z, err := sPub.SM2ZA(sm3.New(), uid)
if err != nil {
return nil, err
}
if isResponder {
copy(buffer[64:], peerZ)
copy(buffer[96:], z)
} else {
copy(buffer[64:], z)
copy(buffer[96:], peerZ)
}
return sm3.Kdf(buffer[:], kenLen), nil
}
// PrivateKey is an ECDH private key, usually kept secret.
//
// These keys can be parsed with [smx509.ParsePKCS8PrivateKey] and encoded
// with [smx509.MarshalPKCS8PrivateKey]. For SM2 curve, it then needs to
// be converted with [sm2.PrivateKey.ECDH] after parsing.
type PrivateKey struct {
curve Curve
privateKey []byte
// publicKey is set under publicKeyOnce, to allow loading private keys with
// NewPrivateKey without having to perform a scalar multiplication.
publicKey *PublicKey
publicKeyOnce sync.Once
}
// ECDH performs a ECDH exchange and returns the shared secret.
//
// For NIST curves, this performs ECDH as specified in SEC 1, Version 2.0,
// Section 3.3.1, and returns the x-coordinate encoded according to SEC 1,
// Version 2.0, Section 2.3.5. The result is never the point at infinity.
//
// For X25519, this performs ECDH as specified in RFC 7748, Section 6.1. If
// the result is the all-zero value, ECDH returns an error.
func (k *PrivateKey) ECDH(remote *PublicKey) ([]byte, error) {
if k.curve != remote.curve {
return nil, errors.New("ecdh: private key and public key curves do not match")
}
return k.curve.ecdh(k, remote)
}
// SM2MQV performs a SM2 specific style ECMQV exchange and return the shared secret.
func (k *PrivateKey) SM2MQV(eLocal *PrivateKey, sRemote, eRemote *PublicKey) (*PublicKey, error) {
if k.curve != eLocal.curve || k.curve != sRemote.curve || k.curve != eRemote.curve {
return nil, errors.New("ecdh: private key and public key curves do not match")
}
return k.curve.sm2mqv(k, eLocal, sRemote, eRemote)
}
// Bytes returns a copy of the encoding of the private key.
func (k *PrivateKey) Bytes() []byte {
// Copy the private key to a fixed size buffer that can get allocated on the
// caller's stack after inlining.
var buf [66]byte
return append(buf[:0], k.privateKey...)
}
// Equal returns whether x represents the same private key as k.
//
// Note that there can be equivalent private keys with different encodings which
// would return false from this check but behave the same way as inputs to [ECDH].
//
// This check is performed in constant time as long as the key types and their
// curve match.
func (k *PrivateKey) Equal(x crypto.PrivateKey) bool {
xx, ok := x.(*PrivateKey)
if !ok {
return false
}
return k.curve == xx.curve &&
subtle.ConstantTimeCompare(k.privateKey, xx.privateKey) == 1
}
func (k *PrivateKey) Curve() Curve {
return k.curve
}
func (k *PrivateKey) PublicKey() *PublicKey {
k.publicKeyOnce.Do(func() {
k.publicKey = k.curve.privateKeyToPublicKey(k)
})
return k.publicKey
}
// Public implements the implicit interface of all standard library private
// keys. See the docs of [crypto.PrivateKey].
func (k *PrivateKey) Public() crypto.PublicKey {
return k.PublicKey()
}
// Package ecdh implements Elliptic Curve Diffie-Hellman / SM2-MQV over
// SM2 curve.
package ecdh
import (
"crypto"
"crypto/subtle"
"hash"
"io"
"sync"
"github.com/emmansun/gmsm/kdf"
"github.com/emmansun/gmsm/sm3"
)
type Curve interface {
// GenerateKey generates a new PrivateKey from rand.
GenerateKey(rand io.Reader) (*PrivateKey, error)
// NewPrivateKey checks that key is valid and returns a PrivateKey.
//
// For NIST curves, this follows SEC 1, Version 2.0, Section 2.3.6, which
// amounts to decoding the bytes as a fixed length big endian integer and
// checking that the result is lower than the order of the curve. The zero
// private key is also rejected, as the encoding of the corresponding public
// key would be irregular.
//
// For X25519, this only checks the scalar length. Adversarially selected
// private keys can cause ECDH to return an error.
NewPrivateKey(key []byte) (*PrivateKey, error)
// NewPublicKey checks that key is valid and returns a PublicKey.
//
// For NIST curves, this decodes an uncompressed point according to SEC 1,
// Version 2.0, Section 2.3.4. Compressed encodings and the point at
// infinity are rejected.
//
// For X25519, this only checks the u-coordinate length. Adversarially
// selected public keys can cause ECDH to return an error.
NewPublicKey(key []byte) (*PublicKey, error)
// ecdh performs a ECDH exchange and returns the shared secret. It's exposed
// as the PrivateKey.ECDH method.
//
// The private method also allow us to expand the ECDH interface with more
// methods in the future without breaking backwards compatibility.
ecdh(local *PrivateKey, remote *PublicKey) ([]byte, error)
// sm2mqv performs a SM2 specific style ECMQV exchange and return the shared secret.
sm2mqv(sLocal, eLocal *PrivateKey, sRemote, eRemote *PublicKey) (*PublicKey, error)
// sm2za ZA = H256(ENTLA || IDA || a || b || xG || yG || xA || yA).
// Compliance with GB/T 32918.2-2016 5.5
sm2za(md hash.Hash, pub *PublicKey, uid []byte) ([]byte, error)
// privateKeyToPublicKey converts a PrivateKey to a PublicKey. It's exposed
// as the PrivateKey.PublicKey method.
//
// This method always succeeds: for X25519, it might output the all-zeroes
// value (unlike the ECDH method); for NIST curves, it would only fail for
// the zero private key, which is rejected by NewPrivateKey.
//
// The private method also allow us to expand the ECDH interface with more
// methods in the future without breaking backwards compatibility.
privateKeyToPublicKey(*PrivateKey) *PublicKey
}
// PublicKey is an ECDH public key, usually a peer's ECDH share sent over the wire.
//
// These keys can be parsed with [smx509.ParsePKIXPublicKey] and encoded
// with [smx509.MarshalPKIXPublicKey]. For SM2 curve, it then needs to
// be converted with [sm2.PublicKeyToECDH] after parsing.
type PublicKey struct {
curve Curve
publicKey []byte
}
// Bytes returns a copy of the encoding of the public key.
func (k *PublicKey) Bytes() []byte {
// Copy the public key to a fixed size buffer that can get allocated on the
// caller's stack after inlining.
var buf [133]byte
return append(buf[:0], k.publicKey...)
}
// Equal returns whether x represents the same public key as k.
//
// Note that there can be equivalent public keys with different encodings which
// would return false from this check but behave the same way as inputs to ECDH.
//
// This check is performed in constant time as long as the key types and their
// curve match.
func (k *PublicKey) Equal(x crypto.PublicKey) bool {
xx, ok := x.(*PublicKey)
if !ok {
return false
}
return k.curve == xx.curve &&
subtle.ConstantTimeCompare(k.publicKey, xx.publicKey) == 1
}
func (k *PublicKey) Curve() Curve {
return k.curve
}
// SM2ZA ZA = H256(ENTLA || IDA || a || b || xG || yG || xA || yA).
// Compliance with GB/T 32918.2-2016 5.5
func (k *PublicKey) SM2ZA(md hash.Hash, uid []byte) ([]byte, error) {
return k.curve.sm2za(md, k, uid)
}
// SM2SharedKey performs SM2 key derivation to generate shared keying data, the uv was generated by SM2MQV.
func (uv *PublicKey) SM2SharedKey(isResponder bool, kenLen int, sPub, sRemote *PublicKey, uid []byte, remoteUID []byte) ([]byte, error) {
var buffer [128]byte
copy(buffer[:], uv.publicKey[1:])
peerZ, err := sRemote.SM2ZA(sm3.New(), remoteUID)
if err != nil {
return nil, err
}
z, err := sPub.SM2ZA(sm3.New(), uid)
if err != nil {
return nil, err
}
if isResponder {
copy(buffer[64:], peerZ)
copy(buffer[96:], z)
} else {
copy(buffer[64:], z)
copy(buffer[96:], peerZ)
}
return kdf.Kdf(sm3.New(), buffer[:], kenLen), nil
}
// PrivateKey is an ECDH private key, usually kept secret.
//
// These keys can be parsed with [smx509.ParsePKCS8PrivateKey] and encoded
// with [smx509.MarshalPKCS8PrivateKey]. For SM2 curve, it then needs to
// be converted with [sm2.PrivateKey.ECDH] after parsing.
type PrivateKey struct {
curve Curve
privateKey []byte
// publicKey is set under publicKeyOnce, to allow loading private keys with
// NewPrivateKey without having to perform a scalar multiplication.
publicKey *PublicKey
publicKeyOnce sync.Once
}
// ECDH performs a ECDH exchange and returns the shared secret.
//
// For NIST curves, this performs ECDH as specified in SEC 1, Version 2.0,
// Section 3.3.1, and returns the x-coordinate encoded according to SEC 1,
// Version 2.0, Section 2.3.5. The result is never the point at infinity.
//
// For X25519, this performs ECDH as specified in RFC 7748, Section 6.1. If
// the result is the all-zero value, ECDH returns an error.
func (k *PrivateKey) ECDH(remote *PublicKey) ([]byte, error) {
return k.curve.ecdh(k, remote)
}
// SM2MQV performs a SM2 specific style ECMQV exchange and return the shared secret.
func (k *PrivateKey) SM2MQV(eLocal *PrivateKey, sRemote, eRemote *PublicKey) (*PublicKey, error) {
return k.curve.sm2mqv(k, eLocal, sRemote, eRemote)
}
// Bytes returns a copy of the encoding of the private key.
func (k *PrivateKey) Bytes() []byte {
// Copy the private key to a fixed size buffer that can get allocated on the
// caller's stack after inlining.
var buf [66]byte
return append(buf[:0], k.privateKey...)
}
// Equal returns whether x represents the same private key as k.
//
// Note that there can be equivalent private keys with different encodings which
// would return false from this check but behave the same way as inputs to ECDH.
//
// This check is performed in constant time as long as the key types and their
// curve match.
func (k *PrivateKey) Equal(x crypto.PrivateKey) bool {
xx, ok := x.(*PrivateKey)
if !ok {
return false
}
return k.curve == xx.curve &&
subtle.ConstantTimeCompare(k.privateKey, xx.privateKey) == 1
}
func (k *PrivateKey) Curve() Curve {
return k.curve
}
func (k *PrivateKey) PublicKey() *PublicKey {
k.publicKeyOnce.Do(func() {
k.publicKey = k.curve.privateKeyToPublicKey(k)
})
return k.publicKey
}
// Public implements the implicit interface of all standard library private
// keys. See the docs of crypto.PrivateKey.
func (k *PrivateKey) Public() crypto.PublicKey {
return k.PublicKey()
}

View File

@ -1,381 +1,343 @@
package ecdh_test
import (
"bytes"
"crypto"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"fmt"
"io"
"testing"
"github.com/emmansun/gmsm/ecdh"
"golang.org/x/crypto/chacha20"
)
// Check that PublicKey and PrivateKey implement the interfaces documented in
// crypto.PublicKey and crypto.PrivateKey.
var _ interface {
Equal(x crypto.PublicKey) bool
} = &ecdh.PublicKey{}
var _ interface {
Public() crypto.PublicKey
Equal(x crypto.PrivateKey) bool
} = &ecdh.PrivateKey{}
func hexDecode(t *testing.T, s string) []byte {
b, err := hex.DecodeString(s)
if err != nil {
t.Fatal("invalid hex string:", s)
}
return b
}
func TestNewPrivateKey(t *testing.T) {
_, err := ecdh.P256().NewPrivateKey(nil)
if err == nil || err.Error() != "ecdh: invalid private key size" {
t.Errorf("ecdh: invalid private key size")
}
_, err = ecdh.P256().NewPrivateKey([]byte{
0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x72, 0x03, 0xdf, 0x6b, 0x21, 0xc6, 0x05, 0x2b,
0x53, 0xbb, 0xf4, 0x09, 0x39, 0xd5, 0x41})
if err == nil || err.Error() != "ecdh: invalid private key size" {
t.Errorf("ecdh: invalid private key size")
}
allzero := make([]byte, 32)
_, err = ecdh.P256().NewPrivateKey(allzero)
if err == nil || err.Error() != "ecdh: invalid private key" {
t.Errorf("expected invalid private key")
}
_, err = ecdh.P256().NewPrivateKey([]byte{
0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x72, 0x03, 0xdf, 0x6b, 0x21, 0xc6, 0x05, 0x2b,
0x53, 0xbb, 0xf4, 0x09, 0x39, 0xd5, 0x41, 0x22})
if err == nil || err.Error() != "ecdh: invalid private key" {
t.Errorf("expected invalid private key")
}
}
func TestNewPublicKey(t *testing.T) {
_, err := ecdh.P256().NewPublicKey(nil)
if err == nil || err.Error() != "ecdh: invalid public key" {
t.Errorf("ecdh: invalid public key")
}
keydata := make([]byte, 65)
_, err = ecdh.P256().NewPublicKey(keydata)
if err == nil || err.Error() != "ecdh: invalid public key" {
t.Errorf("ecdh: invalid public key")
}
}
func TestECDH(t *testing.T) {
aliceKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
bobKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
alicePubKey, err := ecdh.P256().NewPublicKey(aliceKey.PublicKey().Bytes())
if err != nil {
t.Error(err)
}
if !bytes.Equal(aliceKey.PublicKey().Bytes(), alicePubKey.Bytes()) {
t.Error("encoded and decoded public keys are different")
}
if !aliceKey.PublicKey().Equal(alicePubKey) {
t.Error("encoded and decoded public keys are different")
}
alicePrivKey, err := ecdh.P256().NewPrivateKey(aliceKey.Bytes())
if err != nil {
t.Error(err)
}
if !bytes.Equal(aliceKey.Bytes(), alicePrivKey.Bytes()) {
t.Error("encoded and decoded private keys are different")
}
if !aliceKey.Equal(alicePrivKey) {
t.Error("encoded and decoded private keys are different")
}
bobSecret, err := bobKey.ECDH(aliceKey.PublicKey())
if err != nil {
t.Fatal(err)
}
aliceSecret, err := aliceKey.ECDH(bobKey.PublicKey())
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(bobSecret, aliceSecret) {
t.Error("two ECDH computations came out different")
}
}
func TestSM2MQV(t *testing.T) {
aliceSKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
aliceEKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
bobSKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
bobEKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
bobSecret, err := bobSKey.SM2MQV(bobEKey, aliceSKey.PublicKey(), aliceEKey.PublicKey())
if err != nil {
t.Fatal(err)
}
aliceSecret, err := aliceSKey.SM2MQV(aliceEKey, bobSKey.PublicKey(), bobEKey.PublicKey())
if err != nil {
t.Fatal(err)
}
if !aliceSecret.Equal(bobSecret) {
t.Error("two SM2MQV computations came out different")
}
}
func TestSM2SharedKey(t *testing.T) {
aliceSKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
aliceEKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
bobSKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
bobEKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
bobSecret, err := bobSKey.SM2MQV(bobEKey, aliceSKey.PublicKey(), aliceEKey.PublicKey())
if err != nil {
t.Fatal(err)
}
aliceSecret, err := aliceSKey.SM2MQV(aliceEKey, bobSKey.PublicKey(), bobEKey.PublicKey())
if err != nil {
t.Fatal(err)
}
if !aliceSecret.Equal(bobSecret) {
t.Error("two SM2MQV computations came out different")
}
bobKey, err := bobSecret.SM2SharedKey(true, 48, bobSKey.PublicKey(), aliceSKey.PublicKey(), []byte("Bob"), []byte("Alice"))
if err != nil {
t.Fatal(err)
}
aliceKey, err := aliceSecret.SM2SharedKey(false, 48, aliceSKey.PublicKey(), bobSKey.PublicKey(), []byte("Alice"), []byte("Bob"))
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(bobKey, aliceKey) {
t.Error("two SM2SharedKey computations came out different")
}
}
var vectors = []struct {
LocalStaticPriv, LocalEphemeralPriv string
RemoteStaticPriv, RemoteEphemeralPriv string
SharedSecret, Key string
}{
{
"e04c3fd77408b56a648ad439f673511a2ae248def3bab26bdfc9cdbd0ae9607e",
"6fe0bac5b09d3ab10f724638811c34464790520e4604e71e6cb0e5310623b5b1",
"7a1136f60d2c5531447e5a3093078c2a505abf74f33aefed927ac0a5b27e7dd7",
"d0233bdbb0b8a7bfe1aab66132ef06fc4efaedd5d5000692bc21185242a31f6f",
"046ab5c9709277837cedc515730d04751ef81c71e81e0e52357a98cf41796ab560508da6e858b40c6264f17943037434174284a847f32c4f54104a98af5148d89f",
"1ad809ebc56ddda532020c352e1e60b121ebeb7b4e632db4dd90a362cf844f8bba85140e30984ddb581199bf5a9dda22",
},
{
"cb5ac204b38d0e5c9fc38a467075986754018f7dbb7cbbc5b4c78d56a88a8ad8",
"1681a66c02b67fdadfc53cba9b417b9499d0159435c86bb8760c3a03ae157539",
"4f54b10e0d8e9e2fe5cc79893e37fd0fd990762d1372197ed92dde464b2773ef",
"a2fe43dea141e9acc88226eaba8908ad17e81376c92102cb8186e8fef61a8700",
"04677d055355a1dcc9de4df00d3a80b6daa76bdf54ff7e0a3a6359fcd0c6f1e4b4697fffc41bbbcc3a28ea3aa1c6c380d1e92f142233afa4b430d02ab4cebc43b2",
"7a103ae61a30ed9df573a5febb35a9609cbed5681bcb98a8545351bf7d6824cc4635df5203712ea506e2e3c4ec9b12e7",
},
{
"ee690a34a779ab48227a2f68b062a80f92e26d82835608dd01b7452f1e4fb296",
"2046c6cee085665e9f3abeba41fd38e17a26c08f2f5e8f0e1007afc0bf6a2a5d",
"8ef49ea427b13cc31151e1c96ae8a48cb7919063f2d342560fb7eaaffb93d8fe",
"9baf8d602e43fbae83fedb7368f98c969d378b8a647318f8cafb265296ae37de",
"04f7e9f1447968b284ff43548fcec3752063ea386b48bfabb9baf2f9c1caa05c2fb12c2cca37326ce27e68f8cc6414c2554895519c28da1ca21e61890d0bc525c4",
"b18e78e5072f301399dc1f4baf2956c0ed2d5f52f19abb1705131b0865b079031259ee6c629b4faed528bcfa1c5d2cbc",
},
}
func TestSM2SharedKeyVectors(t *testing.T) {
initiator := []byte("Alice")
responder := []byte("Bob")
kenLen := 48
for i, v := range vectors {
aliceSKey, err := ecdh.P256().NewPrivateKey(hexDecode(t, v.LocalStaticPriv))
if err != nil {
t.Fatal(err)
}
aliceEKey, err := ecdh.P256().NewPrivateKey(hexDecode(t, v.LocalEphemeralPriv))
if err != nil {
t.Fatal(err)
}
bobSKey, err := ecdh.P256().NewPrivateKey(hexDecode(t, v.RemoteStaticPriv))
if err != nil {
t.Fatal(err)
}
bobEKey, err := ecdh.P256().NewPrivateKey(hexDecode(t, v.RemoteEphemeralPriv))
if err != nil {
t.Fatal(err)
}
bobSecret, err := bobSKey.SM2MQV(bobEKey, aliceSKey.PublicKey(), aliceEKey.PublicKey())
if err != nil {
t.Fatal(err)
}
aliceSecret, err := aliceSKey.SM2MQV(aliceEKey, bobSKey.PublicKey(), bobEKey.PublicKey())
if err != nil {
t.Fatal(err)
}
if !aliceSecret.Equal(bobSecret) {
t.Error("two SM2MQV computations came out different")
}
if !bytes.Equal(aliceSecret.Bytes(), hexDecode(t, v.SharedSecret)) {
t.Errorf("%v shared secret is not expected.", i)
}
bobKey, err := bobSecret.SM2SharedKey(true, kenLen, bobSKey.PublicKey(), aliceSKey.PublicKey(), responder, initiator)
if err != nil {
t.Fatal(err)
}
aliceKey, err := aliceSecret.SM2SharedKey(false, kenLen, aliceSKey.PublicKey(), bobSKey.PublicKey(), initiator, responder)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(bobKey, aliceKey) {
t.Error("two SM2SharedKey computations came out different")
}
if !bytes.Equal(bobKey, hexDecode(t, v.Key)) {
t.Errorf("%v keying data is not expected.", i)
}
}
}
type countingReader struct {
r io.Reader
n int
}
func (r *countingReader) Read(p []byte) (int, error) {
n, err := r.r.Read(p)
r.n += n
return n, err
}
func TestGenerateKey(t *testing.T) {
r := &countingReader{r: rand.Reader}
k, err := ecdh.P256().GenerateKey(r)
if err != nil {
t.Fatal(err)
}
// GenerateKey does rejection sampling. If the masking works correctly,
// the probability of a rejection is 1-ord(G)/2^ceil(log2(ord(G))),
// which for all curves is small enough (at most 2^-32, for P-256) that
// a bit flip is more likely to make this test fail than bad luck.
// Account for the extra MaybeReadByte byte, too.
if got, expected := r.n, len(k.Bytes())+1; got > expected {
t.Errorf("expected GenerateKey to consume at most %v bytes, got %v", expected, got)
}
}
func TestString(t *testing.T) {
s := fmt.Sprintf("%s", ecdh.P256())
if s != "sm2p256v1" {
t.Errorf("unexpected Curve string encoding: %q", s)
}
}
func BenchmarkECDH(b *testing.B) {
benchmarkAllCurves(b, func(b *testing.B, curve ecdh.Curve) {
c, err := chacha20.NewUnauthenticatedCipher(make([]byte, 32), make([]byte, 12))
if err != nil {
b.Fatal(err)
}
rand := cipher.StreamReader{
S: c, R: zeroReader,
}
peerKey, err := curve.GenerateKey(rand)
if err != nil {
b.Fatal(err)
}
peerShare := peerKey.PublicKey().Bytes()
b.ResetTimer()
b.ReportAllocs()
var allocationsSink byte
for i := 0; i < b.N; i++ {
key, err := curve.GenerateKey(rand)
if err != nil {
b.Fatal(err)
}
share := key.PublicKey().Bytes()
peerPubKey, err := curve.NewPublicKey(peerShare)
if err != nil {
b.Fatal(err)
}
secret, err := key.ECDH(peerPubKey)
if err != nil {
b.Fatal(err)
}
allocationsSink ^= secret[0] ^ share[0]
}
})
}
func benchmarkAllCurves(b *testing.B, f func(b *testing.B, curve ecdh.Curve)) {
b.Run("SM2P256", func(b *testing.B) { f(b, ecdh.P256()) })
}
type zr struct{}
// Read replaces the contents of dst with zeros. It is safe for concurrent use.
func (zr) Read(dst []byte) (n int, err error) {
clear(dst)
return len(dst), nil
}
var zeroReader = zr{}
package ecdh_test
import (
"bytes"
"crypto"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"fmt"
"io"
"testing"
"github.com/emmansun/gmsm/ecdh"
"golang.org/x/crypto/chacha20"
)
// Check that PublicKey and PrivateKey implement the interfaces documented in
// crypto.PublicKey and crypto.PrivateKey.
var _ interface {
Equal(x crypto.PublicKey) bool
} = &ecdh.PublicKey{}
var _ interface {
Public() crypto.PublicKey
Equal(x crypto.PrivateKey) bool
} = &ecdh.PrivateKey{}
func hexDecode(t *testing.T, s string) []byte {
b, err := hex.DecodeString(s)
if err != nil {
t.Fatal("invalid hex string:", s)
}
return b
}
func TestECDH(t *testing.T) {
aliceKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
bobKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
alicePubKey, err := ecdh.P256().NewPublicKey(aliceKey.PublicKey().Bytes())
if err != nil {
t.Error(err)
}
if !bytes.Equal(aliceKey.PublicKey().Bytes(), alicePubKey.Bytes()) {
t.Error("encoded and decoded public keys are different")
}
if !aliceKey.PublicKey().Equal(alicePubKey) {
t.Error("encoded and decoded public keys are different")
}
alicePrivKey, err := ecdh.P256().NewPrivateKey(aliceKey.Bytes())
if err != nil {
t.Error(err)
}
if !bytes.Equal(aliceKey.Bytes(), alicePrivKey.Bytes()) {
t.Error("encoded and decoded private keys are different")
}
if !aliceKey.Equal(alicePrivKey) {
t.Error("encoded and decoded private keys are different")
}
bobSecret, err := bobKey.ECDH(aliceKey.PublicKey())
if err != nil {
t.Fatal(err)
}
aliceSecret, err := aliceKey.ECDH(bobKey.PublicKey())
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(bobSecret, aliceSecret) {
t.Error("two ECDH computations came out different")
}
}
func TestSM2MQV(t *testing.T) {
aliceSKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
aliceEKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
bobSKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
bobEKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
bobSecret, err := bobSKey.SM2MQV(bobEKey, aliceSKey.PublicKey(), aliceEKey.PublicKey())
if err != nil {
t.Fatal(err)
}
aliceSecret, err := aliceSKey.SM2MQV(aliceEKey, bobSKey.PublicKey(), bobEKey.PublicKey())
if err != nil {
t.Fatal(err)
}
if !aliceSecret.Equal(bobSecret) {
t.Error("two SM2MQV computations came out different")
}
}
func TestSM2SharedKey(t *testing.T) {
aliceSKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
aliceEKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
bobSKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
bobEKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
bobSecret, err := bobSKey.SM2MQV(bobEKey, aliceSKey.PublicKey(), aliceEKey.PublicKey())
if err != nil {
t.Fatal(err)
}
aliceSecret, err := aliceSKey.SM2MQV(aliceEKey, bobSKey.PublicKey(), bobEKey.PublicKey())
if err != nil {
t.Fatal(err)
}
if !aliceSecret.Equal(bobSecret) {
t.Error("two SM2MQV computations came out different")
}
bobKey, err := bobSecret.SM2SharedKey(true, 48, bobSKey.PublicKey(), aliceSKey.PublicKey(), []byte("Bob"), []byte("Alice"))
if err != nil {
t.Fatal(err)
}
aliceKey, err := aliceSecret.SM2SharedKey(false, 48, aliceSKey.PublicKey(), bobSKey.PublicKey(), []byte("Alice"), []byte("Bob"))
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(bobKey, aliceKey) {
t.Error("two SM2SharedKey computations came out different")
}
}
var vectors = []struct {
LocalStaticPriv, LocalEphemeralPriv string
RemoteStaticPriv, RemoteEphemeralPriv string
SharedSecret, Key string
}{
{
"e04c3fd77408b56a648ad439f673511a2ae248def3bab26bdfc9cdbd0ae9607e",
"6fe0bac5b09d3ab10f724638811c34464790520e4604e71e6cb0e5310623b5b1",
"7a1136f60d2c5531447e5a3093078c2a505abf74f33aefed927ac0a5b27e7dd7",
"d0233bdbb0b8a7bfe1aab66132ef06fc4efaedd5d5000692bc21185242a31f6f",
"046ab5c9709277837cedc515730d04751ef81c71e81e0e52357a98cf41796ab560508da6e858b40c6264f17943037434174284a847f32c4f54104a98af5148d89f",
"1ad809ebc56ddda532020c352e1e60b121ebeb7b4e632db4dd90a362cf844f8bba85140e30984ddb581199bf5a9dda22",
},
{
"cb5ac204b38d0e5c9fc38a467075986754018f7dbb7cbbc5b4c78d56a88a8ad8",
"1681a66c02b67fdadfc53cba9b417b9499d0159435c86bb8760c3a03ae157539",
"4f54b10e0d8e9e2fe5cc79893e37fd0fd990762d1372197ed92dde464b2773ef",
"a2fe43dea141e9acc88226eaba8908ad17e81376c92102cb8186e8fef61a8700",
"04677d055355a1dcc9de4df00d3a80b6daa76bdf54ff7e0a3a6359fcd0c6f1e4b4697fffc41bbbcc3a28ea3aa1c6c380d1e92f142233afa4b430d02ab4cebc43b2",
"7a103ae61a30ed9df573a5febb35a9609cbed5681bcb98a8545351bf7d6824cc4635df5203712ea506e2e3c4ec9b12e7",
},
{
"ee690a34a779ab48227a2f68b062a80f92e26d82835608dd01b7452f1e4fb296",
"2046c6cee085665e9f3abeba41fd38e17a26c08f2f5e8f0e1007afc0bf6a2a5d",
"8ef49ea427b13cc31151e1c96ae8a48cb7919063f2d342560fb7eaaffb93d8fe",
"9baf8d602e43fbae83fedb7368f98c969d378b8a647318f8cafb265296ae37de",
"04f7e9f1447968b284ff43548fcec3752063ea386b48bfabb9baf2f9c1caa05c2fb12c2cca37326ce27e68f8cc6414c2554895519c28da1ca21e61890d0bc525c4",
"b18e78e5072f301399dc1f4baf2956c0ed2d5f52f19abb1705131b0865b079031259ee6c629b4faed528bcfa1c5d2cbc",
},
}
func TestSM2SharedKeyVectors(t *testing.T) {
initiator := []byte("Alice")
responder := []byte("Bob")
kenLen := 48
for i, v := range vectors {
aliceSKey, err := ecdh.P256().NewPrivateKey(hexDecode(t, v.LocalStaticPriv))
if err != nil {
t.Fatal(err)
}
aliceEKey, err := ecdh.P256().NewPrivateKey(hexDecode(t, v.LocalEphemeralPriv))
if err != nil {
t.Fatal(err)
}
bobSKey, err := ecdh.P256().NewPrivateKey(hexDecode(t, v.RemoteStaticPriv))
if err != nil {
t.Fatal(err)
}
bobEKey, err := ecdh.P256().NewPrivateKey(hexDecode(t, v.RemoteEphemeralPriv))
if err != nil {
t.Fatal(err)
}
bobSecret, err := bobSKey.SM2MQV(bobEKey, aliceSKey.PublicKey(), aliceEKey.PublicKey())
if err != nil {
t.Fatal(err)
}
aliceSecret, err := aliceSKey.SM2MQV(aliceEKey, bobSKey.PublicKey(), bobEKey.PublicKey())
if err != nil {
t.Fatal(err)
}
if !aliceSecret.Equal(bobSecret) {
t.Error("two SM2MQV computations came out different")
}
if !bytes.Equal(aliceSecret.Bytes(), hexDecode(t, v.SharedSecret)) {
t.Errorf("%v shared secret is not expected.", i)
}
bobKey, err := bobSecret.SM2SharedKey(true, kenLen, bobSKey.PublicKey(), aliceSKey.PublicKey(), responder, initiator)
if err != nil {
t.Fatal(err)
}
aliceKey, err := aliceSecret.SM2SharedKey(false, kenLen, aliceSKey.PublicKey(), bobSKey.PublicKey(), initiator, responder)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(bobKey, aliceKey) {
t.Error("two SM2SharedKey computations came out different")
}
if !bytes.Equal(bobKey, hexDecode(t, v.Key)) {
t.Errorf("%v keying data is not expected.", i)
}
}
}
type countingReader struct {
r io.Reader
n int
}
func (r *countingReader) Read(p []byte) (int, error) {
n, err := r.r.Read(p)
r.n += n
return n, err
}
func TestGenerateKey(t *testing.T) {
r := &countingReader{r: rand.Reader}
k, err := ecdh.P256().GenerateKey(r)
if err != nil {
t.Fatal(err)
}
// GenerateKey does rejection sampling. If the masking works correctly,
// the probability of a rejection is 1-ord(G)/2^ceil(log2(ord(G))),
// which for all curves is small enough (at most 2^-32, for P-256) that
// a bit flip is more likely to make this test fail than bad luck.
// Account for the extra MaybeReadByte byte, too.
if got, expected := r.n, len(k.Bytes())+1; got > expected {
t.Errorf("expected GenerateKey to consume at most %v bytes, got %v", expected, got)
}
}
func TestString(t *testing.T) {
s := fmt.Sprintf("%s", ecdh.P256())
if s != "sm2p256v1" {
t.Errorf("unexpected Curve string encoding: %q", s)
}
}
func BenchmarkECDH(b *testing.B) {
benchmarkAllCurves(b, func(b *testing.B, curve ecdh.Curve) {
c, err := chacha20.NewUnauthenticatedCipher(make([]byte, 32), make([]byte, 12))
if err != nil {
b.Fatal(err)
}
rand := cipher.StreamReader{
S: c, R: zeroReader,
}
peerKey, err := curve.GenerateKey(rand)
if err != nil {
b.Fatal(err)
}
peerShare := peerKey.PublicKey().Bytes()
b.ResetTimer()
b.ReportAllocs()
var allocationsSink byte
for i := 0; i < b.N; i++ {
key, err := curve.GenerateKey(rand)
if err != nil {
b.Fatal(err)
}
share := key.PublicKey().Bytes()
peerPubKey, err := curve.NewPublicKey(peerShare)
if err != nil {
b.Fatal(err)
}
secret, err := key.ECDH(peerPubKey)
if err != nil {
b.Fatal(err)
}
allocationsSink ^= secret[0] ^ share[0]
}
})
}
func benchmarkAllCurves(b *testing.B, f func(b *testing.B, curve ecdh.Curve)) {
b.Run("SM2P256", func(b *testing.B) { f(b, ecdh.P256()) })
}
type zr struct{}
// Read replaces the contents of dst with zeros. It is safe for concurrent use.
func (zr) Read(dst []byte) (n int, err error) {
for i := range dst {
dst[i] = 0
}
return len(dst), nil
}
var zeroReader = zr{}

View File

@ -1,246 +1,246 @@
package ecdh
import (
"encoding/binary"
"errors"
"hash"
"io"
"math/bits"
"github.com/emmansun/gmsm/internal/randutil"
sm2ec "github.com/emmansun/gmsm/internal/sm2ec"
"github.com/emmansun/gmsm/internal/subtle"
)
type sm2Curve struct {
name string
newPoint func() *sm2ec.SM2P256Point
scalarOrderMinus1 []byte
constantA []byte
constantB []byte
generator []byte
}
func (c *sm2Curve) String() string {
return c.name
}
func (c *sm2Curve) GenerateKey(rand io.Reader) (*PrivateKey, error) {
key := make([]byte, len(c.scalarOrderMinus1))
randutil.MaybeReadByte(rand)
for {
if _, err := io.ReadFull(rand, key); err != nil {
return nil, err
}
// In tests, rand will return all zeros and NewPrivateKey will reject
// the zero key as it generates the identity as a public key. This also
// makes this function consistent with crypto/elliptic.GenerateKey.
key[1] ^= 0x42
k, err := c.NewPrivateKey(key)
if err == errInvalidPrivateKey {
continue
}
return k, err
}
}
func (c *sm2Curve) NewPrivateKey(key []byte) (*PrivateKey, error) {
if len(key) != len(c.scalarOrderMinus1) {
return nil, errors.New("ecdh: invalid private key size")
}
if subtle.ConstantTimeAllZero(key) == 1 || !isLess(key, c.scalarOrderMinus1) {
return nil, errInvalidPrivateKey
}
return &PrivateKey{
curve: c,
privateKey: append([]byte{}, key...),
}, nil
}
func (c *sm2Curve) privateKeyToPublicKey(key *PrivateKey) *PublicKey {
if key.curve != c {
panic("ecdh: internal error: converting the wrong key type")
}
p, err := c.newPoint().ScalarBaseMult(key.privateKey)
if err != nil {
// This is unreachable because the only error condition of
// ScalarBaseMult is if the input is not the right size.
panic("ecdh: internal error: sm2ec ScalarBaseMult failed for a fixed-size input")
}
publicKey := p.Bytes()
if len(publicKey) == 1 {
// The encoding of the identity is a single 0x00 byte. This is
// unreachable because the only scalar that generates the identity is
// zero, which is rejected by NewPrivateKey.
panic("ecdh: internal error: sm2ec ScalarBaseMult returned the identity")
}
return &PublicKey{
curve: key.curve,
publicKey: publicKey,
}
}
func (c *sm2Curve) NewPublicKey(key []byte) (*PublicKey, error) {
// Reject the point at infinity and compressed encodings.
if len(key) == 0 || key[0] != 4 {
return nil, errors.New("ecdh: invalid public key")
}
// SetBytes also checks that the point is on the curve.
if _, err := c.newPoint().SetBytes(key); err != nil {
return nil, err
}
return &PublicKey{
curve: c,
publicKey: append([]byte{}, key...),
}, nil
}
func (c *sm2Curve) ecdh(local *PrivateKey, remote *PublicKey) ([]byte, error) {
p, err := c.newPoint().SetBytes(remote.publicKey)
if err != nil {
return nil, err
}
if _, err := p.ScalarMult(p, local.privateKey); err != nil {
return nil, err
}
// BytesX will return an error if p is the point at infinity.
return p.BytesX()
}
func (c *sm2Curve) sm2avf(secret *PublicKey) []byte {
bytes := secret.publicKey[1:33]
var result [32]byte
copy(result[16:], bytes[16:])
result[16] = (result[16] & 0x7f) | 0x80
return result[:]
}
func (c *sm2Curve) sm2mqv(sLocal, eLocal *PrivateKey, sRemote, eRemote *PublicKey) (*PublicKey, error) {
// implicitSig: (sLocal + avf(eLocal.Pub) * ePriv) mod N
x2 := c.sm2avf(eLocal.PublicKey())
t, err := sm2ec.ImplicitSig(sLocal.privateKey, eLocal.privateKey, x2)
if err != nil {
return nil, err
}
// new base point: peerPub + [x1](peerSecret)
x1 := c.sm2avf(eRemote)
p2, err := c.newPoint().SetBytes(eRemote.publicKey)
if err != nil {
return nil, err
}
if _, err := p2.ScalarMult(p2, x1); err != nil {
return nil, err
}
p1, err := c.newPoint().SetBytes(sRemote.publicKey)
if err != nil {
return nil, err
}
p2.Add(p1, p2)
if _, err := p2.ScalarMult(p2, t); err != nil {
return nil, err
}
return c.NewPublicKey(p2.Bytes())
}
var defaultUID = []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38}
// CalculateZA ZA = H256(ENTLA || IDA || a || b || xG || yG || xA || yA).
// Compliance with GB/T 32918.2-2016 5.5
func (c *sm2Curve) sm2za(md hash.Hash, pub *PublicKey, uid []byte) ([]byte, error) {
if len(uid) == 0 {
uid = defaultUID
}
uidLen := len(uid)
if uidLen >= 0x2000 {
return nil, errors.New("ecdh: the uid is too long")
}
entla := uint16(uidLen) << 3
md.Write([]byte{byte(entla >> 8), byte(entla)})
if uidLen > 0 {
md.Write(uid)
}
md.Write(c.constantA)
md.Write(c.constantB)
md.Write(c.generator)
md.Write(pub.publicKey[1:])
return md.Sum(nil), nil
}
// P256 returns a [Curve] which implements SM2, also known as sm2p256v1
//
// Multiple invocations of this function will return the same value, so it can
// be used for equality checks and switch statements.
func P256() Curve { return sm2P256 }
var sm2P256 = &sm2Curve{
name: "sm2p256v1",
newPoint: sm2ec.NewSM2P256Point,
scalarOrderMinus1: sm2P256OrderMinus1,
generator: sm2Generator,
constantA: sm2ConstantA,
constantB: sm2ConstantB,
}
var sm2P256OrderMinus1 = []byte{
0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x72, 0x03, 0xdf, 0x6b, 0x21, 0xc6, 0x05, 0x2b,
0x53, 0xbb, 0xf4, 0x09, 0x39, 0xd5, 0x41, 0x22}
var sm2Generator = []byte{
0x32, 0xc4, 0xae, 0x2c, 0x1f, 0x19, 0x81, 0x19,
0x5f, 0x99, 0x4, 0x46, 0x6a, 0x39, 0xc9, 0x94,
0x8f, 0xe3, 0xb, 0xbf, 0xf2, 0x66, 0xb, 0xe1,
0x71, 0x5a, 0x45, 0x89, 0x33, 0x4c, 0x74, 0xc7,
0xbc, 0x37, 0x36, 0xa2, 0xf4, 0xf6, 0x77, 0x9c,
0x59, 0xbd, 0xce, 0xe3, 0x6b, 0x69, 0x21, 0x53,
0xd0, 0xa9, 0x87, 0x7c, 0xc6, 0x2a, 0x47, 0x40,
0x2, 0xdf, 0x32, 0xe5, 0x21, 0x39, 0xf0, 0xa0}
var sm2ConstantA = []byte{
0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc}
var sm2ConstantB = []byte{
0x28, 0xe9, 0xfa, 0x9e, 0x9d, 0x9f, 0x5e, 0x34,
0x4d, 0x5a, 0x9e, 0x4b, 0xcf, 0x65, 0x09, 0xa7,
0xf3, 0x97, 0x89, 0xf5, 0x15, 0xab, 0x8f, 0x92,
0xdd, 0xbc, 0xbd, 0x41, 0x4d, 0x94, 0x0e, 0x93}
// isLess returns whether a < b, where a and b are big-endian buffers of the
// same length and shorter than 72 bytes.
func isLess(a, b []byte) bool {
if len(a) != len(b) {
panic("ecdh: internal error: mismatched isLess inputs")
}
// Copy the values into a fixed-size preallocated little-endian buffer.
// 72 bytes is enough for every scalar in this package, and having a fixed
// size lets us avoid heap allocations.
if len(a) > 72 {
panic("ecdh: internal error: isLess input too large")
}
bufA, bufB := make([]byte, 72), make([]byte, 72)
for i := range a {
bufA[i], bufB[i] = a[len(a)-i-1], b[len(b)-i-1]
}
// Perform a subtraction with borrow.
var borrow uint64
for i := 0; i < len(bufA); i += 8 {
limbA, limbB := binary.LittleEndian.Uint64(bufA[i:]), binary.LittleEndian.Uint64(bufB[i:])
_, borrow = bits.Sub64(limbA, limbB, borrow)
}
// If there is a borrow at the end of the operation, then a < b.
return borrow == 1
}
var errInvalidPrivateKey = errors.New("ecdh: invalid private key")
package ecdh
import (
"encoding/binary"
"errors"
"hash"
"io"
"math/bits"
"github.com/emmansun/gmsm/internal/randutil"
sm2ec "github.com/emmansun/gmsm/internal/sm2ec"
"github.com/emmansun/gmsm/internal/subtle"
)
type sm2Curve struct {
name string
newPoint func() *sm2ec.SM2P256Point
scalarOrder []byte
constantA []byte
constantB []byte
generator []byte
}
func (c *sm2Curve) String() string {
return c.name
}
func (c *sm2Curve) GenerateKey(rand io.Reader) (*PrivateKey, error) {
key := make([]byte, len(c.scalarOrder))
randutil.MaybeReadByte(rand)
for {
if _, err := io.ReadFull(rand, key); err != nil {
return nil, err
}
// In tests, rand will return all zeros and NewPrivateKey will reject
// the zero key as it generates the identity as a public key. This also
// makes this function consistent with crypto/elliptic.GenerateKey.
key[1] ^= 0x42
k, err := c.NewPrivateKey(key)
if err == errInvalidPrivateKey {
continue
}
return k, err
}
}
func (c *sm2Curve) NewPrivateKey(key []byte) (*PrivateKey, error) {
if len(key) != len(c.scalarOrder) {
return nil, errors.New("ecdh: invalid private key size")
}
if subtle.ConstantTimeAllZero(key) || !isLess(key, c.scalarOrder) {
return nil, errInvalidPrivateKey
}
return &PrivateKey{
curve: c,
privateKey: append([]byte{}, key...),
}, nil
}
func (c *sm2Curve) privateKeyToPublicKey(key *PrivateKey) *PublicKey {
if key.curve != c {
panic("ecdh: internal error: converting the wrong key type")
}
p, err := c.newPoint().ScalarBaseMult(key.privateKey)
if err != nil {
// This is unreachable because the only error condition of
// ScalarBaseMult is if the input is not the right size.
panic("ecdh: internal error: sm2ec ScalarBaseMult failed for a fixed-size input")
}
publicKey := p.Bytes()
if len(publicKey) == 1 {
// The encoding of the identity is a single 0x00 byte. This is
// unreachable because the only scalar that generates the identity is
// zero, which is rejected by NewPrivateKey.
panic("ecdh: internal error: sm2ec ScalarBaseMult returned the identity")
}
return &PublicKey{
curve: key.curve,
publicKey: publicKey,
}
}
func (c *sm2Curve) NewPublicKey(key []byte) (*PublicKey, error) {
// Reject the point at infinity and compressed encodings.
if len(key) == 0 || key[0] != 4 {
return nil, errors.New("ecdh: invalid public key")
}
// SetBytes also checks that the point is on the curve.
if _, err := c.newPoint().SetBytes(key); err != nil {
return nil, err
}
return &PublicKey{
curve: c,
publicKey: append([]byte{}, key...),
}, nil
}
func (c *sm2Curve) ecdh(local *PrivateKey, remote *PublicKey) ([]byte, error) {
p, err := c.newPoint().SetBytes(remote.publicKey)
if err != nil {
return nil, err
}
if _, err := p.ScalarMult(p, local.privateKey); err != nil {
return nil, err
}
// BytesX will return an error if p is the point at infinity.
return p.BytesX()
}
func (c *sm2Curve) sm2avf(secret *PublicKey) []byte {
bytes := secret.publicKey[1:33]
var result [32]byte
copy(result[16:], bytes[16:])
result[16] = (result[16] & 0x7f) | 0x80
return result[:]
}
func (c *sm2Curve) sm2mqv(sLocal, eLocal *PrivateKey, sRemote, eRemote *PublicKey) (*PublicKey, error) {
// implicitSig: (sLocal + avf(eLocal.Pub) * ePriv) mod N
x2 := c.sm2avf(eLocal.PublicKey())
t, err := sm2ec.ImplicitSig(sLocal.privateKey, eLocal.privateKey, x2)
if err != nil {
return nil, err
}
// new base point: peerPub + [x1](peerSecret)
x1 := c.sm2avf(eRemote)
p2, err := c.newPoint().SetBytes(eRemote.publicKey)
if err != nil {
return nil, err
}
if _, err := p2.ScalarMult(p2, x1); err != nil {
return nil, err
}
p1, err := c.newPoint().SetBytes(sRemote.publicKey)
if err != nil {
return nil, err
}
p2.Add(p1, p2)
if _, err := p2.ScalarMult(p2, t); err != nil {
return nil, err
}
return c.NewPublicKey(p2.Bytes())
}
var defaultUID = []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38}
// CalculateZA ZA = H256(ENTLA || IDA || a || b || xG || yG || xA || yA).
// Compliance with GB/T 32918.2-2016 5.5
func (c *sm2Curve) sm2za(md hash.Hash, pub *PublicKey, uid []byte) ([]byte, error) {
if len(uid) == 0 {
uid = defaultUID
}
uidLen := len(uid)
if uidLen >= 0x2000 {
return nil, errors.New("ecdh: the uid is too long")
}
entla := uint16(uidLen) << 3
md.Write([]byte{byte(entla >> 8), byte(entla)})
if uidLen > 0 {
md.Write(uid)
}
md.Write(c.constantA)
md.Write(c.constantB)
md.Write(c.generator)
md.Write(pub.publicKey[1:])
return md.Sum(nil), nil
}
// P256 returns a Curve which implements SM2, also known as sm2p256v1
//
// Multiple invocations of this function will return the same value, so it can
// be used for equality checks and switch statements.
func P256() Curve { return sm2P256 }
var sm2P256 = &sm2Curve{
name: "sm2p256v1",
newPoint: sm2ec.NewSM2P256Point,
scalarOrder: sm2P256Order,
generator: sm2Generator,
constantA: sm2ConstantA,
constantB: sm2ConstantB,
}
var sm2P256Order = []byte{
0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x72, 0x03, 0xdf, 0x6b, 0x21, 0xc6, 0x05, 0x2b,
0x53, 0xbb, 0xf4, 0x09, 0x39, 0xd5, 0x41, 0x23}
var sm2Generator = []byte{
0x32, 0xc4, 0xae, 0x2c, 0x1f, 0x19, 0x81, 0x19,
0x5f, 0x99, 0x4, 0x46, 0x6a, 0x39, 0xc9, 0x94,
0x8f, 0xe3, 0xb, 0xbf, 0xf2, 0x66, 0xb, 0xe1,
0x71, 0x5a, 0x45, 0x89, 0x33, 0x4c, 0x74, 0xc7,
0xbc, 0x37, 0x36, 0xa2, 0xf4, 0xf6, 0x77, 0x9c,
0x59, 0xbd, 0xce, 0xe3, 0x6b, 0x69, 0x21, 0x53,
0xd0, 0xa9, 0x87, 0x7c, 0xc6, 0x2a, 0x47, 0x40,
0x2, 0xdf, 0x32, 0xe5, 0x21, 0x39, 0xf0, 0xa0}
var sm2ConstantA = []byte{
0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc}
var sm2ConstantB = []byte{
0x28, 0xe9, 0xfa, 0x9e, 0x9d, 0x9f, 0x5e, 0x34,
0x4d, 0x5a, 0x9e, 0x4b, 0xcf, 0x65, 0x09, 0xa7,
0xf3, 0x97, 0x89, 0xf5, 0x15, 0xab, 0x8f, 0x92,
0xdd, 0xbc, 0xbd, 0x41, 0x4d, 0x94, 0x0e, 0x93}
// isLess returns whether a < b, where a and b are big-endian buffers of the
// same length and shorter than 72 bytes.
func isLess(a, b []byte) bool {
if len(a) != len(b) {
panic("ecdh: internal error: mismatched isLess inputs")
}
// Copy the values into a fixed-size preallocated little-endian buffer.
// 72 bytes is enough for every scalar in this package, and having a fixed
// size lets us avoid heap allocations.
if len(a) > 72 {
panic("ecdh: internal error: isLess input too large")
}
bufA, bufB := make([]byte, 72), make([]byte, 72)
for i := range a {
bufA[i], bufB[i] = a[len(a)-i-1], b[len(b)-i-1]
}
// Perform a subtraction with borrow.
var borrow uint64
for i := 0; i < len(bufA); i += 8 {
limbA, limbB := binary.LittleEndian.Uint64(bufA[i:]), binary.LittleEndian.Uint64(bufB[i:])
_, borrow = bits.Sub64(limbA, limbB, borrow)
}
// If there is a borrow at the end of the operation, then a < b.
return borrow == 1
}
var errInvalidPrivateKey = errors.New("ecdh: invalid private key")

9
go.mod
View File

@ -1,7 +1,8 @@
module github.com/emmansun/gmsm
go 1.23.0
go 1.16
require golang.org/x/crypto v0.39.0
require golang.org/x/sys v0.33.0 // indirect
require (
golang.org/x/crypto v0.5.0
golang.org/x/sys v0.4.0
)

34
go.sum
View File

@ -1,4 +1,30 @@
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -1,42 +1,42 @@
package alias
import "testing"
var a, b [100]byte
var aliasingTests = []struct {
x, y []byte
anyOverlap, inexactOverlap bool
}{
{a[:], b[:], false, false},
{a[:], b[:0], false, false},
{a[:], b[:50], false, false},
{a[40:50], a[50:60], false, false},
{a[40:50], a[60:70], false, false},
{a[:51], a[50:], true, true},
{a[:], a[:], true, false},
{a[:50], a[:60], true, false},
{a[:], nil, false, false},
{nil, nil, false, false},
{a[:], a[:0], false, false},
{a[:10], a[:10:20], true, false},
{a[:10], a[5:10:20], true, true},
}
func testAliasing(t *testing.T, i int, x, y []byte, anyOverlap, inexactOverlap bool) {
any := AnyOverlap(x, y)
if any != anyOverlap {
t.Errorf("%d: wrong AnyOverlap result, expected %v, got %v", i, anyOverlap, any)
}
inexact := InexactOverlap(x, y)
if inexact != inexactOverlap {
t.Errorf("%d: wrong InexactOverlap result, expected %v, got %v", i, inexactOverlap, any)
}
}
func TestAliasing(t *testing.T) {
for i, tt := range aliasingTests {
testAliasing(t, i, tt.x, tt.y, tt.anyOverlap, tt.inexactOverlap)
testAliasing(t, i, tt.y, tt.x, tt.anyOverlap, tt.inexactOverlap)
}
}
package alias
import "testing"
var a, b [100]byte
var aliasingTests = []struct {
x, y []byte
anyOverlap, inexactOverlap bool
}{
{a[:], b[:], false, false},
{a[:], b[:0], false, false},
{a[:], b[:50], false, false},
{a[40:50], a[50:60], false, false},
{a[40:50], a[60:70], false, false},
{a[:51], a[50:], true, true},
{a[:], a[:], true, false},
{a[:50], a[:60], true, false},
{a[:], nil, false, false},
{nil, nil, false, false},
{a[:], a[:0], false, false},
{a[:10], a[:10:20], true, false},
{a[:10], a[5:10:20], true, true},
}
func testAliasing(t *testing.T, i int, x, y []byte, anyOverlap, inexactOverlap bool) {
any := AnyOverlap(x, y)
if any != anyOverlap {
t.Errorf("%d: wrong AnyOverlap result, expected %v, got %v", i, anyOverlap, any)
}
inexact := InexactOverlap(x, y)
if inexact != inexactOverlap {
t.Errorf("%d: wrong InexactOverlap result, expected %v, got %v", i, inexactOverlap, any)
}
}
func TestAliasing(t *testing.T) {
for i, tt := range aliasingTests {
testAliasing(t, i, tt.x, tt.y, tt.anyOverlap, tt.inexactOverlap)
testAliasing(t, i, tt.y, tt.x, tt.anyOverlap, tt.inexactOverlap)
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,52 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !purego
#include "textflag.h"
// func addMulVVW256(z, x *uint, y uint) (c uint)
TEXT ·addMulVVW256(SB), $0-16
MOVL $8, BX
JMP addMulVVWy(SB)
// func addMulVVW1024(z, x *uint, y uint) (c uint)
TEXT ·addMulVVW1024(SB), $0-16
MOVL $32, BX
JMP addMulVVWy(SB)
// func addMulVVW1536(z, x *uint, y uint) (c uint)
TEXT ·addMulVVW1536(SB), $0-16
MOVL $48, BX
JMP addMulVVWy(SB)
// func addMulVVW2048(z, x *uint, y uint) (c uint)
TEXT ·addMulVVW2048(SB), $0-16
MOVL $64, BX
JMP addMulVVWy(SB)
TEXT addMulVVWy(SB), NOFRAME|NOSPLIT, $0
MOVL z+0(FP), DI
MOVL x+4(FP), SI
MOVL y+8(FP), BP
LEAL (DI)(BX*4), DI
LEAL (SI)(BX*4), SI
NEGL BX // i = -n
MOVL $0, CX // c = 0
JMP E6
L6: MOVL (SI)(BX*4), AX
MULL BP
ADDL CX, AX
ADCL $0, DX
ADDL AX, (DI)(BX*4)
ADCL $0, DX
MOVL DX, CX
ADDL $1, BX // i++
E6: CMPL BX, $0 // i < 0
JL L6
MOVL CX, c+12(FP)
RET

View File

@ -0,0 +1,6 @@
//go:build amd64 && gc && !purego
// +build amd64,gc,!purego
package bigmod
func montgomeryLoop(d []uint, a []uint, b []uint, m []uint, m0inv uint) uint

File diff suppressed because it is too large Load Diff

View File

@ -1,52 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !purego
#include "textflag.h"
// func addMulVVW256(z, x *uint, y uint) (c uint)
TEXT ·addMulVVW256(SB), $0-16
MOVW $8, R5
JMP addMulVVWy(SB)
// func addMulVVW1024(z, x *uint, y uint) (c uint)
TEXT ·addMulVVW1024(SB), $0-16
MOVW $32, R5
JMP addMulVVWy(SB)
// func addMulVVW1536(z, x *uint, y uint) (c uint)
TEXT ·addMulVVW1536(SB), $0-16
MOVW $48, R5
JMP addMulVVWy(SB)
// func addMulVVW2048(z, x *uint, y uint) (c uint)
TEXT ·addMulVVW2048(SB), $0-16
MOVW $64, R5
JMP addMulVVWy(SB)
TEXT addMulVVWy(SB), NOFRAME|NOSPLIT, $0
MOVW $0, R0
MOVW z+0(FP), R1
MOVW x+4(FP), R2
MOVW y+8(FP), R3
ADD R5<<2, R1, R5
MOVW $0, R4
B E9
L9: MOVW.P 4(R2), R6
MULLU R6, R3, (R7, R6)
ADD.S R4, R6
ADC R0, R7
MOVW 0(R1), R4
ADD.S R4, R6
ADC R0, R7
MOVW.P R6, 4(R1)
MOVW R7, R4
E9: TEQ R1, R5
BNE L9
MOVW R4, c+12(FP)
RET

View File

@ -1,74 +0,0 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !purego
#include "textflag.h"
// func addMulVVW256(z, x *uint, y uint) (c uint)
TEXT ·addMulVVW256(SB), $0-32
MOVD $4, R0
JMP addMulVVWy(SB)
// func addMulVVW1024(z, x *uint, y uint) (c uint)
TEXT ·addMulVVW1024(SB), $0-32
MOVD $16, R0
JMP addMulVVWy(SB)
// func addMulVVW1536(z, x *uint, y uint) (c uint)
TEXT ·addMulVVW1536(SB), $0-32
MOVD $24, R0
JMP addMulVVWy(SB)
// func addMulVVW2048(z, x *uint, y uint) (c uint)
TEXT ·addMulVVW2048(SB), $0-32
MOVD $32, R0
JMP addMulVVWy(SB)
TEXT addMulVVWy(SB), NOFRAME|NOSPLIT, $0
MOVD z+0(FP), R1
MOVD x+8(FP), R2
MOVD y+16(FP), R3
MOVD $0, R4
// The main loop of this code operates on a block of 4 words every iteration
// performing [R4:R12:R11:R10:R9] = R4 + R3 * [R8:R7:R6:R5] + [R12:R11:R10:R9]
// where R4 is carried from the previous iteration, R8:R7:R6:R5 hold the next
// 4 words of x, R3 is y and R12:R11:R10:R9 are part of the result z.
loop:
CBZ R0, done
LDP.P 16(R2), (R5, R6)
LDP.P 16(R2), (R7, R8)
LDP (R1), (R9, R10)
ADDS R4, R9
MUL R6, R3, R14
ADCS R14, R10
MUL R7, R3, R15
LDP 16(R1), (R11, R12)
ADCS R15, R11
MUL R8, R3, R16
ADCS R16, R12
UMULH R8, R3, R20
ADC $0, R20
MUL R5, R3, R13
ADDS R13, R9
UMULH R5, R3, R17
ADCS R17, R10
UMULH R6, R3, R21
STP.P (R9, R10), 16(R1)
ADCS R21, R11
UMULH R7, R3, R19
ADCS R19, R12
STP.P (R11, R12), 16(R1)
ADC $0, R20, R4
SUB $4, R0
B loop
done:
MOVD R4, c+24(FP)
RET

View File

@ -1,31 +0,0 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !purego && (386 || amd64 || arm || arm64 || ppc64 || ppc64le || riscv64 || s390x)
package bigmod
import "github.com/emmansun/gmsm/internal/deps/cpu"
// amd64 assembly uses ADCX/ADOX/MULX if ADX is available to run two carry
// chains in the flags in parallel across the whole operation, and aggressively
// unrolls loops. arm64 processes four words at a time.
//
// It's unclear why the assembly for all other architectures, as well as for
// amd64 without ADX, perform better than the compiler output.
// TODO(filippo): file cmd/compile performance issue.
var supportADX = cpu.X86.HasADX && cpu.X86.HasBMI2
//go:noescape
func addMulVVW256(z, x *uint, y uint) (c uint)
//go:noescape
func addMulVVW1024(z, x *uint, y uint) (c uint)
//go:noescape
func addMulVVW1536(z, x *uint, y uint) (c uint)
//go:noescape
func addMulVVW2048(z, x *uint, y uint) (c uint)

View File

@ -1,31 +0,0 @@
package bigmod
func (x *Nat) Set(y *Nat) *Nat {
return x.set(y)
}
// SetOverflowedBytes assigns x = (b mode (m-1)) + 1, where b is a slice of big-endian bytes.
//
// The output will be resized to the size of m and overwritten.
//
//go:norace
func (x *Nat) SetOverflowedBytes(b []byte, m *Modulus) *Nat {
mMinusOne := NewNat().set(m.nat)
mMinusOne.limbs[0]-- // due to m is odd, so we can safely subtract 1
mMinusOneM, _ := NewModulus(mMinusOne.Bytes(m))
one := NewNat().resetFor(m)
one.limbs[0] = 1
x.resetToBytes(b)
x = NewNat().Mod(x, mMinusOneM) // x = x mod (m-1)
x.add(one) // we can safely add 1, no need to check overflow
return x
}
// CmpGeq returns 1 if x >= y, and 0 otherwise.
//
// Both operands must have the same announced length.
//
//go:norace
func (x *Nat) CmpGeq(y *Nat) choice {
return x.cmpGeq(y)
}

View File

@ -1,42 +1,12 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build purego || !(386 || amd64 || arm || arm64 || ppc64 || ppc64le || riscv64 || s390x)
package bigmod
import "unsafe"
// TODO: will use unsafe.Slice directly once upgrade golang sdk to 1.17+
func slice256(ptr *uint) []uint {
return (*[256 / _W]uint)(unsafe.Pointer(ptr))[:]
}
func slice1024(ptr *uint) []uint {
return (*[1024 / _W]uint)(unsafe.Pointer(ptr))[:]
}
func slice1536(ptr *uint) []uint {
return (*[1536 / _W]uint)(unsafe.Pointer(ptr))[:]
}
func slice2048(ptr *uint) []uint {
return (*[2048 / _W]uint)(unsafe.Pointer(ptr))[:]
}
func addMulVVW256(z, x *uint, y uint) (c uint) {
return addMulVVW(slice256(z), slice256(x), y)
}
func addMulVVW1024(z, x *uint, y uint) (c uint) {
return addMulVVW(slice1024(z), slice1024(x), y)
}
func addMulVVW1536(z, x *uint, y uint) (c uint) {
return addMulVVW(slice1536(z), slice1536(x), y)
}
func addMulVVW2048(z, x *uint, y uint) (c uint) {
return addMulVVW(slice2048(z), slice2048(x), y)
}
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !amd64 || !gc || purego
// +build !amd64 !gc purego
package bigmod
func montgomeryLoop(d, a, b, m []uint, m0inv uint) uint {
return montgomeryLoopGeneric(d, a, b, m, m0inv)
}

View File

@ -1,87 +0,0 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !purego && (ppc64 || ppc64le)
#include "textflag.h"
// func addMulVVW256(z, x *uint, y uint) (c uint)
TEXT ·addMulVVW256(SB), $0-32
MOVD $1, R6 // R6 = z_len/4
JMP addMulVVWy<>(SB)
// func addMulVVW1024(z, x *uint, y uint) (c uint)
TEXT ·addMulVVW1024(SB), $0-32
MOVD $4, R6 // R6 = z_len/4
JMP addMulVVWy<>(SB)
// func addMulVVW1536(z, x *uint, y uint) (c uint)
TEXT ·addMulVVW1536(SB), $0-32
MOVD $6, R6 // R6 = z_len/4
JMP addMulVVWy<>(SB)
// func addMulVVW2048(z, x *uint, y uint) (c uint)
TEXT ·addMulVVW2048(SB), $0-32
MOVD $8, R6 // R6 = z_len/4
JMP addMulVVWy<>(SB)
// This local function expects to be called only by
// callers above. R6 contains the z length/4
// since 4 values are processed for each
// loop iteration, and is guaranteed to be > 0.
// If other callers are added this function might
// need to change.
TEXT addMulVVWy<>(SB), NOSPLIT, $0
MOVD z+0(FP), R3
MOVD x+8(FP), R4
MOVD y+16(FP), R5
MOVD $0, R9 // R9 = c = 0
MOVD R6, CTR // Initialize loop counter
PCALIGN $16
loop:
MOVD 0(R4), R14 // x[i]
MOVD 8(R4), R16 // x[i+1]
MOVD 16(R4), R18 // x[i+2]
MOVD 24(R4), R20 // x[i+3]
MOVD 0(R3), R15 // z[i]
MOVD 8(R3), R17 // z[i+1]
MOVD 16(R3), R19 // z[i+2]
MOVD 24(R3), R21 // z[i+3]
MULLD R5, R14, R10 // low x[i]*y
MULHDU R5, R14, R11 // high x[i]*y
ADDC R15, R10
ADDZE R11
ADDC R9, R10
ADDZE R11, R9
MULLD R5, R16, R14 // low x[i+1]*y
MULHDU R5, R16, R15 // high x[i+1]*y
ADDC R17, R14
ADDZE R15
ADDC R9, R14
ADDZE R15, R9
MULLD R5, R18, R16 // low x[i+2]*y
MULHDU R5, R18, R17 // high x[i+2]*y
ADDC R19, R16
ADDZE R17
ADDC R9, R16
ADDZE R17, R9
MULLD R5, R20, R18 // low x[i+3]*y
MULHDU R5, R20, R19 // high x[i+3]*y
ADDC R21, R18
ADDZE R19
ADDC R9, R18
ADDZE R19, R9
MOVD R10, 0(R3) // z[i]
MOVD R14, 8(R3) // z[i+1]
MOVD R16, 16(R3) // z[i+2]
MOVD R18, 24(R3) // z[i+3]
ADD $32, R3
ADD $32, R4
BDNZ loop
done:
MOVD R9, c+24(FP)
RET

View File

@ -1,96 +0,0 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !purego
#include "textflag.h"
// func addMulVVW256(z, x *uint, y uint) (c uint)
TEXT ·addMulVVW256(SB),$0-32
MOV $4, X30
JMP addMulVVWy(SB)
// func addMulVVW1024(z, x *uint, y uint) (c uint)
TEXT ·addMulVVW1024(SB),$0-32
MOV $16, X30
JMP addMulVVWy(SB)
// func addMulVVW1536(z, x *uint, y uint) (c uint)
TEXT ·addMulVVW1536(SB),$0-32
MOV $24, X30
JMP addMulVVWy(SB)
// func addMulVVW2048(z, x *uint, y uint) (c uint)
TEXT ·addMulVVW2048(SB),$0-32
MOV $32, X30
JMP addMulVVWy(SB)
TEXT addMulVVWy(SB),NOFRAME|NOSPLIT,$0
MOV z+0(FP), X5
MOV x+8(FP), X7
MOV y+16(FP), X6
MOV $0, X29
BEQZ X30, done
loop:
MOV 0*8(X5), X10 // z[0]
MOV 1*8(X5), X13 // z[1]
MOV 2*8(X5), X16 // z[2]
MOV 3*8(X5), X19 // z[3]
MOV 0*8(X7), X8 // x[0]
MOV 1*8(X7), X11 // x[1]
MOV 2*8(X7), X14 // x[2]
MOV 3*8(X7), X17 // x[3]
MULHU X8, X6, X9 // z_hi[0] = x[0] * y
MUL X8, X6, X8 // z_lo[0] = x[0] * y
ADD X8, X10, X21 // z_lo[0] = x[0] * y + z[0]
SLTU X8, X21, X22
ADD X9, X22, X9 // z_hi[0] = x[0] * y + z[0]
ADD X21, X29, X10 // z_lo[0] = x[0] * y + z[0] + c
SLTU X21, X10, X22
ADD X9, X22, X29 // next c
MULHU X11, X6, X12 // z_hi[1] = x[1] * y
MUL X11, X6, X11 // z_lo[1] = x[1] * y
ADD X11, X13, X21 // z_lo[1] = x[1] * y + z[1]
SLTU X11, X21, X22
ADD X12, X22, X12 // z_hi[1] = x[1] * y + z[1]
ADD X21, X29, X13 // z_lo[1] = x[1] * y + z[1] + c
SLTU X21, X13, X22
ADD X12, X22, X29 // next c
MULHU X14, X6, X15 // z_hi[2] = x[2] * y
MUL X14, X6, X14 // z_lo[2] = x[2] * y
ADD X14, X16, X21 // z_lo[2] = x[2] * y + z[2]
SLTU X14, X21, X22
ADD X15, X22, X15 // z_hi[2] = x[2] * y + z[2]
ADD X21, X29, X16 // z_lo[2] = x[2] * y + z[2] + c
SLTU X21, X16, X22
ADD X15, X22, X29 // next c
MULHU X17, X6, X18 // z_hi[3] = x[3] * y
MUL X17, X6, X17 // z_lo[3] = x[3] * y
ADD X17, X19, X21 // z_lo[3] = x[3] * y + z[3]
SLTU X17, X21, X22
ADD X18, X22, X18 // z_hi[3] = x[3] * y + z[3]
ADD X21, X29, X19 // z_lo[3] = x[3] * y + z[3] + c
SLTU X21, X19, X22
ADD X18, X22, X29 // next c
MOV X10, 0*8(X5) // z[0]
MOV X13, 1*8(X5) // z[1]
MOV X16, 2*8(X5) // z[2]
MOV X19, 3*8(X5) // z[3]
ADD $32, X5
ADD $32, X7
SUB $4, X30
BNEZ X30, loop
done:
MOV X29, c+24(FP)
RET

View File

@ -1,90 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !purego
#include "textflag.h"
// func addMulVVW256(z, x *uint, y uint) (c uint)
TEXT ·addMulVVW256(SB), $0-32
MOVD $4, R5
JMP addMulVVWy(SB)
// func addMulVVW1024(z, x *uint, y uint) (c uint)
TEXT ·addMulVVW1024(SB), $0-32
MOVD $16, R5
JMP addMulVVWy(SB)
// func addMulVVW1536(z, x *uint, y uint) (c uint)
TEXT ·addMulVVW1536(SB), $0-32
MOVD $24, R5
JMP addMulVVWy(SB)
// func addMulVVW2048(z, x *uint, y uint) (c uint)
TEXT ·addMulVVW2048(SB), $0-32
MOVD $32, R5
JMP addMulVVWy(SB)
TEXT addMulVVWy(SB), NOFRAME|NOSPLIT, $0
MOVD z+0(FP), R2
MOVD x+8(FP), R8
MOVD y+16(FP), R9
MOVD $0, R1 // i*8 = 0
MOVD $0, R7 // i = 0
MOVD $0, R0 // make sure it's zero
MOVD $0, R4 // c = 0
MOVD R5, R12
AND $-2, R12
CMPBGE R5, $2, A6
BR E6
A6:
MOVD (R8)(R1*1), R6
MULHDU R9, R6
MOVD (R2)(R1*1), R10
ADDC R10, R11 // add to low order bits
ADDE R0, R6
ADDC R4, R11
ADDE R0, R6
MOVD R6, R4
MOVD R11, (R2)(R1*1)
MOVD (8)(R8)(R1*1), R6
MULHDU R9, R6
MOVD (8)(R2)(R1*1), R10
ADDC R10, R11 // add to low order bits
ADDE R0, R6
ADDC R4, R11
ADDE R0, R6
MOVD R6, R4
MOVD R11, (8)(R2)(R1*1)
ADD $16, R1 // i*8 + 8
ADD $2, R7 // i++
CMPBLT R7, R12, A6
BR E6
L6:
// TODO: drop unused single-step loop.
MOVD (R8)(R1*1), R6
MULHDU R9, R6
MOVD (R2)(R1*1), R10
ADDC R10, R11 // add to low order bits
ADDE R0, R6
ADDC R4, R11
ADDE R0, R6
MOVD R6, R4
MOVD R11, (R2)(R1*1)
ADD $8, R1 // i*8 + 8
ADD $1, R7 // i++
E6:
CMPBLT R7, R5, L6 // i < n
MOVD R4, c+24(FP)
RET

File diff suppressed because it is too large Load Diff

View File

@ -1,115 +0,0 @@
# ModInv tests.
#
# These test vectors satisfy ModInv * A = 1 (mod M) and 0 <= ModInv < M.
ModInv = 00
A = 00
M = 01
ModInv = 00
A = 01
M = 01
ModInv = 00
A = 02
M = 01
ModInv = 00
A = 03
M = 01
ModInv = 64
A = 54
M = e3
ModInv = 13
A = 2b
M = 30
ModInv = 2f
A = 30
M = 37
ModInv = 4
A = 13
M = 4b
ModInv = 1c47
A = cd4
M = 6a21
ModInv = 2b97
A = 8e7
M = 49c0
ModInv = 29b9
A = fcb
M = 3092
ModInv = a83
A = 14bf
M = 41ae
ModInv = 18f15fe1
A = 11b5d53e
M = 322e92a1
ModInv = 32f9453b
A = 8af6df6
M = 33d45eb7
ModInv = d696369
A = c5f89dd5
M = fc09c17c
ModInv = 622839d8
A = 60c2526
M = 74200493
ModInv = fb5a8aee7bbc4ef
A = 24ebd835a70be4e2
M = 9c7256574e0c5e93
ModInv = 846bc225402419c
A = 23026003ab1fbdb
M = 1683cbe32779c59b
ModInv = 5ff84f63a78982f9
A = 4a2420dc733e1a0f
M = a73c6bfabefa09e6
ModInv = 133e74d28ef42b43
A = 2e9511ae29cdd41
M = 15234df99f19fcda
ModInv = 46ae1fabe9521e4b99b198fc8439609023aa69be2247c0d1e27c2a0ea332f9c5
A = 6331fec5f01014046788c919ed50dc86ac7a80c085f1b6f645dd179c0f0dc9cd
M = 8ef409de82318259a8655a39293b1e762fa2cc7e0aeb4c59713a1e1fff6af640
ModInv = 444ccea3a7b21677dd294d34de53cc8a5b51e69b37782310a00fc6bcc975709b
A = 679280bd880994c08322143a4ea8a0825d0466fda1bb6b3eb86fc8e90747512b
M = e4fecab84b365c63a0dab4244ce3f921a9c87ec64d69a2031939f55782e99a2e
ModInv = 1ac7d7a03ceec5f690f567c9d61bf3469c078285bcc5cf00ac944596e887ca17
A = 1593ef32d9c784f5091bdff952f5c5f592a3aed6ba8ea865efa6d7df87be1805
M = 1e276882f90c95e0c1976eb079f97af075445b1361c02018d6bd7191162e67b2
ModInv = 639108b90dfe946f498be21303058413bbb0e59d0bd6a6115788705abd0666d6
A = 9258d6238e4923d120b2d1033573ffcac691526ad0842a3b174dccdbb79887bd
M = ce62909c39371d463aaba3d4b72ea6da49cb9b529e39e1972ef3ccd9a66fe08f
ModInv = aebde7654cb17833a106231c4b9e2f519140e85faee1bfb4192830f03f385e773c0f4767e93e874ffdc3b7a6b7e6a710e5619901c739ee8760a26128e8c91ef8cf761d0e505d8b28ae078d17e6071c372893bb7b72538e518ebc57efa70b7615e406756c49729b7c6e74f84aed7a316b6fa748ff4b9f143129d29dad1bff98bb
A = a29dacaf5487d354280fdd2745b9ace4cd50f2bde41d0ee529bf26a1913244f708085452ff32feab19a7418897990da46a0633f7c8375d583367319091bbbe069b0052c5e48a7daac9fb650db5af768cd2508ec3e2cda7456d4b9ce1c39459627a8b77e038b826cd7e326d0685b0cd0cb50f026f18300dae9f5fd42aa150ee8b
M = d686f9b86697313251685e995c09b9f1e337ddfaa050bd2df15bf4ca1dc46c5565021314765299c434ea1a6ec42bf92a29a7d1ffff599f4e50b79a82243fb24813060580c770d4c1140aeb2ab2685007e948b6f1f62e8001a0545619477d498132c907774479f6d95899e6251e7136f79ab6d3b7c82e4aca421e7d22fe7db19c
ModInv = 1ec872f4f20439e203597ca4de9d1296743f95781b2fe85d5def808558bbadef02a46b8955f47c83e1625f8bb40228eab09cad2a35c9ad62ab77a30e3932872959c5898674162da244a0ec1f68c0ed89f4b0f3572bfdc658ad15bf1b1c6e1176b0784c9935bd3ff1f49bb43753eacee1d8ca1c0b652d39ec727da83984fe3a0f
A = 2e527b0a1dc32460b2dd94ec446c692989f7b3c7451a5cbeebf69fc0ea9c4871fbe78682d5dc5b66689f7ed889b52161cd9830b589a93d21ab26dbede6c33959f5a0f0d107169e2daaac78bac8cf2d41a1eb1369cb6dc9e865e73bb2e51b886f4e896082db199175e3dde0c4ed826468f238a77bd894245d0918efc9ca84f945
M = b13133a9ebe0645f987d170c077eea2aa44e85c9ab10386d02867419a590cb182d9826a882306c212dbe75225adde23f80f5b37ca75ed09df20fc277cc7fbbfac8d9ef37a50f6b68ea158f5447283618e64e1426406d26ea85232afb22bf546c75018c1c55cb84c374d58d9d44c0a13ba88ac2e387765cb4c3269e3a983250fa
ModInv = 30ffa1876313a69de1e4e6ee132ea1d3a3da32f3b56f5cfb11402b0ad517dce605cf8e91d69fa375dd887fa8507bd8a28b2d5ce745799126e86f416047709f93f07fbd88918a047f13100ea71b1d48f6fc6d12e5c917646df3041b302187af641eaedf4908abc36f12c204e1526a7d80e96e302fb0779c28d7da607243732f26
A = 31157208bde6b85ebecaa63735947b3b36fa351b5c47e9e1c40c947339b78bf96066e5dbe21bb42629e6fcdb81f5f88db590bfdd5f4c0a6a0c3fc6377e5c1fd8235e46e291c688b6d6ecfb36604891c2a7c9cbcc58c26e44b43beecb9c5044b58bb58e35de3cf1128f3c116534fe4e421a33f83603c3df1ae36ec88092f67f2a
M = 53408b23d6cb733e6c9bc3d1e2ea2286a5c83cc4e3e7470f8af3a1d9f28727f5b1f8ae348c1678f5d1105dc3edf2de64e65b9c99545c47e64b770b17c8b4ef5cf194b43a0538053e87a6b95ade1439cebf3d34c6aa72a11c1497f58f76011e16c5be087936d88aba7a740113120e939e27bd3ddcb6580c2841aa406566e33c35
ModInv = 87355002f305c81ba0dc97ca2234a2bc02528cefde38b94ac5bd95efc7bf4c140899107fff47f0df9e3c6aa70017ebc90610a750f112cd4f475b9c76b204a953444b4e7196ccf17e93fdaed160b7345ca9b397eddf9446e8ea8ee3676102ce70eaafbe9038a34639789e6f2f1e3f352638f2e8a8f5fc56aaea7ec705ee068dd5
A = 42a25d0bc96f71750f5ac8a51a1605a41b506cca51c9a7ecf80cad713e56f70f1b4b6fa51cbb101f55fd74f318adefb3af04e0c8a7e281055d5a40dd40913c0e1211767c5be915972c73886106dc49325df6c2df49e9eea4536f0343a8e7d332c6159e4f5bdb20d89f90e67597c4a2a632c31b2ef2534080a9ac61f52303990d
M = d3d3f95d50570351528a76ab1e806bae1968bd420899bdb3d87c823fac439a4354c31f6c888c939784f18fe10a95e6d203b1901caa18937ba6f8be033af10c35fc869cf3d16bef479f280f53b3499e645d0387554623207ca4989e5de00bfeaa5e9ab56474fc60dd4967b100e0832eaaf2fcb2ef82a181567057b880b3afef62

View File

@ -1,149 +0,0 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package byteorder provides functions for decoding and encoding
// little and big endian integer types from/to byte slices.
package byteorder
func LEUint16(b []byte) uint16 {
_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
return uint16(b[0]) | uint16(b[1])<<8
}
func LEPutUint16(b []byte, v uint16) {
_ = b[1] // early bounds check to guarantee safety of writes below
b[0] = byte(v)
b[1] = byte(v >> 8)
}
func LEAppendUint16(b []byte, v uint16) []byte {
return append(b,
byte(v),
byte(v>>8),
)
}
func LEUint32(b []byte) uint32 {
_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
}
func LEPutUint32(b []byte, v uint32) {
_ = b[3] // early bounds check to guarantee safety of writes below
b[0] = byte(v)
b[1] = byte(v >> 8)
b[2] = byte(v >> 16)
b[3] = byte(v >> 24)
}
func LEAppendUint32(b []byte, v uint32) []byte {
return append(b,
byte(v),
byte(v>>8),
byte(v>>16),
byte(v>>24),
)
}
func LEUint64(b []byte) uint64 {
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
}
func LEPutUint64(b []byte, v uint64) {
_ = b[7] // early bounds check to guarantee safety of writes below
b[0] = byte(v)
b[1] = byte(v >> 8)
b[2] = byte(v >> 16)
b[3] = byte(v >> 24)
b[4] = byte(v >> 32)
b[5] = byte(v >> 40)
b[6] = byte(v >> 48)
b[7] = byte(v >> 56)
}
func LEAppendUint64(b []byte, v uint64) []byte {
return append(b,
byte(v),
byte(v>>8),
byte(v>>16),
byte(v>>24),
byte(v>>32),
byte(v>>40),
byte(v>>48),
byte(v>>56),
)
}
func BEUint16(b []byte) uint16 {
_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
return uint16(b[1]) | uint16(b[0])<<8
}
func BEPutUint16(b []byte, v uint16) {
_ = b[1] // early bounds check to guarantee safety of writes below
b[0] = byte(v >> 8)
b[1] = byte(v)
}
func BEAppendUint16(b []byte, v uint16) []byte {
return append(b,
byte(v>>8),
byte(v),
)
}
func BEUint32(b []byte) uint32 {
_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
}
func BEPutUint32(b []byte, v uint32) {
_ = b[3] // early bounds check to guarantee safety of writes below
b[0] = byte(v >> 24)
b[1] = byte(v >> 16)
b[2] = byte(v >> 8)
b[3] = byte(v)
}
func BEAppendUint32(b []byte, v uint32) []byte {
return append(b,
byte(v>>24),
byte(v>>16),
byte(v>>8),
byte(v),
)
}
func BEUint64(b []byte) uint64 {
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
}
func BEPutUint64(b []byte, v uint64) {
_ = b[7] // early bounds check to guarantee safety of writes below
b[0] = byte(v >> 56)
b[1] = byte(v >> 48)
b[2] = byte(v >> 40)
b[3] = byte(v >> 32)
b[4] = byte(v >> 24)
b[5] = byte(v >> 16)
b[6] = byte(v >> 8)
b[7] = byte(v)
}
func BEAppendUint64(b []byte, v uint64) []byte {
return append(b,
byte(v>>56),
byte(v>>48),
byte(v>>40),
byte(v>>32),
byte(v>>24),
byte(v>>16),
byte(v>>8),
byte(v),
)
}

View File

@ -1,292 +0,0 @@
// Package xts implements XTS encryption mode, as specified in IEEE P1619/D16 and GB/T 17964-2021.
package xts
import (
"crypto/cipher"
"crypto/subtle"
"errors"
"github.com/emmansun/gmsm/internal/alias"
)
const GF128_FDBK byte = 0x87
type concurrentBlocks interface {
Concurrency() int
EncryptBlocks(dst, src []byte)
DecryptBlocks(dst, src []byte)
}
// Cipher contains an expanded key structure. It is unsafe for concurrent use.
type xts struct {
b cipher.Block
tweak [blockSize]byte
isGB bool // if true, follows GB/T 17964-2021
}
// blockSize is the block size that the underlying cipher must have. XTS is
// only defined for 16-byte ciphers.
const blockSize = 16
type xtsEncrypter xts
// xtsEncAble is an interface implemented by ciphers that have a specific
// optimized implementation of XTS encryption, like sm4.
// NewXTSEncrypter will check for this interface and return the specific
// BlockMode if found.
type xtsEncAble interface {
NewXTSEncrypter(encryptedTweak *[blockSize]byte, isGB bool) cipher.BlockMode
}
func NewXTSEncrypter(cipherFunc func([]byte) (cipher.Block, error), key, tweakKey, tweak []byte, isGB bool) (cipher.BlockMode, error) {
if len(tweak) != blockSize {
return nil, errors.New("cipher: invalid tweak length")
}
k1, err := cipherFunc(key)
if err != nil {
return nil, err
}
if k1.BlockSize() != blockSize {
return nil, errors.New("cipher: cipher does not have a block size of 16")
}
k2, err := cipherFunc(tweakKey)
if err != nil {
return nil, err
}
if xtsable, ok := k1.(xtsEncAble); ok {
var encryptedTweak [blockSize]byte
k2.Encrypt(encryptedTweak[:], tweak)
return xtsable.NewXTSEncrypter(&encryptedTweak, isGB), nil
}
c := &xts{
b: k1,
isGB: isGB,
}
k2.Encrypt(c.tweak[:], tweak)
return (*xtsEncrypter)(c), nil
}
type xtsDecrypter xts
// xtsDecAble is an interface implemented by ciphers that have a specific
// optimized implementation of XTS encryption, like sm4.
// NewXTSDecrypter will check for this interface and return the specific
// BlockMode if found.
type xtsDecAble interface {
NewXTSDecrypter(encryptedTweak *[blockSize]byte, isGB bool) cipher.BlockMode
}
func NewXTSDecrypter(cipherFunc func([]byte) (cipher.Block, error), key, tweakKey, tweak []byte, isGB bool) (cipher.BlockMode, error) {
if len(tweak) != blockSize {
return nil, errors.New("cipher: invalid tweak length")
}
k1, err := cipherFunc(key)
if err != nil {
return nil, err
}
if k1.BlockSize() != blockSize {
return nil, errors.New("cipher: cipher does not have a block size of 16")
}
k2, err := cipherFunc(tweakKey)
if err != nil {
return nil, err
}
if xtsable, ok := k1.(xtsDecAble); ok {
var encryptedTweak [blockSize]byte
k2.Encrypt(encryptedTweak[:], tweak)
return xtsable.NewXTSDecrypter(&encryptedTweak, isGB), nil
}
c := &xts{
b: k1,
isGB: isGB,
}
k2.Encrypt(c.tweak[:], tweak)
return (*xtsDecrypter)(c), nil
}
func (c *xtsEncrypter) BlockSize() int {
return blockSize
}
// CryptBlocks encrypts a sector of plaintext and puts the result into ciphertext.
// Plaintext and ciphertext must overlap entirely or not at all.
// Sectors must be a multiple of 16 bytes and less than 2²⁴ bytes.
func (c *xtsEncrypter) CryptBlocks(ciphertext, plaintext []byte) {
if len(ciphertext) < len(plaintext) {
panic("cipher: ciphertext is smaller than plaintext")
}
if len(plaintext) < blockSize {
panic("cipher: plaintext length is smaller than the block size")
}
if alias.InexactOverlap(ciphertext[:len(plaintext)], plaintext) {
panic("cipher: invalid buffer overlap")
}
lastCiphertext := ciphertext
if concCipher, ok := c.b.(concurrentBlocks); ok {
batchSize := concCipher.Concurrency() * blockSize
var tweaks = make([]byte, batchSize)
for len(plaintext) >= batchSize {
doubleTweaks(&c.tweak, tweaks, c.isGB)
subtle.XORBytes(ciphertext, plaintext, tweaks)
concCipher.EncryptBlocks(ciphertext, ciphertext)
subtle.XORBytes(ciphertext, ciphertext, tweaks)
plaintext = plaintext[batchSize:]
lastCiphertext = ciphertext[batchSize-blockSize:]
ciphertext = ciphertext[batchSize:]
}
}
for len(plaintext) >= blockSize {
subtle.XORBytes(ciphertext, plaintext, c.tweak[:])
c.b.Encrypt(ciphertext, ciphertext)
subtle.XORBytes(ciphertext, ciphertext, c.tweak[:])
plaintext = plaintext[blockSize:]
lastCiphertext = ciphertext
ciphertext = ciphertext[blockSize:]
mul2(&c.tweak, c.isGB)
}
// is there a final partial block to handle?
if remain := len(plaintext); remain > 0 {
var x [blockSize]byte
//Copy the final plaintext bytes
copy(x[:], plaintext)
//Steal ciphertext to complete the block
copy(x[remain:], lastCiphertext[remain:blockSize])
//Copy the final ciphertext bytes
copy(ciphertext, lastCiphertext[:remain])
//Merge the tweak into the input block
subtle.XORBytes(x[:], x[:], c.tweak[:])
//Encrypt the final block using K1
c.b.Encrypt(x[:], x[:])
//Merge the tweak into the output block
subtle.XORBytes(lastCiphertext, x[:], c.tweak[:])
}
}
func (c *xtsDecrypter) BlockSize() int {
return blockSize
}
// CryptBlocks decrypts a sector of ciphertext and puts the result into plaintext.
// Plaintext and ciphertext must overlap entirely or not at all.
// Sectors must be a multiple of 16 bytes and less than 2²⁴ bytes.
func (c *xtsDecrypter) CryptBlocks(plaintext, ciphertext []byte) {
if len(plaintext) < len(ciphertext) {
panic("cipher: plaintext is smaller than ciphertext")
}
if len(ciphertext) < blockSize {
panic("cipher: ciphertext length is smaller than the block size")
}
if alias.InexactOverlap(plaintext[:len(ciphertext)], ciphertext) {
panic("cipher: invalid buffer overlap")
}
if concCipher, ok := c.b.(concurrentBlocks); ok {
batchSize := concCipher.Concurrency() * blockSize
var tweaks = make([]byte, batchSize)
for len(ciphertext) >= batchSize {
doubleTweaks(&c.tweak, tweaks, c.isGB)
subtle.XORBytes(plaintext, ciphertext, tweaks)
concCipher.DecryptBlocks(plaintext, plaintext)
subtle.XORBytes(plaintext, plaintext, tweaks)
plaintext = plaintext[batchSize:]
ciphertext = ciphertext[batchSize:]
}
}
for len(ciphertext) >= 2*blockSize {
subtle.XORBytes(plaintext, ciphertext, c.tweak[:])
c.b.Decrypt(plaintext, plaintext)
subtle.XORBytes(plaintext, plaintext, c.tweak[:])
plaintext = plaintext[blockSize:]
ciphertext = ciphertext[blockSize:]
mul2(&c.tweak, c.isGB)
}
if remain := len(ciphertext); remain >= blockSize {
var x [blockSize]byte
if remain > blockSize {
var tt [blockSize]byte
copy(tt[:], c.tweak[:])
mul2(&tt, c.isGB)
subtle.XORBytes(x[:], ciphertext, tt[:])
c.b.Decrypt(x[:], x[:])
subtle.XORBytes(plaintext, x[:], tt[:])
//Retrieve the length of the final block
remain -= blockSize
//Copy the final ciphertext bytes
copy(x[:], ciphertext[blockSize:])
//Steal ciphertext to complete the block
copy(x[remain:], plaintext[remain:blockSize])
//Copy the final plaintext bytes
copy(plaintext[blockSize:], plaintext)
subtle.XORBytes(x[:], x[:], c.tweak[:])
c.b.Decrypt(x[:], x[:])
subtle.XORBytes(plaintext, x[:], c.tweak[:])
} else {
//The last block contains exactly 128 bits
subtle.XORBytes(plaintext, ciphertext, c.tweak[:])
c.b.Decrypt(plaintext, plaintext)
subtle.XORBytes(plaintext, plaintext, c.tweak[:])
// Maybe there are still ciphertext
mul2(&c.tweak, c.isGB)
}
}
}
// mul2Generic multiplies tweak by 2 in GF(2¹²⁸) with an irreducible polynomial of
// x¹²⁸ + x⁷ + x² + x + 1.
func mul2Generic(tweak *[blockSize]byte, isGB bool) {
var carryIn byte
if !isGB {
// the coefficient of x⁰ can be obtained by tweak[0] & 1
// the coefficient of x⁷ can be obtained by tweak[0] >> 7
// the coefficient of x¹²⁰ can be obtained by tweak[15] & 1
// the coefficient of x¹²⁷ can be obtained by tweak[15] >> 7
for j := range tweak {
carryOut := tweak[j] >> 7
tweak[j] = (tweak[j] << 1) + carryIn
carryIn = carryOut
}
if carryIn != 0 {
// If we have a carry bit then we need to subtract a multiple
// of the irreducible polynomial (x¹²⁸ + x⁷ + x² + x + 1).
// By dropping the carry bit, we're subtracting the x^128 term
// so all that remains is to subtract x⁷ + x² + x + 1.
// Subtraction (and addition) in this representation is just
// XOR.
tweak[0] ^= GF128_FDBK // 1<<7 | 1<<2 | 1<<1 | 1
}
} else {
// GB/T 17964-2021,
// the coefficient of x⁰ can be obtained by tweak[0] >> 7
// the coefficient of x⁷ can be obtained by tweak[0] & 1
// the coefficient of x¹²⁰ can be obtained by tweak[15] >> 7
// the coefficient of x¹²⁷ can be obtained by tweak[15] & 1
for j := range tweak {
carryOut := (tweak[j] << 7) & 0x80
tweak[j] = (tweak[j] >> 1) + carryIn
carryIn = carryOut
}
if carryIn != 0 {
tweak[0] ^= 0xE1 // 1<<7 | 1<<6 | 1<<5 | 1
}
}
}

View File

@ -1,125 +0,0 @@
//go:build !purego
#include "textflag.h"
DATA bswapMask<>+0x00(SB)/8, $0x08090a0b0c0d0e0f
DATA bswapMask<>+0x08(SB)/8, $0x0001020304050607
DATA gcmPoly<>+0x00(SB)/8, $0x0000000000000087
DATA gcmPoly<>+0x08(SB)/8, $0x0000000000000000
DATA gbGcmPoly<>+0x00(SB)/8, $0x0000000000000000
DATA gbGcmPoly<>+0x08(SB)/8, $0xe100000000000000
GLOBL bswapMask<>(SB), (NOPTR+RODATA), $16
GLOBL gcmPoly<>(SB), (NOPTR+RODATA), $16
GLOBL gbGcmPoly<>(SB), (NOPTR+RODATA), $16
#define POLY X0
#define BSWAP X1
#define B0 X2
#define T0 X3
#define T1 X4
#define doubleTweak(B0, POLY, T0, T1) \
\ // B0 * 2
PSHUFD $0xff, B0, T0 \
MOVOU B0, T1 \
PSRAL $31, T0 \ // T0 for reduction
PAND POLY, T0 \
PSRLL $31, T1 \
PSLLDQ $4, T1 \
PSLLL $1, B0 \
PXOR T0, B0 \
PXOR T1, B0
#define gbDoubleTweak(B0, BSWAP, POLY, T0, T1) \
PSHUFB BSWAP, B0 \
\ // B0 * 2
MOVOU B0, T0 \
PSHUFD $0, B0, T1 \
PSRLQ $1, B0 \
PSLLQ $63, T0 \
PSRLDQ $8, T0 \
POR T0, B0 \
\ // reduction
PSLLL $31, T1 \
PSRAL $31, T1 \
PAND POLY, T1 \
PXOR T1, B0 \
PSHUFB BSWAP, B0
// func mul2(tweak *[blockSize]byte, isGB bool)
TEXT ·mul2(SB),NOSPLIT,$0
MOVQ tweak+0(FP), DI
MOVB isGB+8(FP), AX
MOVOU (0*16)(DI), B0
CMPB AX, $1
JE gb_alg
MOVOU gcmPoly<>(SB), POLY
doubleTweak(B0, POLY, T0, T1)
MOVOU B0, (0*16)(DI)
RET
gb_alg:
MOVOU bswapMask<>(SB), BSWAP
MOVOU gbGcmPoly<>(SB), POLY
gbDoubleTweak(B0, BSWAP, POLY, T0, T1)
MOVOU B0, (0*16)(DI)
RET
// func doubleTweaks(tweak *[blockSize]byte, tweaks []byte, isGB bool)
TEXT ·doubleTweaks(SB),NOSPLIT,$0
MOVQ tweak+0(FP), DI
MOVQ tweaks+8(FP), AX
MOVQ tweaks_len+16(FP), BX
MOVB isGB+32(FP), CX
MOVOU (0*16)(DI), B0
SHRQ $4, BX
XORQ DX, DX
CMPB CX, $1
JE dt_gb_alg
MOVOU gcmPoly<>(SB), POLY
loop:
MOVOU B0, (0*16)(AX)
LEAQ 16(AX), AX
doubleTweak(B0, POLY, T0, T1)
ADDQ $1, DX
CMPQ DX, BX
JB loop
MOVOU B0, (0*16)(DI)
RET
dt_gb_alg:
MOVOU bswapMask<>(SB), BSWAP
MOVOU gbGcmPoly<>(SB), POLY
gb_loop:
MOVOU B0, (0*16)(AX)
LEAQ 16(AX), AX
gbDoubleTweak(B0, BSWAP, POLY, T0, T1)
ADDQ $1, DX
CMPQ DX, BX
JB gb_loop
MOVOU B0, (0*16)(DI)
RET

View File

@ -1,124 +0,0 @@
//go:build !purego
#include "textflag.h"
#define B0 V0
#define T1 V1
#define T2 V2
#define POLY V3
#define ZERO V4
#define TW R0
#define GB R1
#define I R2
#define doubleTweak(B0, ZERO, POLY, I, T1, T2) \
VMOV B0.D[1], I \
ASR $63, I \
VMOV I, T1.D2 \
VAND POLY.B16, T1.B16, T1.B16 \
\
VUSHR $63, B0.D2, T2.D2 \
VEXT $8, T2.B16, ZERO.B16, T2.B16 \
VSLI $1, B0.D2, T2.D2 \
VEOR T1.B16, T2.B16, B0.B16
#define gbDoubleTweak(B0, ZERO, POLY, I, T1, T2) \
VREV64 B0.B16, B0.B16 \
VEXT $8, B0.B16, B0.B16, B0.B16 \
\
VMOV B0.D[0], I \
LSL $63, I \
ASR $63, I \
VMOV I, T1.D2 \
VAND POLY.B16, T1.B16, T1.B16 \
\
VSHL $63, B0.D2, T2.D2 \
VEXT $8, ZERO.B16, T2.B16, T2.B16 \
VSRI $1, B0.D2, T2.D2 \
VEOR T1.B16, T2.B16, B0.B16 \
\
VEXT $8, B0.B16, B0.B16, B0.B16 \
VREV64 B0.B16, B0.B16
// func mul2(tweak *[blockSize]byte, isGB bool)
TEXT ·mul2(SB),NOSPLIT,$0
MOVD tweak+0(FP), TW
MOVB isGB+8(FP), GB
VLD1 (TW), [B0.B16]
VEOR POLY.B16, POLY.B16, POLY.B16
VEOR ZERO.B16, ZERO.B16, ZERO.B16
CMP $1, GB
BEQ gb_alg
MOVD $0x87, I
VMOV I, POLY.D[0]
doubleTweak(B0, ZERO, POLY, I, T1, T2)
VST1 [B0.B16], (TW)
RET
gb_alg:
MOVD $0xE1, I
LSL $56, I
VMOV I, POLY.D[1]
gbDoubleTweak(B0, ZERO, POLY, I, T1, T2)
VST1 [B0.B16], (TW)
RET
// func doubleTweaks(tweak *[blockSize]byte, tweaks []byte, isGB bool)
TEXT ·doubleTweaks(SB),NOSPLIT,$0
MOVD tweak+0(FP), TW
MOVD tweaks+8(FP), R3
MOVD tweaks_len+16(FP), R4
MOVB isGB+32(FP), GB
LSR $4, R4
EOR R5, R5
VEOR POLY.B16, POLY.B16, POLY.B16
VEOR ZERO.B16, ZERO.B16, ZERO.B16
VLD1 (TW), [B0.B16]
CMP $1, GB
BEQ dt_gb_alg
MOVD $0x87, I
VMOV I, POLY.D[0]
loop:
VST1.P [B0.B16], 16(R3)
doubleTweak(B0, ZERO, POLY, I, T1, T2)
ADD $1, R5
CMP R4, R5
BNE loop
VST1 [B0.B16], (TW)
RET
dt_gb_alg:
MOVD $0xE1, I
LSL $56, I
VMOV I, POLY.D[1]
gb_loop:
VST1.P [B0.B16], 16(R3)
gbDoubleTweak(B0, ZERO, POLY, I, T1, T2)
ADD $1, R5
CMP R4, R5
BNE gb_loop
VST1 [B0.B16], (TW)
RET

View File

@ -1,9 +0,0 @@
//go:build (amd64 || arm64 || s390x || ppc64 || ppc64le) && !purego
package xts
//go:noescape
func mul2(tweak *[blockSize]byte, isGB bool)
//go:noescape
func doubleTweaks(tweak *[blockSize]byte, tweaks []byte, isGB bool)

View File

@ -1,105 +0,0 @@
//go:build (amd64 || arm64 || s390x || ppc64 || ppc64le) && !purego
package xts
import (
"bytes"
"crypto/rand"
"encoding/hex"
"io"
"testing"
)
var testTweakVector = []string{
"F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF",
"66e94bd4ef8a2c3b884cfa59ca342b2e",
"3f803bcd0d7fd2b37558419f59d5cda6",
"6dcfba212f5d82bf525ee9793cfa505a",
"c172964cd58be2b8d8e09d9c5e9cfe36",
"1a267577a90caad6ae988e22714a2b8b",
"33fab707493702e77ff8d66ba9e6c6fe",
"23fb188b0f87f6ee2ec0803a99771341",
"e8de0a4188b7efbc1ac3979eb906cf36",
}
func testDoubleTweak(t *testing.T, isGB bool) {
for _, tk := range testTweakVector {
tweak, _ := hex.DecodeString(tk)
var t1, t2 [16]byte
copy(t1[:], tweak)
copy(t2[:], tweak)
mul2(&t1, isGB)
mul2Generic(&t2, isGB)
if !bytes.Equal(t1[:], t2[:]) {
t.Errorf("tweak %v, expected %x, got %x", tk, t2[:], t1[:])
}
}
}
func TestDoubleTweak(t *testing.T) {
testDoubleTweak(t, false)
}
func TestDoubleTweakGB(t *testing.T) {
testDoubleTweak(t, true)
}
func testDoubleTweakRandomly(t *testing.T, isGB bool) {
var tweak, t1, t2 [16]byte
io.ReadFull(rand.Reader, tweak[:])
copy(t1[:], tweak[:])
copy(t2[:], tweak[:])
mul2(&t1, isGB)
mul2Generic(&t2, isGB)
if !bytes.Equal(t1[:], t2[:]) {
t.Errorf("tweak %x, expected %x, got %x", tweak[:], t2[:], t1[:])
}
}
func TestDoubleTweakRandomly(t *testing.T) {
for i := 0; i < 10; i++ {
testDoubleTweakRandomly(t, false)
}
}
func TestDoubleTweakGBRandomly(t *testing.T) {
for i := 0; i < 10; i++ {
testDoubleTweakRandomly(t, true)
}
}
func testDoubleTweaks(t *testing.T, isGB bool) {
for _, tk := range testTweakVector {
tweak, _ := hex.DecodeString(tk)
var t1, t2 [16]byte
var t11, t12 [128]byte
copy(t1[:], tweak)
copy(t2[:], tweak)
for i := 0; i < 8; i++ {
copy(t12[16*i:], t2[:])
mul2Generic(&t2, isGB)
}
doubleTweaks(&t1, t11[:], isGB)
if !bytes.Equal(t1[:], t2[:]) {
t.Errorf("1 tweak %v, expected %x, got %x", tk, t2[:], t1[:])
}
if !bytes.Equal(t11[:], t12[:]) {
t.Errorf("2 tweak %v, expected %x, got %x", tk, t12[:], t11[:])
}
}
}
func TestDoubleTweaks(t *testing.T) {
testDoubleTweaks(t, false)
}
func TestDoubleTweaksGB(t *testing.T) {
testDoubleTweaks(t, true)
}

View File

@ -1,15 +0,0 @@
//go:build purego || !(amd64 || arm64 || s390x || ppc64 || ppc64le)
package xts
func mul2(tweak *[blockSize]byte, isGB bool) {
mul2Generic(tweak, isGB)
}
func doubleTweaks(tweak *[blockSize]byte, tweaks []byte, isGB bool) {
count := len(tweaks) >> 4
for i := range count {
copy(tweaks[blockSize*i:], tweak[:])
mul2(tweak, isGB)
}
}

View File

@ -1,166 +0,0 @@
// Copyright 2024 Sun Yimin. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
//go:build (ppc64 || ppc64le) && !purego
#include "textflag.h"
DATA xtsMask<>+0x00(SB)/8, $0x0f0e0d0c0b0a0908 // byte swap BE -> LE
DATA xtsMask<>+0x08(SB)/8, $0x0706050403020100
DATA xtsMask<>+0x10(SB)/8, $0x0000000000000000
DATA xtsMask<>+0x18(SB)/8, $0x0000000000000087
DATA xtsMask<>+0x20(SB)/8, $0xe100000000000000
DATA xtsMask<>+0x28(SB)/8, $0x0000000000000000
GLOBL xtsMask<>(SB), (NOPTR+RODATA), $48
#define ESPERM V21 // Endian swapping permute into BE
#define POLY V0
#define B0 V1
#define T0 V2
#define T1 V3
#define CPOOL R7
#define doubleTweak(B0, POLY, T0, T1) \
\ // Multiply by 2
VSPLTB $0, B0, T0 \
VSPLTISB $7, T1 \
VSRAB T0, T1, T0 \
VAND POLY, T0, T0 \// T0 for reduction
\
VSPLTISB $1, T1 \
VSL B0, T1, T1 \
VXOR T0, T1, B0
#define gbDoubleTweak(B0, POLY, T0, T1) \
VSPLTB $15, B0, T0 \
VSPLTISB $7, T1 \
VSLB T0, T1, T0 \
VSRAB T0, T1, T0 \
VAND POLY, T0, T0 \ // T0 for reduction
VSPLTISB $1, T1 \
VSR B0, T1, B0 \
VXOR T0, B0, B0
// func mul2(tweak *[blockSize]byte, isGB bool)
TEXT ·mul2(SB),NOSPLIT,$0
MOVD tweak+0(FP), R3
MOVBZ isGB+8(FP), R4
MOVD $xtsMask<>(SB), CPOOL
CMPW R4, $1
BEQ gb_alg
// Load polynomial for reduction
MOVD $16, R5
LXVD2X (CPOOL)(R5), POLY
// Load tweak
LXVD2X (R3), B0
#ifdef GOARCH_ppc64le
XXPERMDI B0, B0, $2, B0
doubleTweak(B0, POLY, T0, T1)
XXPERMDI B0, B0, $2, B0
#else
LXVD2X (CPOOL), ESPERM
VPERM B0, B0, ESPERM, B0
doubleTweak(B0, POLY, T0, T1)
VPERM B0, B0, ESPERM, B0
#endif
STXVD2X B0, (R3)
RET
gb_alg:
// Load polynomial for reduction
MOVD $32, R5
LXVD2X (CPOOL)(R5), POLY
// Load tweak
LXVD2X (R3), B0
#ifdef GOARCH_ppc64le
LVX (CPOOL), ESPERM
VPERM B0, B0, ESPERM, B0
gbDoubleTweak(B0, POLY, T0, T1)
VPERM B0, B0, ESPERM, B0
#else
gbDoubleTweak(B0, POLY, T0, T1)
#endif
STXVD2X B0, (R3)
RET
// func doubleTweaks(tweak *[blockSize]byte, tweaks []byte, isGB bool)
TEXT ·doubleTweaks(SB),NOSPLIT,$0
MOVD tweak+0(FP), R3
MOVD tweaks+8(FP), R4
MOVD tweaks_len+16(FP), R5
MOVBZ isGB+32(FP), R6
MOVD $xtsMask<>(SB), CPOOL
// Load tweak
LXVD2X (R3), B0
CMPW R6, $1
BEQ gb_alg
SRD $4, R5
MOVD R5, CTR
#ifndef GOARCH_ppc64le
LXVD2X (CPOOL), ESPERM
#endif
// Load polynomial for reduction
MOVD $16, R5
LXVD2X (CPOOL)(R5), POLY
loop:
STXVD2X B0, (R4)
ADD $16, R4
#ifdef GOARCH_ppc64le
XXPERMDI B0, B0, $2, B0
doubleTweak(B0, POLY, T0, T1)
XXPERMDI B0, B0, $2, B0
#else
VPERM B0, B0, ESPERM, B0
doubleTweak(B0, POLY, T0, T1)
VPERM B0, B0, ESPERM, B0
#endif
BDNZ loop
STXVD2X B0, (R3)
RET
gb_alg:
SRD $4, R5
MOVD R5, CTR
// Load polynomial for reduction
MOVD $32, R5
LXVD2X (CPOOL)(R5), POLY
#ifdef GOARCH_ppc64le
LVX (CPOOL), ESPERM
#endif
gbLoop:
STXVD2X B0, (R4)
ADD $16, R4
#ifdef GOARCH_ppc64le
VPERM B0, B0, ESPERM, B0
gbDoubleTweak(B0, POLY, T0, T1)
VPERM B0, B0, ESPERM, B0
#else
gbDoubleTweak(B0, POLY, T0, T1)
#endif
BDNZ gbLoop
STXVD2X B0, (R3)
RET

View File

@ -1,121 +0,0 @@
// Copyright 2024 Sun Yimin. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
//go:build !purego
#include "textflag.h"
DATA xtsMask<>+0x00(SB)/8, $0x0f0e0d0c0b0a0908 // byte swap BE -> LE
DATA xtsMask<>+0x08(SB)/8, $0x0706050403020100
GLOBL xtsMask<>(SB), (NOPTR+RODATA), $16
#define BSWAP V0
#define POLY V1
#define B0 V2
#define T0 V3
#define T1 V4
#define CPOOL R3
#define doubleTweak(B0, BSWAP, POLY, T0, T1) \
VPERM B0, B0, BSWAP, B0 \// BE -> LE
\ // Multiply by 2
VESRAF $31, B0, T0 \
VREPF $0, T0, T0 \
VN POLY, T0, T0 \ // T0 for reduction
VREPIB $1, T1 \
VSL T1, B0, T1 \
VX T1, T0, B0 \
\
VPERM B0, B0, BSWAP, B0
#define gbDoubleTweak(B0, POLY, T0, T1) \
VESLF $31, B0, T0 \
VESRAF $31, T0, T0 \
VREPF $3, T0, T0 \
VN POLY, T0, T0 \ // T0 for reduction
\
VREPIB $1, T1 \
VSRL T1, B0, T1 \
VX T1, T0, B0
// func mul2(tweak *[blockSize]byte, isGB bool)
TEXT ·mul2(SB),NOSPLIT,$0
MOVD tweak+0(FP), R1
MOVB isGB+8(FP), R2
CMPBEQ R2, $1, gb_alg
MOVD $xtsMask<>+0x00(SB), CPOOL
VL (CPOOL), BSWAP
// Load polynomial for reduction
VZERO POLY
VLEIB $15, $0x87, POLY
// Load tweak
VL 0(R1), B0
doubleTweak(B0, BSWAP, POLY, T0, T1)
VST B0, 0(R1)
RET
gb_alg:
// Load polynomial for reduction
VZERO POLY
VLEIB $0, $0xe1, POLY
// Load tweak
VL 0(R1), B0
gbDoubleTweak(B0, POLY, T0, T1)
VST B0, 0(R1)
RET
// func doubleTweaks(tweak *[blockSize]byte, tweaks []byte, isGB bool)
TEXT ·doubleTweaks(SB),NOSPLIT,$0
MOVD tweak+0(FP), R1
MOVD tweaks+8(FP), R2
MOVD tweaks_len+16(FP), R3
MOVB isGB+32(FP), R4
AND $-16, R3
LAY (R2)(R3*1), R5
VL 0(R1), B0
CMPBEQ R4, $1, gb_alg
MOVD $xtsMask<>+0x00(SB), CPOOL
VL (CPOOL), BSWAP
// Load polynomial for reduction
VZERO POLY
VLEIB $15, $0x87, POLY
loop:
VST B0, 0(R2)
doubleTweak(B0, BSWAP, POLY, T0, T1)
LA 16(R2), R2
CMPBLT R2, R5, loop
VST B0, 0(R1)
RET
gb_alg:
// Load polynomial for reduction
VZERO POLY
VLEIB $0, $0xe1, POLY
gb_alg_loop:
VST B0, 0(R2)
gbDoubleTweak(B0, POLY, T0, T1)
LA 16(R2), R2
CMPBLT R2, R5, gb_alg_loop
VST B0, 0(R1)
RET

Some files were not shown because too many files have changed in this diff Show More