diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5ea9152..6c44d3d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,10 +7,20 @@ on: jobs: test-project: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: + - run: uname -a + - run: lsb_release -a + + - name: Setup + uses: actions/setup-go@v4 + with: + go-version: "1.25" + + - run: go version + - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Test - run: make test \ No newline at end of file + run: make test diff --git a/FUTURE.md b/FUTURE.md index e2f1050..5095c1d 100644 --- a/FUTURE.md +++ b/FUTURE.md @@ -1,20 +1,24 @@ ## ✈️ 未来版本的新特性 (Features in future versions) -### v0.5.x +### v0.6.x * [ ] 增加 cmd 包,提供二进制 cli 使用 * [ ] 支持 SM3 国标算法 * [ ] 支持 SM4 国标算法 -* [ ] 支持 ECC key 生成 -* [ ] 支持 ECC 非对称加密算法 * [ ] 支持更多的散列算法 +### v0.5.x + +* [ ] 支持 ECDSA 算法 +* [ ] 支持 EdDSA 算法 +* [x] 重构代码,优化使用方式 +* [ ] 提升覆盖率到 90% 以上 + ### v0.4.x * [x] 调整 hash 包使用姿势 * [x] 大规模重构,优化代码使用 * [x] 性能优化,尝试减少内存分配 -* [ ] 继续完善单元测试,提升覆盖率到 90% ### v0.3.x diff --git a/HISTORY.md b/HISTORY.md index 97c83d1..8d95a0c 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,12 @@ ## 📜 历史版本的特性介绍 (Features in old versions) +### v0.5.0-alpha + +> 此版本发布于 2025-12-05 + +* 重构版本,目前看着顺眼一些了 +* 周董这么突然就发了个专辑。。 + ### v0.4.3 > 此版本发布于 2024-06-02 diff --git a/Makefile b/Makefile index 4c4db9d..0aafb18 100644 --- a/Makefile +++ b/Makefile @@ -6,10 +6,9 @@ fmt: go fmt ./... test: - go test -cover -count=1 -test.cpu=1 ./... + go test -v -cover ./... bench: - go test -v ./_examples/rand_test.go -bench=. -benchtime=1s go test -v ./_examples/hash_test.go -bench=. -benchtime=1s go test -v ./_examples/hmac_test.go -bench=. -benchtime=1s go test -v ./_examples/des_test.go -bench=. -benchtime=1s diff --git a/README.en.md b/README.en.md index 6a36d63..f23dc1e 100644 --- a/README.en.md +++ b/README.en.md @@ -5,7 +5,7 @@ [![Coverage](_icons/coverage.svg)](_icons/coverage.svg) ![Test](https://github.com/FishGoddess/cryptox/actions/workflows/test.yml/badge.svg) -**Cryptox** is a kit for safety which includes some common encrypted/decrypted algorithms. +**Cryptox** is a safety kit including some common algorithms for convenience. [阅读中文版的文档](./README.md) @@ -16,9 +16,9 @@ * CRC/FNV hash supports. * HMAC mixed hash supports. * DES/3DES/AES encrypt and decrypt supports. -* RSA/ECC encrypt and decrypt supports. +* RSA encrypt and decrypt supports. * ECB/CBC/OFB/CFB/CTR/GCM mode supports. -* NONE/ZERO/PKCS5/PKCS7 padding supports. +* ZERO/PKCS5/PKCS7 padding supports. _Check [HISTORY.md](./HISTORY.md) and [FUTURE.md](./FUTURE.md) to know about more information._ @@ -28,7 +28,6 @@ _Check [HISTORY.md](./HISTORY.md) and [FUTURE.md](./FUTURE.md) to know about mor $ go get -u github.com/FishGoddess/cryptox ``` -* [rand](_examples/rand.go) * [hash](_examples/hash.go) * [hmac](_examples/hmac.go) * [des](_examples/des.go) @@ -43,96 +42,84 @@ $ go get -u github.com/FishGoddess/cryptox $ make bench ``` -_Note: Data size is 128 bytes, ecb/cbc uses pkcs7 padding, cfb/ofb/ctr is no padding._ +_Note: Data size is 128 bytes, ecb/cbc uses pkcs7 padding, cfb/ofb/ctr/gcm is no padding._ ``` goos: linux goarch: amd64 -cpu: AMD EPYC 7K62 48-Core Processor - -BenchmarkGenerateBytes-2 11454386 102.6 ns/op 16 B/op 1 allocs/op -BenchmarkGenerateString-2 11392647 106.1 ns/op 16 B/op 1 allocs/op - -BenchmarkMD5-2 8933902 133.6 ns/op 0 B/op 0 allocs/op -BenchmarkSHA1-2 7997222 148.7 ns/op 0 B/op 0 allocs/op -BenchmarkSHA224-2 12905275 93.82 ns/op 0 B/op 0 allocs/op -BenchmarkSHA256-2 13305572 90.11 ns/op 0 B/op 0 allocs/op -BenchmarkSHA384-2 4501677 266.8 ns/op 0 B/op 0 allocs/op -BenchmarkSHA512-2 4457496 272.0 ns/op 0 B/op 0 allocs/op -BenchmarkCRC32IEEE-2 37977817 26.99 ns/op 0 B/op 0 allocs/op -BenchmarkCRC64ISO-2 49464211 23.77 ns/op 0 B/op 0 allocs/op -BenchmarkCRC64ECMA-2 47987559 23.85 ns/op 0 B/op 0 allocs/op -BenchmarkFnv32-2 88136509 13.91 ns/op 0 B/op 0 allocs/op -BenchmarkFnv32a-2 92693382 13.53 ns/op 0 B/op 0 allocs/op -BenchmarkFnv64-2 78227923 15.31 ns/op 0 B/op 0 allocs/op -BenchmarkFnv64a-2 79213070 15.35 ns/op 0 B/op 0 allocs/op -BenchmarkFnv128-2 15003325 77.31 ns/op 16 B/op 1 allocs/op -BenchmarkFnv128a-2 15473437 76.73 ns/op 16 B/op 1 allocs/op - -BenchmarkHMACUsingMD5-2 1418788 837.4 ns/op 432 B/op 6 allocs/op -BenchmarkHMACUsingSHA1-2 1304995 922.9 ns/op 472 B/op 6 allocs/op -BenchmarkHMACUsingSHA224-2 1879555 651.9 ns/op 512 B/op 6 allocs/op -BenchmarkHMACUsingSHA256-2 1880856 656.8 ns/op 512 B/op 6 allocs/op -BenchmarkHMACUsingSHA384-2 702820 1679 ns/op 848 B/op 6 allocs/op -BenchmarkHMACUsingSHA512-2 691454 1629 ns/op 864 B/op 6 allocs/op - -BenchmarkDESEncryptECB-2 321910 3601 ns/op 656 B/op 4 allocs/op -BenchmarkDESEncryptCBC-2 307196 3911 ns/op 752 B/op 7 allocs/op -BenchmarkDESEncryptCFB-2 319731 3689 ns/op 480 B/op 6 allocs/op -BenchmarkDESEncryptOFB-2 126572 9045 ns/op 984 B/op 6 allocs/op -BenchmarkDESEncryptCTR-2 125204 9471 ns/op 984 B/op 6 allocs/op -BenchmarkDESDecryptECB-2 340486 3461 ns/op 272 B/op 2 allocs/op -BenchmarkDESDecryptCBC-2 321182 3730 ns/op 368 B/op 5 allocs/op -BenchmarkDESDecryptCFB-2 329288 3607 ns/op 352 B/op 5 allocs/op -BenchmarkDESDecryptOFB-2 131550 9054 ns/op 856 B/op 5 allocs/op -BenchmarkDESDecryptCTR-2 125089 9539 ns/op 856 B/op 5 allocs/op - -BenchmarkDESEncryptECBTriple-2 124927 9474 ns/op 912 B/op 4 allocs/op -BenchmarkDESEncryptCBCTriple-2 121834 9701 ns/op 1008 B/op 7 allocs/op -BenchmarkDESEncryptCFBTriple-2 127020 9334 ns/op 736 B/op 6 allocs/op -BenchmarkDESEncryptOFBTriple-2 50922 23598 ns/op 1240 B/op 6 allocs/op -BenchmarkDESEncryptCTRTriple-2 49735 23873 ns/op 1240 B/op 6 allocs/op -BenchmarkDESDecryptECBTriple-2 128005 9328 ns/op 528 B/op 2 allocs/op -BenchmarkDESDecryptCBCTriple-2 122860 9558 ns/op 624 B/op 5 allocs/op -BenchmarkDESDecryptCFBTriple-2 126201 9334 ns/op 608 B/op 5 allocs/op -BenchmarkDESDecryptOFBTriple-2 50688 23291 ns/op 1112 B/op 5 allocs/op -BenchmarkDESDecryptCTRTriple-2 50391 23913 ns/op 1112 B/op 5 allocs/op - -BenchmarkAESEncryptECB-2 1960758 578.4 ns/op 1104 B/op 7 allocs/op -BenchmarkAESEncryptCBC-2 1413351 842.1 ns/op 1216 B/op 10 allocs/op -BenchmarkAESEncryptCFB-2 1657416 726.3 ns/op 944 B/op 9 allocs/op -BenchmarkAESEncryptOFB-2 924843 1206 ns/op 1440 B/op 9 allocs/op -BenchmarkAESEncryptCTR-2 808509 1381 ns/op 1440 B/op 9 allocs/op -BenchmarkAESEncryptGCM-2 1784593 661.2 ns/op 1168 B/op 7 allocs/op -BenchmarkAESDecryptECB-2 2590693 475.5 ns/op 720 B/op 5 allocs/op -BenchmarkAESDecryptCBC-2 1715998 711.6 ns/op 832 B/op 8 allocs/op -BenchmarkAESDecryptCFB-2 1809044 656.0 ns/op 816 B/op 8 allocs/op -BenchmarkAESDecryptOFB-2 956439 1152 ns/op 1312 B/op 8 allocs/op -BenchmarkAESDecryptCTR-2 841315 1348 ns/op 1312 B/op 8 allocs/op -BenchmarkAESDecryptGCM-2 1944147 633.5 ns/op 1040 B/op 6 allocs/op - -BenchmarkRSAEncryptPKCS1v15-2 1252 948252 ns/op 95488 B/op 155 allocs/op -BenchmarkRSAEncryptOAEP-2 1315 909433 ns/op 95592 B/op 160 allocs/op -BenchmarkRSADecryptPKCS1v15-2 166 7219740 ns/op 7600 B/op 16 allocs/op -BenchmarkRSADecryptPKCS1v15SessionKey-2 165 7154349 ns/op 7600 B/op 16 allocs/op -BenchmarkRSADecryptOAEP-2 166 7150228 ns/op 7704 B/op 21 allocs/op -BenchmarkRSASignPSS-2 154 7670215 ns/op 67168 B/op 100 allocs/op -BenchmarkRSASignPKCS1v15-2 156 7637694 ns/op 66992 B/op 95 allocs/op -BenchmarkRSAVerifyPSS-2 1308 901882 ns/op 95184 B/op 159 allocs/op -BenchmarkRSAVerifyPKCS1v15-2 1339 895115 ns/op 94976 B/op 154 allocs/op - -BenchmarkRSAGenerateKey1024PKCS1PKIX-2 93 20882099 ns/op 1048351 B/op 4090 allocs/op -BenchmarkRSAGenerateKey2048PKCS1PKIX-2 12 179918684 ns/op 3038840 B/op 8000 allocs/op -BenchmarkRSAGenerateKey4096PKCS1PKIX-2 1 3257891003 ns/op 15509888 B/op 25112 allocs/op -BenchmarkRSAGenerateKey1024PKCS8PKIX-2 51 22485111 ns/op 1148367 B/op 4450 allocs/op -BenchmarkRSAGenerateKey2048PKCS8PKIX-2 10 149631216 ns/op 2446195 B/op 6550 allocs/op -BenchmarkRSAGenerateKey4096PKCS8PKIX-2 1 1617318757 ns/op 7521280 B/op 12421 allocs/op -BenchmarkRSAGenerateKey1024PKCS1PKCS1-2 57 22467728 ns/op 1144077 B/op 4387 allocs/op -BenchmarkRSAGenerateKey2048PKCS1PKCS1-2 25 176744406 ns/op 2982597 B/op 7835 allocs/op -BenchmarkRSAGenerateKey4096PKCS1PKCS1-2 1 1577239084 ns/op 7272704 B/op 11896 allocs/op -BenchmarkRSAGenerateKey1024PKCS8PKCS1-2 61 20029226 ns/op 1000116 B/op 3942 allocs/op -BenchmarkRSAGenerateKey2048PKCS8PKCS1-2 21 178710269 ns/op 2998867 B/op 7896 allocs/op -BenchmarkRSAGenerateKey4096PKCS8PKCS1-2 1 2782582400 ns/op 13284080 B/op 21606 allocs/op +cpu: Intel(R) Xeon(R) CPU E5-26xx v4 + +BenchmarkHash_MD5-2 5402781 222.2 ns/op 32 B/op 2 allocs/op +BenchmarkHash_SHA1-2 2861774 428.6 ns/op 40 B/op 2 allocs/op +BenchmarkHash_SHA224-2 3223394 377.9 ns/op 48 B/op 2 allocs/op +BenchmarkHash_SHA256-2 3195488 391.1 ns/op 48 B/op 2 allocs/op +BenchmarkHash_SHA384-2 2543738 475.8 ns/op 64 B/op 2 allocs/op +BenchmarkHash_SHA512-2 2521041 484.7 ns/op 80 B/op 2 allocs/op +BenchmarkHash_CRC32IEEE-2 29772795 40.62 ns/op 0 B/op 0 allocs/op +BenchmarkHash_CRC64ISO-2 30194895 38.81 ns/op 0 B/op 0 allocs/op +BenchmarkHash_CRC64ECMA-2 30640298 38.88 ns/op 0 B/op 0 allocs/op +BenchmarkHash_Fnv32-2 76871449 14.81 ns/op 0 B/op 0 allocs/op +BenchmarkHash_Fnv32a-2 77299245 14.90 ns/op 0 B/op 0 allocs/op +BenchmarkHash_Fnv64-2 85886235 13.11 ns/op 0 B/op 0 allocs/op +BenchmarkHash_Fnv64a-2 88818280 13.59 ns/op 0 B/op 0 allocs/op +BenchmarkHash_Fnv128-2 7457739 166.9 ns/op 40 B/op 3 allocs/op +BenchmarkHash_Fnv128a-2 7051596 170.2 ns/op 40 B/op 3 allocs/op + +BenchmarkHMAC_MD5-2 1137166 1080 ns/op 448 B/op 7 allocs/op +BenchmarkHMAC_SHA1-2 534306 1941 ns/op 488 B/op 7 allocs/op +BenchmarkHMAC_SHA224-2 650616 1616 ns/op 528 B/op 7 allocs/op +BenchmarkHMAC_SHA256-2 741920 1643 ns/op 528 B/op 7 allocs/op +BenchmarkHMAC_SHA384-2 521118 2165 ns/op 864 B/op 7 allocs/op +BenchmarkHMAC_SHA512-2 501279 2147 ns/op 880 B/op 7 allocs/op + +BenchmarkDES_EncryptECB-2 228224 5109 ns/op 688 B/op 5 allocs/op +BenchmarkDES_EncryptCBC-2 201259 5450 ns/op 784 B/op 8 allocs/op +BenchmarkDES_EncryptCFB-2 219808 5234 ns/op 512 B/op 7 allocs/op +BenchmarkDES_EncryptOFB-2 101382 11784 ns/op 1016 B/op 7 allocs/op +BenchmarkDES_EncryptCTR-2 97594 12126 ns/op 1016 B/op 7 allocs/op +BenchmarkDES_DecryptECB-2 223354 4725 ns/op 304 B/op 3 allocs/op +BenchmarkDES_DecryptCBC-2 227923 5235 ns/op 400 B/op 6 allocs/op +BenchmarkDES_DecryptCFB-2 231697 4991 ns/op 384 B/op 6 allocs/op +BenchmarkDES_DecryptOFB-2 98470 11694 ns/op 888 B/op 6 allocs/op +BenchmarkDES_DecryptCTR-2 96957 12125 ns/op 888 B/op 6 allocs/op + +BenchmarkDES_EncryptTripleECB-2 89923 13391 ns/op 944 B/op 5 allocs/op +BenchmarkDES_EncryptTripleCBC-2 87915 13518 ns/op 1040 B/op 8 allocs/op +BenchmarkDES_EncryptTripleCFB-2 84099 13238 ns/op 768 B/op 7 allocs/op +BenchmarkDES_EncryptTripleOFB-2 39392 30196 ns/op 1272 B/op 7 allocs/op +BenchmarkDES_EncryptTripleCTR-2 39442 31233 ns/op 1272 B/op 7 allocs/op +BenchmarkDES_DecryptTripleECB-2 94470 12518 ns/op 560 B/op 3 allocs/op +BenchmarkDES_DecryptTripleCBC-2 92020 12995 ns/op 656 B/op 6 allocs/op +BenchmarkDES_DecryptTripleCFB-2 86136 12939 ns/op 640 B/op 6 allocs/op +BenchmarkDES_DecryptTripleOFB-2 39723 29703 ns/op 1144 B/op 6 allocs/op +BenchmarkDES_DecryptTripleCTR-2 33714 31441 ns/op 1144 B/op 6 allocs/op + +BenchmarkAES_EncryptECB-2 1441474 852.3 ns/op 1104 B/op 5 allocs/op +BenchmarkAES_EncryptCBC-2 906620 1127 ns/op 1616 B/op 6 allocs/op +BenchmarkAES_EncryptCFB-2 1198174 1040 ns/op 944 B/op 7 allocs/op +BenchmarkAES_EncryptOFB-2 542982 1909 ns/op 1440 B/op 7 allocs/op +BenchmarkAES_EncryptCTR-2 1678208 691.6 ns/op 1344 B/op 5 allocs/op +BenchmarkAES_EncryptGCM-2 1284980 940.5 ns/op 1616 B/op 5 allocs/op +BenchmarkAES_DecryptECB-2 1832341 670.6 ns/op 720 B/op 3 allocs/op +BenchmarkAES_DecryptCBC-2 1310142 860.3 ns/op 1232 B/op 4 allocs/op +BenchmarkAES_DecryptCFB-2 1206058 981.7 ns/op 816 B/op 6 allocs/op +BenchmarkAES_DecryptOFB-2 568182 1939 ns/op 1312 B/op 6 allocs/op +BenchmarkAES_DecryptCTR-2 1997515 602.6 ns/op 1216 B/op 4 allocs/op +BenchmarkAES_DecryptGCM-2 1393825 921.6 ns/op 1488 B/op 4 allocs/op + +BenchmarkRSA_EncryptPKCS1v15-2 22177 59262 ns/op 1568 B/op 11 allocs/op +BenchmarkRSA_EncryptOAEP-2 21324 57993 ns/op 1672 B/op 16 allocs/op +BenchmarkRSA_DecryptPKCS1v15-2 654 1808449 ns/op 448 B/op 3 allocs/op +BenchmarkRSA_DecryptPKCS1v15SessionKey-2 680 1762642 ns/op 448 B/op 3 allocs/op +BenchmarkRSA_DecryptOAEP-2 674 1727423 ns/op 552 B/op 8 allocs/op +BenchmarkRSA_SignPKCS1v15-2 663 1767622 ns/op 704 B/op 4 allocs/op +BenchmarkRSA_SignPSS-2 676 1893845 ns/op 1104 B/op 9 allocs/op +BenchmarkRSA_VerifyPKCS1v15-2 23022 50468 ns/op 1568 B/op 11 allocs/op +BenchmarkRSA_VerifyPSS-2 20220 56627 ns/op 1520 B/op 15 allocs/op + +BenchmarkRSA_GenerateKeys1024-2 60 21398224 ns/op 283350 B/op 2851 allocs/op +BenchmarkRSA_GenerateKeys2048-2 84 117753488 ns/op 600303 B/op 5459 allocs/op +BenchmarkRSA_GenerateKeys4096-2 1 1432974432 ns/op 2709912 B/op 14359 allocs/op ``` ### 🎨 Contributing diff --git a/README.md b/README.md index 2bfb969..a7c7fdd 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![Coverage](_icons/coverage.svg)](_icons/coverage.svg) ![Test](https://github.com/FishGoddess/cryptox/actions/workflows/test.yml/badge.svg) -**Cryptox** 是使用 Go 开发的安全套件,包括了常用的对称加密和非对称加密算法,还有散列算法、编解码常用算法等,主要是为了方便相关场景使用。 +**Cryptox** 是使用 Go 开发的安全套件,基于标准库封装了一些常用算法,主要为了方便使用。 [Read me in English](./README.en.md) @@ -16,9 +16,9 @@ * 支持 CRC/FNV 等散列算法。 * 支持 HMAC 混合基础的散列算法。 * 支持 DES/3DES/AES 等对称加密算法。 -* 支持 RSA/ECC 等非对称加密算法。 +* 支持 RSA 等非对称加密算法。 * 支持 ECB/CBC/OFB/CFB/CTR/GCM 等分组模式。 -* 支持 NONE/ZERO/PKCS5/PKCS7 等填充方式。 +* 支持 ZERO/PKCS5/PKCS7 等字节填充方式。 _历史版本的特性请查看 [HISTORY.md](./HISTORY.md)。未来版本的新特性和计划请查看 [FUTURE.md](./FUTURE.md)。_ @@ -28,7 +28,6 @@ _历史版本的特性请查看 [HISTORY.md](./HISTORY.md)。未来版本的新 $ go get -u github.com/FishGoddess/cryptox ``` -* [rand](_examples/rand.go) * [hash](_examples/hash.go) * [hmac](_examples/hmac.go) * [des](_examples/des.go) @@ -43,96 +42,84 @@ $ go get -u github.com/FishGoddess/cryptox $ make bench ``` -_注:数据为 128 字节,ecb/cbc 为 pkcs7 填充,cfb/ofb/ctr 为不填充。_ +_注:数据为 128 字节,ecb/cbc 为 pkcs7 填充,cfb/ofb/ctr/gcm 为不填充。_ ``` goos: linux goarch: amd64 -cpu: AMD EPYC 7K62 48-Core Processor - -BenchmarkGenerateBytes-2 11454386 102.6 ns/op 16 B/op 1 allocs/op -BenchmarkGenerateString-2 11392647 106.1 ns/op 16 B/op 1 allocs/op - -BenchmarkMD5-2 8933902 133.6 ns/op 0 B/op 0 allocs/op -BenchmarkSHA1-2 7997222 148.7 ns/op 0 B/op 0 allocs/op -BenchmarkSHA224-2 12905275 93.82 ns/op 0 B/op 0 allocs/op -BenchmarkSHA256-2 13305572 90.11 ns/op 0 B/op 0 allocs/op -BenchmarkSHA384-2 4501677 266.8 ns/op 0 B/op 0 allocs/op -BenchmarkSHA512-2 4457496 272.0 ns/op 0 B/op 0 allocs/op -BenchmarkCRC32IEEE-2 37977817 26.99 ns/op 0 B/op 0 allocs/op -BenchmarkCRC64ISO-2 49464211 23.77 ns/op 0 B/op 0 allocs/op -BenchmarkCRC64ECMA-2 47987559 23.85 ns/op 0 B/op 0 allocs/op -BenchmarkFnv32-2 88136509 13.91 ns/op 0 B/op 0 allocs/op -BenchmarkFnv32a-2 92693382 13.53 ns/op 0 B/op 0 allocs/op -BenchmarkFnv64-2 78227923 15.31 ns/op 0 B/op 0 allocs/op -BenchmarkFnv64a-2 79213070 15.35 ns/op 0 B/op 0 allocs/op -BenchmarkFnv128-2 15003325 77.31 ns/op 16 B/op 1 allocs/op -BenchmarkFnv128a-2 15473437 76.73 ns/op 16 B/op 1 allocs/op - -BenchmarkHMACUsingMD5-2 1418788 837.4 ns/op 432 B/op 6 allocs/op -BenchmarkHMACUsingSHA1-2 1304995 922.9 ns/op 472 B/op 6 allocs/op -BenchmarkHMACUsingSHA224-2 1879555 651.9 ns/op 512 B/op 6 allocs/op -BenchmarkHMACUsingSHA256-2 1880856 656.8 ns/op 512 B/op 6 allocs/op -BenchmarkHMACUsingSHA384-2 702820 1679 ns/op 848 B/op 6 allocs/op -BenchmarkHMACUsingSHA512-2 691454 1629 ns/op 864 B/op 6 allocs/op - -BenchmarkDESEncryptECB-2 321910 3601 ns/op 656 B/op 4 allocs/op -BenchmarkDESEncryptCBC-2 307196 3911 ns/op 752 B/op 7 allocs/op -BenchmarkDESEncryptCFB-2 319731 3689 ns/op 480 B/op 6 allocs/op -BenchmarkDESEncryptOFB-2 126572 9045 ns/op 984 B/op 6 allocs/op -BenchmarkDESEncryptCTR-2 125204 9471 ns/op 984 B/op 6 allocs/op -BenchmarkDESDecryptECB-2 340486 3461 ns/op 272 B/op 2 allocs/op -BenchmarkDESDecryptCBC-2 321182 3730 ns/op 368 B/op 5 allocs/op -BenchmarkDESDecryptCFB-2 329288 3607 ns/op 352 B/op 5 allocs/op -BenchmarkDESDecryptOFB-2 131550 9054 ns/op 856 B/op 5 allocs/op -BenchmarkDESDecryptCTR-2 125089 9539 ns/op 856 B/op 5 allocs/op - -BenchmarkDESEncryptECBTriple-2 124927 9474 ns/op 912 B/op 4 allocs/op -BenchmarkDESEncryptCBCTriple-2 121834 9701 ns/op 1008 B/op 7 allocs/op -BenchmarkDESEncryptCFBTriple-2 127020 9334 ns/op 736 B/op 6 allocs/op -BenchmarkDESEncryptOFBTriple-2 50922 23598 ns/op 1240 B/op 6 allocs/op -BenchmarkDESEncryptCTRTriple-2 49735 23873 ns/op 1240 B/op 6 allocs/op -BenchmarkDESDecryptECBTriple-2 128005 9328 ns/op 528 B/op 2 allocs/op -BenchmarkDESDecryptCBCTriple-2 122860 9558 ns/op 624 B/op 5 allocs/op -BenchmarkDESDecryptCFBTriple-2 126201 9334 ns/op 608 B/op 5 allocs/op -BenchmarkDESDecryptOFBTriple-2 50688 23291 ns/op 1112 B/op 5 allocs/op -BenchmarkDESDecryptCTRTriple-2 50391 23913 ns/op 1112 B/op 5 allocs/op - -BenchmarkAESEncryptECB-2 1960758 578.4 ns/op 1104 B/op 7 allocs/op -BenchmarkAESEncryptCBC-2 1413351 842.1 ns/op 1216 B/op 10 allocs/op -BenchmarkAESEncryptCFB-2 1657416 726.3 ns/op 944 B/op 9 allocs/op -BenchmarkAESEncryptOFB-2 924843 1206 ns/op 1440 B/op 9 allocs/op -BenchmarkAESEncryptCTR-2 808509 1381 ns/op 1440 B/op 9 allocs/op -BenchmarkAESEncryptGCM-2 1784593 661.2 ns/op 1168 B/op 7 allocs/op -BenchmarkAESDecryptECB-2 2590693 475.5 ns/op 720 B/op 5 allocs/op -BenchmarkAESDecryptCBC-2 1715998 711.6 ns/op 832 B/op 8 allocs/op -BenchmarkAESDecryptCFB-2 1809044 656.0 ns/op 816 B/op 8 allocs/op -BenchmarkAESDecryptOFB-2 956439 1152 ns/op 1312 B/op 8 allocs/op -BenchmarkAESDecryptCTR-2 841315 1348 ns/op 1312 B/op 8 allocs/op -BenchmarkAESDecryptGCM-2 1944147 633.5 ns/op 1040 B/op 6 allocs/op - -BenchmarkRSAEncryptPKCS1v15-2 1252 948252 ns/op 95488 B/op 155 allocs/op -BenchmarkRSAEncryptOAEP-2 1315 909433 ns/op 95592 B/op 160 allocs/op -BenchmarkRSADecryptPKCS1v15-2 166 7219740 ns/op 7600 B/op 16 allocs/op -BenchmarkRSADecryptPKCS1v15SessionKey-2 165 7154349 ns/op 7600 B/op 16 allocs/op -BenchmarkRSADecryptOAEP-2 166 7150228 ns/op 7704 B/op 21 allocs/op -BenchmarkRSASignPSS-2 154 7670215 ns/op 67168 B/op 100 allocs/op -BenchmarkRSASignPKCS1v15-2 156 7637694 ns/op 66992 B/op 95 allocs/op -BenchmarkRSAVerifyPSS-2 1308 901882 ns/op 95184 B/op 159 allocs/op -BenchmarkRSAVerifyPKCS1v15-2 1339 895115 ns/op 94976 B/op 154 allocs/op - -BenchmarkRSAGenerateKey1024PKCS1PKIX-2 93 20882099 ns/op 1048351 B/op 4090 allocs/op -BenchmarkRSAGenerateKey2048PKCS1PKIX-2 12 179918684 ns/op 3038840 B/op 8000 allocs/op -BenchmarkRSAGenerateKey4096PKCS1PKIX-2 1 3257891003 ns/op 15509888 B/op 25112 allocs/op -BenchmarkRSAGenerateKey1024PKCS8PKIX-2 51 22485111 ns/op 1148367 B/op 4450 allocs/op -BenchmarkRSAGenerateKey2048PKCS8PKIX-2 10 149631216 ns/op 2446195 B/op 6550 allocs/op -BenchmarkRSAGenerateKey4096PKCS8PKIX-2 1 1617318757 ns/op 7521280 B/op 12421 allocs/op -BenchmarkRSAGenerateKey1024PKCS1PKCS1-2 57 22467728 ns/op 1144077 B/op 4387 allocs/op -BenchmarkRSAGenerateKey2048PKCS1PKCS1-2 25 176744406 ns/op 2982597 B/op 7835 allocs/op -BenchmarkRSAGenerateKey4096PKCS1PKCS1-2 1 1577239084 ns/op 7272704 B/op 11896 allocs/op -BenchmarkRSAGenerateKey1024PKCS8PKCS1-2 61 20029226 ns/op 1000116 B/op 3942 allocs/op -BenchmarkRSAGenerateKey2048PKCS8PKCS1-2 21 178710269 ns/op 2998867 B/op 7896 allocs/op -BenchmarkRSAGenerateKey4096PKCS8PKCS1-2 1 2782582400 ns/op 13284080 B/op 21606 allocs/op +cpu: Intel(R) Xeon(R) CPU E5-26xx v4 + +BenchmarkHash_MD5-2 5402781 222.2 ns/op 32 B/op 2 allocs/op +BenchmarkHash_SHA1-2 2861774 428.6 ns/op 40 B/op 2 allocs/op +BenchmarkHash_SHA224-2 3223394 377.9 ns/op 48 B/op 2 allocs/op +BenchmarkHash_SHA256-2 3195488 391.1 ns/op 48 B/op 2 allocs/op +BenchmarkHash_SHA384-2 2543738 475.8 ns/op 64 B/op 2 allocs/op +BenchmarkHash_SHA512-2 2521041 484.7 ns/op 80 B/op 2 allocs/op +BenchmarkHash_CRC32IEEE-2 29772795 40.62 ns/op 0 B/op 0 allocs/op +BenchmarkHash_CRC64ISO-2 30194895 38.81 ns/op 0 B/op 0 allocs/op +BenchmarkHash_CRC64ECMA-2 30640298 38.88 ns/op 0 B/op 0 allocs/op +BenchmarkHash_Fnv32-2 76871449 14.81 ns/op 0 B/op 0 allocs/op +BenchmarkHash_Fnv32a-2 77299245 14.90 ns/op 0 B/op 0 allocs/op +BenchmarkHash_Fnv64-2 85886235 13.11 ns/op 0 B/op 0 allocs/op +BenchmarkHash_Fnv64a-2 88818280 13.59 ns/op 0 B/op 0 allocs/op +BenchmarkHash_Fnv128-2 7457739 166.9 ns/op 40 B/op 3 allocs/op +BenchmarkHash_Fnv128a-2 7051596 170.2 ns/op 40 B/op 3 allocs/op + +BenchmarkHMAC_MD5-2 1137166 1080 ns/op 448 B/op 7 allocs/op +BenchmarkHMAC_SHA1-2 534306 1941 ns/op 488 B/op 7 allocs/op +BenchmarkHMAC_SHA224-2 650616 1616 ns/op 528 B/op 7 allocs/op +BenchmarkHMAC_SHA256-2 741920 1643 ns/op 528 B/op 7 allocs/op +BenchmarkHMAC_SHA384-2 521118 2165 ns/op 864 B/op 7 allocs/op +BenchmarkHMAC_SHA512-2 501279 2147 ns/op 880 B/op 7 allocs/op + +BenchmarkDES_EncryptECB-2 228224 5109 ns/op 688 B/op 5 allocs/op +BenchmarkDES_EncryptCBC-2 201259 5450 ns/op 784 B/op 8 allocs/op +BenchmarkDES_EncryptCFB-2 219808 5234 ns/op 512 B/op 7 allocs/op +BenchmarkDES_EncryptOFB-2 101382 11784 ns/op 1016 B/op 7 allocs/op +BenchmarkDES_EncryptCTR-2 97594 12126 ns/op 1016 B/op 7 allocs/op +BenchmarkDES_DecryptECB-2 223354 4725 ns/op 304 B/op 3 allocs/op +BenchmarkDES_DecryptCBC-2 227923 5235 ns/op 400 B/op 6 allocs/op +BenchmarkDES_DecryptCFB-2 231697 4991 ns/op 384 B/op 6 allocs/op +BenchmarkDES_DecryptOFB-2 98470 11694 ns/op 888 B/op 6 allocs/op +BenchmarkDES_DecryptCTR-2 96957 12125 ns/op 888 B/op 6 allocs/op + +BenchmarkDES_EncryptTripleECB-2 89923 13391 ns/op 944 B/op 5 allocs/op +BenchmarkDES_EncryptTripleCBC-2 87915 13518 ns/op 1040 B/op 8 allocs/op +BenchmarkDES_EncryptTripleCFB-2 84099 13238 ns/op 768 B/op 7 allocs/op +BenchmarkDES_EncryptTripleOFB-2 39392 30196 ns/op 1272 B/op 7 allocs/op +BenchmarkDES_EncryptTripleCTR-2 39442 31233 ns/op 1272 B/op 7 allocs/op +BenchmarkDES_DecryptTripleECB-2 94470 12518 ns/op 560 B/op 3 allocs/op +BenchmarkDES_DecryptTripleCBC-2 92020 12995 ns/op 656 B/op 6 allocs/op +BenchmarkDES_DecryptTripleCFB-2 86136 12939 ns/op 640 B/op 6 allocs/op +BenchmarkDES_DecryptTripleOFB-2 39723 29703 ns/op 1144 B/op 6 allocs/op +BenchmarkDES_DecryptTripleCTR-2 33714 31441 ns/op 1144 B/op 6 allocs/op + +BenchmarkAES_EncryptECB-2 1441474 852.3 ns/op 1104 B/op 5 allocs/op +BenchmarkAES_EncryptCBC-2 906620 1127 ns/op 1616 B/op 6 allocs/op +BenchmarkAES_EncryptCFB-2 1198174 1040 ns/op 944 B/op 7 allocs/op +BenchmarkAES_EncryptOFB-2 542982 1909 ns/op 1440 B/op 7 allocs/op +BenchmarkAES_EncryptCTR-2 1678208 691.6 ns/op 1344 B/op 5 allocs/op +BenchmarkAES_EncryptGCM-2 1284980 940.5 ns/op 1616 B/op 5 allocs/op +BenchmarkAES_DecryptECB-2 1832341 670.6 ns/op 720 B/op 3 allocs/op +BenchmarkAES_DecryptCBC-2 1310142 860.3 ns/op 1232 B/op 4 allocs/op +BenchmarkAES_DecryptCFB-2 1206058 981.7 ns/op 816 B/op 6 allocs/op +BenchmarkAES_DecryptOFB-2 568182 1939 ns/op 1312 B/op 6 allocs/op +BenchmarkAES_DecryptCTR-2 1997515 602.6 ns/op 1216 B/op 4 allocs/op +BenchmarkAES_DecryptGCM-2 1393825 921.6 ns/op 1488 B/op 4 allocs/op + +BenchmarkRSA_EncryptPKCS1v15-2 22177 59262 ns/op 1568 B/op 11 allocs/op +BenchmarkRSA_EncryptOAEP-2 21324 57993 ns/op 1672 B/op 16 allocs/op +BenchmarkRSA_DecryptPKCS1v15-2 654 1808449 ns/op 448 B/op 3 allocs/op +BenchmarkRSA_DecryptPKCS1v15SessionKey-2 680 1762642 ns/op 448 B/op 3 allocs/op +BenchmarkRSA_DecryptOAEP-2 674 1727423 ns/op 552 B/op 8 allocs/op +BenchmarkRSA_SignPKCS1v15-2 663 1767622 ns/op 704 B/op 4 allocs/op +BenchmarkRSA_SignPSS-2 676 1893845 ns/op 1104 B/op 9 allocs/op +BenchmarkRSA_VerifyPKCS1v15-2 23022 50468 ns/op 1568 B/op 11 allocs/op +BenchmarkRSA_VerifyPSS-2 20220 56627 ns/op 1520 B/op 15 allocs/op + +BenchmarkRSA_GenerateKeys1024-2 60 21398224 ns/op 283350 B/op 2851 allocs/op +BenchmarkRSA_GenerateKeys2048-2 84 117753488 ns/op 600303 B/op 5459 allocs/op +BenchmarkRSA_GenerateKeys4096-2 1 1432974432 ns/op 2709912 B/op 14359 allocs/op ``` ### 🎨 贡献者 diff --git a/_examples/aes.go b/_examples/aes.go index a8c4a55..cb62b2f 100644 --- a/_examples/aes.go +++ b/_examples/aes.go @@ -8,7 +8,6 @@ import ( "bytes" "fmt" - "github.com/FishGoddess/cryptox" "github.com/FishGoddess/cryptox/aes" ) @@ -16,31 +15,25 @@ func main() { // As you know, key is necessary in aes. // However, not all modes need iv, such as ecb. key := []byte("12345678876543211234567887654321") - iv := []byte("8765432112345678") + nonce := []byte("123456abcdef") - msg := []byte("你好,世界") - fmt.Printf("msg: %s\n", msg) + data := []byte("你好,世界") + fmt.Printf("data: %s\n", data) - // We use ctr mode and no padding to encrypt data. - // Of course, you can choose another mode if you want. - // Also, you can choose no/zero/pkcs5/pkcs7 to padding data. - encrypted, err := aes.EncryptCTR(key, iv, cryptox.PaddingNone, msg) + // Use gcm mode to encrypt data with no padding and encoding base64. + encrypt, err := aes.EncryptGCM(data, key, nonce, aes.WithBase64()) if err != nil { panic(err) } - fmt.Println("encrypted:", encrypted) - fmt.Println("encrypted hex:", encrypted.Hex()) - fmt.Println("encrypted base64:", encrypted.Base64()) + fmt.Printf("encrypt: %s\n", encrypt) - // We use ctr mode and no padding to decrypt data. - // Of course, you can choose another mode if you want. - // Also, you can choose no/zero/pkcs5/pkcs7 to undo padding data. - decrypted, err := aes.DecryptCTR(key, iv, cryptox.PaddingNone, encrypted) + // Decrypt data in the same way. + decrypt, err := aes.DecryptGCM(encrypt, key, nonce, aes.WithBase64()) if err != nil { panic(err) } - fmt.Printf("decrypted: %s\n", decrypted) - fmt.Println("decrypted == msg", bytes.Equal(decrypted, msg)) + fmt.Printf("decrypt: %s\n", decrypt) + fmt.Printf("decrypt is right: %+v\n", bytes.Equal(decrypt, data)) } diff --git a/_examples/aes_test.go b/_examples/aes_test.go index e48e9f2..de55fca 100644 --- a/_examples/aes_test.go +++ b/_examples/aes_test.go @@ -7,7 +7,6 @@ package main import ( "testing" - "github.com/FishGoddess/cryptox" "github.com/FishGoddess/cryptox/aes" ) @@ -15,90 +14,90 @@ var ( aesBenchKey = []byte("12345678876543211234567887654321") aesBenchIV = []byte("8765432112345678") aesBenchNonce = []byte("123456abcdef") - aesBenchMsg = cryptox.GenerateBytes(128) + aesBenchMsg = make([]byte, 128) ) -// go test -v -bench=^BenchmarkAESEncryptECB$ -benchtime=1s aes_test.go -func BenchmarkAESEncryptECB(b *testing.B) { +// go test -v -bench=^BenchmarkAES_EncryptECB$ -benchtime=1s des_test.go +func BenchmarkAES_EncryptECB(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := aes.EncryptECB(aesBenchKey, cryptox.PaddingPKCS7, aesBenchMsg) + _, err := aes.EncryptECB(aesBenchMsg, aesBenchKey, aes.WithPKCS7()) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkAESEncryptCBC$ -benchtime=1s aes_test.go -func BenchmarkAESEncryptCBC(b *testing.B) { +// go test -v -bench=^BenchmarkAES_EncryptCBC$ -benchtime=1s des_test.go +func BenchmarkAES_EncryptCBC(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := aes.EncryptCBC(aesBenchKey, aesBenchIV, cryptox.PaddingPKCS7, aesBenchMsg) + _, err := aes.EncryptCBC(aesBenchMsg, aesBenchKey, aesBenchIV, aes.WithPKCS7()) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkAESEncryptCFB$ -benchtime=1s aes_test.go -func BenchmarkAESEncryptCFB(b *testing.B) { +// go test -v -bench=^BenchmarkAES_EncryptCFB$ -benchtime=1s des_test.go +func BenchmarkAES_EncryptCFB(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := aes.EncryptCFB(aesBenchKey, aesBenchIV, cryptox.PaddingNone, aesBenchMsg) + _, err := aes.EncryptCFB(aesBenchMsg, aesBenchKey, aesBenchIV) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkAESEncryptOFB$ -benchtime=1s aes_test.go -func BenchmarkAESEncryptOFB(b *testing.B) { +// go test -v -bench=^BenchmarkAES_EncryptOFB$ -benchtime=1s des_test.go +func BenchmarkAES_EncryptOFB(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := aes.EncryptOFB(aesBenchKey, aesBenchIV, cryptox.PaddingNone, aesBenchMsg) + _, err := aes.EncryptOFB(aesBenchMsg, aesBenchKey, aesBenchIV) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkAESEncryptCTR$ -benchtime=1s aes_test.go -func BenchmarkAESEncryptCTR(b *testing.B) { +// go test -v -bench=^BenchmarkAES_EncryptCTR$ -benchtime=1s des_test.go +func BenchmarkAES_EncryptCTR(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := aes.EncryptCTR(aesBenchKey, aesBenchIV, cryptox.PaddingNone, aesBenchMsg) + _, err := aes.EncryptCTR(aesBenchMsg, aesBenchKey, aesBenchIV) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkAESEncryptGCM$ -benchtime=1s aes_test.go -func BenchmarkAESEncryptGCM(b *testing.B) { +// go test -v -bench=^BenchmarkAES_EncryptGCM$ -benchtime=1s aes_test.go +func BenchmarkAES_EncryptGCM(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := aes.EncryptGCM(aesBenchKey, aesBenchNonce, nil, aesBenchMsg) + _, err := aes.EncryptGCM(aesBenchMsg, aesBenchKey, aesBenchNonce) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkAESDecryptECB$ -benchtime=1s aes_test.go -func BenchmarkAESDecryptECB(b *testing.B) { - encrypted, err := aes.EncryptECB(aesBenchKey, cryptox.PaddingPKCS7, aesBenchMsg) +// go test -v -bench=^BenchmarkAES_DecryptECB$ -benchtime=1s des_test.go +func BenchmarkAES_DecryptECB(b *testing.B) { + encrypt, err := aes.EncryptECB(aesBenchMsg, aesBenchKey, aes.WithPKCS7()) if err != nil { b.Fatal(err) } @@ -107,16 +106,16 @@ func BenchmarkAESDecryptECB(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := aes.DecryptECB(aesBenchKey, cryptox.PaddingPKCS7, encrypted) + _, err := aes.DecryptECB(encrypt, aesBenchKey, aes.WithPKCS7()) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkAESDecryptCBC$ -benchtime=1s aes_test.go -func BenchmarkAESDecryptCBC(b *testing.B) { - encrypted, err := aes.EncryptCBC(aesBenchKey, aesBenchIV, cryptox.PaddingPKCS7, aesBenchMsg) +// go test -v -bench=^BenchmarkAES_DecryptCBC$ -benchtime=1s des_test.go +func BenchmarkAES_DecryptCBC(b *testing.B) { + encrypt, err := aes.EncryptCBC(aesBenchMsg, aesBenchKey, aesBenchIV, aes.WithPKCS7()) if err != nil { b.Fatal(err) } @@ -125,16 +124,16 @@ func BenchmarkAESDecryptCBC(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := aes.DecryptCBC(aesBenchKey, aesBenchIV, cryptox.PaddingPKCS7, encrypted) + _, err := aes.DecryptCBC(encrypt, aesBenchKey, aesBenchIV, aes.WithPKCS7()) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkAESDecryptCFB$ -benchtime=1s aes_test.go -func BenchmarkAESDecryptCFB(b *testing.B) { - encrypted, err := aes.EncryptCFB(aesBenchKey, aesBenchIV, cryptox.PaddingNone, aesBenchMsg) +// go test -v -bench=^BenchmarkAES_DecryptCFB$ -benchtime=1s des_test.go +func BenchmarkAES_DecryptCFB(b *testing.B) { + encrypt, err := aes.EncryptCFB(aesBenchMsg, aesBenchKey, aesBenchIV) if err != nil { b.Fatal(err) } @@ -143,16 +142,16 @@ func BenchmarkAESDecryptCFB(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := aes.DecryptCFB(aesBenchKey, aesBenchIV, cryptox.PaddingNone, encrypted) + _, err := aes.DecryptCFB(encrypt, aesBenchKey, aesBenchIV) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkAESDecryptOFB$ -benchtime=1s aes_test.go -func BenchmarkAESDecryptOFB(b *testing.B) { - encrypted, err := aes.EncryptOFB(aesBenchKey, aesBenchIV, cryptox.PaddingNone, aesBenchMsg) +// go test -v -bench=^BenchmarkAES_DecryptOFB$ -benchtime=1s des_test.go +func BenchmarkAES_DecryptOFB(b *testing.B) { + encrypt, err := aes.EncryptOFB(aesBenchMsg, aesBenchKey, aesBenchIV) if err != nil { b.Fatal(err) } @@ -161,16 +160,16 @@ func BenchmarkAESDecryptOFB(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := aes.DecryptOFB(aesBenchKey, aesBenchIV, cryptox.PaddingNone, encrypted) + _, err := aes.DecryptOFB(encrypt, aesBenchKey, aesBenchIV) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkAESDecryptCTR$ -benchtime=1s aes_test.go -func BenchmarkAESDecryptCTR(b *testing.B) { - encrypted, err := aes.EncryptCTR(aesBenchKey, aesBenchIV, cryptox.PaddingNone, aesBenchMsg) +// go test -v -bench=^BenchmarkAES_DecryptCTR$ -benchtime=1s des_test.go +func BenchmarkAES_DecryptCTR(b *testing.B) { + encrypt, err := aes.EncryptCTR(aesBenchMsg, aesBenchKey, aesBenchIV) if err != nil { b.Fatal(err) } @@ -179,16 +178,16 @@ func BenchmarkAESDecryptCTR(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := aes.DecryptCTR(aesBenchKey, aesBenchIV, cryptox.PaddingNone, encrypted) + _, err := aes.DecryptCTR(encrypt, aesBenchKey, aesBenchIV) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkAESDecryptGCM$ -benchtime=1s aes_test.go -func BenchmarkAESDecryptGCM(b *testing.B) { - encrypted, err := aes.EncryptGCM(aesBenchKey, aesBenchNonce, nil, aesBenchMsg) +// go test -v -bench=^BenchmarkAES_DecryptGCM$ -benchtime=1s aes_test.go +func BenchmarkAES_DecryptGCM(b *testing.B) { + encrypt, err := aes.EncryptGCM(aesBenchMsg, aesBenchKey, aesBenchNonce) if err != nil { b.Fatal(err) } @@ -197,7 +196,7 @@ func BenchmarkAESDecryptGCM(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := aes.DecryptGCM(aesBenchKey, aesBenchNonce, nil, encrypted) + _, err := aes.DecryptGCM(encrypt, aesBenchKey, aesBenchNonce) if err != nil { b.Fatal(err) } diff --git a/_examples/des.go b/_examples/des.go index bdd98a3..7cab5b3 100644 --- a/_examples/des.go +++ b/_examples/des.go @@ -8,7 +8,6 @@ import ( "bytes" "fmt" - "github.com/FishGoddess/cryptox" "github.com/FishGoddess/cryptox/des" ) @@ -18,29 +17,23 @@ func main() { key := []byte("12345678") iv := []byte("87654321") - msg := []byte("你好,世界") - fmt.Printf("msg: %s\n", msg) + data := []byte("你好,世界") + fmt.Printf("data: %s\n", data) - // We use ctr mode and no padding to encrypt data. - // Of course, you can choose another mode if you want. - // Also, you can choose no/zero/pkcs5/pkcs7 to padding data. - encrypted, err := des.EncryptCTR(key, iv, cryptox.PaddingNone, msg) + // Use ctr mode to encrypt data with no padding and encoding base64. + encrypt, err := des.EncryptCTR(data, key, iv, des.WithBase64()) if err != nil { panic(err) } - fmt.Println("encrypted:", encrypted) - fmt.Println("encrypted hex:", encrypted.Hex()) - fmt.Println("encrypted base64:", encrypted.Base64()) + fmt.Printf("encrypt: %s\n", encrypt) - // We use ctr mode and no padding to decrypt data. - // Of course, you can choose another mode if you want. - // Also, you can choose no/zero/pkcs5/pkcs7 to undo padding data. - decrypted, err := des.DecryptCTR(key, iv, cryptox.PaddingNone, encrypted) + // Decrypt data in the same way. + decrypt, err := des.DecryptCTR(encrypt, key, iv, des.WithBase64()) if err != nil { panic(err) } - fmt.Printf("decrypted: %s\n", decrypted) - fmt.Println("decrypted == msg", bytes.Equal(decrypted, msg)) + fmt.Printf("decrypt: %s\n", decrypt) + fmt.Printf("decrypt is right: %+v\n", bytes.Equal(decrypt, data)) } diff --git a/_examples/des_test.go b/_examples/des_test.go index 6d60773..cd82709 100644 --- a/_examples/des_test.go +++ b/_examples/des_test.go @@ -7,7 +7,6 @@ package main import ( "testing" - "github.com/FishGoddess/cryptox" "github.com/FishGoddess/cryptox/des" ) @@ -17,74 +16,74 @@ var ( desBenchMsg = make([]byte, 128) ) -// go test -v -bench=^BenchmarkDESEncryptECB$ -benchtime=1s des_test.go -func BenchmarkDESEncryptECB(b *testing.B) { +// go test -v -bench=^BenchmarkDES_EncryptECB$ -benchtime=1s des_test.go +func BenchmarkDES_EncryptECB(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := des.EncryptECB(desBenchKey, cryptox.PaddingPKCS7, desBenchMsg) + _, err := des.EncryptECB(desBenchMsg, desBenchKey, des.WithPKCS7()) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkDESEncryptCBC$ -benchtime=1s des_test.go -func BenchmarkDESEncryptCBC(b *testing.B) { +// go test -v -bench=^BenchmarkDES_EncryptCBC$ -benchtime=1s des_test.go +func BenchmarkDES_EncryptCBC(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := des.EncryptCBC(desBenchKey, desBenchIV, cryptox.PaddingPKCS7, desBenchMsg) + _, err := des.EncryptCBC(desBenchMsg, desBenchKey, desBenchIV, des.WithPKCS7()) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkDESEncryptCFB$ -benchtime=1s des_test.go -func BenchmarkDESEncryptCFB(b *testing.B) { +// go test -v -bench=^BenchmarkDES_EncryptCFB$ -benchtime=1s des_test.go +func BenchmarkDES_EncryptCFB(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := des.EncryptCFB(desBenchKey, desBenchIV, cryptox.PaddingNone, desBenchMsg) + _, err := des.EncryptCFB(desBenchMsg, desBenchKey, desBenchIV) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkDESEncryptOFB$ -benchtime=1s des_test.go -func BenchmarkDESEncryptOFB(b *testing.B) { +// go test -v -bench=^BenchmarkDES_EncryptOFB$ -benchtime=1s des_test.go +func BenchmarkDES_EncryptOFB(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := des.EncryptOFB(desBenchKey, desBenchIV, cryptox.PaddingNone, desBenchMsg) + _, err := des.EncryptOFB(desBenchMsg, desBenchKey, desBenchIV) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkDESEncryptCTR$ -benchtime=1s des_test.go -func BenchmarkDESEncryptCTR(b *testing.B) { +// go test -v -bench=^BenchmarkDES_EncryptCTR$ -benchtime=1s des_test.go +func BenchmarkDES_EncryptCTR(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := des.EncryptCTR(desBenchKey, desBenchIV, cryptox.PaddingNone, desBenchMsg) + _, err := des.EncryptCTR(desBenchMsg, desBenchKey, desBenchIV) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkDESDecryptECB$ -benchtime=1s des_test.go -func BenchmarkDESDecryptECB(b *testing.B) { - encrypted, err := des.EncryptECB(desBenchKey, cryptox.PaddingPKCS7, desBenchMsg) +// go test -v -bench=^BenchmarkDES_DecryptECB$ -benchtime=1s des_test.go +func BenchmarkDES_DecryptECB(b *testing.B) { + encrypt, err := des.EncryptECB(desBenchMsg, desBenchKey, des.WithPKCS7()) if err != nil { b.Fatal(err) } @@ -93,16 +92,16 @@ func BenchmarkDESDecryptECB(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := des.DecryptECB(desBenchKey, cryptox.PaddingPKCS7, encrypted) + _, err := des.DecryptECB(encrypt, desBenchKey, des.WithPKCS7()) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkDESDecryptCBC$ -benchtime=1s des_test.go -func BenchmarkDESDecryptCBC(b *testing.B) { - encrypted, err := des.EncryptCBC(desBenchKey, desBenchIV, cryptox.PaddingPKCS7, desBenchMsg) +// go test -v -bench=^BenchmarkDES_DecryptCBC$ -benchtime=1s des_test.go +func BenchmarkDES_DecryptCBC(b *testing.B) { + encrypt, err := des.EncryptCBC(desBenchMsg, desBenchKey, desBenchIV, des.WithPKCS7()) if err != nil { b.Fatal(err) } @@ -111,16 +110,16 @@ func BenchmarkDESDecryptCBC(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := des.DecryptCBC(desBenchKey, desBenchIV, cryptox.PaddingPKCS7, encrypted) + _, err := des.DecryptCBC(encrypt, desBenchKey, desBenchIV, des.WithPKCS7()) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkDESDecryptCFB$ -benchtime=1s des_test.go -func BenchmarkDESDecryptCFB(b *testing.B) { - encrypted, err := des.EncryptCFB(desBenchKey, desBenchIV, cryptox.PaddingNone, desBenchMsg) +// go test -v -bench=^BenchmarkDES_DecryptCFB$ -benchtime=1s des_test.go +func BenchmarkDES_DecryptCFB(b *testing.B) { + encrypt, err := des.EncryptCFB(desBenchMsg, desBenchKey, desBenchIV) if err != nil { b.Fatal(err) } @@ -129,16 +128,16 @@ func BenchmarkDESDecryptCFB(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := des.DecryptCFB(desBenchKey, desBenchIV, cryptox.PaddingNone, encrypted) + _, err := des.DecryptCFB(encrypt, desBenchKey, desBenchIV) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkDESDecryptOFB$ -benchtime=1s des_test.go -func BenchmarkDESDecryptOFB(b *testing.B) { - encrypted, err := des.EncryptOFB(desBenchKey, desBenchIV, cryptox.PaddingNone, desBenchMsg) +// go test -v -bench=^BenchmarkDES_DecryptOFB$ -benchtime=1s des_test.go +func BenchmarkDES_DecryptOFB(b *testing.B) { + encrypt, err := des.EncryptOFB(desBenchMsg, desBenchKey, desBenchIV) if err != nil { b.Fatal(err) } @@ -147,16 +146,16 @@ func BenchmarkDESDecryptOFB(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := des.DecryptOFB(desBenchKey, desBenchIV, cryptox.PaddingNone, encrypted) + _, err := des.DecryptOFB(encrypt, desBenchKey, desBenchIV) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkDESDecryptCTR$ -benchtime=1s des_test.go -func BenchmarkDESDecryptCTR(b *testing.B) { - encrypted, err := des.EncryptCTR(desBenchKey, desBenchIV, cryptox.PaddingNone, desBenchMsg) +// go test -v -bench=^BenchmarkDES_DecryptCTR$ -benchtime=1s des_test.go +func BenchmarkDES_DecryptCTR(b *testing.B) { + encrypt, err := des.EncryptCTR(desBenchMsg, desBenchKey, desBenchIV) if err != nil { b.Fatal(err) } @@ -165,7 +164,7 @@ func BenchmarkDESDecryptCTR(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := des.DecryptCTR(desBenchKey, desBenchIV, cryptox.PaddingNone, encrypted) + _, err := des.DecryptCTR(encrypt, desBenchKey, desBenchIV) if err != nil { b.Fatal(err) } diff --git a/_examples/hash.go b/_examples/hash.go index 7836f40..0530bbe 100644 --- a/_examples/hash.go +++ b/_examples/hash.go @@ -12,60 +12,82 @@ import ( func main() { data := []byte("你好,世界") - fmt.Println("data:", data) - - // All hashing functions will return a cryptox.Bytes type which can be encoded to hex and base64. + fmt.Printf("data: %s\n", data) md5 := hash.MD5(data) - fmt.Println("md5 hex:", md5.Hex()) - fmt.Println("md5 base64:", md5.Base64()) + md5Hex := hash.MD5(data, hash.WithHex()) + md5Base64 := hash.MD5(data, hash.WithBase64()) + fmt.Printf("md5: %s\n", md5) + fmt.Printf("md5 hex: %s\n", md5Hex) + fmt.Printf("md5 base64: %s\n", md5Base64) sha1 := hash.SHA1(data) - fmt.Println("sha1 hex:", sha1.Hex()) - fmt.Println("sha1 base64:", sha1.Base64()) + sha1Hex := hash.SHA1(data, hash.WithHex()) + sha1Base64 := hash.SHA1(data, hash.WithBase64()) + fmt.Printf("sha1: %s\n", sha1) + fmt.Printf("sha1 hex: %s\n", sha1Hex) + fmt.Printf("sha1 base64: %s\n", sha1Base64) sha224 := hash.SHA224(data) - fmt.Println("sha224 hex:", sha224.Hex()) - fmt.Println("sha224 base64:", sha224.Base64()) + sha224Hex := hash.SHA224(data, hash.WithHex()) + sha224Base64 := hash.SHA224(data, hash.WithBase64()) + fmt.Printf("sha224: %s\n", sha224) + fmt.Printf("sha224 hex: %s\n", sha224Hex) + fmt.Printf("sha224 base64: %s\n", sha224Base64) sha256 := hash.SHA256(data) - fmt.Println("sha256 hex:", sha256.Hex()) - fmt.Println("sha256 base64:", sha256.Base64()) + sha256Hex := hash.SHA256(data, hash.WithHex()) + sha256Base64 := hash.SHA256(data, hash.WithBase64()) + fmt.Printf("sha256: %s\n", sha256) + fmt.Printf("sha256 hex: %s\n", sha256Hex) + fmt.Printf("sha256 base64: %s\n", sha256Base64) sha384 := hash.SHA384(data) - fmt.Println("sha384 hex:", sha384.Hex()) - fmt.Println("sha384 base64:", sha384.Base64()) + sha384Hex := hash.SHA384(data, hash.WithHex()) + sha384Base64 := hash.SHA384(data, hash.WithBase64()) + fmt.Printf("sha384: %s\n", sha384) + fmt.Printf("sha384 hex: %s\n", sha384Hex) + fmt.Printf("sha384 base64: %s\n", sha384Base64) sha512 := hash.SHA512(data) - fmt.Println("sha512 hex:", sha512.Hex()) - fmt.Println("sha512 base64:", sha512.Base64()) + sha512Hex := hash.SHA512(data, hash.WithHex()) + sha512Base64 := hash.SHA512(data, hash.WithBase64()) + fmt.Printf("sha512: %s\n", sha512) + fmt.Printf("sha512 hex: %s\n", sha512Hex) + fmt.Printf("sha512 base64: %s\n", sha512Base64) crc32 := hash.CRC32IEEE(data) - fmt.Printf("crc32 with ieee: %d\n", crc32) + fmt.Printf("crc32 ieee: %d\n", crc32) crc64 := hash.CRC64ISO(data) - fmt.Printf("crc64 with iso: %d\n", crc64) + fmt.Printf("crc64 iso: %d\n", crc64) crc64 = hash.CRC64ECMA(data) - fmt.Printf("crc64 with ecma: %d\n", crc64) + fmt.Printf("crc64 ecma: %d\n", crc64) fnv32 := hash.Fnv32(data) - fmt.Printf("fnv-1/32: %d\n", fnv32) + fmt.Printf("fnv32: %d\n", fnv32) fnv32a := hash.Fnv32a(data) - fmt.Printf("fnv-1/32a: %d\n", fnv32a) + fmt.Printf("fnv32a: %d\n", fnv32a) fnv64 := hash.Fnv64(data) - fmt.Printf("fnv-1/64: %d\n", fnv64) + fmt.Printf("fnv64: %d\n", fnv64) fnv64a := hash.Fnv64a(data) - fmt.Printf("fnv-1/64a: %d\n", fnv64a) + fmt.Printf("fnv64a: %d\n", fnv64a) fnv128 := hash.Fnv128(data) - fmt.Printf("fnv-1/128 hex: %s\n", fnv128.Hex()) - fmt.Printf("fnv-1/128 base64: %s\n", fnv128.Base64()) + fnv128Hex := hash.Fnv128(data, hash.WithHex()) + fnv128Base64 := hash.Fnv128(data, hash.WithBase64()) + fmt.Printf("fnv128: %s\n", fnv128) + fmt.Printf("fnv128 hex: %s\n", fnv128Hex) + fmt.Printf("fnv128 base64: %s\n", fnv128Base64) fnv128a := hash.Fnv128a(data) - fmt.Printf("fnv-1/128a hex: %s\n", fnv128a.Hex()) - fmt.Printf("fnv-1/128a base64: %s\n", fnv128a.Base64()) + fnv128aHex := hash.Fnv128a(data, hash.WithHex()) + fnv128aBase64 := hash.Fnv128a(data, hash.WithBase64()) + fmt.Printf("fnv128a: %s\n", fnv128a) + fmt.Printf("fnv128a hex: %s\n", fnv128aHex) + fmt.Printf("fnv128a base64: %s\n", fnv128aBase64) } diff --git a/_examples/hash_test.go b/_examples/hash_test.go index 689f44e..d6addb3 100644 --- a/_examples/hash_test.go +++ b/_examples/hash_test.go @@ -11,155 +11,155 @@ import ( ) var ( - benchData = []byte("你好,世界") + hashBenchData = []byte("你好,世界") ) -// go test -v -bench=^BenchmarkMD5$ -benchtime=1s hash_test.go -func BenchmarkMD5(b *testing.B) { +// go test -v -bench=^BenchmarkHash_MD5$ -benchtime=1s hash_test.go +func BenchmarkHash_MD5(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - hash.MD5(benchData) + hash.MD5(hashBenchData) } } -// go test -v -bench=^BenchmarkSHA1$ -benchtime=1s hash_test.go -func BenchmarkSHA1(b *testing.B) { +// go test -v -bench=^BenchmarkHash_SHA1$ -benchtime=1s hash_test.go +func BenchmarkHash_SHA1(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - hash.SHA1(benchData) + hash.SHA1(hashBenchData) } } -// go test -v -bench=^BenchmarkSHA224$ -benchtime=1s hash_test.go -func BenchmarkSHA224(b *testing.B) { +// go test -v -bench=^BenchmarkHash_SHA224$ -benchtime=1s hash_test.go +func BenchmarkHash_SHA224(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - hash.SHA224(benchData) + hash.SHA224(hashBenchData) } } -// go test -v -bench=^BenchmarkSHA256$ -benchtime=1s hash_test.go -func BenchmarkSHA256(b *testing.B) { +// go test -v -bench=^BenchmarkHash_SHA256$ -benchtime=1s hash_test.go +func BenchmarkHash_SHA256(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - hash.SHA256(benchData) + hash.SHA256(hashBenchData) } } -// go test -v -bench=^BenchmarkSHA384$ -benchtime=1s hash_test.go -func BenchmarkSHA384(b *testing.B) { +// go test -v -bench=^BenchmarkHash_SHA384$ -benchtime=1s hash_test.go +func BenchmarkHash_SHA384(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - hash.SHA384(benchData) + hash.SHA384(hashBenchData) } } -// go test -v -bench=^BenchmarkSHA512$ -benchtime=1s hash_test.go -func BenchmarkSHA512(b *testing.B) { +// go test -v -bench=^BenchmarkHash_SHA512$ -benchtime=1s hash_test.go +func BenchmarkHash_SHA512(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - hash.SHA512(benchData) + hash.SHA512(hashBenchData) } } -// go test -v -bench=^BenchmarkCRC32IEEE$ -benchtime=1s hash_test.go -func BenchmarkCRC32IEEE(b *testing.B) { +// go test -v -bench=^BenchmarkHash_CRC32IEEE$ -benchtime=1s hash_test.go +func BenchmarkHash_CRC32IEEE(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - hash.CRC32IEEE(benchData) + hash.CRC32IEEE(hashBenchData) } } -// go test -v -bench=^BenchmarkCRC64ISO$ -benchtime=1s hash_test.go -func BenchmarkCRC64ISO(b *testing.B) { +// go test -v -bench=^BenchmarkHash_CRC64ISO$ -benchtime=1s hash_test.go +func BenchmarkHash_CRC64ISO(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - hash.CRC64ISO(benchData) + hash.CRC64ISO(hashBenchData) } } -// go test -v -bench=^BenchmarkCRC64ECMA$ -benchtime=1s hash_test.go -func BenchmarkCRC64ECMA(b *testing.B) { +// go test -v -bench=^BenchmarkHash_CRC64ECMA$ -benchtime=1s hash_test.go +func BenchmarkHash_CRC64ECMA(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - hash.CRC64ECMA(benchData) + hash.CRC64ECMA(hashBenchData) } } -// go test -v -bench=^BenchmarkFnv32$ -benchtime=1s hash_test.go -func BenchmarkFnv32(b *testing.B) { +// go test -v -bench=^BenchmarkHash_Fnv32$ -benchtime=1s hash_test.go +func BenchmarkHash_Fnv32(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - hash.Fnv32(benchData) + hash.Fnv32(hashBenchData) } } -// go test -v -bench=^BenchmarkFnv32a$ -benchtime=1s hash_test.go -func BenchmarkFnv32a(b *testing.B) { +// go test -v -bench=^BenchmarkHash_Fnv32a$ -benchtime=1s hash_test.go +func BenchmarkHash_Fnv32a(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - hash.Fnv32a(benchData) + hash.Fnv32a(hashBenchData) } } -// go test -v -bench=^BenchmarkFnv64$ -benchtime=1s hash_test.go -func BenchmarkFnv64(b *testing.B) { +// go test -v -bench=^BenchmarkHash_Fnv64$ -benchtime=1s hash_test.go +func BenchmarkHash_Fnv64(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - hash.Fnv64(benchData) + hash.Fnv64(hashBenchData) } } -// go test -v -bench=^BenchmarkFnv64a$ -benchtime=1s hash_test.go -func BenchmarkFnv64a(b *testing.B) { +// go test -v -bench=^BenchmarkHash_Fnv64a$ -benchtime=1s hash_test.go +func BenchmarkHash_Fnv64a(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - hash.Fnv64a(benchData) + hash.Fnv64a(hashBenchData) } } -// go test -v -bench=^BenchmarkFnv128$ -benchtime=1s hash_test.go -func BenchmarkFnv128(b *testing.B) { +// go test -v -bench=^BenchmarkHash_Fnv128$ -benchtime=1s hash_test.go +func BenchmarkHash_Fnv128(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - hash.Fnv128(benchData) + hash.Fnv128(hashBenchData) } } -// go test -v -bench=^BenchmarkFnv128a$ -benchtime=1s hash_test.go -func BenchmarkFnv128a(b *testing.B) { +// go test -v -bench=^BenchmarkHash_Fnv128a$ -benchtime=1s hash_test.go +func BenchmarkHash_Fnv128a(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - hash.Fnv128a(benchData) + hash.Fnv128a(hashBenchData) } } diff --git a/_examples/hmac.go b/_examples/hmac.go index 6b8460f..b42b7cd 100644 --- a/_examples/hmac.go +++ b/_examples/hmac.go @@ -11,33 +11,51 @@ import ( ) func main() { - key := []byte("12345678") - fmt.Println("key:", key) - data := []byte("你好,世界") - fmt.Println("data:", data) - - md5, _ := hmac.MD5(key, data) - fmt.Println("hmac md5 hex:", md5.Hex()) - fmt.Println("hmac md5 base64:", md5.Base64()) - - sha1, _ := hmac.SHA1(key, data) - fmt.Println("hmac sha1 hex:", sha1.Hex()) - fmt.Println("hmac sha1 base64:", sha1.Base64()) - - sha224, _ := hmac.SHA224(key, data) - fmt.Println("hmac sha224 hex:", sha224.Hex()) - fmt.Println("hmac sha224 base64:", sha224.Base64()) - - sha256, _ := hmac.SHA256(key, data) - fmt.Println("hmac sha256 hex:", sha256.Hex()) - fmt.Println("hmac sha256 base64:", sha256.Base64()) - - sha384, _ := hmac.SHA384(key, data) - fmt.Println("hmac sha384 hex:", sha384.Hex()) - fmt.Println("hmac sha384 base64:", sha384.Base64()) - - sha512, _ := hmac.SHA512(key, data) - fmt.Println("hmac sha512 hex:", sha512.Hex()) - fmt.Println("hmac sha512 base64:", sha512.Base64()) + fmt.Printf("data: %s\n", data) + + key := []byte("key") + fmt.Printf("key: %s\n", key) + + md5 := hmac.MD5(data, key) + md5Hex := hmac.MD5(data, key, hmac.WithHex()) + md5Base64 := hmac.MD5(data, key, hmac.WithBase64()) + fmt.Printf("md5: %s\n", md5) + fmt.Printf("md5 hex: %s\n", md5Hex) + fmt.Printf("md5 base64: %s\n", md5Base64) + + sha1 := hmac.SHA1(data, key) + sha1Hex := hmac.SHA1(data, key, hmac.WithHex()) + sha1Base64 := hmac.SHA1(data, key, hmac.WithBase64()) + fmt.Printf("sha1: %s\n", sha1) + fmt.Printf("sha1 hex: %s\n", sha1Hex) + fmt.Printf("sha1 base64: %s\n", sha1Base64) + + sha224 := hmac.SHA224(data, key) + sha224Hex := hmac.SHA224(data, key, hmac.WithHex()) + sha224Base64 := hmac.SHA224(data, key, hmac.WithBase64()) + fmt.Printf("sha224: %s\n", sha224) + fmt.Printf("sha224 hex: %s\n", sha224Hex) + fmt.Printf("sha224 base64: %s\n", sha224Base64) + + sha256 := hmac.SHA256(data, key) + sha256Hex := hmac.SHA256(data, key, hmac.WithHex()) + sha256Base64 := hmac.SHA256(data, key, hmac.WithBase64()) + fmt.Printf("sha256: %s\n", sha256) + fmt.Printf("sha256 hex: %s\n", sha256Hex) + fmt.Printf("sha256 base64: %s\n", sha256Base64) + + sha384 := hmac.SHA384(data, key) + sha384Hex := hmac.SHA384(data, key, hmac.WithHex()) + sha384Base64 := hmac.SHA384(data, key, hmac.WithBase64()) + fmt.Printf("sha384: %s\n", sha384) + fmt.Printf("sha384 hex: %s\n", sha384Hex) + fmt.Printf("sha384 base64: %s\n", sha384Base64) + + sha512 := hmac.SHA512(data, key) + sha512Hex := hmac.SHA512(data, key, hmac.WithHex()) + sha512Base64 := hmac.SHA512(data, key, hmac.WithBase64()) + fmt.Printf("sha512: %s\n", sha512) + fmt.Printf("sha512 hex: %s\n", sha512Hex) + fmt.Printf("sha512 base64: %s\n", sha512Base64) } diff --git a/_examples/hmac_test.go b/_examples/hmac_test.go index b7402de..e2d0ab7 100644 --- a/_examples/hmac_test.go +++ b/_examples/hmac_test.go @@ -11,66 +11,66 @@ import ( ) var ( - hmacBenchKey = []byte("12345678") hmacBenchData = []byte("你好,世界") + hmacBenchKey = []byte("key") ) -// go test -v -bench=^BenchmarkHMACUsingMD5$ -benchtime=1s hash_test.go -func BenchmarkHMACUsingMD5(b *testing.B) { +// go test -v -bench=^BenchmarkHMAC_MD5$ -benchtime=1s hash_test.go +func BenchmarkHMAC_MD5(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - hmac.MD5(hmacBenchKey, hmacBenchData) + hmac.MD5(hmacBenchData, hmacBenchKey) } } -// go test -v -bench=^BenchmarkHMACUsingSHA1$ -benchtime=1s hash_test.go -func BenchmarkHMACUsingSHA1(b *testing.B) { +// go test -v -bench=^BenchmarkHMAC_SHA1$ -benchtime=1s hash_test.go +func BenchmarkHMAC_SHA1(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - hmac.SHA1(hmacBenchKey, hmacBenchData) + hmac.SHA1(hmacBenchData, hmacBenchKey) } } -// go test -v -bench=^BenchmarkHMACUsingSHA224$ -benchtime=1s hash_test.go -func BenchmarkHMACUsingSHA224(b *testing.B) { +// go test -v -bench=^BenchmarkHMAC_SHA224$ -benchtime=1s hash_test.go +func BenchmarkHMAC_SHA224(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - hmac.SHA224(hmacBenchKey, hmacBenchData) + hmac.SHA224(hmacBenchData, hmacBenchKey) } } -// go test -v -bench=^BenchmarkHMACUsingSHA256$ -benchtime=1s hash_test.go -func BenchmarkHMACUsingSHA256(b *testing.B) { +// go test -v -bench=^BenchmarkHMAC_SHA256$ -benchtime=1s hash_test.go +func BenchmarkHMAC_SHA256(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - hmac.SHA256(hmacBenchKey, hmacBenchData) + hmac.SHA256(hmacBenchData, hmacBenchKey) } } -// go test -v -bench=^BenchmarkHMACUsingSHA384$ -benchtime=1s hash_test.go -func BenchmarkHMACUsingSHA384(b *testing.B) { +// go test -v -bench=^BenchmarkHMAC_SHA384$ -benchtime=1s hash_test.go +func BenchmarkHMAC_SHA384(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - hmac.SHA384(hmacBenchKey, hmacBenchData) + hmac.SHA384(hmacBenchData, hmacBenchKey) } } -// go test -v -bench=^BenchmarkHMACUsingSHA512$ -benchtime=1s hash_test.go -func BenchmarkHMACUsingSHA512(b *testing.B) { +// go test -v -bench=^BenchmarkHMAC_SHA512$ -benchtime=1s hash_test.go +func BenchmarkHMAC_SHA512(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - hmac.SHA512(hmacBenchKey, hmacBenchData) + hmac.SHA512(hmacBenchData, hmacBenchKey) } } diff --git a/_examples/rand.go b/_examples/rand.go deleted file mode 100644 index 794b7f5..0000000 --- a/_examples/rand.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2024 FishGoddess. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package main - -import ( - "fmt" - - "github.com/FishGoddess/cryptox" -) - -func main() { - // We provide some rand functions for you. - // If you want to generate some keys or ivs, just feel free to use them. - bs := cryptox.GenerateBytes(32) - fmt.Println(bs) - - str := cryptox.GenerateString(64) - fmt.Println(str) - - // Already have a byte slice? Try AppendBytes: - bs = make(cryptox.Bytes, 0, 16) - bs = cryptox.AppendBytes(bs, 16) - fmt.Println(bs) -} diff --git a/_examples/rand_test.go b/_examples/rand_test.go deleted file mode 100644 index d160c10..0000000 --- a/_examples/rand_test.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2024 FishGoddess. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package main - -import ( - "testing" - - "github.com/FishGoddess/cryptox" -) - -// go test -v -cover -count=1 -test.cpu=1 -run=^$ -bench=^BenchmarkGenerateBytes$ -func BenchmarkGenerateBytes(b *testing.B) { - b.ReportAllocs() - b.ResetTimer() - - for i := 0; i < b.N; i++ { - cryptox.GenerateBytes(16) - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^$ -bench=^BenchmarkGenerateString$ -func BenchmarkGenerateString(b *testing.B) { - b.ReportAllocs() - b.ResetTimer() - - for i := 0; i < b.N; i++ { - cryptox.GenerateString(16) - } -} diff --git a/_examples/rsa.go b/_examples/rsa.go index f97b4f7..1309780 100644 --- a/_examples/rsa.go +++ b/_examples/rsa.go @@ -11,48 +11,55 @@ import ( "github.com/FishGoddess/cryptox/rsa" ) -var ( - // Use public key to encrypt msg. - publicKey = rsa.MustLoadPublicKey("rsa.pub") +func main() { + // Load the private key and the public key from file. + // Check rsa.Option for more information about file encoding. + privateKey, err := rsa.LoadPrivateKey("rsa.key") + if err != nil { + panic(err) + } - // Use private key to decrypt msg. - privateKey = rsa.MustLoadPrivateKey("rsa.key") -) + publicKey, err := rsa.LoadPublicKey("rsa.pub") + if err != nil { + panic(err) + } -func main() { - msg := []byte("戴上头箍,爱不了你;不戴头箍,救不了你。") - fmt.Printf("Msg: %s\n", msg) + data := []byte("戴上头箍,爱不了你;不戴头箍,救不了你。") + fmt.Printf("data: %s\n", data) - // Use public key to encrypt msg. - encrypted, err := publicKey.EncryptPKCS1v15(msg) + // Use the public key to encrypt data using base64 encoding. + label := []byte("你好,世界") + + encrypt, err := publicKey.EncryptOAEP(data, label, rsa.WithBase64()) if err != nil { panic(err) } - fmt.Printf("Encrypted: %s\n", encrypted.Base64()) + fmt.Printf("encrypt: %s\n", encrypt) - // Use private key to decrypt msg. - decrypted, err := privateKey.DecryptPKCS1v15(encrypted) + // Use the private key to decrypt data using base64 encoding. + decrypt, err := privateKey.DecryptOAEP(encrypt, label, rsa.WithBase64()) if err != nil { panic(err) } - fmt.Printf("Decrypted: %s\n", decrypted) + fmt.Printf("decrypt: %s\n", decrypt) + + // Use the private key to sign data. + digest := hash.SHA256(data) - // Use private key to sign msg. - msg = hash.SHA256(msg) - signed, err := privateKey.SignPKCS1v15(msg) + sign, err := privateKey.SignPSS(digest, rsa.WithHex()) if err != nil { panic(err) } - fmt.Printf("Signed: %s\n", signed) + fmt.Printf("sign: %s\n", sign) - // Use public key to verify msg. - err = publicKey.VerifyPKCS1v15(msg, signed) + // Use the public key to verify the sign. + err = publicKey.VerifyPSS(digest, sign, rsa.WithHex()) if err != nil { panic(err) } - fmt.Println("Verified.") + fmt.Printf("verify: %s\n", data) } diff --git a/_examples/rsa.key b/_examples/rsa.key index c8d9664..cd8bf37 100644 --- a/_examples/rsa.key +++ b/_examples/rsa.key @@ -1,51 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIJKQIBAAKCAgEAwhsKSzcPQaHDHebxvhnWCkwfo1LTaCLq8xNRcBsWlcXgwzEt -gEQcLB8WYxpKz8iEc+JmwMu/K+1kfdlkOtjP63PecCJmmRT5BMQp5LrLQsxwMdfx -X6z8oujw5WSCvi1sLQpBwfAJXaCt399qMRK4sge8n/alUoCY4cbys+wnn5OcrYso -IBLve+1ovFUysI1BcQsfynplU8GU7DvL55mvptPwMkALp/MNLMXIsJOgxAHRou+a -wRjVgW5mvpUE+QRfEIPnzoeoTIpJicdurUQKHH4gvnGC+f2UKRwkfRz2Y2XRCFX/ -V7GUxrqj+tHlrMZZPWBh6VzHJwHvCN/aHmNvOo7Y8g5PXwg/0qGNwoowXJ6edhJe -97j309wk6Ewsz1V/tvY4in25yBhe5O4hhmHjxY9SLRvAN82NTnipEDJEM78wMU8c -4NoKvf2TbgpVtDCOtZfAJpMRK0w2+uQGh6n3MpZ2TnUB3TSpoxYM+X0WavI6MgXi -4ioJQpdKVi3w4eS8B4AuoHXh740M6XxAj5emG34mgqBrdPldfuoEgaTxYVGEq0jm -e5VPJ8rpg6r2C5IlfYbXbLMFylT0O1GOZwblr5keDz3+DVOuTvifsVY7CbN/wooh -0sl+LkIQpOQoHT1r+aqxQ9UO6+bqIaciQcwycDVuagiUjvtMo2KsDgE94p0CAwEA -AQKCAgA9alDXe5RYL9aMn4XdoE8Y8v0PsCUzzdiJEJOz5N5AhVuYZvb+no5LZ4qt -0KPbGpbOE9RvLAhQ3cvuBdqww+kFlqPK1xefWANyfp7CwFePyx+0pZ65pwJIiWuv -KR41DiosFhK4SjqsZSqeqxHudP37VndYJbIpaIb26Pvh8daXpxzrzBvmChe8HmIF -NO/U7NzH570nU5K77YQxOrpJjVcGcgiN3bXSbhS9FddqB0vWuLyb0TQl5LWpPkVE -B+KK59csYo/1yPZ3QUoDTha2u9B3M3Eo0MGgV5uJZl4lOAS9bnm1KYChPo7UUjmQ -ItmI0TlrMhPIwpJAxVvccveQpeWLFJXAMSoB138ZwWwKYJP9WdZnwU1oxp+r9ttN -A4cHJ/DG+ciDXIC+5FuzM6KnCpIPDWJEwSQrEsDMBBqEPrqi8/9BXnlRbzo8HtaA -jJ4792pWtchsxyGRTyS66s8PS9eP40LiXYhoFwAjlQ+438yxpmSNDO3T6kFbJtVb -wklB10DWRBEPwAXLP6W/qKuEV0auHvYrligixcbM8tVS5crEpIrpeukU6d0at7FF -HKTB9Xflrk/SSrRb0XDfvCh45PW/oB+gkDmuOi8dEz3xyX1706YU6VqXznCLrFsO -rDghpWz0ga4p1XjJIkxUXx6085gBPimsonb5sVQVRMaBLfucAQKCAQEA3NMg+LCv -/gcdkh9REsfTueyiFFZGd6Z9YPQJQrgArBqGem8WG0TTwuBVzxHpaPh/U4aGUlH+ -GAc+8Pa/4yvzXC8gO2aA+zet8LZ0GtURGWSgxSib2fIRUJS7Ig+12ZmvnOiCDCso -VBtfqa9Otfv/AJEAt7MCsT21i8Y7e51qonj8FndVgioe0o5hmORxDgPhA5FoPNbq -8yxF9v0QkteRC1+uN7WFl1LNevGqnpWR+Ln8TzN666q/lzGn8aGuEx+9pwBkGYaV -887WPhUM2cpsjMUHfPKQLF5PtvAJeagEefB8BKP7C1E2govUIxTuiyDv4SIv6PFm -izjhisUmXN4KZwKCAQEA4QZZsMDRyWt1DceU3b/ec3aMlqlbrKuT1SzfckuWxfB2 -R3KTiFUC42NnbQNulj0hf+1nI8ofv0Dwp52q3IpnUTL5y+BzH3h0Fh1hAhXdStbx -qR8ENcitInkJ1mJZYg7ycjYGenvF30TZSPeRsn3DBFmLjwpU5yZZMNfTSycrAXBP -TZN+GWDDOq3yYHTY+OexUqDTIC0eymt13hAP8iEw4AbKbWgS/iiLCxRU7QuN0Zg0 -5M6aiaTWhYEtrtXxrOBbjWJmmd792GZLqZ2LHEBZx374fZX2QV33eHnz0ewsJtuQ -03N2kgFIBUnf8T+lM3nvdoiJxrQxa/TcZpJJwnBQWwKCAQEAp8XAxzF2hQni7aHd -dudbdWbTvZ/O6hkUKRU/8/DYkemVQnAj25PvT6AhkbGKJLaoBk2uv9yLsLXQnELc -hzxdKSyk0HyQgk1LWhdFHx3CYAiUHTc9Mdk4ToCxrK9C+pBJXG1KYPRpL32csM8A -fkN2+ncwlGDtKrw3snpptwFigw5oeKkxzjMyN8b1X7QwMPXBf3R6Pb8LwrVgcsbS -UL529K09MBY56T5/32i8uL/qp46mo4X5XhPcfIK9llfAaccz0C3732jhhNsg6woT -OWUSE+GeTs/7ubnt1o+Io2WEASE1GFF96ptJTuXtxrmggxQDLIlM3S6LOjt9IHGp -M+V/AQKCAQEAv3jc1parq50BBBGgLTP2//6O0CAZfoqchxbgpuuExra+njrzKF1V -0NCjj9IMxlAL8kO6sGxrqRyD6cwyN+iY0zHx9bXtn4iqFqs3AURDgUv2Y3ad6Xnx -QFUHtuEGt3aKe/5WcuBg1YNK9FicI+n5B/l909/xsKIj4Hi1NMyGPnonMJZhBQ0d -8g9THxouCVV0wB9Spp1eBtV7fNHkD56IbQe6NXAKQTOUZIAPZ4ieSwsdbtgSDS8y -znTFv3ASbYyWum5RaXqiOwGIPjJszURAYI08uffhi2t+iIgUnLo+M24/BH3KFaB7 -fOyIXkpOssd9qDm0KlzndLns1GaD1qwk4wKCAQB1jTlTw1BQcGi4Q1BTobDcpK/T -s9JrnpVt2j35cqAkXOz5DpmpBNmlJ5EeKkPvbKUH1QXUtVEJY/b3QWQg+iaI73Bj -rwtp9kWHi4WflEzR97kozOPvTDFAkH1EFVtJqVzb5UtPYUr3Db007pdWxaz8ugqA -HB4N2+ecKtvprd8/znuA1hNLei3tevoNcuklKaSVW7uOqxkDF3H2nOHK+C47AJwJ -wk9xvsYQAz+anb3FH08nxyxpCZZF25oRJxYO6oau2ELM8JrseMO0wOjHsDdbbJk1 -74m4SV1f0HkLo9nf0RT1Fa7VNHBYIcAHsejtMPYWRohJT5FvWrxUPM4NBrRN +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCnoCnYqtdcxo2Z +zcrpkfHYg+s/gGrlt7Ww//gBJfcCZ64ls4fD4I0BD+Xe0WHuT5poow6Cyyl20yrv +Y13oqlRr05bsnPpu2ZOiEPfiAQb898xgYYwoDwLEk75Uu1ox53unhhZbRKNne1V1 +QMYFpxAgeAMxML09ZmEqPp3q0BNZcjZqaq/2umHVOBOrqrRJZG7APZpixAvwbRKJ +zELq40s0zD6ECovZk/3WVF8BEcrmybmnbns1Y89ptXFZIQVKPqHXUK9+VbIiKZsU ++UKpY29WKjOkELcSrG7HMZbIXivzswGn5c0DskXQ/TbzV5WNxpW07Op0s3y9G7n+ +kTcytZETAgMBAAECggEACpRE/9aBwr+0lnU9WLIW0Y47LdCk2ScChw9omhNI0cnf +B6RwgvAW8MRi7XCeiHlVVURGjqz+ysU3YPtkT7esSKUxJxwd1sV8QqQP2PTX+ZA0 +Fw3LrYg2VFdqEZPvdICCYTIeaBfeuOcOSi5Shm8i8+xPG0QoYAnS7mUcTK0MLj65 +FYcb1p7rteeqpQwKqekaAEqOo084Nbs10++ltct3wM4xOvEFJubdMUj6kruOVwG5 +onuyONTWQ9U1akSRwlx1HnafieTG/Rsvx+6FT4wAgioZy3JKlWsLckI0K5zi/YSz +Z0ZeqdtGLI7JbYIKv+H+zeLv0mPErwOXJk0qm3b6eQKBgQDJVMYzwB1BIVU545dz +8+YX69WfFVjH9RSJ+9monq6fu0QW6s2qH4O8DCKqG5fuSAXPVzYvVrEDV4KGsyPG +m0uQteM5om7ICIQVvewxBiaDX0bvFwiK7gfzdLVMgYirrkxZUQ4NgGTRScAP2ysm +sOn52Om5ZK5JZ3v8C/aQU/D5CQKBgQDVJGcDV7XlOVMptAVICEP4G+jg2fS9ORlz +TkgpK8sw1l1P6eO9WU2TrOh8UCQFntVe5YwgzQuBK4PJACsbHqx1Xww57MI1dlIQ +p2vP6dFmVlneIrcHXZ4QRQug6Envp03Smnbzf9clDCAtZkt3gGDc2r0yxn8AY6rE ++q6mITHMOwKBgQC7ernmzutvDv8yHQGX9HM7q10N+u7lpQ8vPtt87edmzxekz5oc +5aPipNpS1ccxGNhwL6JBitTja8YccQzLkSlY5EdoEB5hH60AIg+jxzpt83c2hZhq +5yV4TCHX0HfYh0KJmbUgVYOMcMTs/wa7zNrU0m0zOtIhgMAwAWPlGoW3IQKBgHSs +l6NRySVwiuCiRd3XgHV5ubIUPY+ziQYAjSnUakcSoUPUkbEeCIRVO3KJYB6fgseO +unVeKPUNf/dwmygeU2Nwoz22J92iJmwtaawHn3P4wvsBX9WtXpAja6kqXwbMO6KU +oZbLnVcPWzHe9GK3KM7dAoKf+/eXl2x6mU4hj6PvAoGBALKTzLcqAr7n+TqcRAJU +nm44K4zj52Nj0lpritovFbP1EiVoj07AFqFULE3aHMeGKwe+aNxz5JTSHPaP6aa9 +iD4CVoJzQ41OimqHnnRSgy3g+ylk7plLk/M0rE+6Ev945Xv8vGOGci6KgkBpOx37 +/yglpQbyju+BbRvq9gDfLuKX -----END PRIVATE KEY----- diff --git a/_examples/rsa.pub b/_examples/rsa.pub index db531e9..9cdb1f2 100644 --- a/_examples/rsa.pub +++ b/_examples/rsa.pub @@ -1,14 +1,9 @@ -----BEGIN PUBLIC KEY----- -MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwhsKSzcPQaHDHebxvhnW -Ckwfo1LTaCLq8xNRcBsWlcXgwzEtgEQcLB8WYxpKz8iEc+JmwMu/K+1kfdlkOtjP -63PecCJmmRT5BMQp5LrLQsxwMdfxX6z8oujw5WSCvi1sLQpBwfAJXaCt399qMRK4 -sge8n/alUoCY4cbys+wnn5OcrYsoIBLve+1ovFUysI1BcQsfynplU8GU7DvL55mv -ptPwMkALp/MNLMXIsJOgxAHRou+awRjVgW5mvpUE+QRfEIPnzoeoTIpJicdurUQK -HH4gvnGC+f2UKRwkfRz2Y2XRCFX/V7GUxrqj+tHlrMZZPWBh6VzHJwHvCN/aHmNv -Oo7Y8g5PXwg/0qGNwoowXJ6edhJe97j309wk6Ewsz1V/tvY4in25yBhe5O4hhmHj -xY9SLRvAN82NTnipEDJEM78wMU8c4NoKvf2TbgpVtDCOtZfAJpMRK0w2+uQGh6n3 -MpZ2TnUB3TSpoxYM+X0WavI6MgXi4ioJQpdKVi3w4eS8B4AuoHXh740M6XxAj5em -G34mgqBrdPldfuoEgaTxYVGEq0jme5VPJ8rpg6r2C5IlfYbXbLMFylT0O1GOZwbl -r5keDz3+DVOuTvifsVY7CbN/wooh0sl+LkIQpOQoHT1r+aqxQ9UO6+bqIaciQcwy -cDVuagiUjvtMo2KsDgE94p0CAwEAAQ== +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp6Ap2KrXXMaNmc3K6ZHx +2IPrP4Bq5be1sP/4ASX3AmeuJbOHw+CNAQ/l3tFh7k+aaKMOgsspdtMq72Nd6KpU +a9OW7Jz6btmTohD34gEG/PfMYGGMKA8CxJO+VLtaMed7p4YWW0SjZ3tVdUDGBacQ +IHgDMTC9PWZhKj6d6tATWXI2amqv9rph1TgTq6q0SWRuwD2aYsQL8G0SicxC6uNL +NMw+hAqL2ZP91lRfARHK5sm5p257NWPPabVxWSEFSj6h11CvflWyIimbFPlCqWNv +ViozpBC3EqxuxzGWyF4r87MBp+XNA7JF0P0281eVjcaVtOzqdLN8vRu5/pE3MrWR +EwIDAQAB -----END PUBLIC KEY----- diff --git a/_examples/rsa_key.go b/_examples/rsa_key.go index d3cbf97..efa7af4 100644 --- a/_examples/rsa_key.go +++ b/_examples/rsa_key.go @@ -5,60 +5,35 @@ package main import ( - "fmt" - "github.com/FishGoddess/cryptox/rsa" ) func main() { - // Generate a 4096 bits key. - // Check rsa.KeyOption for more information of encoder and decoder. - privateKey, publicKey, err := rsa.GenerateKeys(4096) + // Generate a 2048 bits key. + privateKey, publicKey, err := rsa.GenerateKeys(2048) if err != nil { panic(err) } - fmt.Println(privateKey) - fmt.Println(publicKey) - - // Try WriteToFile if you want to write your private key to file. - n, err := privateKey.Bytes().WriteToFile("rsa.key") + // Store the private key and the public key to file. + err = rsa.StorePrivateKey("rsa.key", privateKey) if err != nil { panic(err) } - fmt.Printf("Write %d bytes to private key file\n", n) - - // Try WriteToFile if you want to write your public key to file. - n, err = publicKey.Bytes().WriteToFile("rsa.pub") + err = rsa.StorePublicKey("rsa.pub", publicKey) if err != nil { panic(err) } - fmt.Printf("Write %d bytes to public key file\n", n) - - // Load private key from file. - loadedPrivateKey, err := rsa.LoadPrivateKey("rsa.key") + // Load the private key and the public key from file. + privateKey, err = rsa.LoadPrivateKey("rsa.key") if err != nil { panic(err) } - // Load public key from file. - loadedPublicKey, err := rsa.LoadPublicKey("rsa.pub") + publicKey, err = rsa.LoadPublicKey("rsa.pub") if err != nil { panic(err) } - - fmt.Println(loadedPrivateKey) - fmt.Println(loadedPublicKey) - - // Want to load keys from file and panic if failed? - // Try these: - loadedPrivateKey = rsa.MustLoadPrivateKey("rsa.key") - loadedPublicKey = rsa.MustLoadPublicKey("rsa.pub") - - // Already have a private or public key in bytes? - // Try these: - _, _ = rsa.ParsePrivateKey(privateKey.Bytes()) - _, _ = rsa.ParsePublicKey(publicKey.Bytes()) } diff --git a/_examples/rsa_key_test.go b/_examples/rsa_key_test.go index b14c130..6c66545 100644 --- a/_examples/rsa_key_test.go +++ b/_examples/rsa_key_test.go @@ -10,228 +10,39 @@ import ( "github.com/FishGoddess/cryptox/rsa" ) -// go test -v -bench=^BenchmarkRSAGenerateKey1024PKCS1PKIX$ -benchtime=1s rsa_key_test.go -func BenchmarkRSAGenerateKey1024PKCS1PKIX(b *testing.B) { - opts := []rsa.KeyOption{ - rsa.WithPrivateKeyEncoder(rsa.X509.PKCS1PrivateKeyEncoder), - rsa.WithPrivateKeyDecoder(rsa.X509.PKCS1PrivateKeyDecoder), - rsa.WithPublicKeyEncoder(rsa.X509.PKIXPublicKeyEncoder), - } - - b.ReportAllocs() - b.ResetTimer() - - for i := 0; i < b.N; i++ { - _, _, err := rsa.GenerateKeys(1024, opts...) - if err != nil { - b.Fatal(err) - } - } -} - -// go test -v -bench=^BenchmarkRSAGenerateKey2048PKCS1PKIX$ -benchtime=1s rsa_key_test.go -func BenchmarkRSAGenerateKey2048PKCS1PKIX(b *testing.B) { - opts := []rsa.KeyOption{ - rsa.WithPrivateKeyEncoder(rsa.X509.PKCS1PrivateKeyEncoder), - rsa.WithPrivateKeyDecoder(rsa.X509.PKCS1PrivateKeyDecoder), - rsa.WithPublicKeyEncoder(rsa.X509.PKIXPublicKeyEncoder), - } - - b.ReportAllocs() - b.ResetTimer() - - for i := 0; i < b.N; i++ { - _, _, err := rsa.GenerateKeys(2048, opts...) - if err != nil { - b.Fatal(err) - } - } -} - -// go test -v -bench=^BenchmarkRSAGenerateKey4096PKCS1PKIX$ -benchtime=1s rsa_key_test.go -func BenchmarkRSAGenerateKey4096PKCS1PKIX(b *testing.B) { - opts := []rsa.KeyOption{ - rsa.WithPrivateKeyEncoder(rsa.X509.PKCS1PrivateKeyEncoder), - rsa.WithPrivateKeyDecoder(rsa.X509.PKCS1PrivateKeyDecoder), - rsa.WithPublicKeyEncoder(rsa.X509.PKIXPublicKeyEncoder), - } - - b.ReportAllocs() - b.ResetTimer() - - for i := 0; i < b.N; i++ { - _, _, err := rsa.GenerateKeys(4096, opts...) - if err != nil { - b.Fatal(err) - } - } -} - -// go test -v -bench=^BenchmarkRSAGenerateKey1024PKCS8PKIX$ -benchtime=1s rsa_key_test.go -func BenchmarkRSAGenerateKey1024PKCS8PKIX(b *testing.B) { - opts := []rsa.KeyOption{ - rsa.WithPrivateKeyEncoder(rsa.X509.PKCS8PrivateKeyEncoder), - rsa.WithPrivateKeyDecoder(rsa.X509.PKCS8PrivateKeyDecoder), - rsa.WithPublicKeyEncoder(rsa.X509.PKIXPublicKeyEncoder), - } - - b.ReportAllocs() - b.ResetTimer() - - for i := 0; i < b.N; i++ { - _, _, err := rsa.GenerateKeys(1024, opts...) - if err != nil { - b.Fatal(err) - } - } -} - -// go test -v -bench=^BenchmarkRSAGenerateKey2048PKCS8PKIX$ -benchtime=1s rsa_key_test.go -func BenchmarkRSAGenerateKey2048PKCS8PKIX(b *testing.B) { - opts := []rsa.KeyOption{ - rsa.WithPrivateKeyEncoder(rsa.X509.PKCS8PrivateKeyEncoder), - rsa.WithPrivateKeyDecoder(rsa.X509.PKCS8PrivateKeyDecoder), - rsa.WithPublicKeyEncoder(rsa.X509.PKIXPublicKeyEncoder), - } - - b.ReportAllocs() - b.ResetTimer() - - for i := 0; i < b.N; i++ { - _, _, err := rsa.GenerateKeys(2048, opts...) - if err != nil { - b.Fatal(err) - } - } -} - -// go test -v -bench=^BenchmarkRSAGenerateKey4096PKCS8PKIX$ -benchtime=1s rsa_key_test.go -func BenchmarkRSAGenerateKey4096PKCS8PKIX(b *testing.B) { - opts := []rsa.KeyOption{ - rsa.WithPrivateKeyEncoder(rsa.X509.PKCS8PrivateKeyEncoder), - rsa.WithPrivateKeyDecoder(rsa.X509.PKCS8PrivateKeyDecoder), - rsa.WithPublicKeyEncoder(rsa.X509.PKIXPublicKeyEncoder), - } - +// go test -v -bench=^BenchmarkRSA_GenerateKeys1024$ -benchtime=1s rsa_key_test.go +func BenchmarkRSA_GenerateKeys1024(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - _, _, err := rsa.GenerateKeys(4096, opts...) + _, _, err := rsa.GenerateKeys(1024) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkRSAGenerateKey1024PKCS1PKCS1$ -benchtime=1s rsa_key_test.go -func BenchmarkRSAGenerateKey1024PKCS1PKCS1(b *testing.B) { - opts := []rsa.KeyOption{ - rsa.WithPrivateKeyEncoder(rsa.X509.PKCS1PrivateKeyEncoder), - rsa.WithPrivateKeyDecoder(rsa.X509.PKCS1PrivateKeyDecoder), - rsa.WithPublicKeyEncoder(rsa.X509.PKCS1PublicKeyEncoder), - } - +// go test -v -bench=^BenchmarkRSA_GenerateKeys2048$ -benchtime=1s rsa_key_test.go +func BenchmarkRSA_GenerateKeys2048(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - _, _, err := rsa.GenerateKeys(1024, opts...) + _, _, err := rsa.GenerateKeys(2048) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkRSAGenerateKey2048PKCS1PKCS1$ -benchtime=1s rsa_key_test.go -func BenchmarkRSAGenerateKey2048PKCS1PKCS1(b *testing.B) { - opts := []rsa.KeyOption{ - rsa.WithPrivateKeyEncoder(rsa.X509.PKCS1PrivateKeyEncoder), - rsa.WithPrivateKeyDecoder(rsa.X509.PKCS1PrivateKeyDecoder), - rsa.WithPublicKeyEncoder(rsa.X509.PKCS1PublicKeyEncoder), - } - - b.ReportAllocs() - b.ResetTimer() - - for i := 0; i < b.N; i++ { - _, _, err := rsa.GenerateKeys(2048, opts...) - if err != nil { - b.Fatal(err) - } - } -} - -// go test -v -bench=^BenchmarkRSAGenerateKey4096PKCS1PKCS1$ -benchtime=1s rsa_key_test.go -func BenchmarkRSAGenerateKey4096PKCS1PKCS1(b *testing.B) { - opts := []rsa.KeyOption{ - rsa.WithPrivateKeyEncoder(rsa.X509.PKCS1PrivateKeyEncoder), - rsa.WithPrivateKeyDecoder(rsa.X509.PKCS1PrivateKeyDecoder), - rsa.WithPublicKeyEncoder(rsa.X509.PKCS1PublicKeyEncoder), - } - - b.ReportAllocs() - b.ResetTimer() - - for i := 0; i < b.N; i++ { - _, _, err := rsa.GenerateKeys(4096, opts...) - if err != nil { - b.Fatal(err) - } - } -} - -// go test -v -bench=^BenchmarkRSAGenerateKey1024PKCS8PKCS1$ -benchtime=1s rsa_key_test.go -func BenchmarkRSAGenerateKey1024PKCS8PKCS1(b *testing.B) { - opts := []rsa.KeyOption{ - rsa.WithPrivateKeyEncoder(rsa.X509.PKCS8PrivateKeyEncoder), - rsa.WithPrivateKeyDecoder(rsa.X509.PKCS8PrivateKeyDecoder), - rsa.WithPublicKeyEncoder(rsa.X509.PKCS1PublicKeyEncoder), - } - - b.ReportAllocs() - b.ResetTimer() - - for i := 0; i < b.N; i++ { - _, _, err := rsa.GenerateKeys(1024, opts...) - if err != nil { - b.Fatal(err) - } - } -} - -// go test -v -bench=^BenchmarkRSAGenerateKey2048PKCS8PKCS1$ -benchtime=1s rsa_key_test.go -func BenchmarkRSAGenerateKey2048PKCS8PKCS1(b *testing.B) { - opts := []rsa.KeyOption{ - rsa.WithPrivateKeyEncoder(rsa.X509.PKCS8PrivateKeyEncoder), - rsa.WithPrivateKeyDecoder(rsa.X509.PKCS8PrivateKeyDecoder), - rsa.WithPublicKeyEncoder(rsa.X509.PKCS1PublicKeyEncoder), - } - - b.ReportAllocs() - b.ResetTimer() - - for i := 0; i < b.N; i++ { - _, _, err := rsa.GenerateKeys(2048, opts...) - if err != nil { - b.Fatal(err) - } - } -} - -// go test -v -bench=^BenchmarkRSAGenerateKey4096PKCS8PKCS1$ -benchtime=1s rsa_key_test.go -func BenchmarkRSAGenerateKey4096PKCS8PKCS1(b *testing.B) { - opts := []rsa.KeyOption{ - rsa.WithPrivateKeyEncoder(rsa.X509.PKCS8PrivateKeyEncoder), - rsa.WithPrivateKeyDecoder(rsa.X509.PKCS8PrivateKeyDecoder), - rsa.WithPublicKeyEncoder(rsa.X509.PKCS1PublicKeyEncoder), - } - +// go test -v -bench=^BenchmarkRSA_GenerateKeys4096$ -benchtime=1s rsa_key_test.go +func BenchmarkRSA_GenerateKeys4096(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - _, _, err := rsa.GenerateKeys(4096, opts...) + _, _, err := rsa.GenerateKeys(4096) if err != nil { b.Fatal(err) } diff --git a/_examples/rsa_test.go b/_examples/rsa_test.go index c2f3d25..4cb16f0 100644 --- a/_examples/rsa_test.go +++ b/_examples/rsa_test.go @@ -15,9 +15,12 @@ var ( rsaBenchData = []byte("你好,世界") ) -// go test -v -bench=^BenchmarkRSAEncryptPKCS1v15$ -benchtime=1s rsa_test.go -func BenchmarkRSAEncryptPKCS1v15(b *testing.B) { - publicKey := rsa.MustLoadPublicKey("rsa.pub") +// go test -v -bench=^BenchmarkRSA_EncryptPKCS1v15$ -benchtime=1s rsa_test.go +func BenchmarkRSA_EncryptPKCS1v15(b *testing.B) { + publicKey, err := rsa.LoadPublicKey("rsa.pub") + if err != nil { + b.Fatal(err) + } b.ReportAllocs() b.ResetTimer() @@ -30,9 +33,12 @@ func BenchmarkRSAEncryptPKCS1v15(b *testing.B) { } } -// go test -v -bench=^BenchmarkRSAEncryptOAEP$ -benchtime=1s rsa_test.go -func BenchmarkRSAEncryptOAEP(b *testing.B) { - publicKey := rsa.MustLoadPublicKey("rsa.pub") +// go test -v -bench=^BenchmarkRSA_EncryptOAEP$ -benchtime=1s rsa_test.go +func BenchmarkRSA_EncryptOAEP(b *testing.B) { + publicKey, err := rsa.LoadPublicKey("rsa.pub") + if err != nil { + b.Fatal(err) + } b.ReportAllocs() b.ResetTimer() @@ -45,12 +51,19 @@ func BenchmarkRSAEncryptOAEP(b *testing.B) { } } -// go test -v -bench=^BenchmarkRSADecryptPKCS1v15$ -benchtime=1s rsa_test.go -func BenchmarkRSADecryptPKCS1v15(b *testing.B) { - privateKey := rsa.MustLoadPrivateKey("rsa.key") - publicKey := rsa.MustLoadPublicKey("rsa.pub") +// go test -v -bench=^BenchmarkRSA_DecryptPKCS1v15$ -benchtime=1s rsa_test.go +func BenchmarkRSA_DecryptPKCS1v15(b *testing.B) { + privateKey, err := rsa.LoadPrivateKey("rsa.key") + if err != nil { + b.Fatal(err) + } + + publicKey, err := rsa.LoadPublicKey("rsa.pub") + if err != nil { + b.Fatal(err) + } - encrypted, err := publicKey.EncryptPKCS1v15(rsaBenchData) + encrypt, err := publicKey.EncryptPKCS1v15(rsaBenchData) if err != nil { b.Fatal(err) } @@ -59,19 +72,28 @@ func BenchmarkRSADecryptPKCS1v15(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := privateKey.DecryptPKCS1v15(encrypted) + _, err := privateKey.DecryptPKCS1v15(encrypt) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkRSADecryptPKCS1v15SessionKey$ -benchtime=1s rsa_test.go -func BenchmarkRSADecryptPKCS1v15SessionKey(b *testing.B) { - privateKey := rsa.MustLoadPrivateKey("rsa.key") - publicKey := rsa.MustLoadPublicKey("rsa.pub") +// go test -v -bench=^BenchmarkRSA_DecryptPKCS1v15SessionKey$ -benchtime=1s rsa_test.go +func BenchmarkRSA_DecryptPKCS1v15SessionKey(b *testing.B) { + privateKey, err := rsa.LoadPrivateKey("rsa.key") + if err != nil { + b.Fatal(err) + } - encrypted, err := publicKey.EncryptPKCS1v15(rsaBenchData) + publicKey, err := rsa.LoadPublicKey("rsa.pub") + if err != nil { + b.Fatal(err) + } + + sessionKey := []byte("12345678876543211234567887654321") + + encrypt, err := publicKey.EncryptPKCS1v15(sessionKey) if err != nil { b.Fatal(err) } @@ -80,19 +102,26 @@ func BenchmarkRSADecryptPKCS1v15SessionKey(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - err := privateKey.DecryptPKCS1v15SessionKey(encrypted, nil) + err := privateKey.DecryptPKCS1v15SessionKey(encrypt, sessionKey) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkRSADecryptOAEP$ -benchtime=1s rsa_test.go -func BenchmarkRSADecryptOAEP(b *testing.B) { - privateKey := rsa.MustLoadPrivateKey("rsa.key") - publicKey := rsa.MustLoadPublicKey("rsa.pub") +// go test -v -bench=^BenchmarkRSA_DecryptOAEP$ -benchtime=1s rsa_test.go +func BenchmarkRSA_DecryptOAEP(b *testing.B) { + privateKey, err := rsa.LoadPrivateKey("rsa.key") + if err != nil { + b.Fatal(err) + } - encrypted, err := publicKey.EncryptOAEP(rsaBenchData, nil) + publicKey, err := rsa.LoadPublicKey("rsa.pub") + if err != nil { + b.Fatal(err) + } + + encrypt, err := publicKey.EncryptOAEP(rsaBenchData, nil) if err != nil { b.Fatal(err) } @@ -101,53 +130,68 @@ func BenchmarkRSADecryptOAEP(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := privateKey.DecryptOAEP(encrypted, nil) + _, err := privateKey.DecryptOAEP(encrypt, nil) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkRSASignPSS$ -benchtime=1s rsa_test.go -func BenchmarkRSASignPSS(b *testing.B) { - privateKey := rsa.MustLoadPrivateKey("rsa.key") - digest := hash.SHA256(rsaBenchData) +// go test -v -bench=^BenchmarkRSA_SignPKCS1v15$ -benchtime=1s rsa_test.go +func BenchmarkRSA_SignPKCS1v15(b *testing.B) { + privateKey, err := rsa.LoadPrivateKey("rsa.key") + if err != nil { + b.Fatal(err) + } + + hashed := hash.SHA256(rsaBenchData) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := privateKey.SignPSS(digest, 4) + _, err := privateKey.SignPKCS1v15(hashed) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkRSASignPKCS1v15$ -benchtime=1s rsa_test.go -func BenchmarkRSASignPKCS1v15(b *testing.B) { - privateKey := rsa.MustLoadPrivateKey("rsa.key") - hashed := hash.SHA256(rsaBenchData) +// go test -v -bench=^BenchmarkRSA_SignPSS$ -benchtime=1s rsa_test.go +func BenchmarkRSA_SignPSS(b *testing.B) { + privateKey, err := rsa.LoadPrivateKey("rsa.key") + if err != nil { + b.Fatal(err) + } + + digest := hash.SHA256(rsaBenchData) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := privateKey.SignPKCS1v15(hashed) + _, err := privateKey.SignPSS(digest) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkRSAVerifyPSS$ -benchtime=1s rsa_test.go -func BenchmarkRSAVerifyPSS(b *testing.B) { - privateKey := rsa.MustLoadPrivateKey("rsa.key") - publicKey := rsa.MustLoadPublicKey("rsa.pub") - digest := hash.SHA256(rsaBenchData) +// go test -v -bench=^BenchmarkRSA_VerifyPKCS1v15$ -benchtime=1s rsa_test.go +func BenchmarkRSA_VerifyPKCS1v15(b *testing.B) { + privateKey, err := rsa.LoadPrivateKey("rsa.key") + if err != nil { + b.Fatal(err) + } - saltLength := 4 - signed, err := privateKey.SignPSS(digest, saltLength) + publicKey, err := rsa.LoadPublicKey("rsa.pub") + if err != nil { + b.Fatal(err) + } + + hashed := hash.SHA256(rsaBenchData) + + sign, err := privateKey.SignPKCS1v15(hashed) if err != nil { b.Fatal(err) } @@ -156,20 +200,28 @@ func BenchmarkRSAVerifyPSS(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - err = publicKey.VerifyPSS(digest, signed, saltLength) + err = publicKey.VerifyPKCS1v15(hashed, sign) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkRSAVerifyPKCS1v15$ -benchtime=1s rsa_test.go -func BenchmarkRSAVerifyPKCS1v15(b *testing.B) { - privateKey := rsa.MustLoadPrivateKey("rsa.key") - publicKey := rsa.MustLoadPublicKey("rsa.pub") - hashed := hash.SHA256(rsaBenchData) +// go test -v -bench=^BenchmarkRSA_VerifyPSS$ -benchtime=1s rsa_test.go +func BenchmarkRSA_VerifyPSS(b *testing.B) { + privateKey, err := rsa.LoadPrivateKey("rsa.key") + if err != nil { + b.Fatal(err) + } + + publicKey, err := rsa.LoadPublicKey("rsa.pub") + if err != nil { + b.Fatal(err) + } + + digest := hash.SHA256(rsaBenchData) - signed, err := privateKey.SignPKCS1v15(hashed) + sign, err := privateKey.SignPSS(digest) if err != nil { b.Fatal(err) } @@ -178,7 +230,7 @@ func BenchmarkRSAVerifyPKCS1v15(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - err = publicKey.VerifyPKCS1v15(hashed, signed) + err = publicKey.VerifyPSS(digest, sign) if err != nil { b.Fatal(err) } diff --git a/_examples/triple_des.go b/_examples/triple_des.go index fa038bb..57601c4 100644 --- a/_examples/triple_des.go +++ b/_examples/triple_des.go @@ -8,7 +8,6 @@ import ( "bytes" "fmt" - "github.com/FishGoddess/cryptox" "github.com/FishGoddess/cryptox/des" ) @@ -18,29 +17,23 @@ func main() { key := []byte("123456788765432112345678") iv := []byte("87654321") - msg := []byte("你好,世界") - fmt.Printf("msg: %s\n", msg) + data := []byte("你好,世界") + fmt.Printf("data: %s\n", data) - // We use ctr mode and no padding to encrypt data. - // Of course, you can choose another mode if you want. - // Also, you can choose no/zero/pkcs5/pkcs7 to padding data. - encrypted, err := des.EncryptCTRTriple(key, iv, cryptox.PaddingNone, msg) + // Use ctr mode to encrypt data with no padding and encoding base64. + encrypt, err := des.EncryptTripleCTR(data, key, iv, des.WithBase64()) if err != nil { panic(err) } - fmt.Println("encrypted:", encrypted) - fmt.Println("encrypted hex:", encrypted.Hex()) - fmt.Println("encrypted base64:", encrypted.Base64()) + fmt.Printf("encrypt: %s\n", encrypt) - // We use ctr mode and no padding to decrypt data. - // Of course, you can choose another mode if you want. - // Also, you can choose no/zero/pkcs5/pkcs7 to undo padding data. - decrypted, err := des.DecryptCTRTriple(key, iv, cryptox.PaddingNone, encrypted) + // Decrypt data in the same way. + decrypt, err := des.DecryptTripleCTR(encrypt, key, iv, des.WithBase64()) if err != nil { panic(err) } - fmt.Printf("decrypted: %s\n", decrypted) - fmt.Println("decrypted == msg", bytes.Equal(decrypted, msg)) + fmt.Printf("decrypt: %s\n", decrypt) + fmt.Printf("decrypt is right: %+v\n", bytes.Equal(decrypt, data)) } diff --git a/_examples/triple_des_test.go b/_examples/triple_des_test.go index ff69b18..5502f10 100644 --- a/_examples/triple_des_test.go +++ b/_examples/triple_des_test.go @@ -7,7 +7,6 @@ package main import ( "testing" - "github.com/FishGoddess/cryptox" "github.com/FishGoddess/cryptox/des" ) @@ -17,74 +16,74 @@ var ( tripleDesBenchMsg = make([]byte, 128) ) -// go test -v -bench=^BenchmarkDESEncryptECBTriple$ -benchtime=1s triple_des_test.go -func BenchmarkDESEncryptECBTriple(b *testing.B) { +// go test -v -bench=^BenchmarkDES_EncryptTripleECB$ -benchtime=1s des_test.go +func BenchmarkDES_EncryptTripleECB(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := des.EncryptECBTriple(tripleDesBenchKey, cryptox.PaddingPKCS7, tripleDesBenchMsg) + _, err := des.EncryptTripleECB(tripleDesBenchMsg, tripleDesBenchKey, des.WithPKCS7()) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkDESEncryptCBCTriple$ -benchtime=1s triple_des_test.go -func BenchmarkDESEncryptCBCTriple(b *testing.B) { +// go test -v -bench=^BenchmarkDES_EncryptTripleCBC$ -benchtime=1s des_test.go +func BenchmarkDES_EncryptTripleCBC(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := des.EncryptCBCTriple(tripleDesBenchKey, tripleDesBenchIV, cryptox.PaddingPKCS7, tripleDesBenchMsg) + _, err := des.EncryptTripleCBC(tripleDesBenchMsg, tripleDesBenchKey, tripleDesBenchIV, des.WithPKCS7()) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkDESEncryptCFBTriple$ -benchtime=1s triple_des_test.go -func BenchmarkDESEncryptCFBTriple(b *testing.B) { +// go test -v -bench=^BenchmarkDES_EncryptTripleCFB$ -benchtime=1s des_test.go +func BenchmarkDES_EncryptTripleCFB(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := des.EncryptCFBTriple(tripleDesBenchKey, tripleDesBenchIV, cryptox.PaddingNone, tripleDesBenchMsg) + _, err := des.EncryptTripleCFB(tripleDesBenchMsg, tripleDesBenchKey, tripleDesBenchIV) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkDESEncryptOFBTriple$ -benchtime=1s triple_des_test.go -func BenchmarkDESEncryptOFBTriple(b *testing.B) { +// go test -v -bench=^BenchmarkDES_EncryptTripleOFB$ -benchtime=1s des_test.go +func BenchmarkDES_EncryptTripleOFB(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := des.EncryptOFBTriple(tripleDesBenchKey, tripleDesBenchIV, cryptox.PaddingNone, tripleDesBenchMsg) + _, err := des.EncryptTripleOFB(tripleDesBenchMsg, tripleDesBenchKey, tripleDesBenchIV) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkDESEncryptCTRTriple$ -benchtime=1s triple_des_test.go -func BenchmarkDESEncryptCTRTriple(b *testing.B) { +// go test -v -bench=^BenchmarkDES_EncryptTripleCTR$ -benchtime=1s des_test.go +func BenchmarkDES_EncryptTripleCTR(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := des.EncryptCTRTriple(tripleDesBenchKey, tripleDesBenchIV, cryptox.PaddingNone, tripleDesBenchMsg) + _, err := des.EncryptTripleCTR(tripleDesBenchMsg, tripleDesBenchKey, tripleDesBenchIV) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkDESDecryptECBTriple$ -benchtime=1s triple_des_test.go -func BenchmarkDESDecryptECBTriple(b *testing.B) { - encrypted, err := des.EncryptECBTriple(tripleDesBenchKey, cryptox.PaddingPKCS7, tripleDesBenchMsg) +// go test -v -bench=^BenchmarkDES_DecryptTripleECB$ -benchtime=1s des_test.go +func BenchmarkDES_DecryptTripleECB(b *testing.B) { + encrypt, err := des.EncryptTripleECB(tripleDesBenchMsg, tripleDesBenchKey, des.WithPKCS7()) if err != nil { b.Fatal(err) } @@ -93,16 +92,16 @@ func BenchmarkDESDecryptECBTriple(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := des.DecryptECBTriple(tripleDesBenchKey, cryptox.PaddingPKCS7, encrypted) + _, err := des.DecryptTripleECB(encrypt, tripleDesBenchKey, des.WithPKCS7()) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkDESDecryptCBCTriple$ -benchtime=1s triple_des_test.go -func BenchmarkDESDecryptCBCTriple(b *testing.B) { - encrypted, err := des.EncryptCBCTriple(tripleDesBenchKey, tripleDesBenchIV, cryptox.PaddingPKCS7, tripleDesBenchMsg) +// go test -v -bench=^BenchmarkDES_DecryptTripleCBC$ -benchtime=1s des_test.go +func BenchmarkDES_DecryptTripleCBC(b *testing.B) { + encrypt, err := des.EncryptTripleCBC(tripleDesBenchMsg, tripleDesBenchKey, tripleDesBenchIV, des.WithPKCS7()) if err != nil { b.Fatal(err) } @@ -111,16 +110,16 @@ func BenchmarkDESDecryptCBCTriple(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := des.DecryptCBCTriple(tripleDesBenchKey, tripleDesBenchIV, cryptox.PaddingPKCS7, encrypted) + _, err := des.DecryptTripleCBC(encrypt, tripleDesBenchKey, tripleDesBenchIV, des.WithPKCS7()) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkDESDecryptCFBTriple$ -benchtime=1s triple_des_test.go -func BenchmarkDESDecryptCFBTriple(b *testing.B) { - encrypted, err := des.EncryptCFBTriple(tripleDesBenchKey, tripleDesBenchIV, cryptox.PaddingNone, tripleDesBenchMsg) +// go test -v -bench=^BenchmarkDES_DecryptTripleCFB$ -benchtime=1s des_test.go +func BenchmarkDES_DecryptTripleCFB(b *testing.B) { + encrypt, err := des.EncryptTripleCFB(tripleDesBenchMsg, tripleDesBenchKey, tripleDesBenchIV) if err != nil { b.Fatal(err) } @@ -129,16 +128,16 @@ func BenchmarkDESDecryptCFBTriple(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := des.DecryptCFBTriple(tripleDesBenchKey, tripleDesBenchIV, cryptox.PaddingNone, encrypted) + _, err := des.DecryptTripleCFB(encrypt, tripleDesBenchKey, tripleDesBenchIV) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkDESDecryptOFBTriple$ -benchtime=1s triple_des_test.go -func BenchmarkDESDecryptOFBTriple(b *testing.B) { - encrypted, err := des.EncryptOFBTriple(tripleDesBenchKey, tripleDesBenchIV, cryptox.PaddingNone, tripleDesBenchMsg) +// go test -v -bench=^BenchmarkDES_DecryptTripleOFB$ -benchtime=1s des_test.go +func BenchmarkDES_DecryptTripleOFB(b *testing.B) { + encrypt, err := des.EncryptTripleOFB(tripleDesBenchMsg, tripleDesBenchKey, tripleDesBenchIV) if err != nil { b.Fatal(err) } @@ -147,16 +146,16 @@ func BenchmarkDESDecryptOFBTriple(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := des.DecryptOFBTriple(tripleDesBenchKey, tripleDesBenchIV, cryptox.PaddingNone, encrypted) + _, err := des.DecryptTripleOFB(encrypt, tripleDesBenchKey, tripleDesBenchIV) if err != nil { b.Fatal(err) } } } -// go test -v -bench=^BenchmarkDESDecryptCTRTriple$ -benchtime=1s triple_des_test.go -func BenchmarkDESDecryptCTRTriple(b *testing.B) { - encrypted, err := des.EncryptCTRTriple(tripleDesBenchKey, tripleDesBenchIV, cryptox.PaddingNone, tripleDesBenchMsg) +// go test -v -bench=^BenchmarkDES_DecryptTripleCTR$ -benchtime=1s des_test.go +func BenchmarkDES_DecryptTripleCTR(b *testing.B) { + encrypt, err := des.EncryptTripleCTR(tripleDesBenchMsg, tripleDesBenchKey, tripleDesBenchIV) if err != nil { b.Fatal(err) } @@ -165,7 +164,7 @@ func BenchmarkDESDecryptCTRTriple(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := des.DecryptCTRTriple(tripleDesBenchKey, tripleDesBenchIV, cryptox.PaddingNone, encrypted) + _, err := des.DecryptTripleCTR(encrypt, tripleDesBenchKey, tripleDesBenchIV) if err != nil { b.Fatal(err) } diff --git a/_icons/coverage.svg b/_icons/coverage.svg index 11846fc..1c8d14a 100644 --- a/_icons/coverage.svg +++ b/_icons/coverage.svg @@ -10,7 +10,7 @@ coverage coverage - 88% - 88% + 89% + 89% \ No newline at end of file diff --git a/aes/aes.go b/aes/aes.go index cdc854c..77a3393 100644 --- a/aes/aes.go +++ b/aes/aes.go @@ -5,14 +5,13 @@ package aes import ( + "bytes" "crypto/aes" "crypto/cipher" "fmt" - - "github.com/FishGoddess/cryptox" ) -func newBlock(key cryptox.Bytes) (cipher.Block, int, error) { +func newBlock(key []byte) (cipher.Block, int, error) { block, err := aes.NewCipher(key) if err != nil { return nil, 0, err @@ -22,15 +21,19 @@ func newBlock(key cryptox.Bytes) (cipher.Block, int, error) { return block, blockSize, nil } -func EncryptECB(key cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { +// EncryptECB uses ecb mode to encrypt data. +// It must specify a padding. +func EncryptECB(data []byte, key []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + block, blockSize, err := newBlock(key) if err != nil { return nil, err } - bs = bs.Clone() - src := padding.Padding(bs, blockSize) - dst := src.Clone() + src := bytes.Clone(data) + src = conf.padding.Pad(src, blockSize) + dst := bytes.Clone(src) if len(src)%blockSize != 0 { return nil, fmt.Errorf("cryptox/aes: encrypt ecb len(src) %d %% blockSize %d != 0", len(src), blockSize) @@ -46,176 +49,247 @@ func EncryptECB(key cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (c end += blockSize } + dst = conf.encoding.Encode(dst) return dst, nil } -func DecryptECB(key cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { +// EncryptCBC uses cbc mode to encrypt data. +// It must specify a padding. +func EncryptCBC(data []byte, key []byte, iv []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + block, blockSize, err := newBlock(key) if err != nil { return nil, err } - src := bs - dst := bs.Clone() + src := bytes.Clone(data) + src = conf.padding.Pad(src, blockSize) + dst := bytes.Clone(src) - if len(src)%blockSize != 0 { - return nil, fmt.Errorf("cryptox/aes: decrypt ecb len(src) %d %% blockSize %d != 0", len(src), blockSize) - } + cipher.NewCBCEncrypter(block, iv).CryptBlocks(dst, src) + dst = conf.encoding.Encode(dst) + return dst, nil +} - start := 0 - end := blockSize +// EncryptCFB uses cfb mode to encrypt data. +// There is no need to specify a padding. +func EncryptCFB(data []byte, key []byte, iv []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) - for end <= len(src) { - block.Decrypt(dst[start:end], src[start:end]) - - start += blockSize - end += blockSize + block, _, err := newBlock(key) + if err != nil { + return nil, err } - return padding.UndoPadding(dst, blockSize) + src := bytes.Clone(data) + dst := bytes.Clone(src) + + cipher.NewCFBEncrypter(block, iv).XORKeyStream(dst, src) + dst = conf.encoding.Encode(dst) + return dst, nil } -func EncryptCBC(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, blockSize, err := newBlock(key) +// EncryptOFB uses ofb mode to encrypt data. +// There is no need to specify a padding. +func EncryptOFB(data []byte, key []byte, iv []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + + block, _, err := newBlock(key) if err != nil { return nil, err } - bs = bs.Clone() - src := padding.Padding(bs, blockSize) - dst := src.Clone() + src := bytes.Clone(data) + dst := bytes.Clone(src) - cipher.NewCBCEncrypter(block, iv).CryptBlocks(dst, src) + cipher.NewOFB(block, iv).XORKeyStream(dst, src) + dst = conf.encoding.Encode(dst) return dst, nil } -func DecryptCBC(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, blockSize, err := newBlock(key) +// EncryptCTR uses ctr mode to encrypt data. +// There is no need to specify a padding. +func EncryptCTR(data []byte, key []byte, iv []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + + block, _, err := newBlock(key) if err != nil { return nil, err } - src := bs - dst := src.Clone() + src := bytes.Clone(data) + dst := bytes.Clone(src) - cipher.NewCBCDecrypter(block, iv).CryptBlocks(dst, src) - return padding.UndoPadding(dst, blockSize) + cipher.NewCTR(block, iv).XORKeyStream(dst, src) + dst = conf.encoding.Encode(dst) + return dst, nil } -func EncryptCFB(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, blockSize, err := newBlock(key) +// EncryptGCM uses gcm mode to encrypt data. +// There is no need to specify a padding. +func EncryptGCM(data []byte, key []byte, nonce []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + + block, _, err := newBlock(key) if err != nil { return nil, err } - bs = bs.Clone() - src := padding.Padding(bs, blockSize) - dst := src.Clone() + src := data + dst := bytes.Clone(src) + dst = dst[:0] - cipher.NewCFBEncrypter(block, iv).XORKeyStream(dst, src) + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + + dst = gcm.Seal(dst, nonce, src, conf.additional) + dst = conf.encoding.Encode(dst) return dst, nil } -func DecryptCFB(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { +// DecryptECB uses ecb mode to decrypt data. +// It must specify a padding. +func DecryptECB(data []byte, key []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + block, blockSize, err := newBlock(key) if err != nil { return nil, err } - src := bs - dst := bs.Clone() - - cipher.NewCFBDecrypter(block, iv).XORKeyStream(dst, src) - return padding.UndoPadding(dst, blockSize) -} - -func EncryptOFB(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, blockSize, err := newBlock(key) + src, err := conf.encoding.Decode(data) if err != nil { return nil, err } - bs = bs.Clone() - src := padding.Padding(bs, blockSize) - dst := src.Clone() + dst := bytes.Clone(src) - cipher.NewOFB(block, iv).XORKeyStream(dst, src) - return dst, nil + if len(src)%blockSize != 0 { + return nil, fmt.Errorf("cryptox/aes: decrypt ecb len(src) %d %% blockSize %d != 0", len(src), blockSize) + } + + start := 0 + end := blockSize + + for end <= len(src) { + block.Decrypt(dst[start:end], src[start:end]) + + start += blockSize + end += blockSize + } + + return conf.padding.Unpad(dst, blockSize) } -func DecryptOFB(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { +// DecryptCBC uses cbc mode to decrypt data. +// It must specify a padding. +func DecryptCBC(data []byte, key []byte, iv []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + block, blockSize, err := newBlock(key) if err != nil { return nil, err } - src := bs - dst := bs.Clone() + src, err := conf.encoding.Decode(data) + if err != nil { + return nil, err + } - cipher.NewOFB(block, iv).XORKeyStream(dst, src) - return padding.UndoPadding(dst, blockSize) + dst := bytes.Clone(src) + + cipher.NewCBCDecrypter(block, iv).CryptBlocks(dst, src) + return conf.padding.Unpad(dst, blockSize) } -func EncryptCTR(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, blockSize, err := newBlock(key) +// DecryptCFB uses cfb mode to decrypt data. +// There is no need to specify a padding. +func DecryptCFB(data []byte, key []byte, iv []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + + block, _, err := newBlock(key) + if err != nil { + return nil, err + } + + src, err := conf.encoding.Decode(data) if err != nil { return nil, err } - bs = bs.Clone() - src := padding.Padding(bs, blockSize) - dst := src.Clone() + dst := bytes.Clone(src) - cipher.NewCTR(block, iv).XORKeyStream(dst, src) + cipher.NewCFBDecrypter(block, iv).XORKeyStream(dst, src) return dst, nil } -func DecryptCTR(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, blockSize, err := newBlock(key) +// DecryptOFB uses ofb mode to decrypt data. +// There is no need to specify a padding. +func DecryptOFB(data []byte, key []byte, iv []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + + block, _, err := newBlock(key) + if err != nil { + return nil, err + } + + src, err := conf.encoding.Decode(data) if err != nil { return nil, err } - src := bs - dst := bs.Clone() + dst := bytes.Clone(src) - cipher.NewCTR(block, iv).XORKeyStream(dst, src) - return padding.UndoPadding(dst, blockSize) + cipher.NewOFB(block, iv).XORKeyStream(dst, src) + return dst, nil } -// EncryptGCM uses gcm mode to encrypt bs. -func EncryptGCM(key cryptox.Bytes, nonce cryptox.Bytes, additional cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { +// DecryptCTR uses ctr mode to decrypt data. +// There is no need to specify a padding. +func DecryptCTR(data []byte, key []byte, iv []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + block, _, err := newBlock(key) if err != nil { return nil, err } - src := bs.Clone() - dst := src[:0] - - gcm, err := cipher.NewGCM(block) + src, err := conf.encoding.Decode(data) if err != nil { return nil, err } - dst = gcm.Seal(dst, nonce, src, additional) + dst := bytes.Clone(src) + + cipher.NewCTR(block, iv).XORKeyStream(dst, src) return dst, nil } -// DecryptGCM uses gcm mode to decrypt bs. -func DecryptGCM(key cryptox.Bytes, nonce cryptox.Bytes, additional cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { +// DecryptGCM uses gcm mode to decrypt data. +// There is no need to specify a padding. +func DecryptGCM(data []byte, key []byte, nonce []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + block, _, err := newBlock(key) if err != nil { return nil, err } - src := bs.Clone() - dst := src[:0] + src, err := conf.encoding.Decode(data) + if err != nil { + return nil, err + } + + dst := bytes.Clone(src) + dst = dst[:0] gcm, err := cipher.NewGCM(block) if err != nil { return nil, err } - return gcm.Open(dst, nonce, src, additional) + return gcm.Open(dst, nonce, src, conf.additional) } diff --git a/aes/aes_test.go b/aes/aes_test.go index 7724565..ec718df 100644 --- a/aes/aes_test.go +++ b/aes/aes_test.go @@ -7,39 +7,90 @@ package aes import ( "crypto/aes" "fmt" + "slices" "testing" - - "github.com/FishGoddess/cryptox" ) var ( - testKey = []byte("123456788765432112345678") + testKey = []byte("12345678876543211234567887654321") testIV = []byte("8765432112345678") ) -type testResult struct { - bs []byte - hexString string - base64String string +type testCase struct { + Data []byte + EncryptData []byte + EncryptDataHex []byte + EncryptDataBase64 []byte } -func (tr *testResult) compareTo(bs cryptox.Bytes) error { - if string(tr.bs) != string(bs) { - return fmt.Errorf("result bs %s != bs %s", tr.bs, bs) - } +type testEncryptFunc func(data []byte, opts ...Option) ([]byte, error) - if tr.hexString != bs.Hex() { - return fmt.Errorf("result hexString %s != bs hex %s", tr.hexString, bs.Hex()) - } +type testDecryptFunc func(data []byte, opts ...Option) ([]byte, error) + +func testEncryptAndDecrypt(name string, encrypt testEncryptFunc, decrypt testDecryptFunc, testCases []testCase) error { + for _, testCase := range testCases { + // None + encrypted, err := encrypt(testCase.Data) + if err != nil { + return err + } + + if !slices.Equal(encrypted, testCase.EncryptData) { + return fmt.Errorf("%s data %q: got %+v != expect %+v", name, testCase.Data, encrypted, testCase.EncryptData) + } + + decrypted, err := decrypt(encrypted) + if err != nil { + return err + } + + if !slices.Equal(decrypted, testCase.Data) { + return fmt.Errorf("%s encrypted %q: got %+v != expect %+v", name, encrypted, decrypted, testCase.Data) + } + + // Hex + encrypted, err = encrypt(testCase.Data, WithHex()) + if err != nil { + return err + } + + if !slices.Equal(encrypted, testCase.EncryptDataHex) { + return fmt.Errorf("%s data hex %q: got %s != expect %s", name, testCase.Data, encrypted, testCase.EncryptDataHex) + } + + decrypted, err = decrypt(encrypted, WithHex()) + if err != nil { + return err + } + + if !slices.Equal(decrypted, testCase.Data) { + return fmt.Errorf("%s encrypted hex %q: got %s != expect %s", name, encrypted, decrypted, testCase.Data) + } - if tr.base64String != bs.Base64() { - return fmt.Errorf("result base64String %s != bs base64 %s", tr.base64String, bs.Base64()) + // Base64 + encrypted, err = encrypt(testCase.Data, WithBase64()) + if err != nil { + return err + } + + if !slices.Equal(encrypted, testCase.EncryptDataBase64) { + return fmt.Errorf("%s data base64 %q: got %s != expect %s", name, testCase.Data, encrypted, testCase.EncryptDataBase64) + } + + decrypted, err = decrypt(encrypted, WithBase64()) + if err != nil { + return err + } + + if !slices.Equal(decrypted, testCase.Data) { + return fmt.Errorf("%s encrypted base64 %q: got %s != expect %s", name, encrypted, decrypted, testCase.Data) + } } return nil } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestNewBlock$ +// go test -v -cover -run=^TestNewBlock$ func TestNewBlock(t *testing.T) { block, blockSize, err := newBlock(testKey) if err != nil { @@ -64,244 +115,227 @@ func TestNewBlock(t *testing.T) { } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestECB$ +// go test -v -cover -run=^TestECB$ func TestECB(t *testing.T) { - cases := map[string]*testResult{ - "": { - bs: []byte{254, 194, 2, 244, 7, 195, 25, 158, 172, 88, 119, 145, 234, 39, 193, 11}, - hexString: "fec202f407c3199eac587791ea27c10b", - base64String: "/sIC9AfDGZ6sWHeR6ifBCw==", + testCases := []testCase{ + { + Data: []byte(""), + EncryptData: []byte{46, 71, 207, 219, 18, 238, 77, 216, 177, 177, 186, 232, 19, 197, 96, 172}, + EncryptDataHex: []byte("2e47cfdb12ee4dd8b1b1bae813c560ac"), + EncryptDataBase64: []byte("LkfP2xLuTdixsbroE8VgrA=="), }, - "123": { - bs: []byte{106, 180, 23, 31, 55, 116, 164, 43, 132, 49, 25, 42, 117, 236, 143, 154}, - hexString: "6ab4171f3774a42b8431192a75ec8f9a", - base64String: "arQXHzd0pCuEMRkqdeyPmg==", + { + Data: []byte("123"), + EncryptData: []byte{173, 62, 123, 195, 111, 6, 34, 76, 86, 148, 68, 12, 179, 251, 241, 79}, + EncryptDataHex: []byte("ad3e7bc36f06224c5694440cb3fbf14f"), + EncryptDataBase64: []byte("rT57w28GIkxWlEQMs/vxTw=="), }, - "你好,世界": { - bs: []byte{100, 112, 82, 33, 74, 230, 164, 206, 182, 33, 100, 255, 171, 204, 229, 193}, - hexString: "647052214ae6a4ceb62164ffabcce5c1", - base64String: "ZHBSIUrmpM62IWT/q8zlwQ==", + { + Data: []byte("你好,世界"), + EncryptData: []byte{219, 84, 167, 180, 5, 230, 163, 133, 6, 168, 131, 20, 69, 151, 26, 163}, + EncryptDataHex: []byte("db54a7b405e6a38506a8831445971aa3"), + EncryptDataBase64: []byte("21SntAXmo4UGqIMURZcaow=="), }, } - for input, expect := range cases { - encrypted, err := EncryptECB(testKey, cryptox.PaddingPKCS7, []byte(input)) - if err != nil { - t.Fatal(err) - } - - if err = expect.compareTo(encrypted); err != nil { - t.Fatal(err) - } + encrypt := func(data []byte, opts ...Option) ([]byte, error) { + opts = append(opts, WithPKCS7()) + return EncryptECB(data, testKey, opts...) + } - decrypted, err := DecryptECB(testKey, cryptox.PaddingPKCS7, encrypted) - if err != nil { - t.Fatal(err) - } + decrypt := func(data []byte, opts ...Option) ([]byte, error) { + opts = append(opts, WithPKCS7()) + return DecryptECB(data, testKey, opts...) + } - if string(decrypted) != input { - t.Fatalf("input %s: decrypted %+v != input %+v", input, decrypted, []byte(input)) - } + if err := testEncryptAndDecrypt(t.Name(), encrypt, decrypt, testCases); err != nil { + t.Fatal(err) } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestCBC$ +// go test -v -cover -run=^TestCBC$ func TestCBC(t *testing.T) { - cases := map[string]*testResult{ - "": { - bs: []byte{18, 228, 65, 237, 113, 28, 196, 195, 209, 118, 249, 189, 134, 92, 184, 59}, - hexString: "12e441ed711cc4c3d176f9bd865cb83b", - base64String: "EuRB7XEcxMPRdvm9hly4Ow==", + testCases := []testCase{ + { + Data: []byte(""), + EncryptData: []byte{204, 67, 101, 243, 145, 108, 173, 196, 169, 232, 196, 238, 119, 228, 54, 135}, + EncryptDataHex: []byte("cc4365f3916cadc4a9e8c4ee77e43687"), + EncryptDataBase64: []byte("zENl85FsrcSp6MTud+Q2hw=="), }, - "123": { - bs: []byte{239, 166, 198, 112, 30, 48, 8, 15, 67, 248, 202, 52, 213, 118, 239, 235}, - hexString: "efa6c6701e30080f43f8ca34d576efeb", - base64String: "76bGcB4wCA9D+Mo01Xbv6w==", + { + Data: []byte("123"), + EncryptData: []byte{75, 118, 159, 30, 23, 149, 246, 32, 15, 157, 198, 43, 188, 232, 232, 176}, + EncryptDataHex: []byte("4b769f1e1795f6200f9dc62bbce8e8b0"), + EncryptDataBase64: []byte("S3afHheV9iAPncYrvOjosA=="), }, - "你好,世界": { - bs: []byte{31, 24, 138, 155, 181, 90, 114, 79, 168, 189, 154, 134, 242, 22, 7, 90}, - hexString: "1f188a9bb55a724fa8bd9a86f216075a", - base64String: "HxiKm7Vack+ovZqG8hYHWg==", + { + Data: []byte("你好,世界"), + EncryptData: []byte{102, 137, 142, 49, 76, 207, 80, 189, 78, 85, 77, 31, 166, 172, 180, 144}, + EncryptDataHex: []byte("66898e314ccf50bd4e554d1fa6acb490"), + EncryptDataBase64: []byte("ZomOMUzPUL1OVU0fpqy0kA=="), }, } - for input, expect := range cases { - encrypted, err := EncryptCBC(testKey, testIV, cryptox.PaddingPKCS7, []byte(input)) - if err != nil { - t.Fatal(err) - } - - if err = expect.compareTo(encrypted); err != nil { - t.Fatal(err) - } + encrypt := func(data []byte, opts ...Option) ([]byte, error) { + opts = append(opts, WithPKCS7()) + return EncryptCBC(data, testKey, testIV, opts...) + } - decrypted, err := DecryptCBC(testKey, testIV, cryptox.PaddingPKCS7, encrypted) - if err != nil { - t.Fatal(err) - } + decrypt := func(data []byte, opts ...Option) ([]byte, error) { + opts = append(opts, WithPKCS7()) + return DecryptCBC(data, testKey, testIV, opts...) + } - if string(decrypted) != input { - t.Fatalf("input %s: decrypted %+v != input %+v", input, decrypted, []byte(input)) - } + if err := testEncryptAndDecrypt(t.Name(), encrypt, decrypt, testCases); err != nil { + t.Fatal(err) } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestCFB$ +// go test -v -cover -run=^TestCFB$ func TestCFB(t *testing.T) { - cases := map[string]*testResult{ - "": { - bs: []byte{129, 42, 238, 182, 0, 143, 73, 239, 33, 57, 26, 89, 78, 230, 185, 139}, - hexString: "812aeeb6008f49ef21391a594ee6b98b", - base64String: "gSrutgCPSe8hORpZTua5iw==", + testCases := []testCase{ + { + Data: []byte(""), + EncryptData: []byte{}, + EncryptDataHex: []byte(""), + EncryptDataBase64: []byte(""), }, - "123": { - bs: []byte{160, 8, 205, 171, 29, 146, 84, 242, 60, 36, 7, 68, 83, 251, 164, 150}, - hexString: "a008cdab1d9254f23c24074453fba496", - base64String: "oAjNqx2SVPI8JAdEU/uklg==", + { + Data: []byte("123"), + EncryptData: []byte{108, 145, 37}, + EncryptDataHex: []byte("6c9125"), + EncryptDataBase64: []byte("bJEl"), }, - "你好,世界": { - bs: []byte{117, 135, 94, 67, 181, 34, 182, 67, 189, 205, 178, 223, 185, 99, 37, 154}, - hexString: "75875e43b522b643bdcdb2dfb963259a", - base64String: "dYdeQ7UitkO9zbLfuWMlmg==", + { + Data: []byte("你好,世界"), + EncryptData: []byte{185, 30, 182, 141, 214, 82, 9, 57, 81, 9, 57, 101, 242, 49, 117}, + EncryptDataHex: []byte("b91eb68dd652093951093965f23175"), + EncryptDataBase64: []byte("uR62jdZSCTlRCTll8jF1"), }, } - for input, expect := range cases { - encrypted, err := EncryptCFB(testKey, testIV, cryptox.PaddingPKCS7, []byte(input)) - if err != nil { - t.Fatal(err) - } - - if err = expect.compareTo(encrypted); err != nil { - t.Fatal(err) - } + encrypt := func(data []byte, opts ...Option) ([]byte, error) { + return EncryptCFB(data, testKey, testIV, opts...) + } - decrypted, err := DecryptCFB(testKey, testIV, cryptox.PaddingPKCS7, encrypted) - if err != nil { - t.Fatal(err) - } + decrypt := func(data []byte, opts ...Option) ([]byte, error) { + return DecryptCFB(data, testKey, testIV, opts...) + } - if string(decrypted) != input { - t.Fatalf("input %s: decrypted %+v != input %+v", input, decrypted, []byte(input)) - } + if err := testEncryptAndDecrypt(t.Name(), encrypt, decrypt, testCases); err != nil { + t.Fatal(err) } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestOFB$ +// go test -v -cover -run=^TestOFB$ func TestOFB(t *testing.T) { - cases := map[string]*testResult{ - "": { - bs: []byte{129, 42, 238, 182, 0, 143, 73, 239, 33, 57, 26, 89, 78, 230, 185, 139}, - hexString: "812aeeb6008f49ef21391a594ee6b98b", - base64String: "gSrutgCPSe8hORpZTua5iw==", + testCases := []testCase{ + { + Data: []byte(""), + EncryptData: []byte{}, + EncryptDataHex: []byte(""), + EncryptDataBase64: []byte(""), }, - "123": { - bs: []byte{160, 8, 205, 171, 29, 146, 84, 242, 60, 36, 7, 68, 83, 251, 164, 150}, - hexString: "a008cdab1d9254f23c24074453fba496", - base64String: "oAjNqx2SVPI8JAdEU/uklg==", + { + Data: []byte("123"), + EncryptData: []byte{108, 145, 37}, + EncryptDataHex: []byte("6c9125"), + EncryptDataBase64: []byte("bJEl"), }, - "你好,世界": { - bs: []byte{117, 135, 94, 67, 181, 34, 182, 67, 189, 205, 178, 223, 185, 99, 37, 154}, - hexString: "75875e43b522b643bdcdb2dfb963259a", - base64String: "dYdeQ7UitkO9zbLfuWMlmg==", + { + Data: []byte("你好,世界"), + EncryptData: []byte{185, 30, 182, 141, 214, 82, 9, 57, 81, 9, 57, 101, 242, 49, 117}, + EncryptDataHex: []byte("b91eb68dd652093951093965f23175"), + EncryptDataBase64: []byte("uR62jdZSCTlRCTll8jF1"), }, } - for input, expect := range cases { - encrypted, err := EncryptOFB(testKey, testIV, cryptox.PaddingPKCS7, []byte(input)) - if err != nil { - t.Fatal(err) - } - - if err = expect.compareTo(encrypted); err != nil { - t.Fatal(err) - } + encrypt := func(data []byte, opts ...Option) ([]byte, error) { + return EncryptOFB(data, testKey, testIV, opts...) + } - decrypted, err := DecryptOFB(testKey, testIV, cryptox.PaddingPKCS7, encrypted) - if err != nil { - t.Fatal(err) - } + decrypt := func(data []byte, opts ...Option) ([]byte, error) { + return DecryptOFB(data, testKey, testIV, opts...) + } - if string(decrypted) != input { - t.Fatalf("input %s: decrypted %+v != input %+v", input, decrypted, []byte(input)) - } + if err := testEncryptAndDecrypt(t.Name(), encrypt, decrypt, testCases); err != nil { + t.Fatal(err) } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestCTR$ +// go test -v -cover -run=^TestCTR$ func TestCTR(t *testing.T) { - cases := map[string]*testResult{ - "": { - bs: []byte{129, 42, 238, 182, 0, 143, 73, 239, 33, 57, 26, 89, 78, 230, 185, 139}, - hexString: "812aeeb6008f49ef21391a594ee6b98b", - base64String: "gSrutgCPSe8hORpZTua5iw==", + testCases := []testCase{ + { + Data: []byte(""), + EncryptData: []byte{}, + EncryptDataHex: []byte(""), + EncryptDataBase64: []byte(""), }, - "123": { - bs: []byte{160, 8, 205, 171, 29, 146, 84, 242, 60, 36, 7, 68, 83, 251, 164, 150}, - hexString: "a008cdab1d9254f23c24074453fba496", - base64String: "oAjNqx2SVPI8JAdEU/uklg==", + { + Data: []byte("123"), + EncryptData: []byte{108, 145, 37}, + EncryptDataHex: []byte("6c9125"), + EncryptDataBase64: []byte("bJEl"), }, - "你好,世界": { - bs: []byte{117, 135, 94, 67, 181, 34, 182, 67, 189, 205, 178, 223, 185, 99, 37, 154}, - hexString: "75875e43b522b643bdcdb2dfb963259a", - base64String: "dYdeQ7UitkO9zbLfuWMlmg==", + { + Data: []byte("你好,世界"), + EncryptData: []byte{185, 30, 182, 141, 214, 82, 9, 57, 81, 9, 57, 101, 242, 49, 117}, + EncryptDataHex: []byte("b91eb68dd652093951093965f23175"), + EncryptDataBase64: []byte("uR62jdZSCTlRCTll8jF1"), }, } - for input, expect := range cases { - encrypted, err := EncryptCTR(testKey, testIV, cryptox.PaddingPKCS7, []byte(input)) - if err != nil { - t.Fatal(err) - } - - if err = expect.compareTo(encrypted); err != nil { - t.Fatal(err) - } + encrypt := func(data []byte, opts ...Option) ([]byte, error) { + return EncryptCTR(data, testKey, testIV, opts...) + } - decrypted, err := DecryptCTR(testKey, testIV, cryptox.PaddingPKCS7, encrypted) - if err != nil { - t.Fatal(err) - } + decrypt := func(data []byte, opts ...Option) ([]byte, error) { + return DecryptCTR(data, testKey, testIV, opts...) + } - if string(decrypted) != input { - t.Fatalf("input %s: decrypted %+v != input %+v", input, decrypted, []byte(input)) - } + if err := testEncryptAndDecrypt(t.Name(), encrypt, decrypt, testCases); err != nil { + t.Fatal(err) } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestGCM$ +// go test -v -cover -run=^TestGCM$ func TestGCM(t *testing.T) { - cases := map[string]*testResult{ - "123": { - bs: []byte{249, 132, 42, 40, 152, 17, 118, 4, 46, 97, 216, 169, 163, 62, 71, 150, 67, 189, 73}, - hexString: "f9842a28981176042e61d8a9a33e479643bd49", - base64String: "+YQqKJgRdgQuYdipoz5HlkO9SQ==", + testCases := []testCase{ + { + Data: []byte(""), + EncryptData: []byte{135, 179, 184, 127, 41, 243, 84, 60, 61, 129, 163, 91, 171, 195, 246, 98}, + EncryptDataHex: []byte("87b3b87f29f3543c3d81a35babc3f662"), + EncryptDataBase64: []byte("h7O4fynzVDw9gaNbq8P2Yg=="), + }, + { + Data: []byte("123"), + EncryptData: []byte{224, 234, 86, 29, 157, 167, 33, 35, 34, 123, 227, 204, 243, 177, 205, 151, 173, 141, 174}, + EncryptDataHex: []byte("e0ea561d9da72123227be3ccf3b1cd97ad8dae"), + EncryptDataBase64: []byte("4OpWHZ2nISMie+PM87HNl62Nrg=="), }, - "你好,世界": { - bs: []byte{44, 11, 185, 159, 152, 100, 94, 1, 82, 180, 112, 131, 14, 35, 63, 39, 98, 215, 167, 243, 173, 80, 155, 27, 228, 197, 155, 234, 236, 204, 91}, - hexString: "2c0bb99f98645e0152b470830e233f2762d7a7f3ad509b1be4c59beaeccc5b", - base64String: "LAu5n5hkXgFStHCDDiM/J2LXp/OtUJsb5MWb6uzMWw==", + { + Data: []byte("你好,世界"), + EncryptData: []byte{53, 101, 197, 113, 143, 115, 148, 86, 178, 196, 74, 153, 208, 219, 180, 14, 230, 66, 70, 95, 244, 28, 225, 107, 243, 182, 147, 187, 199, 202, 220}, + EncryptDataHex: []byte("3565c5718f739456b2c44a99d0dbb40ee642465ff41ce16bf3b693bbc7cadc"), + EncryptDataBase64: []byte("NWXFcY9zlFayxEqZ0Nu0DuZCRl/0HOFr87aTu8fK3A=="), }, } nonce := []byte("123456abcdef") - for input, expect := range cases { - encrypted, err := EncryptGCM(testKey, nonce, nil, []byte(input)) - if err != nil { - t.Fatal(err) - } + additional := []byte("8765432112345678") - if err = expect.compareTo(encrypted); err != nil { - t.Fatal(err) - } + encrypt := func(data []byte, opts ...Option) ([]byte, error) { + opts = append(opts, WithAdditional(additional)) + return EncryptGCM(data, testKey, nonce, opts...) + } - decrypted, err := DecryptGCM(testKey, nonce, nil, encrypted) - if err != nil { - t.Fatal(err) - } + decrypt := func(data []byte, opts ...Option) ([]byte, error) { + opts = append(opts, WithAdditional(additional)) + return DecryptGCM(data, testKey, nonce, opts...) + } - if string(decrypted) != input { - t.Fatalf("input %s: decrypted %+v != input %+v", input, decrypted, []byte(input)) - } + if err := testEncryptAndDecrypt(t.Name(), encrypt, decrypt, testCases); err != nil { + t.Fatal(err) } } diff --git a/aes/gcm.go b/aes/gcm.go deleted file mode 100644 index 8c6629d..0000000 --- a/aes/gcm.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2024 FishGoddess. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package aes - -import ( - "github.com/FishGoddess/cryptox" -) - -// GenerateGCMNonce generates a standard nonce for gcm. -func GenerateGCMNonce() cryptox.Bytes { - standardNonceSize := 12 - return cryptox.GenerateBytes(standardNonceSize) -} diff --git a/aes/nonce.go b/aes/nonce.go new file mode 100644 index 0000000..fd722cd --- /dev/null +++ b/aes/nonce.go @@ -0,0 +1,13 @@ +// Copyright 2024 FishGoddess. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package aes + +import "github.com/FishGoddess/cryptox/bytes/rand" + +// Nonce returns a standard nonce for gcm. +func Nonce() []byte { + nonceSize := 12 + return rand.Bytes(nonceSize) +} diff --git a/aes/gcm_test.go b/aes/nonce_test.go similarity index 63% rename from aes/gcm_test.go rename to aes/nonce_test.go index 13dcb31..a566803 100644 --- a/aes/gcm_test.go +++ b/aes/nonce_test.go @@ -8,12 +8,12 @@ import ( "testing" ) -// go test -v -cover -count=1 -test.cpu=1 -run=^TestGenerateGCMNonce$ -func TestGenerateGCMNonce(t *testing.T) { - nonce := GenerateGCMNonce() +// go test -v -cover -run=^TestNonce$ +func TestNonce(t *testing.T) { + nonce := Nonce() if len(nonce) != 12 { t.Fatalf("len(nonce) %d is wrong", len(nonce)) } - t.Log(nonce) + t.Logf("%s\n", nonce) } diff --git a/aes/options.go b/aes/options.go new file mode 100644 index 0000000..d44d7c2 --- /dev/null +++ b/aes/options.go @@ -0,0 +1,78 @@ +// Copyright 2025 FishGoddess. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package aes + +import ( + "github.com/FishGoddess/cryptox/bytes/encoding" + "github.com/FishGoddess/cryptox/bytes/padding" +) + +type Config struct { + encoding encoding.Encoding + padding padding.Padding + additional []byte +} + +func newConfig() *Config { + conf := &Config{ + encoding: encoding.None{}, + padding: padding.None{}, + additional: nil, + } + + return conf +} + +func (c *Config) Apply(opts ...Option) *Config { + for _, opt := range opts { + opt(c) + } + + return c +} + +type Option func(conf *Config) + +// WithHex sets hex encoding to config. +func WithHex() Option { + return func(conf *Config) { + conf.encoding = encoding.Hex{} + } +} + +// WithBase64 sets base64 encoding to config. +func WithBase64() Option { + return func(conf *Config) { + conf.encoding = encoding.Base64{} + } +} + +// WithZero sets zero padding to config. +func WithZero() Option { + return func(conf *Config) { + conf.padding = padding.Zero{} + } +} + +// WithPKCS5 sets pkcs5 padding to config. +func WithPKCS5() Option { + return func(conf *Config) { + conf.padding = padding.PKCS5{} + } +} + +// WithPKCS7 sets pkcs7 padding to config. +func WithPKCS7() Option { + return func(conf *Config) { + conf.padding = padding.PKCS7{} + } +} + +// WithAdditional sets additional to config. +func WithAdditional(additional []byte) Option { + return func(conf *Config) { + conf.additional = additional + } +} diff --git a/aes/options_test.go b/aes/options_test.go new file mode 100644 index 0000000..075f5ad --- /dev/null +++ b/aes/options_test.go @@ -0,0 +1,67 @@ +// Copyright 2025 FishGoddess. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package aes + +import ( + "fmt" + "slices" + "testing" + + "github.com/FishGoddess/cryptox/bytes/encoding" + "github.com/FishGoddess/cryptox/bytes/padding" +) + +// go test -v -cover -run=^TestConfig$ +func TestConfig(t *testing.T) { + additional := []byte("additional") + + opts := []Option{ + WithHex(), + WithZero(), + WithAdditional(additional), + } + + conf := newConfig().Apply(opts...) + + got := fmt.Sprintf("%T", conf.encoding) + expect := fmt.Sprintf("%T", encoding.Hex{}) + if got != expect { + t.Fatalf("got %s != expect %s", got, expect) + } + + conf.Apply(WithBase64()) + + got = fmt.Sprintf("%T", conf.encoding) + expect = fmt.Sprintf("%T", encoding.Base64{}) + if got != expect { + t.Fatalf("got %s != expect %s", got, expect) + } + + got = fmt.Sprintf("%T", conf.padding) + expect = fmt.Sprintf("%T", padding.Zero{}) + if got != expect { + t.Fatalf("got %s != expect %s", got, expect) + } + + conf.Apply(WithPKCS5()) + + got = fmt.Sprintf("%T", conf.padding) + expect = fmt.Sprintf("%T", padding.PKCS5{}) + if got != expect { + t.Fatalf("got %s != expect %s", got, expect) + } + + conf.Apply(WithPKCS7()) + + got = fmt.Sprintf("%T", conf.padding) + expect = fmt.Sprintf("%T", padding.PKCS7{}) + if got != expect { + t.Fatalf("got %s != expect %s", got, expect) + } + + if !slices.Equal(conf.additional, additional) { + t.Fatalf("got %s != expect %s", conf.additional, additional) + } +} diff --git a/bytes.go b/bytes.go deleted file mode 100644 index e0b4228..0000000 --- a/bytes.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2024 FishGoddess. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package cryptox - -import ( - "encoding/base64" - "encoding/hex" - "io" - "os" -) - -type Bytes []byte - -// ParseHex a hex string to bytes. -func ParseHex(hexString string) (Bytes, error) { - return hex.DecodeString(hexString) -} - -// ParseBase64 a base64 string to bytes. -func ParseBase64(base64String string) (Bytes, error) { - return base64.StdEncoding.DecodeString(base64String) -} - -// Hex returns Bytes as hex. -func (bs Bytes) Hex() string { - return hex.EncodeToString(bs) -} - -// Base64 returns Bytes as base64. -func (bs Bytes) Base64() string { - return base64.StdEncoding.EncodeToString(bs) -} - -// Clone clones bs and returns a new slice. -func (bs Bytes) Clone() Bytes { - newSlice := make([]byte, len(bs)) - copy(newSlice, bs) - - return newSlice -} - -func (bs Bytes) newFile(path string) (*os.File, error) { - flag := os.O_CREATE | os.O_EXCL | os.O_WRONLY - mode := os.FileMode(0644) - - return os.OpenFile(path, flag, mode) -} - -// WriteTo writes bytes to writer. -func (bs Bytes) WriteTo(writer io.Writer) (n int64, err error) { - nn, err := writer.Write(bs) - return int64(nn), err -} - -// WriteToFile writes bytes to file. -func (bs Bytes) WriteToFile(path string) (n int64, err error) { - file, err := bs.newFile(path) - if err != nil { - return 0, err - } - - defer file.Close() - return bs.WriteTo(file) -} diff --git a/bytes/encoding/encoding.go b/bytes/encoding/encoding.go new file mode 100644 index 0000000..77b311b --- /dev/null +++ b/bytes/encoding/encoding.go @@ -0,0 +1,80 @@ +// Copyright 2025 FishGoddess. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package encoding + +import ( + "encoding/base64" + "encoding/hex" +) + +type Encoding interface { + // Encode encodes the byte slice. + Encode(data []byte) []byte + + // Decode decodes the byte slice. + Decode(data []byte) ([]byte, error) +} + +type None struct{} + +// Encode returns the original byte slice. +func (None) Encode(data []byte) []byte { + return data +} + +// Decode returns the original byte slice. +func (None) Decode(data []byte) ([]byte, error) { + return data, nil +} + +type Hex struct{} + +// Encode encodes the byte slice with hex encoding. +func (Hex) Encode(data []byte) []byte { + n := hex.EncodedLen(len(data)) + buffer := make([]byte, n) + + n = hex.Encode(buffer, data) + return buffer[:n] +} + +// Decode decodes the byte slice with hex encoding. +func (Hex) Decode(data []byte) ([]byte, error) { + n := hex.DecodedLen(len(data)) + buffer := make([]byte, n) + + n, err := hex.Decode(buffer, data) + if err != nil { + return nil, err + } + + return buffer[:n], nil +} + +type Base64 struct{} + +// Encode encodes the byte slice with base64 encoding. +func (Base64) Encode(data []byte) []byte { + enc := base64.StdEncoding + n := enc.EncodedLen(len(data)) + buffer := make([]byte, n) + + enc.Encode(buffer, data) + return buffer[:n] +} + +// Decode decodes the byte slice with base64 encoding. +func (Base64) Decode(data []byte) ([]byte, error) { + enc := base64.StdEncoding + n := enc.DecodedLen(len(data)) + buffer := make([]byte, n) + + n, err := enc.Decode(buffer, data) + if err != nil { + return nil, err + } + + return buffer[:n], nil +} diff --git a/bytes/encoding/encoding_test.go b/bytes/encoding/encoding_test.go new file mode 100644 index 0000000..57c8e04 --- /dev/null +++ b/bytes/encoding/encoding_test.go @@ -0,0 +1,78 @@ +// Copyright 2025 FishGoddess. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package encoding + +import ( + "fmt" + "slices" + "testing" +) + +type testCase struct { + Data []byte + EncodingData []byte +} + +func testEncoding(name string, encoding Encoding, testCases []testCase) error { + for _, testCase := range testCases { + got := encoding.Encode(testCase.Data) + want := testCase.EncodingData + + if !slices.Equal(got, want) { + return fmt.Errorf("%s data %q: got %+v != want %+v", name, testCase.Data, got, want) + } + + got, err := encoding.Decode(got) + if err != nil { + return err + } + + want = testCase.Data + if !slices.Equal(got, want) { + return fmt.Errorf("%s data %q: got %+v != want %+v", name, testCase.Data, got, want) + } + } + + return nil +} + +// go test -v -cover -run=^TestNone$ +func TestNone(t *testing.T) { + testCases := []testCase{ + {Data: []byte{}, EncodingData: []byte{}}, + {Data: []byte("123"), EncodingData: []byte("123")}, + {Data: []byte("你好,世界"), EncodingData: []byte("你好,世界")}, + } + + if err := testEncoding(t.Name(), None{}, testCases); err != nil { + t.Fatal(err) + } +} + +// go test -v -cover -run=^TestHex$ +func TestHex(t *testing.T) { + testCases := []testCase{ + {Data: []byte{}, EncodingData: []byte{}}, + {Data: []byte("123"), EncodingData: []byte("313233")}, + {Data: []byte("你好,世界"), EncodingData: []byte("e4bda0e5a5bdefbc8ce4b896e7958c")}, + } + + if err := testEncoding(t.Name(), Hex{}, testCases); err != nil { + t.Fatal(err) + } +} + +// go test -v -cover -run=^TestBase64$ +func TestBase64(t *testing.T) { + testCases := []testCase{ + {Data: []byte{}, EncodingData: []byte{}}, + {Data: []byte("123"), EncodingData: []byte("MTIz")}, + {Data: []byte("你好,世界"), EncodingData: []byte("5L2g5aW977yM5LiW55WM")}, + } + + if err := testEncoding(t.Name(), Base64{}, testCases); err != nil { + t.Fatal(err) + } +} diff --git a/bytes/padding/padding.go b/bytes/padding/padding.go new file mode 100644 index 0000000..e21dd23 --- /dev/null +++ b/bytes/padding/padding.go @@ -0,0 +1,109 @@ +// Copyright 2025 FishGoddess. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package padding + +import "fmt" + +type Padding interface { + // Pad pads some bytes to the byte slice. + Pad(data []byte, blockSize int) []byte + + // Unpad unpads some bytes from the byte slice. + Unpad(data []byte, blockSize int) ([]byte, error) +} + +type None struct{} + +// Pad returns the original byte slice. +func (None) Pad(data []byte, blockSize int) []byte { + return data +} + +// Unpad returns the original byte slice. +func (None) Unpad(data []byte, blockSize int) ([]byte, error) { + return data, nil +} + +type Zero struct{} + +// Pad pads some bytes to the byte slice in zero way. +func (Zero) Pad(data []byte, blockSize int) []byte { + padding := blockSize - (len(data) % blockSize) + if padding == blockSize { + return data + } + + for i := 0; i < padding; i++ { + data = append(data, 0) + } + + return data +} + +// Unpad unpads some bytes from the byte slice in zero way. +func (Zero) Unpad(data []byte, blockSize int) ([]byte, error) { + index := len(data) + for index > 0 && data[index-1] == 0 { + index-- + } + + return data[:index], nil +} + +type PKCS5 struct{} + +// Pad pads some bytes to the byte slice in pkcs5 way. +func (PKCS5) Pad(data []byte, blockSize int) []byte { + padding := blockSize - (len(data) % blockSize) + for i := 0; i < padding; i++ { + data = append(data, byte(padding)) + } + + return data +} + +// Unpad unpads some bytes from the byte slice in pkcs5 way. +func (PKCS5) Unpad(data []byte, blockSize int) ([]byte, error) { + length := len(data) + number := int(data[length-1]) + + if number > length { + return nil, fmt.Errorf("cryptox/padding: unpad number %d > length %d", number, length) + } + + if number > blockSize { + return nil, fmt.Errorf("cryptox/padding: unpad number %d > blockSize %d", number, blockSize) + } + + return data[:length-number], nil +} + +type PKCS7 struct{} + +// Pad pads some bytes to the byte slice in pkcs7 way. +func (PKCS7) Pad(data []byte, blockSize int) []byte { + padding := blockSize - (len(data) % blockSize) + for i := 0; i < padding; i++ { + data = append(data, byte(padding)) + } + + return data +} + +// Unpad unpads some bytes from the byte slice in pkcs7 way. +func (PKCS7) Unpad(data []byte, blockSize int) ([]byte, error) { + length := len(data) + number := int(data[length-1]) + + if number > length { + return nil, fmt.Errorf("cryptox/padding: unpad number %d > length %d", number, length) + } + + if number > blockSize { + return nil, fmt.Errorf("cryptox/padding: unpad number %d > blockSize %d", number, blockSize) + } + + return data[:length-number], nil +} diff --git a/bytes/padding/padding_test.go b/bytes/padding/padding_test.go new file mode 100644 index 0000000..10f9a98 --- /dev/null +++ b/bytes/padding/padding_test.go @@ -0,0 +1,235 @@ +// Copyright 2025 FishGoddess. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package padding + +import ( + "fmt" + "slices" + "testing" +) + +type testCase struct { + Data []byte + PaddingData []byte +} + +func testPadding(name string, blockSize int, padding Padding, testCases []testCase) error { + for _, testCase := range testCases { + got := padding.Pad(testCase.Data, blockSize) + want := testCase.PaddingData + + if !slices.Equal(got, want) { + return fmt.Errorf("%s data %+v: got %+v != want %+v", name, testCase.Data, got, want) + } + + got, err := padding.Unpad(got, blockSize) + if err != nil { + return err + } + + want = testCase.Data + if !slices.Equal(got, want) { + return fmt.Errorf("%s data %+v: got %+v != want %+v", name, testCase.Data, got, want) + } + } + + return nil +} + +// go test -v -cover -run=^TestNone$ +func TestNone(t *testing.T) { + testCases := []testCase{ + { + Data: []byte{}, + PaddingData: []byte{}, + }, + { + Data: []byte{1, 2, 3, 4, 5}, + PaddingData: []byte{1, 2, 3, 4, 5}, + }, + { + Data: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + PaddingData: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + }, + { + Data: []byte{1, 2, 3, 4, 5, 6, 7, 8, 1, 1, 1, 1, 1, 1, 1, 1}, + PaddingData: []byte{1, 2, 3, 4, 5, 6, 7, 8, 1, 1, 1, 1, 1, 1, 1, 1}, + }, + } + + if err := testPadding(t.Name(), 8, None{}, testCases); err != nil { + t.Fatal(err) + } + + testCases = []testCase{ + { + Data: []byte{}, + PaddingData: []byte{}, + }, + { + Data: []byte{1, 2, 3, 4, 5}, + PaddingData: []byte{1, 2, 3, 4, 5}, + }, + { + Data: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + PaddingData: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + }, + { + Data: []byte{1, 2, 3, 4, 5, 6, 7, 8, 1, 1, 1, 1, 1, 1, 1, 1}, + PaddingData: []byte{1, 2, 3, 4, 5, 6, 7, 8, 1, 1, 1, 1, 1, 1, 1, 1}, + }, + } + + if err := testPadding(t.Name(), 16, None{}, testCases); err != nil { + t.Fatal(err) + } +} + +// go test -v -cover -run=^TestZero$ +func TestZero(t *testing.T) { + testCases := []testCase{ + { + Data: []byte{}, + PaddingData: []byte{}, + }, + { + Data: []byte{1, 2, 3, 4, 5}, + PaddingData: []byte{1, 2, 3, 4, 5, 0, 0, 0}, + }, + { + Data: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + PaddingData: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + }, + { + Data: []byte{1, 2, 3, 4, 5, 6, 7, 8, 1, 1, 1, 1, 1, 1, 1, 1}, + PaddingData: []byte{1, 2, 3, 4, 5, 6, 7, 8, 1, 1, 1, 1, 1, 1, 1, 1}, + }, + } + + if err := testPadding(t.Name(), 8, Zero{}, testCases); err != nil { + t.Fatal(err) + } + + testCases = []testCase{ + { + Data: []byte{}, + PaddingData: []byte{}, + }, + { + Data: []byte{1, 2, 3, 4, 5}, + PaddingData: []byte{1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + Data: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + PaddingData: []byte{1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + Data: []byte{1, 2, 3, 4, 5, 6, 7, 8, 1, 1, 1, 1, 1, 1, 1, 1}, + PaddingData: []byte{1, 2, 3, 4, 5, 6, 7, 8, 1, 1, 1, 1, 1, 1, 1, 1}, + }, + } + + if err := testPadding(t.Name(), 16, Zero{}, testCases); err != nil { + t.Fatal(err) + } +} + +// go test -v -cover -run=^TestPKCS5$ +func TestPKCS5(t *testing.T) { + testCases := []testCase{ + { + Data: []byte{}, + PaddingData: []byte{8, 8, 8, 8, 8, 8, 8, 8}, + }, + { + Data: []byte{1, 2, 3, 4, 5}, + PaddingData: []byte{1, 2, 3, 4, 5, 3, 3, 3}, + }, + { + Data: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + PaddingData: []byte{1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + }, + { + Data: []byte{1, 2, 3, 4, 5, 6, 7, 8, 1, 1, 1, 1, 1, 1, 1, 1}, + PaddingData: []byte{1, 2, 3, 4, 5, 6, 7, 8, 1, 1, 1, 1, 1, 1, 1, 1, 8, 8, 8, 8, 8, 8, 8, 8}, + }, + } + + if err := testPadding(t.Name(), 8, PKCS5{}, testCases); err != nil { + t.Fatal(err) + } + + testCases = []testCase{ + { + Data: []byte{}, + PaddingData: []byte{16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}, + }, + { + Data: []byte{1, 2, 3, 4, 5}, + PaddingData: []byte{1, 2, 3, 4, 5, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11}, + }, + { + Data: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + PaddingData: []byte{1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + }, + { + Data: []byte{1, 2, 3, 4, 5, 6, 7, 8, 1, 1, 1, 1, 1, 1, 1, 1}, + PaddingData: []byte{1, 2, 3, 4, 5, 6, 7, 8, 1, 1, 1, 1, 1, 1, 1, 1, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}, + }, + } + + if err := testPadding(t.Name(), 16, PKCS5{}, testCases); err != nil { + t.Fatal(err) + } +} + +// go test -v -cover -run=^TestPKCS7$ +func TestPKCS7(t *testing.T) { + testCases := []testCase{ + { + Data: []byte{}, + PaddingData: []byte{8, 8, 8, 8, 8, 8, 8, 8}, + }, + { + Data: []byte{1, 2, 3, 4, 5}, + PaddingData: []byte{1, 2, 3, 4, 5, 3, 3, 3}, + }, + { + Data: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + PaddingData: []byte{1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + }, + { + Data: []byte{1, 2, 3, 4, 5, 6, 7, 8, 1, 1, 1, 1, 1, 1, 1, 1}, + PaddingData: []byte{1, 2, 3, 4, 5, 6, 7, 8, 1, 1, 1, 1, 1, 1, 1, 1, 8, 8, 8, 8, 8, 8, 8, 8}, + }, + } + + if err := testPadding(t.Name(), 8, PKCS7{}, testCases); err != nil { + t.Fatal(err) + } + + testCases = []testCase{ + { + Data: []byte{}, + PaddingData: []byte{16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}, + }, + { + Data: []byte{1, 2, 3, 4, 5}, + PaddingData: []byte{1, 2, 3, 4, 5, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11}, + }, + { + Data: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + PaddingData: []byte{1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + }, + { + Data: []byte{1, 2, 3, 4, 5, 6, 7, 8, 1, 1, 1, 1, 1, 1, 1, 1}, + PaddingData: []byte{1, 2, 3, 4, 5, 6, 7, 8, 1, 1, 1, 1, 1, 1, 1, 1, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}, + }, + } + + if err := testPadding(t.Name(), 16, PKCS5{}, testCases); err != nil { + t.Fatal(err) + } +} diff --git a/bytes/rand/rand.go b/bytes/rand/rand.go new file mode 100644 index 0000000..049b244 --- /dev/null +++ b/bytes/rand/rand.go @@ -0,0 +1,36 @@ +// Copyright 2025 FishGoddess. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package rand + +import ( + "math/rand/v2" + "unsafe" +) + +var words = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") + +func appendBytes(data []byte, n int) []byte { + length := len(words) + for i := 0; i < n; i++ { + index := rand.IntN(length) + data = append(data, words[index]) + } + + return data +} + +// Bytes returns n bytes in random which can be used to generate an iv. +func Bytes(n int) []byte { + data := make([]byte, 0, n) + data = appendBytes(data, n) + return data +} + +// String returns a string including n bytes in random which can be used to generate an iv. +func String(n int) string { + data := Bytes(n) + ptr := unsafe.SliceData(data) + return unsafe.String(ptr, len(data)) +} diff --git a/bytes/rand/rand_test.go b/bytes/rand/rand_test.go new file mode 100644 index 0000000..0d8cdf8 --- /dev/null +++ b/bytes/rand/rand_test.go @@ -0,0 +1,42 @@ +// Copyright 2025 FishGoddess. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package rand + +import ( + "bytes" + "testing" +) + +// go test -v -cover -run=^TestBytes$ +func TestBytes(t *testing.T) { + for i := 1; i <= 64; i++ { + data := Bytes(i) + + for _, b := range data { + index := bytes.IndexByte(words, b) + if index < 0 { + t.Fatalf("b %+v not in words %+v", b, words) + } + } + + t.Logf("%s\n", data) + } +} + +// go test -v -cover -run=^TestString$ +func TestString(t *testing.T) { + for i := 1; i <= 64; i++ { + str := String(i) + + for _, r := range str { + index := bytes.IndexRune(words, r) + if index < 0 { + t.Fatalf("b %+v not in words %+v", r, words) + } + } + + t.Logf("%s\n", str) + } +} diff --git a/bytes_test.go b/bytes_test.go deleted file mode 100644 index f3a6eb3..0000000 --- a/bytes_test.go +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2024 FishGoddess. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package cryptox - -import ( - "bytes" - "encoding/base64" - "encoding/hex" - "os" - "path/filepath" - "testing" -) - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestBytes$ -func TestBytes(t *testing.T) { - str := "Hello World" - - bs := Bytes(str) - if !bytes.Equal(bs, []byte(str)) { - t.Fatalf("bs %+v != []byte(str) %+v", bs, []byte(str)) - } - - expect := hex.EncodeToString(bs) - if bs.Hex() != expect { - t.Fatalf("bs.Hex() %s != expect %s", bs.Hex(), expect) - } - - expect = base64.StdEncoding.EncodeToString(bs) - if bs.Base64() != expect { - t.Fatalf("bs.Base64() %s != expect %s", bs.Base64(), expect) - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestBytesClone$ -func TestBytesClone(t *testing.T) { - bs := Bytes("Hello World") - newSlice := bs.Clone() - - if !bytes.Equal(newSlice, bs) { - t.Fatalf("newSlice %+v != bs %+v", newSlice, bs) - } - - bs[0] = '\n' - if bytes.Equal(newSlice, bs) { - t.Fatalf("newSlice %+v == bs %+v", newSlice, bs) - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestBytesWriteTo$ -func TestBytesWriteTo(t *testing.T) { - bs := Bytes("你好,世界") - - var buff bytes.Buffer - n, err := bs.WriteTo(&buff) - if err != nil { - t.Fatal(err) - } - - if n != int64(len(bs)) { - t.Fatalf("n %d != int64(len(bs)) %d", n, int64(len(bs))) - } - - if !bytes.Equal(bs, buff.Bytes()) { - t.Fatalf("bs %+v != buff.Bytes() %+v", bs, buff.Bytes()) - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestBytesWriteToFile$ -func TestBytesWriteToFile(t *testing.T) { - bs := Bytes("你好,世界") - - path := filepath.Join(t.TempDir(), t.Name()+".key") - t.Log("path:", path) - - n, err := bs.WriteToFile(path) - if err != nil { - t.Fatal(err) - } - - if n != int64(len(bs)) { - t.Fatalf("n %d != int64(len(bs)) %d", n, int64(len(bs))) - } - - readBytes, err := os.ReadFile(path) - if err != nil { - t.Fatal(err) - } - - if !bytes.Equal(bs, readBytes) { - t.Fatalf("bs %+v != readBytes %+v", bs, readBytes) - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestParseHex$ -func TestParseHex(t *testing.T) { - cases := map[string]string{ - "": "", - "313233": "123", - "e4bda0e5a5bdefbc8ce4b896e7958c": "你好,世界", - } - - for encoded, expect := range cases { - decoded, err := ParseHex(encoded) - if err != nil { - t.Fatal(err) - } - - if !bytes.Equal(decoded, []byte(expect)) { - t.Fatalf("encoded %s: decoded %+v != expect %+v", encoded, decoded, []byte(expect)) - } - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestParseBase64$ -func TestParseBase64(t *testing.T) { - cases := map[string]string{ - "": "", - "MTIz": "123", - "5L2g5aW977yM5LiW55WM": "你好,世界", - } - - for encoded, expect := range cases { - decoded, err := ParseBase64(encoded) - if err != nil { - t.Fatal(err) - } - - if !bytes.Equal(decoded, []byte(expect)) { - t.Fatalf("encoded %s: decoded %+v != expect %+v", encoded, decoded, []byte(expect)) - } - } -} diff --git a/des/des.go b/des/des.go index 3e5517a..7dce44f 100644 --- a/des/des.go +++ b/des/des.go @@ -5,14 +5,13 @@ package des import ( + "bytes" "crypto/cipher" "crypto/des" "fmt" - - "github.com/FishGoddess/cryptox" ) -func newBlock(key cryptox.Bytes) (cipher.Block, int, error) { +func newBlock(key []byte) (cipher.Block, int, error) { block, err := des.NewCipher(key) if err != nil { return nil, 0, err @@ -22,15 +21,19 @@ func newBlock(key cryptox.Bytes) (cipher.Block, int, error) { return block, blockSize, nil } -func EncryptECB(key cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { +// EncryptECB uses ecb mode to encrypt data. +// It must specify a padding. +func EncryptECB(data []byte, key []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + block, blockSize, err := newBlock(key) if err != nil { return nil, err } - bs = bs.Clone() - src := padding.Padding(bs, blockSize) - dst := src.Clone() + src := bytes.Clone(data) + src = conf.padding.Pad(src, blockSize) + dst := bytes.Clone(src) if len(src)%blockSize != 0 { return nil, fmt.Errorf("cryptox/des: encrypt ecb len(src) %d %% blockSize %d != 0", len(src), blockSize) @@ -46,139 +49,197 @@ func EncryptECB(key cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (c end += blockSize } + dst = conf.encoding.Encode(dst) return dst, nil } -func DecryptECB(key cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { +// EncryptCBC uses cbc mode to encrypt data. +// It must specify a padding. +func EncryptCBC(data []byte, key []byte, iv []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + block, blockSize, err := newBlock(key) if err != nil { return nil, err } - src := bs - dst := bs.Clone() + src := bytes.Clone(data) + src = conf.padding.Pad(src, blockSize) + dst := bytes.Clone(src) - if len(src)%blockSize != 0 { - return nil, fmt.Errorf("cryptox/des: decrypt ecb len(src) %d %% blockSize %d != 0", len(src), blockSize) - } - - start := 0 - end := blockSize + cipher.NewCBCEncrypter(block, iv).CryptBlocks(dst, src) + dst = conf.encoding.Encode(dst) + return dst, nil +} - for end <= len(src) { - block.Decrypt(dst[start:end], src[start:end]) +// EncryptCFB uses cfb mode to encrypt data. +// There is no need to specify a padding. +func EncryptCFB(data []byte, key []byte, iv []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) - start += blockSize - end += blockSize + block, _, err := newBlock(key) + if err != nil { + return nil, err } - return padding.UndoPadding(dst, blockSize) + src := bytes.Clone(data) + dst := bytes.Clone(src) + + cipher.NewCFBEncrypter(block, iv).XORKeyStream(dst, src) + dst = conf.encoding.Encode(dst) + return dst, nil } -func EncryptCBC(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, blockSize, err := newBlock(key) +// EncryptOFB uses ofb mode to encrypt data. +// There is no need to specify a padding. +func EncryptOFB(data []byte, key []byte, iv []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + + block, _, err := newBlock(key) if err != nil { return nil, err } - bs = bs.Clone() - src := padding.Padding(bs, blockSize) - dst := src.Clone() + src := bytes.Clone(data) + dst := bytes.Clone(src) - cipher.NewCBCEncrypter(block, iv).CryptBlocks(dst, src) + cipher.NewOFB(block, iv).XORKeyStream(dst, src) + dst = conf.encoding.Encode(dst) return dst, nil } -func DecryptCBC(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, blockSize, err := newBlock(key) +// EncryptCTR uses ctr mode to encrypt data. +// There is no need to specify a padding. +func EncryptCTR(data []byte, key []byte, iv []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + + block, _, err := newBlock(key) if err != nil { return nil, err } - src := bs - dst := src.Clone() + src := bytes.Clone(data) + dst := bytes.Clone(src) - cipher.NewCBCDecrypter(block, iv).CryptBlocks(dst, src) - return padding.UndoPadding(dst, blockSize) + cipher.NewCTR(block, iv).XORKeyStream(dst, src) + dst = conf.encoding.Encode(dst) + return dst, nil } -func EncryptCFB(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { +// DecryptECB uses ecb mode to decrypt data. +// It must specify a padding. +func DecryptECB(data []byte, key []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + block, blockSize, err := newBlock(key) if err != nil { return nil, err } - bs = bs.Clone() - src := padding.Padding(bs, blockSize) - dst := src.Clone() + src, err := conf.encoding.Decode(data) + if err != nil { + return nil, err + } - cipher.NewCFBEncrypter(block, iv).XORKeyStream(dst, src) - return dst, nil + dst := bytes.Clone(src) + + if len(src)%blockSize != 0 { + return nil, fmt.Errorf("cryptox/des: decrypt ecb len(src) %d %% blockSize %d != 0", len(src), blockSize) + } + + start := 0 + end := blockSize + + for end <= len(src) { + block.Decrypt(dst[start:end], src[start:end]) + + start += blockSize + end += blockSize + } + + return conf.padding.Unpad(dst, blockSize) } -func DecryptCFB(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { +// DecryptCBC uses cbc mode to decrypt data. +// It must specify a padding. +func DecryptCBC(data []byte, key []byte, iv []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + block, blockSize, err := newBlock(key) if err != nil { return nil, err } - src := bs - dst := bs.Clone() + src, err := conf.encoding.Decode(data) + if err != nil { + return nil, err + } - cipher.NewCFBDecrypter(block, iv).XORKeyStream(dst, src) - return padding.UndoPadding(dst, blockSize) + dst := bytes.Clone(src) + + cipher.NewCBCDecrypter(block, iv).CryptBlocks(dst, src) + return conf.padding.Unpad(dst, blockSize) } -func EncryptOFB(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, blockSize, err := newBlock(key) +// DecryptCFB uses cfb mode to decrypt data. +// There is no need to specify a padding. +func DecryptCFB(data []byte, key []byte, iv []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + + block, _, err := newBlock(key) if err != nil { return nil, err } - bs = bs.Clone() - src := padding.Padding(bs, blockSize) - dst := src.Clone() + src, err := conf.encoding.Decode(data) + if err != nil { + return nil, err + } - cipher.NewOFB(block, iv).XORKeyStream(dst, src) + dst := bytes.Clone(src) + + cipher.NewCFBDecrypter(block, iv).XORKeyStream(dst, src) return dst, nil } -func DecryptOFB(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, blockSize, err := newBlock(key) +// DecryptOFB uses ofb mode to decrypt data. +// There is no need to specify a padding. +func DecryptOFB(data []byte, key []byte, iv []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + + block, _, err := newBlock(key) if err != nil { return nil, err } - src := bs - dst := bs.Clone() - - cipher.NewOFB(block, iv).XORKeyStream(dst, src) - return padding.UndoPadding(dst, blockSize) -} - -func EncryptCTR(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, blockSize, err := newBlock(key) + src, err := conf.encoding.Decode(data) if err != nil { return nil, err } - bs = bs.Clone() - src := padding.Padding(bs, blockSize) - dst := src.Clone() + dst := bytes.Clone(src) - cipher.NewCTR(block, iv).XORKeyStream(dst, src) + cipher.NewOFB(block, iv).XORKeyStream(dst, src) return dst, nil } -func DecryptCTR(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, blockSize, err := newBlock(key) +// DecryptCTR uses ctr mode to decrypt data. +// There is no need to specify a padding. +func DecryptCTR(data []byte, key []byte, iv []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + + block, _, err := newBlock(key) if err != nil { return nil, err } - src := bs - dst := bs.Clone() + src, err := conf.encoding.Decode(data) + if err != nil { + return nil, err + } + + dst := bytes.Clone(src) cipher.NewCTR(block, iv).XORKeyStream(dst, src) - return padding.UndoPadding(dst, blockSize) + return dst, nil } diff --git a/des/des_test.go b/des/des_test.go index 002f9c2..4daef25 100644 --- a/des/des_test.go +++ b/des/des_test.go @@ -7,9 +7,8 @@ package des import ( "crypto/des" "fmt" + "slices" "testing" - - "github.com/FishGoddess/cryptox" ) var ( @@ -17,29 +16,81 @@ var ( testIV = []byte("87654321") ) -type testResult struct { - bs []byte - hexString string - base64String string +type testCase struct { + Data []byte + EncryptData []byte + EncryptDataHex []byte + EncryptDataBase64 []byte } -func (tr *testResult) compareTo(bs cryptox.Bytes) error { - if string(tr.bs) != string(bs) { - return fmt.Errorf("result bs %s != bs %s", tr.bs, bs) - } +type testEncryptFunc func(data []byte, opts ...Option) ([]byte, error) - if tr.hexString != bs.Hex() { - return fmt.Errorf("result hexString %s != bs hex %s", tr.hexString, bs.Hex()) - } +type testDecryptFunc func(data []byte, opts ...Option) ([]byte, error) + +func testEncryptAndDecrypt(name string, encrypt testEncryptFunc, decrypt testDecryptFunc, testCases []testCase) error { + for _, testCase := range testCases { + // None + encrypted, err := encrypt(testCase.Data) + if err != nil { + return err + } + + if !slices.Equal(encrypted, testCase.EncryptData) { + return fmt.Errorf("%s data %q: got %+v != expect %+v", name, testCase.Data, encrypted, testCase.EncryptData) + } + + decrypted, err := decrypt(encrypted) + if err != nil { + return err + } + + if !slices.Equal(decrypted, testCase.Data) { + return fmt.Errorf("%s encrypted %q: got %+v != expect %+v", name, encrypted, decrypted, testCase.Data) + } + + // Hex + encrypted, err = encrypt(testCase.Data, WithHex()) + if err != nil { + return err + } + + if !slices.Equal(encrypted, testCase.EncryptDataHex) { + return fmt.Errorf("%s data hex %q: got %s != expect %s", name, testCase.Data, encrypted, testCase.EncryptDataHex) + } + + decrypted, err = decrypt(encrypted, WithHex()) + if err != nil { + return err + } - if tr.base64String != bs.Base64() { - return fmt.Errorf("result base64String %s != bs base64 %s", tr.base64String, bs.Base64()) + if !slices.Equal(decrypted, testCase.Data) { + return fmt.Errorf("%s encrypted hex %q: got %s != expect %s", name, encrypted, decrypted, testCase.Data) + } + + // Base64 + encrypted, err = encrypt(testCase.Data, WithBase64()) + if err != nil { + return err + } + + if !slices.Equal(encrypted, testCase.EncryptDataBase64) { + return fmt.Errorf("%s data base64 %q: got %s != expect %s", name, testCase.Data, encrypted, testCase.EncryptDataBase64) + } + + decrypted, err = decrypt(encrypted, WithBase64()) + if err != nil { + return err + } + + if !slices.Equal(decrypted, testCase.Data) { + return fmt.Errorf("%s encrypted base64 %q: got %s != expect %s", name, encrypted, decrypted, testCase.Data) + } } return nil } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestNewBlock$ +// go test -v -cover -run=^TestNewBlock$ func TestNewBlock(t *testing.T) { block, blockSize, err := newBlock(testKey) if err != nil { @@ -64,207 +115,186 @@ func TestNewBlock(t *testing.T) { } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestECB$ +// go test -v -cover -run=^TestECB$ func TestECB(t *testing.T) { - cases := map[string]*testResult{ - "": { - bs: []byte{254, 185, 89, 183, 212, 100, 47, 203}, - hexString: "feb959b7d4642fcb", - base64String: "/rlZt9RkL8s=", + testCases := []testCase{ + { + Data: []byte(""), + EncryptData: []byte{254, 185, 89, 183, 212, 100, 47, 203}, + EncryptDataHex: []byte("feb959b7d4642fcb"), + EncryptDataBase64: []byte("/rlZt9RkL8s="), }, - "123": { - bs: []byte{44, 56, 133, 81, 215, 244, 137, 236}, - hexString: "2c388551d7f489ec", - base64String: "LDiFUdf0iew=", + { + Data: []byte("123"), + EncryptData: []byte{44, 56, 133, 81, 215, 244, 137, 236}, + EncryptDataHex: []byte("2c388551d7f489ec"), + EncryptDataBase64: []byte("LDiFUdf0iew="), }, - "你好,世界": { - bs: []byte{109, 82, 56, 231, 116, 36, 60, 100, 116, 149, 15, 240, 198, 38, 198, 204}, - hexString: "6d5238e774243c6474950ff0c626c6cc", - base64String: "bVI453QkPGR0lQ/wxibGzA==", + { + Data: []byte("你好,世界"), + EncryptData: []byte{109, 82, 56, 231, 116, 36, 60, 100, 116, 149, 15, 240, 198, 38, 198, 204}, + EncryptDataHex: []byte("6d5238e774243c6474950ff0c626c6cc"), + EncryptDataBase64: []byte("bVI453QkPGR0lQ/wxibGzA=="), }, } - for input, expect := range cases { - encrypted, err := EncryptECB(testKey, cryptox.PaddingPKCS7, []byte(input)) - if err != nil { - t.Fatal(err) - } - - if err = expect.compareTo(encrypted); err != nil { - t.Fatal(err) - } + encrypt := func(data []byte, opts ...Option) ([]byte, error) { + opts = append(opts, WithPKCS7()) + return EncryptECB(data, testKey, opts...) + } - decrypted, err := DecryptECB(testKey, cryptox.PaddingPKCS7, encrypted) - if err != nil { - t.Fatal(err) - } + decrypt := func(data []byte, opts ...Option) ([]byte, error) { + opts = append(opts, WithPKCS7()) + return DecryptECB(data, testKey, opts...) + } - if string(decrypted) != input { - t.Fatalf("input %s: decrypted %+v != input %+v", input, decrypted, []byte(input)) - } + if err := testEncryptAndDecrypt(t.Name(), encrypt, decrypt, testCases); err != nil { + t.Fatal(err) } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestCBC$ +// go test -v -cover -run=^TestCBC$ func TestCBC(t *testing.T) { - cases := map[string]*testResult{ - "": { - bs: []byte{205, 172, 198, 131, 218, 176, 175, 188}, - hexString: "cdacc683dab0afbc", - base64String: "zazGg9qwr7w=", + testCases := []testCase{ + { + Data: []byte(""), + EncryptData: []byte{205, 172, 198, 131, 218, 176, 175, 188}, + EncryptDataHex: []byte("cdacc683dab0afbc"), + EncryptDataBase64: []byte("zazGg9qwr7w="), }, - "123": { - bs: []byte{243, 126, 30, 174, 181, 95, 17, 128}, - hexString: "f37e1eaeb55f1180", - base64String: "834errVfEYA=", + { + Data: []byte("123"), + EncryptData: []byte{243, 126, 30, 174, 181, 95, 17, 128}, + EncryptDataHex: []byte("f37e1eaeb55f1180"), + EncryptDataBase64: []byte("834errVfEYA="), }, - "你好,世界": { - bs: []byte{185, 108, 29, 112, 42, 71, 169, 240, 62, 215, 156, 154, 145, 88, 110, 10}, - hexString: "b96c1d702a47a9f03ed79c9a91586e0a", - base64String: "uWwdcCpHqfA+15yakVhuCg==", + { + Data: []byte("你好,世界"), + EncryptData: []byte{185, 108, 29, 112, 42, 71, 169, 240, 62, 215, 156, 154, 145, 88, 110, 10}, + EncryptDataHex: []byte("b96c1d702a47a9f03ed79c9a91586e0a"), + EncryptDataBase64: []byte("uWwdcCpHqfA+15yakVhuCg=="), }, } - for input, expect := range cases { - encrypted, err := EncryptCBC(testKey, testIV, cryptox.PaddingPKCS7, []byte(input)) - if err != nil { - t.Fatal(err) - } - - if err = expect.compareTo(encrypted); err != nil { - t.Fatal(err) - } + encrypt := func(data []byte, opts ...Option) ([]byte, error) { + opts = append(opts, WithPKCS7()) + return EncryptCBC(data, testKey, testIV, opts...) + } - decrypted, err := DecryptCBC(testKey, testIV, cryptox.PaddingPKCS7, encrypted) - if err != nil { - t.Fatal(err) - } + decrypt := func(data []byte, opts ...Option) ([]byte, error) { + opts = append(opts, WithPKCS7()) + return DecryptCBC(data, testKey, testIV, opts...) + } - if string(decrypted) != input { - t.Fatalf("input %s: decrypted %+v != input %+v", input, decrypted, []byte(input)) - } + if err := testEncryptAndDecrypt(t.Name(), encrypt, decrypt, testCases); err != nil { + t.Fatal(err) } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestCFB$ +// go test -v -cover -run=^TestCFB$ func TestCFB(t *testing.T) { - cases := map[string]*testResult{ - "": { - bs: []byte{48, 92, 56, 32, 147, 125, 156, 44}, - hexString: "305c3820937d9c2c", - base64String: "MFw4IJN9nCw=", + testCases := []testCase{ + { + Data: []byte(""), + EncryptData: []byte{}, + EncryptDataHex: []byte(""), + EncryptDataBase64: []byte(""), }, - "123": { - bs: []byte{9, 102, 3, 45, 158, 112, 145, 33}, - hexString: "0966032d9e709121", - base64String: "CWYDLZ5wkSE=", + { + Data: []byte("123"), + EncryptData: []byte{9, 102, 3}, + EncryptDataHex: []byte("096603"), + EncryptDataBase64: []byte("CWYD"), }, - "你好,世界": { - bs: []byte{220, 233, 144, 205, 62, 200, 123, 152, 231, 237, 219, 68, 211, 43, 255, 25}, - hexString: "dce990cd3ec87b98e7eddb44d32bff19", - base64String: "3OmQzT7Ie5jn7dtE0yv/GQ==", + { + Data: []byte("你好,世界"), + EncryptData: []byte{220, 233, 144, 205, 62, 200, 123, 152, 231, 237, 219, 68, 211, 43, 255}, + EncryptDataHex: []byte("dce990cd3ec87b98e7eddb44d32bff"), + EncryptDataBase64: []byte("3OmQzT7Ie5jn7dtE0yv/"), }, } - for input, expect := range cases { - encrypted, err := EncryptCFB(testKey, testIV, cryptox.PaddingPKCS7, []byte(input)) - if err != nil { - t.Fatal(err) - } - - if err = expect.compareTo(encrypted); err != nil { - t.Fatal(err) - } + encrypt := func(data []byte, opts ...Option) ([]byte, error) { + return EncryptCFB(data, testKey, testIV, opts...) + } - decrypted, err := DecryptCFB(testKey, testIV, cryptox.PaddingPKCS7, encrypted) - if err != nil { - t.Fatal(err) - } + decrypt := func(data []byte, opts ...Option) ([]byte, error) { + return DecryptCFB(data, testKey, testIV, opts...) + } - if string(decrypted) != input { - t.Fatalf("input %s: decrypted %+v != input %+v", input, decrypted, []byte(input)) - } + if err := testEncryptAndDecrypt(t.Name(), encrypt, decrypt, testCases); err != nil { + t.Fatal(err) } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestOFB$ +// go test -v -cover -run=^TestOFB$ func TestOFB(t *testing.T) { - cases := map[string]*testResult{ - "": { - bs: []byte{48, 92, 56, 32, 147, 125, 156, 44}, - hexString: "305c3820937d9c2c", - base64String: "MFw4IJN9nCw=", + testCases := []testCase{ + { + Data: []byte(""), + EncryptData: []byte{}, + EncryptDataHex: []byte(""), + EncryptDataBase64: []byte(""), }, - "123": { - bs: []byte{9, 102, 3, 45, 158, 112, 145, 33}, - hexString: "0966032d9e709121", - base64String: "CWYDLZ5wkSE=", + { + Data: []byte("123"), + EncryptData: []byte{9, 102, 3}, + EncryptDataHex: []byte("096603"), + EncryptDataBase64: []byte("CWYD"), }, - "你好,世界": { - bs: []byte{220, 233, 144, 205, 62, 200, 123, 152, 169, 42, 97, 1, 193, 120, 15, 149}, - hexString: "dce990cd3ec87b98a92a6101c1780f95", - base64String: "3OmQzT7Ie5ipKmEBwXgPlQ==", + { + Data: []byte("你好,世界"), + EncryptData: []byte{220, 233, 144, 205, 62, 200, 123, 152, 169, 42, 97, 1, 193, 120, 15}, + EncryptDataHex: []byte("dce990cd3ec87b98a92a6101c1780f"), + EncryptDataBase64: []byte("3OmQzT7Ie5ipKmEBwXgP"), }, } - for input, expect := range cases { - encrypted, err := EncryptOFB(testKey, testIV, cryptox.PaddingPKCS7, []byte(input)) - if err != nil { - t.Fatal(err) - } - - if err = expect.compareTo(encrypted); err != nil { - t.Fatal(err) - } + encrypt := func(data []byte, opts ...Option) ([]byte, error) { + return EncryptOFB(data, testKey, testIV, opts...) + } - decrypted, err := DecryptOFB(testKey, testIV, cryptox.PaddingPKCS7, encrypted) - if err != nil { - t.Fatal(err) - } + decrypt := func(data []byte, opts ...Option) ([]byte, error) { + return DecryptOFB(data, testKey, testIV, opts...) + } - if string(decrypted) != input { - t.Fatalf("input %s: decrypted %+v != input %+v", input, decrypted, []byte(input)) - } + if err := testEncryptAndDecrypt(t.Name(), encrypt, decrypt, testCases); err != nil { + t.Fatal(err) } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestCTR$ +// go test -v -cover -run=^TestCTR$ func TestCTR(t *testing.T) { - cases := map[string]*testResult{ - "": { - bs: []byte{48, 92, 56, 32, 147, 125, 156, 44}, - hexString: "305c3820937d9c2c", - base64String: "MFw4IJN9nCw=", + testCases := []testCase{ + { + Data: []byte(""), + EncryptData: []byte{}, + EncryptDataHex: []byte(""), + EncryptDataBase64: []byte(""), }, - "123": { - bs: []byte{9, 102, 3, 45, 158, 112, 145, 33}, - hexString: "0966032d9e709121", - base64String: "CWYDLZ5wkSE=", + { + Data: []byte("123"), + EncryptData: []byte{9, 102, 3}, + EncryptDataHex: []byte("096603"), + EncryptDataBase64: []byte("CWYD"), }, - "你好,世界": { - bs: []byte{220, 233, 144, 205, 62, 200, 123, 152, 82, 201, 236, 67, 30, 240, 63, 228}, - hexString: "dce990cd3ec87b9852c9ec431ef03fe4", - base64String: "3OmQzT7Ie5hSyexDHvA/5A==", + { + Data: []byte("你好,世界"), + EncryptData: []byte{220, 233, 144, 205, 62, 200, 123, 152, 82, 201, 236, 67, 30, 240, 63}, + EncryptDataHex: []byte("dce990cd3ec87b9852c9ec431ef03f"), + EncryptDataBase64: []byte("3OmQzT7Ie5hSyexDHvA/"), }, } - for input, expect := range cases { - encrypted, err := EncryptCTR(testKey, testIV, cryptox.PaddingPKCS7, []byte(input)) - if err != nil { - t.Fatal(err) - } - - if err = expect.compareTo(encrypted); err != nil { - t.Fatal(err) - } + encrypt := func(data []byte, opts ...Option) ([]byte, error) { + return EncryptCTR(data, testKey, testIV, opts...) + } - decrypted, err := DecryptCTR(testKey, testIV, cryptox.PaddingPKCS7, encrypted) - if err != nil { - t.Fatal(err) - } + decrypt := func(data []byte, opts ...Option) ([]byte, error) { + return DecryptCTR(data, testKey, testIV, opts...) + } - if string(decrypted) != input { - t.Fatalf("input %s: decrypted %+v != input %+v", input, decrypted, []byte(input)) - } + if err := testEncryptAndDecrypt(t.Name(), encrypt, decrypt, testCases); err != nil { + t.Fatal(err) } } diff --git a/des/options.go b/des/options.go new file mode 100644 index 0000000..a301b8e --- /dev/null +++ b/des/options.go @@ -0,0 +1,69 @@ +// Copyright 2025 FishGoddess. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package des + +import ( + "github.com/FishGoddess/cryptox/bytes/encoding" + "github.com/FishGoddess/cryptox/bytes/padding" +) + +type Config struct { + encoding encoding.Encoding + padding padding.Padding +} + +func newConfig() *Config { + conf := &Config{ + encoding: encoding.None{}, + padding: padding.None{}, + } + + return conf +} + +func (c *Config) Apply(opts ...Option) *Config { + for _, opt := range opts { + opt(c) + } + + return c +} + +type Option func(conf *Config) + +// WithHex sets hex encoding to config. +func WithHex() Option { + return func(conf *Config) { + conf.encoding = encoding.Hex{} + } +} + +// WithBase64 sets base64 encoding to config. +func WithBase64() Option { + return func(conf *Config) { + conf.encoding = encoding.Base64{} + } +} + +// WithZero sets zero padding to config. +func WithZero() Option { + return func(conf *Config) { + conf.padding = padding.Zero{} + } +} + +// WithPKCS5 sets pkcs5 padding to config. +func WithPKCS5() Option { + return func(conf *Config) { + conf.padding = padding.PKCS5{} + } +} + +// WithPKCS7 sets pkcs7 padding to config. +func WithPKCS7() Option { + return func(conf *Config) { + conf.padding = padding.PKCS7{} + } +} diff --git a/des/options_test.go b/des/options_test.go new file mode 100644 index 0000000..83beb04 --- /dev/null +++ b/des/options_test.go @@ -0,0 +1,59 @@ +// Copyright 2025 FishGoddess. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package des + +import ( + "fmt" + "testing" + + "github.com/FishGoddess/cryptox/bytes/encoding" + "github.com/FishGoddess/cryptox/bytes/padding" +) + +// go test -v -cover -run=^TestConfig$ +func TestConfig(t *testing.T) { + opts := []Option{ + WithHex(), + WithZero(), + } + + conf := newConfig().Apply(opts...) + + got := fmt.Sprintf("%T", conf.encoding) + expect := fmt.Sprintf("%T", encoding.Hex{}) + if got != expect { + t.Fatalf("got %s != expect %s", got, expect) + } + + conf.Apply(WithBase64()) + + got = fmt.Sprintf("%T", conf.encoding) + expect = fmt.Sprintf("%T", encoding.Base64{}) + if got != expect { + t.Fatalf("got %s != expect %s", got, expect) + } + + got = fmt.Sprintf("%T", conf.padding) + expect = fmt.Sprintf("%T", padding.Zero{}) + if got != expect { + t.Fatalf("got %s != expect %s", got, expect) + } + + conf.Apply(WithPKCS5()) + + got = fmt.Sprintf("%T", conf.padding) + expect = fmt.Sprintf("%T", padding.PKCS5{}) + if got != expect { + t.Fatalf("got %s != expect %s", got, expect) + } + + conf.Apply(WithPKCS7()) + + got = fmt.Sprintf("%T", conf.padding) + expect = fmt.Sprintf("%T", padding.PKCS7{}) + if got != expect { + t.Fatalf("got %s != expect %s", got, expect) + } +} diff --git a/des/triple_des.go b/des/triple_des.go index de3fcf6..29a906b 100644 --- a/des/triple_des.go +++ b/des/triple_des.go @@ -5,14 +5,13 @@ package des import ( + "bytes" "crypto/cipher" "crypto/des" "fmt" - - "github.com/FishGoddess/cryptox" ) -func newTripleBlock(key cryptox.Bytes) (cipher.Block, int, error) { +func newTripleBlock(key []byte) (cipher.Block, int, error) { block, err := des.NewTripleDESCipher(key) if err != nil { return nil, 0, err @@ -22,18 +21,22 @@ func newTripleBlock(key cryptox.Bytes) (cipher.Block, int, error) { return block, blockSize, nil } -func EncryptECBTriple(key cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { +// EncryptTripleECB uses ecb mode to encrypt data. +// It must specify a padding. +func EncryptTripleECB(data []byte, key []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + block, blockSize, err := newTripleBlock(key) if err != nil { return nil, err } - bs = bs.Clone() - src := padding.Padding(bs, blockSize) - dst := src.Clone() + src := bytes.Clone(data) + src = conf.padding.Pad(src, blockSize) + dst := bytes.Clone(src) if len(src)%blockSize != 0 { - return nil, fmt.Errorf("cryptox/des: encrypt ecb triple len(src) %d %% blockSize %d != 0", len(src), blockSize) + return nil, fmt.Errorf("cryptox/des: encrypt triple ecb len(src) %d %% blockSize %d != 0", len(src), blockSize) } start := 0 @@ -46,139 +49,197 @@ func EncryptECBTriple(key cryptox.Bytes, padding cryptox.Padding, bs cryptox.Byt end += blockSize } + dst = conf.encoding.Encode(dst) return dst, nil } -func DecryptECBTriple(key cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { +// EncryptTripleCBC uses cbc mode to encrypt data. +// It must specify a padding. +func EncryptTripleCBC(data []byte, key []byte, iv []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + block, blockSize, err := newTripleBlock(key) if err != nil { return nil, err } - src := bs - dst := bs.Clone() - - if len(src)%blockSize != 0 { - return nil, fmt.Errorf("cryptox/des: decrypt ecb triple len(src) %d %% blockSize %d != 0", len(src), blockSize) - } + src := bytes.Clone(data) + src = conf.padding.Pad(src, blockSize) + dst := bytes.Clone(src) - start := 0 - end := blockSize + cipher.NewCBCEncrypter(block, iv).CryptBlocks(dst, src) + dst = conf.encoding.Encode(dst) + return dst, nil +} - for end <= len(src) { - block.Decrypt(dst[start:end], src[start:end]) +// EncryptTripleCFB uses cfb mode to encrypt data. +// There is no need to specify a padding. +func EncryptTripleCFB(data []byte, key []byte, iv []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) - start += blockSize - end += blockSize + block, _, err := newTripleBlock(key) + if err != nil { + return nil, err } - return padding.UndoPadding(dst, blockSize) + src := bytes.Clone(data) + dst := bytes.Clone(src) + + cipher.NewCFBEncrypter(block, iv).XORKeyStream(dst, src) + dst = conf.encoding.Encode(dst) + return dst, nil } -func EncryptCBCTriple(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, blockSize, err := newTripleBlock(key) +// EncryptTripleOFB uses ofb mode to encrypt data. +// There is no need to specify a padding. +func EncryptTripleOFB(data []byte, key []byte, iv []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + + block, _, err := newTripleBlock(key) if err != nil { return nil, err } - bs = bs.Clone() - src := padding.Padding(bs, blockSize) - dst := src.Clone() + src := bytes.Clone(data) + dst := bytes.Clone(src) - cipher.NewCBCEncrypter(block, iv).CryptBlocks(dst, src) + cipher.NewOFB(block, iv).XORKeyStream(dst, src) + dst = conf.encoding.Encode(dst) return dst, nil } -func DecryptCBCTriple(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, blockSize, err := newTripleBlock(key) +// EncryptTripleCTR uses ctr mode to encrypt data. +// There is no need to specify a padding. +func EncryptTripleCTR(data []byte, key []byte, iv []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + + block, _, err := newTripleBlock(key) if err != nil { return nil, err } - src := bs - dst := src.Clone() + src := bytes.Clone(data) + dst := bytes.Clone(src) - cipher.NewCBCDecrypter(block, iv).CryptBlocks(dst, src) - return padding.UndoPadding(dst, blockSize) + cipher.NewCTR(block, iv).XORKeyStream(dst, src) + dst = conf.encoding.Encode(dst) + return dst, nil } -func EncryptCFBTriple(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { +// DecryptTripleECB uses ecb mode to decrypt data. +// It must specify a padding. +func DecryptTripleECB(data []byte, key []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + block, blockSize, err := newTripleBlock(key) if err != nil { return nil, err } - bs = bs.Clone() - src := padding.Padding(bs, blockSize) - dst := src.Clone() + src, err := conf.encoding.Decode(data) + if err != nil { + return nil, err + } - cipher.NewCFBEncrypter(block, iv).XORKeyStream(dst, src) - return dst, nil + dst := bytes.Clone(src) + + if len(src)%blockSize != 0 { + return nil, fmt.Errorf("cryptox/des: decrypt triple ecb len(src) %d %% blockSize %d != 0", len(src), blockSize) + } + + start := 0 + end := blockSize + + for end <= len(src) { + block.Decrypt(dst[start:end], src[start:end]) + + start += blockSize + end += blockSize + } + + return conf.padding.Unpad(dst, blockSize) } -func DecryptCFBTriple(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { +// DecryptTripleCBC uses cbc mode to decrypt data. +// It must specify a padding. +func DecryptTripleCBC(data []byte, key []byte, iv []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + block, blockSize, err := newTripleBlock(key) if err != nil { return nil, err } - src := bs - dst := bs.Clone() + src, err := conf.encoding.Decode(data) + if err != nil { + return nil, err + } - cipher.NewCFBDecrypter(block, iv).XORKeyStream(dst, src) - return padding.UndoPadding(dst, blockSize) + dst := bytes.Clone(src) + + cipher.NewCBCDecrypter(block, iv).CryptBlocks(dst, src) + return conf.padding.Unpad(dst, blockSize) } -func EncryptOFBTriple(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, blockSize, err := newTripleBlock(key) +// DecryptTripleCFB uses cfb mode to decrypt data. +// There is no need to specify a padding. +func DecryptTripleCFB(data []byte, key []byte, iv []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + + block, _, err := newTripleBlock(key) + if err != nil { + return nil, err + } + + src, err := conf.encoding.Decode(data) if err != nil { return nil, err } - bs = bs.Clone() - src := padding.Padding(bs, blockSize) - dst := src.Clone() + dst := bytes.Clone(src) - cipher.NewOFB(block, iv).XORKeyStream(dst, src) + cipher.NewCFBDecrypter(block, iv).XORKeyStream(dst, src) return dst, nil } -func DecryptOFBTriple(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, blockSize, err := newTripleBlock(key) +// DecryptTripleOFB uses ofb mode to decrypt data. +// There is no need to specify a padding. +func DecryptTripleOFB(data []byte, key []byte, iv []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + + block, _, err := newTripleBlock(key) if err != nil { return nil, err } - src := bs - dst := bs.Clone() - - cipher.NewOFB(block, iv).XORKeyStream(dst, src) - return padding.UndoPadding(dst, blockSize) -} - -func EncryptCTRTriple(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, blockSize, err := newTripleBlock(key) + src, err := conf.encoding.Decode(data) if err != nil { return nil, err } - bs = bs.Clone() - src := padding.Padding(bs, blockSize) - dst := src.Clone() + dst := bytes.Clone(src) - cipher.NewCTR(block, iv).XORKeyStream(dst, src) + cipher.NewOFB(block, iv).XORKeyStream(dst, src) return dst, nil } -func DecryptCTRTriple(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, blockSize, err := newTripleBlock(key) +// DecryptTripleCTR uses ctr mode to decrypt data. +// There is no need to specify a padding. +func DecryptTripleCTR(data []byte, key []byte, iv []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + + block, _, err := newTripleBlock(key) if err != nil { return nil, err } - src := bs - dst := bs.Clone() + src, err := conf.encoding.Decode(data) + if err != nil { + return nil, err + } + + dst := bytes.Clone(src) cipher.NewCTR(block, iv).XORKeyStream(dst, src) - return padding.UndoPadding(dst, blockSize) + return dst, nil } diff --git a/des/triple_des_test.go b/des/triple_des_test.go index a295613..6c0bae6 100644 --- a/des/triple_des_test.go +++ b/des/triple_des_test.go @@ -7,15 +7,13 @@ package des import ( "crypto/des" "testing" - - "github.com/FishGoddess/cryptox" ) var ( testTripleKey = []byte("123456788765432112345678") ) -// go test -v -cover -count=1 -test.cpu=1 -run=^TestNewTripleBlock$ +// go test -v -cover -run=^TestNewTripleBlock$ func TestNewTripleBlock(t *testing.T) { block, blockSize, err := newTripleBlock(testTripleKey) if err != nil { @@ -40,207 +38,186 @@ func TestNewTripleBlock(t *testing.T) { } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestTripleECB$ +// go test -v -cover -run=^TestTripleECB$ func TestTripleECB(t *testing.T) { - cases := map[string]*testResult{ - "": { - bs: []byte{163, 133, 24, 236, 31, 63, 147, 38}, - hexString: "a38518ec1f3f9326", - base64String: "o4UY7B8/kyY=", + testCases := []testCase{ + { + Data: []byte(""), + EncryptData: []byte{163, 133, 24, 236, 31, 63, 147, 38}, + EncryptDataHex: []byte("a38518ec1f3f9326"), + EncryptDataBase64: []byte("o4UY7B8/kyY="), }, - "123": { - bs: []byte{185, 2, 158, 11, 229, 10, 126, 217}, - hexString: "b9029e0be50a7ed9", - base64String: "uQKeC+UKftk=", + { + Data: []byte("123"), + EncryptData: []byte{185, 2, 158, 11, 229, 10, 126, 217}, + EncryptDataHex: []byte("b9029e0be50a7ed9"), + EncryptDataBase64: []byte("uQKeC+UKftk="), }, - "你好,世界": { - bs: []byte{224, 251, 123, 121, 70, 219, 201, 188, 14, 248, 74, 206, 42, 34, 16, 102}, - hexString: "e0fb7b7946dbc9bc0ef84ace2a221066", - base64String: "4Pt7eUbbybwO+ErOKiIQZg==", + { + Data: []byte("你好,世界"), + EncryptData: []byte{224, 251, 123, 121, 70, 219, 201, 188, 14, 248, 74, 206, 42, 34, 16, 102}, + EncryptDataHex: []byte("e0fb7b7946dbc9bc0ef84ace2a221066"), + EncryptDataBase64: []byte("4Pt7eUbbybwO+ErOKiIQZg=="), }, } - for input, expect := range cases { - encrypted, err := EncryptECBTriple(testTripleKey, cryptox.PaddingPKCS7, []byte(input)) - if err != nil { - t.Fatal(err) - } - - if err = expect.compareTo(encrypted); err != nil { - t.Fatal(err) - } + encrypt := func(data []byte, opts ...Option) ([]byte, error) { + opts = append(opts, WithPKCS7()) + return EncryptTripleECB(data, testTripleKey, opts...) + } - decrypted, err := DecryptECBTriple(testTripleKey, cryptox.PaddingPKCS7, encrypted) - if err != nil { - t.Fatal(err) - } + decrypt := func(data []byte, opts ...Option) ([]byte, error) { + opts = append(opts, WithPKCS7()) + return DecryptTripleECB(data, testTripleKey, opts...) + } - if string(decrypted) != input { - t.Fatalf("input %s: decrypted %+v != input %+v", input, decrypted, []byte(input)) - } + if err := testEncryptAndDecrypt(t.Name(), encrypt, decrypt, testCases); err != nil { + t.Fatal(err) } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestTripleCBC$ +// go test -v -cover -run=^TestTripleCBC$ func TestTripleCBC(t *testing.T) { - cases := map[string]*testResult{ - "": { - bs: []byte{39, 65, 204, 186, 76, 78, 149, 112}, - hexString: "2741ccba4c4e9570", - base64String: "J0HMukxOlXA=", + testCases := []testCase{ + { + Data: []byte(""), + EncryptData: []byte{39, 65, 204, 186, 76, 78, 149, 112}, + EncryptDataHex: []byte("2741ccba4c4e9570"), + EncryptDataBase64: []byte("J0HMukxOlXA="), }, - "123": { - bs: []byte{0, 247, 123, 125, 239, 59, 132, 68}, - hexString: "00f77b7def3b8444", - base64String: "APd7fe87hEQ=", + { + Data: []byte("123"), + EncryptData: []byte{0, 247, 123, 125, 239, 59, 132, 68}, + EncryptDataHex: []byte("00f77b7def3b8444"), + EncryptDataBase64: []byte("APd7fe87hEQ="), }, - "你好,世界": { - bs: []byte{153, 124, 242, 118, 122, 226, 179, 98, 152, 158, 80, 119, 178, 247, 19, 62}, - hexString: "997cf2767ae2b362989e5077b2f7133e", - base64String: "mXzydnris2KYnlB3svcTPg==", + { + Data: []byte("你好,世界"), + EncryptData: []byte{153, 124, 242, 118, 122, 226, 179, 98, 152, 158, 80, 119, 178, 247, 19, 62}, + EncryptDataHex: []byte("997cf2767ae2b362989e5077b2f7133e"), + EncryptDataBase64: []byte("mXzydnris2KYnlB3svcTPg=="), }, } - for input, expect := range cases { - encrypted, err := EncryptCBCTriple(testTripleKey, testIV, cryptox.PaddingPKCS7, []byte(input)) - if err != nil { - t.Fatal(err) - } - - if err = expect.compareTo(encrypted); err != nil { - t.Fatal(err) - } + encrypt := func(data []byte, opts ...Option) ([]byte, error) { + opts = append(opts, WithPKCS7()) + return EncryptTripleCBC(data, testTripleKey, testIV, opts...) + } - decrypted, err := DecryptCBCTriple(testTripleKey, testIV, cryptox.PaddingPKCS7, encrypted) - if err != nil { - t.Fatal(err) - } + decrypt := func(data []byte, opts ...Option) ([]byte, error) { + opts = append(opts, WithPKCS7()) + return DecryptTripleCBC(data, testTripleKey, testIV, opts...) + } - if string(decrypted) != input { - t.Fatalf("input %s: decrypted %+v != input %+v", input, decrypted, []byte(input)) - } + if err := testEncryptAndDecrypt(t.Name(), encrypt, decrypt, testCases); err != nil { + t.Fatal(err) } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestTripleCFB$ +// go test -v -cover -run=^TestTripleCFB$ func TestTripleCFB(t *testing.T) { - cases := map[string]*testResult{ - "": { - bs: []byte{92, 169, 236, 137, 11, 246, 123, 32}, - hexString: "5ca9ec890bf67b20", - base64String: "XKnsiQv2eyA=", + testCases := []testCase{ + { + Data: []byte(""), + EncryptData: []byte{}, + EncryptDataHex: []byte(""), + EncryptDataBase64: []byte(""), }, - "123": { - bs: []byte{101, 147, 215, 132, 6, 251, 118, 45}, - hexString: "6593d78406fb762d", - base64String: "ZZPXhAb7di0=", + { + Data: []byte("123"), + EncryptData: []byte{101, 147, 215}, + EncryptDataHex: []byte("6593d7"), + EncryptDataBase64: []byte("ZZPX"), }, - "你好,世界": { - bs: []byte{176, 28, 68, 100, 166, 67, 156, 148, 85, 69, 217, 58, 184, 136, 197, 51}, - hexString: "b01c4464a6439c945545d93ab888c533", - base64String: "sBxEZKZDnJRVRdk6uIjFMw==", + { + Data: []byte("你好,世界"), + EncryptData: []byte{176, 28, 68, 100, 166, 67, 156, 148, 85, 69, 217, 58, 184, 136, 197}, + EncryptDataHex: []byte("b01c4464a6439c945545d93ab888c5"), + EncryptDataBase64: []byte("sBxEZKZDnJRVRdk6uIjF"), }, } - for input, expect := range cases { - encrypted, err := EncryptCFBTriple(testTripleKey, testIV, cryptox.PaddingPKCS7, []byte(input)) - if err != nil { - t.Fatal(err) - } - - if err = expect.compareTo(encrypted); err != nil { - t.Fatal(err) - } + encrypt := func(data []byte, opts ...Option) ([]byte, error) { + return EncryptTripleCFB(data, testTripleKey, testIV, opts...) + } - decrypted, err := DecryptCFBTriple(testTripleKey, testIV, cryptox.PaddingPKCS7, encrypted) - if err != nil { - t.Fatal(err) - } + decrypt := func(data []byte, opts ...Option) ([]byte, error) { + return DecryptTripleCFB(data, testTripleKey, testIV, opts...) + } - if string(decrypted) != input { - t.Fatalf("input %s: decrypted %+v != input %+v", input, decrypted, []byte(input)) - } + if err := testEncryptAndDecrypt(t.Name(), encrypt, decrypt, testCases); err != nil { + t.Fatal(err) } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestTripleOFB$ +// go test -v -cover -run=^TestTripleOFB$ func TestTripleOFB(t *testing.T) { - cases := map[string]*testResult{ - "": { - bs: []byte{92, 169, 236, 137, 11, 246, 123, 32}, - hexString: "5ca9ec890bf67b20", - base64String: "XKnsiQv2eyA=", + testCases := []testCase{ + { + Data: []byte(""), + EncryptData: []byte{}, + EncryptDataHex: []byte(""), + EncryptDataBase64: []byte(""), }, - "123": { - bs: []byte{101, 147, 215, 132, 6, 251, 118, 45}, - hexString: "6593d78406fb762d", - base64String: "ZZPXhAb7di0=", + { + Data: []byte("123"), + EncryptData: []byte{101, 147, 215}, + EncryptDataHex: []byte("6593d7"), + EncryptDataBase64: []byte("ZZPX"), }, - "你好,世界": { - bs: []byte{176, 28, 68, 100, 166, 67, 156, 148, 46, 244, 26, 37, 38, 97, 62, 68}, - hexString: "b01c4464a6439c942ef41a2526613e44", - base64String: "sBxEZKZDnJQu9BolJmE+RA==", + { + Data: []byte("你好,世界"), + EncryptData: []byte{176, 28, 68, 100, 166, 67, 156, 148, 46, 244, 26, 37, 38, 97, 62}, + EncryptDataHex: []byte("b01c4464a6439c942ef41a2526613e"), + EncryptDataBase64: []byte("sBxEZKZDnJQu9BolJmE+"), }, } - for input, expect := range cases { - encrypted, err := EncryptOFBTriple(testTripleKey, testIV, cryptox.PaddingPKCS7, []byte(input)) - if err != nil { - t.Fatal(err) - } - - if err = expect.compareTo(encrypted); err != nil { - t.Fatal(err) - } + encrypt := func(data []byte, opts ...Option) ([]byte, error) { + return EncryptTripleOFB(data, testTripleKey, testIV, opts...) + } - decrypted, err := DecryptOFBTriple(testTripleKey, testIV, cryptox.PaddingPKCS7, encrypted) - if err != nil { - t.Fatal(err) - } + decrypt := func(data []byte, opts ...Option) ([]byte, error) { + return DecryptTripleOFB(data, testTripleKey, testIV, opts...) + } - if string(decrypted) != input { - t.Fatalf("input %s: decrypted %+v != input %+v", input, decrypted, []byte(input)) - } + if err := testEncryptAndDecrypt(t.Name(), encrypt, decrypt, testCases); err != nil { + t.Fatal(err) } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestTripleCTR$ +// go test -v -cover -run=^TestTripleCTR$ func TestTripleCTR(t *testing.T) { - cases := map[string]*testResult{ - "": { - bs: []byte{92, 169, 236, 137, 11, 246, 123, 32}, - hexString: "5ca9ec890bf67b20", - base64String: "XKnsiQv2eyA=", + testCases := []testCase{ + { + Data: []byte(""), + EncryptData: []byte{}, + EncryptDataHex: []byte(""), + EncryptDataBase64: []byte(""), }, - "123": { - bs: []byte{101, 147, 215, 132, 6, 251, 118, 45}, - hexString: "6593d78406fb762d", - base64String: "ZZPXhAb7di0=", + { + Data: []byte("123"), + EncryptData: []byte{101, 147, 215}, + EncryptDataHex: []byte("6593d7"), + EncryptDataBase64: []byte("ZZPX"), }, - "你好,世界": { - bs: []byte{176, 28, 68, 100, 166, 67, 156, 148, 76, 184, 154, 31, 42, 134, 28, 205}, - hexString: "b01c4464a6439c944cb89a1f2a861ccd", - base64String: "sBxEZKZDnJRMuJofKoYczQ==", + { + Data: []byte("你好,世界"), + EncryptData: []byte{176, 28, 68, 100, 166, 67, 156, 148, 76, 184, 154, 31, 42, 134, 28}, + EncryptDataHex: []byte("b01c4464a6439c944cb89a1f2a861c"), + EncryptDataBase64: []byte("sBxEZKZDnJRMuJofKoYc"), }, } - for input, expect := range cases { - encrypted, err := EncryptCTRTriple(testTripleKey, testIV, cryptox.PaddingPKCS7, []byte(input)) - if err != nil { - t.Fatal(err) - } - - if err = expect.compareTo(encrypted); err != nil { - t.Fatal(err) - } + encrypt := func(data []byte, opts ...Option) ([]byte, error) { + return EncryptTripleCTR(data, testTripleKey, testIV, opts...) + } - decrypted, err := DecryptCTRTriple(testTripleKey, testIV, cryptox.PaddingPKCS7, encrypted) - if err != nil { - t.Fatal(err) - } + decrypt := func(data []byte, opts ...Option) ([]byte, error) { + return DecryptTripleCTR(data, testTripleKey, testIV, opts...) + } - if string(decrypted) != input { - t.Fatalf("input %s: decrypted %+v != input %+v", input, decrypted, []byte(input)) - } + if err := testEncryptAndDecrypt(t.Name(), encrypt, decrypt, testCases); err != nil { + t.Fatal(err) } } diff --git a/go.mod b/go.mod index 8b7d6ad..a480e0a 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ module github.com/FishGoddess/cryptox -go 1.21 +go 1.25 diff --git a/hash/crc.go b/hash/crc.go index 64fc737..57223df 100644 --- a/hash/crc.go +++ b/hash/crc.go @@ -7,8 +7,6 @@ package hash import ( "hash/crc32" "hash/crc64" - - "github.com/FishGoddess/cryptox" ) var ( @@ -17,41 +15,37 @@ var ( tableECMA = crc64.MakeTable(crc64.ECMA) ) -type Table32 = crc32.Table - -type Table64 = crc64.Table - -// CRC32 uses given table to checksum bs. +// CRC32 uses given table to checksum data. // Use IEEE table if passed table is nil. -func CRC32(bs cryptox.Bytes, table *Table32) uint32 { +func CRC32(data []byte, table *crc32.Table) uint32 { if table == nil { table = tableIEEE } - return crc32.Checksum(bs, table) + return crc32.Checksum(data, table) } -// CRC32IEEE uses ieee table to checksum bs. -func CRC32IEEE(bs cryptox.Bytes) uint32 { - return CRC32(bs, tableIEEE) +// CRC32IEEE uses ieee table to checksum data. +func CRC32IEEE(data []byte) uint32 { + return CRC32(data, tableIEEE) } -// CRC64 uses given table to checksum bs. +// CRC64 uses given table to checksum data. // Use ISO table if passed table is nil. -func CRC64(bs cryptox.Bytes, table *Table64) uint64 { +func CRC64(data []byte, table *crc64.Table) uint64 { if table == nil { table = tableISO } - return crc64.Checksum(bs, table) + return crc64.Checksum(data, table) } -// CRC64ISO uses iso table to checksum bs. -func CRC64ISO(bs cryptox.Bytes) uint64 { - return CRC64(bs, tableISO) +// CRC64ISO uses iso table to checksum data. +func CRC64ISO(data []byte) uint64 { + return CRC64(data, tableISO) } -// CRC64ECMA uses ecma table to checksum bs. -func CRC64ECMA(bs cryptox.Bytes) uint64 { - return CRC64(bs, tableECMA) +// CRC64ECMA uses ecma table to checksum data. +func CRC64ECMA(data []byte) uint64 { + return CRC64(data, tableECMA) } diff --git a/hash/crc_test.go b/hash/crc_test.go index 5a5e526..d55397f 100644 --- a/hash/crc_test.go +++ b/hash/crc_test.go @@ -10,107 +10,107 @@ import ( "testing" ) -// go test -v -cover -count=1 -test.cpu=1 -run=^TestCRC32$ +// go test -v -cover -run=^TestCRC32$ func TestCRC32(t *testing.T) { - cases := map[string]uint32{ + testCases := map[string]uint32{ "": 0, "123": 2286445522, "你好,世界": 2901793364, } - for input, expect := range cases { - crc := CRC32([]byte(input), tableIEEE) - if crc != expect { - t.Fatalf("input %s: crc %d != expect %d", input, crc, expect) + for data, expect := range testCases { + got := CRC32([]byte(data), tableIEEE) + if got != expect { + t.Fatalf("data %q: got %d != expect %d", data, got, expect) } - expectNumber := crc32.ChecksumIEEE([]byte(input)) - if crc != expectNumber { - t.Fatalf("input %s: crc %d != expectNumber %d", input, crc, expectNumber) + expect = crc32.ChecksumIEEE([]byte(data)) + if got != expect { + t.Fatalf("data %q: got %d != expect %d", data, got, expect) } } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestCRC32IEEE$ +// go test -v -cover -run=^TestCRC32IEEE$ func TestCRC32IEEE(t *testing.T) { - cases := map[string]uint32{ + testCases := map[string]uint32{ "": 0, "123": 2286445522, "你好,世界": 2901793364, } - for input, expect := range cases { - crc := CRC32IEEE([]byte(input)) - if crc != expect { - t.Fatalf("input %s: crc %d != expect %d", input, crc, expect) + for data, expect := range testCases { + got := CRC32IEEE([]byte(data)) + if got != expect { + t.Fatalf("data %q: got %d != expect %d", data, got, expect) } - expectNumber := crc32.ChecksumIEEE([]byte(input)) - if crc != expectNumber { - t.Fatalf("input %s: crc %d != expectNumber %d", input, crc, expectNumber) + expect = crc32.ChecksumIEEE([]byte(data)) + if got != expect { + t.Fatalf("data %q: got %d != expect %d", data, got, expect) } } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestCRC64$ +// go test -v -cover -run=^TestCRC64$ func TestCRC64(t *testing.T) { - cases := map[string]uint64{ + testCases := map[string]uint64{ "": 0, "123": 4612164443424423936, "你好,世界": 10914630407878818662, } - for input, expect := range cases { - crc := CRC64([]byte(input), tableISO) - if crc != expect { - t.Fatalf("input %s: crc %d != expect %d", input, crc, expect) + for data, expect := range testCases { + got := CRC64([]byte(data), tableISO) + if got != expect { + t.Fatalf("data %q: got %d != expect %d", data, got, expect) } - expectNumber := crc64.Checksum([]byte(input), crc64.MakeTable(crc64.ISO)) - if crc != expectNumber { - t.Fatalf("input %s: crc %d != expectNumber %d", input, crc, expectNumber) + expect = crc64.Checksum([]byte(data), crc64.MakeTable(crc64.ISO)) + if got != expect { + t.Fatalf("data %q: got %d != expect %d", data, got, expect) } } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestCRC64ISO$ +// go test -v -cover -run=^TestCRC64ISO$ func TestCRC64ISO(t *testing.T) { - cases := map[string]uint64{ + testCases := map[string]uint64{ "": 0, "123": 4612164443424423936, "你好,世界": 10914630407878818662, } - for input, expect := range cases { - crc := CRC64ISO([]byte(input)) - if crc != expect { - t.Fatalf("input %s: crc %d != expect %d", input, crc, expect) + for data, expect := range testCases { + got := CRC64ISO([]byte(data)) + if got != expect { + t.Fatalf("data %q: got %d != expect %d", data, got, expect) } - expectNumber := crc64.Checksum([]byte(input), crc64.MakeTable(crc64.ISO)) - if crc != expectNumber { - t.Fatalf("input %s: crc %d != expectNumber %d", input, crc, expectNumber) + expect = crc64.Checksum([]byte(data), crc64.MakeTable(crc64.ISO)) + if got != expect { + t.Fatalf("data %q: got %d != expect %d", data, got, expect) } } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestCRC64ECMA$ +// go test -v -cover -run=^TestCRC64ECMA$ func TestCRC64ECMA(t *testing.T) { - cases := map[string]uint64{ + testCases := map[string]uint64{ "": 0, "123": 3468660410647627105, "你好,世界": 4520057941183021051, } - for input, expect := range cases { - crc := CRC64ECMA([]byte(input)) - if crc != expect { - t.Fatalf("input %s: crc %d != expect %d", input, crc, expect) + for data, expect := range testCases { + got := CRC64ECMA([]byte(data)) + if got != expect { + t.Fatalf("data %q: got %d != expect %d", data, got, expect) } - expectNumber := crc64.Checksum([]byte(input), crc64.MakeTable(crc64.ECMA)) - if crc != expectNumber { - t.Fatalf("input %s: crc %d != expectNumber %d", input, crc, expectNumber) + expect = crc64.Checksum([]byte(data), crc64.MakeTable(crc64.ECMA)) + if got != expect { + t.Fatalf("data %q: got %d != expect %d", data, got, expect) } } } diff --git a/hash/fnv.go b/hash/fnv.go index 4829b91..8b2fd1b 100644 --- a/hash/fnv.go +++ b/hash/fnv.go @@ -6,54 +6,54 @@ package hash import ( "hash/fnv" - - "github.com/FishGoddess/cryptox" ) -// Fnv32 uses fnv-1/32bit to hash bs. -func Fnv32(bs cryptox.Bytes) uint32 { +// Fnv32 uses fnv-1/32bit to hash data. +func Fnv32(data []byte) uint32 { hash32 := fnv.New32() - hash32.Write(bs) - + hash32.Write(data) return hash32.Sum32() } -// Fnv32a uses fnv-1a/32bit to hash bs. -func Fnv32a(bs cryptox.Bytes) uint32 { +// Fnv32a uses fnv-1a/32bit to hash data. +func Fnv32a(data []byte) uint32 { hash32 := fnv.New32a() - hash32.Write(bs) - + hash32.Write(data) return hash32.Sum32() } -// Fnv64 uses fnv-1/64bit to hash bs. -func Fnv64(bs cryptox.Bytes) uint64 { +// Fnv64 uses fnv-1/64bit to hash data. +func Fnv64(data []byte) uint64 { hash64 := fnv.New64() - hash64.Write(bs) - + hash64.Write(data) return hash64.Sum64() } -// Fnv64a uses fnv-1a/64bit to hash bs. -func Fnv64a(bs cryptox.Bytes) uint64 { +// Fnv64a uses fnv-1a/64bit to hash data. +func Fnv64a(data []byte) uint64 { hash64 := fnv.New64a() - hash64.Write(bs) - + hash64.Write(data) return hash64.Sum64() } -// Fnv128 uses fnv-1/128bit to hash bs. -func Fnv128(bs cryptox.Bytes) cryptox.Bytes { +// Fnv128 uses fnv-1/128bit to hash data. +func Fnv128(data []byte, opts ...Option) []byte { + conf := newConfig().Apply(opts...) + hash128 := fnv.New128() - hash128.Write(bs) + hash128.Write(data) - return hash128.Sum(nil) + sum := hash128.Sum(nil) + return conf.encoding.Encode(sum) } -// Fnv128a uses fnv-1a/128bit to hash bs. -func Fnv128a(bs cryptox.Bytes) cryptox.Bytes { +// Fnv128a uses fnv-1a/128bit to hash data. +func Fnv128a(data []byte, opts ...Option) []byte { + conf := newConfig().Apply(opts...) + hash128 := fnv.New128a() - hash128.Write(bs) + hash128.Write(data) - return hash128.Sum(nil) + sum := hash128.Sum(nil) + return conf.encoding.Encode(sum) } diff --git a/hash/fnv_test.go b/hash/fnv_test.go index a0aee7f..2c8080f 100644 --- a/hash/fnv_test.go +++ b/hash/fnv_test.go @@ -5,151 +5,155 @@ package hash import ( - "bytes" "hash/fnv" + "slices" "testing" + + "github.com/FishGoddess/cryptox/bytes/encoding" ) -// go test -v -cover -count=1 -test.cpu=1 -run=^TestFnv32$ +// go test -v -cover -run=^TestFnv32$ func TestFnv32(t *testing.T) { - cases := map[string]uint32{ + testCases := map[string]uint32{ "": 2166136261, "123": 1926629307, "你好,世界": 4041100496, } - for input, expect := range cases { - fnv32 := Fnv32([]byte(input)) - if fnv32 != expect { - t.Fatalf("input %s: fnv32 %d != expect %d", input, fnv32, expect) + for data, expect := range testCases { + got := Fnv32([]byte(data)) + if got != expect { + t.Fatalf("data %q: got %d != expect %d", data, got, expect) } hash32 := fnv.New32() - hash32.Write([]byte(input)) + hash32.Write([]byte(data)) - expectNumber := hash32.Sum32() - if fnv32 != expectNumber { - t.Fatalf("input %s: fnv32 %d != expectNumber %d", input, fnv32, expectNumber) + expect = hash32.Sum32() + if got != expect { + t.Fatalf("data %q: got %d != expect %d", data, got, expect) } } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestFnv32a$ +// go test -v -cover -run=^TestFnv32a$ func TestFnv32a(t *testing.T) { - cases := map[string]uint32{ + testCases := map[string]uint32{ "": 2166136261, "123": 1916298011, "你好,世界": 3850245558, } - for input, expect := range cases { - fnv32a := Fnv32a([]byte(input)) - if fnv32a != expect { - t.Fatalf("input %s: fnv32a %d != expect %d", input, fnv32a, expect) + for data, expect := range testCases { + got := Fnv32a([]byte(data)) + if got != expect { + t.Fatalf("data %q: got %d != expect %d", data, got, expect) } hash32 := fnv.New32a() - hash32.Write([]byte(input)) + hash32.Write([]byte(data)) - expectNumber := hash32.Sum32() - if fnv32a != expectNumber { - t.Fatalf("input %s: fnv32a %d != expectNumber %d", input, fnv32a, expectNumber) + expect = hash32.Sum32() + if got != expect { + t.Fatalf("data %q: got %d != expect %d", data, got, expect) } } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestFnv64$ +// go test -v -cover -run=^TestFnv64$ func TestFnv64(t *testing.T) { - cases := map[string]uint64{ + testCases := map[string]uint64{ "": 14695981039346656037, "123": 15672520211074539707, "你好,世界": 3537799150651744976, } - for input, expect := range cases { - fnv64 := Fnv64([]byte(input)) - if fnv64 != expect { - t.Fatalf("input %s: fnv64 %d != expect %d", input, fnv64, expect) + for data, expect := range testCases { + got := Fnv64([]byte(data)) + if got != expect { + t.Fatalf("data %q: got %d != expect %d", data, got, expect) } hash64 := fnv.New64() - hash64.Write([]byte(input)) + hash64.Write([]byte(data)) - expectNumber := hash64.Sum64() - if fnv64 != expectNumber { - t.Fatalf("input %s: fnv64 %d != expectNumber %d", input, fnv64, expectNumber) + expect = hash64.Sum64() + if got != expect { + t.Fatalf("data %q: got %d != expect %d", data, got, expect) } } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestFnv64a$ +// go test -v -cover -run=^TestFnv64a$ func TestFnv64a(t *testing.T) { - cases := map[string]uint64{ + testCases := map[string]uint64{ "": 14695981039346656037, "123": 5003431119771845851, "你好,世界": 18316431221504849174, } - for input, expect := range cases { - fnv64a := Fnv64a([]byte(input)) - if fnv64a != expect { - t.Fatalf("input %s: fnv64a %d != expect %d", input, fnv64a, expect) + for data, expect := range testCases { + got := Fnv64a([]byte(data)) + if got != expect { + t.Fatalf("data %q: got %d != expect %d", data, got, expect) } hash64 := fnv.New64a() - hash64.Write([]byte(input)) + hash64.Write([]byte(data)) - expectNumber := hash64.Sum64() - if fnv64a != expectNumber { - t.Fatalf("input %s: fnv64a %d != expectNumber %d", input, fnv64a, expectNumber) + expect = hash64.Sum64() + if got != expect { + t.Fatalf("data %q: got %d != expect %d", data, got, expect) } } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestFnv128$ +// go test -v -cover -run=^TestFnv128$ func TestFnv128(t *testing.T) { - cases := map[string]string{ - "": "6c62272e07bb014262b821756295c58d", - "123": "a68bb31a848b5822836dbc78c6f7cf2b", - "你好,世界": "a143d39fc0ccf3153099c75a3ae16e50", + testCases := map[string][]byte{ + "": []byte("6c62272e07bb014262b821756295c58d"), + "123": []byte("a68bb31a848b5822836dbc78c6f7cf2b"), + "你好,世界": []byte("a143d39fc0ccf3153099c75a3ae16e50"), } - for input, expect := range cases { - fnv128 := Fnv128([]byte(input)) - if fnv128.Hex() != expect { - t.Fatalf("input %s: fnv128 %s != expect %s", input, fnv128.Hex(), expect) + for data, expect := range testCases { + got := Fnv128([]byte(data), WithHex()) + if !slices.Equal(got, expect) { + t.Fatalf("data %q: got %s != expect %s", data, got, expect) } hash128 := fnv.New128() - hash128.Write([]byte(input)) + hash128.Write([]byte(data)) - expectBytes := hash128.Sum(nil) - if !bytes.Equal(fnv128, expectBytes) { - t.Fatalf("input %s: fnv128 %+v != expectBytes %+v", input, fnv128, expectBytes) + expect = hash128.Sum(nil) + expect = encoding.Hex{}.Encode(expect) + if !slices.Equal(got, expect) { + t.Fatalf("data %q: got %s != expectBytes %s", data, got, expect) } } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestFnv128a$ +// go test -v -cover -run=^TestFnv128a$ func TestFnv128a(t *testing.T) { - cases := map[string]string{ - "": "6c62272e07bb014262b821756295c58d", - "123": "a68c893b0c8b5822836dbc791eed46cb", - "你好,世界": "c8a3e17923084bf6e151c7caa750d68e", + testCases := map[string][]byte{ + "": []byte("6c62272e07bb014262b821756295c58d"), + "123": []byte("a68c893b0c8b5822836dbc791eed46cb"), + "你好,世界": []byte("c8a3e17923084bf6e151c7caa750d68e"), } - for input, expect := range cases { - fnv128 := Fnv128a([]byte(input)) - if fnv128.Hex() != expect { - t.Fatalf("input %s: fnv128 %s != expect %s", input, fnv128.Hex(), expect) + for data, expect := range testCases { + got := Fnv128a([]byte(data), WithHex()) + if !slices.Equal(got, expect) { + t.Fatalf("data %q: got %s != expect %s", data, got, expect) } hash128 := fnv.New128a() - hash128.Write([]byte(input)) + hash128.Write([]byte(data)) - expectBytes := hash128.Sum(nil) - if !bytes.Equal(fnv128, expectBytes) { - t.Fatalf("input %s: fnv128 %+v != expectBytes %+v", input, fnv128, expectBytes) + expect = hash128.Sum(nil) + expect = encoding.Hex{}.Encode(expect) + if !slices.Equal(got, expect) { + t.Fatalf("data %q: got %s != expect %s", data, got, expect) } } } diff --git a/hash/hash.go b/hash/hash.go new file mode 100644 index 0000000..ee6805b --- /dev/null +++ b/hash/hash.go @@ -0,0 +1,60 @@ +// Copyright 2024 FishGoddess. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package hash + +import ( + "crypto/md5" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" +) + +// MD5 uses md5 to hash data. +func MD5(data []byte, opts ...Option) []byte { + conf := newConfig().Apply(opts...) + + sum := md5.Sum(data) + return conf.encoding.Encode(sum[:]) +} + +// SHA1 uses sha1 to hash data. +func SHA1(data []byte, opts ...Option) []byte { + conf := newConfig().Apply(opts...) + + sum := sha1.Sum(data) + return conf.encoding.Encode(sum[:]) +} + +// SHA224 uses sha224 to hash data. +func SHA224(data []byte, opts ...Option) []byte { + conf := newConfig().Apply(opts...) + + sum := sha256.Sum224(data) + return conf.encoding.Encode(sum[:]) +} + +// SHA256 uses sha256 to hash data. +func SHA256(data []byte, opts ...Option) []byte { + conf := newConfig().Apply(opts...) + + sum := sha256.Sum256(data) + return conf.encoding.Encode(sum[:]) +} + +// SHA384 uses sha384 to hash data. +func SHA384(data []byte, opts ...Option) []byte { + conf := newConfig().Apply(opts...) + + sum := sha512.Sum384(data) + return conf.encoding.Encode(sum[:]) +} + +// SHA512 uses sha512 to hash data. +func SHA512(data []byte, opts ...Option) []byte { + conf := newConfig().Apply(opts...) + + sum := sha512.Sum512(data) + return conf.encoding.Encode(sum[:]) +} diff --git a/hash/hash_test.go b/hash/hash_test.go new file mode 100644 index 0000000..4016bf4 --- /dev/null +++ b/hash/hash_test.go @@ -0,0 +1,212 @@ +// Copyright 2024 FishGoddess. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package hash + +import ( + "fmt" + "slices" + "testing" +) + +type testHashFunc = func(data []byte, opts ...Option) []byte + +type testCase struct { + Data []byte + HashData []byte + HashDataHex []byte + HashDataBase64 []byte +} + +func testHash(name string, hash testHashFunc, testCases []testCase) error { + for _, testCase := range testCases { + // None + got := hash(testCase.Data) + if !slices.Equal(got, testCase.HashData) { + return fmt.Errorf("%s data %q: got %+v != expect %+v", name, testCase.Data, got, testCase.HashData) + } + + // Hex + got = hash(testCase.Data, WithHex()) + if !slices.Equal(got, testCase.HashDataHex) { + return fmt.Errorf("%s data %q: got hex %s != expect hex %s", name, testCase.Data, got, testCase.HashDataHex) + } + + // Base64 + got = hash(testCase.Data, WithBase64()) + if !slices.Equal(got, testCase.HashDataBase64) { + return fmt.Errorf("%s data %q: got base64 %s != expect base64 %s", name, testCase.Data, got, testCase.HashDataBase64) + } + } + + return nil +} + +// go test -v -cover -run=^TestMD5$ +func TestMD5(t *testing.T) { + testCases := []testCase{ + { + Data: []byte(""), + HashData: []byte{212, 29, 140, 217, 143, 0, 178, 4, 233, 128, 9, 152, 236, 248, 66, 126}, + HashDataHex: []byte("d41d8cd98f00b204e9800998ecf8427e"), + HashDataBase64: []byte("1B2M2Y8AsgTpgAmY7PhCfg=="), + }, + { + Data: []byte("123"), + HashData: []byte{32, 44, 185, 98, 172, 89, 7, 91, 150, 75, 7, 21, 45, 35, 75, 112}, + HashDataHex: []byte("202cb962ac59075b964b07152d234b70"), + HashDataBase64: []byte("ICy5YqxZB1uWSwcVLSNLcA=="), + }, + { + Data: []byte("你好,世界"), + HashData: []byte{219, 239, 211, 173, 160, 24, 97, 91, 53, 88, 138, 1, 226, 22, 174, 110}, + HashDataHex: []byte("dbefd3ada018615b35588a01e216ae6e"), + HashDataBase64: []byte("2+/TraAYYVs1WIoB4haubg=="), + }, + } + + if err := testHash(t.Name(), MD5, testCases); err != nil { + t.Fatal(err) + } +} + +// go test -v -cover -run=^TestSHA1$ +func TestSHA1(t *testing.T) { + testCases := []testCase{ + { + Data: []byte(""), + HashData: []byte{218, 57, 163, 238, 94, 107, 75, 13, 50, 85, 191, 239, 149, 96, 24, 144, 175, 216, 7, 9}, + HashDataHex: []byte("da39a3ee5e6b4b0d3255bfef95601890afd80709"), + HashDataBase64: []byte("2jmj7l5rSw0yVb/vlWAYkK/YBwk="), + }, + { + Data: []byte("123"), + HashData: []byte{64, 189, 0, 21, 99, 8, 95, 195, 81, 101, 50, 158, 161, 255, 92, 94, 203, 219, 190, 239}, + HashDataHex: []byte("40bd001563085fc35165329ea1ff5c5ecbdbbeef"), + HashDataBase64: []byte("QL0AFWMIX8NRZTKeof9cXsvbvu8="), + }, + { + Data: []byte("你好,世界"), + HashData: []byte{59, 236, 176, 59, 1, 94, 212, 128, 80, 97, 28, 141, 122, 254, 75, 136, 247, 13, 90, 32}, + HashDataHex: []byte("3becb03b015ed48050611c8d7afe4b88f70d5a20"), + HashDataBase64: []byte("O+ywOwFe1IBQYRyNev5LiPcNWiA="), + }, + } + + if err := testHash(t.Name(), SHA1, testCases); err != nil { + t.Fatal(err) + } +} + +// go test -v -cover -run=^TestSHA224$ +func TestSHA224(t *testing.T) { + testCases := []testCase{ + { + Data: []byte(""), + HashData: []byte{209, 74, 2, 140, 42, 58, 43, 201, 71, 97, 2, 187, 40, 130, 52, 196, 21, 162, 176, 31, 130, 142, 166, 42, 197, 179, 228, 47}, + HashDataHex: []byte("d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"), + HashDataBase64: []byte("0UoCjCo6K8lHYQK7KII0xBWisB+CjqYqxbPkLw=="), + }, + { + Data: []byte("123"), + HashData: []byte{120, 216, 4, 93, 104, 74, 189, 46, 236, 233, 35, 117, 143, 60, 215, 129, 72, 157, 243, 164, 142, 18, 120, 152, 36, 102, 1, 127}, + HashDataHex: []byte("78d8045d684abd2eece923758f3cd781489df3a48e1278982466017f"), + HashDataBase64: []byte("eNgEXWhKvS7s6SN1jzzXgUid86SOEniYJGYBfw=="), + }, + { + Data: []byte("你好,世界"), + HashData: []byte{154, 101, 161, 40, 24, 184, 230, 172, 53, 124, 238, 147, 55, 86, 83, 55, 245, 91, 218, 138, 69, 176, 193, 191, 185, 244, 64, 60}, + HashDataHex: []byte("9a65a12818b8e6ac357cee9337565337f55bda8a45b0c1bfb9f4403c"), + HashDataBase64: []byte("mmWhKBi45qw1fO6TN1ZTN/Vb2opFsMG/ufRAPA=="), + }, + } + + if err := testHash(t.Name(), SHA224, testCases); err != nil { + t.Fatal(err) + } +} + +// go test -v -cover -run=^TestSHA256$ +func TestSHA256(t *testing.T) { + testCases := []testCase{ + { + Data: []byte(""), + HashData: []byte{227, 176, 196, 66, 152, 252, 28, 20, 154, 251, 244, 200, 153, 111, 185, 36, 39, 174, 65, 228, 100, 155, 147, 76, 164, 149, 153, 27, 120, 82, 184, 85}, + HashDataHex: []byte("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"), + HashDataBase64: []byte("47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU="), + }, + { + Data: []byte("123"), + HashData: []byte{166, 101, 164, 89, 32, 66, 47, 157, 65, 126, 72, 103, 239, 220, 79, 184, 160, 74, 31, 63, 255, 31, 160, 126, 153, 142, 134, 247, 247, 162, 122, 227}, + HashDataHex: []byte("a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3"), + HashDataBase64: []byte("pmWkWSBCL51Bfkhn79xPuKBKHz//H6B+mY6G9/eieuM="), + }, + { + Data: []byte("你好,世界"), + HashData: []byte{70, 147, 47, 30, 110, 165, 33, 110, 119, 245, 139, 25, 8, 215, 46, 201, 50, 46, 209, 41, 49, 140, 109, 75, 212, 69, 11, 94, 170, 185, 215, 231}, + HashDataHex: []byte("46932f1e6ea5216e77f58b1908d72ec9322ed129318c6d4bd4450b5eaab9d7e7"), + HashDataBase64: []byte("RpMvHm6lIW539YsZCNcuyTIu0SkxjG1L1EULXqq51+c="), + }, + } + + if err := testHash(t.Name(), SHA256, testCases); err != nil { + t.Fatal(err) + } +} + +// go test -v -cover -run=^TestSHA384$ +func TestSHA384(t *testing.T) { + testCases := []testCase{ + { + Data: []byte(""), + HashData: []byte{56, 176, 96, 167, 81, 172, 150, 56, 76, 217, 50, 126, 177, 177, 227, 106, 33, 253, 183, 17, 20, 190, 7, 67, 76, 12, 199, 191, 99, 246, 225, 218, 39, 78, 222, 191, 231, 111, 101, 251, 213, 26, 210, 241, 72, 152, 185, 91}, + HashDataHex: []byte("38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"), + HashDataBase64: []byte("OLBgp1GsljhM2TJ+sbHjaiH9txEUvgdDTAzHv2P24donTt6/529l+9Ua0vFImLlb"), + }, + { + Data: []byte("123"), + HashData: []byte{154, 10, 130, 240, 192, 207, 49, 71, 13, 122, 255, 237, 227, 64, 108, 201, 170, 132, 16, 103, 21, 32, 183, 39, 4, 78, 218, 21, 180, 194, 85, 50, 169, 181, 205, 138, 175, 156, 236, 73, 25, 215, 98, 85, 182, 191, 176, 15}, + HashDataHex: []byte("9a0a82f0c0cf31470d7affede3406cc9aa8410671520b727044eda15b4c25532a9b5cd8aaf9cec4919d76255b6bfb00f"), + HashDataBase64: []byte("mgqC8MDPMUcNev/t40BsyaqEEGcVILcnBE7aFbTCVTKptc2Kr5zsSRnXYlW2v7AP"), + }, + { + Data: []byte("你好,世界"), + HashData: []byte{251, 234, 22, 216, 190, 41, 147, 242, 205, 161, 239, 159, 192, 85, 245, 63, 15, 162, 63, 30, 29, 196, 165, 122, 117, 72, 195, 98, 39, 195, 239, 4, 145, 72, 79, 207, 30, 48, 197, 209, 255, 23, 68, 26, 92, 232, 154, 17}, + HashDataHex: []byte("fbea16d8be2993f2cda1ef9fc055f53f0fa23f1e1dc4a57a7548c36227c3ef0491484fcf1e30c5d1ff17441a5ce89a11"), + HashDataBase64: []byte("++oW2L4pk/LNoe+fwFX1Pw+iPx4dxKV6dUjDYifD7wSRSE/PHjDF0f8XRBpc6JoR"), + }, + } + + if err := testHash(t.Name(), SHA384, testCases); err != nil { + t.Fatal(err) + } +} + +// go test -v -cover -run=^TestSHA512$ +func TestSHA512(t *testing.T) { + testCases := []testCase{ + { + Data: []byte(""), + HashData: []byte{207, 131, 225, 53, 126, 239, 184, 189, 241, 84, 40, 80, 214, 109, 128, 7, 214, 32, 228, 5, 11, 87, 21, 220, 131, 244, 169, 33, 211, 108, 233, 206, 71, 208, 209, 60, 93, 133, 242, 176, 255, 131, 24, 210, 135, 126, 236, 47, 99, 185, 49, 189, 71, 65, 122, 129, 165, 56, 50, 122, 249, 39, 218, 62}, + HashDataHex: []byte("cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"), + HashDataBase64: []byte("z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGlODJ6+SfaPg=="), + }, + { + Data: []byte("123"), + HashData: []byte{60, 153, 9, 175, 236, 37, 53, 77, 85, 29, 174, 33, 89, 11, 178, 110, 56, 213, 63, 33, 115, 184, 211, 220, 62, 238, 76, 4, 126, 122, 177, 193, 235, 139, 133, 16, 62, 59, 231, 186, 97, 59, 49, 187, 92, 156, 54, 33, 77, 201, 241, 74, 66, 253, 122, 47, 219, 132, 133, 107, 202, 92, 68, 194}, + HashDataHex: []byte("3c9909afec25354d551dae21590bb26e38d53f2173b8d3dc3eee4c047e7ab1c1eb8b85103e3be7ba613b31bb5c9c36214dc9f14a42fd7a2fdb84856bca5c44c2"), + HashDataBase64: []byte("PJkJr+wlNU1VHa4hWQuybjjVPyFzuNPcPu5MBH56scHri4UQPjvnumE7MbtcnDYhTcnxSkL9ei/bhIVrylxEwg=="), + }, + { + Data: []byte("你好,世界"), + HashData: []byte{69, 166, 227, 254, 120, 175, 74, 51, 38, 218, 155, 248, 195, 64, 123, 202, 95, 239, 128, 179, 52, 192, 70, 210, 5, 68, 176, 178, 139, 230, 199, 97, 113, 140, 250, 245, 183, 82, 234, 168, 152, 73, 184, 58, 77, 78, 95, 109, 244, 144, 142, 25, 92, 216, 193, 89, 24, 30, 120, 151, 25, 16, 219, 19}, + HashDataHex: []byte("45a6e3fe78af4a3326da9bf8c3407bca5fef80b334c046d20544b0b28be6c761718cfaf5b752eaa89849b83a4d4e5f6df4908e195cd8c159181e78971910db13"), + HashDataBase64: []byte("Rabj/nivSjMm2pv4w0B7yl/vgLM0wEbSBUSwsovmx2FxjPr1t1LqqJhJuDpNTl9t9JCOGVzYwVkYHniXGRDbEw=="), + }, + } + + if err := testHash(t.Name(), SHA512, testCases); err != nil { + t.Fatal(err) + } +} diff --git a/hash/md.go b/hash/md.go deleted file mode 100644 index 49a17dc..0000000 --- a/hash/md.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2024 FishGoddess. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package hash - -import ( - "crypto/md5" - - "github.com/FishGoddess/cryptox" -) - -// MD5 uses md5 to hash bs. -func MD5(bs cryptox.Bytes) cryptox.Bytes { - sum := md5.Sum(bs) - return sum[:] -} diff --git a/hash/md_test.go b/hash/md_test.go deleted file mode 100644 index 443de4a..0000000 --- a/hash/md_test.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2024 FishGoddess. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package hash - -import "testing" - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestMD5$ -func TestMD5(t *testing.T) { - cases := map[string]string{ - "": "d41d8cd98f00b204e9800998ecf8427e", - "123": "202cb962ac59075b964b07152d234b70", - "你好,世界": "dbefd3ada018615b35588a01e216ae6e", - } - - for input, expect := range cases { - sum := MD5([]byte(input)) - if sum.Hex() != expect { - t.Fatalf("input %s: sum.Hex() %s != expect %s", input, sum.Hex(), expect) - } - } -} diff --git a/hash/options.go b/hash/options.go new file mode 100644 index 0000000..2d6cda2 --- /dev/null +++ b/hash/options.go @@ -0,0 +1,45 @@ +// Copyright 2025 FishGoddess. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package hash + +import ( + "github.com/FishGoddess/cryptox/bytes/encoding" +) + +type Config struct { + encoding encoding.Encoding +} + +func newConfig() *Config { + conf := &Config{ + encoding: encoding.None{}, + } + + return conf +} + +func (c *Config) Apply(opts ...Option) *Config { + for _, opt := range opts { + opt(c) + } + + return c +} + +type Option func(conf *Config) + +// WithHex sets hex encoding to config. +func WithHex() Option { + return func(conf *Config) { + conf.encoding = encoding.Hex{} + } +} + +// WithBase64 sets base64 encoding to config. +func WithBase64() Option { + return func(conf *Config) { + conf.encoding = encoding.Base64{} + } +} diff --git a/hash/options_test.go b/hash/options_test.go new file mode 100644 index 0000000..f20dbc6 --- /dev/null +++ b/hash/options_test.go @@ -0,0 +1,35 @@ +// Copyright 2025 FishGoddess. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package hash + +import ( + "fmt" + "testing" + + "github.com/FishGoddess/cryptox/bytes/encoding" +) + +// go test -v -cover -run=^TestConfig$ +func TestConfig(t *testing.T) { + opts := []Option{ + WithHex(), + } + + conf := newConfig().Apply(opts...) + + got := fmt.Sprintf("%T", conf.encoding) + expect := fmt.Sprintf("%T", encoding.Hex{}) + if got != expect { + t.Fatalf("got %s != expect %s", got, expect) + } + + conf.Apply(WithBase64()) + + got = fmt.Sprintf("%T", conf.encoding) + expect = fmt.Sprintf("%T", encoding.Base64{}) + if got != expect { + t.Fatalf("got %s != expect %s", got, expect) + } +} diff --git a/hash/sha.go b/hash/sha.go deleted file mode 100644 index 848d39b..0000000 --- a/hash/sha.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2024 FishGoddess. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package hash - -import ( - "crypto/sha1" - "crypto/sha256" - "crypto/sha512" - - "github.com/FishGoddess/cryptox" -) - -// SHA1 uses sha1 to hash bs. -func SHA1(bs cryptox.Bytes) cryptox.Bytes { - sum := sha1.Sum(bs) - return sum[:] -} - -// SHA224 uses sha224 to hash bs. -func SHA224(bs cryptox.Bytes) cryptox.Bytes { - sum := sha256.Sum224(bs) - return sum[:] -} - -// SHA256 uses sha256 to hash bs. -func SHA256(bs cryptox.Bytes) cryptox.Bytes { - sum := sha256.Sum256(bs) - return sum[:] -} - -// SHA384 uses sha384 to hash bs. -func SHA384(bs cryptox.Bytes) cryptox.Bytes { - sum := sha512.Sum384(bs) - return sum[:] -} - -// SHA512 uses sha512 to hash bs. -func SHA512(bs cryptox.Bytes) cryptox.Bytes { - sum := sha512.Sum512(bs) - return sum[:] -} diff --git a/hash/sha_test.go b/hash/sha_test.go deleted file mode 100644 index 942f324..0000000 --- a/hash/sha_test.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2024 FishGoddess. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package hash - -import ( - "testing" -) - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestSHA1$ -func TestSHA1(t *testing.T) { - cases := map[string]string{ - "": "da39a3ee5e6b4b0d3255bfef95601890afd80709", - "123": "40bd001563085fc35165329ea1ff5c5ecbdbbeef", - "你好,世界": "3becb03b015ed48050611c8d7afe4b88f70d5a20", - } - - for input, expect := range cases { - sum := SHA1([]byte(input)) - if sum.Hex() != expect { - t.Fatalf("input %s: sum.Hex() %s != expect %s", input, sum.Hex(), expect) - } - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestSHA224$ -func TestSHA224(t *testing.T) { - cases := map[string]string{ - "": "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", - "123": "78d8045d684abd2eece923758f3cd781489df3a48e1278982466017f", - "你好,世界": "9a65a12818b8e6ac357cee9337565337f55bda8a45b0c1bfb9f4403c", - } - - for input, expect := range cases { - sum := SHA224([]byte(input)) - if sum.Hex() != expect { - t.Fatalf("input %s: sum.Hex() %s != expect %s", input, sum.Hex(), expect) - } - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestSHA256$ -func TestSHA256(t *testing.T) { - cases := map[string]string{ - "": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", - "123": "a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3", - "你好,世界": "46932f1e6ea5216e77f58b1908d72ec9322ed129318c6d4bd4450b5eaab9d7e7", - } - - for input, expect := range cases { - sum := SHA256([]byte(input)) - if sum.Hex() != expect { - t.Fatalf("input %s: sum.Hex() %s != expect %s", input, sum.Hex(), expect) - } - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestSHA384$ -func TestSHA384(t *testing.T) { - cases := map[string]string{ - "": "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b", - "123": "9a0a82f0c0cf31470d7affede3406cc9aa8410671520b727044eda15b4c25532a9b5cd8aaf9cec4919d76255b6bfb00f", - "你好,世界": "fbea16d8be2993f2cda1ef9fc055f53f0fa23f1e1dc4a57a7548c36227c3ef0491484fcf1e30c5d1ff17441a5ce89a11", - } - - for input, expect := range cases { - sum := SHA384([]byte(input)) - if sum.Hex() != expect { - t.Fatalf("input %s: sum.Hex() %s != expect %s", input, sum.Hex(), expect) - } - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestSHA512$ -func TestSHA512(t *testing.T) { - cases := map[string]string{ - "": "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", - "123": "3c9909afec25354d551dae21590bb26e38d53f2173b8d3dc3eee4c047e7ab1c1eb8b85103e3be7ba613b31bb5c9c36214dc9f14a42fd7a2fdb84856bca5c44c2", - "你好,世界": "45a6e3fe78af4a3326da9bf8c3407bca5fef80b334c046d20544b0b28be6c761718cfaf5b752eaa89849b83a4d4e5f6df4908e195cd8c159181e78971910db13", - } - - for input, expect := range cases { - sum := SHA512([]byte(input)) - if sum.Hex() != expect { - t.Fatalf("input %s: sum.Hex() %s != expect %s", input, sum.Hex(), expect) - } - } -} diff --git a/hmac/hmac.go b/hmac/hmac.go index 5f0a1b4..c1cc11e 100644 --- a/hmac/hmac.go +++ b/hmac/hmac.go @@ -10,53 +10,48 @@ import ( "crypto/sha1" "crypto/sha256" "crypto/sha512" - "fmt" stdhash "hash" - - "github.com/FishGoddess/cryptox" ) -func hash(hashFunc func() stdhash.Hash, key cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - h := hmac.New(hashFunc, key) +type hashFunc = func() stdhash.Hash - n, err := h.Write(bs) - if err != nil { - return nil, err - } +func hash(hashFunc hashFunc, data []byte, key []byte, opts ...Option) []byte { + conf := newConfig().Apply(opts...) - if n != len(bs) { - return nil, fmt.Errorf("cryptox/hmac: hashed n %d != len(bs) %d", n, len(bs)) - } + h := hmac.New(hashFunc, key) + h.Write(data) - return h.Sum(nil), nil + data = h.Sum(nil) + data = conf.encoding.Encode(data) + return data } -// MD5 uses hmac-md5 to hash bs and returns an error if failed. -func MD5(key cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - return hash(md5.New, key, bs) +// MD5 uses hmac-md5 to hash data and returns an error if failed. +func MD5(data []byte, key []byte, opts ...Option) []byte { + return hash(md5.New, data, key, opts...) } -// SHA1 uses hmac-sha1 to hash bs and returns an error if failed. -func SHA1(key cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - return hash(sha1.New, key, bs) +// SHA1 uses hmac-sha1 to hash data and returns an error if failed. +func SHA1(data []byte, key []byte, opts ...Option) []byte { + return hash(sha1.New, data, key, opts...) } -// SHA224 uses hmac-sha224 to hash bs and returns an error if failed. -func SHA224(key cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - return hash(sha256.New224, key, bs) +// SHA224 uses hmac-sha224 to hash data and returns an error if failed. +func SHA224(data []byte, key []byte, opts ...Option) []byte { + return hash(sha256.New224, data, key, opts...) } -// SHA256 uses hmac-sha256 to hash bs and returns an error if failed. -func SHA256(key cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - return hash(sha256.New, key, bs) +// SHA256 uses hmac-sha256 to hash data and returns an error if failed. +func SHA256(data []byte, key []byte, opts ...Option) []byte { + return hash(sha256.New, data, key, opts...) } -// SHA384 uses hmac-sha384 to hash bs and returns an error if failed. -func SHA384(key cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - return hash(sha512.New384, key, bs) +// SHA384 uses hmac-sha384 to hash data and returns an error if failed. +func SHA384(data []byte, key []byte, opts ...Option) []byte { + return hash(sha512.New384, data, key, opts...) } -// SHA512 uses hmac-sha512 to hash bs and returns an error if failed. -func SHA512(key cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - return hash(sha512.New, key, bs) +// SHA512 uses hmac-sha512 to hash data and returns an error if failed. +func SHA512(data []byte, key []byte, opts ...Option) []byte { + return hash(sha512.New, data, key, opts...) } diff --git a/hmac/hmac_test.go b/hmac/hmac_test.go index 3fb92aa..65b8072 100644 --- a/hmac/hmac_test.go +++ b/hmac/hmac_test.go @@ -5,131 +5,197 @@ package hmac import ( + "fmt" + "slices" "testing" ) -// go test -v -cover -count=1 -test.cpu=1 -run=^TestMD5$ -func TestMD5(t *testing.T) { - cases := map[string]string{ - "": "63530468a04e386459855da0063b6596", - "123": "52851cb05258c8d98da1672d95729e53", - "你好,世界": "e76d8f84103533dc5d22a6e00cef74f3", - } +type testHashFunc = func(data []byte, key []byte, opts ...Option) []byte + +type testCase struct { + Data []byte + HashData []byte + HashDataHex []byte + HashDataBase64 []byte +} - key := "key" - for input, expect := range cases { - sum, err := MD5([]byte(key), []byte(input)) - if err != nil { - t.Fatal(err) +func testHash(name string, hash testHashFunc, testCases []testCase) error { + key := []byte("key") + for _, testCase := range testCases { + // None + got := hash(testCase.Data, key) + if !slices.Equal(got, testCase.HashData) { + return fmt.Errorf("%s data %q: got %+v != expect %+v", name, testCase.Data, got, testCase.HashData) } - if sum.Hex() != expect { - t.Fatalf("input %s: sum.Hex() %s != expect %s", input, sum.Hex(), expect) + // Hex + got = hash(testCase.Data, key, WithHex()) + if !slices.Equal(got, testCase.HashDataHex) { + return fmt.Errorf("%s data %q: got hex %s != expect hex %s", name, testCase.Data, got, testCase.HashDataHex) + } + + // Base64 + got = hash(testCase.Data, key, WithBase64()) + if !slices.Equal(got, testCase.HashDataBase64) { + return fmt.Errorf("%s data %q: got base64 %s != expect base64 %s", name, testCase.Data, got, testCase.HashDataBase64) } } + + return nil } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestSHA1$ -func TestSHA1(t *testing.T) { - cases := map[string]string{ - "": "f42bb0eeb018ebbd4597ae7213711ec60760843f", - "123": "d4a5b6721d75a5ac15ec698818c77fe1f6e40187", - "你好,世界": "03ab4e9d2332e664fbab840a980820b5ec2a5fad", +// go test -v -cover -run=^TestMD5$ +func TestMD5(t *testing.T) { + testCases := []testCase{ + { + Data: []byte(""), + HashData: []byte{99, 83, 4, 104, 160, 78, 56, 100, 89, 133, 93, 160, 6, 59, 101, 150}, + HashDataHex: []byte("63530468a04e386459855da0063b6596"), + HashDataBase64: []byte("Y1MEaKBOOGRZhV2gBjtllg=="), + }, + { + Data: []byte("123"), + HashData: []byte{82, 133, 28, 176, 82, 88, 200, 217, 141, 161, 103, 45, 149, 114, 158, 83}, + HashDataHex: []byte("52851cb05258c8d98da1672d95729e53"), + HashDataBase64: []byte("UoUcsFJYyNmNoWctlXKeUw=="), + }, + { + Data: []byte("你好,世界"), + HashData: []byte{231, 109, 143, 132, 16, 53, 51, 220, 93, 34, 166, 224, 12, 239, 116, 243}, + HashDataHex: []byte("e76d8f84103533dc5d22a6e00cef74f3"), + HashDataBase64: []byte("522PhBA1M9xdIqbgDO908w=="), + }, } - key := "key" - for input, expect := range cases { - sum, err := SHA1([]byte(key), []byte(input)) - if err != nil { - t.Fatal(err) - } + testHash(t.Name(), MD5, testCases) +} - if sum.Hex() != expect { - t.Fatalf("input %s: sum.Hex() %s != expect %s", input, sum.Hex(), expect) - } +// go test -v -cover -run=^TestSHA1$ +func TestSHA1(t *testing.T) { + testCases := []testCase{ + { + Data: []byte(""), + HashData: []byte{244, 43, 176, 238, 176, 24, 235, 189, 69, 151, 174, 114, 19, 113, 30, 198, 7, 96, 132, 63}, + HashDataHex: []byte("f42bb0eeb018ebbd4597ae7213711ec60760843f"), + HashDataBase64: []byte("9Cuw7rAY671Fl65yE3EexgdghD8="), + }, + { + Data: []byte("123"), + HashData: []byte{212, 165, 182, 114, 29, 117, 165, 172, 21, 236, 105, 136, 24, 199, 127, 225, 246, 228, 1, 135}, + HashDataHex: []byte("d4a5b6721d75a5ac15ec698818c77fe1f6e40187"), + HashDataBase64: []byte("1KW2ch11pawV7GmIGMd/4fbkAYc="), + }, + { + Data: []byte("你好,世界"), + HashData: []byte{3, 171, 78, 157, 35, 50, 230, 100, 251, 171, 132, 10, 152, 8, 32, 181, 236, 42, 95, 173}, + HashDataHex: []byte("03ab4e9d2332e664fbab840a980820b5ec2a5fad"), + HashDataBase64: []byte("A6tOnSMy5mT7q4QKmAggtewqX60="), + }, } + + testHash(t.Name(), SHA1, testCases) } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestSHA224$ +// go test -v -cover -run=^TestSHA224$ func TestSHA224(t *testing.T) { - cases := map[string]string{ - "": "5aa677c13ce1128eeb3a5c01cef7f16557cd0b76d18fd557d6ac3962", - "123": "0036623d2876383f1bf426d7ca8f46884f0d811f664118b2f93c774b", - "你好,世界": "bf21d1f41a11711a43559efc66c169f33c9b6581328fcdb0197abc83", + testCases := []testCase{ + { + Data: []byte(""), + HashData: []byte{90, 166, 119, 193, 60, 225, 18, 142, 235, 58, 92, 1, 206, 247, 241, 101, 87, 205, 11, 118, 209, 143, 213, 87, 214, 172, 57, 98}, + HashDataHex: []byte("5aa677c13ce1128eeb3a5c01cef7f16557cd0b76d18fd557d6ac3962"), + HashDataBase64: []byte("WqZ3wTzhEo7rOlwBzvfxZVfNC3bRj9VX1qw5Yg=="), + }, + { + Data: []byte("123"), + HashData: []byte{0, 54, 98, 61, 40, 118, 56, 63, 27, 244, 38, 215, 202, 143, 70, 136, 79, 13, 129, 31, 102, 65, 24, 178, 249, 60, 119, 75}, + HashDataHex: []byte("0036623d2876383f1bf426d7ca8f46884f0d811f664118b2f93c774b"), + HashDataBase64: []byte("ADZiPSh2OD8b9CbXyo9GiE8NgR9mQRiy+Tx3Sw=="), + }, + { + Data: []byte("你好,世界"), + HashData: []byte{191, 33, 209, 244, 26, 17, 113, 26, 67, 85, 158, 252, 102, 193, 105, 243, 60, 155, 101, 129, 50, 143, 205, 176, 25, 122, 188, 131}, + HashDataHex: []byte("bf21d1f41a11711a43559efc66c169f33c9b6581328fcdb0197abc83"), + HashDataBase64: []byte("vyHR9BoRcRpDVZ78ZsFp8zybZYEyj82wGXq8gw=="), + }, } - key := "key" - for input, expect := range cases { - sum, err := SHA224([]byte(key), []byte(input)) - if err != nil { - t.Fatal(err) - } - - if sum.Hex() != expect { - t.Fatalf("input %s: sum.Hex() %s != expect %s", input, sum.Hex(), expect) - } - } + testHash(t.Name(), SHA224, testCases) } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestSHA256$ +// go test -v -cover -run=^TestSHA256$ func TestSHA256(t *testing.T) { - cases := map[string]string{ - "": "5d5d139563c95b5967b9bd9a8c9b233a9dedb45072794cd232dc1b74832607d0", - "123": "a7f7739b1dc5b4e922b1226c9fcbdc83498dee375382caee08fd52a13eb7cfe2", - "你好,世界": "ecebc269659999d50a6f74743f5814cf08000c7f7da1bf4efd46ed651778ed94", + testCases := []testCase{ + { + Data: []byte(""), + HashData: []byte{93, 93, 19, 149, 99, 201, 91, 89, 103, 185, 189, 154, 140, 155, 35, 58, 157, 237, 180, 80, 114, 121, 76, 210, 50, 220, 27, 116, 131, 38, 7, 208}, + HashDataHex: []byte("5d5d139563c95b5967b9bd9a8c9b233a9dedb45072794cd232dc1b74832607d0"), + HashDataBase64: []byte("XV0TlWPJW1lnub2ajJsjOp3ttFByeUzSMtwbdIMmB9A="), + }, + { + Data: []byte("123"), + HashData: []byte{167, 247, 115, 155, 29, 197, 180, 233, 34, 177, 34, 108, 159, 203, 220, 131, 73, 141, 238, 55, 83, 130, 202, 238, 8, 253, 82, 161, 62, 183, 207, 226}, + HashDataHex: []byte("a7f7739b1dc5b4e922b1226c9fcbdc83498dee375382caee08fd52a13eb7cfe2"), + HashDataBase64: []byte("p/dzmx3FtOkisSJsn8vcg0mN7jdTgsruCP1SoT63z+I="), + }, + { + Data: []byte("你好,世界"), + HashData: []byte{236, 235, 194, 105, 101, 153, 153, 213, 10, 111, 116, 116, 63, 88, 20, 207, 8, 0, 12, 127, 125, 161, 191, 78, 253, 70, 237, 101, 23, 120, 237, 148}, + HashDataHex: []byte("ecebc269659999d50a6f74743f5814cf08000c7f7da1bf4efd46ed651778ed94"), + HashDataBase64: []byte("7OvCaWWZmdUKb3R0P1gUzwgADH99ob9O/UbtZRd47ZQ="), + }, } - key := "key" - for input, expect := range cases { - sum, err := SHA256([]byte(key), []byte(input)) - if err != nil { - t.Fatal(err) - } - - if sum.Hex() != expect { - t.Fatalf("input %s: sum.Hex() %s != expect %s", input, sum.Hex(), expect) - } - } + testHash(t.Name(), SHA256, testCases) } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestSHA384$ +// go test -v -cover -run=^TestSHA384$ func TestSHA384(t *testing.T) { - cases := map[string]string{ - "": "99f44bb4e73c9d0ef26533596c8d8a32a5f8c10a9b997d30d89a7e35ba1ccf200b985f72431202b891fe350da410e43f", - "123": "a94c9966bd530d65b5b09fd226479926bef037705e2090a0b24ab11922d821a7076c0a8bc120a9b49e41cd38428ec7ec", - "你好,世界": "49cb156c54572f3fa27fe882f4f08eec06347435f3f641e042b718cab8e8d1ecca76b41e657ff46591ff6c5efd89c1a3", + testCases := []testCase{ + { + Data: []byte(""), + HashData: []byte{153, 244, 75, 180, 231, 60, 157, 14, 242, 101, 51, 89, 108, 141, 138, 50, 165, 248, 193, 10, 155, 153, 125, 48, 216, 154, 126, 53, 186, 28, 207, 32, 11, 152, 95, 114, 67, 18, 2, 184, 145, 254, 53, 13, 164, 16, 228, 63}, + HashDataHex: []byte("99f44bb4e73c9d0ef26533596c8d8a32a5f8c10a9b997d30d89a7e35ba1ccf200b985f72431202b891fe350da410e43f"), + HashDataBase64: []byte("mfRLtOc8nQ7yZTNZbI2KMqX4wQqbmX0w2Jp+NboczyALmF9yQxICuJH+NQ2kEOQ/"), + }, + { + Data: []byte("123"), + HashData: []byte{169, 76, 153, 102, 189, 83, 13, 101, 181, 176, 159, 210, 38, 71, 153, 38, 190, 240, 55, 112, 94, 32, 144, 160, 178, 74, 177, 25, 34, 216, 33, 167, 7, 108, 10, 139, 193, 32, 169, 180, 158, 65, 205, 56, 66, 142, 199, 236}, + HashDataHex: []byte("a94c9966bd530d65b5b09fd226479926bef037705e2090a0b24ab11922d821a7076c0a8bc120a9b49e41cd38428ec7ec"), + HashDataBase64: []byte("qUyZZr1TDWW1sJ/SJkeZJr7wN3BeIJCgskqxGSLYIacHbAqLwSCptJ5BzThCjsfs"), + }, + { + Data: []byte("你好,世界"), + HashData: []byte{73, 203, 21, 108, 84, 87, 47, 63, 162, 127, 232, 130, 244, 240, 142, 236, 6, 52, 116, 53, 243, 246, 65, 224, 66, 183, 24, 202, 184, 232, 209, 236, 202, 118, 180, 30, 101, 127, 244, 101, 145, 255, 108, 94, 253, 137, 193, 163}, + HashDataHex: []byte("49cb156c54572f3fa27fe882f4f08eec06347435f3f641e042b718cab8e8d1ecca76b41e657ff46591ff6c5efd89c1a3"), + HashDataBase64: []byte("ScsVbFRXLz+if+iC9PCO7AY0dDXz9kHgQrcYyrjo0ezKdrQeZX/0ZZH/bF79icGj"), + }, } - key := "key" - for input, expect := range cases { - sum, err := SHA384([]byte(key), []byte(input)) - if err != nil { - t.Fatal(err) - } - - if sum.Hex() != expect { - t.Fatalf("input %s: sum.Hex() %s != expect %s", input, sum.Hex(), expect) - } - } + testHash(t.Name(), SHA384, testCases) } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestSHA512$ +// go test -v -cover -run=^TestSHA512$ func TestSHA512(t *testing.T) { - cases := map[string]string{ - "": "84fa5aa0279bbc473267d05a53ea03310a987cecc4c1535ff29b6d76b8f1444a728df3aadb89d4a9a6709e1998f373566e8f824a8ca93b1821f0b69bc2a2f65e", - "123": "2ea823c645b1baf845ef76096a6d7fa9e568304ba9f7910bd52f01c03eec39cdfeec54e50b86b62ef5bfb9e6ce5c0be747ec13b3a199f9d235e99a36de369a84", - "你好,世界": "f92aac8fe726f61d7247bf918c0f118bfef4ecd1765c363ce53738130f690755552363e387869a4a5cf7f0fe587a2e6a6d79a4f113dc7229a9845e5b94c9689f", + testCases := []testCase{ + { + Data: []byte(""), + HashData: []byte{132, 250, 90, 160, 39, 155, 188, 71, 50, 103, 208, 90, 83, 234, 3, 49, 10, 152, 124, 236, 196, 193, 83, 95, 242, 155, 109, 118, 184, 241, 68, 74, 114, 141, 243, 170, 219, 137, 212, 169, 166, 112, 158, 25, 152, 243, 115, 86, 110, 143, 130, 74, 140, 169, 59, 24, 33, 240, 182, 155, 194, 162, 246, 94}, + HashDataHex: []byte("84fa5aa0279bbc473267d05a53ea03310a987cecc4c1535ff29b6d76b8f1444a728df3aadb89d4a9a6709e1998f373566e8f824a8ca93b1821f0b69bc2a2f65e"), + HashDataBase64: []byte("hPpaoCebvEcyZ9BaU+oDMQqYfOzEwVNf8pttdrjxREpyjfOq24nUqaZwnhmY83NWbo+CSoypOxgh8LabwqL2Xg=="), + }, + { + Data: []byte("123"), + HashData: []byte{46, 168, 35, 198, 69, 177, 186, 248, 69, 239, 118, 9, 106, 109, 127, 169, 229, 104, 48, 75, 169, 247, 145, 11, 213, 47, 1, 192, 62, 236, 57, 205, 254, 236, 84, 229, 11, 134, 182, 46, 245, 191, 185, 230, 206, 92, 11, 231, 71, 236, 19, 179, 161, 153, 249, 210, 53, 233, 154, 54, 222, 54, 154, 132}, + HashDataHex: []byte("2ea823c645b1baf845ef76096a6d7fa9e568304ba9f7910bd52f01c03eec39cdfeec54e50b86b62ef5bfb9e6ce5c0be747ec13b3a199f9d235e99a36de369a84"), + HashDataBase64: []byte("LqgjxkWxuvhF73YJam1/qeVoMEup95EL1S8BwD7sOc3+7FTlC4a2LvW/uebOXAvnR+wTs6GZ+dI16Zo23jaahA=="), + }, + { + Data: []byte("你好,世界"), + HashData: []byte{249, 42, 172, 143, 231, 38, 246, 29, 114, 71, 191, 145, 140, 15, 17, 139, 254, 244, 236, 209, 118, 92, 54, 60, 229, 55, 56, 19, 15, 105, 7, 85, 85, 35, 99, 227, 135, 134, 154, 74, 92, 247, 240, 254, 88, 122, 46, 106, 109, 121, 164, 241, 19, 220, 114, 41, 169, 132, 94, 91, 148, 201, 104, 159}, + HashDataHex: []byte("f92aac8fe726f61d7247bf918c0f118bfef4ecd1765c363ce53738130f690755552363e387869a4a5cf7f0fe587a2e6a6d79a4f113dc7229a9845e5b94c9689f"), + HashDataBase64: []byte("+Sqsj+cm9h1yR7+RjA8Ri/707NF2XDY85Tc4Ew9pB1VVI2Pjh4aaSlz38P5Yei5qbXmk8RPccimphF5blMlonw=="), + }, } - key := "key" - for input, expect := range cases { - sum, err := SHA512([]byte(key), []byte(input)) - if err != nil { - t.Fatal(err) - } - - if sum.Hex() != expect { - t.Fatalf("input %s: sum.Hex() %s != expect %s", input, sum.Hex(), expect) - } - } + testHash(t.Name(), SHA512, testCases) } diff --git a/hmac/options.go b/hmac/options.go new file mode 100644 index 0000000..001f529 --- /dev/null +++ b/hmac/options.go @@ -0,0 +1,45 @@ +// Copyright 2025 FishGoddess. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package hmac + +import ( + "github.com/FishGoddess/cryptox/bytes/encoding" +) + +type Config struct { + encoding encoding.Encoding +} + +func newConfig() *Config { + conf := &Config{ + encoding: encoding.None{}, + } + + return conf +} + +func (c *Config) Apply(opts ...Option) *Config { + for _, opt := range opts { + opt(c) + } + + return c +} + +type Option func(conf *Config) + +// WithHex sets hex encoding to config. +func WithHex() Option { + return func(conf *Config) { + conf.encoding = encoding.Hex{} + } +} + +// WithBase64 sets base64 encoding to config. +func WithBase64() Option { + return func(conf *Config) { + conf.encoding = encoding.Base64{} + } +} diff --git a/hmac/options_test.go b/hmac/options_test.go new file mode 100644 index 0000000..cb69db5 --- /dev/null +++ b/hmac/options_test.go @@ -0,0 +1,35 @@ +// Copyright 2025 FishGoddess. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package hmac + +import ( + "fmt" + "testing" + + "github.com/FishGoddess/cryptox/bytes/encoding" +) + +// go test -v -cover -run=^TestConfig$ +func TestConfig(t *testing.T) { + opts := []Option{ + WithHex(), + } + + conf := newConfig().Apply(opts...) + + got := fmt.Sprintf("%T", conf.encoding) + expect := fmt.Sprintf("%T", encoding.Hex{}) + if got != expect { + t.Fatalf("got %s != expect %s", got, expect) + } + + conf.Apply(WithBase64()) + + got = fmt.Sprintf("%T", conf.encoding) + expect = fmt.Sprintf("%T", encoding.Base64{}) + if got != expect { + t.Fatalf("got %s != expect %s", got, expect) + } +} diff --git a/padding.go b/padding.go deleted file mode 100644 index 85b6b39..0000000 --- a/padding.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2024 FishGoddess. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package cryptox - -import "fmt" - -var ( - PaddingNone Padding = paddingNone{} - PaddingZero Padding = paddingZero{} - PaddingPKCS5 Padding = paddingPKCS7{} // PKCS5 is actually the same as pkcs7. - PaddingPKCS7 Padding = paddingPKCS7{} -) - -// Padding paddings and undo paddings to a byte slice. -type Padding interface { - Padding(bs Bytes, blockSize int) Bytes - UndoPadding(bs Bytes, blockSize int) (Bytes, error) -} - -type paddingNone struct{} - -func (paddingNone) Padding(bs Bytes, blockSize int) Bytes { - return bs -} - -func (paddingNone) UndoPadding(bs Bytes, blockSize int) (Bytes, error) { - return bs, nil -} - -type paddingZero struct{} - -func (paddingZero) Padding(bs Bytes, blockSize int) Bytes { - padding := blockSize - (len(bs) % blockSize) - - for i := 0; i < padding; i++ { - bs = append(bs, 0) - } - - return bs -} - -func (paddingZero) UndoPadding(bs Bytes, blockSize int) (Bytes, error) { - length := len(bs) - - var i int - for i = length; i > 0; i-- { - if bs[i-1] != 0 { - break - } - - // Remove blockSize of bytes at most due to padding blockSize of bytes at most. - if length-i >= blockSize { - break - } - } - - return bs[:i], nil -} - -type paddingPKCS7 struct{} - -func (paddingPKCS7) Padding(bs Bytes, blockSize int) Bytes { - padding := blockSize - (len(bs) % blockSize) - - for i := 0; i < padding; i++ { - bs = append(bs, byte(padding)) - } - - return bs -} - -func (paddingPKCS7) UndoPadding(bs Bytes, blockSize int) (Bytes, error) { - length := len(bs) - number := int(bs[length-1]) - - if number > length { - return nil, fmt.Errorf("cryptox: unpadding pkcs7 number %d > length %d", number, length) - } - - if number > blockSize { - return nil, fmt.Errorf("cryptox: unpadding pkcs7 number %d > blockSize %d", number, blockSize) - } - - return bs[:length-number], nil -} diff --git a/padding_test.go b/padding_test.go deleted file mode 100644 index d965288..0000000 --- a/padding_test.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2024 FishGoddess. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package cryptox - -import ( - "bytes" - "testing" -) - -type paddingTestCase struct { - Data Bytes - Want Bytes -} - -func testPadding(t *testing.T, padding Padding, testCases []paddingTestCase) { - blockSize := 8 - - for _, testCase := range testCases { - got := padding.Padding(testCase.Data, blockSize) - if !bytes.Equal(got, testCase.Want) { - t.Fatalf("got %+v != want %+v", got, testCase.Want) - } - - gotUndo, err := padding.UndoPadding(got, blockSize) - if err != nil { - t.Fatal(err) - } - - if !bytes.Equal(gotUndo, testCase.Data) { - t.Fatalf("gotUndo %+v != data %+v", gotUndo, testCase.Data) - } - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestPaddingNone$ -func TestPaddingNone(t *testing.T) { - testCases := []paddingTestCase{ - {Data: Bytes{}, Want: Bytes{}}, - {Data: Bytes{1, 2, 3, 4, 5}, Want: Bytes{1, 2, 3, 4, 5}}, - {Data: Bytes{1, 2, 3, 4, 5, 6, 7, 8}, Want: Bytes{1, 2, 3, 4, 5, 6, 7, 8}}, - {Data: Bytes{1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0}, Want: Bytes{1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0}}, - } - - testPadding(t, PaddingNone, testCases) -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestPaddingZero$ -func TestPaddingZero(t *testing.T) { - testCases := []paddingTestCase{ - {Data: Bytes{}, Want: Bytes{0, 0, 0, 0, 0, 0, 0, 0}}, - {Data: Bytes{1, 2, 3, 4, 5}, Want: Bytes{1, 2, 3, 4, 5, 0, 0, 0}}, - {Data: Bytes{1, 2, 3, 4, 5, 6, 7, 8}, Want: Bytes{1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0}}, - {Data: Bytes{1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0}, Want: Bytes{1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, - } - - testPadding(t, PaddingZero, testCases) -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestPaddingPKCS5$ -func TestPaddingPKCS5(t *testing.T) { - testCases := []paddingTestCase{ - {Data: Bytes{}, Want: Bytes{8, 8, 8, 8, 8, 8, 8, 8}}, - {Data: Bytes{1, 2, 3, 4, 5}, Want: Bytes{1, 2, 3, 4, 5, 3, 3, 3}}, - {Data: Bytes{1, 2, 3, 4, 5, 6, 7, 8}, Want: Bytes{1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8}}, - {Data: Bytes{1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8}, Want: Bytes{1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}}, - } - - testPadding(t, PaddingPKCS5, testCases) -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestPaddingPKCS7$ -func TestPaddingPKCS7(t *testing.T) { - testCases := []paddingTestCase{ - {Data: Bytes{}, Want: Bytes{8, 8, 8, 8, 8, 8, 8, 8}}, - {Data: Bytes{1, 2, 3, 4, 5}, Want: Bytes{1, 2, 3, 4, 5, 3, 3, 3}}, - {Data: Bytes{1, 2, 3, 4, 5, 6, 7, 8}, Want: Bytes{1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8}}, - {Data: Bytes{1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8}, Want: Bytes{1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}}, - } - - testPadding(t, PaddingPKCS7, testCases) -} diff --git a/rand.go b/rand.go deleted file mode 100644 index e59518f..0000000 --- a/rand.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2024 FishGoddess. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package cryptox - -import ( - "math/rand" - "time" - "unsafe" -) - -const words = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" - -var source = rand.NewSource(time.Now().UnixNano()) - -// AppendBytes appends n random bytes to bs. -func AppendBytes(bs Bytes, n int) Bytes { - length := int64(len(words)) - - for i := 0; i < n; i++ { - index := source.Int63() % length - bs = append(bs, words[index]) - } - - return bs -} - -// GenerateBytes generates n random bytes and returns them. -// It usually used to generate an iv and store it to an safe database. -// It means each encrypted data has an different iv so your encrypted data is safer than using a fixed iv. -// However, you should know that the encrypted data of same data will be different if the iv is different. -func GenerateBytes(n int) Bytes { - bs := make(Bytes, 0, n) - bs = AppendBytes(bs, n) - - return bs -} - -// GenerateString generates a string including n random bytes and returns it. -func GenerateString(n int) string { - bs := GenerateBytes(n) - bsPtr := unsafe.SliceData(bs) - - return unsafe.String(bsPtr, len(bs)) -} diff --git a/rand_test.go b/rand_test.go deleted file mode 100644 index 5a88a32..0000000 --- a/rand_test.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2024 FishGoddess. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package cryptox - -import ( - "testing" -) - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestAppendBytes$ -func TestAppendBytes(t *testing.T) { - n := 32 - - for i := 0; i < 10; i++ { - bs := make([]byte, 0, n) - bs = AppendBytes(bs, n) - - if len(bs) != n { - t.Fatalf(" len(bs) %d != n %d", len(bs), n) - } - - t.Logf("%s\n", bs) - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestGenerateBytes$ -func TestGenerateBytes(t *testing.T) { - for i := 1; i <= 64; i++ { - bs := GenerateBytes(i) - - if len(bs) != i { - t.Fatalf(" len(bs) %d != %d", len(bs), i) - } - - t.Logf("%s\n", bs) - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestGenerateString$ -func TestGenerateString(t *testing.T) { - for i := 1; i <= 64; i++ { - str := GenerateString(i) - - if len(str) != i { - t.Fatalf(" len(str) %d != %d", len(str), i) - } - - t.Logf("%s\n", str) - } -} diff --git a/rsa/key.go b/rsa/key.go index 2679ba9..20a3ad5 100644 --- a/rsa/key.go +++ b/rsa/key.go @@ -5,139 +5,133 @@ package rsa import ( - "crypto/rand" "crypto/rsa" "io" "os" - - "github.com/FishGoddess/cryptox" ) -// GenerateKeys generates a key set of bits. +// GenerateKeys generates a private key of bits and a public key. func GenerateKeys(bits int, opts ...KeyOption) (PrivateKey, PublicKey, error) { - privateKey, err := GeneratePrivateKey(bits, opts...) - if err != nil { - return PrivateKey{}, PublicKey{}, err - } + conf := newKeyConfig().Apply(opts...) - publicKey, err := GeneratePublicKey(privateKey, opts...) + key, err := rsa.GenerateKey(conf.random, bits) if err != nil { return PrivateKey{}, PublicKey{}, err } + privateKey := PrivateKey{key: key} + publicKey := PublicKey{key: &(key.PublicKey)} return privateKey, publicKey, nil } -// GeneratePrivateKey generates a private key of bits. -func GeneratePrivateKey(bits int, opts ...KeyOption) (PrivateKey, error) { - conf := newKeyConfig(opts) +// WritePrivateKey writes the private key to the writer. +func WritePrivateKey(writer io.Writer, privateKey PrivateKey, opts ...KeyOption) error { + conf := newKeyConfig().Apply(opts...) - privateKey, err := rsa.GenerateKey(rand.Reader, bits) + data, err := conf.encodePrivateKey(privateKey.key) if err != nil { - return PrivateKey{}, err + return err } - privateKeyBytes, err := conf.privateKeyEncoder.Encode(privateKey) - if err != nil { - return PrivateKey{}, err - } - - return newPrivateKey(privateKey, privateKeyBytes), nil + _, err = writer.Write(data) + return err } -// GeneratePublicKey generates a public key from private key. -func GeneratePublicKey(privateKey PrivateKey, opts ...KeyOption) (PublicKey, error) { - conf := newKeyConfig(opts) - publicKey := &(privateKey.Key().PublicKey) +// WritePublicKey writes the public key to the writer. +func WritePublicKey(writer io.Writer, publicKey PublicKey, opts ...KeyOption) error { + conf := newKeyConfig().Apply(opts...) - publicKeyBytes, err := conf.publicKeyEncoder.Encode(publicKey) + data, err := conf.encodePublicKey(publicKey.key) if err != nil { - return PublicKey{}, err + return err } - return newPublicKey(publicKey, publicKeyBytes), nil + _, err = writer.Write(data) + return err } -// ParsePrivateKey parses a private key from pem bytes. -func ParsePrivateKey(keyBytes cryptox.Bytes, opts ...KeyOption) (PrivateKey, error) { - conf := newKeyConfig(opts) +// ReadPrivateKey reads the private key from the reader. +func ReadPrivateKey(reader io.Reader, opts ...KeyOption) (PrivateKey, error) { + conf := newKeyConfig().Apply(opts...) - privateKey, err := conf.privateKeyDecoder.Decode(keyBytes) + data, err := io.ReadAll(reader) if err != nil { return PrivateKey{}, err } - return newPrivateKey(privateKey, keyBytes), nil -} - -// ParsePublicKey parses a public key from pem bytes. -func ParsePublicKey(keyBytes cryptox.Bytes, opts ...KeyOption) (PublicKey, error) { - conf := newKeyConfig(opts) - - publicKey, err := conf.publicKeyDecoder.Decode(keyBytes) + key, err := conf.decodePrivateKey(data) if err != nil { - return PublicKey{}, err + return PrivateKey{}, err } - return newPublicKey(publicKey, keyBytes), nil + privateKey := PrivateKey{key: key} + return privateKey, nil } -// ReadPrivateKey reads a private key from a reader. -func ReadPrivateKey(keyReader io.Reader, opts ...KeyOption) (PrivateKey, error) { - keyBytes, err := io.ReadAll(keyReader) +// ReadPublicKey reads the public key from the reader. +func ReadPublicKey(reader io.Reader, opts ...KeyOption) (PublicKey, error) { + conf := newKeyConfig().Apply(opts...) + + data, err := io.ReadAll(reader) if err != nil { - return PrivateKey{}, err + return PublicKey{}, err } - return ParsePrivateKey(keyBytes, opts...) -} - -// ReadPublicKey reads a public key from a reader. -func ReadPublicKey(keyReader io.Reader, opts ...KeyOption) (PublicKey, error) { - keyBytes, err := io.ReadAll(keyReader) + key, err := conf.decodePublicKey(data) if err != nil { return PublicKey{}, err } - return ParsePublicKey(keyBytes, opts...) + publicKey := PublicKey{key: key} + return publicKey, nil } -// LoadPrivateKey loads a private key from a file. -func LoadPrivateKey(keyFile string, opts ...KeyOption) (PrivateKey, error) { - keyBytes, err := os.ReadFile(keyFile) +func newFile(file string) (*os.File, error) { + flag := os.O_CREATE | os.O_WRONLY | os.O_EXCL + perm := os.FileMode(0644) + return os.OpenFile(file, flag, perm) +} + +// StorePrivateKey stores the private key to the file. +func StorePrivateKey(file string, privateKey PrivateKey, opts ...KeyOption) error { + f, err := newFile(file) if err != nil { - return PrivateKey{}, err + return err } - return ParsePrivateKey(keyBytes, opts...) + defer f.Close() + return WritePrivateKey(f, privateKey, opts...) } -// LoadPublicKey loads a public key from a file. -func LoadPublicKey(keyFile string, opts ...KeyOption) (PublicKey, error) { - keyBytes, err := os.ReadFile(keyFile) +// StorePublicKey stores the public key to the file. +func StorePublicKey(file string, publicKey PublicKey, opts ...KeyOption) error { + f, err := newFile(file) if err != nil { - return PublicKey{}, err + return err } - return ParsePublicKey(keyBytes, opts...) + defer f.Close() + return WritePublicKey(f, publicKey, opts...) } -// MustLoadPrivateKey loads private key from a file or panic if failed. -func MustLoadPrivateKey(keyFile string, opts ...KeyOption) PrivateKey { - key, err := LoadPrivateKey(keyFile, opts...) +// LoadPrivateKey loads the private key from the file. +func LoadPrivateKey(file string, opts ...KeyOption) (PrivateKey, error) { + f, err := os.Open(file) if err != nil { - panic(err) + return PrivateKey{}, err } - return key + defer f.Close() + return ReadPrivateKey(f, opts...) } -// MustLoadPublicKey loads public key from a file or panic if failed. -func MustLoadPublicKey(keyFile string, opts ...KeyOption) PublicKey { - key, err := LoadPublicKey(keyFile, opts...) +// LoadPublicKey loads the public key from the file. +func LoadPublicKey(file string, opts ...KeyOption) (PublicKey, error) { + f, err := os.Open(file) if err != nil { - panic(err) + return PublicKey{}, err } - return key + defer f.Close() + return ReadPublicKey(f, opts...) } diff --git a/rsa/key_test.go b/rsa/key_test.go index fde5ea9..ed41749 100644 --- a/rsa/key_test.go +++ b/rsa/key_test.go @@ -10,234 +10,109 @@ import ( "testing" ) -// go test -v -cover -count=1 -test.cpu=1 -run=^TestGenerateKeys$ +// go test -v -cover -run=^TestGenerateKeys$ func TestGenerateKeys(t *testing.T) { privateKey, publicKey, err := GenerateKeys(2048) if err != nil { t.Fatal(err) } - t.Log("Public Key:", privateKey) - t.Log("Private Key:", publicKey) + t.Log("Public Key:", privateKey.key) + t.Log("Private Key:", publicKey.key) } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestGeneratePrivateKey$ -func TestGeneratePrivateKey(t *testing.T) { - privateKey, err := GeneratePrivateKey(2048) +// go test -v -cover -run=^TestWriteReadPrivateKey$ +func TestWriteReadPrivateKey(t *testing.T) { + privateKey, _, err := GenerateKeys(2048) if err != nil { t.Fatal(err) } - t.Log("Private Key:", privateKey) -} + buffer := bytes.NewBuffer(make([]byte, 0, 4096)) -// go test -v -cover -count=1 -test.cpu=1 -run=^TestGeneratePublicKey$ -func TestGeneratePublicKey(t *testing.T) { - privateKey, publicKey1, err := GenerateKeys(2048) + err = WritePrivateKey(buffer, privateKey) if err != nil { t.Fatal(err) } - publicKey2, err := GeneratePublicKey(privateKey) + bufferKey, err := ReadPrivateKey(buffer) if err != nil { t.Fatal(err) } - if !publicKey2.EqualsTo(publicKey1) { - t.Fatalf("publicKey2 %+v != publicKey1 %+v", publicKey2, publicKey1) + if !bufferKey.key.Equal(privateKey.key) { + t.Fatalf("bufferKey %+v != privateKey %+v", bufferKey.key, privateKey.key) } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestParsePrivateKey$ -func TestParsePrivateKey(t *testing.T) { - privateKey, err := GeneratePrivateKey(2048) - if err != nil { - t.Fatal(err) - } - - parsedPrivateKey, err := ParsePrivateKey(privateKey.Bytes()) +// go test -v -cover -run=^TestWriteReadPublicKey$ +func TestWriteReadPublicKey(t *testing.T) { + _, publicKey, err := GenerateKeys(2048) if err != nil { t.Fatal(err) } - if !parsedPrivateKey.EqualsTo(privateKey) { - t.Fatalf("parsedPrivateKey %+v != privateKey %+v", parsedPrivateKey, privateKey) - } -} + buffer := bytes.NewBuffer(make([]byte, 0, 4096)) -// go test -v -cover -count=1 -test.cpu=1 -run=^TestParsePublicKey$ -func TestParsePublicKey(t *testing.T) { - privateKey, err := GeneratePrivateKey(2048) + err = WritePublicKey(buffer, publicKey) if err != nil { t.Fatal(err) } - publicKey, err := GeneratePublicKey(privateKey) + bufferKey, err := ReadPublicKey(buffer) if err != nil { t.Fatal(err) } - parsedPublicKey, err := ParsePublicKey(publicKey.Bytes()) - if err != nil { - t.Fatal(err) - } - - if !parsedPublicKey.EqualsTo(publicKey) { - t.Fatalf("parsedPublicKey %+v != publicKey %+v", parsedPublicKey, publicKey) + if !bufferKey.key.Equal(publicKey.key) { + t.Fatalf("bufferKey %+v != publicKey %+v", bufferKey.key, publicKey.key) } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestReadPrivateKey$ -func TestReadPrivateKey(t *testing.T) { - privateKey, err := GeneratePrivateKey(2048) +// go test -v -cover -run=^TestStoreLoadPrivateKey$ +func TestStoreLoadPrivateKey(t *testing.T) { + privateKey, _, err := GenerateKeys(2048) if err != nil { t.Fatal(err) } - reader := bytes.NewReader(privateKey.Bytes()) - - readPrivateKey, err := ReadPrivateKey(reader) - if err != nil { - t.Fatal(err) - } - - if !readPrivateKey.EqualsTo(privateKey) { - t.Fatalf("readPrivateKey %+v != privateKey %+v", readPrivateKey, privateKey) - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestReadPublicKey$ -func TestReadPublicKey(t *testing.T) { - privateKey, err := GeneratePrivateKey(2048) - if err != nil { - t.Fatal(err) - } + file := filepath.Join(t.TempDir(), t.Name()+".key") - publicKey, err := GeneratePublicKey(privateKey) + err = StorePrivateKey(file, privateKey) if err != nil { t.Fatal(err) } - reader := bytes.NewReader(publicKey.Bytes()) - - readPublicKey, err := ReadPublicKey(reader) + fileKey, err := LoadPrivateKey(file) if err != nil { t.Fatal(err) } - if !readPublicKey.EqualsTo(publicKey) { - t.Fatalf("readPublicKey %+v != publicKey %+v", readPublicKey, publicKey) + if !fileKey.key.Equal(privateKey.key) { + t.Fatalf("fileKey %+v != privateKey %+v", fileKey.key, privateKey.key) } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestLoadPrivateKey$ -func TestLoadPrivateKey(t *testing.T) { - privateKey, err := GeneratePrivateKey(2048) +// go test -v -cover -run=^TestStoreLoadPublicKey$ +func TestStoreLoadPublicKey(t *testing.T) { + _, publicKey, err := GenerateKeys(2048) if err != nil { t.Fatal(err) } - privateKeyFile := filepath.Join(t.TempDir(), "TestLoadPrivateKey.key") + file := filepath.Join(t.TempDir(), t.Name()+".key") - _, err = privateKey.Bytes().WriteToFile(privateKeyFile) + err = StorePublicKey(file, publicKey) if err != nil { t.Fatal(err) } - loadedPrivateKey, err := LoadPrivateKey(privateKeyFile) + fileKey, err := LoadPublicKey(file) if err != nil { t.Fatal(err) } - if !loadedPrivateKey.EqualsTo(privateKey) { - t.Fatalf("loadedPrivateKey %+v != privateKey %+v", loadedPrivateKey, privateKey) - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestLoadPublicKey$ -func TestLoadPublicKey(t *testing.T) { - privateKey, err := GeneratePrivateKey(2048) - if err != nil { - t.Fatal(err) - } - - publicKey, err := GeneratePublicKey(privateKey) - if err != nil { - t.Fatal(err) - } - - publicKeyFile := filepath.Join(t.TempDir(), "TestLoadPublicKey.pub") - - _, err = publicKey.Bytes().WriteToFile(publicKeyFile) - if err != nil { - t.Fatal(err) - } - - loadedPublicKey, err := LoadPublicKey(publicKeyFile) - if err != nil { - t.Fatal(err) - } - - if !loadedPublicKey.EqualsTo(publicKey) { - t.Fatalf("loadedPublicKey %+v != publicKey %+v", loadedPublicKey, publicKey) - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestMustLoadPrivateKey$ -func TestMustLoadPrivateKey(t *testing.T) { - privateKey, err := GeneratePrivateKey(2048) - if err != nil { - t.Fatal(err) - } - - privateKeyFile := filepath.Join(t.TempDir(), "TestMustLoadPrivateKey.key") - - _, err = privateKey.Bytes().WriteToFile(privateKeyFile) - if err != nil { - t.Fatal(err) - } - - defer func() { - if err := recover(); err != nil { - t.Fatal(err) - } - }() - - loadedPrivateKey := MustLoadPrivateKey(privateKeyFile) - - if !loadedPrivateKey.EqualsTo(privateKey) { - t.Fatalf("loadedPrivateKey %+v != privateKey %+v", loadedPrivateKey, privateKey) - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestMustLoadPublicKey$ -func TestMustLoadPublicKey(t *testing.T) { - privateKey, err := GeneratePrivateKey(2048) - if err != nil { - t.Fatal(err) - } - - publicKey, err := GeneratePublicKey(privateKey) - if err != nil { - t.Fatal(err) - } - - publicKeyFile := filepath.Join(t.TempDir(), "TestMustLoadPublicKey.pub") - - _, err = publicKey.Bytes().WriteToFile(publicKeyFile) - if err != nil { - t.Fatal(err) - } - - defer func() { - if err := recover(); err != nil { - t.Fatal(err) - } - }() - - loadedPublicKey := MustLoadPublicKey(publicKeyFile) - - if !loadedPublicKey.EqualsTo(publicKey) { - t.Fatalf("loadedPublicKey %+v != publicKey %+v", loadedPublicKey, publicKey) + if !fileKey.key.Equal(publicKey.key) { + t.Fatalf("fileKey %+v != publicKey %+v", fileKey.key, publicKey.key) } } diff --git a/rsa/options.go b/rsa/options.go index 4f6369b..eb6098d 100644 --- a/rsa/options.go +++ b/rsa/options.go @@ -1,4 +1,4 @@ -// Copyright 2024 FishGoddess. All rights reserved. +// Copyright 2025 FishGoddess. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. @@ -7,110 +7,148 @@ package rsa import ( "crypto" "crypto/rand" + "crypto/rsa" "crypto/sha256" "hash" "io" + + "github.com/FishGoddess/cryptox/bytes/encoding" + "github.com/FishGoddess/cryptox/x509" ) type KeyConfig struct { - privateKeyEncoder PrivateKeyEncoder - privateKeyDecoder PrivateKeyDecoder - publicKeyEncoder PublicKeyEncoder - publicKeyDecoder PublicKeyDecoder + random io.Reader + encodePrivateKey func(key *rsa.PrivateKey) ([]byte, error) + encodePublicKey func(key *rsa.PublicKey) ([]byte, error) + decodePrivateKey func(data []byte) (*rsa.PrivateKey, error) + decodePublicKey func(data []byte) (*rsa.PublicKey, error) } -func newKeyConfig(opts []KeyOption) *KeyConfig { +func newKeyConfig() *KeyConfig { conf := &KeyConfig{ - privateKeyEncoder: X509.PKCS1PrivateKeyEncoder, - privateKeyDecoder: X509.PKCS1PrivateKeyDecoder, - publicKeyEncoder: X509.PKIXPublicKeyEncoder, - publicKeyDecoder: X509.PKIXPublicKeyDecoder, + random: rand.Reader, + encodePrivateKey: x509.EncodePrivateKeyPKCS8[*rsa.PrivateKey], + encodePublicKey: x509.EncodePublicKeyPKIX[*rsa.PublicKey], + decodePrivateKey: x509.DecodePrivateKeyPKCS8[*rsa.PrivateKey], + decodePublicKey: x509.DecodePublicKeyPKIX[*rsa.PublicKey], } + return conf +} + +func (kc *KeyConfig) Apply(opts ...KeyOption) *KeyConfig { for _, opt := range opts { - opt.ApplyTo(conf) + opt(kc) } - return conf + return kc } type KeyOption func(conf *KeyConfig) -func (ko KeyOption) ApplyTo(conf *KeyConfig) { - ko(conf) +// WithKeyRandom sets random to key config. +func WithKeyRandom(random io.Reader) KeyOption { + return func(conf *KeyConfig) { + conf.random = random + } } -// WithPrivateKeyEncoder sets private key encoder to conf. -func WithPrivateKeyEncoder(encoder PrivateKeyEncoder) KeyOption { +// WithKeyEncodePrivate sets encode to key config. +func WithKeyEncodePrivate(encode func(key *rsa.PrivateKey) ([]byte, error)) KeyOption { return func(conf *KeyConfig) { - conf.privateKeyEncoder = encoder + conf.encodePrivateKey = encode } } -// WithPrivateKeyDecoder sets private key decoder to conf. -func WithPrivateKeyDecoder(decoder PrivateKeyDecoder) KeyOption { +// WithKeyEncodePublic sets encode to key config. +func WithKeyEncodePublic(encode func(key *rsa.PublicKey) ([]byte, error)) KeyOption { return func(conf *KeyConfig) { - conf.privateKeyDecoder = decoder + conf.encodePublicKey = encode } } -// WithPublicKeyEncoder sets public key encoder to conf. -func WithPublicKeyEncoder(encoder PublicKeyEncoder) KeyOption { +// WithKeyDecodePrivate sets decode to key config. +func WithKeyDecodePrivate(decode func(data []byte) (*rsa.PrivateKey, error)) KeyOption { return func(conf *KeyConfig) { - conf.publicKeyEncoder = encoder + conf.decodePrivateKey = decode } } -// WithPublicKeyDecoder sets public key decoder to conf. -func WithPublicKeyDecoder(decoder PublicKeyDecoder) KeyOption { +// WithKeyDecodePublic sets decode to key config. +func WithKeyDecodePublic(decode func(data []byte) (*rsa.PublicKey, error)) KeyOption { return func(conf *KeyConfig) { - conf.publicKeyDecoder = decoder + conf.decodePublicKey = decode } } type Config struct { + encoding encoding.Encoding random io.Reader hash hash.Hash cryptoHash crypto.Hash + saltLength int } -func newConfig(opts []Option) *Config { +func newConfig() *Config { conf := &Config{ + encoding: encoding.None{}, random: rand.Reader, hash: sha256.New(), cryptoHash: crypto.SHA256, + saltLength: rsa.PSSSaltLengthAuto, } + return conf +} + +func (c *Config) Apply(opts ...Option) *Config { for _, opt := range opts { - opt.ApplyTo(conf) + opt(c) } - return conf + return c } type Option func(conf *Config) -func (o Option) ApplyTo(conf *Config) { - o(conf) +// WithHex sets hex encoding to config. +func WithHex() Option { + return func(conf *Config) { + conf.encoding = encoding.Hex{} + } } -// WithRandom sets random to conf. +// WithBase64 sets base64 encoding to config. +func WithBase64() Option { + return func(conf *Config) { + conf.encoding = encoding.Base64{} + } +} + +// WithRandom sets random to config. func WithRandom(random io.Reader) Option { return func(conf *Config) { conf.random = random } } -// WithHash sets hash to conf. +// WithHash sets hash to config. func WithHash(hash hash.Hash) Option { return func(conf *Config) { conf.hash = hash } } -// WithCryptoHash sets crypto hash to conf. +// WithCryptoHash sets crypto hash to config. func WithCryptoHash(hash crypto.Hash) Option { return func(conf *Config) { conf.cryptoHash = hash } } + +// WithSalt sets salt length to config. +func WithSalt(saltLength int) Option { + return func(conf *Config) { + conf.saltLength = saltLength + } +} diff --git a/rsa/options_test.go b/rsa/options_test.go index 73a7f66..b9235fd 100644 --- a/rsa/options_test.go +++ b/rsa/options_test.go @@ -1,4 +1,4 @@ -// Copyright 2024 FishGoddess. All rights reserved. +// Copyright 2025 FishGoddess. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. @@ -7,182 +7,104 @@ package rsa import ( "crypto" "crypto/rand" + "crypto/rsa" "crypto/sha256" "fmt" "testing" + + "github.com/FishGoddess/cryptox/bytes/encoding" + "github.com/FishGoddess/cryptox/x509" ) -// go test -v -cover -count=1 -test.cpu=1 -run=^TestNewKeyConfig$ -func TestNewKeyConfig(t *testing.T) { +// go test -v -cover -run=^TestKeyConfig$ +func TestKeyConfig(t *testing.T) { opts := []KeyOption{ - WithPrivateKeyEncoder(X509.PKCS8PrivateKeyEncoder), - WithPrivateKeyDecoder(X509.PKCS8PrivateKeyDecoder), - WithPublicKeyEncoder(X509.PKCS1PublicKeyEncoder), - WithPublicKeyDecoder(X509.PKCS1PublicKeyDecoder), + WithKeyRandom(rand.Reader), + WithKeyEncodePrivate(x509.EncodePrivateKeyPKCS8), + WithKeyEncodePublic(x509.EncodePublicKeyPKIX), + WithKeyDecodePrivate(x509.DecodePrivateKeyPKCS8), + WithKeyDecodePublic(x509.DecodePublicKeyPKIX), } - conf := newKeyConfig(opts) - - encoderPointer := fmt.Sprintf("%p", conf.privateKeyEncoder) - expectPointer := fmt.Sprintf("%p", X509.PKCS8PrivateKeyEncoder) + conf := newKeyConfig().Apply(opts...) - if encoderPointer != expectPointer { - t.Fatalf("encoderPointer %s != expectPointer %s", encoderPointer, expectPointer) + got := fmt.Sprintf("%p", conf.random) + expect := fmt.Sprintf("%p", rand.Reader) + if got != expect { + t.Fatalf("got %s != expect %s", got, expect) } - decoderPointer := fmt.Sprintf("%p", conf.privateKeyDecoder) - expectPointer = fmt.Sprintf("%p", X509.PKCS8PrivateKeyDecoder) - - if decoderPointer != expectPointer { - t.Fatalf("decoderPointer %s != expectPointer %s", decoderPointer, expectPointer) + got = fmt.Sprintf("%p", conf.encodePrivateKey) + expect = fmt.Sprintf("%p", x509.EncodePrivateKeyPKCS8[*rsa.PrivateKey]) + if got != expect { + t.Fatalf("got %s != expect %s", got, expect) } - encoderPointer = fmt.Sprintf("%p", conf.publicKeyEncoder) - expectPointer = fmt.Sprintf("%p", X509.PKCS1PublicKeyEncoder) - - if encoderPointer != expectPointer { - t.Fatalf("encoderPointer %s != expectPointer %s", encoderPointer, expectPointer) + got = fmt.Sprintf("%p", conf.encodePublicKey) + expect = fmt.Sprintf("%p", x509.EncodePublicKeyPKIX[*rsa.PublicKey]) + if got != expect { + t.Fatalf("got %s != expect %s", got, expect) } - decoderPointer = fmt.Sprintf("%p", conf.publicKeyDecoder) - expectPointer = fmt.Sprintf("%p", X509.PKCS1PublicKeyDecoder) - - if decoderPointer != expectPointer { - t.Fatalf("decoderPointer %s != expectPointer %s", decoderPointer, expectPointer) - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestWithPrivateKeyEncoder$ -func TestWithPrivateKeyEncoder(t *testing.T) { - conf := &KeyConfig{privateKeyEncoder: nil} - - opt := WithPrivateKeyEncoder(X509.PKCS1PrivateKeyEncoder) - opt.ApplyTo(conf) - - encoderPointer := fmt.Sprintf("%p", conf.privateKeyEncoder) - expectPointer := fmt.Sprintf("%p", X509.PKCS1PrivateKeyEncoder) - - if encoderPointer != expectPointer { - t.Fatalf("encoderPointer %s != expectPointer %s", encoderPointer, expectPointer) - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestWithPrivateKeyDecoder$ -func TestWithPrivateKeyDecoder(t *testing.T) { - conf := &KeyConfig{privateKeyDecoder: nil} - - opt := WithPrivateKeyDecoder(X509.PKCS1PrivateKeyDecoder) - opt.ApplyTo(conf) - - decoderPointer := fmt.Sprintf("%p", conf.privateKeyDecoder) - expectPointer := fmt.Sprintf("%p", X509.PKCS1PrivateKeyDecoder) - - if decoderPointer != expectPointer { - t.Fatalf("decoderPointer %s != expectPointer %s", decoderPointer, expectPointer) - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestWithPublicKeyEncoder$ -func TestWithPublicKeyEncoder(t *testing.T) { - conf := &KeyConfig{publicKeyEncoder: nil} - - opt := WithPublicKeyEncoder(X509.PKIXPublicKeyEncoder) - opt.ApplyTo(conf) - - encoderPointer := fmt.Sprintf("%p", conf.publicKeyEncoder) - expectPointer := fmt.Sprintf("%p", X509.PKIXPublicKeyEncoder) - - if encoderPointer != expectPointer { - t.Fatalf("encoderPointer %s != expectPointer %s", encoderPointer, expectPointer) + got = fmt.Sprintf("%p", conf.decodePrivateKey) + expect = fmt.Sprintf("%p", x509.DecodePrivateKeyPKCS8[*rsa.PrivateKey]) + if got != expect { + t.Fatalf("got %s != expect %s", got, expect) } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestWithPublicKeyDecoder$ -func TestWithPublicKeyDecoder(t *testing.T) { - conf := &KeyConfig{publicKeyDecoder: nil} - - opt := WithPublicKeyDecoder(X509.PKIXPublicKeyDecoder) - opt.ApplyTo(conf) - - decoderPointer := fmt.Sprintf("%p", conf.publicKeyDecoder) - expectPointer := fmt.Sprintf("%p", X509.PKIXPublicKeyDecoder) - if decoderPointer != expectPointer { - t.Fatalf("decoderPointer %s != expectPointer %s", decoderPointer, expectPointer) + got = fmt.Sprintf("%p", conf.decodePublicKey) + expect = fmt.Sprintf("%p", x509.DecodePublicKeyPKIX[*rsa.PublicKey]) + if got != expect { + t.Fatalf("got %s != expect %s", got, expect) } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestNewConfig$ -func TestNewConfig(t *testing.T) { +// go test -v -cover -run=^TestConfig$ +func TestConfig(t *testing.T) { hash := sha256.New() + saltLength := 32 opts := []Option{ + WithHex(), WithRandom(rand.Reader), WithHash(hash), WithCryptoHash(crypto.SHA256), + WithSalt(saltLength), } - conf := newConfig(opts) + conf := newConfig().Apply(opts...) - randomPointer := fmt.Sprintf("%p", conf.random) - expectPointer := fmt.Sprintf("%p", rand.Reader) - - if randomPointer != expectPointer { - t.Fatalf("randomPointer %s != expectPointer %s", randomPointer, expectPointer) + got := fmt.Sprintf("%T", conf.encoding) + expect := fmt.Sprintf("%T", encoding.Hex{}) + if got != expect { + t.Fatalf("got %s != expect %s", got, expect) } - hashPointer := fmt.Sprintf("%p", conf.hash) - expectPointer = fmt.Sprintf("%p", hash) + conf.Apply(WithBase64()) - if hashPointer != expectPointer { - t.Fatalf("hashPointer %s != expectPointer %s", hashPointer, expectPointer) + got = fmt.Sprintf("%T", conf.encoding) + expect = fmt.Sprintf("%T", encoding.Base64{}) + if got != expect { + t.Fatalf("got %s != expect %s", got, expect) } - if conf.cryptoHash != crypto.SHA256 { - t.Fatalf("conf.cryptoHash %d != crypto.SHA256", conf.cryptoHash) + got = fmt.Sprintf("%p", conf.random) + expect = fmt.Sprintf("%p", rand.Reader) + if got != expect { + t.Fatalf("got %s != expect %s", got, expect) } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestWithRandom$ -func TestWithRandom(t *testing.T) { - conf := &Config{random: nil} - - opt := WithRandom(rand.Reader) - opt.ApplyTo(conf) - randomPointer := fmt.Sprintf("%p", conf.random) - expectPointer := fmt.Sprintf("%p", rand.Reader) - - if randomPointer != expectPointer { - t.Fatalf("randomPointer %s != expectPointer %s", randomPointer, expectPointer) + got = fmt.Sprintf("%p", conf.hash) + expect = fmt.Sprintf("%p", hash) + if got != expect { + t.Fatalf("got %s != expect %s", got, expect) } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestWithHash$ -func TestWithHash(t *testing.T) { - conf := &Config{random: nil} - - hash := sha256.New() - opt := WithHash(hash) - opt.ApplyTo(conf) - hashPointer := fmt.Sprintf("%p", conf.hash) - expectPointer := fmt.Sprintf("%p", hash) - - if hashPointer != expectPointer { - t.Fatalf("hashPointer %s != expectPointer %s", hashPointer, expectPointer) + if conf.cryptoHash != crypto.SHA256 { + t.Fatalf("got %d != expect %d", conf.cryptoHash, crypto.SHA256) } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestWithCryptoHash$ -func TestWithCryptoHash(t *testing.T) { - conf := &Config{cryptoHash: 0} - - hash := crypto.SHA256 - opt := WithCryptoHash(hash) - opt.ApplyTo(conf) - if conf.cryptoHash != hash { - t.Fatalf("conf.cryptoHash %d != hash %d", conf.cryptoHash, hash) + if conf.saltLength != saltLength { + t.Fatalf("got %d != expect %d", conf.saltLength, saltLength) } } diff --git a/rsa/private.go b/rsa/private.go index dd15b71..fb9003a 100644 --- a/rsa/private.go +++ b/rsa/private.go @@ -6,71 +6,77 @@ package rsa import ( "crypto/rsa" - - "github.com/FishGoddess/cryptox" + "fmt" ) type PrivateKey struct { - key *rsa.PrivateKey - keyBytes cryptox.Bytes + key *rsa.PrivateKey } -func newPrivateKey(key *rsa.PrivateKey, keyBytes cryptox.Bytes) PrivateKey { - return PrivateKey{key: key, keyBytes: keyBytes} -} +// DecryptPKCS1v15 decrypts data with pkcs1 v15. +func (pk PrivateKey) DecryptPKCS1v15(data []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) -// Key returns the key of pk. -func (pk PrivateKey) Key() *rsa.PrivateKey { - return pk.key -} + data, err := conf.encoding.Decode(data) + if err != nil { + return nil, err + } -// Bytes returns the bytes of pk. -func (pk PrivateKey) Bytes() cryptox.Bytes { - return pk.keyBytes + return rsa.DecryptPKCS1v15(conf.random, pk.key, data) } -// String returns the string of pk. -func (pk PrivateKey) String() string { - return string(pk.keyBytes) -} +// DecryptPKCS1v15SessionKey decrypts data using a session key with pkcs1 v15. +func (pk PrivateKey) DecryptPKCS1v15SessionKey(data []byte, sessionKey []byte, opts ...Option) error { + conf := newConfig().Apply(opts...) -// EqualsTo returns if pk equals to privateKey. -func (pk PrivateKey) EqualsTo(privateKey PrivateKey) bool { - return pk.key.Equal(privateKey.key) -} + data, err := conf.encoding.Decode(data) + if err != nil { + return err + } -// DecryptPKCS1v15 decrypts msg with pkcs1 v15. -func (pk PrivateKey) DecryptPKCS1v15(msg cryptox.Bytes, opts ...Option) (cryptox.Bytes, error) { - conf := newConfig(opts) - return rsa.DecryptPKCS1v15(conf.random, pk.key, msg) + return rsa.DecryptPKCS1v15SessionKey(conf.random, pk.key, data, sessionKey) } -// DecryptPKCS1v15SessionKey decrypts msg using a session key with pkcs1 v15. -func (pk PrivateKey) DecryptPKCS1v15SessionKey(msg cryptox.Bytes, sessionKey cryptox.Bytes, opts ...Option) error { - conf := newConfig(opts) - return rsa.DecryptPKCS1v15SessionKey(conf.random, pk.key, msg, sessionKey) -} +// DecryptOAEP decrypts data with oaep. +func (pk PrivateKey) DecryptOAEP(data []byte, label []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) -// DecryptOAEP decrypts msg with oaep. -func (pk PrivateKey) DecryptOAEP(msg cryptox.Bytes, label cryptox.Bytes, opts ...Option) (cryptox.Bytes, error) { - conf := newConfig(opts) - return rsa.DecryptOAEP(conf.hash, conf.random, pk.key, msg, label) + data, err := conf.encoding.Decode(data) + if err != nil { + return nil, err + } + + return rsa.DecryptOAEP(conf.hash, conf.random, pk.key, data, label) } -// SignPKCS1v15 signs hashed data with pkcs1 v15. -func (pk PrivateKey) SignPKCS1v15(hashed cryptox.Bytes, opts ...Option) (cryptox.Bytes, error) { - conf := newConfig(opts) - return rsa.SignPKCS1v15(conf.random, pk.key, conf.cryptoHash, hashed) +// SignPKCS1v15 signs hashed with pkcs1 v15. +func (pk PrivateKey) SignPKCS1v15(hashed []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + + sign, err := rsa.SignPKCS1v15(conf.random, pk.key, conf.cryptoHash, hashed) + if err != nil { + return nil, err + } + + sign = conf.encoding.Encode(sign) + return sign, nil } -// SignPSS signs digest data with pss. -func (pk PrivateKey) SignPSS(digest cryptox.Bytes, saltLength int, opts ...Option) (cryptox.Bytes, error) { - conf := newConfig(opts) +// SignPSS signs digest with pss. +func (pk PrivateKey) SignPSS(digest []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) + + if !conf.cryptoHash.Available() { + return nil, fmt.Errorf("cryptox/rsa: crypto hash %+v isn't available", conf.cryptoHash) + } + + pssOpts := &rsa.PSSOptions{Hash: conf.cryptoHash, SaltLength: conf.saltLength} - pssOpts := &rsa.PSSOptions{ - Hash: conf.cryptoHash, - SaltLength: saltLength, + sign, err := rsa.SignPSS(conf.random, pk.key, conf.cryptoHash, digest, pssOpts) + if err != nil { + return nil, err } - return rsa.SignPSS(conf.random, pk.key, conf.cryptoHash, digest, pssOpts) + sign = conf.encoding.Encode(sign) + return sign, nil } diff --git a/rsa/private_test.go b/rsa/private_test.go index af06e97..d39b1d6 100644 --- a/rsa/private_test.go +++ b/rsa/private_test.go @@ -6,214 +6,186 @@ package rsa import ( "bytes" - "crypto/rand" - "crypto/rsa" + "crypto/sha256" "testing" - - "github.com/FishGoddess/cryptox" ) -func newTestPrivateKey(t *testing.T) PrivateKey { - keyBytes := []byte(`-----BEGIN PRIVATE KEY----- -MIIEowIBAAKCAQEAu0KvOo1/9owLI+GZuzluPmixfDEeNBA+t2qppsVT9xb4huZb -wXwowNP6KU4vPpdF0KhHSmaFOf8IIXSoZ/xI7bLxs10Te1fSqZInVuj912VLj/uw -uK7OG1zfsN0mt8I2d+9zYzAGykh/U/skYALOzvmfvamcQGHT1TuxOsQln3Eq0477 -VGmk53vTMOxEU033CUEabuNOiWlM8TsaDEqxYWO3Two+rSNW4S48WTQhekhqtxxg -0LhJfB/T9tCOmzuTln4oVk4peZW+CH0UJijtd/2Ypx/Hyk0yXQgGtIKUN35avn2/ -ga56HOxGYumk22Q4Xv4OZOmevzPLyvRZDZMWuwIDAQABAoIBACxasCybRXr9Usot -7n7VLJKls5xp+fB1BJXnMsXoqWm2TCmPuJ4MrY525yMPfMtDg2rX4QLzY40IJkHe -YuE2dYOvxeYpHqsxcxltH9pLF40EVjCFeidUaEc86VL4HuqZmtMvqVIOFx2krFwU -+VmwcJG/uKFw4iyxvz4bhHAZ85tfB+zvB5ZwwBNQ1hhcUlbdzIFQxe0p+IIoxVTy -bHGRX5Pvm95PbgLAJUYXans7xzmN+czIQ4+7REBXoO6OmnnhoWvkfOx4oaflnq5H -BlesQbbPNXVzFBQA6JzokavkfkgiMJh5582P9AmA+hEzeA+0o5aN5UcK8LzceFc/ -CTE+pRECgYEA0shkcL0EMibMkKzwpJ5eZzIOtcraBHA7AJgG5m8LJhmAZGQSAHdx -Ycp/pXJbkKzEt1QoH+YHxP8YsaCHtcNvggVwKHJgDqDh2RVVGGWDI4E+kswd/8nc -EO4Ki+znURn95mSFTQZt6ukUS8RVo9fEmGPG730KspAHIvmgiXct7HMCgYEA426D -PcxpULZ6TF/TiZ8QyVQiD/lAGLyXkqgz9tvvn4LPQbwg48ACEs17Cyg/YjEe6hOt -e3Ia+LHvYbHnQ+6JkbstBf4xMNqpIdrvTs87m/ev/prUPTTitwhLKYjBCquitk6d -sGIn6TTZdC0wbEodI1nFP6SIIXBpejNQic6AopkCgYBmgGjHokNuGAwWtuL3SsRh -rqgUo6bNzb4DleqVGJ71UiVrrHZMn8kVYyIb1LbObhXjiRtSF8zjcaISjxwvufB5 -7CcUpDouIvJxXLxa9tKE648AWB6miwVnfjrGvNfoSpl79poUUPIW8G2cQsfau0yx -RqQxRj9zgjLWQUpeTwSYHQKBgCM+gQAWqUtku9cSEooFKGjKrOykx5YNw79qaYMb -2ipx7wRUzxP8MVYQmbzE4+2nhw7nNb8nk55ulJYjJ5+TW6ZFx1hiZ+UWPZeNggBI -hQhKfe+KttE1XNzYYC1zj9bDeleeHzmyPFUbZ4dlaVeetJ1B0Btot9/Wt8HEKfrx -EWYhAoGBAMkxeAD1q9LA6lgpNzflna0bWgKLpHj0Iz6/9wMmVkxTMJzQDxMbNhUj -Fx6f6eSBQiEJPPypIKoMRctcBX+tmR4iuLhe5y8S9uVW7DUP3I4mGeGkap/Fz4fO -jqy/B5Twb/tggfdM5id+3frrF2xf7/bgPwNij9zLKovJgEIALil4 ------END PRIVATE KEY-----`) - - privateKey, err := ParsePrivateKey(keyBytes) +func newTestPrivateKey() PrivateKey { + reader := bytes.NewReader([]byte(`-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCnoCnYqtdcxo2Z +zcrpkfHYg+s/gGrlt7Ww//gBJfcCZ64ls4fD4I0BD+Xe0WHuT5poow6Cyyl20yrv +Y13oqlRr05bsnPpu2ZOiEPfiAQb898xgYYwoDwLEk75Uu1ox53unhhZbRKNne1V1 +QMYFpxAgeAMxML09ZmEqPp3q0BNZcjZqaq/2umHVOBOrqrRJZG7APZpixAvwbRKJ +zELq40s0zD6ECovZk/3WVF8BEcrmybmnbns1Y89ptXFZIQVKPqHXUK9+VbIiKZsU ++UKpY29WKjOkELcSrG7HMZbIXivzswGn5c0DskXQ/TbzV5WNxpW07Op0s3y9G7n+ +kTcytZETAgMBAAECggEACpRE/9aBwr+0lnU9WLIW0Y47LdCk2ScChw9omhNI0cnf +B6RwgvAW8MRi7XCeiHlVVURGjqz+ysU3YPtkT7esSKUxJxwd1sV8QqQP2PTX+ZA0 +Fw3LrYg2VFdqEZPvdICCYTIeaBfeuOcOSi5Shm8i8+xPG0QoYAnS7mUcTK0MLj65 +FYcb1p7rteeqpQwKqekaAEqOo084Nbs10++ltct3wM4xOvEFJubdMUj6kruOVwG5 +onuyONTWQ9U1akSRwlx1HnafieTG/Rsvx+6FT4wAgioZy3JKlWsLckI0K5zi/YSz +Z0ZeqdtGLI7JbYIKv+H+zeLv0mPErwOXJk0qm3b6eQKBgQDJVMYzwB1BIVU545dz +8+YX69WfFVjH9RSJ+9monq6fu0QW6s2qH4O8DCKqG5fuSAXPVzYvVrEDV4KGsyPG +m0uQteM5om7ICIQVvewxBiaDX0bvFwiK7gfzdLVMgYirrkxZUQ4NgGTRScAP2ysm +sOn52Om5ZK5JZ3v8C/aQU/D5CQKBgQDVJGcDV7XlOVMptAVICEP4G+jg2fS9ORlz +TkgpK8sw1l1P6eO9WU2TrOh8UCQFntVe5YwgzQuBK4PJACsbHqx1Xww57MI1dlIQ +p2vP6dFmVlneIrcHXZ4QRQug6Envp03Smnbzf9clDCAtZkt3gGDc2r0yxn8AY6rE ++q6mITHMOwKBgQC7ernmzutvDv8yHQGX9HM7q10N+u7lpQ8vPtt87edmzxekz5oc +5aPipNpS1ccxGNhwL6JBitTja8YccQzLkSlY5EdoEB5hH60AIg+jxzpt83c2hZhq +5yV4TCHX0HfYh0KJmbUgVYOMcMTs/wa7zNrU0m0zOtIhgMAwAWPlGoW3IQKBgHSs +l6NRySVwiuCiRd3XgHV5ubIUPY+ziQYAjSnUakcSoUPUkbEeCIRVO3KJYB6fgseO +unVeKPUNf/dwmygeU2Nwoz22J92iJmwtaawHn3P4wvsBX9WtXpAja6kqXwbMO6KU +oZbLnVcPWzHe9GK3KM7dAoKf+/eXl2x6mU4hj6PvAoGBALKTzLcqAr7n+TqcRAJU +nm44K4zj52Nj0lpritovFbP1EiVoj07AFqFULE3aHMeGKwe+aNxz5JTSHPaP6aa9 +iD4CVoJzQ41OimqHnnRSgy3g+ylk7plLk/M0rE+6Ev945Xv8vGOGci6KgkBpOx37 +/yglpQbyju+BbRvq9gDfLuKX +-----END PRIVATE KEY-----`)) + + privateKey, err := ReadPrivateKey(reader) if err != nil { - t.Fatal(err) + panic(err) } return privateKey } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestPrivateKey$ -func TestPrivateKey(t *testing.T) { - privateKey, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - t.Fatal(err) - } - - privateKeyBytes, err := X509.PKCS1PrivateKeyEncoder(privateKey) - if err != nil { - t.Fatal(err) - } - - key := newPrivateKey(privateKey, privateKeyBytes) - if key.key != privateKey { - t.Fatalf("key.key %+v != privateKey %+v", key.key, privateKey) - } - - if !bytes.Equal(key.keyBytes, privateKeyBytes) { - t.Fatalf("key.keyBytes %+v != privateKeyBytes %+v", key.keyBytes, privateKeyBytes) - } - - if key.Key() != privateKey { - t.Fatalf("key.Key() %+v != privateKey %+v", key.Key(), privateKey) - } - - if !bytes.Equal(key.Bytes(), privateKeyBytes) { - t.Fatalf("key.Bytes() %+v != privateKeyBytes %+v", key.Bytes(), privateKeyBytes) - } - - expectPrivateKey := PrivateKey{ - key: privateKey, - keyBytes: privateKeyBytes, - } - - if !key.EqualsTo(expectPrivateKey) { - t.Fatalf("key %+v != expectPrivateKey %+v", key, expectPrivateKey) - } - - if key.String() != string(privateKeyBytes) { - t.Fatalf("key.String() %s != privateKeyBytes %s", key.String(), privateKeyBytes) - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestPrivateKeyDecryptPKCS1v15$ -func TestPrivateKeyDecryptPKCS1v15(t *testing.T) { - publicKey := newTestPublicKey(t) - privateKey := newTestPrivateKey(t) - - cases := []string{ - "", "123", "你好,世界", - } - - for _, msg := range cases { - encrypted, err := publicKey.EncryptPKCS1v15(cryptox.Bytes(msg)) +// go test -v -cover -run=^TestSignVerifyPKCS1v15$ +func TestSignVerifyPKCS1v15(t *testing.T) { + privateKey := newTestPrivateKey() + publicKey := newTestPublicKey() + + type testCase struct { + Data []byte + SignData []byte + SignDataHex []byte + SignDataBase64 []byte + } + + testCases := []testCase{ + { + Data: []byte(""), + SignData: []byte(""), + SignDataHex: []byte(""), + SignDataBase64: []byte(""), + }, + { + Data: []byte("123"), + SignData: []byte(""), + SignDataHex: []byte(""), + SignDataBase64: []byte(""), + }, + { + Data: []byte("你好,世界"), + SignData: []byte(""), + SignDataHex: []byte(""), + SignDataBase64: []byte(""), + }, + } + + for _, testCase := range testCases { + sum := sha256.Sum256(testCase.Data) + hashed := sum[:] + + // None + sign, err := privateKey.SignPKCS1v15(hashed) if err != nil { t.Fatal(err) } - decrypted, err := privateKey.DecryptPKCS1v15(encrypted) + err = publicKey.VerifyPKCS1v15(hashed, sign) if err != nil { t.Fatal(err) } - if string(decrypted) != msg { - t.Fatalf("decrypted %s != msg %s", decrypted, msg) - } - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestPrivateKeyDecryptPKCS1v15SessionKey$ -func TestPrivateKeyDecryptPKCS1v15SessionKey(t *testing.T) { - publicKey := newTestPublicKey(t) - privateKey := newTestPrivateKey(t) - - cases := []string{ - "", "123", "你好,世界", - } - - for _, msg := range cases { - encrypted, err := publicKey.EncryptPKCS1v15(cryptox.Bytes(msg)) + // Hex + sign, err = privateKey.SignPKCS1v15(hashed, WithHex()) if err != nil { t.Fatal(err) } - sessionKey := cryptox.GenerateBytes(32) - if err = privateKey.DecryptPKCS1v15SessionKey(encrypted, sessionKey); err != nil { + err = publicKey.VerifyPKCS1v15(hashed, sign, WithHex()) + if err != nil { t.Fatal(err) } - decrypted, err := privateKey.DecryptPKCS1v15(encrypted) + // Base64 + sign, err = privateKey.SignPKCS1v15(hashed, WithBase64()) if err != nil { t.Fatal(err) } - if string(decrypted) != msg { - t.Fatalf("decrypted %s != msg %s", decrypted, msg) + err = publicKey.VerifyPKCS1v15(hashed, sign, WithBase64()) + if err != nil { + t.Fatal(err) } } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestPrivateKeyDecryptOAEP$ -func TestPrivateKeyDecryptOAEP(t *testing.T) { - publicKey := newTestPublicKey(t) - privateKey := newTestPrivateKey(t) - - cases := []string{ - "", "123", "你好,世界", - } - - for _, msg := range cases { - encrypted, err := publicKey.EncryptOAEP(cryptox.Bytes(msg), cryptox.Bytes(msg)) +// go test -v -cover -run=^TestSignVerifyPSS$ +func TestSignVerifyPSS(t *testing.T) { + privateKey := newTestPrivateKey() + publicKey := newTestPublicKey() + + type testCase struct { + Data []byte + SignData []byte + SignDataHex []byte + SignDataBase64 []byte + } + + testCases := []testCase{ + { + Data: []byte(""), + SignData: []byte(""), + SignDataHex: []byte(""), + SignDataBase64: []byte(""), + }, + { + Data: []byte("123"), + SignData: []byte(""), + SignDataHex: []byte(""), + SignDataBase64: []byte(""), + }, + { + Data: []byte("你好,世界"), + SignData: []byte(""), + SignDataHex: []byte(""), + SignDataBase64: []byte(""), + }, + } + + for _, testCase := range testCases { + sum := sha256.Sum256(testCase.Data) + digest := sum[:] + + // None + sign, err := privateKey.SignPSS(digest) if err != nil { t.Fatal(err) } - decrypted, err := privateKey.DecryptOAEP(encrypted, cryptox.Bytes(msg)) + err = publicKey.VerifyPSS(digest, sign) if err != nil { t.Fatal(err) } - if string(decrypted) != msg { - t.Fatalf("decrypted %s != msg %s", decrypted, msg) - } - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestPrivateKeySignPKCS1v15$ -func TestPrivateKeySignPKCS1v15(t *testing.T) { - publicKey := newTestPublicKey(t) - privateKey := newTestPrivateKey(t) - - cases := []string{ - "d41d8cd98f00b204e9800998ecf8427e", "202cb962ac59075b964b07152d234b70", "dbefd3ada018615b35588a01e216ae6e", - } - - for _, msg := range cases { - signature, err := privateKey.SignPKCS1v15(cryptox.Bytes(msg)) + // Hex + sign, err = privateKey.SignPSS(digest, WithHex()) if err != nil { t.Fatal(err) } - err = publicKey.VerifyPKCS1v15(cryptox.Bytes(msg), signature) + err = publicKey.VerifyPSS(digest, sign, WithHex()) if err != nil { t.Fatal(err) } - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestPrivateKeySignPSS$ -func TestPrivateKeySignPSS(t *testing.T) { - publicKey := newTestPublicKey(t) - privateKey := newTestPrivateKey(t) - - cases := []string{ - "d41d8cd98f00b204e9800998ecf8427e", "202cb962ac59075b964b07152d234b70", "dbefd3ada018615b35588a01e216ae6e", - } - for _, msg := range cases { - signature, err := privateKey.SignPSS(cryptox.Bytes(msg), 0) + // Base64 + sign, err = privateKey.SignPSS(digest, WithBase64()) if err != nil { t.Fatal(err) } - err = publicKey.VerifyPSS(cryptox.Bytes(msg), signature, 0) + err = publicKey.VerifyPSS(digest, sign, WithBase64()) if err != nil { t.Fatal(err) } diff --git a/rsa/public.go b/rsa/public.go index a8c2a35..31df882 100644 --- a/rsa/public.go +++ b/rsa/public.go @@ -6,65 +6,64 @@ package rsa import ( "crypto/rsa" - - "github.com/FishGoddess/cryptox" + "fmt" ) type PublicKey struct { - key *rsa.PublicKey - keyBytes cryptox.Bytes + key *rsa.PublicKey } -func newPublicKey(key *rsa.PublicKey, keyBytes cryptox.Bytes) PublicKey { - return PublicKey{key: key, keyBytes: keyBytes} -} +// EncryptPKCS1v15 encrypts data with pkcs1 v15. +func (pk PublicKey) EncryptPKCS1v15(data []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) -// Key returns the key of pk. -func (pk PublicKey) Key() *rsa.PublicKey { - return pk.key -} + data, err := rsa.EncryptPKCS1v15(conf.random, pk.key, data) + if err != nil { + return nil, err + } -// Bytes returns the bytes of pk. -func (pk PublicKey) Bytes() cryptox.Bytes { - return pk.keyBytes + data = conf.encoding.Encode(data) + return data, nil } -// String returns the string of pk. -func (pk PublicKey) String() string { - return string(pk.keyBytes) -} +// EncryptOAEP encrypts data with oaep. +func (pk PublicKey) EncryptOAEP(data []byte, label []byte, opts ...Option) ([]byte, error) { + conf := newConfig().Apply(opts...) -// EqualsTo returns if pk equals to privateKey. -func (pk PublicKey) EqualsTo(publicKey PublicKey) bool { - return pk.key.Equal(publicKey.key) -} + data, err := rsa.EncryptOAEP(conf.hash, conf.random, pk.key, data, label) + if err != nil { + return nil, err + } -// EncryptPKCS1v15 encrypts msg with pkcs1 v15. -func (pk PublicKey) EncryptPKCS1v15(msg cryptox.Bytes, opts ...Option) (cryptox.Bytes, error) { - conf := newConfig(opts) - return rsa.EncryptPKCS1v15(conf.random, pk.key, msg) + data = conf.encoding.Encode(data) + return data, nil } -// EncryptOAEP encrypts msg with oaep. -func (pk PublicKey) EncryptOAEP(msg cryptox.Bytes, label cryptox.Bytes, opts ...Option) (cryptox.Bytes, error) { - conf := newConfig(opts) - return rsa.EncryptOAEP(conf.hash, conf.random, pk.key, msg, label) -} +// VerifyPKCS1v15 verifies hashed with pkcs1 v15. +func (pk PublicKey) VerifyPKCS1v15(hashed []byte, sign []byte, opts ...Option) error { + conf := newConfig().Apply(opts...) + + sign, err := conf.encoding.Decode(sign) + if err != nil { + return err + } -// VerifyPKCS1v15 verifies signature with pkcs1 v15. -func (pk PublicKey) VerifyPKCS1v15(hashed cryptox.Bytes, signature cryptox.Bytes, opts ...Option) error { - conf := newConfig(opts) - return rsa.VerifyPKCS1v15(pk.key, conf.cryptoHash, hashed, signature) + return rsa.VerifyPKCS1v15(pk.key, conf.cryptoHash, hashed, sign) } -// VerifyPSS verifies signature with pss. -func (pk PublicKey) VerifyPSS(digest cryptox.Bytes, signature cryptox.Bytes, saltLength int, opts ...Option) error { - conf := newConfig(opts) +// VerifyPSS verifies digest with pss. +func (pk PublicKey) VerifyPSS(digest []byte, sign []byte, opts ...Option) error { + conf := newConfig().Apply(opts...) + + if !conf.cryptoHash.Available() { + return fmt.Errorf("cryptox/rsa: crypto hash %+v isn't available", conf.cryptoHash) + } - pssOpts := &rsa.PSSOptions{ - Hash: conf.cryptoHash, - SaltLength: saltLength, + sign, err := conf.encoding.Decode(sign) + if err != nil { + return err } - return rsa.VerifyPSS(pk.key, conf.cryptoHash, digest, signature, pssOpts) + pssOpts := &rsa.PSSOptions{Hash: conf.cryptoHash, SaltLength: conf.saltLength} + return rsa.VerifyPSS(pk.key, conf.cryptoHash, digest, sign, pssOpts) } diff --git a/rsa/public_test.go b/rsa/public_test.go index 85b943b..1de7dbd 100644 --- a/rsa/public_test.go +++ b/rsa/public_test.go @@ -6,169 +6,242 @@ package rsa import ( "bytes" - "crypto/rand" - "crypto/rsa" + "slices" "testing" - - "github.com/FishGoddess/cryptox" ) -func newTestPublicKey(t *testing.T) PublicKey { - keyBytes := []byte(`-----BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu0KvOo1/9owLI+GZuzlu -PmixfDEeNBA+t2qppsVT9xb4huZbwXwowNP6KU4vPpdF0KhHSmaFOf8IIXSoZ/xI -7bLxs10Te1fSqZInVuj912VLj/uwuK7OG1zfsN0mt8I2d+9zYzAGykh/U/skYALO -zvmfvamcQGHT1TuxOsQln3Eq0477VGmk53vTMOxEU033CUEabuNOiWlM8TsaDEqx -YWO3Two+rSNW4S48WTQhekhqtxxg0LhJfB/T9tCOmzuTln4oVk4peZW+CH0UJijt -d/2Ypx/Hyk0yXQgGtIKUN35avn2/ga56HOxGYumk22Q4Xv4OZOmevzPLyvRZDZMW -uwIDAQAB ------END PUBLIC KEY-----`) - - publicKey, err := ParsePublicKey(keyBytes) - if err != nil { - t.Fatal(err) - } - - return publicKey +type testCase struct { + Data []byte + EncryptData []byte + EncryptDataHex []byte + EncryptDataBase64 []byte } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestPublicKey$ -func TestPublicKey(t *testing.T) { - privateKey, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - t.Fatal(err) +type testRandomReader struct{} + +func (testRandomReader) Read(p []byte) (n int, err error) { + for i := range p { + p[i] = 1 } - publicKey := &privateKey.PublicKey + return len(p), nil +} - publicKeyBytes, err := X509.PKIXPublicKeyEncoder(publicKey) +func newTestPublicKey() PublicKey { + reader := bytes.NewReader([]byte(`-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp6Ap2KrXXMaNmc3K6ZHx +2IPrP4Bq5be1sP/4ASX3AmeuJbOHw+CNAQ/l3tFh7k+aaKMOgsspdtMq72Nd6KpU +a9OW7Jz6btmTohD34gEG/PfMYGGMKA8CxJO+VLtaMed7p4YWW0SjZ3tVdUDGBacQ +IHgDMTC9PWZhKj6d6tATWXI2amqv9rph1TgTq6q0SWRuwD2aYsQL8G0SicxC6uNL +NMw+hAqL2ZP91lRfARHK5sm5p257NWPPabVxWSEFSj6h11CvflWyIimbFPlCqWNv +ViozpBC3EqxuxzGWyF4r87MBp+XNA7JF0P0281eVjcaVtOzqdLN8vRu5/pE3MrWR +EwIDAQAB +-----END PUBLIC KEY-----`)) + + publicKey, err := ReadPublicKey(reader) if err != nil { - t.Fatal(err) + panic(err) } - key := newPublicKey(publicKey, publicKeyBytes) - if key.key != publicKey { - t.Fatalf("key.key %+v != publicKey %+v", key.key, publicKey) - } + return publicKey +} - if !bytes.Equal(key.keyBytes, publicKeyBytes) { - t.Fatalf("key.keyBytes %+v != publicKeyBytes %+v", key.keyBytes, publicKeyBytes) - } +// go test -v -cover -run=^TestEncryptDecryptPKCS1v15$ +func TestEncryptDecryptPKCS1v15(t *testing.T) { + privateKey := newTestPrivateKey() + publicKey := newTestPublicKey() + + testCases := []testCase{ + { + Data: []byte(""), + EncryptData: []byte{116, 242, 43, 134, 99, 44, 254, 137, 141, 79, 25, 122, 116, 221, 33, 96, 98, 244, 87, 125, 98, 109, 108, 84, 77, 195, 236, 156, 53, 182, 149, 206, 127, 200, 196, 60, 107, 48, 158, 215, 245, 177, 47, 197, 102, 116, 39, 76, 206, 55, 19, 32, 199, 254, 96, 0, 237, 156, 213, 200, 198, 78, 28, 110, 103, 8, 177, 211, 58, 215, 159, 252, 26, 122, 230, 78, 197, 219, 93, 8, 164, 72, 191, 25, 127, 54, 108, 22, 245, 188, 66, 155, 239, 1, 145, 209, 147, 37, 170, 38, 16, 210, 247, 191, 83, 22, 26, 148, 46, 124, 56, 12, 24, 36, 128, 80, 36, 58, 99, 104, 222, 236, 163, 212, 151, 30, 83, 117, 177, 175, 227, 251, 145, 219, 14, 7, 53, 224, 21, 193, 56, 180, 8, 199, 189, 211, 19, 42, 75, 216, 176, 164, 88, 101, 87, 219, 92, 222, 164, 13, 169, 153, 236, 23, 104, 132, 203, 245, 150, 247, 177, 241, 124, 220, 79, 159, 152, 213, 222, 144, 70, 100, 44, 36, 86, 36, 198, 224, 175, 252, 252, 113, 37, 253, 140, 184, 180, 155, 236, 236, 155, 138, 65, 233, 51, 93, 50, 122, 194, 253, 251, 3, 172, 161, 118, 130, 106, 71, 150, 68, 174, 136, 242, 164, 0, 83, 232, 128, 165, 138, 203, 54, 157, 239, 36, 254, 174, 105, 100, 11, 193, 22, 184, 233, 198, 247, 167, 187, 6, 188, 124, 186, 234, 54, 79, 242}, + EncryptDataHex: []byte("74f22b86632cfe898d4f197a74dd216062f4577d626d6c544dc3ec9c35b695ce7fc8c43c6b309ed7f5b12fc56674274cce371320c7fe6000ed9cd5c8c64e1c6e6708b1d33ad79ffc1a7ae64ec5db5d08a448bf197f366c16f5bc429bef0191d19325aa2610d2f7bf53161a942e7c380c18248050243a6368deeca3d4971e5375b1afe3fb91db0e0735e015c138b408c7bdd3132a4bd8b0a4586557db5cdea40da999ec176884cbf596f7b1f17cdc4f9f98d5de9046642c245624c6e0affcfc7125fd8cb8b49becec9b8a41e9335d327ac2fdfb03aca176826a479644ae88f2a40053e880a58acb369def24feae69640bc116b8e9c6f7a7bb06bc7cbaea364ff2"), + EncryptDataBase64: []byte("dPIrhmMs/omNTxl6dN0hYGL0V31ibWxUTcPsnDW2lc5/yMQ8azCe1/WxL8VmdCdMzjcTIMf+YADtnNXIxk4cbmcIsdM615/8GnrmTsXbXQikSL8ZfzZsFvW8QpvvAZHRkyWqJhDS979TFhqULnw4DBgkgFAkOmNo3uyj1JceU3Wxr+P7kdsOBzXgFcE4tAjHvdMTKkvYsKRYZVfbXN6kDamZ7BdohMv1lvex8XzcT5+Y1d6QRmQsJFYkxuCv/PxxJf2MuLSb7OybikHpM10yesL9+wOsoXaCakeWRK6I8qQAU+iApYrLNp3vJP6uaWQLwRa46cb3p7sGvHy66jZP8g=="), + }, + { + Data: []byte("123"), + EncryptData: []byte{65, 112, 83, 139, 223, 173, 140, 44, 39, 240, 29, 168, 95, 0, 100, 219, 11, 167, 173, 23, 10, 160, 41, 193, 242, 188, 167, 55, 123, 173, 114, 85, 5, 99, 5, 52, 27, 85, 52, 123, 175, 181, 56, 57, 30, 96, 241, 153, 77, 178, 246, 166, 192, 90, 174, 66, 1, 206, 64, 247, 158, 239, 146, 184, 206, 43, 109, 140, 51, 65, 228, 237, 172, 105, 83, 106, 18, 199, 216, 22, 114, 76, 198, 88, 103, 7, 184, 133, 33, 108, 177, 185, 17, 164, 168, 17, 167, 145, 0, 243, 100, 196, 194, 164, 254, 235, 72, 254, 37, 255, 68, 0, 243, 222, 88, 115, 81, 22, 41, 155, 211, 61, 226, 10, 4, 141, 106, 101, 79, 24, 90, 36, 225, 141, 178, 1, 8, 233, 179, 68, 43, 96, 111, 200, 152, 255, 5, 11, 189, 74, 200, 244, 181, 26, 43, 26, 137, 220, 141, 76, 167, 0, 46, 61, 73, 85, 67, 16, 214, 192, 109, 237, 186, 193, 34, 16, 88, 154, 161, 113, 222, 188, 146, 250, 114, 82, 215, 35, 42, 162, 2, 204, 86, 112, 143, 156, 64, 255, 31, 126, 111, 131, 112, 208, 28, 68, 118, 194, 190, 22, 127, 77, 101, 28, 188, 254, 198, 157, 202, 5, 126, 209, 47, 211, 27, 31, 72, 56, 137, 97, 96, 80, 115, 7, 0, 190, 104, 91, 81, 103, 220, 139, 209, 87, 156, 70, 67, 17, 245, 149, 58, 253, 39, 85, 126, 19}, + EncryptDataHex: []byte("4170538bdfad8c2c27f01da85f0064db0ba7ad170aa029c1f2bca7377bad7255056305341b55347bafb538391e60f1994db2f6a6c05aae4201ce40f79eef92b8ce2b6d8c3341e4edac69536a12c7d816724cc6586707b885216cb1b911a4a811a79100f364c4c2a4feeb48fe25ff4400f3de58735116299bd33de20a048d6a654f185a24e18db20108e9b3442b606fc898ff050bbd4ac8f4b51a2b1a89dc8d4ca7002e3d49554310d6c06dedbac12210589aa171debc92fa7252d7232aa202cc56708f9c40ff1f7e6f8370d01c4476c2be167f4d651cbcfec69dca057ed12fd31b1f483889616050730700be685b5167dc8bd1579c464311f5953afd27557e13"), + EncryptDataBase64: []byte("QXBTi9+tjCwn8B2oXwBk2wunrRcKoCnB8rynN3utclUFYwU0G1U0e6+1ODkeYPGZTbL2psBarkIBzkD3nu+SuM4rbYwzQeTtrGlTahLH2BZyTMZYZwe4hSFssbkRpKgRp5EA82TEwqT+60j+Jf9EAPPeWHNRFimb0z3iCgSNamVPGFok4Y2yAQjps0QrYG/ImP8FC71KyPS1GisaidyNTKcALj1JVUMQ1sBt7brBIhBYmqFx3ryS+nJS1yMqogLMVnCPnED/H35vg3DQHER2wr4Wf01lHLz+xp3KBX7RL9MbH0g4iWFgUHMHAL5oW1Fn3IvRV5xGQxH1lTr9J1V+Ew=="), + }, + { + Data: []byte("你好,世界"), + EncryptData: []byte{77, 240, 180, 40, 245, 65, 99, 253, 77, 0, 80, 90, 201, 188, 155, 206, 95, 249, 121, 236, 131, 155, 144, 213, 59, 108, 56, 230, 202, 241, 138, 154, 80, 173, 120, 123, 149, 255, 65, 90, 31, 93, 201, 82, 249, 121, 22, 50, 73, 21, 179, 131, 169, 7, 139, 101, 26, 59, 161, 194, 48, 233, 57, 234, 210, 81, 193, 253, 18, 15, 228, 4, 89, 28, 11, 51, 157, 39, 231, 124, 100, 56, 114, 122, 65, 140, 8, 60, 155, 112, 31, 64, 79, 15, 105, 99, 107, 125, 152, 42, 5, 44, 48, 104, 73, 238, 229, 236, 168, 86, 151, 138, 236, 38, 133, 56, 150, 181, 202, 247, 114, 233, 95, 194, 137, 82, 170, 0, 208, 78, 116, 241, 177, 60, 196, 212, 163, 35, 215, 227, 189, 146, 160, 171, 246, 80, 168, 231, 235, 101, 206, 101, 137, 233, 96, 82, 148, 212, 104, 134, 131, 61, 28, 25, 240, 120, 59, 30, 44, 179, 126, 149, 5, 5, 152, 61, 79, 52, 203, 157, 90, 233, 236, 135, 82, 225, 51, 252, 224, 183, 122, 175, 53, 200, 232, 58, 19, 186, 171, 223, 11, 186, 12, 128, 57, 93, 232, 56, 63, 78, 238, 108, 199, 248, 82, 161, 73, 33, 124, 158, 175, 176, 5, 99, 250, 157, 143, 11, 12, 38, 57, 226, 234, 12, 212, 29, 107, 251, 179, 39, 110, 251, 109, 185, 242, 23, 232, 133, 173, 84, 129, 63, 61, 253, 56, 175}, + EncryptDataHex: []byte("4df0b428f54163fd4d00505ac9bc9bce5ff979ec839b90d53b6c38e6caf18a9a50ad787b95ff415a1f5dc952f97916324915b383a9078b651a3ba1c230e939ead251c1fd120fe404591c0b339d27e77c6438727a418c083c9b701f404f0f69636b7d982a052c306849eee5eca856978aec26853896b5caf772e95fc28952aa00d04e74f1b13cc4d4a323d7e3bd92a0abf650a8e7eb65ce6589e9605294d46886833d1c19f0783b1e2cb37e950505983d4f34cb9d5ae9ec8752e133fce0b77aaf35c8e83a13baabdf0bba0c80395de8383f4eee6cc7f852a149217c9eafb00563fa9d8f0b0c2639e2ea0cd41d6bfbb3276efb6db9f217e885ad54813f3dfd38af"), + EncryptDataBase64: []byte("TfC0KPVBY/1NAFBaybybzl/5eeyDm5DVO2w45srxippQrXh7lf9BWh9dyVL5eRYySRWzg6kHi2UaO6HCMOk56tJRwf0SD+QEWRwLM50n53xkOHJ6QYwIPJtwH0BPD2lja32YKgUsMGhJ7uXsqFaXiuwmhTiWtcr3culfwolSqgDQTnTxsTzE1KMj1+O9kqCr9lCo5+tlzmWJ6WBSlNRohoM9HBnweDseLLN+lQUFmD1PNMudWunsh1LhM/zgt3qvNcjoOhO6q98LugyAOV3oOD9O7mzH+FKhSSF8nq+wBWP6nY8LDCY54uoM1B1r+7MnbvttufIX6IWtVIE/Pf04rw=="), + }, + } + + random := testRandomReader{} + for _, testCase := range testCases { + // None + encrypted, err := publicKey.EncryptPKCS1v15(testCase.Data, WithRandom(random)) + if err != nil { + t.Fatal(err) + } - if key.Key() != publicKey { - t.Fatalf("key.Key() %+v != publicKey %+v", key.Key(), publicKey) - } + if !slices.Equal(encrypted, testCase.EncryptData) { + t.Fatalf("data %q: got %+v != expect %+v", testCase.Data, encrypted, testCase.EncryptData) + } - if !bytes.Equal(key.Bytes(), publicKeyBytes) { - t.Fatalf("key.Bytes() %+v != publicKeyBytes %+v", key.Bytes(), publicKeyBytes) - } + decrypted, err := privateKey.DecryptPKCS1v15(encrypted, WithRandom(random)) + if err != nil { + t.Fatal(err) + } - expectPublicKey := PublicKey{ - key: publicKey, - keyBytes: publicKeyBytes, - } + if !slices.Equal(decrypted, testCase.Data) { + t.Fatalf("encrypted %q: got %+v != expect %+v", encrypted, decrypted, testCase.Data) + } - if !key.EqualsTo(expectPublicKey) { - t.Fatalf("key %+v != expectPublicKey %+v", key, expectPublicKey) - } + // Hex + encrypted, err = publicKey.EncryptPKCS1v15(testCase.Data, WithHex(), WithRandom(random)) + if err != nil { + t.Fatal(err) + } - if key.String() != string(publicKeyBytes) { - t.Fatalf("key.String() %s != publicKeyBytes %s", key.String(), publicKeyBytes) - } -} + if !slices.Equal(encrypted, testCase.EncryptDataHex) { + t.Fatalf("data %q: got %+v != expect %+v", testCase.Data, encrypted, testCase.EncryptDataHex) + } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestPublicKeyEncryptPKCS1v15$ -func TestPublicKeyEncryptPKCS1v15(t *testing.T) { - publicKey := newTestPublicKey(t) - privateKey := newTestPrivateKey(t) + decrypted, err = privateKey.DecryptPKCS1v15(encrypted, WithHex(), WithRandom(random)) + if err != nil { + t.Fatal(err) + } - cases := []string{ - "", "123", "你好,世界", - } + if !slices.Equal(decrypted, testCase.Data) { + t.Fatalf("encrypted %q: got %+v != expect %+v", encrypted, decrypted, testCase.Data) + } - for _, msg := range cases { - encrypted, err := publicKey.EncryptPKCS1v15(cryptox.Bytes(msg)) + // Base64 + encrypted, err = publicKey.EncryptPKCS1v15(testCase.Data, WithBase64(), WithRandom(random)) if err != nil { t.Fatal(err) } - decrypted, err := privateKey.DecryptPKCS1v15(encrypted) + if !slices.Equal(encrypted, testCase.EncryptDataBase64) { + t.Fatalf("data %q: got %+v != expect %+v", testCase.Data, encrypted, testCase.EncryptDataBase64) + } + + decrypted, err = privateKey.DecryptPKCS1v15(encrypted, WithBase64(), WithRandom(random)) if err != nil { t.Fatal(err) } - if string(decrypted) != msg { - t.Fatalf("decrypted %s != msg %s", decrypted, msg) + if !slices.Equal(decrypted, testCase.Data) { + t.Fatalf("encrypted %q: got %+v != expect %+v", encrypted, decrypted, testCase.Data) } } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestPublicKeyEncryptOAEP$ -func TestPublicKeyEncryptOAEP(t *testing.T) { - publicKey := newTestPublicKey(t) - privateKey := newTestPrivateKey(t) +// go test -v -cover -run=^TestEncryptDecryptOAEP$ +func TestEncryptDecryptOAEP(t *testing.T) { + privateKey := newTestPrivateKey() + publicKey := newTestPublicKey() + + testCases := []testCase{ + { + Data: []byte(""), + EncryptData: []byte{93, 21, 253, 18, 191, 140, 145, 9, 36, 234, 228, 96, 42, 8, 3, 111, 186, 171, 32, 0, 227, 220, 31, 173, 88, 184, 250, 55, 71, 16, 193, 185, 37, 16, 140, 70, 2, 82, 92, 146, 182, 1, 168, 184, 83, 227, 21, 123, 238, 157, 73, 142, 23, 30, 240, 129, 242, 6, 216, 247, 93, 7, 7, 112, 214, 166, 171, 193, 246, 163, 43, 146, 87, 246, 235, 254, 40, 130, 72, 190, 154, 52, 162, 23, 204, 143, 217, 139, 62, 123, 142, 140, 124, 152, 38, 213, 189, 94, 205, 165, 221, 174, 213, 160, 96, 218, 71, 121, 187, 105, 2, 99, 226, 97, 109, 122, 141, 73, 255, 252, 123, 212, 83, 154, 190, 213, 107, 29, 163, 67, 122, 169, 38, 220, 29, 149, 181, 106, 92, 126, 227, 193, 129, 225, 203, 153, 157, 165, 252, 68, 132, 77, 131, 109, 77, 100, 178, 182, 155, 5, 250, 89, 203, 215, 59, 82, 63, 46, 21, 49, 87, 190, 154, 115, 49, 98, 26, 222, 113, 186, 18, 205, 132, 74, 125, 141, 215, 179, 2, 211, 68, 72, 119, 40, 107, 232, 75, 120, 60, 146, 176, 39, 255, 124, 199, 195, 77, 68, 93, 227, 174, 17, 165, 139, 91, 84, 21, 252, 124, 199, 74, 171, 223, 17, 144, 193, 101, 151, 195, 147, 163, 96, 174, 37, 6, 97, 48, 216, 116, 159, 250, 165, 140, 235, 27, 45, 136, 152, 96, 163, 4, 207, 57, 245, 102, 204}, + EncryptDataHex: []byte("5d15fd12bf8c910924eae4602a08036fbaab2000e3dc1fad58b8fa374710c1b925108c4602525c92b601a8b853e3157bee9d498e171ef081f206d8f75d070770d6a6abc1f6a32b9257f6ebfe288248be9a34a217cc8fd98b3e7b8e8c7c9826d5bd5ecda5ddaed5a060da4779bb690263e2616d7a8d49fffc7bd4539abed56b1da3437aa926dc1d95b56a5c7ee3c181e1cb999da5fc44844d836d4d64b2b69b05fa59cbd73b523f2e153157be9a7331621ade71ba12cd844a7d8dd7b302d3444877286be84b783c92b027ff7cc7c34d445de3ae11a58b5b5415fc7cc74aabdf1190c16597c393a360ae25066130d8749ffaa58ceb1b2d889860a304cf39f566cc"), + EncryptDataBase64: []byte("XRX9Er+MkQkk6uRgKggDb7qrIADj3B+tWLj6N0cQwbklEIxGAlJckrYBqLhT4xV77p1Jjhce8IHyBtj3XQcHcNamq8H2oyuSV/br/iiCSL6aNKIXzI/Ziz57jox8mCbVvV7Npd2u1aBg2kd5u2kCY+JhbXqNSf/8e9RTmr7Vax2jQ3qpJtwdlbVqXH7jwYHhy5mdpfxEhE2DbU1ksrabBfpZy9c7Uj8uFTFXvppzMWIa3nG6Es2ESn2N17MC00RIdyhr6Et4PJKwJ/98x8NNRF3jrhGli1tUFfx8x0qr3xGQwWWXw5OjYK4lBmEw2HSf+qWM6xstiJhgowTPOfVmzA=="), + }, + { + Data: []byte("123"), + EncryptData: []byte{131, 48, 98, 101, 212, 117, 12, 31, 19, 157, 186, 200, 121, 122, 207, 186, 233, 45, 145, 134, 199, 150, 107, 88, 154, 185, 54, 141, 206, 210, 1, 101, 4, 120, 249, 147, 36, 31, 39, 27, 168, 136, 164, 32, 220, 185, 6, 221, 135, 208, 145, 214, 99, 126, 147, 7, 193, 159, 231, 170, 232, 12, 2, 67, 223, 103, 96, 190, 32, 40, 198, 247, 151, 247, 112, 13, 72, 223, 20, 3, 20, 6, 152, 0, 252, 22, 228, 254, 182, 180, 149, 83, 168, 194, 203, 6, 192, 135, 145, 166, 171, 44, 121, 19, 133, 199, 41, 192, 209, 194, 241, 218, 100, 172, 121, 250, 18, 19, 152, 109, 51, 164, 175, 208, 51, 239, 140, 219, 138, 75, 3, 61, 73, 123, 182, 3, 104, 86, 106, 244, 30, 145, 228, 233, 202, 63, 142, 157, 88, 175, 230, 157, 93, 181, 199, 240, 111, 132, 147, 215, 31, 177, 146, 55, 130, 224, 218, 43, 87, 88, 233, 150, 109, 33, 72, 37, 236, 41, 220, 72, 237, 1, 230, 34, 33, 39, 254, 38, 245, 121, 54, 192, 38, 226, 245, 43, 226, 190, 194, 200, 156, 182, 143, 197, 22, 92, 223, 29, 143, 148, 79, 25, 22, 245, 235, 129, 22, 162, 194, 167, 188, 2, 249, 214, 210, 142, 61, 151, 224, 84, 147, 212, 67, 252, 224, 67, 44, 34, 10, 207, 55, 235, 237, 198, 199, 229, 122, 150, 178, 206, 93, 79, 63, 252, 14, 0}, + EncryptDataHex: []byte("83306265d4750c1f139dbac8797acfbae92d9186c7966b589ab9368dced201650478f993241f271ba888a420dcb906dd87d091d6637e9307c19fe7aae80c0243df6760be2028c6f797f7700d48df140314069800fc16e4feb6b49553a8c2cb06c08791a6ab2c791385c729c0d1c2f1da64ac79fa1213986d33a4afd033ef8cdb8a4b033d497bb60368566af41e91e4e9ca3f8e9d58afe69d5db5c7f06f8493d71fb1923782e0da2b5758e9966d214825ec29dc48ed01e6222127fe26f57936c026e2f52be2bec2c89cb68fc5165cdf1d8f944f1916f5eb8116a2c2a7bc02f9d6d28e3d97e05493d443fce0432c220acf37ebedc6c7e57a96b2ce5d4f3ffc0e00"), + EncryptDataBase64: []byte("gzBiZdR1DB8TnbrIeXrPuuktkYbHlmtYmrk2jc7SAWUEePmTJB8nG6iIpCDcuQbdh9CR1mN+kwfBn+eq6AwCQ99nYL4gKMb3l/dwDUjfFAMUBpgA/Bbk/ra0lVOowssGwIeRpqsseROFxynA0cLx2mSsefoSE5htM6Sv0DPvjNuKSwM9SXu2A2hWavQekeTpyj+OnViv5p1dtcfwb4ST1x+xkjeC4NorV1jplm0hSCXsKdxI7QHmIiEn/ib1eTbAJuL1K+K+wsicto/FFlzfHY+UTxkW9euBFqLCp7wC+dbSjj2X4FST1EP84EMsIgrPN+vtxsflepayzl1PP/wOAA=="), + }, + { + Data: []byte("你好,世界"), + EncryptData: []byte{74, 48, 16, 184, 244, 162, 46, 60, 78, 194, 73, 221, 250, 253, 69, 8, 197, 241, 219, 80, 128, 75, 110, 216, 91, 109, 10, 139, 159, 240, 43, 108, 69, 251, 114, 158, 212, 19, 186, 20, 18, 161, 160, 5, 214, 69, 101, 200, 198, 89, 127, 226, 132, 1, 118, 52, 29, 27, 16, 31, 230, 248, 64, 123, 18, 115, 17, 1, 175, 199, 88, 213, 127, 73, 75, 33, 106, 242, 23, 19, 136, 0, 246, 187, 119, 234, 156, 86, 32, 26, 23, 203, 109, 222, 129, 149, 162, 89, 13, 92, 172, 240, 218, 7, 65, 0, 146, 162, 95, 93, 119, 245, 100, 149, 204, 130, 199, 169, 245, 49, 80, 19, 143, 129, 4, 21, 29, 244, 172, 57, 81, 63, 84, 86, 141, 160, 154, 63, 149, 200, 207, 108, 215, 186, 18, 44, 136, 70, 125, 171, 165, 11, 78, 105, 57, 102, 169, 61, 14, 228, 226, 18, 153, 18, 93, 206, 117, 149, 186, 5, 98, 161, 251, 116, 134, 162, 93, 55, 225, 11, 185, 44, 159, 241, 198, 183, 228, 249, 51, 247, 85, 139, 211, 82, 96, 106, 250, 210, 106, 96, 30, 239, 63, 224, 211, 202, 44, 41, 189, 165, 90, 240, 124, 134, 32, 245, 99, 54, 233, 231, 177, 191, 50, 195, 102, 245, 235, 182, 67, 132, 108, 112, 157, 221, 84, 187, 254, 2, 223, 11, 120, 195, 181, 221, 61, 232, 166, 158, 122, 60, 186, 255, 71, 37, 132, 101}, + EncryptDataHex: []byte("4a3010b8f4a22e3c4ec249ddfafd4508c5f1db50804b6ed85b6d0a8b9ff02b6c45fb729ed413ba1412a1a005d64565c8c6597fe2840176341d1b101fe6f8407b12731101afc758d57f494b216af217138800f6bb77ea9c56201a17cb6dde8195a2590d5cacf0da07410092a25f5d77f56495cc82c7a9f53150138f8104151df4ac39513f54568da09a3f95c8cf6cd7ba122c88467daba50b4e693966a93d0ee4e21299125dce7595ba0562a1fb7486a25d37e10bb92c9ff1c6b7e4f933f7558bd352606afad26a601eef3fe0d3ca2c29bda55af07c8620f56336e9e7b1bf32c366f5ebb643846c709ddd54bbfe02df0b78c3b5dd3de8a69e7a3cbaff47258465"), + EncryptDataBase64: []byte("SjAQuPSiLjxOwknd+v1FCMXx21CAS27YW20Ki5/wK2xF+3Ke1BO6FBKhoAXWRWXIxll/4oQBdjQdGxAf5vhAexJzEQGvx1jVf0lLIWryFxOIAPa7d+qcViAaF8tt3oGVolkNXKzw2gdBAJKiX1139WSVzILHqfUxUBOPgQQVHfSsOVE/VFaNoJo/lcjPbNe6EiyIRn2rpQtOaTlmqT0O5OISmRJdznWVugVioft0hqJdN+ELuSyf8ca35Pkz91WL01JgavrSamAe7z/g08osKb2lWvB8hiD1Yzbp57G/MsNm9eu2Q4RscJ3dVLv+At8LeMO13T3opp56PLr/RyWEZQ=="), + }, + } + + label := []byte("label") + random := testRandomReader{} + for _, testCase := range testCases { + // None + encrypted, err := publicKey.EncryptOAEP(testCase.Data, label, WithRandom(random)) + if err != nil { + t.Fatal(err) + } - cases := []string{ - "", "123", "你好,世界", - } + if !slices.Equal(encrypted, testCase.EncryptData) { + t.Fatalf("data %q: got %+v != expect %+v", testCase.Data, encrypted, testCase.EncryptData) + } - for _, msg := range cases { - encrypted, err := publicKey.EncryptOAEP(cryptox.Bytes(msg), cryptox.Bytes(msg)) + decrypted, err := privateKey.DecryptOAEP(encrypted, label, WithRandom(random)) if err != nil { t.Fatal(err) } - decrypted, err := privateKey.DecryptOAEP(encrypted, cryptox.Bytes(msg)) + if !slices.Equal(decrypted, testCase.Data) { + t.Fatalf("encrypted %q: got %+v != expect %+v", encrypted, decrypted, testCase.Data) + } + + // Hex + encrypted, err = publicKey.EncryptOAEP(testCase.Data, label, WithHex(), WithRandom(random)) if err != nil { t.Fatal(err) } - if string(decrypted) != msg { - t.Fatalf("decrypted %s != msg %s", decrypted, msg) + if !slices.Equal(encrypted, testCase.EncryptDataHex) { + t.Fatalf("data %q: got %+v != expect %+v", testCase.Data, encrypted, testCase.EncryptDataHex) } - } -} -// go test -v -cover -count=1 -test.cpu=1 -run=^TestPublicKeyVerifyPKCS1v15$ -func TestPublicKeyVerifyPKCS1v15(t *testing.T) { - publicKey := newTestPublicKey(t) - privateKey := newTestPrivateKey(t) + decrypted, err = privateKey.DecryptOAEP(encrypted, label, WithHex(), WithRandom(random)) + if err != nil { + t.Fatal(err) + } - cases := []string{ - "d41d8cd98f00b204e9800998ecf8427e", "202cb962ac59075b964b07152d234b70", "dbefd3ada018615b35588a01e216ae6e", - } + if !slices.Equal(decrypted, testCase.Data) { + t.Fatalf("encrypted %q: got %+v != expect %+v", encrypted, decrypted, testCase.Data) + } - for _, msg := range cases { - signature, err := privateKey.SignPKCS1v15(cryptox.Bytes(msg)) + // Base64 + encrypted, err = publicKey.EncryptOAEP(testCase.Data, label, WithBase64(), WithRandom(random)) if err != nil { t.Fatal(err) } - err = publicKey.VerifyPKCS1v15(cryptox.Bytes(msg), signature) + if !slices.Equal(encrypted, testCase.EncryptDataBase64) { + t.Fatalf("data %q: got %+v != expect %+v", testCase.Data, encrypted, testCase.EncryptDataBase64) + } + + decrypted, err = privateKey.DecryptOAEP(encrypted, label, WithBase64(), WithRandom(random)) if err != nil { t.Fatal(err) } + + if !slices.Equal(decrypted, testCase.Data) { + t.Fatalf("encrypted %q: got %+v != expect %+v", encrypted, decrypted, testCase.Data) + } } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestPublicKeyVerifyPSS$ -func TestPublicKeyVerifyPSS(t *testing.T) { - publicKey := newTestPublicKey(t) - privateKey := newTestPrivateKey(t) +// go test -v -cover -run=^TestDecryptPKCS1v15SessionKey$ +func TestDecryptPKCS1v15SessionKey(t *testing.T) { + privateKey := newTestPrivateKey() + publicKey := newTestPublicKey() - cases := []string{ - "d41d8cd98f00b204e9800998ecf8427e", "202cb962ac59075b964b07152d234b70", "dbefd3ada018615b35588a01e216ae6e", + sessionKey := []byte("12345678876543211234567887654321") + random := testRandomReader{} + + encrypt, err := publicKey.EncryptPKCS1v15(sessionKey, WithRandom(random)) + if err != nil { + t.Fatal(err) } - for _, msg := range cases { - signature, err := privateKey.SignPSS(cryptox.Bytes(msg), 0) - if err != nil { - t.Fatal(err) - } + gotSessionKey := make([]byte, len(sessionKey)) - err = publicKey.VerifyPSS(cryptox.Bytes(msg), signature, 0) - if err != nil { - t.Fatal(err) - } + err = privateKey.DecryptPKCS1v15SessionKey(encrypt, gotSessionKey, WithRandom(random)) + if err != nil { + panic(err) + } + + if !slices.Equal(sessionKey, gotSessionKey) { + t.Fatalf("got %+v != expect %+v", sessionKey, gotSessionKey) } } diff --git a/rsa/x509.go b/rsa/x509.go deleted file mode 100644 index 1ee3a28..0000000 --- a/rsa/x509.go +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright 2024 FishGoddess. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package rsa - -import ( - "bytes" - "crypto/rsa" - "crypto/x509" - "encoding/pem" - "fmt" - "strings" - - "github.com/FishGoddess/cryptox" -) - -const ( - blockTypePrivate = "PRIVATE KEY" - blockTypePublic = "PUBLIC KEY" -) - -var ( - // X509 is a x509Pem instance with X509 methods. - X509 = x509Pem{} -) - -var ( - _ PrivateKeyEncoder = X509.PKCS1PrivateKeyEncoder - _ PrivateKeyDecoder = X509.PKCS1PrivateKeyDecoder - _ PrivateKeyEncoder = X509.PKCS8PrivateKeyEncoder - _ PrivateKeyDecoder = X509.PKCS8PrivateKeyDecoder - _ PublicKeyEncoder = X509.PKIXPublicKeyEncoder - _ PublicKeyDecoder = X509.PKIXPublicKeyDecoder - _ PublicKeyEncoder = X509.PKCS1PublicKeyEncoder - _ PublicKeyDecoder = X509.PKCS1PublicKeyDecoder -) - -// PrivateKeyEncoder encodes private key to pem bytes. -type PrivateKeyEncoder func(key *rsa.PrivateKey) (cryptox.Bytes, error) - -// Encode encodes private key to pem bytes. -func (pke PrivateKeyEncoder) Encode(key *rsa.PrivateKey) (cryptox.Bytes, error) { - return pke(key) -} - -// PrivateKeyDecoder decodes private key from pem bytes. -type PrivateKeyDecoder func(keyPem cryptox.Bytes) (*rsa.PrivateKey, error) - -// Decode decodes private key from pem bytes. -func (pke PrivateKeyDecoder) Decode(keyPem cryptox.Bytes) (*rsa.PrivateKey, error) { - return pke(keyPem) -} - -// PublicKeyEncoder encodes public key to pem bytes. -type PublicKeyEncoder func(key *rsa.PublicKey) (cryptox.Bytes, error) - -// Encode encodes public key to pem bytes. -func (pke PublicKeyEncoder) Encode(key *rsa.PublicKey) (cryptox.Bytes, error) { - return pke(key) -} - -// PublicKeyDecoder decodes public key from pem bytes. -type PublicKeyDecoder func(keyPem cryptox.Bytes) (*rsa.PublicKey, error) - -// Decode decodes public key from pem bytes. -func (pke PublicKeyDecoder) Decode(keyPem cryptox.Bytes) (*rsa.PublicKey, error) { - return pke(keyPem) -} - -// x509Pem wraps some methods about x509 with pem. -// We recommend you to use X509 variable directly. -type x509Pem struct{} - -// encode encodes block to pem bytes. -func (xp x509Pem) encode(blockType string, blockBytes cryptox.Bytes) (cryptox.Bytes, error) { - block := &pem.Block{ - Type: blockType, - Bytes: blockBytes, - } - - var keyPem bytes.Buffer - if err := pem.Encode(&keyPem, block); err != nil { - return nil, err - } - - return keyPem.Bytes(), nil -} - -// decode decodes block from pem bytes. -func (xp x509Pem) decode(blockType string, keyPem cryptox.Bytes) (*pem.Block, error) { - block, _ := pem.Decode(keyPem) - if block == nil { - return nil, fmt.Errorf("cryptox/rsa: decode %s from pem failed", strings.ToLower(blockType)) - } - - return block, nil -} - -// PKCS1PrivateKeyEncoder encodes private key to bytes using pkcs1. -func (xp x509Pem) PKCS1PrivateKeyEncoder(key *rsa.PrivateKey) (cryptox.Bytes, error) { - privateKeyBytes := x509.MarshalPKCS1PrivateKey(key) - return xp.encode(blockTypePrivate, privateKeyBytes) -} - -// PKCS1PrivateKeyDecoder decodes private key from data using pkcs1. -func (xp x509Pem) PKCS1PrivateKeyDecoder(privateKeyPem cryptox.Bytes) (*rsa.PrivateKey, error) { - block, err := xp.decode(blockTypePrivate, privateKeyPem) - if err != nil { - return nil, err - } - - return x509.ParsePKCS1PrivateKey(block.Bytes) -} - -// PKCS8PrivateKeyEncoder encodes private key to bytes using pkcs8. -func (xp x509Pem) PKCS8PrivateKeyEncoder(key *rsa.PrivateKey) (cryptox.Bytes, error) { - privateKeyBytes, err := x509.MarshalPKCS8PrivateKey(key) - if err != nil { - return nil, err - } - - return xp.encode(blockTypePrivate, privateKeyBytes) -} - -// PKCS8PrivateKeyDecoder decodes private key from data using pkcs1. -func (xp x509Pem) PKCS8PrivateKeyDecoder(privateKeyPem cryptox.Bytes) (*rsa.PrivateKey, error) { - block, err := xp.decode(blockTypePrivate, privateKeyPem) - if err != nil { - return nil, err - } - - key, err := x509.ParsePKCS8PrivateKey(block.Bytes) - if err != nil { - return nil, err - } - - privateKey, ok := key.(*rsa.PrivateKey) - if !ok { - return nil, fmt.Errorf("cryptox/rsa: parsed key %T isn't a *rsa.PrivateKey", key) - } - - return privateKey, nil -} - -// PKIXPublicKeyEncoder encodes public key to bytes using pkix. -func (xp x509Pem) PKIXPublicKeyEncoder(key *rsa.PublicKey) (cryptox.Bytes, error) { - publicKeyBytes, err := x509.MarshalPKIXPublicKey(key) - if err != nil { - return nil, err - } - - return xp.encode(blockTypePublic, publicKeyBytes) -} - -// PKIXPublicKeyDecoder encodes public key to bytes using pkix. -func (xp x509Pem) PKIXPublicKeyDecoder(publicKeyPem cryptox.Bytes) (*rsa.PublicKey, error) { - block, err := xp.decode(blockTypePublic, publicKeyPem) - if err != nil { - return nil, err - } - - key, err := x509.ParsePKIXPublicKey(block.Bytes) - if err != nil { - return nil, err - } - - publicKey, ok := key.(*rsa.PublicKey) - if !ok { - return nil, fmt.Errorf("cryptox/rsa: parsed key %T isn't a *rsa.PublicKey", key) - } - - return publicKey, nil -} - -// PKCS1PublicKeyEncoder encodes public key to bytes using pkcs1. -func (xp x509Pem) PKCS1PublicKeyEncoder(key *rsa.PublicKey) (cryptox.Bytes, error) { - publicKeyBytes := x509.MarshalPKCS1PublicKey(key) - return xp.encode(blockTypePublic, publicKeyBytes) -} - -// PKCS1PublicKeyDecoder encodes public key to bytes using pkcs1. -func (xp x509Pem) PKCS1PublicKeyDecoder(publicKeyPem cryptox.Bytes) (*rsa.PublicKey, error) { - block, err := xp.decode(blockTypePublic, publicKeyPem) - if err != nil { - return nil, err - } - - return x509.ParsePKCS1PublicKey(block.Bytes) -} diff --git a/rsa/x509_test.go b/rsa/x509_test.go deleted file mode 100644 index 0ea0e44..0000000 --- a/rsa/x509_test.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2024 FishGoddess. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package rsa - -import ( - "crypto/rand" - "crypto/rsa" - "testing" -) - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestPKCS1PrivateKey$ -func TestPKCS1PrivateKey(t *testing.T) { - privateKey, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - t.Fatal(err) - } - - privateKeyBytes, err := X509.PKCS1PrivateKeyEncoder(privateKey) - if err != nil { - t.Fatal(err) - } - - decodedPrivateKey, err := X509.PKCS1PrivateKeyDecoder(privateKeyBytes) - if err != nil { - t.Fatal(err) - } - - if !decodedPrivateKey.Equal(privateKey) { - t.Fatalf("decodedPrivateKey %+v != privateKey %+v", decodedPrivateKey, privateKey) - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestPKCS8PrivateKey$ -func TestPKCS8PrivateKey(t *testing.T) { - privateKey, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - t.Fatal(err) - } - - privateKeyBytes, err := X509.PKCS8PrivateKeyEncoder(privateKey) - if err != nil { - t.Fatal(err) - } - - decodedPrivateKey, err := X509.PKCS8PrivateKeyDecoder(privateKeyBytes) - if err != nil { - t.Fatal(err) - } - - if !decodedPrivateKey.Equal(privateKey) { - t.Fatalf("decodedPrivateKey %+v != privateKey %+v", decodedPrivateKey, privateKey) - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestPKIXPublicKey$ -func TestPKIXPublicKey(t *testing.T) { - privateKey, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - t.Fatal(err) - } - - publicKey := &privateKey.PublicKey - - publicKeyBytes, err := X509.PKIXPublicKeyEncoder(publicKey) - if err != nil { - t.Fatal(err) - } - - decodedPublicKey, err := X509.PKIXPublicKeyDecoder(publicKeyBytes) - if err != nil { - t.Fatal(err) - } - - if !decodedPublicKey.Equal(publicKey) { - t.Fatalf("decodedPublicKey %+v != publicKey %+v", decodedPublicKey, publicKey) - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestPKCS1PublicKey$ -func TestPKCS1PublicKey(t *testing.T) { - privateKey, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - t.Fatal(err) - } - - publicKey := &privateKey.PublicKey - - publicKeyBytes, err := X509.PKCS1PublicKeyEncoder(publicKey) - if err != nil { - t.Fatal(err) - } - - decodedPublicKey, err := X509.PKCS1PublicKeyDecoder(publicKeyBytes) - if err != nil { - t.Fatal(err) - } - - if !decodedPublicKey.Equal(publicKey) { - t.Fatalf("decodedPublicKey %+v != publicKey %+v", decodedPublicKey, publicKey) - } -} diff --git a/x509/pem.go b/x509/pem.go new file mode 100644 index 0000000..9ae51f5 --- /dev/null +++ b/x509/pem.go @@ -0,0 +1,33 @@ +// Copyright 2025 FishGoddess. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package x509 + +import ( + "bytes" + "encoding/pem" + "errors" +) + +func encode(blockType string, blockBytes []byte) (data []byte, err error) { + block := &pem.Block{Type: blockType, Bytes: blockBytes} + + var buffer bytes.Buffer + if err := pem.Encode(&buffer, block); err != nil { + return nil, err + } + + data = buffer.Bytes() + return data, nil +} + +func decode(data []byte) (blockType string, blockBytes []byte, err error) { + block, _ := pem.Decode(data) + if block == nil { + return "", nil, errors.New("cryptox/x509: decode block is nil") + } + + blockType, blockBytes = block.Type, block.Bytes + return blockType, blockBytes, nil +} diff --git a/x509/pem_test.go b/x509/pem_test.go new file mode 100644 index 0000000..7a21df3 --- /dev/null +++ b/x509/pem_test.go @@ -0,0 +1,58 @@ +// Copyright 2025 FishGoddess. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package x509 + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "slices" + "testing" +) + +// go test -v -cover -run=^TestPem$ +func TestPem(t *testing.T) { + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatal(err) + } + + blockBytes, err := x509.MarshalPKCS8PrivateKey(privateKey) + if err != nil { + t.Fatal(err) + } + + data, err := encode(blockTypePrivate, blockBytes) + if err != nil { + t.Fatal(err) + } + + decodeType, decodeBytes, err := decode(data) + if err != nil { + t.Fatal(err) + } + + if decodeType != blockTypePrivate { + t.Fatalf("decodeType %s != blockTypePrivate %s", decodeType, blockTypePrivate) + } + + if !slices.Equal(decodeBytes, blockBytes) { + t.Fatalf("decodeBytes %s != blockBytes %s", decodeBytes, blockBytes) + } + + pk, err := x509.ParsePKCS8PrivateKey(decodeBytes) + if err != nil { + t.Fatal(err) + } + + decodeKey, ok := pk.(*rsa.PrivateKey) + if !ok { + t.Fatalf("got type %T is wrong", pk) + } + + if !decodeKey.Equal(privateKey) { + t.Fatalf("decodeKey %+v != privateKey %+v", decodeKey, privateKey) + } +} diff --git a/x509/private.go b/x509/private.go new file mode 100644 index 0000000..3904bb9 --- /dev/null +++ b/x509/private.go @@ -0,0 +1,65 @@ +// Copyright 2025 FishGoddess. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package x509 + +import ( + "crypto/rsa" + "crypto/x509" + "fmt" +) + +const ( + blockTypePrivate = "PRIVATE KEY" + blockTypePrivateRSA = "RSA PRIVATE KEY" +) + +// EncodePrivateKeyPKCS1 uses pkcs1 to encode private key. +func EncodePrivateKeyPKCS1(key *rsa.PrivateKey) ([]byte, error) { + blockBytes := x509.MarshalPKCS1PrivateKey(key) + return encode(blockTypePrivateRSA, blockBytes) +} + +// EncodePrivateKeyPKCS8 uses pkcs8 to encode private key. +func EncodePrivateKeyPKCS8[Key any](key Key) ([]byte, error) { + blockBytes, err := x509.MarshalPKCS8PrivateKey(key) + if err != nil { + return nil, err + } + + return encode(blockTypePrivate, blockBytes) +} + +// DecodePrivateKeyPKCS1 uses pkcs1 to decode private key. +func DecodePrivateKeyPKCS1(data []byte) (*rsa.PrivateKey, error) { + _, blockBytes, err := decode(data) + if err != nil { + return nil, err + } + + return x509.ParsePKCS1PrivateKey(blockBytes) +} + +// DecodePrivateKeyPKCS8 uses pkcs8 to decode private key. +func DecodePrivateKeyPKCS8[Key any](data []byte) (Key, error) { + _, blockBytes, err := decode(data) + if err != nil { + var empty Key + return empty, err + } + + pk, err := x509.ParsePKCS8PrivateKey(blockBytes) + if err != nil { + var empty Key + return empty, err + } + + privateKey, ok := pk.(Key) + if !ok { + var empty Key + return empty, fmt.Errorf("cryptox/x509: got type %T but expect type %T", pk, empty) + } + + return privateKey, nil +} diff --git a/x509/private_test.go b/x509/private_test.go new file mode 100644 index 0000000..4ecc495 --- /dev/null +++ b/x509/private_test.go @@ -0,0 +1,55 @@ +// Copyright 2024 FishGoddess. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package x509 + +import ( + "crypto/rand" + "crypto/rsa" + "testing" +) + +// go test -v -cover -run=^TestPrivateKeyPKCS1$ +func TestPrivateKeyPKCS1(t *testing.T) { + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatal(err) + } + + data, err := EncodePrivateKeyPKCS1(privateKey) + if err != nil { + t.Fatal(err) + } + + decodeKey, err := DecodePrivateKeyPKCS1(data) + if err != nil { + t.Fatal(err) + } + + if !decodeKey.Equal(privateKey) { + t.Fatalf("decodeKey %+v != privateKey %+v", decodeKey, privateKey) + } +} + +// go test -v -cover -run=^TestPrivateKeyPKCS8$ +func TestPrivateKeyPKCS8(t *testing.T) { + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatal(err) + } + + data, err := EncodePrivateKeyPKCS8(privateKey) + if err != nil { + t.Fatal(err) + } + + decodeKey, err := DecodePrivateKeyPKCS8[*rsa.PrivateKey](data) + if err != nil { + t.Fatal(err) + } + + if !decodeKey.Equal(privateKey) { + t.Fatalf("decodeKey %+v != privateKey %+v", decodeKey, privateKey) + } +} diff --git a/x509/public.go b/x509/public.go new file mode 100644 index 0000000..20d6d29 --- /dev/null +++ b/x509/public.go @@ -0,0 +1,65 @@ +// Copyright 2025 FishGoddess. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package x509 + +import ( + "crypto/rsa" + "crypto/x509" + "fmt" +) + +const ( + blockTypePublic = "PUBLIC KEY" + blockTypePublicRSA = "RSA PUBLIC KEY" +) + +// EncodePublicKeyPKCS1 uses pkcs1 to encode public key. +func EncodePublicKeyPKCS1(key *rsa.PublicKey) ([]byte, error) { + blockBytes := x509.MarshalPKCS1PublicKey(key) + return encode(blockTypePublicRSA, blockBytes) +} + +// EncodePublicKeyPKIX uses pkix to encode public key. +func EncodePublicKeyPKIX[Key any](key Key) ([]byte, error) { + publicKeyBytes, err := x509.MarshalPKIXPublicKey(key) + if err != nil { + return nil, err + } + + return encode(blockTypePublic, publicKeyBytes) +} + +// DecodePublicKeyPKCS1 uses pkcs1 to decode public key. +func DecodePublicKeyPKCS1(data []byte) (*rsa.PublicKey, error) { + _, blockBytes, err := decode(data) + if err != nil { + return nil, err + } + + return x509.ParsePKCS1PublicKey(blockBytes) +} + +// DecodePublicKeyPKIX uses pkix to decode public key. +func DecodePublicKeyPKIX[Key any](data []byte) (Key, error) { + _, blockBytes, err := decode(data) + if err != nil { + var empty Key + return empty, err + } + + pk, err := x509.ParsePKIXPublicKey(blockBytes) + if err != nil { + var empty Key + return empty, err + } + + publicKey, ok := pk.(Key) + if !ok { + var empty Key + return empty, fmt.Errorf("cryptox/x509: got type %T but expect type %T", pk, empty) + } + + return publicKey, nil +} diff --git a/x509/public_test.go b/x509/public_test.go new file mode 100644 index 0000000..df04a47 --- /dev/null +++ b/x509/public_test.go @@ -0,0 +1,59 @@ +// Copyright 2025 FishGoddess. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package x509 + +import ( + "crypto/rand" + "crypto/rsa" + "testing" +) + +// go test -v -cover -run=^TestPublicKeyPKCS1$ +func TestPublicKeyPKCS1(t *testing.T) { + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatal(err) + } + + publicKey := &privateKey.PublicKey + + data, err := EncodePublicKeyPKCS1(publicKey) + if err != nil { + t.Fatal(err) + } + + decodeKey, err := DecodePublicKeyPKCS1(data) + if err != nil { + t.Fatal(err) + } + + if !decodeKey.Equal(publicKey) { + t.Fatalf("decodeKey %+v != publicKey %+v", decodeKey, publicKey) + } +} + +// go test -v -cover -run=^TestPublicKeyPKIX$ +func TestPublicKeyPKIX(t *testing.T) { + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatal(err) + } + + publicKey := &privateKey.PublicKey + + data, err := EncodePublicKeyPKIX(publicKey) + if err != nil { + t.Fatal(err) + } + + decodeKey, err := DecodePublicKeyPKIX[*rsa.PublicKey](data) + if err != nil { + t.Fatal(err) + } + + if !decodeKey.Equal(publicKey) { + t.Fatalf("decodeKey %+v != publicKey %+v", decodeKey, publicKey) + } +}