mirror of
https://github.com/emmansun/gmsm.git
synced 2025-04-22 10:16:18 +08:00
Compare commits
123 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7b75b6b26b | ||
![]() |
5aacbc2011 | ||
![]() |
fe532e12b4 | ||
![]() |
1dc82305e4 | ||
![]() |
ed256a9ea3 | ||
![]() |
d8c6788e8f | ||
![]() |
0ef30b3ab5 | ||
![]() |
d3eece5560 | ||
![]() |
0af92d8e48 | ||
![]() |
bf644fbb4e | ||
![]() |
359b46453b | ||
![]() |
b8d52dd11d | ||
![]() |
9ea8293d10 | ||
![]() |
e8a847e005 | ||
![]() |
a7c4473a48 | ||
![]() |
11d0438cc4 | ||
![]() |
88df15c64c | ||
![]() |
3eea15b3b8 | ||
![]() |
dd69d32930 | ||
![]() |
a84fec09af | ||
![]() |
c43e0488a6 | ||
![]() |
a4affe6006 | ||
![]() |
3cc92436ee | ||
![]() |
93c965f3c1 | ||
![]() |
d8eb166dfc | ||
![]() |
2d3329a2ea | ||
![]() |
f41a5c69e7 | ||
![]() |
cf027254dc | ||
![]() |
069babe703 | ||
![]() |
5734e67634 | ||
![]() |
82ccb95527 | ||
![]() |
e79aab4935 | ||
![]() |
c32a9849f8 | ||
![]() |
7ec46d700d | ||
![]() |
7a5253bfb5 | ||
![]() |
d6f18a2cbf | ||
![]() |
bdb169b06b | ||
![]() |
43ffd49e2f | ||
![]() |
e9692d23ab | ||
![]() |
21f96e536b | ||
![]() |
a49eecd572 | ||
![]() |
537c80a28b | ||
![]() |
5edcb0f966 | ||
![]() |
65a69ad83c | ||
![]() |
71b196a5ac | ||
![]() |
500cb8a418 | ||
![]() |
762cbbf0c6 | ||
![]() |
9d6f8087f9 | ||
![]() |
a6d8014ac9 | ||
![]() |
d7a6169fbf | ||
![]() |
67f187b1d3 | ||
![]() |
759bb4c0b9 | ||
![]() |
5ade794e6b | ||
![]() |
89962cf1e3 | ||
![]() |
d433e416fa | ||
![]() |
2c0f5f68fc | ||
![]() |
3d4dd002a4 | ||
![]() |
9ba88a32a4 | ||
![]() |
0bb54adc1e | ||
![]() |
a98b806453 | ||
![]() |
51a003b022 | ||
![]() |
dc1c5806c9 | ||
![]() |
869be23867 | ||
![]() |
8a25134c82 | ||
![]() |
27e7ceacbc | ||
![]() |
ec8580b01f | ||
![]() |
2192b702b0 | ||
![]() |
d03a8fba3f | ||
![]() |
33c609242c | ||
![]() |
d2dd4de947 | ||
![]() |
1aed89febd | ||
![]() |
a248fea7f9 | ||
![]() |
53465ae10a | ||
![]() |
ea592fe3d0 | ||
![]() |
9e57bb91b2 | ||
![]() |
ee55d370f8 | ||
![]() |
4c88efbaee | ||
![]() |
80a264c995 | ||
![]() |
50a5e49d38 | ||
![]() |
cb51b3657a | ||
![]() |
cf6e203d06 | ||
![]() |
057ddb5bcb | ||
![]() |
94e533ca15 | ||
![]() |
818e14ee32 | ||
![]() |
8331b37e37 | ||
![]() |
bef7f5421e | ||
![]() |
5a0ff81dc3 | ||
![]() |
b865b59a95 | ||
![]() |
0a961f152c | ||
![]() |
3544c09801 | ||
![]() |
e5652b6638 | ||
![]() |
309e920050 | ||
![]() |
805fa40c97 | ||
![]() |
d199697b20 | ||
![]() |
6b18205bba | ||
![]() |
89317b8f0b | ||
![]() |
a71e806a2d | ||
![]() |
a53c8b97f8 | ||
![]() |
4fd9003243 | ||
![]() |
0d3a2d2d18 | ||
![]() |
008b209765 | ||
![]() |
ab670f8ced | ||
![]() |
8e02380f47 | ||
![]() |
ac38d8f6aa | ||
![]() |
d2e96cff57 | ||
![]() |
1bbcc232db | ||
![]() |
cdf1805d4f | ||
![]() |
6a98350f2e | ||
![]() |
65532624e8 | ||
![]() |
3371b7c65c | ||
![]() |
c6881ae849 | ||
![]() |
df3db923c6 | ||
![]() |
1d894ef955 | ||
![]() |
a3161b7581 | ||
![]() |
01b0570c3a | ||
![]() |
717a0feea6 | ||
![]() |
39bb9469b1 | ||
![]() |
5ada783793 | ||
![]() |
4c5dc14a6b | ||
![]() |
1d24c4640d | ||
![]() |
531388ce5e | ||
![]() |
1cf8782870 | ||
![]() |
0d1fe8c95c |
14
.github/workflows/ci.yml
vendored
14
.github/workflows/ci.yml
vendored
@ -12,7 +12,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
goVer: ['1.18', '1.19', '1.21', '1.22', '1.23']
|
goVer: ['1.23', '1.24']
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Repo
|
- name: Checkout Repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@ -23,19 +23,19 @@ jobs:
|
|||||||
go-version: ${{ matrix.goVer }}
|
go-version: ${{ matrix.goVer }}
|
||||||
|
|
||||||
- name: Test with Coverage
|
- name: Test with Coverage
|
||||||
if: ${{ matrix.goVer == '1.18' }}
|
if: ${{ matrix.goVer == '1.23' }}
|
||||||
run: go test -coverpkg=./... -v -short -race -coverprofile=coverage1.txt -covermode=atomic ./...
|
run: go test -coverpkg=./... -v -short -race -coverprofile=coverage1.txt -covermode=atomic ./...
|
||||||
env:
|
env:
|
||||||
GODEBUG: x509sha1=1
|
GODEBUG: x509sha1=1
|
||||||
|
|
||||||
- name: Test Generic with Coverage
|
- name: Test Generic with Coverage
|
||||||
if: ${{ matrix.goVer == '1.18' }}
|
if: ${{ matrix.goVer == '1.23' }}
|
||||||
run: go test -coverpkg=./... -v -short -tags purego -coverprofile=coverage2.txt -covermode=atomic ./...
|
run: go test -coverpkg=./... -v -short -tags purego -coverprofile=coverage2.txt -covermode=atomic ./...
|
||||||
env:
|
env:
|
||||||
GODEBUG: x509sha1=1
|
GODEBUG: x509sha1=1
|
||||||
|
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
if: ${{ matrix.goVer == '1.18' }}
|
if: ${{ matrix.goVer == '1.23' }}
|
||||||
uses: codecov/codecov-action@v5
|
uses: codecov/codecov-action@v5
|
||||||
with:
|
with:
|
||||||
files: ./coverage1.txt,./coverage2.txt
|
files: ./coverage1.txt,./coverage2.txt
|
||||||
@ -48,19 +48,19 @@ jobs:
|
|||||||
FORCE_SM4BLOCK_AESNI: 1
|
FORCE_SM4BLOCK_AESNI: 1
|
||||||
|
|
||||||
- name: Test only
|
- name: Test only
|
||||||
if: ${{ matrix.goVer != '1.18' }}
|
if: ${{ matrix.goVer != '1.23' }}
|
||||||
run: go test -short ./...
|
run: go test -short ./...
|
||||||
env:
|
env:
|
||||||
GODEBUG: x509sha1=1
|
GODEBUG: x509sha1=1
|
||||||
|
|
||||||
- name: Test Generic only
|
- name: Test Generic only
|
||||||
if: ${{ matrix.goVer != '1.18' && matrix.goVer != '1.23' }}
|
if: ${{ matrix.goVer != '1.23' }}
|
||||||
run: go test -short -tags purego ./...
|
run: go test -short -tags purego ./...
|
||||||
env:
|
env:
|
||||||
GODEBUG: x509sha1=1
|
GODEBUG: x509sha1=1
|
||||||
|
|
||||||
- name: Test Plugin only
|
- name: Test Plugin only
|
||||||
if: ${{ matrix.goVer != '1.18' }}
|
if: ${{ matrix.goVer == '1.23' }}
|
||||||
run: go test -short -tags plugin ./...
|
run: go test -short -tags plugin ./...
|
||||||
env:
|
env:
|
||||||
GODEBUG: x509sha1=1
|
GODEBUG: x509sha1=1
|
||||||
|
2
.github/workflows/macos.yml
vendored
2
.github/workflows/macos.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: 1.22
|
go-version: 1.23
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: go build -v ./...
|
run: go build -v ./...
|
||||||
|
26
.github/workflows/test_ppc64.yaml
vendored
26
.github/workflows/test_ppc64.yaml
vendored
@ -14,7 +14,7 @@ jobs:
|
|||||||
test:
|
test:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [1.19.x]
|
go-version: [1.23.x]
|
||||||
arch: [ppc64le]
|
arch: [ppc64le]
|
||||||
ppc64: [power8]
|
ppc64: [power8]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -36,30 +36,6 @@ jobs:
|
|||||||
GOARCH: ${{ matrix.arch }}
|
GOARCH: ${{ matrix.arch }}
|
||||||
GOPPC64: ${{ matrix.ppc64 }}
|
GOPPC64: ${{ matrix.ppc64 }}
|
||||||
|
|
||||||
- name: Test bn256
|
|
||||||
run: go test -v ./sm9/bn256/...
|
|
||||||
env:
|
|
||||||
GOARCH: ${{ matrix.arch }}
|
|
||||||
GOPPC64: ${{ matrix.ppc64 }}
|
|
||||||
|
|
||||||
- name: Test ZUC
|
|
||||||
run: go test -v ./zuc/...
|
|
||||||
env:
|
|
||||||
GOARCH: ${{ matrix.arch }}
|
|
||||||
GOPPC64: ${{ matrix.ppc64 }}
|
|
||||||
|
|
||||||
- name: Test SM3
|
|
||||||
run: go test -v ./sm3/...
|
|
||||||
env:
|
|
||||||
GOARCH: ${{ matrix.arch }}
|
|
||||||
GOPPC64: ${{ matrix.ppc64 }}
|
|
||||||
|
|
||||||
- name: Test SM4
|
|
||||||
run: go test -v -short ./sm4/...
|
|
||||||
env:
|
|
||||||
GOARCH: ${{ matrix.arch }}
|
|
||||||
GOPPC64: ${{ matrix.ppc64 }}
|
|
||||||
|
|
||||||
- name: Test Cipher
|
- name: Test Cipher
|
||||||
run: go test -v -short ./cipher/...
|
run: go test -v -short ./cipher/...
|
||||||
env:
|
env:
|
||||||
|
2
.github/workflows/test_qemu.yml
vendored
2
.github/workflows/test_qemu.yml
vendored
@ -14,7 +14,7 @@ jobs:
|
|||||||
test:
|
test:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [1.18.x]
|
go-version: [1.23.x]
|
||||||
arch: [arm64]
|
arch: [arm64]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
2
.github/workflows/test_riscv64.yaml
vendored
2
.github/workflows/test_riscv64.yaml
vendored
@ -14,7 +14,7 @@ jobs:
|
|||||||
test:
|
test:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [1.22.x]
|
go-version: [1.23.x]
|
||||||
arch: [riscv64]
|
arch: [riscv64]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
7
.github/workflows/test_s390x.yaml
vendored
7
.github/workflows/test_s390x.yaml
vendored
@ -14,7 +14,7 @@ jobs:
|
|||||||
test:
|
test:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [1.19.x]
|
go-version: [1.23.x]
|
||||||
arch: [s390x]
|
arch: [s390x]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
@ -34,11 +34,6 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GOARCH: ${{ matrix.arch }}
|
GOARCH: ${{ matrix.arch }}
|
||||||
|
|
||||||
- name: Test sm3
|
|
||||||
run: go test -v ./sm3/...
|
|
||||||
env:
|
|
||||||
GOARCH: ${{ matrix.arch }}
|
|
||||||
|
|
||||||
- name: Test Cipher
|
- name: Test Cipher
|
||||||
run: go test -v -short ./cipher/...
|
run: go test -v -short ./cipher/...
|
||||||
env:
|
env:
|
||||||
|
2
.github/workflows/test_sm_ni.yml
vendored
2
.github/workflows/test_sm_ni.yml
vendored
@ -14,7 +14,7 @@ jobs:
|
|||||||
test:
|
test:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [1.18.x]
|
go-version: [1.23.x]
|
||||||
arch: [arm64]
|
arch: [arm64]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
@ -1,23 +1,27 @@
|
|||||||
# 免责申明
|
# 免责声明
|
||||||
|
|
||||||
## 声明
|
## 版权声明
|
||||||
|
本项目(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. **无担保声明**
|
||||||
|
本项目以“原样”提供,不提供任何形式的明示或暗示担保,包括但不限于对适销性、特定用途适用性和非侵权性的担保。
|
||||||
|
|
||||||
本项目(gmsm)**没有**经过相关机构资质认证。
|
2. **责任限制**
|
||||||
|
对于因使用或无法使用本项目代码而导致的任何直接、间接、偶然、特殊、典型或惩罚性损害(包括但不限于数据丢失、利润损失、业务中断等),项目作者或贡献者概不负责。
|
||||||
|
|
||||||
## 保证
|
3. **使用风险**
|
||||||
|
使用本项目代码的风险由使用者自行承担。使用者在使用本项目时,应自行评估代码的适用性和安全性,并根据实际需求进行测试和调整。
|
||||||
|
|
||||||
本项目(gmsm)不提供任何形式的保证。所有与使用本项目(gmsm)软件库及源代码相关的直接或间接风险均由用户自行承担。本项目(gmsm)提供的所有代码,并不对性能、适用性、适销性及其他方面提供任何保证。
|
4. **第三方依赖**
|
||||||
|
本项目可能依赖于第三方库或服务,这些库或服务的使用应遵守其各自的许可证条款。项目作者或贡献者不对第三方库或服务的稳定性、安全性或合法性负责。
|
||||||
|
|
||||||
本项目(gmsm)实现的内容可能包含不准确性或错误,我们不对内容的准确性进行保证。如果您发现本项目(gmsm)内容包含错误,请直接提交错误至[issue tracker](https://github.com/emmansun/gmsm/issues),或者提交代码至[pull requests](https://github.com/emmansun/gmsm/pulls)。
|
5. **法律合规**
|
||||||
|
使用本项目代码时,使用者应确保遵守所在国家或地区的法律法规。如因使用本项目代码而违反相关法律,使用者需自行承担法律责任。
|
||||||
|
|
||||||
## 您的行为
|
6. **产品认证**
|
||||||
|
本项目(gmsm)**没有**经过相关机构(包括但不限于**国家密码管理局商用密码检测中心**)认证,也不承诺会进行相关认证工作。
|
||||||
|
|
||||||
当您以任何方式使用本项目(gmsm)软件库及源代码时,说明您已经同意并接受本页面的所有信息。
|
7. **修改与分发**
|
||||||
|
如果您修改本项目代码并重新分发,请确保保留上述版权声明和免责条款,并明确标注您的修改内容。
|
||||||
## 联系方式
|
|
||||||
|
|
||||||
联系邮箱:emman.sun@foxmail.com(相关事务请发函至该邮箱)
|
|
||||||
|
@ -23,6 +23,8 @@ Go语言商用密码软件,简称**GMSM**,一个安全、高性能、易于
|
|||||||
- [PKCS7应用指南](./docs/pkcs7.md)
|
- [PKCS7应用指南](./docs/pkcs7.md)
|
||||||
- [PKCS12应用指南](./docs/pkcs12.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密码算法使用规范》中的密钥对保护数据格式。
|
- **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密码算法使用规范》中的密钥对保护数据格式。
|
||||||
|
|
||||||
@ -35,13 +37,13 @@ Go语言商用密码软件,简称**GMSM**,一个安全、高性能、易于
|
|||||||
- **ZUC** - 祖冲之序列密码算法实现。使用SIMD、AES指令以及无进位乘法指令,分别对**amd64**、**arm64**和**ppc64x**架构做了优化实现, 您也可以参考[ZUC实现及优化](https://github.com/emmansun/gmsm/wiki/Efficient-Software-Implementations-of-ZUC)和相关代码,以获得更多实现细节。ZUC包实现了基于祖冲之序列密码算法的机密性算法、128/256位完整性算法。
|
- **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 采用分组密码的机制》的消息鉴别码。
|
- **CBCMAC** - 符合《GB/T 15852.1-2020 采用分组密码的机制》的消息鉴别码。
|
||||||
- **CFCA** - CFCA(中金)特定实现,目前实现的是SM2私钥、证书封装处理,对应SADK中的**PKCS12_SM2**。
|
- **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)模式的话,从软件实现的角度来看,基本没有性能优化的空间。
|
- **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包的分支,加入了商用密码支持。
|
- **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,如有使用需求,可以考虑实现。
|
- **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日被归档)的分支,加入了商用密码支持。
|
- **PKCS7** - [mozilla-services/pkcs7](https://github.com/mozilla-services/pkcs7) 项目(该项目已于2024年2月10日被归档)的分支,加入了商用密码支持。
|
||||||
|
|
||||||
@ -80,5 +82,5 @@ PKCS8包代码是[youmark/pkcs8](https://github.com/youmark/pkcs8)项目的一
|
|||||||
|
|
||||||
使用本项目前,请务必仔细阅读[GMSM软件免责声明](DISCLAIMER.md)!
|
使用本项目前,请务必仔细阅读[GMSM软件免责声明](DISCLAIMER.md)!
|
||||||
|
|
||||||
## 关注度
|
## 项目星标趋势
|
||||||
[](https://starchart.cc/emmansun/gmsm)
|
[](https://starchart.cc/emmansun/gmsm)
|
||||||
|
@ -7,16 +7,16 @@ package cbcmac
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
|
"crypto/subtle"
|
||||||
|
|
||||||
"github.com/emmansun/gmsm/internal/subtle"
|
|
||||||
"github.com/emmansun/gmsm/padding"
|
"github.com/emmansun/gmsm/padding"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Reference: GB/T 15821.1-2020 Security techniques
|
// Reference: GB/T 15821.1-2020 Security techniques
|
||||||
// Message authentication codes - Part 1: Mechanisms using block ciphers
|
// Message authentication codes - Part 1: Mechanisms using block ciphers
|
||||||
|
|
||||||
// BockCipherMAC is the interface that wraps the basic MAC method.
|
// BlockCipherMAC is the interface that wraps the basic MAC method.
|
||||||
type BockCipherMAC interface {
|
type BlockCipherMAC interface {
|
||||||
// Size returns the MAC value's number of bytes.
|
// Size returns the MAC value's number of bytes.
|
||||||
Size() int
|
Size() int
|
||||||
|
|
||||||
@ -33,14 +33,21 @@ type cbcmac struct {
|
|||||||
size int
|
size int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCBCMAC returns a CBC-MAC instance that implements the MAC with the given block cipher.
|
// NewCBCMAC returns a CBC-MAC (GB/T 15821.1-2020 MAC scheme 1) instance that
|
||||||
// The padding scheme is ISO/IEC 9797-1 method 2.
|
// implements the MAC with the given block cipher. The padding scheme is ISO/IEC 9797-1 method 2.
|
||||||
// GB/T 15821.1-2020 MAC scheme 1
|
func NewCBCMAC(b cipher.Block, size int) BlockCipherMAC {
|
||||||
func NewCBCMAC(b cipher.Block, size int) BockCipherMAC {
|
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() {
|
if size <= 0 || size > b.BlockSize() {
|
||||||
panic("cbcmac: invalid size")
|
panic("cbcmac: invalid size")
|
||||||
}
|
}
|
||||||
return &cbcmac{b: b, pad: padding.NewISO9797M2Padding(uint(b.BlockSize())), size: size}
|
return &cbcmac{b: b, pad: newPaddingFunc(uint(b.BlockSize())), size: size}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cbcmac) Size() int {
|
func (c *cbcmac) Size() int {
|
||||||
@ -68,10 +75,14 @@ type emac struct {
|
|||||||
size int
|
size int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEMAC returns an EMAC instance that implements MAC with the given block cipher.
|
// NewEMAC returns an EMAC (GB/T 15821.1-2020 MAC scheme 2) instance that
|
||||||
// The padding scheme is ISO/IEC 9797-1 method 2.
|
// implements MAC with the given block cipher. The padding scheme is ISO/IEC 9797-1 method 2.
|
||||||
// GB/T 15821.1-2020 MAC scheme 2
|
func NewEMAC(creator func(key []byte) (cipher.Block, error), key1, key2 []byte, size int) BlockCipherMAC {
|
||||||
func NewEMAC(creator func(key []byte) (cipher.Block, error), key1, key2 []byte, size int) BockCipherMAC {
|
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 b1, b2 cipher.Block
|
||||||
var err error
|
var err error
|
||||||
if b1, err = creator(key1); err != nil {
|
if b1, err = creator(key1); err != nil {
|
||||||
@ -83,7 +94,7 @@ func NewEMAC(creator func(key []byte) (cipher.Block, error), key1, key2 []byte,
|
|||||||
if b2, err = creator(key2); err != nil {
|
if b2, err = creator(key2); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
return &emac{pad: padding.NewISO9797M2Padding(uint(b1.BlockSize())), b1: b1, b2: b2, size: size}
|
return &emac{pad: newPaddingFunc(uint(b1.BlockSize())), b1: b1, b2: b2, size: size}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *emac) Size() int {
|
func (e *emac) Size() int {
|
||||||
@ -105,11 +116,15 @@ func (e *emac) MAC(src []byte) []byte {
|
|||||||
|
|
||||||
type ansiRetailMAC emac
|
type ansiRetailMAC emac
|
||||||
|
|
||||||
// NewANSIRetailMAC returns an ANSI Retail MAC instance that implements MAC with the given block cipher.
|
// NewANSIRetailMAC returns an ANSI Retail MAC (GB/T 15821.1-2020 MAC scheme 3) instance that
|
||||||
// The padding scheme is ISO/IEC 9797-1 method 2.
|
// implements MAC with the given block cipher. The padding scheme is ISO/IEC 9797-1 method 2.
|
||||||
// GB/T 15821.1-2020 MAC scheme 3
|
func NewANSIRetailMAC(creator func(key []byte) (cipher.Block, error), key1, key2 []byte, size int) BlockCipherMAC {
|
||||||
func NewANSIRetailMAC(creator func(key []byte) (cipher.Block, error), key1, key2 []byte, size int) BockCipherMAC {
|
return NewANSIRetailMACWithPadding(creator, key1, key2, size, padding.NewISO9797M2Padding)
|
||||||
return (*ansiRetailMAC)(NewEMAC(creator, key1, key2, size).(*emac))
|
}
|
||||||
|
|
||||||
|
// 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 {
|
func (e *ansiRetailMAC) Size() int {
|
||||||
@ -136,10 +151,14 @@ type macDES struct {
|
|||||||
size int
|
size int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMACDES returns a MAC-DES instance that implements MAC with the given block cipher.
|
// NewMACDES returns a MAC-DES (GB/T 15821.1-2020 MAC scheme 4) instance that
|
||||||
// The padding scheme is ISO/IEC 9797-1 method 2.
|
// implements MAC with the given block cipher. The padding scheme is ISO/IEC 9797-1 method 2.
|
||||||
// GB/T 15821.1-2020 MAC scheme 4
|
func NewMACDES(creator func(key []byte) (cipher.Block, error), key1, key2 []byte, size int) BlockCipherMAC {
|
||||||
func NewMACDES(creator func(key []byte) (cipher.Block, error), key1, key2 []byte, size int) BockCipherMAC {
|
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 b1, b2, b3 cipher.Block
|
||||||
var err error
|
var err error
|
||||||
if b1, err = creator(key1); err != nil {
|
if b1, err = creator(key1); err != nil {
|
||||||
@ -153,13 +172,13 @@ func NewMACDES(creator func(key []byte) (cipher.Block, error), key1, key2 []byte
|
|||||||
}
|
}
|
||||||
key3 := make([]byte, len(key2))
|
key3 := make([]byte, len(key2))
|
||||||
copy(key3, key2)
|
copy(key3, key2)
|
||||||
for i := 0; i < len(key3); i++ {
|
for i := range key3 {
|
||||||
key3[i] ^= 0xF0
|
key3[i] ^= 0xF0
|
||||||
}
|
}
|
||||||
if b3, err = creator(key3); err != nil {
|
if b3, err = creator(key3); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
return &macDES{pad: padding.NewISO9797M2Padding(uint(b1.BlockSize())), b1: b1, b2: b2, b3: b3, size: size}
|
return &macDES{pad: newPaddingFunc(uint(b1.BlockSize())), b1: b1, b2: b2, b3: b3, size: size}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *macDES) Size() int {
|
func (m *macDES) Size() int {
|
||||||
@ -194,8 +213,7 @@ type cmac struct {
|
|||||||
len uint64
|
len uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCMAC returns a CMAC instance that implements MAC with the given block cipher.
|
// NewCMAC returns a CMAC (GB/T 15821.1-2020 MAC scheme 5) instance that implements MAC with the given block cipher.
|
||||||
// GB/T 15821.1-2020 MAC scheme 5
|
|
||||||
//
|
//
|
||||||
// Reference: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38B.pdf
|
// Reference: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38B.pdf
|
||||||
func NewCMAC(b cipher.Block, size int) *cmac {
|
func NewCMAC(b cipher.Block, size int) *cmac {
|
||||||
@ -334,9 +352,14 @@ type lmac struct {
|
|||||||
size int
|
size int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLMAC returns an LMAC instance that implements MAC with the given block cipher.
|
// NewLMAC returns an LMAC (GB/T 15821.1-2020 MAC scheme 6) instance that
|
||||||
// GB/T 15821.1-2020 MAC scheme 6
|
// 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) BockCipherMAC {
|
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 b, b1, b2 cipher.Block
|
||||||
var err error
|
var err error
|
||||||
if b, err = creator(key); err != nil {
|
if b, err = creator(key); err != nil {
|
||||||
@ -359,7 +382,7 @@ func NewLMAC(creator func(key []byte) (cipher.Block, error), key []byte, size in
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &lmac{b1: b1, b2: b2, pad: padding.NewISO9797M2Padding(uint(blockSize)), size: size}
|
return &lmac{b1: b1, b2: b2, pad: newPaddingFunc(uint(blockSize)), size: size}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *lmac) Size() int {
|
func (l *lmac) Size() int {
|
||||||
@ -385,11 +408,11 @@ type trCBCMAC struct {
|
|||||||
size int
|
size int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTRCBCMAC returns a TR-CBC-MAC instance that implements MAC with the given block cipher.
|
// NewTRCBCMAC returns a TR-CBC-MAC (GB/T 15821.1-2020 MAC scheme 7) instance that
|
||||||
// GB/T 15821.1-2020 MAC scheme 7
|
// implements MAC with the given block cipher.
|
||||||
//
|
//
|
||||||
// Reference: TrCBC: Another look at CBC-MAC.
|
// Reference: TrCBC: Another look at CBC-MAC.
|
||||||
func NewTRCBCMAC(b cipher.Block, size int) BockCipherMAC {
|
func NewTRCBCMAC(b cipher.Block, size int) BlockCipherMAC {
|
||||||
if size <= 0 || size > b.BlockSize() {
|
if size <= 0 || size > b.BlockSize() {
|
||||||
panic("cbcmac: invalid size")
|
panic("cbcmac: invalid size")
|
||||||
}
|
}
|
||||||
@ -425,11 +448,10 @@ type cbcrMAC struct {
|
|||||||
size int
|
size int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCBCRMAC returns a CBCRMAC instance that implements MAC with the given block cipher.
|
// NewCBCRMAC returns a CBCRMAC (GB/T 15821.1-2020 MAC scheme 8) instance that implements MAC with the given block cipher.
|
||||||
// GB/T 15821.1-2020 MAC scheme 8
|
|
||||||
//
|
//
|
||||||
// Reference: CBCR: CBC MAC with rotating transformations.
|
// Reference: CBCR: CBC MAC with rotating transformations.
|
||||||
func NewCBCRMAC(b cipher.Block, size int) BockCipherMAC {
|
func NewCBCRMAC(b cipher.Block, size int) BlockCipherMAC {
|
||||||
if size <= 0 || size > b.BlockSize() {
|
if size <= 0 || size > b.BlockSize() {
|
||||||
panic("cbcmac: invalid size")
|
panic("cbcmac: invalid size")
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/emmansun/gmsm/internal/cryptotest"
|
"github.com/emmansun/gmsm/internal/cryptotest"
|
||||||
|
"github.com/emmansun/gmsm/padding"
|
||||||
"github.com/emmansun/gmsm/sm4"
|
"github.com/emmansun/gmsm/sm4"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -48,6 +49,114 @@ func TestCBCMAC(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 := 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 := 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) {
|
func TestEMAC(t *testing.T) {
|
||||||
// Test vectors from GB/T 15821.1-2020 Appendix B.
|
// Test vectors from GB/T 15821.1-2020 Appendix B.
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
@ -122,6 +231,61 @@ func TestANSIRetailMAC(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 := 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) {
|
func TestMACDES(t *testing.T) {
|
||||||
// Test vectors from GB/T 15821.1-2020 Appendix B.
|
// Test vectors from GB/T 15821.1-2020 Appendix B.
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
@ -159,6 +323,61 @@ func TestMACDES(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 := 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) {
|
func TestCMAC(t *testing.T) {
|
||||||
// Test vectors from GB/T 15821.1-2020 Appendix B.
|
// Test vectors from GB/T 15821.1-2020 Appendix B.
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
@ -229,6 +448,55 @@ func TestLMAC(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 := 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) {
|
func TestTRCBCMAC(t *testing.T) {
|
||||||
// Test vectors from GB/T 15821.1-2020 Appendix B.
|
// Test vectors from GB/T 15821.1-2020 Appendix B.
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
|
57
cfca/encrypt.go
Normal file
57
cfca/encrypt.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// 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)
|
||||||
|
}
|
180
cfca/example_test.go
Normal file
180
cfca/example_test.go
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
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
|
||||||
|
}
|
92
cfca/pkcs10.go
Normal file
92
cfca/pkcs10.go
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
// 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
|
||||||
|
}
|
283
cfca/pkcs10_test.go
Normal file
283
cfca/pkcs10_test.go
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
// 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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,38 +1,35 @@
|
|||||||
// Package cfca handles cfca issued key and certificate
|
// Package cfca supports part of CFCA SADK's functions, provides interoperability with CFCA SADK.
|
||||||
package cfca
|
package cfca
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/cipher"
|
|
||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/emmansun/gmsm/padding"
|
|
||||||
"github.com/emmansun/gmsm/pkcs"
|
"github.com/emmansun/gmsm/pkcs"
|
||||||
"github.com/emmansun/gmsm/pkcs7"
|
"github.com/emmansun/gmsm/pkcs7"
|
||||||
"github.com/emmansun/gmsm/sm2"
|
"github.com/emmansun/gmsm/sm2"
|
||||||
"github.com/emmansun/gmsm/sm3"
|
|
||||||
"github.com/emmansun/gmsm/sm4"
|
|
||||||
"github.com/emmansun/gmsm/smx509"
|
"github.com/emmansun/gmsm/smx509"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CFCA私有格式,在SADK中把它定义为PKCS12_SM2
|
// 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 {
|
type cfcaKeyPairData struct {
|
||||||
Version int `asn1:"default:1"`
|
Version int `asn1:"default:1"`
|
||||||
EncryptedKey keyData
|
EncryptedKey keyData
|
||||||
Certificate certData
|
Certificate certData
|
||||||
}
|
}
|
||||||
|
|
||||||
// 被加密的私钥数据
|
// Encrypted private key data
|
||||||
type keyData struct {
|
type keyData struct {
|
||||||
ContentType asn1.ObjectIdentifier
|
ContentType asn1.ObjectIdentifier
|
||||||
Algorithm asn1.ObjectIdentifier
|
Algorithm asn1.ObjectIdentifier
|
||||||
EncryptedContent asn1.RawValue
|
EncryptedContent asn1.RawValue
|
||||||
}
|
}
|
||||||
|
|
||||||
// 对应的证书
|
// Corresponding certificate
|
||||||
type certData struct {
|
type certData struct {
|
||||||
ContentType asn1.ObjectIdentifier
|
ContentType asn1.ObjectIdentifier
|
||||||
Content asn1.RawContent
|
Content asn1.RawContent
|
||||||
@ -45,6 +42,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ParseSM2 parses the der data, returns private key and related certificate, it's CFCA private structure.
|
// 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) {
|
func ParseSM2(password, data []byte) (*sm2.PrivateKey, *smx509.Certificate, error) {
|
||||||
var keys cfcaKeyPairData
|
var keys cfcaKeyPairData
|
||||||
if _, err := asn1.Unmarshal(data, &keys); err != nil {
|
if _, err := asn1.Unmarshal(data, &keys); err != nil {
|
||||||
@ -59,22 +57,14 @@ func ParseSM2(password, data []byte) (*sm2.PrivateKey, *smx509.Certificate, erro
|
|||||||
if !keys.EncryptedKey.Algorithm.Equal(oidSM4) && !keys.EncryptedKey.Algorithm.Equal(oidSM4CBC) {
|
if !keys.EncryptedKey.Algorithm.Equal(oidSM4) && !keys.EncryptedKey.Algorithm.Equal(oidSM4CBC) {
|
||||||
return nil, nil, fmt.Errorf("cfca: unsupported algorithm <%v>", keys.EncryptedKey.Algorithm)
|
return nil, nil, fmt.Errorf("cfca: unsupported algorithm <%v>", keys.EncryptedKey.Algorithm)
|
||||||
}
|
}
|
||||||
ivkey := sm3.Kdf(password, 32)
|
pk, err := DecryptBySM4CBC(keys.EncryptedKey.EncryptedContent.Bytes, password)
|
||||||
marshalledIV, err := asn1.Marshal(ivkey[:16])
|
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 {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
pk, err := pkcs.SM4CBC.Decrypt(ivkey[16:], &asn1.RawValue{FullBytes: marshalledIV}, keys.EncryptedKey.EncryptedContent.Bytes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
d := new(big.Int).SetBytes(pk) // here we do NOT check if the d is in (0, N) or not
|
|
||||||
// Create private key from *big.Int
|
|
||||||
prvKey := new(sm2.PrivateKey)
|
|
||||||
prvKey.Curve = sm2.P256()
|
|
||||||
prvKey.D = d
|
|
||||||
prvKey.PublicKey.X, prvKey.PublicKey.Y = prvKey.ScalarBaseMult(prvKey.D.Bytes())
|
|
||||||
|
|
||||||
cert, err := smx509.ParseCertificate(keys.Certificate.Content)
|
cert, err := smx509.ParseCertificate(keys.Certificate.Content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@ -86,24 +76,15 @@ func ParseSM2(password, data []byte) (*sm2.PrivateKey, *smx509.Certificate, erro
|
|||||||
return prvKey, cert, nil
|
return prvKey, cert, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalSM2 encodes sm2 private key and related certificate to cfca defined format
|
// 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) {
|
func MarshalSM2(password []byte, key *sm2.PrivateKey, cert *smx509.Certificate) ([]byte, error) {
|
||||||
if len(password) == 0 {
|
var err error
|
||||||
return nil, errors.New("cfca: invalid password")
|
var ciphertext []byte
|
||||||
}
|
if ciphertext, err = EncryptBySM4CBC(key.D.Bytes(), password); err != nil {
|
||||||
ivkey := sm3.Kdf(password, 32)
|
|
||||||
block, err := sm4.NewCipher(ivkey[16:])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
mode := cipher.NewCBCEncrypter(block, ivkey[:16])
|
if ciphertext, err = asn1.Marshal(ciphertext); err != nil {
|
||||||
pkcs7 := padding.NewPKCS7Padding(uint(block.BlockSize()))
|
|
||||||
plainText := pkcs7.Pad(key.D.Bytes())
|
|
||||||
ciphertext := make([]byte, len(plainText))
|
|
||||||
mode.CryptBlocks(ciphertext, plainText)
|
|
||||||
|
|
||||||
ciphertext, err = asn1.Marshal(ciphertext)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,9 +58,57 @@ func parseTestKeyAndCert() (*sm2.PrivateKey, *smx509.Certificate, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseSM2(t *testing.T) {
|
func TestParseSM2(t *testing.T) {
|
||||||
_, _, err := parseTestKeyAndCert()
|
cases := []struct {
|
||||||
if err != nil {
|
pem string
|
||||||
t.Fatal(err)
|
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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,8 +163,8 @@ func TestParseSM2WithInvalidPwd(t *testing.T) {
|
|||||||
password := []byte("wrongpwd")
|
password := []byte("wrongpwd")
|
||||||
der, _ := hex.DecodeString("308203490201013047060a2a811ccf55060104020106072a811ccf5501680430016ca58c339f32f5bbe2b0491d5087c9b5de0f5aef4b4d0726941a0c21d9795f51e802f2a1596cc909d1638067ca28cc308202f9060a2a811ccf550601040201048202e9308202e530820289a00302010202051040990809300c06082a811ccf550183750500305c310b300906035504061302434e3130302e060355040a0c274368696e612046696e616e6369616c2043657274696669636174696f6e20417574686f72697479311b301906035504030c1243464341205445535420534d32204f434131301e170d3230313131393038333131385a170d3235313131393038333131385a308189310b300906035504061302434e31173015060355040a0c0e434643412054455354204f434131310d300b060355040b0c045053424331193017060355040b0c104f7267616e697a6174696f6e616c2d323137303506035504030c2e30353140e982aee582a8e7babfe4b88ae694b6e58d95e59586e688b7404e353130313133303030313838373840313059301306072a8648ce3d020106082a811ccf5501822d0342000495510badce29f70be07df6e2b0ce75be124a56c08e82435e72b4aa6c17679f455a6892aadde2a6b7a58ca7b0e10ca78d3811ff27e9f728cd80d53c1b9a6461dba382010630820102301f0603551d230418301680146bfe18da8f423aa6b86db32e88833a34a2c130e1300c0603551d130101ff0402300030480603551d200441303f303d060860811c86ef2a01013031302f06082b060105050702011623687474703a2f2f7777772e636663612e636f6d2e636e2f75732f75732d31342e68746d30390603551d1f04323030302ea02ca02a8628687474703a2f2f7563726c2e636663612e636f6d2e636e2f534d322f63726c31343335362e63726c300e0603551d0f0101ff0404030206c0301d0603551d0e04160414f8863d94f4a13b915ef9321a83a0be23445a7735301d0603551d250416301406082b0601050507030206082b06010505070304300c06082a811ccf5501837505000348003045022100890d2b153df86bfa09c3012323e52cadfa1c6c6a61f280f79c8c22db71d1504202204970b89aeb05e459e3fc2a4d4eabe4f7ee7a16e20d9004c64bcc5187b9332811")
|
der, _ := hex.DecodeString("308203490201013047060a2a811ccf55060104020106072a811ccf5501680430016ca58c339f32f5bbe2b0491d5087c9b5de0f5aef4b4d0726941a0c21d9795f51e802f2a1596cc909d1638067ca28cc308202f9060a2a811ccf550601040201048202e9308202e530820289a00302010202051040990809300c06082a811ccf550183750500305c310b300906035504061302434e3130302e060355040a0c274368696e612046696e616e6369616c2043657274696669636174696f6e20417574686f72697479311b301906035504030c1243464341205445535420534d32204f434131301e170d3230313131393038333131385a170d3235313131393038333131385a308189310b300906035504061302434e31173015060355040a0c0e434643412054455354204f434131310d300b060355040b0c045053424331193017060355040b0c104f7267616e697a6174696f6e616c2d323137303506035504030c2e30353140e982aee582a8e7babfe4b88ae694b6e58d95e59586e688b7404e353130313133303030313838373840313059301306072a8648ce3d020106082a811ccf5501822d0342000495510badce29f70be07df6e2b0ce75be124a56c08e82435e72b4aa6c17679f455a6892aadde2a6b7a58ca7b0e10ca78d3811ff27e9f728cd80d53c1b9a6461dba382010630820102301f0603551d230418301680146bfe18da8f423aa6b86db32e88833a34a2c130e1300c0603551d130101ff0402300030480603551d200441303f303d060860811c86ef2a01013031302f06082b060105050702011623687474703a2f2f7777772e636663612e636f6d2e636e2f75732f75732d31342e68746d30390603551d1f04323030302ea02ca02a8628687474703a2f2f7563726c2e636663612e636f6d2e636e2f534d322f63726c31343335362e63726c300e0603551d0f0101ff0404030206c0301d0603551d0e04160414f8863d94f4a13b915ef9321a83a0be23445a7735301d0603551d250416301406082b0601050507030206082b06010505070304300c06082a811ccf5501837505000348003045022100890d2b153df86bfa09c3012323e52cadfa1c6c6a61f280f79c8c22db71d1504202204970b89aeb05e459e3fc2a4d4eabe4f7ee7a16e20d9004c64bcc5187b9332811")
|
||||||
_, _, err := ParseSM2(password, der)
|
_, _, err := ParseSM2(password, der)
|
||||||
if err == nil || err.Error() != "padding: invalid padding byte/length" {
|
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("expected error of 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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ import (
|
|||||||
|
|
||||||
type certKeyPair struct {
|
type certKeyPair struct {
|
||||||
Certificate *smx509.Certificate
|
Certificate *smx509.Certificate
|
||||||
PrivateKey *crypto.PrivateKey
|
PrivateKey crypto.PrivateKey
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTestSM2Certificate(allCA bool) (certKeyPair, error) {
|
func createTestSM2Certificate(allCA bool) (certKeyPair, error) {
|
||||||
@ -64,7 +64,7 @@ func createTestSM2CertificateByIssuer(name string, issuer *certKeyPair, sigAlg x
|
|||||||
}
|
}
|
||||||
if issuer != nil {
|
if issuer != nil {
|
||||||
issuerCert = issuer.Certificate
|
issuerCert = issuer.Certificate
|
||||||
issuerKey = *issuer.PrivateKey
|
issuerKey = issuer.PrivateKey
|
||||||
}
|
}
|
||||||
|
|
||||||
switch sigAlg {
|
switch sigAlg {
|
||||||
@ -106,7 +106,7 @@ func createTestSM2CertificateByIssuer(name string, issuer *certKeyPair, sigAlg x
|
|||||||
// pem.Encode(os.Stdout, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})
|
// pem.Encode(os.Stdout, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})
|
||||||
return &certKeyPair{
|
return &certKeyPair{
|
||||||
Certificate: cert,
|
Certificate: cert,
|
||||||
PrivateKey: &priv,
|
PrivateKey: priv,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,12 +125,12 @@ func TestEnvelopeMessage(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = OpenEnvelopedMessage(encrypted[:len(encrypted)-1], cert.Certificate, *cert.PrivateKey)
|
_, err = OpenEnvelopedMessage(encrypted[:len(encrypted)-1], cert.Certificate, cert.PrivateKey)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("expected error when decrypting with wrong key, got nil")
|
t.Fatalf("expected error when decrypting with wrong key, got nil")
|
||||||
}
|
}
|
||||||
// pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: encrypted})
|
// pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: encrypted})
|
||||||
result, err := OpenEnvelopedMessage(encrypted, cert.Certificate, *cert.PrivateKey)
|
result, err := OpenEnvelopedMessage(encrypted, cert.Certificate, cert.PrivateKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("cannot Decrypt encrypted result: %v", err)
|
t.Fatalf("cannot Decrypt encrypted result: %v", err)
|
||||||
}
|
}
|
||||||
@ -155,12 +155,12 @@ func TestEnvelopeMessageLegacy(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = OpenEnvelopedMessage(encrypted[:len(encrypted)-1], cert.Certificate, *cert.PrivateKey)
|
_, err = OpenEnvelopedMessage(encrypted[:len(encrypted)-1], cert.Certificate, cert.PrivateKey)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("expected error when decrypting with wrong key, got nil")
|
t.Fatalf("expected error when decrypting with wrong key, got nil")
|
||||||
}
|
}
|
||||||
// pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: encrypted})
|
// pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: encrypted})
|
||||||
result, err := OpenEnvelopedMessageLegacy(encrypted, cert.Certificate, *cert.PrivateKey)
|
result, err := OpenEnvelopedMessageLegacy(encrypted, cert.Certificate, cert.PrivateKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("cannot Decrypt encrypted result: %v", err)
|
t.Fatalf("cannot Decrypt encrypted result: %v", err)
|
||||||
}
|
}
|
||||||
|
83
cfca/pkcs7_sign.go
Normal file
83
cfca/pkcs7_sign.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
// 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()
|
||||||
|
}
|
122
cfca/pkcs7_sign_test.go
Normal file
122
cfca/pkcs7_sign_test.go
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
24
cipher/bc.go
24
cipher/bc.go
@ -3,25 +3,23 @@
|
|||||||
package cipher
|
package cipher
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_cipher "crypto/cipher"
|
"bytes"
|
||||||
|
"crypto/cipher"
|
||||||
"github.com/emmansun/gmsm/internal/subtle"
|
"crypto/subtle"
|
||||||
)
|
)
|
||||||
|
|
||||||
type bc struct {
|
type bc struct {
|
||||||
b _cipher.Block
|
b cipher.Block
|
||||||
blockSize int
|
blockSize int
|
||||||
iv []byte
|
iv []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func newBC(b _cipher.Block, iv []byte) *bc {
|
func newBC(b cipher.Block, iv []byte) *bc {
|
||||||
c := &bc{
|
return &bc{
|
||||||
b: b,
|
b: b,
|
||||||
blockSize: b.BlockSize(),
|
blockSize: b.BlockSize(),
|
||||||
iv: make([]byte, b.BlockSize()),
|
iv: bytes.Clone(iv),
|
||||||
}
|
}
|
||||||
copy(c.iv, iv)
|
|
||||||
return c
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type bcEncrypter bc
|
type bcEncrypter bc
|
||||||
@ -31,13 +29,13 @@ type bcEncrypter bc
|
|||||||
// NewBCEncrypter will check for this interface and return the specific
|
// NewBCEncrypter will check for this interface and return the specific
|
||||||
// BlockMode if found.
|
// BlockMode if found.
|
||||||
type bcEncAble interface {
|
type bcEncAble interface {
|
||||||
NewBCEncrypter(iv []byte) _cipher.BlockMode
|
NewBCEncrypter(iv []byte) cipher.BlockMode
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBCEncrypter returns a BlockMode which encrypts in block chaining
|
// NewBCEncrypter returns a BlockMode which encrypts in block chaining
|
||||||
// mode, using the given Block. The length of iv must be the same as the
|
// mode, using the given Block. The length of iv must be the same as the
|
||||||
// Block's block size.
|
// Block's block size.
|
||||||
func NewBCEncrypter(b _cipher.Block, iv []byte) _cipher.BlockMode {
|
func NewBCEncrypter(b cipher.Block, iv []byte) cipher.BlockMode {
|
||||||
if len(iv) != b.BlockSize() {
|
if len(iv) != b.BlockSize() {
|
||||||
panic("cipher.NewBCEncrypter: IV length must equal block size")
|
panic("cipher.NewBCEncrypter: IV length must equal block size")
|
||||||
}
|
}
|
||||||
@ -82,13 +80,13 @@ type bcDecrypter bc
|
|||||||
// NewBCDecrypter will check for this interface and return the specific
|
// NewBCDecrypter will check for this interface and return the specific
|
||||||
// BlockMode if found.
|
// BlockMode if found.
|
||||||
type bcDecAble interface {
|
type bcDecAble interface {
|
||||||
NewBCDecrypter(iv []byte) _cipher.BlockMode
|
NewBCDecrypter(iv []byte) cipher.BlockMode
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBCDecrypter returns a BlockMode which decrypts in block chaining
|
// NewBCDecrypter returns a BlockMode which decrypts in block chaining
|
||||||
// mode, using the given Block. The length of iv must be the same as the
|
// 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.
|
// Block's block size and must match the iv used to encrypt the data.
|
||||||
func NewBCDecrypter(b _cipher.Block, iv []byte) _cipher.BlockMode {
|
func NewBCDecrypter(b cipher.Block, iv []byte) cipher.BlockMode {
|
||||||
if len(iv) != b.BlockSize() {
|
if len(iv) != b.BlockSize() {
|
||||||
panic("cipher.NewBCDecrypter: IV length must equal block size")
|
panic("cipher.NewBCDecrypter: IV length must equal block size")
|
||||||
}
|
}
|
||||||
|
@ -2,15 +2,14 @@
|
|||||||
package cipher
|
package cipher
|
||||||
|
|
||||||
import (
|
import (
|
||||||
goCipher "crypto/cipher"
|
"crypto/cipher"
|
||||||
goSubtle "crypto/subtle"
|
"crypto/subtle"
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/emmansun/gmsm/internal/alias"
|
"github.com/emmansun/gmsm/internal/alias"
|
||||||
"github.com/emmansun/gmsm/internal/byteorder"
|
"github.com/emmansun/gmsm/internal/byteorder"
|
||||||
"github.com/emmansun/gmsm/internal/subtle"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -23,11 +22,11 @@ const (
|
|||||||
// ccmAble is an interface implemented by ciphers that have a specific optimized
|
// ccmAble is an interface implemented by ciphers that have a specific optimized
|
||||||
// implementation of CCM.
|
// implementation of CCM.
|
||||||
type ccmAble interface {
|
type ccmAble interface {
|
||||||
NewCCM(nonceSize, tagSize int) (goCipher.AEAD, error)
|
NewCCM(nonceSize, tagSize int) (cipher.AEAD, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ccm struct {
|
type ccm struct {
|
||||||
cipher goCipher.Block
|
cipher cipher.Block
|
||||||
nonceSize int
|
nonceSize int
|
||||||
tagSize int
|
tagSize int
|
||||||
}
|
}
|
||||||
@ -57,14 +56,14 @@ func maxlen(L, tagsize int) int {
|
|||||||
|
|
||||||
// NewCCM returns the given 128-bit, block cipher wrapped in CCM
|
// NewCCM returns the given 128-bit, block cipher wrapped in CCM
|
||||||
// with the standard nonce length.
|
// with the standard nonce length.
|
||||||
func NewCCM(cipher goCipher.Block) (goCipher.AEAD, error) {
|
func NewCCM(cipher cipher.Block) (cipher.AEAD, error) {
|
||||||
return NewCCMWithNonceAndTagSize(cipher, ccmStandardNonceSize, ccmTagSize)
|
return NewCCMWithNonceAndTagSize(cipher, ccmStandardNonceSize, ccmTagSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCCMWithNonceSize returns the given 128-bit, block cipher wrapped in CCM,
|
// NewCCMWithNonceSize returns the given 128-bit, block cipher wrapped in CCM,
|
||||||
// which accepts nonces of the given length. The length must not
|
// which accepts nonces of the given length. The length must not
|
||||||
// be zero.
|
// be zero.
|
||||||
func NewCCMWithNonceSize(cipher goCipher.Block, size int) (goCipher.AEAD, error) {
|
func NewCCMWithNonceSize(cipher cipher.Block, size int) (cipher.AEAD, error) {
|
||||||
return NewCCMWithNonceAndTagSize(cipher, size, ccmTagSize)
|
return NewCCMWithNonceAndTagSize(cipher, size, ccmTagSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,12 +71,12 @@ func NewCCMWithNonceSize(cipher goCipher.Block, size int) (goCipher.AEAD, error)
|
|||||||
// which generates tags with the given length.
|
// which generates tags with the given length.
|
||||||
//
|
//
|
||||||
// Tag sizes between 8 and 16 bytes are allowed.
|
// Tag sizes between 8 and 16 bytes are allowed.
|
||||||
func NewCCMWithTagSize(cipher goCipher.Block, tagSize int) (goCipher.AEAD, error) {
|
func NewCCMWithTagSize(cipher cipher.Block, tagSize int) (cipher.AEAD, error) {
|
||||||
return NewCCMWithNonceAndTagSize(cipher, ccmStandardNonceSize, tagSize)
|
return NewCCMWithNonceAndTagSize(cipher, ccmStandardNonceSize, tagSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://tools.ietf.org/html/rfc3610
|
// https://tools.ietf.org/html/rfc3610
|
||||||
func NewCCMWithNonceAndTagSize(cipher goCipher.Block, nonceSize, tagSize int) (goCipher.AEAD, error) {
|
func NewCCMWithNonceAndTagSize(cipher cipher.Block, nonceSize, tagSize int) (cipher.AEAD, error) {
|
||||||
if tagSize < ccmMinimumTagSize || tagSize > ccmBlockSize || tagSize&1 != 0 {
|
if tagSize < ccmMinimumTagSize || tagSize > ccmBlockSize || tagSize&1 != 0 {
|
||||||
return nil, errors.New("cipher: incorrect tag size given to CCM")
|
return nil, errors.New("cipher: incorrect tag size given to CCM")
|
||||||
}
|
}
|
||||||
@ -189,7 +188,7 @@ func (c *ccm) Seal(dst, nonce, plaintext, data []byte) []byte {
|
|||||||
c.cipher.Encrypt(tagMask[:], counter[:])
|
c.cipher.Encrypt(tagMask[:], counter[:])
|
||||||
|
|
||||||
counter[len(counter)-1] |= 1
|
counter[len(counter)-1] |= 1
|
||||||
ctr := goCipher.NewCTR(c.cipher, counter[:])
|
ctr := cipher.NewCTR(c.cipher, counter[:])
|
||||||
ctr.XORKeyStream(out, plaintext)
|
ctr.XORKeyStream(out, plaintext)
|
||||||
|
|
||||||
tag := c.auth(nonce, plaintext, data, &tagMask)
|
tag := c.auth(nonce, plaintext, data, &tagMask)
|
||||||
@ -231,17 +230,15 @@ func (c *ccm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
counter[len(counter)-1] |= 1
|
counter[len(counter)-1] |= 1
|
||||||
ctr := goCipher.NewCTR(c.cipher, counter[:])
|
ctr := cipher.NewCTR(c.cipher, counter[:])
|
||||||
ctr.XORKeyStream(out, ciphertext)
|
ctr.XORKeyStream(out, ciphertext)
|
||||||
expectedTag := c.auth(nonce, out, data, &tagMask)
|
expectedTag := c.auth(nonce, out, data, &tagMask)
|
||||||
if goSubtle.ConstantTimeCompare(expectedTag, tag) != 1 {
|
if subtle.ConstantTimeCompare(expectedTag, tag) != 1 {
|
||||||
// The AESNI code decrypts and authenticates concurrently, and
|
// The AESNI code decrypts and authenticates concurrently, and
|
||||||
// so overwrites dst in the event of a tag mismatch. That
|
// so overwrites dst in the event of a tag mismatch. That
|
||||||
// behavior is mimicked here in order to be consistent across
|
// behavior is mimicked here in order to be consistent across
|
||||||
// platforms.
|
// platforms.
|
||||||
for i := range out {
|
clear(out)
|
||||||
out[i] = 0
|
|
||||||
}
|
|
||||||
return nil, errOpen
|
return nil, errOpen
|
||||||
}
|
}
|
||||||
return ret, nil
|
return ret, nil
|
||||||
|
14
cipher/common.go
Normal file
14
cipher/common.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
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)
|
@ -1,12 +1,12 @@
|
|||||||
package cipher
|
package cipher
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_cipher "crypto/cipher"
|
"crypto/cipher"
|
||||||
|
"crypto/subtle"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/emmansun/gmsm/internal/alias"
|
"github.com/emmansun/gmsm/internal/alias"
|
||||||
"github.com/emmansun/gmsm/internal/byteorder"
|
"github.com/emmansun/gmsm/internal/byteorder"
|
||||||
"github.com/emmansun/gmsm/internal/subtle"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// A LengthPreservingMode represents a block cipher running in a length preserving mode (HCTR,
|
// A LengthPreservingMode represents a block cipher running in a length preserving mode (HCTR,
|
||||||
@ -105,7 +105,7 @@ var hctrReductionTable = []uint16{
|
|||||||
// https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.470.5288
|
// https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.470.5288
|
||||||
// GB/T 17964-2021 第11章 带泛杂凑函数的计数器工作模式
|
// GB/T 17964-2021 第11章 带泛杂凑函数的计数器工作模式
|
||||||
type hctr struct {
|
type hctr struct {
|
||||||
cipher _cipher.Block
|
cipher cipher.Block
|
||||||
tweak [blockSize]byte
|
tweak [blockSize]byte
|
||||||
// productTable contains the first sixteen powers of the hash key.
|
// productTable contains the first sixteen powers of the hash key.
|
||||||
// However, they are in bit reversed order.
|
// However, they are in bit reversed order.
|
||||||
@ -118,7 +118,7 @@ func (h *hctr) BlockSize() int {
|
|||||||
|
|
||||||
// NewHCTR returns a [LengthPreservingMode] which encrypts/decrypts useing the given [Block]
|
// NewHCTR returns a [LengthPreservingMode] which encrypts/decrypts useing the given [Block]
|
||||||
// in HCTR mode. The lenght of tweak and hash key must be the same as the [Block]'s block size.
|
// in HCTR mode. The lenght 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) {
|
func NewHCTR(cipher cipher.Block, tweak, hkey []byte) (LengthPreservingMode, error) {
|
||||||
if len(tweak) != blockSize || len(hkey) != blockSize {
|
if len(tweak) != blockSize || len(hkey) != blockSize {
|
||||||
return nil, errors.New("cipher: invalid tweak and/or hash key length")
|
return nil, errors.New("cipher: invalid tweak and/or hash key length")
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
package cipher
|
package cipher
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
_cipher "crypto/cipher"
|
_cipher "crypto/cipher"
|
||||||
"errors"
|
"errors"
|
||||||
)
|
)
|
||||||
@ -28,8 +29,8 @@ func newOFBNLF(cipherFunc CipherCreator, key, iv []byte) (*ofbnlf, error) {
|
|||||||
if len(iv) != c.blockSize {
|
if len(iv) != c.blockSize {
|
||||||
return nil, errors.New("cipher: IV length must equal block size")
|
return nil, errors.New("cipher: IV length must equal block size")
|
||||||
}
|
}
|
||||||
c.iv = make([]byte, c.blockSize)
|
c.iv = bytes.Clone(iv)
|
||||||
copy(c.iv, iv)
|
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
313
cipher/xts.go
313
cipher/xts.go
@ -1,54 +1,21 @@
|
|||||||
package cipher
|
package cipher
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_cipher "crypto/cipher"
|
"crypto/cipher"
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/emmansun/gmsm/internal/alias"
|
|
||||||
"github.com/emmansun/gmsm/internal/byteorder"
|
"github.com/emmansun/gmsm/internal/byteorder"
|
||||||
"github.com/emmansun/gmsm/internal/subtle"
|
"github.com/emmansun/gmsm/internal/cipher/xts"
|
||||||
)
|
)
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewXTSEncrypter creates a Cipher given a function for creating the underlying
|
// NewXTSEncrypter creates a Cipher given a function for creating the underlying
|
||||||
// block cipher (which must have a block size of 16 bytes).
|
// block cipher (which must have a block size of 16 bytes).
|
||||||
func NewXTSEncrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte) (_cipher.BlockMode, error) {
|
func NewXTSEncrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte) (cipher.BlockMode, error) {
|
||||||
return newXTSEncrypter(cipherFunc, key, tweakKey, tweak, false)
|
return xts.NewXTSEncrypter(cipherFunc, key, tweakKey, tweak, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewXTSEncrypterWithSector creates a Cipher given a function for creating the underlying
|
// 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.
|
// 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) {
|
func NewXTSEncrypterWithSector(cipherFunc CipherCreator, key, tweakKey []byte, sectorNum uint64) (cipher.BlockMode, error) {
|
||||||
tweak := make([]byte, blockSize)
|
tweak := make([]byte, blockSize)
|
||||||
byteorder.LEPutUint64(tweak[:8], sectorNum)
|
byteorder.LEPutUint64(tweak[:8], sectorNum)
|
||||||
return NewXTSEncrypter(cipherFunc, key, tweakKey, tweak)
|
return NewXTSEncrypter(cipherFunc, key, tweakKey, tweak)
|
||||||
@ -57,70 +24,28 @@ func NewXTSEncrypterWithSector(cipherFunc CipherCreator, key, tweakKey []byte, s
|
|||||||
// NewGBXTSEncrypter creates a Cipher given a function for creating the underlying
|
// NewGBXTSEncrypter creates a Cipher given a function for creating the underlying
|
||||||
// block cipher (which must have a block size of 16 bytes).
|
// block cipher (which must have a block size of 16 bytes).
|
||||||
// It follows GB/T 17964-2021.
|
// It follows GB/T 17964-2021.
|
||||||
func NewGBXTSEncrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte) (_cipher.BlockMode, error) {
|
func NewGBXTSEncrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte) (cipher.BlockMode, error) {
|
||||||
return newXTSEncrypter(cipherFunc, key, tweakKey, tweak, true)
|
return xts.NewXTSEncrypter(cipherFunc, key, tweakKey, tweak, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewGBXTSEncrypterWithSector creates a Cipher given a function for creating the underlying
|
// 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.
|
// block cipher (which must have a block size of 16 bytes) with sector number.
|
||||||
// It follows GB/T 17964-2021.
|
// It follows GB/T 17964-2021.
|
||||||
func NewGBXTSEncrypterWithSector(cipherFunc CipherCreator, key, tweakKey []byte, sectorNum uint64) (_cipher.BlockMode, error) {
|
func NewGBXTSEncrypterWithSector(cipherFunc CipherCreator, key, tweakKey []byte, sectorNum uint64) (cipher.BlockMode, error) {
|
||||||
tweak := make([]byte, blockSize)
|
tweak := make([]byte, blockSize)
|
||||||
byteorder.LEPutUint64(tweak[:8], sectorNum)
|
byteorder.LEPutUint64(tweak[:8], sectorNum)
|
||||||
return NewGBXTSEncrypter(cipherFunc, key, tweakKey, tweak)
|
return NewGBXTSEncrypter(cipherFunc, key, tweakKey, tweak)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newXTSEncrypter(cipherFunc CipherCreator, 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewXTSDecrypter creates a Cipher given a function for creating the underlying
|
// NewXTSDecrypter creates a Cipher given a function for creating the underlying
|
||||||
// block cipher (which must have a block size of 16 bytes) for decryption.
|
// block cipher (which must have a block size of 16 bytes) for decryption.
|
||||||
func NewXTSDecrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte) (_cipher.BlockMode, error) {
|
func NewXTSDecrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte) (cipher.BlockMode, error) {
|
||||||
return newXTSDecrypter(cipherFunc, key, tweakKey, tweak, false)
|
return xts.NewXTSDecrypter(cipherFunc, key, tweakKey, tweak, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewXTSDecrypterWithSector creates a Cipher given a function for creating the underlying
|
// 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.
|
// 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) {
|
func NewXTSDecrypterWithSector(cipherFunc CipherCreator, key, tweakKey []byte, sectorNum uint64) (cipher.BlockMode, error) {
|
||||||
tweak := make([]byte, blockSize)
|
tweak := make([]byte, blockSize)
|
||||||
byteorder.LEPutUint64(tweak[:8], sectorNum)
|
byteorder.LEPutUint64(tweak[:8], sectorNum)
|
||||||
return NewXTSDecrypter(cipherFunc, key, tweakKey, tweak)
|
return NewXTSDecrypter(cipherFunc, key, tweakKey, tweak)
|
||||||
@ -129,225 +54,15 @@ func NewXTSDecrypterWithSector(cipherFunc CipherCreator, key, tweakKey []byte, s
|
|||||||
// NewGBXTSDecrypter creates a Cipher given a function for creating the underlying
|
// NewGBXTSDecrypter creates a Cipher given a function for creating the underlying
|
||||||
// block cipher (which must have a block size of 16 bytes) for decryption.
|
// block cipher (which must have a block size of 16 bytes) for decryption.
|
||||||
// It follows GB/T 17964-2021.
|
// It follows GB/T 17964-2021.
|
||||||
func NewGBXTSDecrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte) (_cipher.BlockMode, error) {
|
func NewGBXTSDecrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte) (cipher.BlockMode, error) {
|
||||||
return newXTSDecrypter(cipherFunc, key, tweakKey, tweak, true)
|
return xts.NewXTSDecrypter(cipherFunc, key, tweakKey, tweak, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewGBXTSDecrypterWithSector creates a Cipher given a function for creating the underlying
|
// 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.
|
// block cipher (which must have a block size of 16 bytes) with sector number for decryption.
|
||||||
// It follows GB/T 17964-2021.
|
// It follows GB/T 17964-2021.
|
||||||
func NewGBXTSDecrypterWithSector(cipherFunc CipherCreator, key, tweakKey []byte, sectorNum uint64) (_cipher.BlockMode, error) {
|
func NewGBXTSDecrypterWithSector(cipherFunc CipherCreator, key, tweakKey []byte, sectorNum uint64) (cipher.BlockMode, error) {
|
||||||
tweak := make([]byte, blockSize)
|
tweak := make([]byte, blockSize)
|
||||||
byteorder.LEPutUint64(tweak[:8], sectorNum)
|
byteorder.LEPutUint64(tweak[:8], sectorNum)
|
||||||
return NewGBXTSDecrypter(cipherFunc, key, tweakKey, tweak)
|
return NewGBXTSDecrypter(cipherFunc, key, tweakKey, tweak)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newXTSDecrypter(cipherFunc CipherCreator, 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 []byte = 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 []byte = 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
24
docs/cfca.md
24
docs/cfca.md
@ -10,6 +10,7 @@ SADK(Security Application Development Kit)是CFCA推出的一套支持全平
|
|||||||
其JAVA版本(其它语言版本未知)基本上是一个基于[Bouncy Castle](https://www.bouncycastle.org/)的实现(当然它看起来也支持JNI接入OpenSSL、U盾?)。
|
其JAVA版本(其它语言版本未知)基本上是一个基于[Bouncy Castle](https://www.bouncycastle.org/)的实现(当然它看起来也支持JNI接入OpenSSL、U盾?)。
|
||||||
|
|
||||||
## 为什么会有互操作性问题
|
## 为什么会有互操作性问题
|
||||||
|
* CFCA有一些实现没有相关标准。
|
||||||
* SADK存在较早,可能有些实现早于标准发布。
|
* SADK存在较早,可能有些实现早于标准发布。
|
||||||
* SADK版本较多,不同版本也会有互操作性兼容问题。
|
* SADK版本较多,不同版本也会有互操作性兼容问题。
|
||||||
* 其它未知原因。
|
* 其它未知原因。
|
||||||
@ -59,6 +60,12 @@ SADK 3.2之后的版本,支持下列SM2密文格式(encryptedType):
|
|||||||
2. 对称加密算法的OID。```public static final ASN1ObjectIdentifier id_sm4_CBC = new ASN1ObjectIdentifier("1.2.156.10197.1.104");```。
|
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=2(0:从证书扩展中获取SubjectKeyID,找不到抛异常;1:根据公钥数据直接计算SubjectKeyID;2:使用证书的IssuerAndSerialNumber)才会使用IssuerAndSerialNumber。正常情况下,只有CA证书才一定会在证书扩展中有SubjectKeyID信息。如果要产生和CFCA一样的加密信封,请使用```pkcs7.EnvelopeMessageCFCA```方法。
|
3. 如果需要用本软件库去解密CFCA生成的SM2数字信封,目前会有问题(从**v0.29.3**开始可以解密)。CFCA实现不符合《GB/T 35275-2017:信息安全技术 SM2密码算法加密签名消息语法规范》,它的**RecipientInfo**默认使用SubjectKeyIdentifier而不是IssuerAndSerialNumber。在SADK 3.7.1.0中,需要指定recipientPolicyType=2(0:从证书扩展中获取SubjectKeyID,找不到抛异常;1:根据公钥数据直接计算SubjectKeyID;2:使用证书的IssuerAndSerialNumber)才会使用IssuerAndSerialNumber。正常情况下,只有CA证书才一定会在证书扩展中有SubjectKeyID信息。如果要产生和CFCA一样的加密信封,请使用```pkcs7.EnvelopeMessageCFCA```方法。
|
||||||
|
|
||||||
|
**v0.29.6**之后,请直接使用
|
||||||
|
* `cfca.EnvelopeMessage`
|
||||||
|
* `cfca.OpenEnvelopedMessage`
|
||||||
|
* `cfca.EnvelopeMessageLegacy`
|
||||||
|
* `cfca.OpenEnvelopedMessageLegacy`
|
||||||
|
|
||||||
#### SADK 3.2之前版本
|
#### SADK 3.2之前版本
|
||||||
1. 数据对称加密密钥的密文格式为**C1C2C3 格式,不带0x04这个点非压缩标识**。这个不符合《GM/T 0010-2012 SM2密码算法加密签名消息语法规范》以及《GB/T 35275-2017 信息安全技术 SM2密码算法加密签名消息语法规范》。
|
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)。
|
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)。
|
||||||
@ -69,7 +76,7 @@ SADK 3.2之后的版本,支持下列SM2密文格式(encryptedType):
|
|||||||
1. 数据对称加密密钥的密文格式为**ASN.1编码格式**,这个符合《GB/T 35275-2017 信息安全技术 SM2密码算法加密签名消息语法规范》。
|
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)。
|
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.EncryptSM```方法```Decrypt```方法提供了SADK 3.2+版本的信封加解密兼容性。使用时,请确保`cipher`参数选择```pkcs.SM4```。```pkcs7.EncryptSM```方法符合《GB/T 35275-2017 信息安全技术 SM2密码算法加密签名消息语法规范》,CFCA的SADK可实现相应数据的解密。
|
||||||
|
|
||||||
本软件库的```pkcs7.EnvelopeMessageCFCA```方法提供了CFCA SADK更兼容的实现,也就是recipientPolicyType=0。
|
本软件库的```pkcs7.EnvelopeMessageCFCA```方法提供了CFCA SADK更兼容的实现,也就是recipientPolicyType=0。
|
||||||
|
|
||||||
@ -82,11 +89,24 @@ SADK 3.2之后的版本,支持下列SM2密文格式(encryptedType):
|
|||||||
|
|
||||||
参考[cfca sadk 3.0.2.0](https://github.com/emmansun/gmsm/issues/260)
|
参考[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解密也实现了一定的自动检测(通过首字节判断,基于首字节只有固定那几个的假设):
|
要穷举、尝试所有可能的密文格式不是不可以,但这会或多或少地影响解密的性能。你要和对方集成,还是知己知彼比较好,对于加解密来说,对用户透明不代表是好事。本软件库的SM2解密也实现了一定的自动检测(通过首字节判断,基于首字节只有固定那几个的假设):
|
||||||
* 0x30 - ASN.1格式。
|
* 0x30 - ASN.1格式。
|
||||||
* 0x04 - C1为非压缩点格式,具体是C1C3C2还是C1C2C3取决于解密时的选项参数,默认为C1C3C2。
|
* 0x04 - C1为非压缩点格式,具体是C1C3C2还是C1C2C3取决于解密时的选项参数,默认为C1C3C2。
|
||||||
* 0x02/0x03 - 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私钥、证书的解析
|
### SM2私钥、证书的解析
|
||||||
这个是CFCA自定义的,未见相关标准,可以通过```cfca.ParseSM2```来解析。
|
这个是CFCA自定义的,未见相关标准,可以通过```cfca.ParseSM2```来解析。```cfca.ParseSM2```函数只接受**DER**编码的二进制数据,如果你的数据是**base64**编码的,请先自行解码。
|
||||||
|
@ -40,18 +40,25 @@
|
|||||||
### 签名数据(Signed Data)
|
### 签名数据(Signed Data)
|
||||||
签名数据,使用证书对应的私钥进行签名,理论上支持多个签名者,但通常使用场景都是单签。和数字信封数据类似,也分国密和非国密。
|
签名数据,使用证书对应的私钥进行签名,理论上支持多个签名者,但通常使用场景都是单签。和数字信封数据类似,也分国密和非国密。
|
||||||
|
|
||||||
#### 创建签名数据
|
#### 签名流程
|
||||||
|
1. 创建SignedData
|
||||||
(是否国密是指OID也使用国密体系)
|
(是否国密是指OID也使用国密体系)
|
||||||
|
|
||||||
| 是否国密 | 方法 | 默认签名算法 |
|
| 是否国密 | 数据是否是哈希值 | 方法 | 默认签名算法 |
|
||||||
| :--- | :--- | :--- |
|
| :--- | :--- | :--- | :--- |
|
||||||
| 否 | ```NewSignedData``` | SHA1 |
|
| 否 | 否 | ```NewSignedData``` | SHA1 |
|
||||||
| 是 | ```NewSMSignedData``` | SM3 |
|
| 否 | 是 | ```NewSignedDataWithDigest``` | SHA1 |
|
||||||
|
| 是 | 否 | ```NewSMSignedData``` | SM3 |
|
||||||
|
| 是 | 是 | ```NewSMSignedDataWithDigest``` | SM3 |
|
||||||
|
|
||||||
可选步骤:调用```SetDigestAlgorithm```设置想要的签名算法,通常国密不需要修改。
|
2. 可选步骤:调用```SetDigestAlgorithm```设置想要的签名算法,通常国密**不需要**修改。
|
||||||
接着调用```AddSigner```或```AddSignerChain```方法,进行签名;可以通过```SignerInfoConfig.SkipCertificates```指定忽略证书项(最终签名数据中不包含证书项);
|
3. 接着调用```AddSigner```或```AddSignerChain```方法,进行签名;可以通过```SignerInfoConfig.SkipCertificates```指定忽略证书项(最终签名数据中不包含证书项);
|
||||||
如果进行Detach签名,则调用```Detach```方法;
|
4. 如果进行Detach签名,则调用```Detach```方法;
|
||||||
最后调用```Finish```方法,序列化输出结果。
|
5. 最后调用```Finish```方法,序列化输出结果。
|
||||||
|
|
||||||
|
**注意**:
|
||||||
|
1. 如果是直接对哈希值签名,一定是Detach签名。
|
||||||
|
2. 国密签名如果要传入哈希值,在有Attribute的情况下,则哈希值只是标准的SM3哈希值;否则必须是符合SM2签名标准的哈希值(含SM2公钥信息)。
|
||||||
|
|
||||||
#### Detach签名
|
#### Detach签名
|
||||||
就是外部签名,**被签名数据**不包含在SignedData中(也就是其ContentInfo.Content为空)。
|
就是外部签名,**被签名数据**不包含在SignedData中(也就是其ContentInfo.Content为空)。
|
||||||
@ -86,7 +93,7 @@ if err := p7.VerifyWithChain(truststore); err != nil {
|
|||||||
1. 调用```Parse```方法;
|
1. 调用```Parse```方法;
|
||||||
2. 如果是Detach签名数据,则手动设置原始数据(参考```testSign```方法);
|
2. 如果是Detach签名数据,则手动设置原始数据(参考```testSign```方法);
|
||||||
3. 如果签名数据中不包含证书项,则手动设置验签证书(参考```TestSkipCertificates```);
|
3. 如果签名数据中不包含证书项,则手动设置验签证书(参考```TestSkipCertificates```);
|
||||||
4. 调用```Verify```或```VerifyWithChain```方法。
|
4. 如果Content是原始数据,调用```Verify```或```VerifyWithChain```方法;如果Content是哈希值,调用```VerifyAsDigest```或```VerifyAsDigestWithChain```方法。
|
||||||
|
|
||||||
#### 特殊方法
|
#### 特殊方法
|
||||||
```DegenerateCertificate```,退化成签名数据中只包含证书,目前没有使用SM2 OID的方法,如果需要可以请求添加。可以参考```TestDegenerateCertificate```和```TestParseSM2CertificateChain```。
|
```DegenerateCertificate```,退化成签名数据中只包含证书,目前没有使用SM2 OID的方法,如果需要可以请求添加。可以参考```TestDegenerateCertificate```和```TestParseSM2CertificateChain```。
|
||||||
|
61
docs/sm2.md
61
docs/sm2.md
@ -186,6 +186,18 @@ func ExampleVerifyASN1WithSM2() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 如何处理不用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中的应用。
|
这里有两个实现,一个是传统实现,位于sm2包中;另外一个参考最新go语言的实现在ecdh包中。在这里不详细介绍使用方法,一般只有tls/tlcp才会用到,普通应用通常不会涉及这一块,感兴趣的话可以参考github.com/Trisia/gotlcp中的应用。
|
||||||
|
|
||||||
@ -260,29 +272,26 @@ func ExamplePrivateKey_Decrypt() {
|
|||||||
我没有找到到哪个文档写有固定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的描述。
|
我没有找到到哪个文档写有固定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的问答
|
#### 来自AI的问答
|
||||||
**问: Why some crypto implementations of EC point to octet string conversion without form indicator?**
|
**问: 为什么一些crypto实现中,椭圆曲线点转换为八位字节串时没有使用格式指示符?**
|
||||||
**答:** In Elliptic Curve Cryptography (ECC), the conversion of an elliptic curve point to an octet string can be done in several ways, typically either in compressed or uncompressed form. The form indicator (also known as the prefix) is used to denote whether the point is represented in its compressed or uncompressed form.
|
**答:** 在椭圆曲线密码学(ECC)中,椭圆曲线点转换为八位字节串的方法有多种,通常可以是压缩形式或未压缩形式。格式指示符(也称为前缀)用于表示该点是以压缩形式还是未压缩形式表示。
|
||||||
|
|
||||||
However, some implementations might choose to omit the form indicator for a few reasons:
|
然而,一些实现可能会出于几个原因选择省略格式指示符:
|
||||||
|
|
||||||
1. **Simplicity**: Omitting the form indicator simplifies the implementation, as there's no need to handle different forms of point representation.
|
1. **简洁性**:省略格式指示符可以简化实现过程,因为不需要处理不同形式的点表示。
|
||||||
|
2. **预设知识**:如果事先知道所有点都将处于某种特定形式(例如,所有点总是未压缩的),则可能会认为格式指示符是不必要的。
|
||||||
|
3. **兼容性**:并非所有系统或库都支持两种形式的点表示。为了确保最大兼容性,实现可能会选择始终使用一种形式,从而省略格式指示符。
|
||||||
|
4. **安全性**:在某些情况下,为了防止依赖于点形式的某些类型的攻击,可能会省略格式指示符。
|
||||||
|
|
||||||
2. **Assumed Knowledge**: If it's known in advance that all points will be in a certain form (e.g., all points are always uncompressed), the form indicator might be deemed unnecessary.
|
请注意,这些是通用原因,具体原因可能会根据确切的上下文以及使用的特定密码协议或系统而有所不同。
|
||||||
|
|
||||||
3. **Compatibility**: Not all systems or libraries support both forms of point representation. To ensure maximum compatibility, an implementation might choose to always use one form and thus omit the form indicator.
|
**问:这不会引起互操作性问题吗?**
|
||||||
|
**答:** 是的,你说得对。在将椭圆曲线点转换为八位字节串时省略格式指示符,确实可能导致互操作性问题。
|
||||||
|
|
||||||
4. **Security**: In some cases, to prevent certain types of attacks that rely on the form of the point, the form indicator might be omitted.
|
当系统需要互操作时,它们必须就数据交换的通用格式达成一致。如果一个系统在编码椭圆曲线点时没有使用格式指示符,而另一个系统期望格式指示符存在,它们将无法正确解释彼此的数据。
|
||||||
|
|
||||||
Please note that these are general reasons and the specific reason can vary depending on the exact context and the specific cryptographic protocol or system in use.
|
这就是为什么像SEC(高效密码学标准)这样的标准会指定使用格式指示符。例如,SEC 1标准指定未压缩点的格式指示符应为0x04,压缩点的格式指示符应为0x02或0x03。
|
||||||
|
|
||||||
**问:Won't this cause interoperability issues?**
|
因此,虽然在封闭系统中省略格式指示符可能会简化实现或提供其他好处,但它可能会阻碍与其他系统的互操作性。在编码和解码密码数据时,通常最好遵循已建立的标准,以确保最大的兼容性。
|
||||||
**答:** Yes, you're correct. Omitting the form indicator in the conversion of an elliptic curve point to an octet string can indeed cause interoperability issues.
|
|
||||||
|
|
||||||
When systems need to interoperate, they must agree on a common format for data exchange. If one system encodes elliptic curve points without a form indicator and another system expects the form indicator to be present, they won't be able to correctly interpret each other's data.
|
|
||||||
|
|
||||||
This is why standards like those defined by the SEC (Standards for Efficient Cryptography) specify the use of a form indicator. For example, the SEC 1 standard specifies that the form indicator should be 0x04 for uncompressed points and 0x02 or 0x03 for compressed points.
|
|
||||||
|
|
||||||
So, while omitting the form indicator might simplify the implementation or provide other benefits within a closed system, it can hinder interoperability with other systems. It's generally best to follow established standards when encoding and decoding cryptographic data to ensure maximum compatibility.
|
|
||||||
|
|
||||||
### 性能
|
### 性能
|
||||||
从**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)。
|
从**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)。
|
||||||
@ -306,6 +315,26 @@ func calculateSM2Hash(pub *ecdsa.PublicKey, data, uid []byte) ([]byte, error) {
|
|||||||
```
|
```
|
||||||
公钥加密就没啥特殊,只要确保输出密文的编码格式和KMS一致即可。
|
公钥加密就没啥特殊,只要确保输出密文的编码格式和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扩展应用
|
||||||
SM2的一些扩展应用,譬如从签名中恢复公钥、半同态加密、环签名等,大多尚处于POC状态,也无相关标准。其它扩展应用(但凡椭圆曲线公钥密码算法能用到的场合),包括但不限于:
|
SM2的一些扩展应用,譬如从签名中恢复公钥、半同态加密、环签名等,大多尚处于POC状态,也无相关标准。其它扩展应用(但凡椭圆曲线公钥密码算法能用到的场合),包括但不限于:
|
||||||
* [确定性签名](https://datatracker.ietf.org/doc/html/rfc6979)
|
* [确定性签名](https://datatracker.ietf.org/doc/html/rfc6979)
|
||||||
|
@ -324,3 +324,6 @@ SM9公钥加密算法支持多种对称加密算法,不像SM2公钥加密算
|
|||||||
|
|
||||||
## 密钥交换
|
## 密钥交换
|
||||||
在这里不详细介绍使用方法,一般只有tls/tlcp才会用到,普通应用通常不会涉及这一块,请参考[API Document](https://godoc.org/github.com/emmansun/gmsm)。
|
在这里不详细介绍使用方法,一般只有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)。
|
||||||
|
@ -99,6 +99,10 @@ func ExampleNewCipher_zuc256() {
|
|||||||
// Output: some plaintext
|
// Output: some plaintext
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
### Seekable Stream
|
||||||
|
完整性算法支持Seekable Stream,也就是随机定位到某点进行处理,内部实现了分桶缓存状态,每个状态的大小大概是88字节,`bucketSize`的大小可以结合要处理的流大小以及内存占用来平衡考虑。同时,`bucketSize`内部会被处理成128字节的倍数,以利于实现。
|
||||||
|
|
||||||
|
如果您没有对同一个流反复进行**前进**、**后退**加解密的需求,可以使用`NewCipher`或者`NewEEACipher`方法,避免内部状态缓存。
|
||||||
|
|
||||||
## 完整性算法
|
## 完整性算法
|
||||||
完整性算法实现了```hash.Hash```接口,所以其使用方法和其它哈希算法类似。
|
完整性算法实现了```hash.Hash```接口,所以其使用方法和其它哈希算法类似。
|
||||||
|
@ -2,11 +2,11 @@ package drbg
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
|
"crypto/subtle"
|
||||||
"errors"
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/emmansun/gmsm/internal/byteorder"
|
"github.com/emmansun/gmsm/internal/byteorder"
|
||||||
"github.com/emmansun/gmsm/internal/subtle"
|
|
||||||
"github.com/emmansun/gmsm/sm4"
|
"github.com/emmansun/gmsm/sm4"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -120,12 +120,12 @@ func (hd *CtrDrbg) MaxBytesPerRequest() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate CTR DRBG pseudorandom bits generate process.
|
// Generate CTR DRBG pseudorandom bits generate process.
|
||||||
func (hd *CtrDrbg) Generate(b, additional []byte) error {
|
func (hd *CtrDrbg) Generate(out, additional []byte) error {
|
||||||
if hd.NeedReseed() {
|
if hd.NeedReseed() {
|
||||||
return ErrReseedRequired
|
return ErrReseedRequired
|
||||||
}
|
}
|
||||||
outlen := len(hd.v)
|
outlen := len(hd.v)
|
||||||
if (hd.gm && len(b) > outlen) || (!hd.gm && len(b) > MAX_BYTES_PER_GENERATE) {
|
if (hd.gm && len(out) > outlen) || (!hd.gm && len(out) > MAX_BYTES_PER_GENERATE) {
|
||||||
return errors.New("drbg: too many bytes requested")
|
return errors.New("drbg: too many bytes requested")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,14 +140,14 @@ func (hd *CtrDrbg) Generate(b, additional []byte) error {
|
|||||||
block := hd.newBlockCipher(hd.key)
|
block := hd.newBlockCipher(hd.key)
|
||||||
temp := make([]byte, outlen)
|
temp := make([]byte, outlen)
|
||||||
|
|
||||||
m := len(b)
|
m := len(out)
|
||||||
limit := uint64(m+outlen-1) / uint64(outlen)
|
limit := uint64(m+outlen-1) / uint64(outlen)
|
||||||
for i := 0; i < int(limit); i++ {
|
for i := range int(limit) {
|
||||||
// V = (V + 1) mod 2^outlen)
|
// V = (V + 1) mod 2^outlen)
|
||||||
addOne(hd.v, outlen)
|
addOne(hd.v, outlen)
|
||||||
// output_block = Encrypt(Key, V)
|
// output_block = Encrypt(Key, V)
|
||||||
block.Encrypt(temp, hd.v)
|
block.Encrypt(temp, hd.v)
|
||||||
copy(b[i*outlen:], temp)
|
copy(out[i*outlen:], temp)
|
||||||
}
|
}
|
||||||
hd.update(additional)
|
hd.update(additional)
|
||||||
hd.reseedCounter++
|
hd.reseedCounter++
|
||||||
@ -162,7 +162,7 @@ func (cd *CtrDrbg) update(seedMaterial []byte) {
|
|||||||
v := make([]byte, outlen)
|
v := make([]byte, outlen)
|
||||||
output := make([]byte, outlen)
|
output := make([]byte, outlen)
|
||||||
copy(v, cd.v)
|
copy(v, cd.v)
|
||||||
for i := 0; i < (cd.seedLength+outlen-1)/outlen; i++ {
|
for i := range (cd.seedLength+outlen-1)/outlen {
|
||||||
// V = (V + 1) mod 2^outlen
|
// V = (V + 1) mod 2^outlen
|
||||||
addOne(v, outlen)
|
addOne(v, outlen)
|
||||||
// output_block = Encrypt(Key, V)
|
// output_block = Encrypt(Key, V)
|
||||||
@ -191,7 +191,7 @@ func (cd *CtrDrbg) derive(seedMaterial []byte, returnBytes int) []byte {
|
|||||||
S[outlen+8+len(seedMaterial)] = 0x80
|
S[outlen+8+len(seedMaterial)] = 0x80
|
||||||
|
|
||||||
key := make([]byte, cd.keyLen)
|
key := make([]byte, cd.keyLen)
|
||||||
for i := 0; i < cd.keyLen; i++ {
|
for i := range cd.keyLen {
|
||||||
key[i] = byte(i)
|
key[i] = byte(i)
|
||||||
}
|
}
|
||||||
blocks := (cd.seedLength + outlen - 1) / outlen
|
blocks := (cd.seedLength + outlen - 1) / outlen
|
||||||
|
@ -187,7 +187,7 @@ func (hd *HashDrbg) Generate(b, additional []byte) error {
|
|||||||
limit := uint64(m+md.Size()-1) / uint64(md.Size())
|
limit := uint64(m+md.Size()-1) / uint64(md.Size())
|
||||||
data := make([]byte, hd.seedLength)
|
data := make([]byte, hd.seedLength)
|
||||||
copy(data, hd.v)
|
copy(data, hd.v)
|
||||||
for i := 0; i < int(limit); i++ {
|
for i := range int(limit) {
|
||||||
md.Write(data)
|
md.Write(data)
|
||||||
copy(b[i*md.Size():], md.Sum(nil))
|
copy(b[i*md.Size():], md.Sum(nil))
|
||||||
addOne(data, hd.seedLength)
|
addOne(data, hd.seedLength)
|
||||||
@ -211,7 +211,7 @@ func (hd *HashDrbg) derive(seedMaterial []byte, len int) []byte {
|
|||||||
byteorder.BEPutUint32(requireBytes[:], uint32(len<<3))
|
byteorder.BEPutUint32(requireBytes[:], uint32(len<<3))
|
||||||
var ct byte = 1
|
var ct byte = 1
|
||||||
k := make([]byte, len)
|
k := make([]byte, len)
|
||||||
for i := 0; i < int(limit); i++ {
|
for i := range int(limit) {
|
||||||
// Hash( counter_byte || return_bits || seed_material )
|
// Hash( counter_byte || return_bits || seed_material )
|
||||||
md.Write([]byte{ct})
|
md.Write([]byte{ct})
|
||||||
md.Write(requireBytes[:])
|
md.Write(requireBytes[:])
|
||||||
|
@ -45,7 +45,7 @@ func NewHmacDrbg(newHash func() hash.Hash, securityLevel SecurityLevel, gm bool,
|
|||||||
// HMAC_DRBG_Instantiate_process
|
// HMAC_DRBG_Instantiate_process
|
||||||
hd.key = make([]byte, hd.hashSize)
|
hd.key = make([]byte, hd.hashSize)
|
||||||
hd.v = make([]byte, hd.hashSize)
|
hd.v = make([]byte, hd.hashSize)
|
||||||
for i := 0; i < hd.hashSize; i++ {
|
for i := range hd.hashSize {
|
||||||
hd.key[i] = 0x00
|
hd.key[i] = 0x00
|
||||||
hd.v[i] = 0x01
|
hd.v[i] = 0x01
|
||||||
}
|
}
|
||||||
|
@ -32,8 +32,25 @@ func hexDecode(t *testing.T, s string) []byte {
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewPrivateKeyWithOrderMinus1(t *testing.T) {
|
func TestNewPrivateKey(t *testing.T) {
|
||||||
_, err := ecdh.P256().NewPrivateKey([]byte{
|
_, 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, 0xfe, 0xff, 0xff, 0xff, 0xff,
|
||||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
0x72, 0x03, 0xdf, 0x6b, 0x21, 0xc6, 0x05, 0x2b,
|
0x72, 0x03, 0xdf, 0x6b, 0x21, 0xc6, 0x05, 0x2b,
|
||||||
@ -43,6 +60,18 @@ func TestNewPrivateKeyWithOrderMinus1(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
func TestECDH(t *testing.T) {
|
||||||
aliceKey, err := ecdh.P256().GenerateKey(rand.Reader)
|
aliceKey, err := ecdh.P256().GenerateKey(rand.Reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -345,9 +374,7 @@ type zr struct{}
|
|||||||
|
|
||||||
// Read replaces the contents of dst with zeros. It is safe for concurrent use.
|
// Read replaces the contents of dst with zeros. It is safe for concurrent use.
|
||||||
func (zr) Read(dst []byte) (n int, err error) {
|
func (zr) Read(dst []byte) (n int, err error) {
|
||||||
for i := range dst {
|
clear(dst)
|
||||||
dst[i] = 0
|
|
||||||
}
|
|
||||||
return len(dst), nil
|
return len(dst), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
9
go.mod
9
go.mod
@ -1,8 +1,7 @@
|
|||||||
module github.com/emmansun/gmsm
|
module github.com/emmansun/gmsm
|
||||||
|
|
||||||
go 1.18
|
go 1.23.0
|
||||||
|
|
||||||
require (
|
require golang.org/x/crypto v0.37.0
|
||||||
golang.org/x/crypto v0.29.0
|
|
||||||
golang.org/x/sys v0.27.0
|
require golang.org/x/sys v0.32.0 // indirect
|
||||||
)
|
|
||||||
|
8
go.sum
8
go.sum
@ -1,4 +1,4 @@
|
|||||||
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||||
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
|
@ -85,10 +85,7 @@ func (x *Nat) expand(n int) *Nat {
|
|||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
extraLimbs := x.limbs[len(x.limbs):n]
|
extraLimbs := x.limbs[len(x.limbs):n]
|
||||||
// clear(extraLimbs)
|
clear(extraLimbs)
|
||||||
for i := range extraLimbs {
|
|
||||||
extraLimbs[i] = 0
|
|
||||||
}
|
|
||||||
x.limbs = x.limbs[:n]
|
x.limbs = x.limbs[:n]
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
@ -99,10 +96,8 @@ func (x *Nat) reset(n int) *Nat {
|
|||||||
x.limbs = make([]uint, n)
|
x.limbs = make([]uint, n)
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
// clear(x.limbs)
|
// Clear both the returned limbs and the previously used ones.
|
||||||
for i := range x.limbs {
|
clear(x.limbs[:max(n, len(x.limbs))])
|
||||||
x.limbs[i] = 0
|
|
||||||
}
|
|
||||||
x.limbs = x.limbs[:n]
|
x.limbs = x.limbs[:n]
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
package bigmod
|
package bigmod
|
||||||
|
|
||||||
import "golang.org/x/sys/cpu"
|
import "github.com/emmansun/gmsm/internal/deps/cpu"
|
||||||
|
|
||||||
// amd64 assembly uses ADCX/ADOX/MULX if ADX is available to run two carry
|
// 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
|
// chains in the flags in parallel across the whole operation, and aggressively
|
||||||
|
292
internal/cipher/xts/xts.go
Normal file
292
internal/cipher/xts/xts.go
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
// 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 []byte = 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 []byte = 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
//go:build (amd64 || arm64 || s390x || ppc64 || ppc64le) && !purego
|
//go:build (amd64 || arm64 || s390x || ppc64 || ppc64le) && !purego
|
||||||
|
|
||||||
package cipher
|
package xts
|
||||||
|
|
||||||
//go:noescape
|
//go:noescape
|
||||||
func mul2(tweak *[blockSize]byte, isGB bool)
|
func mul2(tweak *[blockSize]byte, isGB bool)
|
@ -1,6 +1,6 @@
|
|||||||
//go:build (amd64 || arm64 || s390x || ppc64 || ppc64le) && !purego
|
//go:build (amd64 || arm64 || s390x || ppc64 || ppc64le) && !purego
|
||||||
|
|
||||||
package cipher
|
package xts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
@ -1,6 +1,6 @@
|
|||||||
//go:build purego || !(amd64 || arm64 || s390x || ppc64 || ppc64le)
|
//go:build purego || !(amd64 || arm64 || s390x || ppc64 || ppc64le)
|
||||||
|
|
||||||
package cipher
|
package xts
|
||||||
|
|
||||||
func mul2(tweak *[blockSize]byte, isGB bool) {
|
func mul2(tweak *[blockSize]byte, isGB bool) {
|
||||||
mul2Generic(tweak, isGB)
|
mul2Generic(tweak, isGB)
|
||||||
@ -8,7 +8,7 @@ func mul2(tweak *[blockSize]byte, isGB bool) {
|
|||||||
|
|
||||||
func doubleTweaks(tweak *[blockSize]byte, tweaks []byte, isGB bool) {
|
func doubleTweaks(tweak *[blockSize]byte, tweaks []byte, isGB bool) {
|
||||||
count := len(tweaks) >> 4
|
count := len(tweaks) >> 4
|
||||||
for i := 0; i < count; i++ {
|
for i := range count {
|
||||||
copy(tweaks[blockSize*i:], tweak[:])
|
copy(tweaks[blockSize*i:], tweak[:])
|
||||||
mul2(tweak, isGB)
|
mul2(tweak, isGB)
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package cipher
|
package xts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/aes"
|
"crypto/aes"
|
@ -1,7 +1,9 @@
|
|||||||
package cpuid
|
package cpuid
|
||||||
|
|
||||||
import "golang.org/x/sys/cpu"
|
import "github.com/emmansun/gmsm/internal/deps/cpu"
|
||||||
|
|
||||||
var HasAES = cpu.X86.HasAES
|
var (
|
||||||
var HasGFMUL = cpu.X86.HasPCLMULQDQ
|
HasAES = cpu.X86.HasAES
|
||||||
var HasVPMSUMD = false
|
HasGFMUL = cpu.X86.HasPCLMULQDQ
|
||||||
|
HasVPMSUMD = false
|
||||||
|
)
|
||||||
|
@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
package cpuid
|
package cpuid
|
||||||
|
|
||||||
import "golang.org/x/sys/cpu"
|
import "github.com/emmansun/gmsm/internal/deps/cpu"
|
||||||
|
|
||||||
var HasAES = cpu.ARM64.HasAES
|
var (
|
||||||
var HasGFMUL = cpu.ARM64.HasPMULL
|
HasAES = cpu.ARM64.HasAES
|
||||||
var HasVPMSUMD = false
|
HasGFMUL = cpu.ARM64.HasPMULL
|
||||||
|
HasVPMSUMD = false
|
||||||
|
)
|
||||||
|
@ -6,6 +6,8 @@ package cpuid
|
|||||||
// to detect their supported state dynamically. Assume the CPU features that
|
// to detect their supported state dynamically. Assume the CPU features that
|
||||||
// Apple Silicon M1 supports to be available as a minimal set of features
|
// Apple Silicon M1 supports to be available as a minimal set of features
|
||||||
// to all Go programs running on darwin/arm64.
|
// to all Go programs running on darwin/arm64.
|
||||||
var HasAES = true
|
var (
|
||||||
var HasGFMUL = true
|
HasAES = true
|
||||||
var HasVPMSUMD = false
|
HasGFMUL = true
|
||||||
|
HasVPMSUMD = false
|
||||||
|
)
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
package cpuid
|
package cpuid
|
||||||
|
|
||||||
var HasAES = true
|
var (
|
||||||
var HasGFMUL = false
|
HasAES = true
|
||||||
var HasVPMSUMD = true
|
HasGFMUL = false
|
||||||
|
HasVPMSUMD = true
|
||||||
|
)
|
||||||
|
@ -7,11 +7,10 @@ package cryptotest
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
|
"crypto/subtle"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/emmansun/gmsm/internal/subtle"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Each test is executed with each of the buffer lengths in bufLens.
|
// Each test is executed with each of the buffer lengths in bufLens.
|
||||||
|
17
internal/deps/cpu/asm_aix_ppc64.s
Normal file
17
internal/deps/cpu/asm_aix_ppc64.s
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2018 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 gc
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// System calls for ppc64, AIX are implemented in runtime/syscall_aix.go
|
||||||
|
//
|
||||||
|
|
||||||
|
TEXT ·syscall6(SB),NOSPLIT,$0-88
|
||||||
|
JMP syscall·syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·rawSyscall6(SB),NOSPLIT,$0-88
|
||||||
|
JMP syscall·rawSyscall6(SB)
|
17
internal/deps/cpu/asm_arwin_x86_gc.s
Normal file
17
internal/deps/cpu/asm_arwin_x86_gc.s
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
//go:build darwin && amd64 && gc
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0
|
||||||
|
JMP libc_sysctl(SB)
|
||||||
|
GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8
|
||||||
|
DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB)
|
||||||
|
|
||||||
|
TEXT libc_sysctlbyname_trampoline<>(SB),NOSPLIT,$0-0
|
||||||
|
JMP libc_sysctlbyname(SB)
|
||||||
|
GLOBL ·libc_sysctlbyname_trampoline_addr(SB), RODATA, $8
|
||||||
|
DATA ·libc_sysctlbyname_trampoline_addr(SB)/8, $libc_sysctlbyname_trampoline<>(SB)
|
66
internal/deps/cpu/byteorder.go
Normal file
66
internal/deps/cpu/byteorder.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// Copyright 2019 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 cpu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// byteOrder is a subset of encoding/binary.ByteOrder.
|
||||||
|
type byteOrder interface {
|
||||||
|
Uint32([]byte) uint32
|
||||||
|
Uint64([]byte) uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type littleEndian struct{}
|
||||||
|
type bigEndian struct{}
|
||||||
|
|
||||||
|
func (littleEndian) Uint32(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 (littleEndian) Uint64(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 (bigEndian) Uint32(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 (bigEndian) Uint64(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
|
||||||
|
}
|
||||||
|
|
||||||
|
// hostByteOrder returns littleEndian on little-endian machines and
|
||||||
|
// bigEndian on big-endian machines.
|
||||||
|
func hostByteOrder() byteOrder {
|
||||||
|
switch runtime.GOARCH {
|
||||||
|
case "386", "amd64", "amd64p32",
|
||||||
|
"alpha",
|
||||||
|
"arm", "arm64",
|
||||||
|
"loong64",
|
||||||
|
"mipsle", "mips64le", "mips64p32le",
|
||||||
|
"nios2",
|
||||||
|
"ppc64le",
|
||||||
|
"riscv", "riscv64",
|
||||||
|
"sh":
|
||||||
|
return littleEndian{}
|
||||||
|
case "armbe", "arm64be",
|
||||||
|
"m68k",
|
||||||
|
"mips", "mips64", "mips64p32",
|
||||||
|
"ppc", "ppc64",
|
||||||
|
"s390", "s390x",
|
||||||
|
"shbe",
|
||||||
|
"sparc", "sparc64":
|
||||||
|
return bigEndian{}
|
||||||
|
}
|
||||||
|
panic("unknown architecture")
|
||||||
|
}
|
315
internal/deps/cpu/cpu.go
Normal file
315
internal/deps/cpu/cpu.go
Normal file
@ -0,0 +1,315 @@
|
|||||||
|
// Copyright 2018 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 cpu implements processor feature detection for
|
||||||
|
// various CPU architectures.
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Initialized reports whether the CPU features were initialized.
|
||||||
|
//
|
||||||
|
// For some GOOS/GOARCH combinations initialization of the CPU features depends
|
||||||
|
// on reading an operating specific file, e.g. /proc/self/auxv on linux/arm
|
||||||
|
// Initialized will report false if reading the file fails.
|
||||||
|
var Initialized bool
|
||||||
|
|
||||||
|
// CacheLinePad is used to pad structs to avoid false sharing.
|
||||||
|
type CacheLinePad struct{ _ [cacheLineSize]byte }
|
||||||
|
|
||||||
|
// X86 contains the supported CPU features of the
|
||||||
|
// current X86/AMD64 platform. If the current platform
|
||||||
|
// is not X86/AMD64 then all feature flags are false.
|
||||||
|
//
|
||||||
|
// X86 is padded to avoid false sharing. Further the HasAVX
|
||||||
|
// and HasAVX2 are only set if the OS supports XMM and YMM
|
||||||
|
// registers in addition to the CPUID feature bit being set.
|
||||||
|
var X86 struct {
|
||||||
|
_ CacheLinePad
|
||||||
|
HasAES bool // AES hardware implementation (AES NI)
|
||||||
|
HasADX bool // Multi-precision add-carry instruction extensions
|
||||||
|
HasAVX bool // Advanced vector extension
|
||||||
|
HasAVX2 bool // Advanced vector extension 2
|
||||||
|
HasAVX512 bool // Advanced vector extension 512
|
||||||
|
HasAVX512F bool // Advanced vector extension 512 Foundation Instructions
|
||||||
|
HasAVX512CD bool // Advanced vector extension 512 Conflict Detection Instructions
|
||||||
|
HasAVX512ER bool // Advanced vector extension 512 Exponential and Reciprocal Instructions
|
||||||
|
HasAVX512PF bool // Advanced vector extension 512 Prefetch Instructions
|
||||||
|
HasAVX512VL bool // Advanced vector extension 512 Vector Length Extensions
|
||||||
|
HasAVX512BW bool // Advanced vector extension 512 Byte and Word Instructions
|
||||||
|
HasAVX512DQ bool // Advanced vector extension 512 Doubleword and Quadword Instructions
|
||||||
|
HasAVX512IFMA bool // Advanced vector extension 512 Integer Fused Multiply Add
|
||||||
|
HasAVX512VBMI bool // Advanced vector extension 512 Vector Byte Manipulation Instructions
|
||||||
|
HasAVX5124VNNIW bool // Advanced vector extension 512 Vector Neural Network Instructions Word variable precision
|
||||||
|
HasAVX5124FMAPS bool // Advanced vector extension 512 Fused Multiply Accumulation Packed Single precision
|
||||||
|
HasAVX512VPOPCNTDQ bool // Advanced vector extension 512 Double and quad word population count instructions
|
||||||
|
HasAVX512VPCLMULQDQ bool // Advanced vector extension 512 Vector carry-less multiply operations
|
||||||
|
HasAVX512VNNI bool // Advanced vector extension 512 Vector Neural Network Instructions
|
||||||
|
HasAVX512GFNI bool // Advanced vector extension 512 Galois field New Instructions
|
||||||
|
HasAVX512VAES bool // Advanced vector extension 512 Vector AES instructions
|
||||||
|
HasAVX512VBMI2 bool // Advanced vector extension 512 Vector Byte Manipulation Instructions 2
|
||||||
|
HasAVX512BITALG bool // Advanced vector extension 512 Bit Algorithms
|
||||||
|
HasAVX512BF16 bool // Advanced vector extension 512 BFloat16 Instructions
|
||||||
|
HasAMXTile bool // Advanced Matrix Extension Tile instructions
|
||||||
|
HasAMXInt8 bool // Advanced Matrix Extension Int8 instructions
|
||||||
|
HasAMXBF16 bool // Advanced Matrix Extension BFloat16 instructions
|
||||||
|
HasBMI1 bool // Bit manipulation instruction set 1
|
||||||
|
HasBMI2 bool // Bit manipulation instruction set 2
|
||||||
|
HasCX16 bool // Compare and exchange 16 Bytes
|
||||||
|
HasERMS bool // Enhanced REP for MOVSB and STOSB
|
||||||
|
HasFMA bool // Fused-multiply-add instructions
|
||||||
|
HasOSXSAVE bool // OS supports XSAVE/XRESTOR for saving/restoring XMM registers.
|
||||||
|
HasPCLMULQDQ bool // PCLMULQDQ instruction - most often used for AES-GCM
|
||||||
|
HasPOPCNT bool // Hamming weight instruction POPCNT.
|
||||||
|
HasRDRAND bool // RDRAND instruction (on-chip random number generator)
|
||||||
|
HasRDSEED bool // RDSEED instruction (on-chip random number generator)
|
||||||
|
HasSSE2 bool // Streaming SIMD extension 2 (always available on amd64)
|
||||||
|
HasSSE3 bool // Streaming SIMD extension 3
|
||||||
|
HasSSSE3 bool // Supplemental streaming SIMD extension 3
|
||||||
|
HasSSE41 bool // Streaming SIMD extension 4 and 4.1
|
||||||
|
HasSSE42 bool // Streaming SIMD extension 4 and 4.2
|
||||||
|
HasAVXIFMA bool // Advanced vector extension Integer Fused Multiply Add
|
||||||
|
HasAVXVNNI bool // Advanced vector extension Vector Neural Network Instructions
|
||||||
|
HasAVXVNNIInt8 bool // Advanced vector extension Vector Neural Network Int8 instructions
|
||||||
|
_ CacheLinePad
|
||||||
|
}
|
||||||
|
|
||||||
|
// ARM64 contains the supported CPU features of the
|
||||||
|
// current ARMv8(aarch64) platform. If the current platform
|
||||||
|
// is not arm64 then all feature flags are false.
|
||||||
|
var ARM64 struct {
|
||||||
|
_ CacheLinePad
|
||||||
|
HasFP bool // Floating-point instruction set (always available)
|
||||||
|
HasASIMD bool // Advanced SIMD (always available)
|
||||||
|
HasEVTSTRM bool // Event stream support
|
||||||
|
HasAES bool // AES hardware implementation
|
||||||
|
HasPMULL bool // Polynomial multiplication instruction set
|
||||||
|
HasSHA1 bool // SHA1 hardware implementation
|
||||||
|
HasSHA2 bool // SHA2 hardware implementation
|
||||||
|
HasCRC32 bool // CRC32 hardware implementation
|
||||||
|
HasATOMICS bool // Atomic memory operation instruction set
|
||||||
|
HasFPHP bool // Half precision floating-point instruction set
|
||||||
|
HasASIMDHP bool // Advanced SIMD half precision instruction set
|
||||||
|
HasCPUID bool // CPUID identification scheme registers
|
||||||
|
HasASIMDRDM bool // Rounding double multiply add/subtract instruction set
|
||||||
|
HasJSCVT bool // Javascript conversion from floating-point to integer
|
||||||
|
HasFCMA bool // Floating-point multiplication and addition of complex numbers
|
||||||
|
HasLRCPC bool // Release Consistent processor consistent support
|
||||||
|
HasDCPOP bool // Persistent memory support
|
||||||
|
HasSHA3 bool // SHA3 hardware implementation
|
||||||
|
HasSM3 bool // SM3 hardware implementation
|
||||||
|
HasSM4 bool // SM4 hardware implementation
|
||||||
|
HasASIMDDP bool // Advanced SIMD double precision instruction set
|
||||||
|
HasSHA512 bool // SHA512 hardware implementation
|
||||||
|
HasSVE bool // Scalable Vector Extensions
|
||||||
|
HasSVE2 bool // Scalable Vector Extensions 2
|
||||||
|
HasASIMDFHM bool // Advanced SIMD multiplication FP16 to FP32
|
||||||
|
HasDIT bool // Data Independent Timing support
|
||||||
|
HasI8MM bool // Advanced SIMD Int8 matrix multiplication instructions
|
||||||
|
_ CacheLinePad
|
||||||
|
}
|
||||||
|
|
||||||
|
// ARM contains the supported CPU features of the current ARM (32-bit) platform.
|
||||||
|
// All feature flags are false if:
|
||||||
|
// 1. the current platform is not arm, or
|
||||||
|
// 2. the current operating system is not Linux.
|
||||||
|
var ARM struct {
|
||||||
|
_ CacheLinePad
|
||||||
|
HasSWP bool // SWP instruction support
|
||||||
|
HasHALF bool // Half-word load and store support
|
||||||
|
HasTHUMB bool // ARM Thumb instruction set
|
||||||
|
Has26BIT bool // Address space limited to 26-bits
|
||||||
|
HasFASTMUL bool // 32-bit operand, 64-bit result multiplication support
|
||||||
|
HasFPA bool // Floating point arithmetic support
|
||||||
|
HasVFP bool // Vector floating point support
|
||||||
|
HasEDSP bool // DSP Extensions support
|
||||||
|
HasJAVA bool // Java instruction set
|
||||||
|
HasIWMMXT bool // Intel Wireless MMX technology support
|
||||||
|
HasCRUNCH bool // MaverickCrunch context switching and handling
|
||||||
|
HasTHUMBEE bool // Thumb EE instruction set
|
||||||
|
HasNEON bool // NEON instruction set
|
||||||
|
HasVFPv3 bool // Vector floating point version 3 support
|
||||||
|
HasVFPv3D16 bool // Vector floating point version 3 D8-D15
|
||||||
|
HasTLS bool // Thread local storage support
|
||||||
|
HasVFPv4 bool // Vector floating point version 4 support
|
||||||
|
HasIDIVA bool // Integer divide instruction support in ARM mode
|
||||||
|
HasIDIVT bool // Integer divide instruction support in Thumb mode
|
||||||
|
HasVFPD32 bool // Vector floating point version 3 D15-D31
|
||||||
|
HasLPAE bool // Large Physical Address Extensions
|
||||||
|
HasEVTSTRM bool // Event stream support
|
||||||
|
HasAES bool // AES hardware implementation
|
||||||
|
HasPMULL bool // Polynomial multiplication instruction set
|
||||||
|
HasSHA1 bool // SHA1 hardware implementation
|
||||||
|
HasSHA2 bool // SHA2 hardware implementation
|
||||||
|
HasCRC32 bool // CRC32 hardware implementation
|
||||||
|
_ CacheLinePad
|
||||||
|
}
|
||||||
|
|
||||||
|
// MIPS64X contains the supported CPU features of the current mips64/mips64le
|
||||||
|
// platforms. If the current platform is not mips64/mips64le or the current
|
||||||
|
// operating system is not Linux then all feature flags are false.
|
||||||
|
var MIPS64X struct {
|
||||||
|
_ CacheLinePad
|
||||||
|
HasMSA bool // MIPS SIMD architecture
|
||||||
|
_ CacheLinePad
|
||||||
|
}
|
||||||
|
|
||||||
|
// PPC64 contains the supported CPU features of the current ppc64/ppc64le platforms.
|
||||||
|
// If the current platform is not ppc64/ppc64le then all feature flags are false.
|
||||||
|
//
|
||||||
|
// For ppc64/ppc64le, it is safe to check only for ISA level starting on ISA v3.00,
|
||||||
|
// since there are no optional categories. There are some exceptions that also
|
||||||
|
// require kernel support to work (DARN, SCV), so there are feature bits for
|
||||||
|
// those as well. The struct is padded to avoid false sharing.
|
||||||
|
var PPC64 struct {
|
||||||
|
_ CacheLinePad
|
||||||
|
HasDARN bool // Hardware random number generator (requires kernel enablement)
|
||||||
|
HasSCV bool // Syscall vectored (requires kernel enablement)
|
||||||
|
IsPOWER8 bool // ISA v2.07 (POWER8)
|
||||||
|
IsPOWER9 bool // ISA v3.00 (POWER9), implies IsPOWER8
|
||||||
|
_ CacheLinePad
|
||||||
|
}
|
||||||
|
|
||||||
|
// S390X contains the supported CPU features of the current IBM Z
|
||||||
|
// (s390x) platform. If the current platform is not IBM Z then all
|
||||||
|
// feature flags are false.
|
||||||
|
//
|
||||||
|
// S390X is padded to avoid false sharing. Further HasVX is only set
|
||||||
|
// if the OS supports vector registers in addition to the STFLE
|
||||||
|
// feature bit being set.
|
||||||
|
var S390X struct {
|
||||||
|
_ CacheLinePad
|
||||||
|
HasZARCH bool // z/Architecture mode is active [mandatory]
|
||||||
|
HasSTFLE bool // store facility list extended
|
||||||
|
HasLDISP bool // long (20-bit) displacements
|
||||||
|
HasEIMM bool // 32-bit immediates
|
||||||
|
HasDFP bool // decimal floating point
|
||||||
|
HasETF3EH bool // ETF-3 enhanced
|
||||||
|
HasMSA bool // message security assist (CPACF)
|
||||||
|
HasAES bool // KM-AES{128,192,256} functions
|
||||||
|
HasAESCBC bool // KMC-AES{128,192,256} functions
|
||||||
|
HasAESCTR bool // KMCTR-AES{128,192,256} functions
|
||||||
|
HasAESGCM bool // KMA-GCM-AES{128,192,256} functions
|
||||||
|
HasGHASH bool // KIMD-GHASH function
|
||||||
|
HasSHA1 bool // K{I,L}MD-SHA-1 functions
|
||||||
|
HasSHA256 bool // K{I,L}MD-SHA-256 functions
|
||||||
|
HasSHA512 bool // K{I,L}MD-SHA-512 functions
|
||||||
|
HasSHA3 bool // K{I,L}MD-SHA3-{224,256,384,512} and K{I,L}MD-SHAKE-{128,256} functions
|
||||||
|
HasVX bool // vector facility
|
||||||
|
HasVXE bool // vector-enhancements facility 1
|
||||||
|
_ CacheLinePad
|
||||||
|
}
|
||||||
|
|
||||||
|
// RISCV64 contains the supported CPU features and performance characteristics for riscv64
|
||||||
|
// platforms. The booleans in RISCV64, with the exception of HasFastMisaligned, indicate
|
||||||
|
// the presence of RISC-V extensions.
|
||||||
|
//
|
||||||
|
// It is safe to assume that all the RV64G extensions are supported and so they are omitted from
|
||||||
|
// this structure. As riscv64 Go programs require at least RV64G, the code that populates
|
||||||
|
// this structure cannot run successfully if some of the RV64G extensions are missing.
|
||||||
|
// The struct is padded to avoid false sharing.
|
||||||
|
var RISCV64 struct {
|
||||||
|
_ CacheLinePad
|
||||||
|
HasFastMisaligned bool // Fast misaligned accesses
|
||||||
|
HasC bool // Compressed instruction-set extension
|
||||||
|
HasV bool // Vector extension compatible with RVV 1.0
|
||||||
|
HasZba bool // Address generation instructions extension
|
||||||
|
HasZbb bool // Basic bit-manipulation extension
|
||||||
|
HasZbs bool // Single-bit instructions extension
|
||||||
|
_ CacheLinePad
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
archInit()
|
||||||
|
initOptions()
|
||||||
|
processOptions()
|
||||||
|
}
|
||||||
|
|
||||||
|
// options contains the cpu debug options that can be used in GODEBUG.
|
||||||
|
// Options are arch dependent and are added by the arch specific initOptions functions.
|
||||||
|
// Features that are mandatory for the specific GOARCH should have the Required field set
|
||||||
|
// (e.g. SSE2 on amd64).
|
||||||
|
var options []option
|
||||||
|
|
||||||
|
// Option names should be lower case. e.g. avx instead of AVX.
|
||||||
|
type option struct {
|
||||||
|
Name string
|
||||||
|
Feature *bool
|
||||||
|
Specified bool // whether feature value was specified in GODEBUG
|
||||||
|
Enable bool // whether feature should be enabled
|
||||||
|
Required bool // whether feature is mandatory and can not be disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
func processOptions() {
|
||||||
|
env := os.Getenv("GODEBUG")
|
||||||
|
field:
|
||||||
|
for env != "" {
|
||||||
|
field := ""
|
||||||
|
i := strings.IndexByte(env, ',')
|
||||||
|
if i < 0 {
|
||||||
|
field, env = env, ""
|
||||||
|
} else {
|
||||||
|
field, env = env[:i], env[i+1:]
|
||||||
|
}
|
||||||
|
if len(field) < 4 || field[:4] != "cpu." {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
i = strings.IndexByte(field, '=')
|
||||||
|
if i < 0 {
|
||||||
|
print("GODEBUG sys/cpu: no value specified for \"", field, "\"\n")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
key, value := field[4:i], field[i+1:] // e.g. "SSE2", "on"
|
||||||
|
|
||||||
|
var enable bool
|
||||||
|
switch value {
|
||||||
|
case "on":
|
||||||
|
enable = true
|
||||||
|
case "off":
|
||||||
|
enable = false
|
||||||
|
default:
|
||||||
|
print("GODEBUG sys/cpu: value \"", value, "\" not supported for cpu option \"", key, "\"\n")
|
||||||
|
continue field
|
||||||
|
}
|
||||||
|
|
||||||
|
if key == "all" {
|
||||||
|
for i := range options {
|
||||||
|
options[i].Specified = true
|
||||||
|
options[i].Enable = enable || options[i].Required
|
||||||
|
}
|
||||||
|
continue field
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range options {
|
||||||
|
if options[i].Name == key {
|
||||||
|
options[i].Specified = true
|
||||||
|
options[i].Enable = enable
|
||||||
|
continue field
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print("GODEBUG sys/cpu: unknown cpu feature \"", key, "\"\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, o := range options {
|
||||||
|
if !o.Specified {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.Enable && !*o.Feature {
|
||||||
|
print("GODEBUG sys/cpu: can not enable \"", o.Name, "\", missing CPU support\n")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !o.Enable && o.Required {
|
||||||
|
print("GODEBUG sys/cpu: can not disable \"", o.Name, "\", required CPU feature\n")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
*o.Feature = o.Enable
|
||||||
|
}
|
||||||
|
}
|
33
internal/deps/cpu/cpu_aix.go
Normal file
33
internal/deps/cpu/cpu_aix.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// Copyright 2019 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 aix
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
const (
|
||||||
|
// getsystemcfg constants
|
||||||
|
_SC_IMPL = 2
|
||||||
|
_IMPL_POWER8 = 0x10000
|
||||||
|
_IMPL_POWER9 = 0x20000
|
||||||
|
)
|
||||||
|
|
||||||
|
func archInit() {
|
||||||
|
impl := getsystemcfg(_SC_IMPL)
|
||||||
|
if impl&_IMPL_POWER8 != 0 {
|
||||||
|
PPC64.IsPOWER8 = true
|
||||||
|
}
|
||||||
|
if impl&_IMPL_POWER9 != 0 {
|
||||||
|
PPC64.IsPOWER8 = true
|
||||||
|
PPC64.IsPOWER9 = true
|
||||||
|
}
|
||||||
|
|
||||||
|
Initialized = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func getsystemcfg(label int) (n uint64) {
|
||||||
|
r0, _ := callgetsystemcfg(label)
|
||||||
|
n = uint64(r0)
|
||||||
|
return
|
||||||
|
}
|
72
internal/deps/cpu/cpu_arm.go
Normal file
72
internal/deps/cpu/cpu_arm.go
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// Copyright 2018 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 cpu
|
||||||
|
|
||||||
|
const cacheLineSize = 32
|
||||||
|
|
||||||
|
// HWCAP/HWCAP2 bits.
|
||||||
|
// These are specific to Linux.
|
||||||
|
const (
|
||||||
|
hwcap_SWP = 1 << 0
|
||||||
|
hwcap_HALF = 1 << 1
|
||||||
|
hwcap_THUMB = 1 << 2
|
||||||
|
hwcap_26BIT = 1 << 3
|
||||||
|
hwcap_FAST_MULT = 1 << 4
|
||||||
|
hwcap_FPA = 1 << 5
|
||||||
|
hwcap_VFP = 1 << 6
|
||||||
|
hwcap_EDSP = 1 << 7
|
||||||
|
hwcap_JAVA = 1 << 8
|
||||||
|
hwcap_IWMMXT = 1 << 9
|
||||||
|
hwcap_CRUNCH = 1 << 10
|
||||||
|
hwcap_THUMBEE = 1 << 11
|
||||||
|
hwcap_NEON = 1 << 12
|
||||||
|
hwcap_VFPv3 = 1 << 13
|
||||||
|
hwcap_VFPv3D16 = 1 << 14
|
||||||
|
hwcap_TLS = 1 << 15
|
||||||
|
hwcap_VFPv4 = 1 << 16
|
||||||
|
hwcap_IDIVA = 1 << 17
|
||||||
|
hwcap_IDIVT = 1 << 18
|
||||||
|
hwcap_VFPD32 = 1 << 19
|
||||||
|
hwcap_LPAE = 1 << 20
|
||||||
|
hwcap_EVTSTRM = 1 << 21
|
||||||
|
|
||||||
|
hwcap2_AES = 1 << 0
|
||||||
|
hwcap2_PMULL = 1 << 1
|
||||||
|
hwcap2_SHA1 = 1 << 2
|
||||||
|
hwcap2_SHA2 = 1 << 3
|
||||||
|
hwcap2_CRC32 = 1 << 4
|
||||||
|
)
|
||||||
|
|
||||||
|
func initOptions() {
|
||||||
|
options = []option{
|
||||||
|
{Name: "pmull", Feature: &ARM.HasPMULL},
|
||||||
|
{Name: "sha1", Feature: &ARM.HasSHA1},
|
||||||
|
{Name: "sha2", Feature: &ARM.HasSHA2},
|
||||||
|
{Name: "swp", Feature: &ARM.HasSWP},
|
||||||
|
{Name: "thumb", Feature: &ARM.HasTHUMB},
|
||||||
|
{Name: "thumbee", Feature: &ARM.HasTHUMBEE},
|
||||||
|
{Name: "tls", Feature: &ARM.HasTLS},
|
||||||
|
{Name: "vfp", Feature: &ARM.HasVFP},
|
||||||
|
{Name: "vfpd32", Feature: &ARM.HasVFPD32},
|
||||||
|
{Name: "vfpv3", Feature: &ARM.HasVFPv3},
|
||||||
|
{Name: "vfpv3d16", Feature: &ARM.HasVFPv3D16},
|
||||||
|
{Name: "vfpv4", Feature: &ARM.HasVFPv4},
|
||||||
|
{Name: "half", Feature: &ARM.HasHALF},
|
||||||
|
{Name: "26bit", Feature: &ARM.Has26BIT},
|
||||||
|
{Name: "fastmul", Feature: &ARM.HasFASTMUL},
|
||||||
|
{Name: "fpa", Feature: &ARM.HasFPA},
|
||||||
|
{Name: "edsp", Feature: &ARM.HasEDSP},
|
||||||
|
{Name: "java", Feature: &ARM.HasJAVA},
|
||||||
|
{Name: "iwmmxt", Feature: &ARM.HasIWMMXT},
|
||||||
|
{Name: "crunch", Feature: &ARM.HasCRUNCH},
|
||||||
|
{Name: "neon", Feature: &ARM.HasNEON},
|
||||||
|
{Name: "idivt", Feature: &ARM.HasIDIVT},
|
||||||
|
{Name: "idiva", Feature: &ARM.HasIDIVA},
|
||||||
|
{Name: "lpae", Feature: &ARM.HasLPAE},
|
||||||
|
{Name: "evtstrm", Feature: &ARM.HasEVTSTRM},
|
||||||
|
{Name: "aes", Feature: &ARM.HasAES},
|
||||||
|
{Name: "crc32", Feature: &ARM.HasCRC32},
|
||||||
|
}
|
||||||
|
}
|
194
internal/deps/cpu/cpu_arm64.go
Normal file
194
internal/deps/cpu/cpu_arm64.go
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
// Copyright 2019 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 cpu
|
||||||
|
|
||||||
|
import "runtime"
|
||||||
|
|
||||||
|
// cacheLineSize is used to prevent false sharing of cache lines.
|
||||||
|
// We choose 128 because Apple Silicon, a.k.a. M1, has 128-byte cache line size.
|
||||||
|
// It doesn't cost much and is much more future-proof.
|
||||||
|
const cacheLineSize = 128
|
||||||
|
|
||||||
|
func initOptions() {
|
||||||
|
options = []option{
|
||||||
|
{Name: "fp", Feature: &ARM64.HasFP},
|
||||||
|
{Name: "asimd", Feature: &ARM64.HasASIMD},
|
||||||
|
{Name: "evstrm", Feature: &ARM64.HasEVTSTRM},
|
||||||
|
{Name: "aes", Feature: &ARM64.HasAES},
|
||||||
|
{Name: "fphp", Feature: &ARM64.HasFPHP},
|
||||||
|
{Name: "jscvt", Feature: &ARM64.HasJSCVT},
|
||||||
|
{Name: "lrcpc", Feature: &ARM64.HasLRCPC},
|
||||||
|
{Name: "pmull", Feature: &ARM64.HasPMULL},
|
||||||
|
{Name: "sha1", Feature: &ARM64.HasSHA1},
|
||||||
|
{Name: "sha2", Feature: &ARM64.HasSHA2},
|
||||||
|
{Name: "sha3", Feature: &ARM64.HasSHA3},
|
||||||
|
{Name: "sha512", Feature: &ARM64.HasSHA512},
|
||||||
|
{Name: "sm3", Feature: &ARM64.HasSM3},
|
||||||
|
{Name: "sm4", Feature: &ARM64.HasSM4},
|
||||||
|
{Name: "sve", Feature: &ARM64.HasSVE},
|
||||||
|
{Name: "sve2", Feature: &ARM64.HasSVE2},
|
||||||
|
{Name: "crc32", Feature: &ARM64.HasCRC32},
|
||||||
|
{Name: "atomics", Feature: &ARM64.HasATOMICS},
|
||||||
|
{Name: "asimdhp", Feature: &ARM64.HasASIMDHP},
|
||||||
|
{Name: "cpuid", Feature: &ARM64.HasCPUID},
|
||||||
|
{Name: "asimrdm", Feature: &ARM64.HasASIMDRDM},
|
||||||
|
{Name: "fcma", Feature: &ARM64.HasFCMA},
|
||||||
|
{Name: "dcpop", Feature: &ARM64.HasDCPOP},
|
||||||
|
{Name: "asimddp", Feature: &ARM64.HasASIMDDP},
|
||||||
|
{Name: "asimdfhm", Feature: &ARM64.HasASIMDFHM},
|
||||||
|
{Name: "dit", Feature: &ARM64.HasDIT},
|
||||||
|
{Name: "i8mm", Feature: &ARM64.HasI8MM},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func archInit() {
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "freebsd":
|
||||||
|
readARM64Registers()
|
||||||
|
case "linux", "netbsd", "openbsd":
|
||||||
|
doinit()
|
||||||
|
default:
|
||||||
|
// Many platforms don't seem to allow reading these registers.
|
||||||
|
setMinimalFeatures()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setMinimalFeatures fakes the minimal ARM64 features expected by
|
||||||
|
// TestARM64minimalFeatures.
|
||||||
|
func setMinimalFeatures() {
|
||||||
|
ARM64.HasASIMD = true
|
||||||
|
ARM64.HasFP = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func readARM64Registers() {
|
||||||
|
Initialized = true
|
||||||
|
|
||||||
|
parseARM64SystemRegisters(getisar0(), getisar1(), getpfr0())
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseARM64SystemRegisters(isar0, isar1, pfr0 uint64) {
|
||||||
|
// ID_AA64ISAR0_EL1
|
||||||
|
switch extractBits(isar0, 4, 7) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasAES = true
|
||||||
|
case 2:
|
||||||
|
ARM64.HasAES = true
|
||||||
|
ARM64.HasPMULL = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(isar0, 8, 11) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasSHA1 = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(isar0, 12, 15) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasSHA2 = true
|
||||||
|
case 2:
|
||||||
|
ARM64.HasSHA2 = true
|
||||||
|
ARM64.HasSHA512 = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(isar0, 16, 19) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasCRC32 = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(isar0, 20, 23) {
|
||||||
|
case 2:
|
||||||
|
ARM64.HasATOMICS = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(isar0, 28, 31) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasASIMDRDM = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(isar0, 32, 35) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasSHA3 = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(isar0, 36, 39) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasSM3 = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(isar0, 40, 43) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasSM4 = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(isar0, 44, 47) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasASIMDDP = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID_AA64ISAR1_EL1
|
||||||
|
switch extractBits(isar1, 0, 3) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasDCPOP = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(isar1, 12, 15) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasJSCVT = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(isar1, 16, 19) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasFCMA = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(isar1, 20, 23) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasLRCPC = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(isar1, 52, 55) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasI8MM = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID_AA64PFR0_EL1
|
||||||
|
switch extractBits(pfr0, 16, 19) {
|
||||||
|
case 0:
|
||||||
|
ARM64.HasFP = true
|
||||||
|
case 1:
|
||||||
|
ARM64.HasFP = true
|
||||||
|
ARM64.HasFPHP = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(pfr0, 20, 23) {
|
||||||
|
case 0:
|
||||||
|
ARM64.HasASIMD = true
|
||||||
|
case 1:
|
||||||
|
ARM64.HasASIMD = true
|
||||||
|
ARM64.HasASIMDHP = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(pfr0, 32, 35) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasSVE = true
|
||||||
|
|
||||||
|
parseARM64SVERegister(getzfr0())
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(pfr0, 48, 51) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasDIT = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseARM64SVERegister(zfr0 uint64) {
|
||||||
|
switch extractBits(zfr0, 0, 3) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasSVE2 = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractBits(data uint64, start, end uint) uint {
|
||||||
|
return (uint)(data>>start) & ((1 << (end - start + 1)) - 1)
|
||||||
|
}
|
39
internal/deps/cpu/cpu_arm64.s
Normal file
39
internal/deps/cpu/cpu_arm64.s
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Copyright 2019 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 gc
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// func getisar0() uint64
|
||||||
|
TEXT ·getisar0(SB),NOSPLIT,$0-8
|
||||||
|
// get Instruction Set Attributes 0 into x0
|
||||||
|
// mrs x0, ID_AA64ISAR0_EL1 = d5380600
|
||||||
|
WORD $0xd5380600
|
||||||
|
MOVD R0, ret+0(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func getisar1() uint64
|
||||||
|
TEXT ·getisar1(SB),NOSPLIT,$0-8
|
||||||
|
// get Instruction Set Attributes 1 into x0
|
||||||
|
// mrs x0, ID_AA64ISAR1_EL1 = d5380620
|
||||||
|
WORD $0xd5380620
|
||||||
|
MOVD R0, ret+0(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func getpfr0() uint64
|
||||||
|
TEXT ·getpfr0(SB),NOSPLIT,$0-8
|
||||||
|
// get Processor Feature Register 0 into x0
|
||||||
|
// mrs x0, ID_AA64PFR0_EL1 = d5380400
|
||||||
|
WORD $0xd5380400
|
||||||
|
MOVD R0, ret+0(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func getzfr0() uint64
|
||||||
|
TEXT ·getzfr0(SB),NOSPLIT,$0-8
|
||||||
|
// get SVE Feature Register 0 into x0
|
||||||
|
// mrs x0, ID_AA64ZFR0_EL1 = d5380480
|
||||||
|
WORD $0xd5380480
|
||||||
|
MOVD R0, ret+0(FP)
|
||||||
|
RET
|
61
internal/deps/cpu/cpu_darwin_x86.go
Normal file
61
internal/deps/cpu/cpu_darwin_x86.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
//go:build darwin && amd64 && gc
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
// darwinSupportsAVX512 checks Darwin kernel for AVX512 support via sysctl
|
||||||
|
// call (see issue 43089). It also restricts AVX512 support for Darwin to
|
||||||
|
// kernel version 21.3.0 (MacOS 12.2.0) or later (see issue 49233).
|
||||||
|
//
|
||||||
|
// Background:
|
||||||
|
// Darwin implements a special mechanism to economize on thread state when
|
||||||
|
// AVX512 specific registers are not in use. This scheme minimizes state when
|
||||||
|
// preempting threads that haven't yet used any AVX512 instructions, but adds
|
||||||
|
// special requirements to check for AVX512 hardware support at runtime (e.g.
|
||||||
|
// via sysctl call or commpage inspection). See issue 43089 and link below for
|
||||||
|
// full background:
|
||||||
|
// https://github.com/apple-oss-distributions/xnu/blob/xnu-11215.1.10/osfmk/i386/fpu.c#L214-L240
|
||||||
|
//
|
||||||
|
// Additionally, all versions of the Darwin kernel from 19.6.0 through 21.2.0
|
||||||
|
// (corresponding to MacOS 10.15.6 - 12.1) have a bug that can cause corruption
|
||||||
|
// of the AVX512 mask registers (K0-K7) upon signal return. For this reason
|
||||||
|
// AVX512 is considered unsafe to use on Darwin for kernel versions prior to
|
||||||
|
// 21.3.0, where a fix has been confirmed. See issue 49233 for full background.
|
||||||
|
func darwinSupportsAVX512() bool {
|
||||||
|
return darwinSysctlEnabled([]byte("hw.optional.avx512f\x00")) && darwinKernelVersionCheck(21, 3, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure Darwin kernel version is at least major.minor.patch, avoiding dependencies
|
||||||
|
func darwinKernelVersionCheck(major, minor, patch int) bool {
|
||||||
|
var release [256]byte
|
||||||
|
err := darwinOSRelease(&release)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var mmp [3]int
|
||||||
|
c := 0
|
||||||
|
Loop:
|
||||||
|
for _, b := range release[:] {
|
||||||
|
switch {
|
||||||
|
case b >= '0' && b <= '9':
|
||||||
|
mmp[c] = 10*mmp[c] + int(b-'0')
|
||||||
|
case b == '.':
|
||||||
|
c++
|
||||||
|
if c > 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case b == 0:
|
||||||
|
break Loop
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if c != 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return mmp[0] > major || mmp[0] == major && (mmp[1] > minor || mmp[1] == minor && mmp[2] >= patch)
|
||||||
|
}
|
12
internal/deps/cpu/cpu_gc_arm64.go
Normal file
12
internal/deps/cpu/cpu_gc_arm64.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// Copyright 2019 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 gc
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
func getisar0() uint64
|
||||||
|
func getisar1() uint64
|
||||||
|
func getpfr0() uint64
|
||||||
|
func getzfr0() uint64
|
21
internal/deps/cpu/cpu_gc_s390x.go
Normal file
21
internal/deps/cpu/cpu_gc_s390x.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Copyright 2019 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 gc
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
// haveAsmFunctions reports whether the other functions in this file can
|
||||||
|
// be safely called.
|
||||||
|
func haveAsmFunctions() bool { return true }
|
||||||
|
|
||||||
|
// The following feature detection functions are defined in cpu_s390x.s.
|
||||||
|
// They are likely to be expensive to call so the results should be cached.
|
||||||
|
func stfle() facilityList
|
||||||
|
func kmQuery() queryResult
|
||||||
|
func kmcQuery() queryResult
|
||||||
|
func kmctrQuery() queryResult
|
||||||
|
func kmaQuery() queryResult
|
||||||
|
func kimdQuery() queryResult
|
||||||
|
func klmdQuery() queryResult
|
15
internal/deps/cpu/cpu_gc_x86.go
Normal file
15
internal/deps/cpu/cpu_gc_x86.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// Copyright 2018 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 (386 || amd64 || amd64p32) && gc
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
// cpuid is implemented in cpu_gc_x86.s for gc compiler
|
||||||
|
// and in cpu_gccgo.c for gccgo.
|
||||||
|
func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32)
|
||||||
|
|
||||||
|
// xgetbv with ecx = 0 is implemented in cpu_gc_x86.s for gc compiler
|
||||||
|
// and in cpu_gccgo.c for gccgo.
|
||||||
|
func xgetbv() (eax, edx uint32)
|
26
internal/deps/cpu/cpu_gc_x86.s
Normal file
26
internal/deps/cpu/cpu_gc_x86.s
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Copyright 2018 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 (386 || amd64 || amd64p32) && gc
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32)
|
||||||
|
TEXT ·cpuid(SB), NOSPLIT, $0-24
|
||||||
|
MOVL eaxArg+0(FP), AX
|
||||||
|
MOVL ecxArg+4(FP), CX
|
||||||
|
CPUID
|
||||||
|
MOVL AX, eax+8(FP)
|
||||||
|
MOVL BX, ebx+12(FP)
|
||||||
|
MOVL CX, ecx+16(FP)
|
||||||
|
MOVL DX, edx+20(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func xgetbv() (eax, edx uint32)
|
||||||
|
TEXT ·xgetbv(SB), NOSPLIT, $0-8
|
||||||
|
MOVL $0, CX
|
||||||
|
XGETBV
|
||||||
|
MOVL AX, eax+0(FP)
|
||||||
|
MOVL DX, edx+4(FP)
|
||||||
|
RET
|
11
internal/deps/cpu/cpu_gccgo_arm64.go
Normal file
11
internal/deps/cpu/cpu_gccgo_arm64.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// Copyright 2019 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 gccgo
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
func getisar0() uint64 { return 0 }
|
||||||
|
func getisar1() uint64 { return 0 }
|
||||||
|
func getpfr0() uint64 { return 0 }
|
22
internal/deps/cpu/cpu_gccgo_s390x.go
Normal file
22
internal/deps/cpu/cpu_gccgo_s390x.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Copyright 2019 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 gccgo
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
// haveAsmFunctions reports whether the other functions in this file can
|
||||||
|
// be safely called.
|
||||||
|
func haveAsmFunctions() bool { return false }
|
||||||
|
|
||||||
|
// TODO(mundaym): the following feature detection functions are currently
|
||||||
|
// stubs. See https://golang.org/cl/162887 for how to fix this.
|
||||||
|
// They are likely to be expensive to call so the results should be cached.
|
||||||
|
func stfle() facilityList { panic("not implemented for gccgo") }
|
||||||
|
func kmQuery() queryResult { panic("not implemented for gccgo") }
|
||||||
|
func kmcQuery() queryResult { panic("not implemented for gccgo") }
|
||||||
|
func kmctrQuery() queryResult { panic("not implemented for gccgo") }
|
||||||
|
func kmaQuery() queryResult { panic("not implemented for gccgo") }
|
||||||
|
func kimdQuery() queryResult { panic("not implemented for gccgo") }
|
||||||
|
func klmdQuery() queryResult { panic("not implemented for gccgo") }
|
37
internal/deps/cpu/cpu_gccgo_x86.c
Normal file
37
internal/deps/cpu/cpu_gccgo_x86.c
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// Copyright 2018 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 (386 || amd64 || amd64p32) && gccgo
|
||||||
|
|
||||||
|
#include <cpuid.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <x86intrin.h>
|
||||||
|
|
||||||
|
// Need to wrap __get_cpuid_count because it's declared as static.
|
||||||
|
int
|
||||||
|
gccgoGetCpuidCount(uint32_t leaf, uint32_t subleaf,
|
||||||
|
uint32_t *eax, uint32_t *ebx,
|
||||||
|
uint32_t *ecx, uint32_t *edx)
|
||||||
|
{
|
||||||
|
return __get_cpuid_count(leaf, subleaf, eax, ebx, ecx, edx);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
|
||||||
|
#pragma GCC push_options
|
||||||
|
#pragma GCC target("xsave")
|
||||||
|
#pragma clang attribute push (__attribute__((target("xsave"))), apply_to=function)
|
||||||
|
|
||||||
|
// xgetbv reads the contents of an XCR (Extended Control Register)
|
||||||
|
// specified in the ECX register into registers EDX:EAX.
|
||||||
|
// Currently, the only supported value for XCR is 0.
|
||||||
|
void
|
||||||
|
gccgoXgetbv(uint32_t *eax, uint32_t *edx)
|
||||||
|
{
|
||||||
|
uint64_t v = _xgetbv(0);
|
||||||
|
*eax = v & 0xffffffff;
|
||||||
|
*edx = v >> 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma clang attribute pop
|
||||||
|
#pragma GCC pop_options
|
25
internal/deps/cpu/cpu_gccgo_x86.go
Normal file
25
internal/deps/cpu/cpu_gccgo_x86.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2018 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 (386 || amd64 || amd64p32) && gccgo
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
//extern gccgoGetCpuidCount
|
||||||
|
func gccgoGetCpuidCount(eaxArg, ecxArg uint32, eax, ebx, ecx, edx *uint32)
|
||||||
|
|
||||||
|
func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) {
|
||||||
|
var a, b, c, d uint32
|
||||||
|
gccgoGetCpuidCount(eaxArg, ecxArg, &a, &b, &c, &d)
|
||||||
|
return a, b, c, d
|
||||||
|
}
|
||||||
|
|
||||||
|
//extern gccgoXgetbv
|
||||||
|
func gccgoXgetbv(eax, edx *uint32)
|
||||||
|
|
||||||
|
func xgetbv() (eax, edx uint32) {
|
||||||
|
var a, d uint32
|
||||||
|
gccgoXgetbv(&a, &d)
|
||||||
|
return a, d
|
||||||
|
}
|
@ -2,9 +2,14 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build (ppc64 || ppc64le) && !purego
|
//go:build !386 && !amd64 && !amd64p32 && !arm64
|
||||||
|
|
||||||
package subtle
|
package cpu
|
||||||
|
|
||||||
//go:noescape
|
func archInit() {
|
||||||
func xorBytes(dst, a, b *byte, n int)
|
if err := readHWCAP(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
doinit()
|
||||||
|
Initialized = true
|
||||||
|
}
|
39
internal/deps/cpu/cpu_linux_arm.go
Normal file
39
internal/deps/cpu/cpu_linux_arm.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Copyright 2019 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 cpu
|
||||||
|
|
||||||
|
func doinit() {
|
||||||
|
ARM.HasSWP = isSet(hwCap, hwcap_SWP)
|
||||||
|
ARM.HasHALF = isSet(hwCap, hwcap_HALF)
|
||||||
|
ARM.HasTHUMB = isSet(hwCap, hwcap_THUMB)
|
||||||
|
ARM.Has26BIT = isSet(hwCap, hwcap_26BIT)
|
||||||
|
ARM.HasFASTMUL = isSet(hwCap, hwcap_FAST_MULT)
|
||||||
|
ARM.HasFPA = isSet(hwCap, hwcap_FPA)
|
||||||
|
ARM.HasVFP = isSet(hwCap, hwcap_VFP)
|
||||||
|
ARM.HasEDSP = isSet(hwCap, hwcap_EDSP)
|
||||||
|
ARM.HasJAVA = isSet(hwCap, hwcap_JAVA)
|
||||||
|
ARM.HasIWMMXT = isSet(hwCap, hwcap_IWMMXT)
|
||||||
|
ARM.HasCRUNCH = isSet(hwCap, hwcap_CRUNCH)
|
||||||
|
ARM.HasTHUMBEE = isSet(hwCap, hwcap_THUMBEE)
|
||||||
|
ARM.HasNEON = isSet(hwCap, hwcap_NEON)
|
||||||
|
ARM.HasVFPv3 = isSet(hwCap, hwcap_VFPv3)
|
||||||
|
ARM.HasVFPv3D16 = isSet(hwCap, hwcap_VFPv3D16)
|
||||||
|
ARM.HasTLS = isSet(hwCap, hwcap_TLS)
|
||||||
|
ARM.HasVFPv4 = isSet(hwCap, hwcap_VFPv4)
|
||||||
|
ARM.HasIDIVA = isSet(hwCap, hwcap_IDIVA)
|
||||||
|
ARM.HasIDIVT = isSet(hwCap, hwcap_IDIVT)
|
||||||
|
ARM.HasVFPD32 = isSet(hwCap, hwcap_VFPD32)
|
||||||
|
ARM.HasLPAE = isSet(hwCap, hwcap_LPAE)
|
||||||
|
ARM.HasEVTSTRM = isSet(hwCap, hwcap_EVTSTRM)
|
||||||
|
ARM.HasAES = isSet(hwCap2, hwcap2_AES)
|
||||||
|
ARM.HasPMULL = isSet(hwCap2, hwcap2_PMULL)
|
||||||
|
ARM.HasSHA1 = isSet(hwCap2, hwcap2_SHA1)
|
||||||
|
ARM.HasSHA2 = isSet(hwCap2, hwcap2_SHA2)
|
||||||
|
ARM.HasCRC32 = isSet(hwCap2, hwcap2_CRC32)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSet(hwc uint, value uint) bool {
|
||||||
|
return hwc&value != 0
|
||||||
|
}
|
120
internal/deps/cpu/cpu_linux_arm64.go
Normal file
120
internal/deps/cpu/cpu_linux_arm64.go
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
// Copyright 2018 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 cpu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HWCAP/HWCAP2 bits. These are exposed by Linux.
|
||||||
|
const (
|
||||||
|
hwcap_FP = 1 << 0
|
||||||
|
hwcap_ASIMD = 1 << 1
|
||||||
|
hwcap_EVTSTRM = 1 << 2
|
||||||
|
hwcap_AES = 1 << 3
|
||||||
|
hwcap_PMULL = 1 << 4
|
||||||
|
hwcap_SHA1 = 1 << 5
|
||||||
|
hwcap_SHA2 = 1 << 6
|
||||||
|
hwcap_CRC32 = 1 << 7
|
||||||
|
hwcap_ATOMICS = 1 << 8
|
||||||
|
hwcap_FPHP = 1 << 9
|
||||||
|
hwcap_ASIMDHP = 1 << 10
|
||||||
|
hwcap_CPUID = 1 << 11
|
||||||
|
hwcap_ASIMDRDM = 1 << 12
|
||||||
|
hwcap_JSCVT = 1 << 13
|
||||||
|
hwcap_FCMA = 1 << 14
|
||||||
|
hwcap_LRCPC = 1 << 15
|
||||||
|
hwcap_DCPOP = 1 << 16
|
||||||
|
hwcap_SHA3 = 1 << 17
|
||||||
|
hwcap_SM3 = 1 << 18
|
||||||
|
hwcap_SM4 = 1 << 19
|
||||||
|
hwcap_ASIMDDP = 1 << 20
|
||||||
|
hwcap_SHA512 = 1 << 21
|
||||||
|
hwcap_SVE = 1 << 22
|
||||||
|
hwcap_ASIMDFHM = 1 << 23
|
||||||
|
hwcap_DIT = 1 << 24
|
||||||
|
|
||||||
|
hwcap2_SVE2 = 1 << 1
|
||||||
|
hwcap2_I8MM = 1 << 13
|
||||||
|
)
|
||||||
|
|
||||||
|
// linuxKernelCanEmulateCPUID reports whether we're running
|
||||||
|
// on Linux 4.11+. Ideally we'd like to ask the question about
|
||||||
|
// whether the current kernel contains
|
||||||
|
// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=77c97b4ee21290f5f083173d957843b615abbff2
|
||||||
|
// but the version number will have to do.
|
||||||
|
func linuxKernelCanEmulateCPUID() bool {
|
||||||
|
var un syscall.Utsname
|
||||||
|
syscall.Uname(&un)
|
||||||
|
var sb strings.Builder
|
||||||
|
for _, b := range un.Release[:] {
|
||||||
|
if b == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
sb.WriteByte(byte(b))
|
||||||
|
}
|
||||||
|
major, minor, _, ok := parseRelease(sb.String())
|
||||||
|
return ok && (major > 4 || major == 4 && minor >= 11)
|
||||||
|
}
|
||||||
|
|
||||||
|
func doinit() {
|
||||||
|
if err := readHWCAP(); err != nil {
|
||||||
|
// We failed to read /proc/self/auxv. This can happen if the binary has
|
||||||
|
// been given extra capabilities(7) with /bin/setcap.
|
||||||
|
//
|
||||||
|
// When this happens, we have two options. If the Linux kernel is new
|
||||||
|
// enough (4.11+), we can read the arm64 registers directly which'll
|
||||||
|
// trap into the kernel and then return back to userspace.
|
||||||
|
//
|
||||||
|
// But on older kernels, such as Linux 4.4.180 as used on many Synology
|
||||||
|
// devices, calling readARM64Registers (specifically getisar0) will
|
||||||
|
// cause a SIGILL and we'll die. So for older kernels, parse /proc/cpuinfo
|
||||||
|
// instead.
|
||||||
|
//
|
||||||
|
// See golang/go#57336.
|
||||||
|
if linuxKernelCanEmulateCPUID() {
|
||||||
|
readARM64Registers()
|
||||||
|
} else {
|
||||||
|
readLinuxProcCPUInfo()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// HWCAP feature bits
|
||||||
|
ARM64.HasFP = isSet(hwCap, hwcap_FP)
|
||||||
|
ARM64.HasASIMD = isSet(hwCap, hwcap_ASIMD)
|
||||||
|
ARM64.HasEVTSTRM = isSet(hwCap, hwcap_EVTSTRM)
|
||||||
|
ARM64.HasAES = isSet(hwCap, hwcap_AES)
|
||||||
|
ARM64.HasPMULL = isSet(hwCap, hwcap_PMULL)
|
||||||
|
ARM64.HasSHA1 = isSet(hwCap, hwcap_SHA1)
|
||||||
|
ARM64.HasSHA2 = isSet(hwCap, hwcap_SHA2)
|
||||||
|
ARM64.HasCRC32 = isSet(hwCap, hwcap_CRC32)
|
||||||
|
ARM64.HasATOMICS = isSet(hwCap, hwcap_ATOMICS)
|
||||||
|
ARM64.HasFPHP = isSet(hwCap, hwcap_FPHP)
|
||||||
|
ARM64.HasASIMDHP = isSet(hwCap, hwcap_ASIMDHP)
|
||||||
|
ARM64.HasCPUID = isSet(hwCap, hwcap_CPUID)
|
||||||
|
ARM64.HasASIMDRDM = isSet(hwCap, hwcap_ASIMDRDM)
|
||||||
|
ARM64.HasJSCVT = isSet(hwCap, hwcap_JSCVT)
|
||||||
|
ARM64.HasFCMA = isSet(hwCap, hwcap_FCMA)
|
||||||
|
ARM64.HasLRCPC = isSet(hwCap, hwcap_LRCPC)
|
||||||
|
ARM64.HasDCPOP = isSet(hwCap, hwcap_DCPOP)
|
||||||
|
ARM64.HasSHA3 = isSet(hwCap, hwcap_SHA3)
|
||||||
|
ARM64.HasSM3 = isSet(hwCap, hwcap_SM3)
|
||||||
|
ARM64.HasSM4 = isSet(hwCap, hwcap_SM4)
|
||||||
|
ARM64.HasASIMDDP = isSet(hwCap, hwcap_ASIMDDP)
|
||||||
|
ARM64.HasSHA512 = isSet(hwCap, hwcap_SHA512)
|
||||||
|
ARM64.HasSVE = isSet(hwCap, hwcap_SVE)
|
||||||
|
ARM64.HasASIMDFHM = isSet(hwCap, hwcap_ASIMDFHM)
|
||||||
|
ARM64.HasDIT = isSet(hwCap, hwcap_DIT)
|
||||||
|
|
||||||
|
// HWCAP2 feature bits
|
||||||
|
ARM64.HasSVE2 = isSet(hwCap2, hwcap2_SVE2)
|
||||||
|
ARM64.HasI8MM = isSet(hwCap2, hwcap2_I8MM)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSet(hwc uint, value uint) bool {
|
||||||
|
return hwc&value != 0
|
||||||
|
}
|
22
internal/deps/cpu/cpu_linux_mips64x.go
Normal file
22
internal/deps/cpu/cpu_linux_mips64x.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Copyright 2020 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 linux && (mips64 || mips64le)
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
// HWCAP bits. These are exposed by the Linux kernel 5.4.
|
||||||
|
const (
|
||||||
|
// CPU features
|
||||||
|
hwcap_MIPS_MSA = 1 << 1
|
||||||
|
)
|
||||||
|
|
||||||
|
func doinit() {
|
||||||
|
// HWCAP feature bits
|
||||||
|
MIPS64X.HasMSA = isSet(hwCap, hwcap_MIPS_MSA)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSet(hwc uint, value uint) bool {
|
||||||
|
return hwc&value != 0
|
||||||
|
}
|
9
internal/deps/cpu/cpu_linux_noinit.go
Normal file
9
internal/deps/cpu/cpu_linux_noinit.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// Copyright 2019 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 linux && !arm && !arm64 && !mips64 && !mips64le && !ppc64 && !ppc64le && !s390x && !riscv64
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
func doinit() {}
|
30
internal/deps/cpu/cpu_linux_ppc64x.go
Normal file
30
internal/deps/cpu/cpu_linux_ppc64x.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Copyright 2018 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 linux && (ppc64 || ppc64le)
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
// HWCAP/HWCAP2 bits. These are exposed by the kernel.
|
||||||
|
const (
|
||||||
|
// ISA Level
|
||||||
|
_PPC_FEATURE2_ARCH_2_07 = 0x80000000
|
||||||
|
_PPC_FEATURE2_ARCH_3_00 = 0x00800000
|
||||||
|
|
||||||
|
// CPU features
|
||||||
|
_PPC_FEATURE2_DARN = 0x00200000
|
||||||
|
_PPC_FEATURE2_SCV = 0x00100000
|
||||||
|
)
|
||||||
|
|
||||||
|
func doinit() {
|
||||||
|
// HWCAP2 feature bits
|
||||||
|
PPC64.IsPOWER8 = isSet(hwCap2, _PPC_FEATURE2_ARCH_2_07)
|
||||||
|
PPC64.IsPOWER9 = isSet(hwCap2, _PPC_FEATURE2_ARCH_3_00)
|
||||||
|
PPC64.HasDARN = isSet(hwCap2, _PPC_FEATURE2_DARN)
|
||||||
|
PPC64.HasSCV = isSet(hwCap2, _PPC_FEATURE2_SCV)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSet(hwc uint, value uint) bool {
|
||||||
|
return hwc&value != 0
|
||||||
|
}
|
137
internal/deps/cpu/cpu_linux_riscv64.go
Normal file
137
internal/deps/cpu/cpu_linux_riscv64.go
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
// 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 cpu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RISC-V extension discovery code for Linux. The approach here is to first try the riscv_hwprobe
|
||||||
|
// syscall falling back to HWCAP to check for the C extension if riscv_hwprobe is not available.
|
||||||
|
//
|
||||||
|
// A note on detection of the Vector extension using HWCAP.
|
||||||
|
//
|
||||||
|
// Support for the Vector extension version 1.0 was added to the Linux kernel in release 6.5.
|
||||||
|
// Support for the riscv_hwprobe syscall was added in 6.4. It follows that if the riscv_hwprobe
|
||||||
|
// syscall is not available then neither is the Vector extension (which needs kernel support).
|
||||||
|
// The riscv_hwprobe syscall should then be all we need to detect the Vector extension.
|
||||||
|
// However, some RISC-V board manufacturers ship boards with an older kernel on top of which
|
||||||
|
// they have back-ported various versions of the Vector extension patches but not the riscv_hwprobe
|
||||||
|
// patches. These kernels advertise support for the Vector extension using HWCAP. Falling
|
||||||
|
// back to HWCAP to detect the Vector extension, if riscv_hwprobe is not available, or simply not
|
||||||
|
// bothering with riscv_hwprobe at all and just using HWCAP may then seem like an attractive option.
|
||||||
|
//
|
||||||
|
// Unfortunately, simply checking the 'V' bit in AT_HWCAP will not work as this bit is used by
|
||||||
|
// RISC-V board and cloud instance providers to mean different things. The Lichee Pi 4A board
|
||||||
|
// and the Scaleway RV1 cloud instances use the 'V' bit to advertise their support for the unratified
|
||||||
|
// 0.7.1 version of the Vector Specification. The Banana Pi BPI-F3 and the CanMV-K230 board use
|
||||||
|
// it to advertise support for 1.0 of the Vector extension. Versions 0.7.1 and 1.0 of the Vector
|
||||||
|
// extension are binary incompatible. HWCAP can then not be used in isolation to populate the
|
||||||
|
// HasV field as this field indicates that the underlying CPU is compatible with RVV 1.0.
|
||||||
|
//
|
||||||
|
// There is a way at runtime to distinguish between versions 0.7.1 and 1.0 of the Vector
|
||||||
|
// specification by issuing a RVV 1.0 vsetvli instruction and checking the vill bit of the vtype
|
||||||
|
// register. This check would allow us to safely detect version 1.0 of the Vector extension
|
||||||
|
// with HWCAP, if riscv_hwprobe were not available. However, the check cannot
|
||||||
|
// be added until the assembler supports the Vector instructions.
|
||||||
|
//
|
||||||
|
// Note the riscv_hwprobe syscall does not suffer from these ambiguities by design as all of the
|
||||||
|
// extensions it advertises support for are explicitly versioned. It's also worth noting that
|
||||||
|
// the riscv_hwprobe syscall is the only way to detect multi-letter RISC-V extensions, e.g., Zba.
|
||||||
|
// These cannot be detected using HWCAP and so riscv_hwprobe must be used to detect the majority
|
||||||
|
// of RISC-V extensions.
|
||||||
|
//
|
||||||
|
// Please see https://docs.kernel.org/arch/riscv/hwprobe.html for more information.
|
||||||
|
|
||||||
|
// golang.org/x/sys/cpu is not allowed to depend on golang.org/x/sys/unix so we must
|
||||||
|
// reproduce the constants, types and functions needed to make the riscv_hwprobe syscall
|
||||||
|
// here.
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Copied from golang.org/x/sys/unix/ztypes_linux_riscv64.go.
|
||||||
|
riscv_HWPROBE_KEY_IMA_EXT_0 = 0x4
|
||||||
|
riscv_HWPROBE_IMA_C = 0x2
|
||||||
|
riscv_HWPROBE_IMA_V = 0x4
|
||||||
|
riscv_HWPROBE_EXT_ZBA = 0x8
|
||||||
|
riscv_HWPROBE_EXT_ZBB = 0x10
|
||||||
|
riscv_HWPROBE_EXT_ZBS = 0x20
|
||||||
|
riscv_HWPROBE_KEY_CPUPERF_0 = 0x5
|
||||||
|
riscv_HWPROBE_MISALIGNED_FAST = 0x3
|
||||||
|
riscv_HWPROBE_MISALIGNED_MASK = 0x7
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// sys_RISCV_HWPROBE is copied from golang.org/x/sys/unix/zsysnum_linux_riscv64.go.
|
||||||
|
sys_RISCV_HWPROBE = 258
|
||||||
|
)
|
||||||
|
|
||||||
|
// riscvHWProbePairs is copied from golang.org/x/sys/unix/ztypes_linux_riscv64.go.
|
||||||
|
type riscvHWProbePairs struct {
|
||||||
|
key int64
|
||||||
|
value uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// CPU features
|
||||||
|
hwcap_RISCV_ISA_C = 1 << ('C' - 'A')
|
||||||
|
)
|
||||||
|
|
||||||
|
func doinit() {
|
||||||
|
// A slice of key/value pair structures is passed to the RISCVHWProbe syscall. The key
|
||||||
|
// field should be initialised with one of the key constants defined above, e.g.,
|
||||||
|
// RISCV_HWPROBE_KEY_IMA_EXT_0. The syscall will set the value field to the appropriate value.
|
||||||
|
// If the kernel does not recognise a key it will set the key field to -1 and the value field to 0.
|
||||||
|
|
||||||
|
pairs := []riscvHWProbePairs{
|
||||||
|
{riscv_HWPROBE_KEY_IMA_EXT_0, 0},
|
||||||
|
{riscv_HWPROBE_KEY_CPUPERF_0, 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
// This call only indicates that extensions are supported if they are implemented on all cores.
|
||||||
|
if riscvHWProbe(pairs, 0) {
|
||||||
|
if pairs[0].key != -1 {
|
||||||
|
v := uint(pairs[0].value)
|
||||||
|
RISCV64.HasC = isSet(v, riscv_HWPROBE_IMA_C)
|
||||||
|
RISCV64.HasV = isSet(v, riscv_HWPROBE_IMA_V)
|
||||||
|
RISCV64.HasZba = isSet(v, riscv_HWPROBE_EXT_ZBA)
|
||||||
|
RISCV64.HasZbb = isSet(v, riscv_HWPROBE_EXT_ZBB)
|
||||||
|
RISCV64.HasZbs = isSet(v, riscv_HWPROBE_EXT_ZBS)
|
||||||
|
}
|
||||||
|
if pairs[1].key != -1 {
|
||||||
|
v := pairs[1].value & riscv_HWPROBE_MISALIGNED_MASK
|
||||||
|
RISCV64.HasFastMisaligned = v == riscv_HWPROBE_MISALIGNED_FAST
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let's double check with HWCAP if the C extension does not appear to be supported.
|
||||||
|
// This may happen if we're running on a kernel older than 6.4.
|
||||||
|
|
||||||
|
if !RISCV64.HasC {
|
||||||
|
RISCV64.HasC = isSet(hwCap, hwcap_RISCV_ISA_C)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSet(hwc uint, value uint) bool {
|
||||||
|
return hwc&value != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// riscvHWProbe is a simplified version of the generated wrapper function found in
|
||||||
|
// golang.org/x/sys/unix/zsyscall_linux_riscv64.go. We simplify it by removing the
|
||||||
|
// cpuCount and cpus parameters which we do not need. We always want to pass 0 for
|
||||||
|
// these parameters here so the kernel only reports the extensions that are present
|
||||||
|
// on all cores.
|
||||||
|
func riscvHWProbe(pairs []riscvHWProbePairs, flags uint) bool {
|
||||||
|
var _zero uintptr
|
||||||
|
var p0 unsafe.Pointer
|
||||||
|
if len(pairs) > 0 {
|
||||||
|
p0 = unsafe.Pointer(&pairs[0])
|
||||||
|
} else {
|
||||||
|
p0 = unsafe.Pointer(&_zero)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, e1 := syscall.Syscall6(sys_RISCV_HWPROBE, uintptr(p0), uintptr(len(pairs)), uintptr(0), uintptr(0), uintptr(flags), 0)
|
||||||
|
return e1 == 0
|
||||||
|
}
|
40
internal/deps/cpu/cpu_linux_s390x.go
Normal file
40
internal/deps/cpu/cpu_linux_s390x.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// Copyright 2019 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 cpu
|
||||||
|
|
||||||
|
const (
|
||||||
|
// bit mask values from /usr/include/bits/hwcap.h
|
||||||
|
hwcap_ZARCH = 2
|
||||||
|
hwcap_STFLE = 4
|
||||||
|
hwcap_MSA = 8
|
||||||
|
hwcap_LDISP = 16
|
||||||
|
hwcap_EIMM = 32
|
||||||
|
hwcap_DFP = 64
|
||||||
|
hwcap_ETF3EH = 256
|
||||||
|
hwcap_VX = 2048
|
||||||
|
hwcap_VXE = 8192
|
||||||
|
)
|
||||||
|
|
||||||
|
func initS390Xbase() {
|
||||||
|
// test HWCAP bit vector
|
||||||
|
has := func(featureMask uint) bool {
|
||||||
|
return hwCap&featureMask == featureMask
|
||||||
|
}
|
||||||
|
|
||||||
|
// mandatory
|
||||||
|
S390X.HasZARCH = has(hwcap_ZARCH)
|
||||||
|
|
||||||
|
// optional
|
||||||
|
S390X.HasSTFLE = has(hwcap_STFLE)
|
||||||
|
S390X.HasLDISP = has(hwcap_LDISP)
|
||||||
|
S390X.HasEIMM = has(hwcap_EIMM)
|
||||||
|
S390X.HasETF3EH = has(hwcap_ETF3EH)
|
||||||
|
S390X.HasDFP = has(hwcap_DFP)
|
||||||
|
S390X.HasMSA = has(hwcap_MSA)
|
||||||
|
S390X.HasVX = has(hwcap_VX)
|
||||||
|
if S390X.HasVX {
|
||||||
|
S390X.HasVXE = has(hwcap_VXE)
|
||||||
|
}
|
||||||
|
}
|
12
internal/deps/cpu/cpu_loong64.go
Normal file
12
internal/deps/cpu/cpu_loong64.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// 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 loong64
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
const cacheLineSize = 64
|
||||||
|
|
||||||
|
func initOptions() {
|
||||||
|
}
|
@ -1,14 +1,15 @@
|
|||||||
// Copyright 2018 The Go Authors. All rights reserved.
|
// Copyright 2018 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
//
|
|
||||||
//go:build !purego
|
|
||||||
|
|
||||||
package subtle
|
//go:build mips64 || mips64le
|
||||||
|
|
||||||
import "golang.org/x/sys/cpu"
|
package cpu
|
||||||
|
|
||||||
var useAVX2 = cpu.X86.HasAVX2
|
const cacheLineSize = 32
|
||||||
|
|
||||||
//go:noescape
|
func initOptions() {
|
||||||
func xorBytes(dst, a, b *byte, n int)
|
options = []option{
|
||||||
|
{Name: "msa", Feature: &MIPS64X.HasMSA},
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,11 @@
|
|||||||
// Copyright 2018 The Go Authors. All rights reserved.
|
// Copyright 2018 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
//
|
|
||||||
//go:build !purego
|
|
||||||
|
|
||||||
package subtle
|
//go:build mips || mipsle
|
||||||
|
|
||||||
//go:noescape
|
package cpu
|
||||||
func xorBytes(dst, a, b *byte, n int)
|
|
||||||
|
const cacheLineSize = 32
|
||||||
|
|
||||||
|
func initOptions() {}
|
173
internal/deps/cpu/cpu_netbsd_arm64.go
Normal file
173
internal/deps/cpu/cpu_netbsd_arm64.go
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
// Copyright 2020 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 cpu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Minimal copy of functionality from x/sys/unix so the cpu package can call
|
||||||
|
// sysctl without depending on x/sys/unix.
|
||||||
|
|
||||||
|
const (
|
||||||
|
_CTL_QUERY = -2
|
||||||
|
|
||||||
|
_SYSCTL_VERS_1 = 0x1000000
|
||||||
|
)
|
||||||
|
|
||||||
|
var _zero uintptr
|
||||||
|
|
||||||
|
func sysctl(mib []int32, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) {
|
||||||
|
var _p0 unsafe.Pointer
|
||||||
|
if len(mib) > 0 {
|
||||||
|
_p0 = unsafe.Pointer(&mib[0])
|
||||||
|
} else {
|
||||||
|
_p0 = unsafe.Pointer(&_zero)
|
||||||
|
}
|
||||||
|
_, _, errno := syscall.Syscall6(
|
||||||
|
syscall.SYS___SYSCTL,
|
||||||
|
uintptr(_p0),
|
||||||
|
uintptr(len(mib)),
|
||||||
|
uintptr(unsafe.Pointer(old)),
|
||||||
|
uintptr(unsafe.Pointer(oldlen)),
|
||||||
|
uintptr(unsafe.Pointer(new)),
|
||||||
|
uintptr(newlen))
|
||||||
|
if errno != 0 {
|
||||||
|
return errno
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type sysctlNode struct {
|
||||||
|
Flags uint32
|
||||||
|
Num int32
|
||||||
|
Name [32]int8
|
||||||
|
Ver uint32
|
||||||
|
__rsvd uint32
|
||||||
|
Un [16]byte
|
||||||
|
_sysctl_size [8]byte
|
||||||
|
_sysctl_func [8]byte
|
||||||
|
_sysctl_parent [8]byte
|
||||||
|
_sysctl_desc [8]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func sysctlNodes(mib []int32) ([]sysctlNode, error) {
|
||||||
|
var olen uintptr
|
||||||
|
|
||||||
|
// Get a list of all sysctl nodes below the given MIB by performing
|
||||||
|
// a sysctl for the given MIB with CTL_QUERY appended.
|
||||||
|
mib = append(mib, _CTL_QUERY)
|
||||||
|
qnode := sysctlNode{Flags: _SYSCTL_VERS_1}
|
||||||
|
qp := (*byte)(unsafe.Pointer(&qnode))
|
||||||
|
sz := unsafe.Sizeof(qnode)
|
||||||
|
if err := sysctl(mib, nil, &olen, qp, sz); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that we know the size, get the actual nodes.
|
||||||
|
nodes := make([]sysctlNode, olen/sz)
|
||||||
|
np := (*byte)(unsafe.Pointer(&nodes[0]))
|
||||||
|
if err := sysctl(mib, np, &olen, qp, sz); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func nametomib(name string) ([]int32, error) {
|
||||||
|
// Split name into components.
|
||||||
|
var parts []string
|
||||||
|
last := 0
|
||||||
|
for i := 0; i < len(name); i++ {
|
||||||
|
if name[i] == '.' {
|
||||||
|
parts = append(parts, name[last:i])
|
||||||
|
last = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parts = append(parts, name[last:])
|
||||||
|
|
||||||
|
mib := []int32{}
|
||||||
|
// Discover the nodes and construct the MIB OID.
|
||||||
|
for partno, part := range parts {
|
||||||
|
nodes, err := sysctlNodes(mib)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, node := range nodes {
|
||||||
|
n := make([]byte, 0)
|
||||||
|
for i := range node.Name {
|
||||||
|
if node.Name[i] != 0 {
|
||||||
|
n = append(n, byte(node.Name[i]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if string(n) == part {
|
||||||
|
mib = append(mib, int32(node.Num))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(mib) != partno+1 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mib, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// aarch64SysctlCPUID is struct aarch64_sysctl_cpu_id from NetBSD's <aarch64/armreg.h>
|
||||||
|
type aarch64SysctlCPUID struct {
|
||||||
|
midr uint64 /* Main ID Register */
|
||||||
|
revidr uint64 /* Revision ID Register */
|
||||||
|
mpidr uint64 /* Multiprocessor Affinity Register */
|
||||||
|
aa64dfr0 uint64 /* A64 Debug Feature Register 0 */
|
||||||
|
aa64dfr1 uint64 /* A64 Debug Feature Register 1 */
|
||||||
|
aa64isar0 uint64 /* A64 Instruction Set Attribute Register 0 */
|
||||||
|
aa64isar1 uint64 /* A64 Instruction Set Attribute Register 1 */
|
||||||
|
aa64mmfr0 uint64 /* A64 Memory Model Feature Register 0 */
|
||||||
|
aa64mmfr1 uint64 /* A64 Memory Model Feature Register 1 */
|
||||||
|
aa64mmfr2 uint64 /* A64 Memory Model Feature Register 2 */
|
||||||
|
aa64pfr0 uint64 /* A64 Processor Feature Register 0 */
|
||||||
|
aa64pfr1 uint64 /* A64 Processor Feature Register 1 */
|
||||||
|
aa64zfr0 uint64 /* A64 SVE Feature ID Register 0 */
|
||||||
|
mvfr0 uint32 /* Media and VFP Feature Register 0 */
|
||||||
|
mvfr1 uint32 /* Media and VFP Feature Register 1 */
|
||||||
|
mvfr2 uint32 /* Media and VFP Feature Register 2 */
|
||||||
|
pad uint32
|
||||||
|
clidr uint64 /* Cache Level ID Register */
|
||||||
|
ctr uint64 /* Cache Type Register */
|
||||||
|
}
|
||||||
|
|
||||||
|
func sysctlCPUID(name string) (*aarch64SysctlCPUID, error) {
|
||||||
|
mib, err := nametomib(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
out := aarch64SysctlCPUID{}
|
||||||
|
n := unsafe.Sizeof(out)
|
||||||
|
_, _, errno := syscall.Syscall6(
|
||||||
|
syscall.SYS___SYSCTL,
|
||||||
|
uintptr(unsafe.Pointer(&mib[0])),
|
||||||
|
uintptr(len(mib)),
|
||||||
|
uintptr(unsafe.Pointer(&out)),
|
||||||
|
uintptr(unsafe.Pointer(&n)),
|
||||||
|
uintptr(0),
|
||||||
|
uintptr(0))
|
||||||
|
if errno != 0 {
|
||||||
|
return nil, errno
|
||||||
|
}
|
||||||
|
return &out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func doinit() {
|
||||||
|
cpuid, err := sysctlCPUID("machdep.cpu0.cpu_id")
|
||||||
|
if err != nil {
|
||||||
|
setMinimalFeatures()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
parseARM64SystemRegisters(cpuid.aa64isar0, cpuid.aa64isar1, cpuid.aa64pfr0)
|
||||||
|
|
||||||
|
Initialized = true
|
||||||
|
}
|
65
internal/deps/cpu/cpu_openbsd_arm64.go
Normal file
65
internal/deps/cpu/cpu_openbsd_arm64.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Minimal copy of functionality from x/sys/unix so the cpu package can call
|
||||||
|
// sysctl without depending on x/sys/unix.
|
||||||
|
|
||||||
|
const (
|
||||||
|
// From OpenBSD's sys/sysctl.h.
|
||||||
|
_CTL_MACHDEP = 7
|
||||||
|
|
||||||
|
// From OpenBSD's machine/cpu.h.
|
||||||
|
_CPU_ID_AA64ISAR0 = 2
|
||||||
|
_CPU_ID_AA64ISAR1 = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// Implemented in the runtime package (runtime/sys_openbsd3.go)
|
||||||
|
func syscall_syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno)
|
||||||
|
|
||||||
|
//go:linkname syscall_syscall6 syscall.syscall6
|
||||||
|
|
||||||
|
func sysctl(mib []uint32, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) {
|
||||||
|
_, _, errno := syscall_syscall6(libc_sysctl_trampoline_addr, uintptr(unsafe.Pointer(&mib[0])), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen))
|
||||||
|
if errno != 0 {
|
||||||
|
return errno
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var libc_sysctl_trampoline_addr uintptr
|
||||||
|
|
||||||
|
//go:cgo_import_dynamic libc_sysctl sysctl "libc.so"
|
||||||
|
|
||||||
|
func sysctlUint64(mib []uint32) (uint64, bool) {
|
||||||
|
var out uint64
|
||||||
|
nout := unsafe.Sizeof(out)
|
||||||
|
if err := sysctl(mib, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0); err != nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
return out, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func doinit() {
|
||||||
|
setMinimalFeatures()
|
||||||
|
|
||||||
|
// Get ID_AA64ISAR0 and ID_AA64ISAR1 from sysctl.
|
||||||
|
isar0, ok := sysctlUint64([]uint32{_CTL_MACHDEP, _CPU_ID_AA64ISAR0})
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
isar1, ok := sysctlUint64([]uint32{_CTL_MACHDEP, _CPU_ID_AA64ISAR1})
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
parseARM64SystemRegisters(isar0, isar1, 0)
|
||||||
|
|
||||||
|
Initialized = true
|
||||||
|
}
|
11
internal/deps/cpu/cpu_openbsd_arm64.s
Normal file
11
internal/deps/cpu/cpu_openbsd_arm64.s
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0
|
||||||
|
JMP libc_sysctl(SB)
|
||||||
|
|
||||||
|
GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8
|
||||||
|
DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB)
|
9
internal/deps/cpu/cpu_other_arm.go
Normal file
9
internal/deps/cpu/cpu_other_arm.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// Copyright 2020 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 !linux && arm
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
func archInit() {}
|
9
internal/deps/cpu/cpu_other_arm64.go
Normal file
9
internal/deps/cpu/cpu_other_arm64.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// Copyright 2019 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 !linux && !netbsd && !openbsd && arm64
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
func doinit() {}
|
11
internal/deps/cpu/cpu_other_mips64x.go
Normal file
11
internal/deps/cpu/cpu_other_mips64x.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// Copyright 2020 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 !linux && (mips64 || mips64le)
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
func archInit() {
|
||||||
|
Initialized = true
|
||||||
|
}
|
12
internal/deps/cpu/cpu_other_ppc64x.go
Normal file
12
internal/deps/cpu/cpu_other_ppc64x.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// 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 !aix && !linux && (ppc64 || ppc64le)
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
func archInit() {
|
||||||
|
PPC64.IsPOWER8 = true
|
||||||
|
Initialized = true
|
||||||
|
}
|
11
internal/deps/cpu/cpu_other_riscv64.go
Normal file
11
internal/deps/cpu/cpu_other_riscv64.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// 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 !linux && riscv64
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
func archInit() {
|
||||||
|
Initialized = true
|
||||||
|
}
|
11
internal/deps/cpu/cpu_other_x86.go
Normal file
11
internal/deps/cpu/cpu_other_x86.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
//go:build 386 || amd64p32 || (amd64 && (!darwin || !gc))
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
func darwinSupportsAVX512() bool {
|
||||||
|
panic("only implemented for gc && amd64 && darwin")
|
||||||
|
}
|
16
internal/deps/cpu/cpu_ppc64x.go
Normal file
16
internal/deps/cpu/cpu_ppc64x.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// Copyright 2020 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 ppc64 || ppc64le
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
const cacheLineSize = 128
|
||||||
|
|
||||||
|
func initOptions() {
|
||||||
|
options = []option{
|
||||||
|
{Name: "darn", Feature: &PPC64.HasDARN},
|
||||||
|
{Name: "scv", Feature: &PPC64.HasSCV},
|
||||||
|
}
|
||||||
|
}
|
20
internal/deps/cpu/cpu_riscv64.go
Normal file
20
internal/deps/cpu/cpu_riscv64.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Copyright 2019 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 riscv64
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
const cacheLineSize = 64
|
||||||
|
|
||||||
|
func initOptions() {
|
||||||
|
options = []option{
|
||||||
|
{Name: "fastmisaligned", Feature: &RISCV64.HasFastMisaligned},
|
||||||
|
{Name: "c", Feature: &RISCV64.HasC},
|
||||||
|
{Name: "v", Feature: &RISCV64.HasV},
|
||||||
|
{Name: "zba", Feature: &RISCV64.HasZba},
|
||||||
|
{Name: "zbb", Feature: &RISCV64.HasZbb},
|
||||||
|
{Name: "zbs", Feature: &RISCV64.HasZbs},
|
||||||
|
}
|
||||||
|
}
|
172
internal/deps/cpu/cpu_s390x.go
Normal file
172
internal/deps/cpu/cpu_s390x.go
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
// Copyright 2020 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 cpu
|
||||||
|
|
||||||
|
const cacheLineSize = 256
|
||||||
|
|
||||||
|
func initOptions() {
|
||||||
|
options = []option{
|
||||||
|
{Name: "zarch", Feature: &S390X.HasZARCH, Required: true},
|
||||||
|
{Name: "stfle", Feature: &S390X.HasSTFLE, Required: true},
|
||||||
|
{Name: "ldisp", Feature: &S390X.HasLDISP, Required: true},
|
||||||
|
{Name: "eimm", Feature: &S390X.HasEIMM, Required: true},
|
||||||
|
{Name: "dfp", Feature: &S390X.HasDFP},
|
||||||
|
{Name: "etf3eh", Feature: &S390X.HasETF3EH},
|
||||||
|
{Name: "msa", Feature: &S390X.HasMSA},
|
||||||
|
{Name: "aes", Feature: &S390X.HasAES},
|
||||||
|
{Name: "aescbc", Feature: &S390X.HasAESCBC},
|
||||||
|
{Name: "aesctr", Feature: &S390X.HasAESCTR},
|
||||||
|
{Name: "aesgcm", Feature: &S390X.HasAESGCM},
|
||||||
|
{Name: "ghash", Feature: &S390X.HasGHASH},
|
||||||
|
{Name: "sha1", Feature: &S390X.HasSHA1},
|
||||||
|
{Name: "sha256", Feature: &S390X.HasSHA256},
|
||||||
|
{Name: "sha3", Feature: &S390X.HasSHA3},
|
||||||
|
{Name: "sha512", Feature: &S390X.HasSHA512},
|
||||||
|
{Name: "vx", Feature: &S390X.HasVX},
|
||||||
|
{Name: "vxe", Feature: &S390X.HasVXE},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bitIsSet reports whether the bit at index is set. The bit index
|
||||||
|
// is in big endian order, so bit index 0 is the leftmost bit.
|
||||||
|
func bitIsSet(bits []uint64, index uint) bool {
|
||||||
|
return bits[index/64]&((1<<63)>>(index%64)) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// facility is a bit index for the named facility.
|
||||||
|
type facility uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
// mandatory facilities
|
||||||
|
zarch facility = 1 // z architecture mode is active
|
||||||
|
stflef facility = 7 // store-facility-list-extended
|
||||||
|
ldisp facility = 18 // long-displacement
|
||||||
|
eimm facility = 21 // extended-immediate
|
||||||
|
|
||||||
|
// miscellaneous facilities
|
||||||
|
dfp facility = 42 // decimal-floating-point
|
||||||
|
etf3eh facility = 30 // extended-translation 3 enhancement
|
||||||
|
|
||||||
|
// cryptography facilities
|
||||||
|
msa facility = 17 // message-security-assist
|
||||||
|
msa3 facility = 76 // message-security-assist extension 3
|
||||||
|
msa4 facility = 77 // message-security-assist extension 4
|
||||||
|
msa5 facility = 57 // message-security-assist extension 5
|
||||||
|
msa8 facility = 146 // message-security-assist extension 8
|
||||||
|
msa9 facility = 155 // message-security-assist extension 9
|
||||||
|
|
||||||
|
// vector facilities
|
||||||
|
vx facility = 129 // vector facility
|
||||||
|
vxe facility = 135 // vector-enhancements 1
|
||||||
|
vxe2 facility = 148 // vector-enhancements 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// facilityList contains the result of an STFLE call.
|
||||||
|
// Bits are numbered in big endian order so the
|
||||||
|
// leftmost bit (the MSB) is at index 0.
|
||||||
|
type facilityList struct {
|
||||||
|
bits [4]uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has reports whether the given facilities are present.
|
||||||
|
func (s *facilityList) Has(fs ...facility) bool {
|
||||||
|
if len(fs) == 0 {
|
||||||
|
panic("no facility bits provided")
|
||||||
|
}
|
||||||
|
for _, f := range fs {
|
||||||
|
if !bitIsSet(s.bits[:], uint(f)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// function is the code for the named cryptographic function.
|
||||||
|
type function uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
// KM{,A,C,CTR} function codes
|
||||||
|
aes128 function = 18 // AES-128
|
||||||
|
aes192 function = 19 // AES-192
|
||||||
|
aes256 function = 20 // AES-256
|
||||||
|
|
||||||
|
// K{I,L}MD function codes
|
||||||
|
sha1 function = 1 // SHA-1
|
||||||
|
sha256 function = 2 // SHA-256
|
||||||
|
sha512 function = 3 // SHA-512
|
||||||
|
sha3_224 function = 32 // SHA3-224
|
||||||
|
sha3_256 function = 33 // SHA3-256
|
||||||
|
sha3_384 function = 34 // SHA3-384
|
||||||
|
sha3_512 function = 35 // SHA3-512
|
||||||
|
shake128 function = 36 // SHAKE-128
|
||||||
|
shake256 function = 37 // SHAKE-256
|
||||||
|
|
||||||
|
// KLMD function codes
|
||||||
|
ghash function = 65 // GHASH
|
||||||
|
)
|
||||||
|
|
||||||
|
// queryResult contains the result of a Query function
|
||||||
|
// call. Bits are numbered in big endian order so the
|
||||||
|
// leftmost bit (the MSB) is at index 0.
|
||||||
|
type queryResult struct {
|
||||||
|
bits [2]uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has reports whether the given functions are present.
|
||||||
|
func (q *queryResult) Has(fns ...function) bool {
|
||||||
|
if len(fns) == 0 {
|
||||||
|
panic("no function codes provided")
|
||||||
|
}
|
||||||
|
for _, f := range fns {
|
||||||
|
if !bitIsSet(q.bits[:], uint(f)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func doinit() {
|
||||||
|
initS390Xbase()
|
||||||
|
|
||||||
|
// We need implementations of stfle, km and so on
|
||||||
|
// to detect cryptographic features.
|
||||||
|
if !haveAsmFunctions() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// optional cryptographic functions
|
||||||
|
if S390X.HasMSA {
|
||||||
|
aes := []function{aes128, aes192, aes256}
|
||||||
|
|
||||||
|
// cipher message
|
||||||
|
km, kmc := kmQuery(), kmcQuery()
|
||||||
|
S390X.HasAES = km.Has(aes...)
|
||||||
|
S390X.HasAESCBC = kmc.Has(aes...)
|
||||||
|
if S390X.HasSTFLE {
|
||||||
|
facilities := stfle()
|
||||||
|
if facilities.Has(msa4) {
|
||||||
|
kmctr := kmctrQuery()
|
||||||
|
S390X.HasAESCTR = kmctr.Has(aes...)
|
||||||
|
}
|
||||||
|
if facilities.Has(msa8) {
|
||||||
|
kma := kmaQuery()
|
||||||
|
S390X.HasAESGCM = kma.Has(aes...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute message digest
|
||||||
|
kimd := kimdQuery() // intermediate (no padding)
|
||||||
|
klmd := klmdQuery() // last (padding)
|
||||||
|
S390X.HasSHA1 = kimd.Has(sha1) && klmd.Has(sha1)
|
||||||
|
S390X.HasSHA256 = kimd.Has(sha256) && klmd.Has(sha256)
|
||||||
|
S390X.HasSHA512 = kimd.Has(sha512) && klmd.Has(sha512)
|
||||||
|
S390X.HasGHASH = kimd.Has(ghash) // KLMD-GHASH does not exist
|
||||||
|
sha3 := []function{
|
||||||
|
sha3_224, sha3_256, sha3_384, sha3_512,
|
||||||
|
shake128, shake256,
|
||||||
|
}
|
||||||
|
S390X.HasSHA3 = kimd.Has(sha3...) && klmd.Has(sha3...)
|
||||||
|
}
|
||||||
|
}
|
57
internal/deps/cpu/cpu_s390x.s
Normal file
57
internal/deps/cpu/cpu_s390x.s
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// Copyright 2019 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 gc
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// func stfle() facilityList
|
||||||
|
TEXT ·stfle(SB), NOSPLIT|NOFRAME, $0-32
|
||||||
|
MOVD $ret+0(FP), R1
|
||||||
|
MOVD $3, R0 // last doubleword index to store
|
||||||
|
XC $32, (R1), (R1) // clear 4 doublewords (32 bytes)
|
||||||
|
WORD $0xb2b01000 // store facility list extended (STFLE)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func kmQuery() queryResult
|
||||||
|
TEXT ·kmQuery(SB), NOSPLIT|NOFRAME, $0-16
|
||||||
|
MOVD $0, R0 // set function code to 0 (KM-Query)
|
||||||
|
MOVD $ret+0(FP), R1 // address of 16-byte return value
|
||||||
|
WORD $0xB92E0024 // cipher message (KM)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func kmcQuery() queryResult
|
||||||
|
TEXT ·kmcQuery(SB), NOSPLIT|NOFRAME, $0-16
|
||||||
|
MOVD $0, R0 // set function code to 0 (KMC-Query)
|
||||||
|
MOVD $ret+0(FP), R1 // address of 16-byte return value
|
||||||
|
WORD $0xB92F0024 // cipher message with chaining (KMC)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func kmctrQuery() queryResult
|
||||||
|
TEXT ·kmctrQuery(SB), NOSPLIT|NOFRAME, $0-16
|
||||||
|
MOVD $0, R0 // set function code to 0 (KMCTR-Query)
|
||||||
|
MOVD $ret+0(FP), R1 // address of 16-byte return value
|
||||||
|
WORD $0xB92D4024 // cipher message with counter (KMCTR)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func kmaQuery() queryResult
|
||||||
|
TEXT ·kmaQuery(SB), NOSPLIT|NOFRAME, $0-16
|
||||||
|
MOVD $0, R0 // set function code to 0 (KMA-Query)
|
||||||
|
MOVD $ret+0(FP), R1 // address of 16-byte return value
|
||||||
|
WORD $0xb9296024 // cipher message with authentication (KMA)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func kimdQuery() queryResult
|
||||||
|
TEXT ·kimdQuery(SB), NOSPLIT|NOFRAME, $0-16
|
||||||
|
MOVD $0, R0 // set function code to 0 (KIMD-Query)
|
||||||
|
MOVD $ret+0(FP), R1 // address of 16-byte return value
|
||||||
|
WORD $0xB93E0024 // compute intermediate message digest (KIMD)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func klmdQuery() queryResult
|
||||||
|
TEXT ·klmdQuery(SB), NOSPLIT|NOFRAME, $0-16
|
||||||
|
MOVD $0, R0 // set function code to 0 (KLMD-Query)
|
||||||
|
MOVD $ret+0(FP), R1 // address of 16-byte return value
|
||||||
|
WORD $0xB93F0024 // compute last message digest (KLMD)
|
||||||
|
RET
|
75
internal/deps/cpu/cpu_s390x_test.go
Normal file
75
internal/deps/cpu/cpu_s390x_test.go
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
// Copyright 2020 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 cpu_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/emmansun/gmsm/internal/deps/cpu"
|
||||||
|
)
|
||||||
|
|
||||||
|
var s390xTests = []struct {
|
||||||
|
name string
|
||||||
|
feature bool
|
||||||
|
facility uint
|
||||||
|
mandatory bool
|
||||||
|
}{
|
||||||
|
{"ZARCH", cpu.S390X.HasZARCH, 1, true},
|
||||||
|
{"STFLE", cpu.S390X.HasSTFLE, 7, true},
|
||||||
|
{"LDISP", cpu.S390X.HasLDISP, 18, true},
|
||||||
|
{"EIMM", cpu.S390X.HasEIMM, 21, true},
|
||||||
|
{"DFP", cpu.S390X.HasDFP, 42, false},
|
||||||
|
{"MSA", cpu.S390X.HasMSA, 17, false},
|
||||||
|
{"VX", cpu.S390X.HasVX, 129, false},
|
||||||
|
{"VXE", cpu.S390X.HasVXE, 135, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
// bitIsSet reports whether the bit at index is set. The bit index
|
||||||
|
// is in big endian order, so bit index 0 is the leftmost bit.
|
||||||
|
func bitIsSet(bits [4]uint64, i uint) bool {
|
||||||
|
return bits[i/64]&((1<<63)>>(i%64)) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// facilityList contains the contents of location 200 on zos.
|
||||||
|
// Bits are numbered in big endian order so the
|
||||||
|
// leftmost bit (the MSB) is at index 0.
|
||||||
|
type facilityList struct {
|
||||||
|
bits [4]uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestS390XVectorFacilityFeatures(t *testing.T) {
|
||||||
|
// vector-enhancements require vector facility to be enabled
|
||||||
|
if cpu.S390X.HasVXE && !cpu.S390X.HasVX {
|
||||||
|
t.Error("HasVX expected true, got false (VXE is true)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestS390XMandatoryFeatures(t *testing.T) {
|
||||||
|
for _, tc := range s390xTests {
|
||||||
|
if tc.mandatory && !tc.feature {
|
||||||
|
t.Errorf("Feature %s is mandatory but is not present", tc.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestS390XFeatures(t *testing.T) {
|
||||||
|
if runtime.GOOS != "zos" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Read available facilities from address 200.
|
||||||
|
facilitiesAddress := uintptr(200)
|
||||||
|
var facilities facilityList
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
facilities.bits[i] = *(*uint64)(unsafe.Pointer(facilitiesAddress + uintptr(8*i)))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range s390xTests {
|
||||||
|
if want := bitIsSet(facilities.bits, tc.facility); want != tc.feature {
|
||||||
|
t.Errorf("Feature %s expected %v, got %v", tc.name, want, tc.feature)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
internal/deps/cpu/cpu_wasm.go
Normal file
17
internal/deps/cpu/cpu_wasm.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2019 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 wasm
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
// We're compiling the cpu package for an unknown (software-abstracted) CPU.
|
||||||
|
// Make CacheLinePad an empty struct and hope that the usual struct alignment
|
||||||
|
// rules are good enough.
|
||||||
|
|
||||||
|
const cacheLineSize = 0
|
||||||
|
|
||||||
|
func initOptions() {}
|
||||||
|
|
||||||
|
func archInit() {}
|
162
internal/deps/cpu/cpu_x86.go
Normal file
162
internal/deps/cpu/cpu_x86.go
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
// Copyright 2018 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 386 || amd64 || amd64p32
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
import "runtime"
|
||||||
|
|
||||||
|
const cacheLineSize = 64
|
||||||
|
|
||||||
|
func initOptions() {
|
||||||
|
options = []option{
|
||||||
|
{Name: "adx", Feature: &X86.HasADX},
|
||||||
|
{Name: "aes", Feature: &X86.HasAES},
|
||||||
|
{Name: "avx", Feature: &X86.HasAVX},
|
||||||
|
{Name: "avx2", Feature: &X86.HasAVX2},
|
||||||
|
{Name: "avx512", Feature: &X86.HasAVX512},
|
||||||
|
{Name: "avx512f", Feature: &X86.HasAVX512F},
|
||||||
|
{Name: "avx512cd", Feature: &X86.HasAVX512CD},
|
||||||
|
{Name: "avx512er", Feature: &X86.HasAVX512ER},
|
||||||
|
{Name: "avx512pf", Feature: &X86.HasAVX512PF},
|
||||||
|
{Name: "avx512vl", Feature: &X86.HasAVX512VL},
|
||||||
|
{Name: "avx512bw", Feature: &X86.HasAVX512BW},
|
||||||
|
{Name: "avx512dq", Feature: &X86.HasAVX512DQ},
|
||||||
|
{Name: "avx512ifma", Feature: &X86.HasAVX512IFMA},
|
||||||
|
{Name: "avx512vbmi", Feature: &X86.HasAVX512VBMI},
|
||||||
|
{Name: "avx512vnniw", Feature: &X86.HasAVX5124VNNIW},
|
||||||
|
{Name: "avx5124fmaps", Feature: &X86.HasAVX5124FMAPS},
|
||||||
|
{Name: "avx512vpopcntdq", Feature: &X86.HasAVX512VPOPCNTDQ},
|
||||||
|
{Name: "avx512vpclmulqdq", Feature: &X86.HasAVX512VPCLMULQDQ},
|
||||||
|
{Name: "avx512vnni", Feature: &X86.HasAVX512VNNI},
|
||||||
|
{Name: "avx512gfni", Feature: &X86.HasAVX512GFNI},
|
||||||
|
{Name: "avx512vaes", Feature: &X86.HasAVX512VAES},
|
||||||
|
{Name: "avx512vbmi2", Feature: &X86.HasAVX512VBMI2},
|
||||||
|
{Name: "avx512bitalg", Feature: &X86.HasAVX512BITALG},
|
||||||
|
{Name: "avx512bf16", Feature: &X86.HasAVX512BF16},
|
||||||
|
{Name: "amxtile", Feature: &X86.HasAMXTile},
|
||||||
|
{Name: "amxint8", Feature: &X86.HasAMXInt8},
|
||||||
|
{Name: "amxbf16", Feature: &X86.HasAMXBF16},
|
||||||
|
{Name: "bmi1", Feature: &X86.HasBMI1},
|
||||||
|
{Name: "bmi2", Feature: &X86.HasBMI2},
|
||||||
|
{Name: "cx16", Feature: &X86.HasCX16},
|
||||||
|
{Name: "erms", Feature: &X86.HasERMS},
|
||||||
|
{Name: "fma", Feature: &X86.HasFMA},
|
||||||
|
{Name: "osxsave", Feature: &X86.HasOSXSAVE},
|
||||||
|
{Name: "pclmulqdq", Feature: &X86.HasPCLMULQDQ},
|
||||||
|
{Name: "popcnt", Feature: &X86.HasPOPCNT},
|
||||||
|
{Name: "rdrand", Feature: &X86.HasRDRAND},
|
||||||
|
{Name: "rdseed", Feature: &X86.HasRDSEED},
|
||||||
|
{Name: "sse3", Feature: &X86.HasSSE3},
|
||||||
|
{Name: "sse41", Feature: &X86.HasSSE41},
|
||||||
|
{Name: "sse42", Feature: &X86.HasSSE42},
|
||||||
|
{Name: "ssse3", Feature: &X86.HasSSSE3},
|
||||||
|
{Name: "avxifma", Feature: &X86.HasAVXIFMA},
|
||||||
|
{Name: "avxvnni", Feature: &X86.HasAVXVNNI},
|
||||||
|
{Name: "avxvnniint8", Feature: &X86.HasAVXVNNIInt8},
|
||||||
|
|
||||||
|
// These capabilities should always be enabled on amd64:
|
||||||
|
{Name: "sse2", Feature: &X86.HasSSE2, Required: runtime.GOARCH == "amd64"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func archInit() {
|
||||||
|
|
||||||
|
Initialized = true
|
||||||
|
|
||||||
|
maxID, _, _, _ := cpuid(0, 0)
|
||||||
|
|
||||||
|
if maxID < 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, ecx1, edx1 := cpuid(1, 0)
|
||||||
|
X86.HasSSE2 = isSet(26, edx1)
|
||||||
|
|
||||||
|
X86.HasSSE3 = isSet(0, ecx1) // Check presence of SSE3 - bit 0 of ECX
|
||||||
|
X86.HasPCLMULQDQ = isSet(1, ecx1) // Check presence of PCLMULQDQ - bit 1 of ECX
|
||||||
|
X86.HasSSSE3 = isSet(9, ecx1) // Check presence of SSSE3 - bit 9 of ECX
|
||||||
|
X86.HasFMA = isSet(12, ecx1) // Check presence of FMA - bit 12 of ECX
|
||||||
|
X86.HasCX16 = isSet(13, ecx1) // Check presence of CX16 - bit 13 of ECX
|
||||||
|
X86.HasSSE41 = isSet(19, ecx1) // Check presence of SSE4.1 - bit 19 of ECX
|
||||||
|
X86.HasSSE42 = isSet(20, ecx1) // Check presence of SSE4.2 - bit 20 of ECX
|
||||||
|
X86.HasPOPCNT = isSet(23, ecx1) // Check presence of POPCNT - bit 23 of ECX
|
||||||
|
X86.HasAES = isSet(25, ecx1) // Check presence of AESNI - bit 25 of ECX
|
||||||
|
X86.HasOSXSAVE = isSet(27, ecx1) // Check presence of OSXSAVE - bit 27 of ECX
|
||||||
|
X86.HasRDRAND = isSet(30, ecx1) // Check presence of RDRAND - bit 30 of ECX
|
||||||
|
|
||||||
|
var osSupportsAVX, osSupportsAVX512 bool
|
||||||
|
// For XGETBV, OSXSAVE bit is required and sufficient.
|
||||||
|
if X86.HasOSXSAVE {
|
||||||
|
eax, _ := xgetbv()
|
||||||
|
// Check if XMM and YMM registers have OS support.
|
||||||
|
osSupportsAVX = isSet(1, eax) && isSet(2, eax)
|
||||||
|
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
// Darwin requires special AVX512 checks, see cpu_darwin_x86.go
|
||||||
|
osSupportsAVX512 = osSupportsAVX && darwinSupportsAVX512()
|
||||||
|
} else {
|
||||||
|
// Check if OPMASK and ZMM registers have OS support.
|
||||||
|
osSupportsAVX512 = osSupportsAVX && isSet(5, eax) && isSet(6, eax) && isSet(7, eax)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
X86.HasAVX = isSet(28, ecx1) && osSupportsAVX
|
||||||
|
|
||||||
|
if maxID < 7 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
eax7, ebx7, ecx7, edx7 := cpuid(7, 0)
|
||||||
|
X86.HasBMI1 = isSet(3, ebx7) // Check presence of BMI1 - bit 3 of EBX
|
||||||
|
X86.HasAVX2 = isSet(5, ebx7) && osSupportsAVX // Check presence of AVX2 - bit 5 of EBX
|
||||||
|
X86.HasBMI2 = isSet(8, ebx7) // Check presence of BMI2 - bit 8 of EBX
|
||||||
|
X86.HasERMS = isSet(9, ebx7)
|
||||||
|
X86.HasRDSEED = isSet(18, ebx7)
|
||||||
|
X86.HasADX = isSet(19, ebx7)
|
||||||
|
|
||||||
|
X86.HasAVX512 = isSet(16, ebx7) && osSupportsAVX512 // Because avx-512 foundation is the core required extension
|
||||||
|
if X86.HasAVX512 {
|
||||||
|
X86.HasAVX512F = true
|
||||||
|
X86.HasAVX512CD = isSet(28, ebx7) // Check presence of AVX512CD - bit 28 of EBX
|
||||||
|
X86.HasAVX512ER = isSet(27, ebx7) // Check presence of AVX512ER - bit 27 of EBX
|
||||||
|
X86.HasAVX512PF = isSet(26, ebx7) // Check presence of AVX512PF - bit 26 of EBX
|
||||||
|
X86.HasAVX512VL = isSet(31, ebx7) // Check presence of AVX512VL - bit 31 of EBX
|
||||||
|
X86.HasAVX512BW = isSet(30, ebx7) // Check presence of AVX512BW - bit 30 of EBX
|
||||||
|
X86.HasAVX512DQ = isSet(17, ebx7) // Check presence of AVX512F - bit 16 of EBX
|
||||||
|
X86.HasAVX512IFMA = isSet(21, ebx7) // Check presence of AVX512IFMA - bit 21 of EBX
|
||||||
|
X86.HasAVX512VBMI = isSet(1, ecx7) // Check presence of AVX512VBMI - bit 1 of ECX
|
||||||
|
X86.HasAVX5124VNNIW = isSet(2, edx7) // Check presence of AVX5124VNNIW - bit 2 of EDX
|
||||||
|
X86.HasAVX5124FMAPS = isSet(3, edx7) // Check presence of AVX5124FMAPS - bit 3 of EDX
|
||||||
|
X86.HasAVX512VPOPCNTDQ = isSet(14, ecx7) // Check presence of AVX512VPOPCNTDQ - bit 14 of ECX
|
||||||
|
X86.HasAVX512VPCLMULQDQ = isSet(10, ecx7) // Check presence of VPCLMULQDQ - bit 10 of ECX
|
||||||
|
X86.HasAVX512VNNI = isSet(11, ecx7) // Check presence of AVX512VNNI - bit 11 of ECX
|
||||||
|
X86.HasAVX512GFNI = isSet(8, ecx7) // Check presence of AVX512GFNI - bit 8 of ECX
|
||||||
|
X86.HasAVX512VAES = isSet(9, ecx7) // Check presence of AVX512VAES - bit 9 of ECX
|
||||||
|
X86.HasAVX512VBMI2 = isSet(6, ecx7) // Check presence of AVX512VBMI2 - bit 6 of ECX
|
||||||
|
X86.HasAVX512BITALG = isSet(12, ecx7) // Check presence of AVX512BITALG - bit 12 of ECX
|
||||||
|
}
|
||||||
|
|
||||||
|
X86.HasAMXTile = isSet(24, edx7)
|
||||||
|
X86.HasAMXInt8 = isSet(25, edx7)
|
||||||
|
X86.HasAMXBF16 = isSet(22, edx7)
|
||||||
|
|
||||||
|
// These features depend on the second level of extended features.
|
||||||
|
if eax7 >= 1 {
|
||||||
|
eax71, _, _, edx71 := cpuid(7, 1)
|
||||||
|
if X86.HasAVX512 {
|
||||||
|
X86.HasAVX512BF16 = isSet(5, eax71)
|
||||||
|
}
|
||||||
|
if X86.HasAVX {
|
||||||
|
X86.HasAVXIFMA = isSet(23, eax71) // Check presence of AVXIFMA - bit 23 of EAX
|
||||||
|
X86.HasAVXVNNI = isSet(4, eax71)
|
||||||
|
X86.HasAVXVNNIInt8 = isSet(4, edx71)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSet(bitpos uint, value uint32) bool {
|
||||||
|
return value&(1<<bitpos) != 0
|
||||||
|
}
|
37
internal/deps/cpu/cpu_x86_test.go
Normal file
37
internal/deps/cpu/cpu_x86_test.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// Copyright 2018 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 386 || amd64
|
||||||
|
|
||||||
|
package cpu_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/emmansun/gmsm/internal/deps/cpu"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestX86ifAVX2hasAVX(t *testing.T) {
|
||||||
|
if X86.HasAVX2 && !X86.HasAVX {
|
||||||
|
t.Fatalf("HasAVX expected true when HasAVX2 is true, got false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestX86ifAVX512FhasAVX2(t *testing.T) {
|
||||||
|
if X86.HasAVX512F && !X86.HasAVX2 {
|
||||||
|
t.Fatalf("HasAVX2 expected true when HasAVX512F is true, got false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestX86ifAVX512BWhasAVX512F(t *testing.T) {
|
||||||
|
if X86.HasAVX512BW && !X86.HasAVX512F {
|
||||||
|
t.Fatalf("HasAVX512F expected true when HasAVX512BW is true, got false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestX86ifAVX512VLhasAVX512F(t *testing.T) {
|
||||||
|
if X86.HasAVX512VL && !X86.HasAVX512F {
|
||||||
|
t.Fatalf("HasAVX512F expected true when HasAVX512VL is true, got false")
|
||||||
|
}
|
||||||
|
}
|
10
internal/deps/cpu/cpu_zos.go
Normal file
10
internal/deps/cpu/cpu_zos.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
// Copyright 2020 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 cpu
|
||||||
|
|
||||||
|
func archInit() {
|
||||||
|
doinit()
|
||||||
|
Initialized = true
|
||||||
|
}
|
25
internal/deps/cpu/cpu_zos_s390x.go
Normal file
25
internal/deps/cpu/cpu_zos_s390x.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2020 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 cpu
|
||||||
|
|
||||||
|
func initS390Xbase() {
|
||||||
|
// get the facilities list
|
||||||
|
facilities := stfle()
|
||||||
|
|
||||||
|
// mandatory
|
||||||
|
S390X.HasZARCH = facilities.Has(zarch)
|
||||||
|
S390X.HasSTFLE = facilities.Has(stflef)
|
||||||
|
S390X.HasLDISP = facilities.Has(ldisp)
|
||||||
|
S390X.HasEIMM = facilities.Has(eimm)
|
||||||
|
|
||||||
|
// optional
|
||||||
|
S390X.HasETF3EH = facilities.Has(etf3eh)
|
||||||
|
S390X.HasDFP = facilities.Has(dfp)
|
||||||
|
S390X.HasMSA = facilities.Has(msa)
|
||||||
|
S390X.HasVX = facilities.Has(vx)
|
||||||
|
if S390X.HasVX {
|
||||||
|
S390X.HasVXE = facilities.Has(vxe)
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user