├── go.sum ├── go.mod ├── test ├── certs │ ├── sm2.key │ ├── sm2 │ │ ├── client_enc.key │ │ ├── client_sign.key │ │ ├── server_enc.key │ │ ├── server_sign.key │ │ ├── server_enc.crt │ │ ├── client_enc.crt │ │ ├── client_sign.crt │ │ ├── server_sign.crt │ │ └── chain-ca.crt │ └── sm2-cert.pem └── sni_certs │ ├── default │ ├── server_enc.key │ ├── server_sign.key │ ├── server_enc.crt │ └── server_sign.crt │ └── example.com │ ├── server_enc.key │ ├── server_sign.key │ ├── server_enc.crt │ └── server_sign.crt ├── .gitignore ├── CHANGELOG.md ├── crypto ├── build.go ├── build_static.go ├── dh_test.go ├── engine.go ├── mapping.go ├── dhparam.go ├── init_windows.go ├── digest.go ├── init_posix.go ├── dh.go ├── hmac_test.go ├── hmac.go ├── sha256 │ ├── sha256.go │ └── sha256_test.go ├── sha1 │ ├── sha1.go │ └── sha1_test.go ├── sm3 │ ├── sm3.go │ └── sm3_test.go ├── init.go ├── md5 │ ├── md5.go │ └── md5_test.go ├── hostname.go ├── ciphers_gcm.go ├── sm2 │ ├── sm2.go │ └── sm2_test.go ├── shim.h ├── bio.go ├── sm4 │ └── sm4.go ├── cert_test.go ├── nid.go └── ciphers.go ├── examples ├── sm2_keygen │ └── main.go ├── sm3 │ └── main.go ├── hmac_sm3 │ └── main.go ├── sm2_encrypt │ └── main.go ├── sm2_signasn1 │ └── main.go ├── sm2_sign │ └── main.go ├── cert_gen │ └── main.go ├── sm4 │ └── main.go └── tlcp_client │ └── main.go ├── pem.go ├── AUTHORS ├── init.go ├── sni.c ├── utils ├── errors.go └── future.go ├── ctx_test.go ├── README.md ├── http.go ├── .golangci.yml ├── shim.h ├── net.go ├── .github └── workflows │ └── main.yml ├── ssl.go ├── shim.c └── tickets.go /go.sum: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/tongsuo-project/tongsuo-go-sdk 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /test/certs/sm2.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgf9J4nk4pDYtXbngw 3 | 1epbcqxbRVYuAML+W90yJatzBRWhRANCAARmJiuMDZvqstW5mi1yj931E6S5jxkJ 4 | dqO4hE0A4n05vGGlc/K0JrCNNDPxYM97jcmbNDogre5Vh6m+9IGLtXvN 5 | -----END PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /test/certs/sm2/client_enc.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQguz0M4/6qUhgHAxuG 3 | WI2NPtNciIwmYAN4AUDoBEka1tehRANCAAQpIN3pNIBB3oZ+SaXKoZNtMkH5t5y1 4 | 3MXG1Zsx+NiEZ7BbOFBbEB99vyQry6c9rzlM8IedPw6OwIc58dsA+ncM 5 | -----END PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /test/certs/sm2/client_sign.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQg5zLNmBvXdesfATxu 3 | gMMpvEHfuxhUCr6L8rS2EEBr6SGhRANCAAQtYX501Vht3iPSSQ+9Ro4w8R0BLVCo 4 | 85LNGEmxCxZ+mgZh9+OioA7n7EcY7ZN7SrLFCqDTQdFeQJV0OzhQr208 5 | -----END PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /test/certs/sm2/server_enc.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgLrRk3CWTe+WZOFSf 3 | TMYwbOocLs3MSRpOO0/AvSmvH5mhRANCAAR9vqVFQ0WBcr07aI5QnC31RYas4AtY 4 | 7JQUmflKUKWMZ11vmtr/CJ6BN6djQ6zS81yjCopcz4G3zc5SZqAWueNk 5 | -----END PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /test/certs/sm2/server_sign.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgeQTrKtO8mNXn/yvg 3 | R+pdbCgH5sl+WCFfXcqGl64soU2hRANCAAQFv/ruxAbI8/WApuOcUoR2wN8rYQZd 4 | SnT0dq8PtmiQ+JasxLIdiwNtE/F71NOCNJCL7bd/jj6uhwZU/G+oBI0M 5 | -----END PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /test/sni_certs/default/server_enc.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgLrRk3CWTe+WZOFSf 3 | TMYwbOocLs3MSRpOO0/AvSmvH5mhRANCAAR9vqVFQ0WBcr07aI5QnC31RYas4AtY 4 | 7JQUmflKUKWMZ11vmtr/CJ6BN6djQ6zS81yjCopcz4G3zc5SZqAWueNk 5 | -----END PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /test/sni_certs/default/server_sign.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgeQTrKtO8mNXn/yvg 3 | R+pdbCgH5sl+WCFfXcqGl64soU2hRANCAAQFv/ruxAbI8/WApuOcUoR2wN8rYQZd 4 | SnT0dq8PtmiQ+JasxLIdiwNtE/F71NOCNJCL7bd/jj6uhwZU/G+oBI0M 5 | -----END PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /test/sni_certs/example.com/server_enc.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgNJC8Bzem59RJoHlf 3 | /aVfBxUEK/wow6NrvmCogBWxcQKhRANCAASB9wehAvnshZd+drdV3D7ykGFlEuMD 4 | gP4u+Z99v6ettWFR3dDGzfGejr/YPmOldUYxOdLMKmWEBSSBwE4msaJA 5 | -----END PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /test/sni_certs/example.com/server_sign.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgU4wVy8VEGMvXccs1 3 | BqOXXFvoDEIl+a2m7qIZtEeyj0ShRANCAARjMNvoLh6dW3eMqYekyn8ErkdeFPn+ 4 | f3WDltkIOcsuV2h0clneYZPT4Z1VomZ9iMIdqe8qZTifdrSHpr16/dCL 5 | -----END PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Temp files for IDEs 9 | *.exrc 10 | 11 | # Test binary, built with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Dependency directories (remove the comment below to include it) 18 | tongsuo/ 19 | 20 | crypto/test-runs/ 21 | examples/cert_gen/ 22 | .vscode/ 23 | .idea/ 24 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Version 1.0.0 (2025-01-07) 2 | 3 | Initial release based on Tongsuo 8.3-stable. 4 | 5 | New Features: 6 | 7 | - Support hash algorithms: SM3, MD5, SHA1, SHA256 8 | - Support SM4 symmetric encryption algorithm, including CBC/ECB/CFB/OFB/CTR/GCM/CCM mode 9 | - Support SM2 keygen, encryption and decryption 10 | - Support SM2withSM3 digital signature algorithm 11 | - Support HMAC 12 | - Support issuing SM2 certificate 13 | - Support secure transport protocols, including TLCP, TLSv1.0/1.1/1.2/1.3 14 | -------------------------------------------------------------------------------- /test/sni_certs/example.com/server_enc.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBZzCCAQ6gAwIBAgIBATAKBggqgRzPVQGDdTAsMQswCQYDVQQGEwJVUzEQMA4G 3 | A1UECgwHVGVzdCBDQTELMAkGA1UEAwwCQ0EwHhcNMjQwODE1MDIzNTM1WhcNMzQw 4 | ODEzMDIzNTM1WjAwMQswCQYDVQQGEwJVUzENMAsGA1UECgwEVGVzdDESMBAGA1UE 5 | AwwJbG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEgfcHoQL57IWX 6 | fna3Vdw+8pBhZRLjA4D+Lvmffb+nrbVhUd3Qxs3xno6/2D5jpXVGMTnSzCplhAUk 7 | gcBOJrGiQKMdMBswDAYDVR0TAQH/BAIwADALBgNVHQ8EBAMCAzgwCgYIKoEcz1UB 8 | g3UDRwAwRAIgUZ4VjGFYc70O5E/OvyDZJ5cp9NyQPhWrWP6h5MWLzsMCIAWa8kxb 9 | JdWfHWE6S9U+4hFJ4BxlCt4RqIZLhen1wR5J 10 | -----END CERTIFICATE----- 11 | -------------------------------------------------------------------------------- /test/sni_certs/example.com/server_sign.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBaDCCAQ6gAwIBAgIBATAKBggqgRzPVQGDdTAsMQswCQYDVQQGEwJVUzEQMA4G 3 | A1UECgwHVGVzdCBDQTELMAkGA1UEAwwCQ0EwHhcNMjQwODE1MDIzNTM1WhcNMzQw 4 | ODEzMDIzNTM1WjAwMQ0wCwYDVQQKDARUZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3Qx 5 | CzAJBgNVBAYTAlVTMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEYzDb6C4enVt3 6 | jKmHpMp/BK5HXhT5/n91g5bZCDnLLldodHJZ3mGT0+GdVaJmfYjCHanvKmU4n3a0 7 | h6a9ev3Qi6MdMBswDAYDVR0TAQH/BAIwADALBgNVHQ8EBAMCBsAwCgYIKoEcz1UB 8 | g3UDSAAwRQIhAMgVJkzdVkOEKcNoeX5g3F2cOK4AOlWLiY+kXsQKnwAiAiA7ujBK 9 | VvZiKSp2KLSQqXqEHwqqA2ydwgs+ilVIMjbqrg== 10 | -----END CERTIFICATE----- 11 | -------------------------------------------------------------------------------- /crypto/build.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Tongsuo Project Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License 2.0 (the "License"). You may not use 4 | // this file except in compliance with the License. You can obtain a copy 5 | // in the file LICENSE in the source distribution or at 6 | // https://github.com/Tongsuo-Project/tongsuo-go-sdk/blob/main/LICENSE 7 | 8 | //go:build !static 9 | // +build !static 10 | 11 | package crypto 12 | 13 | // #cgo linux LDFLAGS: -lcrypto 14 | // #cgo darwin LDFLAGS: -lcrypto 15 | // #cgo windows CFLAGS: -DWIN32_LEAN_AND_MEAN 16 | // #cgo windows LDFLAGS: -lcrypto 17 | import "C" 18 | -------------------------------------------------------------------------------- /crypto/build_static.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Tongsuo Project Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License 2.0 (the "License"). You may not use 4 | // this file except in compliance with the License. You can obtain a copy 5 | // in the file LICENSE in the source distribution or at 6 | // https://github.com/Tongsuo-Project/tongsuo-go-sdk/blob/main/LICENSE 7 | 8 | //go:build static 9 | // +build static 10 | 11 | package crypto 12 | 13 | // #cgo linux LDFLAGS: -extldflags -static -lcrypto 14 | // #cgo darwin LDFLAGS: -lcrypto 15 | // #cgo windows CFLAGS: -DWIN32_LEAN_AND_MEAN 16 | // #cgo windows LDFLAGS: -extldflags -static -lcrypto 17 | import "C" 18 | -------------------------------------------------------------------------------- /test/certs/sm2/server_enc.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIB6zCCAZKgAwIBAgIUaNiS6WOsoEViDnmdb8Mdk3Qz5XwwCgYIKoEcz1UBg3Uw 3 | RTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UE 4 | CwwCREQxDzANBgNVBAMMBnN1YiBjYTAgFw0yMzAyMjIwMjMwMTRaGA8yMTIzMDEy 5 | OTAyMzAxNFowSTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJD 6 | QzELMAkGA1UECwwCREQxEzARBgNVBAMMCnNlcnZlciBlbmMwWTATBgcqhkjOPQIB 7 | BggqgRzPVQGCLQNCAAR9vqVFQ0WBcr07aI5QnC31RYas4AtY7JQUmflKUKWMZ11v 8 | mtr/CJ6BN6djQ6zS81yjCopcz4G3zc5SZqAWueNko1owWDAJBgNVHRMEAjAAMAsG 9 | A1UdDwQEAwIDODAdBgNVHQ4EFgQUZ6Wt1ZR24FqcXla4hg/xOyju7FQwHwYDVR0j 10 | BBgwFoAUrGHrIoBiWQg+lsjRf850XAKvPJkwCgYIKoEcz1UBg3UDRwAwRAIgR1k1 11 | ecSt7I2335jEquFmHBE5pe8Sk/IqOqQS0Jvs1uYCIG5XMB0XeUaVb9OctaxgOQLN 12 | F8dRftiUHsyYXqfbaVjI 13 | -----END CERTIFICATE----- 14 | -------------------------------------------------------------------------------- /test/certs/sm2/client_enc.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIB7TCCAZKgAwIBAgIUWy6/ole1R8GwTrzoOtZebFla3aowCgYIKoEcz1UBg3Uw 3 | RTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UE 4 | CwwCREQxDzANBgNVBAMMBnN1YiBjYTAgFw0yMzAyMjIwMjMwMTRaGA8yMTIzMDEy 5 | OTAyMzAxNFowSTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJD 6 | QzELMAkGA1UECwwCREQxEzARBgNVBAMMCmNsaWVudCBlbmMwWTATBgcqhkjOPQIB 7 | BggqgRzPVQGCLQNCAAQpIN3pNIBB3oZ+SaXKoZNtMkH5t5y13MXG1Zsx+NiEZ7Bb 8 | OFBbEB99vyQry6c9rzlM8IedPw6OwIc58dsA+ncMo1owWDAJBgNVHRMEAjAAMAsG 9 | A1UdDwQEAwIDODAdBgNVHQ4EFgQUfPE8T3aPRzOi/+LiWTRrkM+0dKgwHwYDVR0j 10 | BBgwFoAUrGHrIoBiWQg+lsjRf850XAKvPJkwCgYIKoEcz1UBg3UDSQAwRgIhAOIT 11 | GEUHnILUpLCbSZCyG8TigYmbg7ImyZFtXF/uEhOfAiEA59PnEVYaegvpI5Ltn5T2 12 | PKKqiZ2QOWEfRHJIi/FFZeo= 13 | -----END CERTIFICATE----- 14 | -------------------------------------------------------------------------------- /test/certs/sm2/client_sign.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIB7jCCAZOgAwIBAgIUbEstbqUWeJMWK3rlwLXE9YdtJ8QwCgYIKoEcz1UBg3Uw 3 | RTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UE 4 | CwwCREQxDzANBgNVBAMMBnN1YiBjYTAgFw0yMzAyMjIwMjMwMTRaGA8yMTIzMDEy 5 | OTAyMzAxNFowSjELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJD 6 | QzELMAkGA1UECwwCREQxFDASBgNVBAMMC2NsaWVudCBzaWduMFkwEwYHKoZIzj0C 7 | AQYIKoEcz1UBgi0DQgAELWF+dNVYbd4j0kkPvUaOMPEdAS1QqPOSzRhJsQsWfpoG 8 | YffjoqAO5+xHGO2Te0qyxQqg00HRXkCVdDs4UK9tPKNaMFgwCQYDVR0TBAIwADAL 9 | BgNVHQ8EBAMCBsAwHQYDVR0OBBYEFFRpNXBAPyjFwT7w8EBNJLMVaFgdMB8GA1Ud 10 | IwQYMBaAFKxh6yKAYlkIPpbI0X/OdFwCrzyZMAoGCCqBHM9VAYN1A0kAMEYCIQDH 11 | LBaKDJFhHYRNLhYXFBtZH6BIa6cJfewLyVaH0oDaMgIhAMgZQDmGvHCzJ9vdgL2P 12 | 7upTf3I28uj+3pq7ZiwKRBlO 13 | -----END CERTIFICATE----- 14 | -------------------------------------------------------------------------------- /test/certs/sm2/server_sign.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIB7jCCAZOgAwIBAgIUcbKTlc6+CNoHglmEk+xm+WIqZcAwCgYIKoEcz1UBg3Uw 3 | RTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UE 4 | CwwCREQxDzANBgNVBAMMBnN1YiBjYTAgFw0yMzAyMjIwMjMwMTRaGA8yMTIzMDEy 5 | OTAyMzAxNFowSjELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJD 6 | QzELMAkGA1UECwwCREQxFDASBgNVBAMMC3NlcnZlciBzaWduMFkwEwYHKoZIzj0C 7 | AQYIKoEcz1UBgi0DQgAEBb/67sQGyPP1gKbjnFKEdsDfK2EGXUp09HavD7ZokPiW 8 | rMSyHYsDbRPxe9TTgjSQi+23f44+rocGVPxvqASNDKNaMFgwCQYDVR0TBAIwADAL 9 | BgNVHQ8EBAMCBsAwHQYDVR0OBBYEFH3uBqkdowIvk//P7n5UtnpV9TR6MB8GA1Ud 10 | IwQYMBaAFKxh6yKAYlkIPpbI0X/OdFwCrzyZMAoGCCqBHM9VAYN1A0kAMEYCIQCz 11 | W/6Z/d/IJUTrO0o8nCxNle6R0AkRCKUFhW9zbIRlNwIhAJZxg4gs2cV2QF37oHs6 12 | 9TD+MkRbql4Yb47+jLf8f247 13 | -----END CERTIFICATE----- 14 | -------------------------------------------------------------------------------- /test/sni_certs/default/server_enc.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIB6zCCAZKgAwIBAgIUaNiS6WOsoEViDnmdb8Mdk3Qz5XwwCgYIKoEcz1UBg3Uw 3 | RTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UE 4 | CwwCREQxDzANBgNVBAMMBnN1YiBjYTAgFw0yMzAyMjIwMjMwMTRaGA8yMTIzMDEy 5 | OTAyMzAxNFowSTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJD 6 | QzELMAkGA1UECwwCREQxEzARBgNVBAMMCnNlcnZlciBlbmMwWTATBgcqhkjOPQIB 7 | BggqgRzPVQGCLQNCAAR9vqVFQ0WBcr07aI5QnC31RYas4AtY7JQUmflKUKWMZ11v 8 | mtr/CJ6BN6djQ6zS81yjCopcz4G3zc5SZqAWueNko1owWDAJBgNVHRMEAjAAMAsG 9 | A1UdDwQEAwIDODAdBgNVHQ4EFgQUZ6Wt1ZR24FqcXla4hg/xOyju7FQwHwYDVR0j 10 | BBgwFoAUrGHrIoBiWQg+lsjRf850XAKvPJkwCgYIKoEcz1UBg3UDRwAwRAIgR1k1 11 | ecSt7I2335jEquFmHBE5pe8Sk/IqOqQS0Jvs1uYCIG5XMB0XeUaVb9OctaxgOQLN 12 | F8dRftiUHsyYXqfbaVjI 13 | -----END CERTIFICATE----- 14 | -------------------------------------------------------------------------------- /test/sni_certs/default/server_sign.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIB7jCCAZOgAwIBAgIUcbKTlc6+CNoHglmEk+xm+WIqZcAwCgYIKoEcz1UBg3Uw 3 | RTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UE 4 | CwwCREQxDzANBgNVBAMMBnN1YiBjYTAgFw0yMzAyMjIwMjMwMTRaGA8yMTIzMDEy 5 | OTAyMzAxNFowSjELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJD 6 | QzELMAkGA1UECwwCREQxFDASBgNVBAMMC3NlcnZlciBzaWduMFkwEwYHKoZIzj0C 7 | AQYIKoEcz1UBgi0DQgAEBb/67sQGyPP1gKbjnFKEdsDfK2EGXUp09HavD7ZokPiW 8 | rMSyHYsDbRPxe9TTgjSQi+23f44+rocGVPxvqASNDKNaMFgwCQYDVR0TBAIwADAL 9 | BgNVHQ8EBAMCBsAwHQYDVR0OBBYEFH3uBqkdowIvk//P7n5UtnpV9TR6MB8GA1Ud 10 | IwQYMBaAFKxh6yKAYlkIPpbI0X/OdFwCrzyZMAoGCCqBHM9VAYN1A0kAMEYCIQCz 11 | W/6Z/d/IJUTrO0o8nCxNle6R0AkRCKUFhW9zbIRlNwIhAJZxg4gs2cV2QF37oHs6 12 | 9TD+MkRbql4Yb47+jLf8f247 13 | -----END CERTIFICATE----- 14 | -------------------------------------------------------------------------------- /test/certs/sm2-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIB/zCCAaSgAwIBAgIUXL0hEsZsfetdIiPFtxDgKFHRfWswCgYIKoEcz1UBg3Uw 3 | TjEPMA0GA1UECgwGU00yLUNBMSQwIgYDVQQDDBtTTTIgY2VydGlmaWNhdGUgc2ln 4 | bmluZyBrZXkxFTATBgkqhkiG9w0BCQEWBmNhQHNtMjAgFw0yMzA0MjUwNjUxNTZa 5 | GA8yMDUwMDkxMDA2NTE1NlowTjEPMA0GA1UECgwGU00yLUNBMSQwIgYDVQQDDBtT 6 | TTIgY2VydGlmaWNhdGUgc2lnbmluZyBrZXkxFTATBgkqhkiG9w0BCQEWBmNhQHNt 7 | MjBaMBQGCCqBHM9VAYItBggqgRzPVQGCLQNCAARmJiuMDZvqstW5mi1yj931E6S5 8 | jxkJdqO4hE0A4n05vGGlc/K0JrCNNDPxYM97jcmbNDogre5Vh6m+9IGLtXvNo10w 9 | WzAMBgNVHRMBAf8EAjAAMAsGA1UdDwQEAwIGwDAdBgNVHQ4EFgQU1Q7WULoMM5g/ 10 | AyY0AxfrrvehM9cwHwYDVR0jBBgwFoAUPjscE6pUGFdHLY1byo1sbfpdspMwCgYI 11 | KoEcz1UBg3UDSQAwRgIhALwTlkh4uvDB/S9bP0m/pxrf6D5yBjOqqojCDCyflrVY 12 | AiEA9kzSE4ASlZDZ9HLxg4QZ/+4Wj18yrOpNEIugmGcP52w= 13 | -----END CERTIFICATE----- 14 | -------------------------------------------------------------------------------- /examples/sm2_keygen/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Tongsuo Project Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License 2.0 (the "License"). You may not use 4 | // this file except in compliance with the License. You can obtain a copy 5 | // in the file LICENSE in the source distribution or at 6 | // https://github.com/Tongsuo-Project/tongsuo-go-sdk/blob/main/LICENSE 7 | 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | 13 | "github.com/tongsuo-project/tongsuo-go-sdk/crypto/sm2" 14 | ) 15 | 16 | func main() { 17 | priv, err := sm2.GenerateKey() 18 | if err != nil { 19 | panic(err) 20 | } 21 | 22 | pem, err := priv.MarshalPKCS1PrivateKeyPEM() 23 | if err != nil { 24 | panic(err) 25 | } 26 | 27 | fmt.Printf("Private Key:\n%s\n", pem) 28 | 29 | pub := priv.Public() 30 | 31 | pem, err = pub.MarshalPKIXPublicKeyPEM() 32 | if err != nil { 33 | panic(err) 34 | } 35 | 36 | fmt.Printf("Public Key:\n%s\n", pem) 37 | } 38 | -------------------------------------------------------------------------------- /examples/sm3/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Tongsuo Project Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License 2.0 (the "License"). You may not use 4 | // this file except in compliance with the License. You can obtain a copy 5 | // in the file LICENSE in the source distribution or at 6 | // https://github.com/Tongsuo-Project/tongsuo-go-sdk/blob/main/LICENSE 7 | 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | "log" 13 | 14 | "github.com/tongsuo-project/tongsuo-go-sdk/crypto/sm3" 15 | ) 16 | 17 | func main() { 18 | msg := "hello world" 19 | fmt.Printf("SM3(%s)=%x\n", msg, sm3.Sum([]byte(msg))) 20 | 21 | h, err := sm3.New() 22 | if err != nil { 23 | log.Fatal(err) 24 | } 25 | 26 | if _, err := h.Write([]byte("hello")); err != nil { 27 | log.Fatal(err) 28 | } 29 | 30 | if _, err := h.Write([]byte(" world")); err != nil { 31 | log.Fatal(err) 32 | } 33 | 34 | var res [sm3.MDSize]byte 35 | 36 | fmt.Printf("SM3(%s)=%x\n", msg, h.Sum(res[:0])) 37 | } 38 | -------------------------------------------------------------------------------- /examples/hmac_sm3/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 The Tongsuo Project Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License 2.0 (the "License"). You may not use 4 | // this file except in compliance with the License. You can obtain a copy 5 | // in the file LICENSE in the source distribution or at 6 | // https://github.com/Tongsuo-Project/tongsuo-go-sdk/blob/main/LICENSE 7 | 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | 13 | "github.com/tongsuo-project/tongsuo-go-sdk/crypto" 14 | ) 15 | 16 | func main() { 17 | key := []byte("1234567890123456") 18 | 19 | h, err := crypto.NewHMAC(key, crypto.DigestSM3) 20 | if err != nil { 21 | panic(err) 22 | } 23 | 24 | _, err = h.Write([]byte("hello")) 25 | if err != nil { 26 | panic(err) 27 | } 28 | 29 | _, err = h.Write([]byte(" world")) 30 | if err != nil { 31 | panic(err) 32 | } 33 | 34 | res, err := h.Final() 35 | if err != nil { 36 | panic(err) 37 | } 38 | 39 | fmt.Printf("HMAC-SM3(hello world)=%x\n", res) 40 | } 41 | -------------------------------------------------------------------------------- /pem.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package tongsuogo 16 | 17 | import ( 18 | "regexp" 19 | ) 20 | 21 | var pemSplit = regexp.MustCompile(`(?sm)` + 22 | `(^-----[\s-]*?BEGIN.*?-----$` + 23 | `.*?` + 24 | `^-----[\s-]*?END.*?-----$)`) 25 | 26 | func SplitPEM(data []byte) [][]byte { 27 | var results [][]byte 28 | 29 | results = append(results, pemSplit.FindAll(data, -1)...) 30 | 31 | return results 32 | } 33 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Andrew Brampton 2 | Anton Baklanov 3 | Carlos Martín Nieto 4 | Charles Strahan 5 | Christopher Dudley 6 | Christopher Fredericks 7 | Colin Misare 8 | dequis 9 | Gabriel Russell 10 | Giulio 11 | Jakob Unterwurzacher 12 | Juuso Haavisto 13 | kujenga 14 | Phus Lu 15 | Russ Egan 16 | Ryan Hileman 17 | Scott J. Goldman 18 | Scott Kidder 19 | Space Monkey, Inc 20 | Stephen Gallagher 21 | Viacheslav Biriukov 22 | Zack Owens 23 | Ramesh Rayaprolu 24 | Paras Shah 25 | Gao Xiang 26 | K1 27 | -------------------------------------------------------------------------------- /init.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // Copyright 2023 The Tongsuo Project Authors. All Rights Reserved. 16 | // 17 | // Licensed under the Apache License 2.0 (the "License"). You may not use 18 | // this file except in compliance with the License. You can obtain a copy 19 | // in the file LICENSE in the source distribution or at 20 | // https://github.com/Tongsuo-Project/tongsuo-go-sdk/blob/main/LICENSE 21 | 22 | package tongsuogo 23 | 24 | // #include "shim.h" 25 | import "C" 26 | 27 | func init() { 28 | C.X_tongsuogo_init() 29 | } 30 | -------------------------------------------------------------------------------- /examples/sm2_encrypt/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | 7 | "github.com/tongsuo-project/tongsuo-go-sdk/crypto" 8 | "github.com/tongsuo-project/tongsuo-go-sdk/crypto/sm2" 9 | ) 10 | 11 | var sm2_key1 = []byte(`-----BEGIN PRIVATE KEY----- 12 | MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQg0JFWczAXva2An9m7 13 | 2MaT9gIwWTFptvlKrxyO4TjMmbWhRANCAAQ5OirZ4n5DrKqrhaGdO4VZHhRAYVcX 14 | Wt3Te/d/8Mr57Tf886i09VwDhSMmH8pmNq/mp6+ioUgqYG9cs6GLLioe 15 | -----END PRIVATE KEY----- 16 | `) 17 | 18 | func main() { 19 | data := []byte("hello world") 20 | 21 | priv, err := crypto.LoadPrivateKeyFromPEM(sm2_key1) 22 | if err != nil { 23 | panic(err) 24 | } 25 | 26 | pub := priv.Public() 27 | 28 | // Encrypt data 29 | ciphertext, err := sm2.Encrypt(pub, data) 30 | if err != nil { 31 | panic(err) 32 | } 33 | 34 | fmt.Printf("SM2(%s)=%s\n", data, hex.EncodeToString(ciphertext)) 35 | 36 | // Decrypt ciphertext 37 | plaintext, err := sm2.Decrypt(priv, ciphertext) 38 | if err != nil { 39 | panic(err) 40 | } 41 | 42 | if string(plaintext) != string(data) { 43 | panic("Decryption failure") 44 | } 45 | 46 | fmt.Printf("Decryption OK: %s\n", plaintext) 47 | } 48 | -------------------------------------------------------------------------------- /sni.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include "_cgo_export.h" 17 | #include 18 | 19 | int sni_cb(SSL *con, int *ad, void *arg) { 20 | SSL_CTX* ssl_ctx = SSL_get_SSL_CTX(con); 21 | void* p = SSL_CTX_get_ex_data(ssl_ctx, get_ssl_ctx_idx()); 22 | return sniCbThunk(p, con, ad, arg); 23 | } 24 | 25 | int alpn_cb(SSL *ssl_conn, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) { 26 | SSL_CTX* ssl_ctx = SSL_get_SSL_CTX(ssl_conn); 27 | void* p = SSL_CTX_get_ex_data(ssl_ctx, get_ssl_ctx_idx()); 28 | return alpn_cb_thunk(p, ssl_conn, (unsigned char **)out, outlen, (unsigned char *)in, inlen, arg); 29 | } -------------------------------------------------------------------------------- /crypto/dh_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package crypto_test 16 | 17 | import ( 18 | "bytes" 19 | "testing" 20 | 21 | "github.com/tongsuo-project/tongsuo-go-sdk/crypto" 22 | ) 23 | 24 | func TestECDH(t *testing.T) { 25 | t.Parallel() 26 | 27 | myKey, err := crypto.GenerateECKey(crypto.Prime256v1) 28 | if err != nil { 29 | t.Fatal(err) 30 | } 31 | 32 | peerKey, err := crypto.GenerateECKey(crypto.Prime256v1) 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | 37 | mySecret, err := crypto.DeriveSharedSecret(myKey, peerKey) 38 | if err != nil { 39 | t.Fatal(err) 40 | } 41 | 42 | theirSecret, err := crypto.DeriveSharedSecret(peerKey, myKey) 43 | if err != nil { 44 | t.Fatal(err) 45 | } 46 | 47 | if !bytes.Equal(mySecret, theirSecret) { 48 | t.Fatal("shared secrets are different") 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /examples/sm2_signasn1/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Tongsuo Project Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License 2.0 (the "License"). You may not use 4 | // this file except in compliance with the License. You can obtain a copy 5 | // in the file LICENSE in the source distribution or at 6 | // https://github.com/Tongsuo-Project/tongsuo-go-sdk/blob/main/LICENSE 7 | 8 | package main 9 | 10 | import ( 11 | "encoding/hex" 12 | "fmt" 13 | 14 | "github.com/tongsuo-project/tongsuo-go-sdk/crypto" 15 | "github.com/tongsuo-project/tongsuo-go-sdk/crypto/sm2" 16 | ) 17 | 18 | var sm2_key1 = []byte(`-----BEGIN PRIVATE KEY----- 19 | MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQg0JFWczAXva2An9m7 20 | 2MaT9gIwWTFptvlKrxyO4TjMmbWhRANCAAQ5OirZ4n5DrKqrhaGdO4VZHhRAYVcX 21 | Wt3Te/d/8Mr57Tf886i09VwDhSMmH8pmNq/mp6+ioUgqYG9cs6GLLioe 22 | -----END PRIVATE KEY----- 23 | `) 24 | 25 | func main() { 26 | data := []byte("hello world") 27 | 28 | priv, err := crypto.LoadPrivateKeyFromPEM(sm2_key1) 29 | if err != nil { 30 | panic(err) 31 | } 32 | 33 | // Sign data 34 | signature, err := sm2.SignASN1(priv, data) 35 | if err != nil { 36 | panic(err) 37 | } 38 | 39 | fmt.Printf("SM2withSM3(%s)=%s\n", data, hex.EncodeToString(signature)) 40 | 41 | pub := priv.Public() 42 | 43 | // Verify signature 44 | if sm2.VerifyASN1(pub, data, signature) != nil { 45 | panic("Verification failure") 46 | } 47 | 48 | fmt.Println("Verification OK") 49 | } 50 | -------------------------------------------------------------------------------- /examples/sm2_sign/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Tongsuo Project Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License 2.0 (the "License"). You may not use 4 | // this file except in compliance with the License. You can obtain a copy 5 | // in the file LICENSE in the source distribution or at 6 | // https://github.com/Tongsuo-Project/tongsuo-go-sdk/blob/main/LICENSE 7 | 8 | package main 9 | 10 | import ( 11 | "encoding/hex" 12 | "fmt" 13 | 14 | "github.com/tongsuo-project/tongsuo-go-sdk/crypto" 15 | "github.com/tongsuo-project/tongsuo-go-sdk/crypto/sm2" 16 | ) 17 | 18 | var sm2_key1 = []byte(`-----BEGIN PRIVATE KEY----- 19 | MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQg0JFWczAXva2An9m7 20 | 2MaT9gIwWTFptvlKrxyO4TjMmbWhRANCAAQ5OirZ4n5DrKqrhaGdO4VZHhRAYVcX 21 | Wt3Te/d/8Mr57Tf886i09VwDhSMmH8pmNq/mp6+ioUgqYG9cs6GLLioe 22 | -----END PRIVATE KEY----- 23 | `) 24 | 25 | func main() { 26 | data := []byte("hello world") 27 | 28 | priv, err := crypto.LoadPrivateKeyFromPEM(sm2_key1) 29 | if err != nil { 30 | panic(err) 31 | } 32 | 33 | // Sign data 34 | r, s, err := sm2.Sign(priv, data) 35 | if err != nil { 36 | panic(err) 37 | } 38 | 39 | fmt.Printf("SM2withSM3(%s)=(r, s)=(%s, %s)\n", data, hex.EncodeToString(r.Bytes()), hex.EncodeToString(s.Bytes())) 40 | 41 | pub := priv.Public() 42 | 43 | // Verify signature 44 | if sm2.Verify(pub, data, r, s) != nil { 45 | panic("Verification failure") 46 | } 47 | 48 | fmt.Println("Verification OK") 49 | } 50 | -------------------------------------------------------------------------------- /crypto/engine.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package crypto 16 | 17 | /* 18 | #include 19 | */ 20 | import "C" 21 | 22 | import ( 23 | "fmt" 24 | "runtime" 25 | "unsafe" 26 | ) 27 | 28 | type Engine struct { 29 | e *C.ENGINE 30 | } 31 | 32 | func (e *Engine) Engine() *C.ENGINE { 33 | if e == nil { 34 | return nil 35 | } 36 | return e.e 37 | } 38 | 39 | func EngineByID(name string) (*Engine, error) { 40 | cname := C.CString(name) 41 | defer C.free(unsafe.Pointer(cname)) 42 | e := &Engine{ 43 | e: C.ENGINE_by_id(cname), 44 | } 45 | if e.e == nil { 46 | return nil, ErrNoEngine 47 | } 48 | if C.ENGINE_init(e.e) == 0 { 49 | C.ENGINE_free(e.e) 50 | return nil, fmt.Errorf("failed to init engine: %w", PopError()) 51 | } 52 | runtime.SetFinalizer(e, func(e *Engine) { 53 | C.ENGINE_finish(e.e) 54 | C.ENGINE_free(e.e) 55 | }) 56 | return e, nil 57 | } 58 | -------------------------------------------------------------------------------- /crypto/mapping.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package crypto 16 | 17 | // #include 18 | import "C" 19 | 20 | import ( 21 | "sync" 22 | "unsafe" 23 | ) 24 | 25 | type mapping struct { 26 | lock sync.Mutex 27 | values map[token]unsafe.Pointer 28 | } 29 | 30 | func newMapping() *mapping { 31 | return &mapping{ 32 | values: make(map[token]unsafe.Pointer), 33 | } 34 | } 35 | 36 | type token unsafe.Pointer 37 | 38 | func (m *mapping) Add(x unsafe.Pointer) token { 39 | res := token(C.malloc(1)) 40 | 41 | m.lock.Lock() 42 | m.values[res] = x 43 | m.lock.Unlock() 44 | 45 | return res 46 | } 47 | 48 | func (m *mapping) Get(x token) unsafe.Pointer { 49 | m.lock.Lock() 50 | res := m.values[x] 51 | m.lock.Unlock() 52 | 53 | return res 54 | } 55 | 56 | func (m *mapping) Del(x token) { 57 | m.lock.Lock() 58 | delete(m.values, x) 59 | m.lock.Unlock() 60 | 61 | C.free(unsafe.Pointer(x)) 62 | } 63 | -------------------------------------------------------------------------------- /test/certs/sm2/chain-ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIB4DCCAYagAwIBAgIBADAKBggqgRzPVQGDdTBGMQswCQYDVQQGEwJBQTELMAkG 3 | A1UECAwCQkIxCzAJBgNVBAoMAkNDMQswCQYDVQQLDAJERDEQMA4GA1UEAwwHcm9v 4 | dCBjYTAgFw0yMzAyMjIwMjMwMTNaGA8yMTIzMDEyOTAyMzAxM1owRjELMAkGA1UE 5 | BhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UECwwCREQxEDAO 6 | BgNVBAMMB3Jvb3QgY2EwWTATBgcqhkjOPQIBBggqgRzPVQGCLQNCAASN55Ju2pvU 7 | Bi8UrWHc4ZaKnsqiFPWfcM/6H2Gu/VQ7I1oVnyPktvlTrtwhSy6K43JoCnjVPHrq 8 | jOXxnkOtGVDVo2MwYTAdBgNVHQ4EFgQUxu7mMmVaB3vq7JRi8UEFHcxVFY4wHwYD 9 | VR0jBBgwFoAUxu7mMmVaB3vq7JRi8UEFHcxVFY4wDwYDVR0TAQH/BAUwAwEB/zAO 10 | BgNVHQ8BAf8EBAMCAYYwCgYIKoEcz1UBg3UDSAAwRQIhAIz7tgrp7LmOQEJGPAU3 11 | 8m9PNzMOTqGWZqux8CxIuEGjAiB4cFVYQ4sTCYb/4fNayKYO1FH+Q2Cc7xGq7WPd 12 | knwWpw== 13 | -----END CERTIFICATE----- 14 | -----BEGIN CERTIFICATE----- 15 | MIIB4zCCAYigAwIBAgIBATAKBggqgRzPVQGDdTBGMQswCQYDVQQGEwJBQTELMAkG 16 | A1UECAwCQkIxCzAJBgNVBAoMAkNDMQswCQYDVQQLDAJERDEQMA4GA1UEAwwHcm9v 17 | dCBjYTAgFw0yMzAyMjIwMjMwMTNaGA8yMTIzMDEyOTAyMzAxM1owRTELMAkGA1UE 18 | BhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UECwwCREQxDzAN 19 | BgNVBAMMBnN1YiBjYTBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABH0feWwae0S0 20 | w4QQA5cBGYwaQPaxZFcLzIqph+I6BQQCGXaIAabqpO0zjAyf1twYmoM3ZRLJgbZz 21 | HE/2rRMPBiajZjBkMB0GA1UdDgQWBBSsYesigGJZCD6WyNF/znRcAq88mTAfBgNV 22 | HSMEGDAWgBTG7uYyZVoHe+rslGLxQQUdzFUVjjASBgNVHRMBAf8ECDAGAQH/AgEA 23 | MA4GA1UdDwEB/wQEAwIBhjAKBggqgRzPVQGDdQNJADBGAiEApoHDue1bzGukE97O 24 | BqQbboU1d3jqNg4gAgpMe5fFIosCIQDwndSp7Tc3DZ0QCifXKNqgykjepsWTPZ3R 25 | NrMzM0rflg== 26 | -----END CERTIFICATE----- 27 | -------------------------------------------------------------------------------- /crypto/dhparam.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package crypto 16 | 17 | // #include "shim.h" 18 | import "C" 19 | 20 | import ( 21 | "runtime" 22 | "unsafe" 23 | ) 24 | 25 | type DH struct { 26 | dh *C.DH 27 | } 28 | 29 | func (dh *DH) GetDH() *C.DH { 30 | return dh.dh 31 | } 32 | 33 | // LoadDHParametersFromPEM loads the Diffie-Hellman parameters from 34 | // a PEM-encoded block. 35 | func LoadDHParametersFromPEM(pemBlock []byte) (*DH, error) { 36 | if len(pemBlock) == 0 { 37 | return nil, ErrNoCert 38 | } 39 | bio := C.BIO_new_mem_buf(unsafe.Pointer(&pemBlock[0]), 40 | C.int(len(pemBlock))) 41 | if bio == nil { 42 | return nil, ErrMallocFailure 43 | } 44 | defer C.BIO_free(bio) 45 | 46 | params := C.PEM_read_bio_DHparams(bio, nil, nil, nil) 47 | if params == nil { 48 | return nil, PopError() 49 | } 50 | dhparams := &DH{dh: params} 51 | runtime.SetFinalizer(dhparams, func(dhparams *DH) { 52 | C.DH_free(dhparams.dh) 53 | }) 54 | return dhparams, nil 55 | } 56 | -------------------------------------------------------------------------------- /crypto/init_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build windows 16 | // +build windows 17 | 18 | package crypto 19 | 20 | /* 21 | #include 22 | #include 23 | #include 24 | 25 | CRITICAL_SECTION* goopenssl_locks; 26 | 27 | int go_init_locks() { 28 | int nlock; 29 | int locks_needed = CRYPTO_num_locks(); 30 | 31 | goopenssl_locks = (CRITICAL_SECTION*)malloc( 32 | sizeof(*goopenssl_locks) * locks_needed); 33 | if (!goopenssl_locks) { 34 | return ENOMEM; 35 | } 36 | for (nlock = 0; nlock < locks_needed; ++nlock) { 37 | InitializeCriticalSection(&goopenssl_locks[nlock]); 38 | } 39 | 40 | return 0; 41 | } 42 | 43 | void go_thread_locking_callback(int mode, int n, const char *file, 44 | int line) { 45 | if (mode & CRYPTO_LOCK) { 46 | EnterCriticalSection(&goopenssl_locks[n]); 47 | } else { 48 | LeaveCriticalSection(&goopenssl_locks[n]); 49 | } 50 | } 51 | 52 | unsigned long go_thread_id_callback(void) { 53 | return (unsigned long)GetCurrentThreadId(); 54 | } 55 | */ 56 | import "C" 57 | -------------------------------------------------------------------------------- /utils/errors.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2014 Space Monkey, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package utils 16 | 17 | import ( 18 | "fmt" 19 | "strings" 20 | ) 21 | 22 | // ErrorGroup collates errors. 23 | type ErrorGroup struct { 24 | Errors []error 25 | } 26 | 27 | // Add adds an error to an existing error group. 28 | func (e *ErrorGroup) Add(err error) { 29 | if err != nil { 30 | e.Errors = append(e.Errors, err) 31 | } 32 | } 33 | 34 | // Finalize returns an error corresponding to the ErrorGroup state. If there's 35 | // no errors in the group, finalize returns nil. If there's only one error, 36 | // Finalize returns that error. Otherwise, Finalize will make a new error 37 | // consisting of the messages from the constituent errors. 38 | func (e *ErrorGroup) Finalize() error { 39 | if len(e.Errors) == 0 { 40 | return nil 41 | } 42 | 43 | if len(e.Errors) == 1 { 44 | return e.Errors[0] 45 | } 46 | 47 | msgs := make([]string, 0, len(e.Errors)) 48 | for _, err := range e.Errors { 49 | msgs = append(msgs, err.Error()) 50 | } 51 | 52 | return fmt.Errorf("errors: %s", strings.Join(msgs, "\n")) 53 | } 54 | -------------------------------------------------------------------------------- /crypto/digest.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package crypto 16 | 17 | // #include "shim.h" 18 | import "C" 19 | 20 | import ( 21 | "unsafe" 22 | ) 23 | 24 | // Digest represents and openssl message digest. 25 | type Digest struct { 26 | ptr *C.EVP_MD 27 | } 28 | 29 | func (d *Digest) Ptr() *C.EVP_MD { 30 | return d.ptr 31 | } 32 | 33 | // GetDigestByName returns the Digest with the name or nil and an error if the 34 | // digest was not found. 35 | func GetDigestByName(name string) (*Digest, error) { 36 | cname := C.CString(name) 37 | defer C.free(unsafe.Pointer(cname)) 38 | p := C.X_EVP_get_digestbyname(cname) 39 | if p == nil { 40 | return nil, ErrUnsupportedDigest 41 | } 42 | // we can consider digests to use static mem; don't need to free 43 | return &Digest{ptr: p}, nil 44 | } 45 | 46 | // GetDigestByName returns the Digest with the NID or nil and an error if the 47 | // digest was not found. 48 | func GetDigestByNid(nid NID) (*Digest, error) { 49 | sn, err := Nid2ShortName(nid) 50 | if err != nil { 51 | return nil, err 52 | } 53 | return GetDigestByName(sn) 54 | } 55 | -------------------------------------------------------------------------------- /ctx_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package tongsuogo_test 16 | 17 | import ( 18 | "testing" 19 | "time" 20 | 21 | ts "github.com/tongsuo-project/tongsuo-go-sdk" 22 | ) 23 | 24 | func TestCtxTimeoutOption(t *testing.T) { 25 | t.Parallel() 26 | 27 | ctx, _ := ts.NewCtx() 28 | oldTimeout1 := ctx.GetTimeout() 29 | newTimeout1 := oldTimeout1 + (time.Duration(99) * time.Second) 30 | oldTimeout2 := ctx.SetTimeout(newTimeout1) 31 | newTimeout2 := ctx.GetTimeout() 32 | 33 | if oldTimeout1 != oldTimeout2 { 34 | t.Error("SetTimeout() returns something undocumented") 35 | } 36 | 37 | if newTimeout1 != newTimeout2 { 38 | t.Error("SetTimeout() does not save anything to ctx") 39 | } 40 | } 41 | 42 | func TestCtxSessCacheSizeOption(t *testing.T) { 43 | t.Parallel() 44 | 45 | ctx, _ := ts.NewCtx() 46 | oldSize1 := ctx.SessGetCacheSize() 47 | newSize1 := oldSize1 + 42 48 | oldSize2 := ctx.SessSetCacheSize(newSize1) 49 | newSize2 := ctx.SessGetCacheSize() 50 | 51 | if oldSize1 != oldSize2 { 52 | t.Error("SessSetCacheSize() returns something undocumented") 53 | } 54 | 55 | if newSize1 != newSize2 { 56 | t.Error("SessSetCacheSize() does not save anything to ctx") 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /crypto/init_posix.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build (linux || darwin || solaris) && !windows 16 | // +build linux darwin solaris 17 | // +build !windows 18 | 19 | package crypto 20 | 21 | /* 22 | #include 23 | #include 24 | #include 25 | 26 | pthread_mutex_t* goopenssl_locks; 27 | 28 | int go_init_locks() { 29 | int rc = 0; 30 | int nlock; 31 | int i; 32 | int locks_needed = CRYPTO_num_locks(); 33 | 34 | goopenssl_locks = (pthread_mutex_t*)malloc( 35 | sizeof(pthread_mutex_t) * locks_needed); 36 | if (!goopenssl_locks) { 37 | return ENOMEM; 38 | } 39 | for (nlock = 0; nlock < locks_needed; ++nlock) { 40 | rc = pthread_mutex_init(&goopenssl_locks[nlock], NULL); 41 | if (rc != 0) { 42 | break; 43 | } 44 | } 45 | 46 | if (rc != 0) { 47 | for (i = nlock - 1; i >= 0; --i) { 48 | pthread_mutex_destroy(&goopenssl_locks[i]); 49 | } 50 | free(goopenssl_locks); 51 | goopenssl_locks = NULL; 52 | } 53 | return rc; 54 | } 55 | 56 | void go_thread_locking_callback(int mode, int n, const char *file, 57 | int line) { 58 | if (mode & CRYPTO_LOCK) { 59 | pthread_mutex_lock(&goopenssl_locks[n]); 60 | } else { 61 | pthread_mutex_unlock(&goopenssl_locks[n]); 62 | } 63 | } 64 | 65 | unsigned long go_thread_id_callback(void) { 66 | return (unsigned long)pthread_self(); 67 | } 68 | */ 69 | import "C" 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tongsuo-Go-SDK 2 | 3 | Tongsuo-Go-SDK uses Tongsuo to provide cryptographic primitives and secure transport protocols for 4 | golang applications. 5 | 6 | ## Features 7 | 8 | - Hash algorithms: SM3, MD5, SHA1, SHA256 9 | - Symmetric algorithms: SM4 10 | - SM2 keygen, encryption and decryption 11 | - Digital signature algorithm: SM2withSM3 12 | - Message Authentication Code: HMAC 13 | - Support issuing SM2 certificate 14 | - Secure transport protocols: TLCP, TLSv1.0/1.1/1.2/1.3 15 | 16 | ## Installation 17 | 18 | tongsuo-go-sdk is based on Tongsuo, so we must install Tongsuo firstly. 19 | Build and install Tongsuo from source code is as follows: 20 | 21 | ```bash 22 | git clone https://github.com/Tongsuo-Project/Tongsuo.git 23 | cd Tongsuo 24 | 25 | git checkout 8.3-stable 26 | 27 | ./config --prefix=/opt/tongsuo --libdir=/opt/tongsuo/lib enable-ntls enable-export-sm4 28 | make -j 29 | make install 30 | ``` 31 | 32 | Then install tongsuo-go-sdk: 33 | 34 | ```bash 35 | go get github.com/tongsuo-project/tongsuo-go-sdk 36 | ``` 37 | 38 | ### Run examples 39 | 40 | On Linux: 41 | 42 | ```bash 43 | TONGSUO_HOME=/opt/tongsuo 44 | LD_LIBRARY_PATH=${TONGSUO_HOME}/lib CGO_CFLAGS="-I${TONGSUO_HOME}/include -Wno-deprecated-declarations" CGO_LDFLAGS="-L${TONGSUO_HOME}/lib" go run examples/sm4/main.go 45 | ``` 46 | 47 | On MacOS: 48 | 49 | ```bash 50 | TONGSUO_HOME=/opt/tongsuo 51 | DYLD_LIBRARY_PATH=${TONGSUO_HOME}/lib CGO_CFLAGS="-I${TONGSUO_HOME}/include -Wno-deprecated-declarations" CGO_LDFLAGS="-L${TONGSUO_HOME}/lib" go run examples/sm4/main.go 52 | ``` 53 | 54 | ### Run tests 55 | 56 | On Linux: 57 | 58 | ```bash 59 | TONGSUO_HOME=/opt/tongsuo 60 | LD_LIBRARY_PATH=${TONGSUO_HOME}/lib CGO_CFLAGS="-I${TONGSUO_HOME}/include -Wno-deprecated-declarations" CGO_LDFLAGS="-L${TONGSUO_HOME}/lib" go test ./... 61 | ``` 62 | 63 | On MacOS: 64 | 65 | ```bash 66 | TONGSUO_HOME=/opt/tongsuo 67 | DYLD_LIBRARY_PATH=${TONGSUO_HOME}/lib CGO_CFLAGS="-I${TONGSUO_HOME}/include -Wno-deprecated-declarations" CGO_LDFLAGS="-L${TONGSUO_HOME}/lib" go test ./... 68 | ``` 69 | -------------------------------------------------------------------------------- /crypto/dh.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package crypto 16 | 17 | // #include "shim.h" 18 | import "C" 19 | 20 | // DeriveSharedSecret derives a shared secret using a private key and a peer's 21 | // public key. 22 | // The specific algorithm that is used depends on the types of the 23 | // keys, but it is most commonly a variant of Diffie-Hellman. 24 | func DeriveSharedSecret(private PrivateKey, public PublicKey) ([]byte, error) { 25 | // Create context for the shared secret derivation 26 | dhCtx := C.EVP_PKEY_CTX_new(private.EvpPKey(), nil) 27 | if dhCtx == nil { 28 | return nil, PopError() 29 | } 30 | defer C.EVP_PKEY_CTX_free(dhCtx) 31 | 32 | // Initialize the context 33 | if int(C.EVP_PKEY_derive_init(dhCtx)) != 1 { 34 | return nil, PopError() 35 | } 36 | 37 | // Provide the peer's public key 38 | if int(C.EVP_PKEY_derive_set_peer(dhCtx, public.EvpPKey())) != 1 { 39 | return nil, PopError() 40 | } 41 | 42 | // Determine how large of a buffer we need for the shared secret 43 | var buffLen C.size_t 44 | if int(C.EVP_PKEY_derive(dhCtx, nil, &buffLen)) != 1 { 45 | return nil, PopError() 46 | } 47 | 48 | // Allocate a buffer 49 | buffer := C.X_OPENSSL_malloc(buffLen) 50 | if buffer == nil { 51 | return nil, ErrMallocFailure 52 | } 53 | defer C.X_OPENSSL_free(buffer) 54 | 55 | // Derive the shared secret 56 | if int(C.EVP_PKEY_derive(dhCtx, (*C.uchar)(buffer), &buffLen)) != 1 { 57 | return nil, PopError() 58 | } 59 | 60 | secret := C.GoBytes(buffer, C.int(buffLen)) 61 | return secret, nil 62 | } 63 | -------------------------------------------------------------------------------- /utils/future.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2014 Space Monkey, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package utils 16 | 17 | import ( 18 | "sync" 19 | ) 20 | 21 | // Future is a type that is essentially the inverse of a channel. With a 22 | // channel, you have multiple senders and one receiver. With a future, you can 23 | // have multiple receivers and one sender. Additionally, a future protects 24 | // against double-sends. Since this is usually used for returning function 25 | // results, we also capture and return error values as well. Use NewFuture 26 | // to initialize. 27 | type Future struct { 28 | mutex *sync.Mutex 29 | cond *sync.Cond 30 | received bool 31 | val interface{} 32 | err error 33 | } 34 | 35 | // NewFuture returns an initialized and ready Future. 36 | func NewFuture() *Future { 37 | mutex := &sync.Mutex{} 38 | 39 | return &Future{ 40 | mutex: mutex, 41 | cond: sync.NewCond(mutex), 42 | received: false, 43 | val: nil, 44 | err: nil, 45 | } 46 | } 47 | 48 | // Get blocks until the Future has a value set. 49 | func (f *Future) Get() (interface{}, error) { 50 | f.mutex.Lock() 51 | defer f.mutex.Unlock() 52 | 53 | for { 54 | if f.received { 55 | return f.val, f.err 56 | } 57 | 58 | f.cond.Wait() 59 | } 60 | } 61 | 62 | // Fired returns whether or not a value has been set. If Fired is true, Get 63 | // won't block. 64 | func (f *Future) Fired() bool { 65 | f.mutex.Lock() 66 | defer f.mutex.Unlock() 67 | 68 | return f.received 69 | } 70 | 71 | // Set provides the value to present and future Get calls. If Set has already 72 | // been called, this is a no-op. 73 | func (f *Future) Set(val interface{}, err error) { 74 | f.mutex.Lock() 75 | defer f.mutex.Unlock() 76 | 77 | if f.received { 78 | return 79 | } 80 | 81 | f.received = true 82 | f.val = val 83 | f.err = err 84 | f.cond.Broadcast() 85 | } 86 | -------------------------------------------------------------------------------- /crypto/hmac_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package crypto_test 16 | 17 | import ( 18 | "crypto/hmac" 19 | "crypto/sha256" 20 | "encoding/hex" 21 | "testing" 22 | 23 | "github.com/tongsuo-project/tongsuo-go-sdk/crypto" 24 | ) 25 | 26 | func TestSHA256HMAC(t *testing.T) { 27 | t.Parallel() 28 | 29 | key := []byte("d741787cc61851af045ccd37") 30 | data := []byte("5912EEFD-59EC-43E3-ADB8-D5325AEC3271") 31 | 32 | tsHmac, err := crypto.NewHMAC(key, crypto.DigestSHA256) 33 | if err != nil { 34 | t.Fatalf("Unable to create new HMAC: %s", err) 35 | } 36 | 37 | if _, err := tsHmac.Write(data); err != nil { 38 | t.Fatalf("Unable to write data into HMAC: %s", err) 39 | } 40 | 41 | var actualHMACBytes []byte 42 | 43 | if actualHMACBytes, err = tsHmac.Final(); err != nil { 44 | t.Fatalf("Error while finalizing HMAC: %s", err) 45 | } 46 | 47 | actualString := hex.EncodeToString(actualHMACBytes) 48 | 49 | // generate HMAC with built-in crypto lib 50 | mac := hmac.New(sha256.New, key) 51 | mac.Write(data) 52 | expectedString := hex.EncodeToString(mac.Sum(nil)) 53 | 54 | if expectedString != actualString { 55 | t.Errorf("HMAC was incorrect: expected=%s, actual=%s", expectedString, actualString) 56 | } 57 | } 58 | 59 | func BenchmarkSHA256HMAC(b *testing.B) { 60 | key := []byte("d741787cc61851af045ccd37") 61 | data := []byte("5912EEFD-59EC-43E3-ADB8-D5325AEC3271") 62 | 63 | tsHmac, err := crypto.NewHMAC(key, crypto.DigestSHA256) 64 | if err != nil { 65 | b.Fatalf("Unable to create new HMAC: %s", err) 66 | } 67 | 68 | b.ResetTimer() 69 | 70 | for i := 0; i < b.N; i++ { 71 | if _, err := tsHmac.Write(data); err != nil { 72 | b.Fatalf("Unable to write data into HMAC: %s", err) 73 | } 74 | 75 | var err error 76 | if _, err = tsHmac.Final(); err != nil { 77 | b.Fatalf("Error while finalizing HMAC: %s", err) 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /http.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package tongsuogo 16 | 17 | import ( 18 | "fmt" 19 | "net/http" 20 | "time" 21 | ) 22 | 23 | const defaultReadHeaderTimeout = 120 24 | 25 | // ListenAndServeTLS will take an http.Handler and serve it using OpenSSL over 26 | // the given tcp address, configured to use the provided cert and key files. 27 | func ListenAndServeTLS(addr string, certFile string, keyFile string, 28 | handler http.Handler, 29 | ) error { 30 | return ServerListenAndServeTLS( 31 | &http.Server{Addr: addr, Handler: handler, ReadHeaderTimeout: defaultReadHeaderTimeout * time.Second}, 32 | certFile, keyFile) 33 | } 34 | 35 | // ServerListenAndServeTLS will take an http.Server and serve it using OpenSSL 36 | // configured to use the provided cert and key files. 37 | func ServerListenAndServeTLS(srv *http.Server, 38 | certFile, keyFile string, 39 | ) error { 40 | addr := srv.Addr 41 | if addr == "" { 42 | addr = ":https" 43 | } 44 | 45 | ctx, err := NewCtxFromFiles(certFile, keyFile) 46 | if err != nil { 47 | return err 48 | } 49 | 50 | l, err := Listen("tcp", addr, ctx) 51 | if err != nil { 52 | return err 53 | } 54 | 55 | err = srv.Serve(l) 56 | if err != nil { 57 | return fmt.Errorf("failed to serve tls: %w", err) 58 | } 59 | 60 | return nil 61 | } 62 | 63 | // TODO: http client integration 64 | // holy crap, getting this integrated nicely with the Go stdlib HTTP client 65 | // stack so that it does proxying, connection pooling, and most importantly 66 | // hostname verification is really hard. So much stuff is hardcoded to just use 67 | // the built-in TLS lib. I think to get this to work either some crazy 68 | // hacktackery beyond me, an almost straight up fork of the HTTP client, or 69 | // serious stdlib internal refactoring is necessary. 70 | // even more so, good luck getting openssl to use the operating system default 71 | // root certificates if the user doesn't provide any. sadlol 72 | // NOTE: if you're going to try and write your own round tripper, at least use 73 | // openssl.Dial, or equivalent logic 74 | -------------------------------------------------------------------------------- /crypto/hmac.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package crypto 16 | 17 | // #include "shim.h" 18 | import "C" 19 | 20 | import ( 21 | "fmt" 22 | "runtime" 23 | "unsafe" 24 | ) 25 | 26 | type HMAC struct { 27 | ctx *C.HMAC_CTX 28 | engine *Engine 29 | md *C.EVP_MD 30 | } 31 | 32 | func NewHMAC(key []byte, digest DigestAlgo) (*HMAC, error) { 33 | return NewHMACWithEngine(key, digest, nil) 34 | } 35 | 36 | func NewHMACWithEngine(key []byte, digest DigestAlgo, e *Engine) (*HMAC, error) { 37 | var md *C.EVP_MD = getDigestFunction(digest) 38 | hmac := &HMAC{ctx: nil, engine: e, md: md} 39 | hmac.ctx = C.X_HMAC_CTX_new() 40 | if hmac.ctx == nil { 41 | return nil, ErrMallocFailure 42 | } 43 | 44 | var cEngine *C.ENGINE 45 | if e != nil { 46 | cEngine = e.Engine() 47 | } 48 | if rc := C.X_HMAC_Init_ex(hmac.ctx, unsafe.Pointer(&key[0]), C.int(len(key)), md, cEngine); rc != 1 { 49 | C.X_HMAC_CTX_free(hmac.ctx) 50 | return nil, fmt.Errorf("failed to init HMAC_CTX: %w", PopError()) 51 | } 52 | 53 | runtime.SetFinalizer(hmac, func(h *HMAC) { h.Close() }) 54 | return hmac, nil 55 | } 56 | 57 | func (h *HMAC) Close() { 58 | C.X_HMAC_CTX_free(h.ctx) 59 | } 60 | 61 | func (h *HMAC) Write(data []byte) (int, error) { 62 | if len(data) == 0 { 63 | return 0, nil 64 | } 65 | if C.X_HMAC_Update(h.ctx, (*C.uchar)(unsafe.Pointer(&data[0])), C.size_t(len(data))) != 1 { 66 | return 0, fmt.Errorf("failed to update HMAC: %w", PopError()) 67 | } 68 | return len(data), nil 69 | } 70 | 71 | func (h *HMAC) Reset() error { 72 | if C.X_HMAC_Init_ex(h.ctx, nil, 0, nil, nil) != 1 { 73 | return fmt.Errorf("failed to reset HMAC_CTX: %w", PopError()) 74 | } 75 | return nil 76 | } 77 | 78 | func (h *HMAC) Final() ([]byte, error) { 79 | mdLength := C.X_EVP_MD_size(h.md) 80 | result := make([]byte, mdLength) 81 | if rc := C.X_HMAC_Final(h.ctx, (*C.uchar)(unsafe.Pointer(&result[0])), 82 | (*C.uint)(unsafe.Pointer(&mdLength))); rc != 1 { 83 | return nil, fmt.Errorf("failed to final HMAC: %w", PopError()) 84 | } 85 | return result, h.Reset() 86 | } 87 | -------------------------------------------------------------------------------- /crypto/sha256/sha256.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package sha256 16 | 17 | // #include "../shim.h" 18 | import "C" 19 | 20 | import ( 21 | "fmt" 22 | "runtime" 23 | "unsafe" 24 | 25 | "github.com/tongsuo-project/tongsuo-go-sdk/crypto" 26 | ) 27 | 28 | type SHA256 struct { 29 | ctx *C.EVP_MD_CTX 30 | engine *crypto.Engine 31 | } 32 | 33 | func New() (*SHA256, error) { return NewWithEngine(nil) } 34 | 35 | func NewWithEngine(e *crypto.Engine) (*SHA256, error) { 36 | hash := &SHA256{ctx: nil, engine: e} 37 | hash.ctx = C.X_EVP_MD_CTX_new() 38 | if hash.ctx == nil { 39 | return nil, fmt.Errorf("failed to create md ctx %w", crypto.ErrMallocFailure) 40 | } 41 | runtime.SetFinalizer(hash, func(hash *SHA256) { hash.Close() }) 42 | if err := hash.Reset(); err != nil { 43 | return nil, err 44 | } 45 | 46 | return hash, nil 47 | } 48 | 49 | func (s *SHA256) Close() { 50 | if s.ctx != nil { 51 | C.X_EVP_MD_CTX_free(s.ctx) 52 | s.ctx = nil 53 | } 54 | } 55 | 56 | func (s *SHA256) Reset() error { 57 | if C.X_EVP_DigestInit_ex(s.ctx, C.X_EVP_sha256(), (*C.ENGINE)(s.engine.Engine())) != 1 { 58 | return fmt.Errorf("failed to init digest ctx: %w", crypto.PopError()) 59 | } 60 | 61 | return nil 62 | } 63 | 64 | func (s *SHA256) Write(data []byte) (int, error) { 65 | if len(data) == 0 { 66 | return 0, nil 67 | } 68 | if C.X_EVP_DigestUpdate(s.ctx, unsafe.Pointer(&data[0]), C.size_t(len(data))) != 1 { 69 | return 0, fmt.Errorf("failed to update digest: %w", crypto.PopError()) 70 | } 71 | 72 | return len(data), nil 73 | } 74 | 75 | func (s *SHA256) Sum() ([32]byte, error) { 76 | var result [32]byte 77 | 78 | if C.X_EVP_DigestFinal_ex(s.ctx, (*C.uchar)(unsafe.Pointer(&result[0])), nil) != 1 { 79 | return result, fmt.Errorf("failed to finalize digest: %w", crypto.PopError()) 80 | } 81 | 82 | return result, s.Reset() 83 | } 84 | 85 | func Sum(data []byte) ([32]byte, error) { 86 | hash, err := New() 87 | if err != nil { 88 | return [32]byte{}, err 89 | } 90 | 91 | defer hash.Close() 92 | 93 | if _, err := hash.Write(data); err != nil { 94 | return [32]byte{}, err 95 | } 96 | 97 | return hash.Sum() 98 | } 99 | -------------------------------------------------------------------------------- /crypto/sha1/sha1.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package sha1 16 | 17 | // #include "../shim.h" 18 | import "C" 19 | 20 | import ( 21 | "fmt" 22 | "runtime" 23 | "unsafe" 24 | 25 | "github.com/tongsuo-project/tongsuo-go-sdk/crypto" 26 | ) 27 | 28 | const MDSize = 20 29 | 30 | type SHA1 struct { 31 | ctx *C.EVP_MD_CTX 32 | engine *crypto.Engine 33 | } 34 | 35 | func New() (*SHA1, error) { return NewWithEngine(nil) } 36 | 37 | func NewWithEngine(e *crypto.Engine) (*SHA1, error) { 38 | hash := &SHA1{ctx: nil, engine: e} 39 | hash.ctx = C.X_EVP_MD_CTX_new() 40 | if hash.ctx == nil { 41 | return nil, fmt.Errorf("failed to create md ctx: %w", crypto.ErrMallocFailure) 42 | } 43 | runtime.SetFinalizer(hash, func(hash *SHA1) { hash.Close() }) 44 | if err := hash.Reset(); err != nil { 45 | return nil, err 46 | } 47 | 48 | return hash, nil 49 | } 50 | 51 | func (s *SHA1) Close() { 52 | if s.ctx != nil { 53 | C.X_EVP_MD_CTX_free(s.ctx) 54 | s.ctx = nil 55 | } 56 | } 57 | 58 | func (s *SHA1) Reset() error { 59 | if C.X_EVP_DigestInit_ex(s.ctx, C.X_EVP_sha1(), (*C.ENGINE)(s.engine.Engine())) != 1 { 60 | return fmt.Errorf("failed to init digest ctx %w", crypto.PopError()) 61 | } 62 | 63 | return nil 64 | } 65 | 66 | func (s *SHA1) Write(data []byte) (int, error) { 67 | if len(data) == 0 { 68 | return 0, nil 69 | } 70 | if C.X_EVP_DigestUpdate(s.ctx, unsafe.Pointer(&data[0]), C.size_t(len(data))) != 1 { 71 | return 0, fmt.Errorf("failed to update digest: %w", crypto.PopError()) 72 | } 73 | 74 | return len(data), nil 75 | } 76 | 77 | func (s *SHA1) Sum() ([MDSize]byte, error) { 78 | var result [MDSize]byte 79 | 80 | if C.X_EVP_DigestFinal_ex(s.ctx, (*C.uchar)(unsafe.Pointer(&result[0])), nil) != 1 { 81 | return result, fmt.Errorf("failed to finalize digest: %w", crypto.PopError()) 82 | } 83 | 84 | return result, s.Reset() 85 | } 86 | 87 | func Sum(data []byte) ([MDSize]byte, error) { 88 | hash, err := New() 89 | if err != nil { 90 | return [MDSize]byte{}, err 91 | } 92 | 93 | defer hash.Close() 94 | 95 | if _, err := hash.Write(data); err != nil { 96 | return [MDSize]byte{}, err 97 | } 98 | return hash.Sum() 99 | } 100 | -------------------------------------------------------------------------------- /crypto/sm3/sm3.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Tongsuo Project Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License 2.0 (the "License"). You may not use 4 | // this file except in compliance with the License. You can obtain a copy 5 | // in the file LICENSE in the source distribution or at 6 | // https://github.com/Tongsuo-Project/tongsuo-go-sdk/blob/main/LICENSE 7 | 8 | package sm3 9 | 10 | // #include "../shim.h" 11 | import "C" 12 | 13 | import ( 14 | "fmt" 15 | "hash" 16 | "runtime" 17 | "unsafe" 18 | 19 | "github.com/tongsuo-project/tongsuo-go-sdk/crypto" 20 | ) 21 | 22 | const ( 23 | MDSize = 32 24 | sm3Cblock = 64 25 | ) 26 | 27 | var _ hash.Hash = new(SM3) 28 | 29 | type SM3 struct { 30 | ctx *C.EVP_MD_CTX 31 | engine *crypto.Engine 32 | } 33 | 34 | func New() (*SM3, error) { return NewWithEngine(nil) } 35 | 36 | func NewWithEngine(e *crypto.Engine) (*SM3, error) { 37 | hash, err := newWithEngine(e) 38 | if err != nil { 39 | return nil, err 40 | } 41 | hash.Reset() 42 | 43 | return hash, nil 44 | } 45 | 46 | func newWithEngine(e *crypto.Engine) (*SM3, error) { 47 | hash := &SM3{ctx: nil, engine: e} 48 | hash.ctx = C.X_EVP_MD_CTX_new() 49 | if hash.ctx == nil { 50 | return nil, fmt.Errorf("failed to create md ctx: %w", crypto.ErrMallocFailure) 51 | } 52 | runtime.SetFinalizer(hash, func(hash *SM3) { hash.Close() }) 53 | 54 | return hash, nil 55 | } 56 | 57 | func (s *SM3) BlockSize() int { 58 | return sm3Cblock 59 | } 60 | 61 | func (s *SM3) Size() int { 62 | return MDSize 63 | } 64 | 65 | func (s *SM3) Close() { 66 | if s.ctx != nil { 67 | C.X_EVP_MD_CTX_free(s.ctx) 68 | s.ctx = nil 69 | } 70 | } 71 | 72 | func (s *SM3) Reset() { 73 | C.X_EVP_DigestInit_ex(s.ctx, C.EVP_sm3(), (*C.ENGINE)(s.engine.Engine())) 74 | } 75 | 76 | func (s *SM3) Write(data []byte) (int, error) { 77 | if len(data) == 0 { 78 | return 0, nil 79 | } 80 | if C.X_EVP_DigestUpdate(s.ctx, unsafe.Pointer(&data[0]), C.size_t(len(data))) != 1 { 81 | return 0, fmt.Errorf("failed to update digest: %w", crypto.PopError()) 82 | } 83 | return len(data), nil 84 | } 85 | 86 | func (s *SM3) Sum(in []byte) []byte { 87 | hash, err := NewWithEngine(s.engine) 88 | if err != nil { 89 | panic("NewSM3 fail " + err.Error()) 90 | } 91 | 92 | if C.X_EVP_MD_CTX_copy_ex(hash.ctx, s.ctx) == 0 { 93 | panic("NewSM3 X_EVP_MD_CTX_copy_ex fail") 94 | } 95 | 96 | result := hash.checkSum() 97 | return append(in, result[:]...) 98 | } 99 | 100 | func (s *SM3) checkSum() [MDSize]byte { 101 | var result [MDSize]byte 102 | 103 | C.X_EVP_DigestFinal_ex(s.ctx, (*C.uchar)(unsafe.Pointer(&result[0])), nil) 104 | 105 | return result 106 | } 107 | 108 | func Sum(data []byte) [MDSize]byte { 109 | var result [MDSize]byte 110 | 111 | C.X_EVP_Digest(unsafe.Pointer(&data[0]), C.size_t(len(data)), (*C.uchar)(unsafe.Pointer(&result[0])), nil, 112 | C.EVP_sm3(), nil) 113 | 114 | return result 115 | } 116 | -------------------------------------------------------------------------------- /crypto/init.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Tongsuo Project Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License 2.0 (the "License"). You may not use 4 | // this file except in compliance with the License. You can obtain a copy 5 | // in the file LICENSE in the source distribution or at 6 | // https://github.com/Tongsuo-Project/tongsuo-go-sdk/blob/main/LICENSE 7 | 8 | package crypto 9 | 10 | // #include "shim.h" 11 | import "C" 12 | 13 | import ( 14 | "errors" 15 | "fmt" 16 | "strings" 17 | ) 18 | 19 | var ( 20 | ErrMallocFailure = errors.New("malloc failure") 21 | ErrNilParameter = errors.New("nil parameter") 22 | ErrNoCipher = errors.New("no cipher") 23 | ErrNoVersion = errors.New("no version") 24 | ErrUnexpectedEOF = errors.New("unexpected EOF") 25 | ErrNoPeerCert = errors.New("no peer certificate") 26 | ErrShutdown = errors.New("shutdown") 27 | ErrNoSession = errors.New("no session") 28 | ErrSessionLength = errors.New("session length error") 29 | ErrEmptySession = errors.New("empty session") 30 | ErrNoALPN = errors.New("no ALPN negotiated") 31 | ErrWrongKeyType = errors.New("wrong key type") 32 | ErrUnknownTLSVersion = errors.New("unknown TLS version") 33 | ErrNoCert = errors.New("no certificate") 34 | ErrNoKey = errors.New("no key") 35 | ErrUnsupportedMode = errors.New("unsupported cipher mode") 36 | ErrPartialWrite = errors.New("partial write") 37 | ErrUnsupportedDigest = errors.New("unsupported digest") 38 | ErrInvalidNid = errors.New("invalid NID") 39 | ErrEmptyExtensionValue = errors.New("empty extension value") 40 | ErrNoPubKey = errors.New("no public key") 41 | ErrCipherNotFound = errors.New("cipher not found") 42 | ErrBadKeySize = errors.New("bad key size") 43 | ErrBadIvSize = errors.New("bad IV size") 44 | ErrUknownBlockSize = errors.New("unknown block size") 45 | ErrNoEngine = errors.New("engine not found") 46 | ErrMatchFailed = errors.New("match failed") 47 | ErrInputInvalid = errors.New("input invalid") 48 | ErrInternalError = errors.New("internal error") 49 | ErrEmptyKey = errors.New("empty key") 50 | ErrNoData = errors.New("no data") 51 | ErrInvalidKeySize = errors.New("invalid key size") 52 | ) 53 | 54 | func init() { 55 | if rc := C.X_tscrypto_init(); rc != 0 { 56 | panic(fmt.Sprintf("X_tscrypto_init failed with %d", rc)) 57 | } 58 | } 59 | 60 | // PopError needs to run in the same OS thread as the operation 61 | // that caused the possible error 62 | func PopError() error { 63 | var errs []string 64 | for { 65 | err := C.ERR_get_error() 66 | if err == 0 { 67 | break 68 | } 69 | errs = append(errs, fmt.Sprintf("%s:%s:%s", 70 | C.GoString(C.ERR_lib_error_string(err)), 71 | C.GoString(C.ERR_func_error_string(err)), 72 | C.GoString(C.ERR_reason_error_string(err)))) 73 | } 74 | 75 | return errors.New("error string: " + strings.Join(errs, "\n")) 76 | } 77 | -------------------------------------------------------------------------------- /crypto/sha256/sha256_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package sha256_test 16 | 17 | import ( 18 | "crypto/rand" 19 | "crypto/sha256" 20 | "io" 21 | "testing" 22 | 23 | tsSHA256 "github.com/tongsuo-project/tongsuo-go-sdk/crypto/sha256" 24 | ) 25 | 26 | func Test(t *testing.T) { 27 | t.Parallel() 28 | 29 | for i := 0; i < 100; i++ { 30 | buf := make([]byte, 10*1024-i) 31 | if _, err := io.ReadFull(rand.Reader, buf); err != nil { 32 | t.Fatal(err) 33 | } 34 | 35 | expected := sha256.Sum256(buf) 36 | 37 | got, err := tsSHA256.Sum(buf) 38 | if err != nil { 39 | t.Fatal(err) 40 | } 41 | 42 | if expected != got { 43 | t.Fatalf("exp:%x got:%x", expected, got) 44 | } 45 | } 46 | } 47 | 48 | func TestWriter(t *testing.T) { 49 | t.Parallel() 50 | 51 | ohash, err := tsSHA256.New() 52 | if err != nil { 53 | t.Fatal(err) 54 | } 55 | 56 | hash := sha256.New() 57 | 58 | for i := 0; i < 100; i++ { 59 | if err := ohash.Reset(); err != nil { 60 | t.Fatal(err) 61 | } 62 | 63 | hash.Reset() 64 | 65 | buf := make([]byte, 10*1024-i) 66 | if _, err := io.ReadFull(rand.Reader, buf); err != nil { 67 | t.Fatal(err) 68 | } 69 | 70 | if _, err := ohash.Write(buf); err != nil { 71 | t.Fatal(err) 72 | } 73 | 74 | if _, err := hash.Write(buf); err != nil { 75 | t.Fatal(err) 76 | } 77 | 78 | var got, exp [32]byte 79 | 80 | hash.Sum(exp[:0]) 81 | 82 | got, err := ohash.Sum() 83 | if err != nil { 84 | t.Fatal(err) 85 | } 86 | 87 | if got != exp { 88 | t.Fatalf("exp:%x got:%x", exp, got) 89 | } 90 | } 91 | } 92 | 93 | func benchmark(b *testing.B, length int64, fn func([]byte)) { 94 | b.Helper() 95 | 96 | buf := make([]byte, length) 97 | if _, err := io.ReadFull(rand.Reader, buf); err != nil { 98 | b.Fatal(err) 99 | } 100 | 101 | b.SetBytes(length) 102 | b.ResetTimer() 103 | 104 | for i := 0; i < b.N; i++ { 105 | fn(buf) 106 | } 107 | } 108 | 109 | func BenchmarkLarge_openssl(b *testing.B) { 110 | benchmark(b, 1024*1024, func(buf []byte) { _, _ = tsSHA256.Sum(buf) }) 111 | } 112 | 113 | func BenchmarkLarge_stdlib(b *testing.B) { 114 | benchmark(b, 1024*1024, func(buf []byte) { sha256.Sum256(buf) }) 115 | } 116 | 117 | func BenchmarkSmall_openssl(b *testing.B) { 118 | benchmark(b, 1, func(buf []byte) { _, _ = tsSHA256.Sum(buf) }) 119 | } 120 | 121 | func BenchmarkSmall_stdlib(b *testing.B) { 122 | benchmark(b, 1, func(buf []byte) { sha256.Sum256(buf) }) 123 | } 124 | -------------------------------------------------------------------------------- /crypto/sha1/sha1_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package sha1_test 16 | 17 | import ( 18 | "crypto/rand" 19 | "crypto/sha1" 20 | "io" 21 | "testing" 22 | 23 | tsSHA1 "github.com/tongsuo-project/tongsuo-go-sdk/crypto/sha1" 24 | ) 25 | 26 | func TestSHA1(t *testing.T) { 27 | t.Parallel() 28 | 29 | for i := 0; i < 100; i++ { 30 | buf := make([]byte, 10*1024-i) 31 | if _, err := io.ReadFull(rand.Reader, buf); err != nil { 32 | t.Fatal(err) 33 | } 34 | 35 | expected := sha1.Sum(buf) 36 | 37 | got, err := tsSHA1.Sum(buf) 38 | if err != nil { 39 | t.Fatal(err) 40 | } 41 | 42 | if expected != got { 43 | t.Fatalf("exp:%x got:%x", expected, got) 44 | } 45 | } 46 | } 47 | 48 | func TestSHA1Writer(t *testing.T) { 49 | t.Parallel() 50 | 51 | ohash, err := tsSHA1.New() 52 | if err != nil { 53 | t.Fatal(err) 54 | } 55 | 56 | hash := sha1.New() 57 | 58 | for i := 0; i < 100; i++ { 59 | if err := ohash.Reset(); err != nil { 60 | t.Fatal(err) 61 | } 62 | 63 | hash.Reset() 64 | 65 | buf := make([]byte, 10*1024-i) 66 | 67 | if _, err := io.ReadFull(rand.Reader, buf); err != nil { 68 | t.Fatal(err) 69 | } 70 | 71 | if _, err := ohash.Write(buf); err != nil { 72 | t.Fatal(err) 73 | } 74 | 75 | if _, err := hash.Write(buf); err != nil { 76 | t.Fatal(err) 77 | } 78 | 79 | var got, exp [20]byte 80 | 81 | hash.Sum(exp[:0]) 82 | 83 | got, err := ohash.Sum() 84 | if err != nil { 85 | t.Fatal(err) 86 | } 87 | 88 | if got != exp { 89 | t.Fatalf("exp:%x got:%x", exp, got) 90 | } 91 | } 92 | } 93 | 94 | type shafunc func([]byte) 95 | 96 | func benchmarkSHA1(b *testing.B, length int64, fn shafunc) { 97 | b.Helper() 98 | 99 | buf := make([]byte, length) 100 | if _, err := io.ReadFull(rand.Reader, buf); err != nil { 101 | b.Fatal(err) 102 | } 103 | 104 | b.SetBytes(length) 105 | b.ResetTimer() 106 | 107 | for i := 0; i < b.N; i++ { 108 | fn(buf) 109 | } 110 | } 111 | 112 | func BenchmarkSHA1Large_openssl(b *testing.B) { 113 | benchmarkSHA1(b, 1024*1024, func(buf []byte) { _, _ = tsSHA1.Sum(buf) }) 114 | } 115 | 116 | func BenchmarkSHA1Large_stdlib(b *testing.B) { 117 | benchmarkSHA1(b, 1024*1024, func(buf []byte) { sha1.Sum(buf) }) 118 | } 119 | 120 | func BenchmarkSHA1Small_openssl(b *testing.B) { 121 | benchmarkSHA1(b, 1, func(buf []byte) { _, _ = tsSHA1.Sum(buf) }) 122 | } 123 | 124 | func BenchmarkSHA1Small_stdlib(b *testing.B) { 125 | benchmarkSHA1(b, 1, func(buf []byte) { sha1.Sum(buf) }) 126 | } 127 | -------------------------------------------------------------------------------- /crypto/md5/md5.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package md5 16 | 17 | // #include "../shim.h" 18 | import "C" 19 | 20 | import ( 21 | "fmt" 22 | "hash" 23 | "runtime" 24 | "unsafe" 25 | 26 | "github.com/tongsuo-project/tongsuo-go-sdk/crypto" 27 | ) 28 | 29 | const ( 30 | MDSize = 16 31 | md5Cblock = 64 32 | ) 33 | 34 | var _ hash.Hash = new(MD5) 35 | 36 | type MD5 struct { 37 | ctx *C.EVP_MD_CTX 38 | engine *crypto.Engine 39 | } 40 | 41 | func New() (*MD5, error) { return NewWithEngine(nil) } 42 | 43 | func NewWithEngine(e *crypto.Engine) (*MD5, error) { 44 | hash, err := newMD5WithEngine(e) 45 | if err != nil { 46 | return nil, err 47 | } 48 | 49 | hash.Reset() 50 | 51 | return hash, nil 52 | } 53 | 54 | func newMD5WithEngine(e *crypto.Engine) (*MD5, error) { 55 | hash := &MD5{ctx: nil, engine: e} 56 | hash.ctx = C.X_EVP_MD_CTX_new() 57 | if hash.ctx == nil { 58 | return nil, fmt.Errorf("failed to create md ctx: %w", crypto.ErrMallocFailure) 59 | } 60 | 61 | runtime.SetFinalizer(hash, func(hash *MD5) { hash.Close() }) 62 | 63 | return hash, nil 64 | } 65 | 66 | func (s *MD5) BlockSize() int { 67 | return md5Cblock 68 | } 69 | 70 | func (s *MD5) Size() int { 71 | return MDSize 72 | } 73 | 74 | func (s *MD5) Close() { 75 | if s.ctx != nil { 76 | C.X_EVP_MD_CTX_free(s.ctx) 77 | s.ctx = nil 78 | } 79 | } 80 | 81 | func (s *MD5) Reset() { 82 | C.X_EVP_DigestInit_ex(s.ctx, C.X_EVP_md5(), (*C.ENGINE)(s.engine.Engine())) 83 | } 84 | 85 | func (s *MD5) Write(data []byte) (int, error) { 86 | if len(data) == 0 { 87 | return 0, nil 88 | } 89 | 90 | if C.X_EVP_DigestUpdate(s.ctx, unsafe.Pointer(&data[0]), C.size_t(len(data))) != 1 { 91 | return 0, fmt.Errorf("failed to update digest: %w", crypto.PopError()) 92 | } 93 | 94 | return len(data), nil 95 | } 96 | 97 | func (s *MD5) Sum(in []byte) []byte { 98 | hash, err := NewWithEngine(s.engine) 99 | if err != nil { 100 | panic("New fail " + err.Error()) 101 | } 102 | 103 | if C.X_EVP_MD_CTX_copy_ex(hash.ctx, s.ctx) == 0 { 104 | panic("New X_EVP_MD_CTX_copy_ex fail") 105 | } 106 | 107 | result := hash.checkSum() 108 | return append(in, result[:]...) 109 | } 110 | 111 | func (s *MD5) checkSum() [MDSize]byte { 112 | var result [MDSize]byte 113 | 114 | C.X_EVP_DigestFinal_ex(s.ctx, (*C.uchar)(unsafe.Pointer(&result[0])), nil) 115 | 116 | return result 117 | } 118 | 119 | func Sum(data []byte) [MDSize]byte { 120 | var result [MDSize]byte 121 | 122 | C.X_EVP_Digest(unsafe.Pointer(&data[0]), C.size_t(len(data)), (*C.uchar)(unsafe.Pointer(&result[0])), nil, 123 | C.X_EVP_md5(), nil) 124 | 125 | return result 126 | } 127 | -------------------------------------------------------------------------------- /crypto/md5/md5_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package md5_test 16 | 17 | import ( 18 | "crypto/md5" 19 | "crypto/rand" 20 | "io" 21 | "testing" 22 | 23 | tsMD5 "github.com/tongsuo-project/tongsuo-go-sdk/crypto/md5" 24 | ) 25 | 26 | func TestMD5(t *testing.T) { 27 | t.Parallel() 28 | 29 | for i := 0; i < 100; i++ { 30 | buf := make([]byte, 10*1024-i) 31 | if _, err := io.ReadFull(rand.Reader, buf); err != nil { 32 | t.Fatal(err) 33 | } 34 | 35 | var got, expected [tsMD5.MDSize]byte 36 | 37 | s := md5.Sum(buf) 38 | got = tsMD5.Sum(buf) 39 | 40 | copy(expected[:], s[:tsMD5.MDSize]) 41 | 42 | if expected != got { 43 | t.Fatalf("exp:%x got:%x", expected, got) 44 | } 45 | } 46 | } 47 | 48 | func TestMD5Writer(t *testing.T) { 49 | t.Parallel() 50 | 51 | ohash, err := tsMD5.New() 52 | if err != nil { 53 | t.Fatal(err) 54 | } 55 | 56 | hash := md5.New() 57 | 58 | for i := 0; i < 100; i++ { 59 | ohash.Reset() 60 | hash.Reset() 61 | 62 | buf := make([]byte, 10*1024-i) 63 | if _, err := io.ReadFull(rand.Reader, buf); err != nil { 64 | t.Fatal(err) 65 | } 66 | 67 | if _, err := ohash.Write(buf); err != nil { 68 | t.Fatal(err) 69 | } 70 | 71 | if _, err := hash.Write(buf); err != nil { 72 | t.Fatal(err) 73 | } 74 | 75 | var got, exp [tsMD5.MDSize]byte 76 | 77 | hash.Sum(exp[:0]) 78 | ohash.Sum(got[:0]) 79 | 80 | if got != exp { 81 | t.Fatalf("exp:%x got:%x", exp, got) 82 | } 83 | } 84 | } 85 | 86 | type md5func func([]byte) 87 | 88 | func benchmarkMD5(b *testing.B, length int64, fn md5func) { 89 | b.Helper() 90 | 91 | buf := make([]byte, length) 92 | if _, err := io.ReadFull(rand.Reader, buf); err != nil { 93 | b.Fatal(err) 94 | } 95 | 96 | b.SetBytes(length) 97 | b.ResetTimer() 98 | 99 | for i := 0; i < b.N; i++ { 100 | fn(buf) 101 | } 102 | } 103 | 104 | func BenchmarkMD5Large_openssl(b *testing.B) { 105 | benchmarkMD5(b, 1024*1024, func(buf []byte) { tsMD5.Sum(buf) }) 106 | } 107 | 108 | func BenchmarkMD5Large_stdlib(b *testing.B) { 109 | benchmarkMD5(b, 1024*1024, func(buf []byte) { md5.Sum(buf) }) 110 | } 111 | 112 | func BenchmarkMD5Normal_openssl(b *testing.B) { 113 | benchmarkMD5(b, 1024, func(buf []byte) { tsMD5.Sum(buf) }) 114 | } 115 | 116 | func BenchmarkMD5Normal_stdlib(b *testing.B) { 117 | benchmarkMD5(b, 1024, func(buf []byte) { md5.Sum(buf) }) 118 | } 119 | 120 | func BenchmarkMD5Small_openssl(b *testing.B) { 121 | benchmarkMD5(b, 1, func(buf []byte) { tsMD5.Sum(buf) }) 122 | } 123 | 124 | func BenchmarkMD5Small_stdlib(b *testing.B) { 125 | benchmarkMD5(b, 1, func(buf []byte) { md5.Sum(buf) }) 126 | } 127 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | linters: 2 | enable-all: true 3 | disable: 4 | - ireturn 5 | - gochecknoinits 6 | - exhaustruct 7 | - nlreturn 8 | 9 | linters-settings: 10 | cyclop: 11 | max-complexity: 20 12 | interfacebloat: 13 | max: 11 14 | lll: 15 | line-length: 120 16 | funlen: 17 | lines: 120 18 | statements: 80 19 | ignore-comments: true 20 | depguard: 21 | rules: 22 | main: 23 | allow: 24 | - $gostd 25 | - github.com/tongsuo-project/tongsuo-go-sdk 26 | - github.com/tongsuo-project/tongsuo-go-sdk/crypto 27 | - github.com/tongsuo-project/tongsuo-go-sdk/utils 28 | 29 | issues: 30 | exclude: 31 | - "variable name '(i|e|n|wg|md|ok|ca|bn|iv|ip|rv|rc|fn)' is too short for the scope of its usage" 32 | - "parameter name '(e|r|s|ok|in|ip|iv|fn|rv)' is too short for the scope of its usage" 33 | exclude-rules: 34 | - path: crypto/sha1/sha1_test.go 35 | linters: 36 | - gosec 37 | - path: crypto/md5/md5_test.go 38 | linters: 39 | - gosec 40 | - path: conn.go 41 | text: "Error return value of `c.flushOutputBuffer` is not checked" 42 | - path: utils/errors.go 43 | text: "do not define dynamic errors, use wrapped static errors instead:" 44 | - path: ntls_test.go 45 | text: "Error return value of `server.(Run|RunForALPN)` is not checked" 46 | - path: ssl_test.go 47 | text: "G402: TLS InsecureSkipVerify set true." 48 | - path: crypto/key_test.go 49 | text: "G101: Potential hardcoded credentials: (RSA|SSH \\(EC\\)) private key" 50 | - path: ssl_test.go 51 | text: "G101: Potential hardcoded credentials: (RSA|SSH \\(EC\\)) private key" 52 | - path: ssl_test.go 53 | text: "G402: TLS MinVersion too low." 54 | - path: ctx.go 55 | text: "Consider pre-allocating `protoList`" 56 | - path: crypto/ciphers_gcm.go 57 | text: "Magic number: (128|192|256), in detected" 58 | - path: .*\.go 59 | text: "dupSubExpr: suspicious identical LHS and RHS for `==` operator" 60 | - path: crypto/sm2/sm2.go 61 | text: "return with no blank line before" 62 | - path: crypto/bio.go 63 | text: "return with no blank line before" 64 | - path: crypto/bio.go 65 | text: "(readBioMapping|writeBioMapping) is a global variable" 66 | - path: crypto/key_test.go 67 | text: "Function '(TestMarshal|TestMarshalEC)' has too many statements" 68 | - path: ctx.go 69 | text: "sslCtxIdx is a global variable" 70 | - path: ssl.go 71 | text: "sslIdx is a global variable" 72 | - path: .*_test\.go 73 | text: "cognitive complexity (.*) of func `(TestMarshalEC|TestMarshal|TestSessionReuse|TestNTLS)` is high" 74 | - path: .*_test\.go 75 | text: "cyclomatic complexity (.*) of func `(TestMarshalEC|TestMarshal)` is high" 76 | - path: .*_test\.go 77 | text: "calculated cyclomatic complexity for function (TestMarshal|TestMarshalEC) is (.*), max is (.*)" 78 | - path: .*_test\.go 79 | text: "error returned from external package is unwrapped" 80 | - path: crypto/key.go 81 | text: "`if curve == SM2Curve` has complex nested blocks \\(complexity: 6\\)" 82 | - path: crypto/init.go 83 | text: "do not define dynamic errors, use wrapped static errors instead:" 84 | - path: http.go 85 | text: "http.go:(.*): Line contains TODO/BUG/FIXME: \"TODO: http client integration\"" 86 | - path: ctx.go 87 | text: "errorf: should replace errors.New" 88 | - path: ctx.go 89 | text: "do not define dynamic errors, use wrapped static errors instead:" 90 | -------------------------------------------------------------------------------- /examples/cert_gen/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "math/big" 5 | "path/filepath" 6 | "time" 7 | 8 | "github.com/tongsuo-project/tongsuo-go-sdk/crypto" 9 | ) 10 | 11 | const genPath = "./examples/cert_gen/" 12 | 13 | func main() { 14 | // Helper function: generate and save key 15 | generateAndSaveKey := func(filename string) crypto.PrivateKey { 16 | key, err := crypto.GenerateECKey(crypto.SM2Curve) 17 | if err != nil { 18 | panic(err) 19 | } 20 | 21 | pem, err := key.MarshalPKCS8PrivateKeyPEM() 22 | if err != nil { 23 | panic(err) 24 | } 25 | 26 | err = crypto.SavePEMToFile(pem, filename) 27 | if err != nil { 28 | panic(err) 29 | } 30 | 31 | return key 32 | } 33 | 34 | // Helper function: create certificate 35 | createCertificate := func(info crypto.CertificateInfo, key crypto.PrivateKey, extensions map[crypto.NID]string) *crypto.Certificate { 36 | cert, err := crypto.NewCertificate(&info, key) 37 | if err != nil { 38 | panic(err) 39 | } 40 | 41 | err = cert.AddExtensions(extensions) 42 | if err != nil { 43 | panic(err) 44 | } 45 | 46 | return cert 47 | } 48 | 49 | // Helper function: sign and save certificate 50 | signAndSaveCert := func(cert *crypto.Certificate, caKey crypto.PrivateKey, filename string) { 51 | err := cert.Sign(caKey, crypto.DigestSM3) 52 | if err != nil { 53 | panic(err) 54 | } 55 | 56 | certPem, err := cert.MarshalPEM() 57 | if err != nil { 58 | panic(err) 59 | } 60 | 61 | err = crypto.SavePEMToFile(certPem, filename) 62 | if err != nil { 63 | panic(err) 64 | } 65 | } 66 | 67 | // Create CA certificate 68 | caKey, err := crypto.GenerateECKey(crypto.SM2Curve) 69 | if err != nil { 70 | panic(err) 71 | } 72 | 73 | caInfo := crypto.CertificateInfo{ 74 | Serial: big.NewInt(1), 75 | Expires: 87600 * time.Hour, // 10 years 76 | Country: "US", 77 | Organization: "Test CA", 78 | CommonName: "CA", 79 | } 80 | caExtensions := map[crypto.NID]string{ 81 | crypto.NidBasicConstraints: "critical,CA:TRUE", 82 | crypto.NidKeyUsage: "critical,digitalSignature,keyCertSign,cRLSign", 83 | crypto.NidSubjectKeyIdentifier: "hash", 84 | crypto.NidAuthorityKeyIdentifier: "keyid:always,issuer", 85 | } 86 | ca := createCertificate(caInfo, caKey, caExtensions) 87 | caFile := filepath.Join(genPath, "chain-ca.crt") 88 | signAndSaveCert(ca, caKey, caFile) 89 | 90 | // Define additional certificate information 91 | certInfos := []struct { 92 | name string 93 | keyUsage string 94 | }{ 95 | {"server_enc", "keyAgreement, keyEncipherment, dataEncipherment"}, 96 | {"server_sign", "nonRepudiation, digitalSignature"}, 97 | {"client_sign", "nonRepudiation, digitalSignature"}, 98 | {"client_enc", "keyAgreement, keyEncipherment, dataEncipherment"}, 99 | } 100 | 101 | // Create additional certificates 102 | for _, info := range certInfos { 103 | keyFile := filepath.Join(genPath, info.name+".key") 104 | key := generateAndSaveKey(keyFile) 105 | certInfo := crypto.CertificateInfo{ 106 | Serial: big.NewInt(1), 107 | Issued: 0, 108 | Expires: 87600 * time.Hour, // 10 years 109 | Country: "US", 110 | Organization: "Test", 111 | CommonName: "localhost", 112 | } 113 | extensions := map[crypto.NID]string{ 114 | crypto.NidBasicConstraints: "critical,CA:FALSE", 115 | crypto.NidKeyUsage: info.keyUsage, 116 | } 117 | cert := createCertificate(certInfo, key, extensions) 118 | 119 | err = cert.SetIssuer(ca) 120 | if err != nil { 121 | panic(err) 122 | } 123 | 124 | certFile := filepath.Join(genPath, info.name+".crt") 125 | signAndSaveCert(cert, caKey, certFile) 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /shim.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Space Monkey, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "crypto/shim.h" 24 | 25 | #ifndef SSL_MODE_RELEASE_BUFFERS 26 | #define SSL_MODE_RELEASE_BUFFERS 0 27 | #endif 28 | 29 | #ifndef SSL_OP_NO_COMPRESSION 30 | #define SSL_OP_NO_COMPRESSION 0 31 | #endif 32 | 33 | /* shim methods */ 34 | extern void X_tongsuogo_init(void); 35 | 36 | /* SSL methods */ 37 | extern long X_SSL_set_options(SSL* ssl, long options); 38 | extern long X_SSL_get_options(SSL* ssl); 39 | extern long X_SSL_clear_options(SSL* ssl, long options); 40 | extern long X_SSL_set_tlsext_host_name(SSL *ssl, const char *name); 41 | extern const char *X_SSL_get_cipher_name(const SSL *ssl); 42 | extern const char *X_SSL_get_version(const SSL *ssl); 43 | extern int X_SSL_session_reused(SSL *ssl); 44 | extern int X_SSL_new_index(); 45 | 46 | extern const SSL_METHOD *X_SSLv23_method(); 47 | extern const SSL_METHOD *X_SSLv3_method(); 48 | extern const SSL_METHOD *X_TLSv1_method(); 49 | extern const SSL_METHOD *X_TLSv1_1_method(); 50 | extern const SSL_METHOD *X_TLSv1_2_method(); 51 | extern const SSL_METHOD *X_NTLS_method(); 52 | extern const SSL_METHOD *X_NTLS_client_method(); 53 | extern const SSL_METHOD *X_NTLS_server_method(); 54 | 55 | #if defined SSL_CTRL_SET_TLSEXT_HOSTNAME 56 | extern int sni_cb(SSL *ssl_conn, int *ad, void *arg); 57 | #endif 58 | 59 | extern int alpn_cb(SSL *ssl_conn, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg); 60 | extern int X_SSL_verify_cb(int ok, X509_STORE_CTX* store); 61 | 62 | /* SSL_CTX methods */ 63 | extern int X_SSL_CTX_new_index(); 64 | extern void X_SSL_CTX_enable_ntls(SSL_CTX* ctx); 65 | extern long X_SSL_CTX_set_options(SSL_CTX* ctx, long options); 66 | extern long X_SSL_CTX_clear_options(SSL_CTX* ctx, long options); 67 | extern long X_SSL_CTX_get_options(SSL_CTX* ctx); 68 | extern long X_SSL_CTX_set_mode(SSL_CTX* ctx, long modes); 69 | extern long X_SSL_CTX_get_mode(SSL_CTX* ctx); 70 | extern long X_SSL_CTX_set_session_cache_mode(SSL_CTX* ctx, long modes); 71 | extern long X_SSL_CTX_sess_set_cache_size(SSL_CTX* ctx, long t); 72 | extern long X_SSL_CTX_sess_get_cache_size(SSL_CTX* ctx); 73 | extern long X_SSL_CTX_set_timeout(SSL_CTX* ctx, long t); 74 | extern long X_SSL_CTX_get_timeout(SSL_CTX* ctx); 75 | extern long X_SSL_CTX_add_extra_chain_cert(SSL_CTX* ctx, X509 *cert); 76 | extern long X_SSL_CTX_set_tmp_ecdh(SSL_CTX* ctx, EC_KEY *key); 77 | extern long X_SSL_CTX_set_tlsext_servername_callback(SSL_CTX* ctx, int (*cb)(SSL *con, int *ad, void *args)); 78 | extern int X_SSL_CTX_verify_cb(int ok, X509_STORE_CTX* store); 79 | extern long X_SSL_CTX_set_tmp_dh(SSL_CTX* ctx, DH *dh); 80 | extern long X_PEM_read_DHparams(SSL_CTX* ctx, DH *dh); 81 | extern int X_SSL_CTX_set_tlsext_ticket_key_cb(SSL_CTX *sslctx, 82 | int (*cb)(SSL *s, unsigned char key_name[16], 83 | unsigned char iv[EVP_MAX_IV_LENGTH], 84 | EVP_CIPHER_CTX *ctx, HMAC_CTX *hctx, int enc)); 85 | extern int X_SSL_CTX_ticket_key_cb(SSL *s, unsigned char key_name[16], 86 | unsigned char iv[EVP_MAX_IV_LENGTH], 87 | EVP_CIPHER_CTX *cctx, HMAC_CTX *hctx, int enc); 88 | extern int X_SSL_CTX_set_max_proto_version(SSL_CTX *ctx, int version); 89 | extern int X_SSL_CTX_set_min_proto_version(SSL_CTX *ctx, int version); 90 | 91 | extern int X_X509_add_ref(X509* x509); 92 | extern int X_sk_X509_num(STACK_OF(X509) *sk); 93 | extern X509 *X_sk_X509_value(STACK_OF(X509)* sk, int i); 94 | -------------------------------------------------------------------------------- /crypto/hostname.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package crypto 16 | 17 | /* 18 | #include 19 | #include 20 | #include 21 | 22 | #ifndef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT 23 | #define X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT 0x1 24 | #define X509_CHECK_FLAG_NO_WILDCARDS 0x2 25 | 26 | extern int X509_check_host(X509 *x, const unsigned char *chk, size_t chklen, 27 | unsigned int flags, char **peername); 28 | extern int X509_check_email(X509 *x, const unsigned char *chk, size_t chklen, 29 | unsigned int flags); 30 | extern int X509_check_ip(X509 *x, const unsigned char *chk, size_t chklen, 31 | unsigned int flags); 32 | #endif 33 | */ 34 | import "C" 35 | 36 | import ( 37 | "net" 38 | "unsafe" 39 | ) 40 | 41 | type CheckFlags int 42 | 43 | const ( 44 | AlwaysCheckSubject CheckFlags = C.X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT 45 | NoWildcards CheckFlags = C.X509_CHECK_FLAG_NO_WILDCARDS 46 | ) 47 | 48 | // CheckHost checks that the X509 certificate is signed for the provided 49 | // host name. See http://www.openssl.org/docs/crypto/X509_check_host.html for 50 | // more. Note that CheckHost does not check the IP field. See VerifyHostname. 51 | // Specifically returns ValidationError if the Certificate didn't match but 52 | // there was no internal error. 53 | func (c *Certificate) CheckHost(host string, flags CheckFlags) error { 54 | chost := unsafe.Pointer(C.CString(host)) 55 | defer C.free(chost) 56 | 57 | rv := C.X509_check_host(c.x, (*C.uchar)(chost), C.size_t(len(host)), 58 | C.uint(flags), nil) 59 | if rv > 0 { 60 | return nil 61 | } 62 | if rv == 0 { 63 | return ErrMatchFailed 64 | } 65 | if rv == -2 { 66 | return ErrInputInvalid 67 | } 68 | 69 | return ErrInternalError 70 | } 71 | 72 | // CheckEmail checks that the X509 certificate is signed for the provided 73 | // email address. See http://www.openssl.org/docs/crypto/X509_check_host.html 74 | // for more. 75 | // Specifically returns ValidationError if the Certificate didn't match but 76 | // there was no internal error. 77 | func (c *Certificate) CheckEmail(email string, flags CheckFlags) error { 78 | cemail := unsafe.Pointer(C.CString(email)) 79 | defer C.free(cemail) 80 | rv := C.X509_check_email(c.x, (*C.uchar)(cemail), C.size_t(len(email)), 81 | C.uint(flags)) 82 | if rv > 0 { 83 | return nil 84 | } 85 | if rv == 0 { 86 | return ErrMatchFailed 87 | } 88 | if rv == -2 { 89 | return ErrInputInvalid 90 | } 91 | 92 | return ErrInternalError 93 | } 94 | 95 | // CheckIP checks that the X509 certificate is signed for the provided 96 | // IP address. See http://www.openssl.org/docs/crypto/X509_check_host.html 97 | // for more. 98 | // Specifically returns ValidationError if the Certificate didn't match but 99 | // there was no internal error. 100 | func (c *Certificate) CheckIP(ip net.IP, flags CheckFlags) error { 101 | // X509_check_ip will fail to validate the 16-byte representation of an IPv4 102 | // address, so convert to the 4-byte representation. 103 | if ip4 := ip.To4(); ip4 != nil { 104 | ip = ip4 105 | } 106 | 107 | cip := unsafe.Pointer(&ip[0]) 108 | rv := C.X509_check_ip(c.x, (*C.uchar)(cip), C.size_t(len(ip)), 109 | C.uint(flags)) 110 | if rv > 0 { 111 | return nil 112 | } 113 | if rv == 0 { 114 | return ErrMatchFailed 115 | } 116 | if rv == -2 { 117 | return ErrInputInvalid 118 | } 119 | 120 | return ErrInternalError 121 | } 122 | 123 | // VerifyHostname is a combination of CheckHost and CheckIP. If the provided 124 | // hostname looks like an IP address, it will be checked as an IP address, 125 | // otherwise it will be checked as a hostname. 126 | // Specifically returns ValidationError if the Certificate didn't match but 127 | // there was no internal error. 128 | func (c *Certificate) VerifyHostname(host string) error { 129 | var ip net.IP 130 | if len(host) >= 3 && host[0] == '[' && host[len(host)-1] == ']' { 131 | ip = net.ParseIP(host[1 : len(host)-1]) 132 | } else { 133 | ip = net.ParseIP(host) 134 | } 135 | if ip != nil { 136 | return c.CheckIP(ip, 0) 137 | } 138 | return c.CheckHost(host, 0) 139 | } 140 | -------------------------------------------------------------------------------- /crypto/ciphers_gcm.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package crypto 16 | 17 | // #include 18 | import "C" 19 | 20 | import ( 21 | "fmt" 22 | ) 23 | 24 | type AuthenticatedEncryptionCipherCtx interface { 25 | EncryptionCipherCtx 26 | 27 | // data passed in to ExtraData() is part of the final output; it is 28 | // not encrypted itself, but is part of the authenticated data. when 29 | // decrypting or authenticating, pass back with the decryption 30 | // context's ExtraData() 31 | ExtraData(extra []byte) error 32 | 33 | // use after finalizing encryption to get the authenticating tag 34 | GetTag() ([]byte, error) 35 | } 36 | 37 | type AuthenticatedDecryptionCipherCtx interface { 38 | DecryptionCipherCtx 39 | 40 | // pass in any extra data that was added during encryption with the 41 | // encryption context's ExtraData() 42 | ExtraData(extra []byte) error 43 | 44 | // use before finalizing decryption to tell the library what the 45 | // tag is expected to be 46 | SetTag(tag []byte) error 47 | } 48 | 49 | type authEncryptionCipherCtx struct { 50 | *encryptionCipherCtx 51 | } 52 | 53 | type authDecryptionCipherCtx struct { 54 | *decryptionCipherCtx 55 | } 56 | 57 | func getGCMCipher(blocksize int) (*Cipher, error) { 58 | var cipherptr *C.EVP_CIPHER 59 | switch blocksize { 60 | case 256: 61 | cipherptr = C.EVP_aes_256_gcm() 62 | case 192: 63 | cipherptr = C.EVP_aes_192_gcm() 64 | case 128: 65 | cipherptr = C.EVP_aes_128_gcm() 66 | default: 67 | return nil, ErrUknownBlockSize 68 | } 69 | return &Cipher{ptr: cipherptr}, nil 70 | } 71 | 72 | func NewGCMEncryptionCipherCtx(blocksize int, e *Engine, key, iv []byte) ( 73 | AuthenticatedEncryptionCipherCtx, error, 74 | ) { 75 | cipher, err := getGCMCipher(blocksize) 76 | if err != nil { 77 | return nil, err 78 | } 79 | ctx, err := newEncryptionCipherCtx(cipher, e, key, nil) 80 | if err != nil { 81 | return nil, err 82 | } 83 | if len(iv) > 0 { 84 | err := ctx.SetCtrl(C.EVP_CTRL_GCM_SET_IVLEN, len(iv)) 85 | if err != nil { 86 | return nil, fmt.Errorf("could not set IV len to %d: %w", 87 | len(iv), err) 88 | } 89 | if C.EVP_EncryptInit_ex(ctx.ctx, nil, nil, nil, (*C.uchar)(&iv[0])) != 1 { 90 | return nil, fmt.Errorf("failed to apply IV: %w", PopError()) 91 | } 92 | } 93 | return &authEncryptionCipherCtx{encryptionCipherCtx: ctx}, nil 94 | } 95 | 96 | func NewGCMDecryptionCipherCtx(blocksize int, e *Engine, key, iv []byte) ( 97 | AuthenticatedDecryptionCipherCtx, error, 98 | ) { 99 | cipher, err := getGCMCipher(blocksize) 100 | if err != nil { 101 | return nil, err 102 | } 103 | ctx, err := newDecryptionCipherCtx(cipher, e, key, nil) 104 | if err != nil { 105 | return nil, err 106 | } 107 | if len(iv) > 0 { 108 | err := ctx.SetCtrl(C.EVP_CTRL_GCM_SET_IVLEN, len(iv)) 109 | if err != nil { 110 | return nil, fmt.Errorf("could not set IV len to %d: %w", 111 | len(iv), err) 112 | } 113 | if C.EVP_DecryptInit_ex(ctx.ctx, nil, nil, nil, (*C.uchar)(&iv[0])) != 1 { 114 | return nil, fmt.Errorf("failed to apply IV: %w", PopError()) 115 | } 116 | } 117 | return &authDecryptionCipherCtx{decryptionCipherCtx: ctx}, nil 118 | } 119 | 120 | func (ctx *authEncryptionCipherCtx) ExtraData(aad []byte) error { 121 | if aad == nil { 122 | return nil 123 | } 124 | var outlen C.int 125 | if C.EVP_EncryptUpdate(ctx.ctx, nil, &outlen, (*C.uchar)(&aad[0]), C.int(len(aad))) != 1 { 126 | return fmt.Errorf("failed to add additional authenticated data: %w", PopError()) 127 | } 128 | return nil 129 | } 130 | 131 | func (ctx *authDecryptionCipherCtx) ExtraData(aad []byte) error { 132 | if aad == nil { 133 | return nil 134 | } 135 | var outlen C.int 136 | if C.EVP_DecryptUpdate(ctx.ctx, nil, &outlen, (*C.uchar)(&aad[0]), C.int(len(aad))) != 1 { 137 | return fmt.Errorf("failed to add additional authenticated data: %w", PopError()) 138 | } 139 | return nil 140 | } 141 | 142 | func (ctx *authEncryptionCipherCtx) GetTag() ([]byte, error) { 143 | return ctx.GetCtrlBytes(C.EVP_CTRL_GCM_GET_TAG, GCMTagMaxLen, 144 | GCMTagMaxLen) 145 | } 146 | 147 | func (ctx *authDecryptionCipherCtx) SetTag(tag []byte) error { 148 | return ctx.SetCtrlBytes(C.EVP_CTRL_GCM_SET_TAG, len(tag), tag) 149 | } 150 | -------------------------------------------------------------------------------- /crypto/sm2/sm2.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Tongsuo Project Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License 2.0 (the "License"). You may not use 4 | // this file except in compliance with the License. You can obtain a copy 5 | // in the file LICENSE in the source distribution or at 6 | // https://github.com/Tongsuo-Project/tongsuo-go-sdk/blob/main/LICENSE 7 | 8 | package sm2 9 | 10 | // #include "../shim.h" 11 | import "C" 12 | 13 | import ( 14 | "fmt" 15 | "math/big" 16 | "unsafe" 17 | 18 | "github.com/tongsuo-project/tongsuo-go-sdk/crypto" 19 | ) 20 | 21 | // VerifyASN1 verifies ASN.1 encoded signature. Returns nil on success. 22 | func VerifyASN1(pub crypto.PublicKey, data, sig []byte) error { 23 | if pub.KeyType() != crypto.NidSM2 { 24 | return fmt.Errorf("key type is not sm2: %w", crypto.ErrWrongKeyType) 25 | } 26 | 27 | err := pub.VerifyPKCS1v15(crypto.SM3Method(), data, sig) 28 | if err != nil { 29 | return fmt.Errorf("failed to verify: %w", err) 30 | } 31 | 32 | return nil 33 | } 34 | 35 | // SignASN1 signs the data with priv and returns ASN.1 encoded signature. 36 | func SignASN1(priv crypto.PrivateKey, data []byte) ([]byte, error) { 37 | if priv.KeyType() != crypto.NidSM2 { 38 | return nil, fmt.Errorf("key type is not sm2: %w", crypto.ErrWrongKeyType) 39 | } 40 | 41 | ret, err := priv.SignPKCS1v15(crypto.SM3Method(), data) 42 | if err != nil { 43 | return nil, fmt.Errorf("failed to sign: %w", err) 44 | } 45 | 46 | return ret, nil 47 | } 48 | 49 | // Verify verifies the signature in r, s of data using the public key, pub. 50 | // Returns nil on success. 51 | func Verify(pub crypto.PublicKey, data []byte, r, s *big.Int) error { 52 | if pub.KeyType() != crypto.NidSM2 { 53 | return fmt.Errorf("key type is not sm2 %w", crypto.ErrWrongKeyType) 54 | } 55 | 56 | sm2Sig := C.ECDSA_SIG_new() 57 | defer C.ECDSA_SIG_free(sm2Sig) 58 | 59 | rBig := C.BN_bin2bn((*C.uchar)(unsafe.Pointer(&r.Bytes()[0])), C.int(len(r.Bytes())), nil) 60 | sBig := C.BN_bin2bn((*C.uchar)(unsafe.Pointer(&s.Bytes()[0])), C.int(len(s.Bytes())), nil) 61 | 62 | ret := C.ECDSA_SIG_set0(sm2Sig, rBig, sBig) 63 | if ret != 1 { 64 | return fmt.Errorf("failed to set r/s: %w", crypto.ErrNilParameter) 65 | } 66 | 67 | len1 := C.i2d_ECDSA_SIG(sm2Sig, nil) 68 | 69 | buf := (*C.uchar)(C.malloc(C.size_t(len1))) 70 | defer C.free(unsafe.Pointer(buf)) 71 | 72 | tmp := buf 73 | len2 := C.i2d_ECDSA_SIG(sm2Sig, &tmp) 74 | 75 | return VerifyASN1(pub, data, C.GoBytes(unsafe.Pointer(buf), len2)) 76 | } 77 | 78 | // Sign signs the data with the private key, priv. 79 | func Sign(priv crypto.PrivateKey, data []byte) (*big.Int, *big.Int, error) { 80 | if priv.KeyType() != crypto.NidSM2 { 81 | return nil, nil, fmt.Errorf("key type is not sm2: %w", crypto.ErrWrongKeyType) 82 | } 83 | 84 | sig, err := priv.SignPKCS1v15(crypto.SM3Method(), data) 85 | if err != nil { 86 | return nil, nil, fmt.Errorf("failed to sign data: %w", err) 87 | } 88 | 89 | buf := (*C.uchar)(C.malloc(C.size_t(len(sig)))) 90 | defer C.free(unsafe.Pointer(buf)) 91 | C.memcpy(unsafe.Pointer(buf), unsafe.Pointer(&sig[0]), C.size_t(len(sig))) 92 | 93 | sm2Sig := C.d2i_ECDSA_SIG(nil, &buf, C.long(len(sig))) 94 | if sm2Sig == nil { 95 | return nil, nil, fmt.Errorf("failed to decode signature: %w", err) 96 | } 97 | defer C.ECDSA_SIG_free(sm2Sig) 98 | 99 | var rBig, sBig *C.BIGNUM 100 | C.ECDSA_SIG_get0(sm2Sig, &rBig, &sBig) 101 | 102 | rBytes := make([]byte, C.X_BN_num_bytes(rBig)) 103 | sBytes := make([]byte, C.X_BN_num_bytes(sBig)) 104 | 105 | rLen := C.BN_bn2bin(rBig, (*C.uchar)(unsafe.Pointer(&rBytes[0]))) 106 | sLen := C.BN_bn2bin(sBig, (*C.uchar)(unsafe.Pointer(&sBytes[0]))) 107 | 108 | r := new(big.Int).SetBytes(rBytes[:rLen]) 109 | s := new(big.Int).SetBytes(sBytes[:sLen]) 110 | 111 | return r, s, nil 112 | } 113 | 114 | // GenerateKey generates a new SM2 key pair. 115 | func GenerateKey() (crypto.PrivateKey, error) { 116 | priv, err := crypto.GenerateECKey(crypto.SM2Curve) 117 | if err != nil { 118 | return nil, fmt.Errorf("failed to create key: %w", err) 119 | } 120 | 121 | return priv, nil 122 | } 123 | 124 | // Encrypt encrypts the data with the public key, publ. 125 | func Encrypt(pub crypto.PublicKey, data []byte) ([]byte, error) { 126 | if pub.KeyType() != crypto.NidSM2 { 127 | return nil, fmt.Errorf("key type is not sm2: %w", crypto.ErrWrongKeyType) 128 | } 129 | 130 | ret, err := pub.Encrypt(data) 131 | if err != nil { 132 | return nil, fmt.Errorf("failed to encrypt: %w", err) 133 | } 134 | 135 | return ret, nil 136 | } 137 | 138 | // Decrypt decrypts the ciphertext with the private key, priv. 139 | func Decrypt(priv crypto.PrivateKey, data []byte) ([]byte, error) { 140 | if priv.KeyType() != crypto.NidSM2 { 141 | return nil, fmt.Errorf("key type is not sm2: %w", crypto.ErrWrongKeyType) 142 | } 143 | 144 | ret, err := priv.Decrypt(data) 145 | if err != nil { 146 | return nil, fmt.Errorf("failed to decrypt: %w", err) 147 | } 148 | 149 | return ret, nil 150 | } 151 | -------------------------------------------------------------------------------- /net.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package tongsuogo 16 | 17 | import ( 18 | "errors" 19 | "fmt" 20 | "net" 21 | ) 22 | 23 | var ErrNilParam = errors.New("nil parameter") 24 | 25 | type listener struct { 26 | net.Listener 27 | ctx *Ctx 28 | } 29 | 30 | func (l *listener) Accept() (net.Conn, error) { 31 | conn, err := l.Listener.Accept() 32 | if err != nil { 33 | return nil, fmt.Errorf("failed to accept: %w", err) 34 | } 35 | 36 | server, err := Server(conn, l.ctx) 37 | if err != nil { 38 | conn.Close() 39 | 40 | return nil, err 41 | } 42 | 43 | return server, nil 44 | } 45 | 46 | // NewListener wraps an existing net.Listener such that all accepted 47 | // connections are wrapped as OpenSSL server connections using the provided 48 | // context ctx. 49 | func NewListener(inner net.Listener, ctx *Ctx) net.Listener { 50 | return &listener{ 51 | Listener: inner, 52 | ctx: ctx, 53 | } 54 | } 55 | 56 | // Listen is a wrapper around net.Listen that wraps incoming connections with 57 | // an OpenSSL server connection using the provided context ctx. 58 | func Listen(network, laddr string, ctx *Ctx) (net.Listener, error) { 59 | if ctx == nil { 60 | return nil, fmt.Errorf("no ssl context provided: %w", ErrNilParam) 61 | } 62 | 63 | l, err := net.Listen(network, laddr) 64 | if err != nil { 65 | return nil, fmt.Errorf("failed to listen: %w", err) 66 | } 67 | 68 | return NewListener(l, ctx), nil 69 | } 70 | 71 | type DialFlags int 72 | 73 | const ( 74 | InsecureSkipHostVerification DialFlags = 1 << iota 75 | DisableSNI 76 | ) 77 | 78 | // Dial will connect to network/address and then wrap the corresponding 79 | // underlying connection with an OpenSSL client connection using context ctx. 80 | // If flags includes InsecureSkipHostVerification, the server certificate's 81 | // hostname will not be checked to match the hostname in addr. Otherwise, flags 82 | // should be 0. 83 | // 84 | // Dial probably won't work for you unless you set a verify location or add 85 | // some certs to the certificate store of the client context you're using. 86 | // This library is not nice enough to use the system certificate store by 87 | // default for you yet. 88 | func Dial(network, addr string, ctx *Ctx, flags DialFlags, host string) (*Conn, error) { 89 | return DialSession(network, addr, ctx, flags, nil, host) 90 | } 91 | 92 | // DialSession will connect to network/address and then wrap the corresponding 93 | // underlying connection with an OpenSSL client connection using context ctx. 94 | // If flags includes InsecureSkipHostVerification, the server certificate's 95 | // hostname will not be checked to match the hostname in addr. Otherwise, flags 96 | // should be 0. 97 | // 98 | // Dial probably won't work for you unless you set a verify location or add 99 | // some certs to the certificate store of the client context you're using. 100 | // This library is not nice enough to use the system certificate store by 101 | // default for you yet. 102 | // 103 | // If session is not nil it will be used to resume the tls state. The session 104 | // can be retrieved from the GetSession method on the Conn. 105 | func DialSession(network, addr string, ctx *Ctx, flags DialFlags, 106 | session []byte, host string, 107 | ) (*Conn, error) { 108 | var err error 109 | if host == "" { 110 | host, _, err = net.SplitHostPort(addr) 111 | } 112 | 113 | if err != nil { 114 | return nil, fmt.Errorf("failed to split host and port: %w", err) 115 | } 116 | 117 | if ctx == nil { 118 | var err error 119 | 120 | ctx, err = NewCtx() 121 | if err != nil { 122 | return nil, err 123 | } 124 | } 125 | 126 | conn, err := net.Dial(network, addr) 127 | if err != nil { 128 | return nil, fmt.Errorf("failed to connect: %w", err) 129 | } 130 | 131 | client, err := Client(conn, ctx) 132 | if err != nil { 133 | conn.Close() 134 | 135 | return nil, err 136 | } 137 | 138 | if session != nil { 139 | err := client.setSession(session) 140 | if err != nil { 141 | conn.Close() 142 | 143 | return nil, err 144 | } 145 | } 146 | 147 | if flags&DisableSNI == 0 { 148 | err = client.SetTLSExtHostName(host) 149 | if err != nil { 150 | client.Close() 151 | 152 | return nil, fmt.Errorf("failed to set TLS host name: %w", err) 153 | } 154 | } 155 | 156 | err = client.Handshake() 157 | if err != nil { 158 | client.Close() 159 | 160 | return nil, fmt.Errorf("failed to handshake: %w", err) 161 | } 162 | 163 | if flags&InsecureSkipHostVerification == 0 { 164 | err = client.VerifyHostname(host) 165 | if err != nil { 166 | client.Close() 167 | 168 | return nil, fmt.Errorf("failed to verify host name: %w", err) 169 | } 170 | } 171 | 172 | return client, nil 173 | } 174 | -------------------------------------------------------------------------------- /crypto/sm2/sm2_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Tongsuo Project Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License 2.0 (the "License"). You may not use 4 | // this file except in compliance with the License. You can obtain a copy 5 | // in the file LICENSE in the source distribution or at 6 | // https://github.com/Tongsuo-Project/tongsuo-go-sdk/blob/main/LICENSE 7 | 8 | package sm2_test 9 | 10 | import ( 11 | "encoding/hex" 12 | "math/big" 13 | "strings" 14 | "testing" 15 | 16 | "github.com/tongsuo-project/tongsuo-go-sdk/crypto" 17 | "github.com/tongsuo-project/tongsuo-go-sdk/crypto/sm2" 18 | ) 19 | 20 | const sm2Key1 = `-----BEGIN PRIVATE KEY----- 21 | MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQg0JFWczAXva2An9m7 22 | 2MaT9gIwWTFptvlKrxyO4TjMmbWhRANCAAQ5OirZ4n5DrKqrhaGdO4VZHhRAYVcX 23 | Wt3Te/d/8Mr57Tf886i09VwDhSMmH8pmNq/mp6+ioUgqYG9cs6GLLioe 24 | -----END PRIVATE KEY----- 25 | ` 26 | 27 | const sm2Pubkey1 = `-----BEGIN PUBLIC KEY----- 28 | MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEOToq2eJ+Q6yqq4WhnTuFWR4UQGFX 29 | F1rd03v3f/DK+e03/POotPVcA4UjJh/KZjav5qevoqFIKmBvXLOhiy4qHg== 30 | -----END PUBLIC KEY----- 31 | ` 32 | 33 | func TestSM2PublicKeyVerifyASN1(t *testing.T) { 34 | t.Parallel() 35 | 36 | pub, err := crypto.LoadPublicKeyFromPEM([]byte(sm2Pubkey1)) 37 | if err != nil { 38 | t.Fatal(err) 39 | } 40 | 41 | data := []byte("hello world") 42 | hexSig := `3046022100ba37b776135afbf5bf36b21f4a65889bcd0037092be47f6429f877790b8cb9c402210097b59fd56d41317d490dd300e 43 | 7e69d7909a0885414ac3b2c5a24bdfc1588cb55` 44 | sig, _ := hex.DecodeString(strings.ReplaceAll(hexSig, "\n", "")) 45 | 46 | if sm2.VerifyASN1(pub, data, sig) != nil { 47 | t.Fatal() 48 | } 49 | } 50 | 51 | func TestSM2PrivateKey2PublicVerifyASN1(t *testing.T) { 52 | t.Parallel() 53 | 54 | priv, err := crypto.LoadPrivateKeyFromPEM([]byte(sm2Key1)) 55 | if err != nil { 56 | t.Fatal(err) 57 | } 58 | 59 | data := []byte("hello world") 60 | hexSig := `3046022100ba37b776135afbf5bf36b21f4a65889bcd0037092be47f6429f877790b8cb9c402210097b59fd56d41317d490dd300e 61 | 7e69d7909a0885414ac3b2c5a24bdfc1588cb55` 62 | sig, _ := hex.DecodeString(strings.ReplaceAll(hexSig, "\n", "")) 63 | 64 | if sm2.VerifyASN1(priv.Public(), data, sig) != nil { 65 | t.Fatal() 66 | } 67 | } 68 | 69 | func TestSM2PrivateKey2PublicVerify(t *testing.T) { 70 | t.Parallel() 71 | 72 | priv, err := crypto.LoadPrivateKeyFromPEM([]byte(sm2Key1)) 73 | if err != nil { 74 | t.Fatal(err) 75 | } 76 | 77 | data := []byte("hello world") 78 | rBytes, _ := hex.DecodeString("ba37b776135afbf5bf36b21f4a65889bcd0037092be47f6429f877790b8cb9c4") 79 | sBytes, _ := hex.DecodeString("97b59fd56d41317d490dd300e7e69d7909a0885414ac3b2c5a24bdfc1588cb55") 80 | r := new(big.Int).SetBytes(rBytes) 81 | s := new(big.Int).SetBytes(sBytes) 82 | 83 | if sm2.Verify(priv.Public(), data, r, s) != nil { 84 | t.Fatal() 85 | } 86 | } 87 | 88 | func TestSM2VerifySignASN1(t *testing.T) { 89 | t.Parallel() 90 | 91 | priv, err := crypto.LoadPrivateKeyFromPEM([]byte(sm2Key1)) 92 | if err != nil { 93 | t.Fatal(err) 94 | } 95 | 96 | data := []byte("hello world") 97 | 98 | sig, err := sm2.SignASN1(priv, data) 99 | if err != nil { 100 | t.Fatal(err) 101 | } 102 | 103 | if sm2.VerifyASN1(priv.Public(), data, sig) != nil { 104 | t.Fatal() 105 | } 106 | } 107 | 108 | func TestNewSM2VerifySignASN1(t *testing.T) { 109 | t.Parallel() 110 | 111 | priv, err := sm2.GenerateKey() 112 | if err != nil { 113 | t.Fatal(err) 114 | } 115 | 116 | pub := priv.Public() 117 | data := []byte("hello world") 118 | 119 | sig, err := sm2.SignASN1(priv, data) 120 | if err != nil { 121 | t.Fatal(err) 122 | } 123 | 124 | if sm2.VerifyASN1(pub, data, sig) != nil { 125 | t.Fatal() 126 | } 127 | } 128 | 129 | func TestSM2VerifySign(t *testing.T) { 130 | t.Parallel() 131 | 132 | priv, err := crypto.LoadPrivateKeyFromPEM([]byte(sm2Key1)) 133 | if err != nil { 134 | t.Fatal(err) 135 | } 136 | 137 | data := []byte("hello world") 138 | 139 | r, s, err := sm2.Sign(priv, data) 140 | if err != nil { 141 | t.Fatal(err) 142 | } 143 | 144 | if sm2.Verify(priv.Public(), data, r, s) != nil { 145 | t.Fatal() 146 | } 147 | } 148 | 149 | func TestNewSM2VerifySign(t *testing.T) { 150 | t.Parallel() 151 | 152 | priv, err := sm2.GenerateKey() 153 | if err != nil { 154 | t.Fatal(err) 155 | } 156 | 157 | pub := priv.Public() 158 | data := []byte("hello world") 159 | 160 | r, s, err := sm2.Sign(priv, data) 161 | if err != nil { 162 | t.Fatal(err) 163 | } 164 | 165 | if sm2.Verify(pub, data, r, s) != nil { 166 | t.Fatal() 167 | } 168 | } 169 | 170 | func TestSM2DecryptEncrypt(t *testing.T) { 171 | t.Parallel() 172 | 173 | priv, err := crypto.LoadPrivateKeyFromPEM([]byte(sm2Key1)) 174 | if err != nil { 175 | t.Fatal(err) 176 | } 177 | 178 | pub := priv.Public() 179 | data := []byte("hello world") 180 | 181 | ciphertext, err := sm2.Encrypt(pub, data) 182 | if err != nil { 183 | t.Fatal(err) 184 | } 185 | 186 | plaintext, err := sm2.Decrypt(priv, ciphertext) 187 | if err != nil { 188 | t.Fatal(err) 189 | } 190 | 191 | if string(plaintext) != string(data) { 192 | t.Fatal() 193 | } 194 | } 195 | 196 | func TestNewSM2DecryptEncrypt(t *testing.T) { 197 | t.Parallel() 198 | 199 | priv, err := sm2.GenerateKey() 200 | if err != nil { 201 | t.Fatal(err) 202 | } 203 | 204 | pub := priv.Public() 205 | data := []byte("hello world") 206 | 207 | ciphertext, err := sm2.Encrypt(pub, data) 208 | if err != nil { 209 | t.Fatal(err) 210 | } 211 | 212 | plaintext, err := sm2.Decrypt(priv, ciphertext) 213 | if err != nil { 214 | t.Fatal(err) 215 | } 216 | 217 | if string(plaintext) != string(data) { 218 | t.Fatal() 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /examples/sm4/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Tongsuo Project Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License 2.0 (the "License"). You may not use 4 | // this file except in compliance with the License. You can obtain a copy 5 | // in the file LICENSE in the source distribution or at 6 | // https://github.com/Tongsuo-Project/tongsuo-go-sdk/blob/main/LICENSE 7 | 8 | package main 9 | 10 | import ( 11 | "bytes" 12 | "crypto/cipher" 13 | "encoding/hex" 14 | "fmt" 15 | "log" 16 | 17 | "github.com/tongsuo-project/tongsuo-go-sdk/crypto/sm4" 18 | ) 19 | 20 | func sm4CBCEncrypt() { 21 | key, _ := hex.DecodeString("0123456789ABCDEFFEDCBA9876543210") 22 | iv, _ := hex.DecodeString("0123456789ABCDEFFEDCBA9876543210") 23 | plainText, _ := hex.DecodeString("0123456789ABCDEFFEDCBA98765432100123456789ABCDEFFEDCBA9876543210") 24 | cipherText, _ := hex.DecodeString("2677F46B09C122CC975533105BD4A22AF6125F7275CE552C3A2BBCF533DE8A3B") 25 | 26 | block, err := sm4.NewCipher(key) 27 | if err != nil { 28 | log.Fatal("failed to create SM4 cipher: ", err) 29 | } 30 | 31 | cipherText1 := make([]byte, len(plainText)) 32 | 33 | stream := cipher.NewCBCEncrypter(block, iv) 34 | stream.CryptBlocks(cipherText1, plainText) 35 | 36 | if !bytes.Equal(cipherText1, cipherText) { 37 | log.Fatalf("exp:%x got:%x", cipherText, cipherText1) 38 | } 39 | 40 | fmt.Println("[sm4CBCEncrypt]") 41 | fmt.Println("Key=", hex.EncodeToString(key)) 42 | fmt.Println("IV=", hex.EncodeToString(iv)) 43 | fmt.Println("plainText=", hex.EncodeToString(plainText)) 44 | fmt.Println("cipherText=", hex.EncodeToString(cipherText)) 45 | } 46 | 47 | func sm4CBCDecrypt() { 48 | key, _ := hex.DecodeString("0123456789ABCDEFFEDCBA9876543210") 49 | iv, _ := hex.DecodeString("0123456789ABCDEFFEDCBA9876543210") 50 | plainText, _ := hex.DecodeString("0123456789ABCDEFFEDCBA98765432100123456789ABCDEFFEDCBA9876543210") 51 | cipherText, _ := hex.DecodeString("2677F46B09C122CC975533105BD4A22AF6125F7275CE552C3A2BBCF533DE8A3B") 52 | 53 | block, err := sm4.NewCipher(key) 54 | if err != nil { 55 | log.Fatal("failed to create SM4 cipher: ", err) 56 | } 57 | 58 | plainText1 := make([]byte, len(cipherText)) 59 | 60 | stream := cipher.NewCBCDecrypter(block, iv) 61 | stream.CryptBlocks(plainText1, cipherText) 62 | 63 | if !bytes.Equal(plainText, plainText1) { 64 | log.Fatalf("exp:%x got:%x", plainText, plainText1) 65 | } 66 | 67 | fmt.Println("[sm4CBCDecrypt]") 68 | fmt.Println("Key=", hex.EncodeToString(key)) 69 | fmt.Println("IV=", hex.EncodeToString(iv)) 70 | fmt.Println("cipherText=", hex.EncodeToString(cipherText)) 71 | fmt.Println("plainText=", hex.EncodeToString(plainText)) 72 | } 73 | 74 | func sm4GCMEncrypt() { 75 | key, _ := hex.DecodeString("0123456789ABCDEFFEDCBA9876543210") 76 | iv, _ := hex.DecodeString("00001234567800000000ABCD") 77 | aad, _ := hex.DecodeString("FEEDFACEDEADBEEFFEEDFACEDEADBEEFABADDAD2") 78 | tag, _ := hex.DecodeString("83DE3541E4C2B58177E065A9BF7B62EC") 79 | plainText, _ := hex.DecodeString("AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDEEEEEEEEEEEEEEEEFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAA") 80 | cipherText, _ := hex.DecodeString("17F399F08C67D5EE19D0DC9969C4BB7D5FD46FD3756489069157B282BB200735D82710CA5C22F0CCFA7CBF93D496AC15A56834CBCF98C397B4024A2691233B8D") 81 | 82 | block, err := sm4.NewCipher(key) 83 | if err != nil { 84 | log.Fatal("failed to create SM4 cipher: ", err) 85 | } 86 | 87 | stream, err := cipher.NewGCM(block) 88 | if err != nil { 89 | log.Fatal("failed to create GCM: ", err) 90 | } 91 | 92 | cipherText1 := stream.Seal(nil, iv, plainText, aad) 93 | 94 | if !bytes.Equal(cipherText1, append(cipherText, tag...)) { 95 | log.Fatalf("exp:%x got:%x", cipherText1, append(cipherText, tag...)) 96 | } 97 | 98 | fmt.Println("[sm4GCMEncrypt]") 99 | fmt.Println("Key=", hex.EncodeToString(key)) 100 | fmt.Println("IV=", hex.EncodeToString(iv)) 101 | fmt.Println("AAD=", hex.EncodeToString(aad)) 102 | fmt.Println("plainText=", hex.EncodeToString(plainText)) 103 | fmt.Println("cipherText=", hex.EncodeToString(cipherText)) 104 | fmt.Println("tag=", hex.EncodeToString(tag)) 105 | } 106 | 107 | func sm4GCMDecrypt() { 108 | key, _ := hex.DecodeString("0123456789ABCDEFFEDCBA9876543210") 109 | iv, _ := hex.DecodeString("00001234567800000000ABCD") 110 | aad, _ := hex.DecodeString("FEEDFACEDEADBEEFFEEDFACEDEADBEEFABADDAD2") 111 | tag, _ := hex.DecodeString("83DE3541E4C2B58177E065A9BF7B62EC") 112 | plainText, _ := hex.DecodeString("AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDEEEEEEEEEEEEEEEEFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAA") 113 | cipherText, _ := hex.DecodeString("17F399F08C67D5EE19D0DC9969C4BB7D5FD46FD3756489069157B282BB200735D82710CA5C22F0CCFA7CBF93D496AC15A56834CBCF98C397B4024A2691233B8D") 114 | 115 | block, err := sm4.NewCipher(key) 116 | if err != nil { 117 | log.Fatal("failed to create SM4 cipher: ", err) 118 | } 119 | 120 | stream, err := cipher.NewGCM(block) 121 | if err != nil { 122 | log.Fatal("failed to create GCM: ", err) 123 | } 124 | 125 | plainText1, err := stream.Open(nil, iv, append(cipherText, tag...), aad) 126 | if err != nil { 127 | log.Fatal("failed to decrypt: ", err) 128 | } 129 | 130 | if !bytes.Equal(plainText1, plainText) { 131 | log.Fatalf("exp:%x got:%x", plainText1, plainText) 132 | } 133 | 134 | fmt.Println("[sm4GCMDecrypt]") 135 | fmt.Println("Key=", hex.EncodeToString(key)) 136 | fmt.Println("IV=", hex.EncodeToString(iv)) 137 | fmt.Println("AAD=", hex.EncodeToString(aad)) 138 | fmt.Println("tag=", hex.EncodeToString(tag)) 139 | fmt.Println("cipherText=", hex.EncodeToString(cipherText)) 140 | fmt.Println("plainText=", hex.EncodeToString(plainText)) 141 | } 142 | 143 | func main() { 144 | sm4CBCEncrypt() 145 | sm4CBCDecrypt() 146 | sm4GCMEncrypt() 147 | sm4GCMDecrypt() 148 | } 149 | -------------------------------------------------------------------------------- /crypto/sm3/sm3_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Tongsuo Project Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License 2.0 (the "License"). You may not use 4 | // this file except in compliance with the License. You can obtain a copy 5 | // in the file LICENSE in the source distribution or at 6 | // https://github.com/Tongsuo-Project/tongsuo-go-sdk/blob/main/LICENSE 7 | 8 | package sm3_test 9 | 10 | import ( 11 | "bytes" 12 | "crypto/rand" 13 | "encoding/hex" 14 | "io" 15 | "strings" 16 | "testing" 17 | 18 | "github.com/tongsuo-project/tongsuo-go-sdk/crypto/sm3" 19 | ) 20 | 21 | func TestSM3(t *testing.T) { 22 | t.Parallel() 23 | 24 | testData := []struct { 25 | in string 26 | out string 27 | }{ 28 | { 29 | `0090414C494345313233405941484F4F2E434F4D787968B4FA32C3FD2417842E73BBFEFF2F3C848B6831D7E0EC65228B3937E49863E4C6 30 | D3B23B0C849CF84241484BFE48F61D59A5B16BA06E6E12D1DA27C5249A421DEBD61B62EAB6746434EBC3CC315E32220B3BADD50BDC4C4E6C147FEDD4 31 | 3D0680512BCBB42C07D47349D2153B70C4E5D7FDFCBFA36EA1A85841B9E46E09A20AE4C7798AA0F119471BEE11825BE46202BB79E2A5844495E97C04 32 | FF4DF2548A7C0240F88F1CD4E16352A73C17B7F16F07353E53A176D684A9FE0C6BB798E857`, 33 | "F4A38489E32B45B6F876E3AC2168CA392362DC8F23459C1D1146FC3DBFB7BC9A", 34 | }, 35 | {"616263", "66C7F0F462EEEDD9D1F2D46BDC10E4E24167C4875CF2F7A2297DA02B8F4BA8E0"}, 36 | {`61626364616263646162636461626364616263646162636461626364616263646162636461626364616263646162636461626364616263 37 | 646162636461626364`, "DEBE9FF92275B8A138604889C18E5A4D6FDB70E5387E5765293dCbA39C0C5732"}, 38 | { 39 | `0090414C494345313233405941484F4F2E434F4D787968B4FA32C3FD2417842E73BBFEFF2F3C848B6831D7E0EC65228B3937E49863E4C6 40 | D3B23B0C849CF84241484BFE48F61D59A5B16BA06E6E12D1DA27C5249A421DEBD61B62EAB6746434EBC3CC315E32220B3BADD50BDC4C4E6C147FEDD4 41 | 3D0680512BCBB42C07D47349D2153B70C4E5D7FDFCBFA36EA1A85841B9E46E09A20AE4C7798AA0F119471BEE11825BE46202BB79E2A5844495E97C04 42 | FF4DF2548A7C0240F88F1CD4E16352A73C17B7F16F07353E53A176D684A9FE0C6BB798E857`, 43 | "F4A38489E32B45B6F876E3AC2168CA392362DC8F23459C1D1146FC3DBFB7BC9A", 44 | }, 45 | { 46 | `0090414C494345313233405941484F4F2E434F4D00000000000000000000000000000000000000000000000000000000000000000000E7 47 | 8BCD09746C202378A7E72B12BCE00266B9627ECB0B5A25367AD1AD4CC6242B00CDB9CA7F1E6B0441F658343F4B10297C0EF9B6491082400A62E7A748 48 | 5735FADD013DE74DA65951C4D76DC89220D5F7777A611B1C38BAE260B175951DC8060C2B3E0165961645281A8626607B917F657D7E9382F1EA5CD931 49 | F40F6627F357542653B201686522130D590FB8DE635D8FCA715CC6BF3D05BEF3F75DA5D543454448166612`, 50 | "26352AF82EC19F207BBC6F9474E11E90CE0F7DDACE03B27F801817E897A81FD5", 51 | }, 52 | { 53 | `0090414C494345313233405941484F4F2E434F4D787968B4FA32C3FD2417842E73BBFEFF2F3C848B6831D7E0EC65228B3937E49863E4C6 54 | D3B23B0C849CF84241484BFE48F61D59A5B16BA06E6E12D1DA27C5249A421DEBD61B62EAB6746434EBC3CC315E32220B3BADD50BDC4C4E6C147FEDD4 55 | 3D0680512BCBB42C07D47349D2153B70C4E5D7FDFCBFA36EA1A85841B9E46E09A23099093BF3C137D8FCBBCDF4A2AE50F3B0F216C3122D79425FE03A 56 | 45DBFE16553DF79E8DAC1CF0ECBAA2F2B49D51A4B387F2EFAF482339086A27A8E05BAED98B`, 57 | "E4D1D0C3CA4C7F11BC8FF8CB3F4C02A78F108FA098E51A668487240F75E20F31", 58 | }, 59 | { 60 | `008842494C4C343536405941484F4F2E434F4D787968B4FA32C3FD2417842E73BBFEFF2F3C848B6831D7E0EC65228B3937E49863E4C6D3 61 | B23B0C849CF84241484BFE48F61D59A5B16BA06E6E12D1DA27C5249A421DEBD61B62EAB6746434EBC3CC315E32220B3BADD50BDC4C4E6C147FEDD43D 62 | 0680512BCBB42C07D47349D2153B70C4E5D7FDFCBFA36EA1A85841B9E46E09A2245493D446C38D8CC0F118374690E7DF633A8A4BFB3329B5ECE604B2 63 | B4F37F4353C0869F4B9E17773DE68FEC45E14904E0DEA45BF6CECF9918C85EA047C60A4C`, 64 | "6B4B6D0E276691BD4A11BF72F4FB501AE309FDACB72FA6CC336E6656119ABD67", 65 | }, 66 | { 67 | `4D38D2958CA7FD2CFAE3AF04486959CF92C8EF48E8B83A05C112E739D5F181D03082020CA003020102020900AF28725D98D33143300C06 68 | 082A811CCF550183750500307D310B300906035504060C02636E310B300906035504080C02626A310B300906035504070C02626A310F300D06035504 69 | 0A0C06746F70736563310F300D060355040B0C06746F707365633111300F06035504030C08546F707365634341311F301D06092A864886F70D010901 70 | 0C10626A40746F707365632E636F6D2E636E301E170D3132303632343037353433395A170D3332303632303037353433395A307D310B300906035504 71 | 060C02636E310B300906035504080C02626A310B300906035504070C02626A310F300D060355040A0C06746F70736563310F300D060355040B0C0674 72 | 6F707365633111300F06035504030C08546F707365634341311F301D06092A864886F70D0109010C10626A40746F707365632E636F6D2E636E305930 73 | 1306072A8648CE3D020106082A811CCF5501822D03420004D69C2F1EEC3BFB6B95B30C28085C77B125D77A9C39525D8190768F37D6B205B589DCD316 74 | BBE7D89A9DC21917F17799E698531F5E6E3E10BD31370B259C3F81C3A3733071300F0603551D130101FF040530030101FF301D0603551D0E04160414 75 | 8E5D90347858BAAAD870D8BDFBA6A85E7B563B64301F0603551D230418301680148E5D90347858BAAAD870D8BDFBA6A85E7B563B64300B0603551D0F 76 | 040403020106301106096086480186F8420101040403020057`, 77 | "C3B02E500A8B60B77DEDCF6F4C11BEF8D56E5CDE708C72065654FD7B2167915A", 78 | }, 79 | } 80 | 81 | for _, tt := range testData { 82 | buf, _ := hex.DecodeString(strings.ReplaceAll(tt.in, "\n", "")) 83 | got := sm3.Sum(buf) 84 | expected, _ := hex.DecodeString(tt.out) 85 | 86 | if !bytes.Equal(expected, got[:]) { 87 | t.Fatalf("exp:%x got:%x", expected, got) 88 | } 89 | } 90 | } 91 | 92 | type sm3func func([]byte) 93 | 94 | func benchmarkSM3(b *testing.B, length int64, fn sm3func) { 95 | b.Helper() 96 | 97 | buf := make([]byte, length) 98 | if _, err := io.ReadFull(rand.Reader, buf); err != nil { 99 | b.Fatal(err) 100 | } 101 | 102 | b.SetBytes(length) 103 | b.ResetTimer() 104 | 105 | for i := 0; i < b.N; i++ { 106 | fn(buf) 107 | } 108 | } 109 | 110 | func BenchmarkSM3Large(b *testing.B) { 111 | benchmarkSM3(b, 1024*1024, func(buf []byte) { sm3.Sum(buf) }) 112 | } 113 | 114 | func BenchmarkSM3Normal(b *testing.B) { 115 | benchmarkSM3(b, 1024, func(buf []byte) { sm3.Sum(buf) }) 116 | } 117 | 118 | func BenchmarkSM3Small(b *testing.B) { 119 | benchmarkSM3(b, 1, func(buf []byte) { sm3.Sum(buf) }) 120 | } 121 | -------------------------------------------------------------------------------- /examples/tlcp_client/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Tongsuo Project Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License 2.0 (the "License"). You may not use 4 | // this file except in compliance with the License. You can obtain a copy 5 | // in the file LICENSE in the source distribution or at 6 | // https://github.com/Tongsuo-Project/tongsuo-go-sdk/blob/main/LICENSE 7 | 8 | package main 9 | 10 | import ( 11 | "bufio" 12 | "flag" 13 | "fmt" 14 | "os" 15 | "strings" 16 | 17 | ts "github.com/tongsuo-project/tongsuo-go-sdk" 18 | "github.com/tongsuo-project/tongsuo-go-sdk/crypto" 19 | ) 20 | 21 | var cipherSuites = "" 22 | 23 | func main() { 24 | cipherSuite := "" 25 | signCertFile := "" 26 | signKeyFile := "" 27 | encCertFile := "" 28 | encKeyFile := "" 29 | caFile := "" 30 | connAddr := "" 31 | serverName := "" 32 | alpnProtocols := []string{"h2", "http/1.1"} 33 | tlsVersion := "" 34 | 35 | flag.StringVar(&connAddr, "conn", "127.0.0.1:4438", "host:port") 36 | flag.StringVar(&cipherSuite, "cipher", "ECC-SM2-SM4-CBC-SM3", "cipher suite") 37 | flag.StringVar(&signCertFile, "sign_cert", "test/certs/sm2/client_sign.crt", "sign certificate file") 38 | flag.StringVar(&signKeyFile, "sign_key", "test/certs/sm2/client_sign.key", "sign private key file") 39 | flag.StringVar(&encCertFile, "enc_cert", "test/certs/sm2/client_enc.crt", "encrypt certificate file") 40 | flag.StringVar(&encKeyFile, "enc_key", "test/certs/sm2/client_enc.key", "encrypt private key file") 41 | flag.StringVar(&caFile, "CAfile", "test/certs/sm2/chain-ca.crt", "CA certificate file") 42 | flag.StringVar(&serverName, "servername", "", "server name") 43 | flag.Var((*stringSlice)(&alpnProtocols), "alpn", "ALPN protocols") 44 | flag.StringVar(&tlsVersion, "version", "NTLS", "TLS version") 45 | flag.StringVar(&cipherSuites, "ciphersuites", "ECC-SM2-SM4-CBC-SM3", "cipherSuites") 46 | flag.Parse() 47 | 48 | var version ts.SSLVersion 49 | 50 | switch tlsVersion { 51 | case "TLSv1.3": 52 | version = ts.TLSv1_3 53 | case "TLSv1.2": 54 | version = ts.TLSv1_2 55 | case "TLSv1.1": 56 | version = ts.TLSv1_1 57 | case "TLSv1": 58 | version = ts.TLSv1 59 | case "NTLS": 60 | version = ts.NTLS 61 | default: 62 | version = ts.NTLS 63 | } 64 | 65 | ctx, err := ts.NewCtxWithVersion(version) 66 | if err != nil { 67 | panic("NewCtxWithVersion failed: " + err.Error()) 68 | } 69 | 70 | if err := ctx.SetClientALPNProtos(alpnProtocols); err != nil { 71 | panic(err) 72 | } 73 | 74 | if version >= ts.TLSv1_3 { 75 | if err := ctx.SetCipherSuites(cipherSuites); err != nil { 76 | panic(err) 77 | } 78 | } else { 79 | if err := ctx.SetCipherList(cipherSuites); err != nil { 80 | panic(err) 81 | } 82 | 83 | if signCertFile != "" { 84 | signCertPEM, err := os.ReadFile(signCertFile) 85 | if err != nil { 86 | panic(err) 87 | } 88 | 89 | signCert, err := crypto.LoadCertificateFromPEM(signCertPEM) 90 | if err != nil { 91 | panic(err) 92 | } 93 | 94 | if err := ctx.UseSignCertificate(signCert); err != nil { 95 | panic(err) 96 | } 97 | } 98 | 99 | if signKeyFile != "" { 100 | signKeyPEM, err := os.ReadFile(signKeyFile) 101 | if err != nil { 102 | panic(err) 103 | } 104 | 105 | signKey, err := crypto.LoadPrivateKeyFromPEM(signKeyPEM) 106 | if err != nil { 107 | panic(err) 108 | } 109 | 110 | if err := ctx.UseSignPrivateKey(signKey); err != nil { 111 | panic(err) 112 | } 113 | } 114 | 115 | if encCertFile != "" { 116 | encCertPEM, err := os.ReadFile(encCertFile) 117 | if err != nil { 118 | panic(err) 119 | } 120 | 121 | encCert, err := crypto.LoadCertificateFromPEM(encCertPEM) 122 | if err != nil { 123 | panic(err) 124 | } 125 | 126 | if err := ctx.UseEncryptCertificate(encCert); err != nil { 127 | panic(err) 128 | } 129 | } 130 | 131 | if encKeyFile != "" { 132 | encKeyPEM, err := os.ReadFile(encKeyFile) 133 | if err != nil { 134 | panic(err) 135 | } 136 | 137 | encKey, err := crypto.LoadPrivateKeyFromPEM(encKeyPEM) 138 | if err != nil { 139 | panic(err) 140 | } 141 | 142 | if err := ctx.UseEncryptPrivateKey(encKey); err != nil { 143 | panic(err) 144 | } 145 | } 146 | 147 | if caFile != "" { 148 | if err := ctx.LoadVerifyLocations(caFile, ""); err != nil { 149 | panic(err) 150 | } 151 | } 152 | } 153 | 154 | conn, err := ts.Dial("tcp", connAddr, ctx, ts.InsecureSkipHostVerification, serverName) 155 | if err != nil { 156 | panic("connected failed" + err.Error()) 157 | } 158 | defer conn.Close() 159 | 160 | // Get the negotiated ALPN protocol 161 | negotiatedProto, err := conn.GetALPNNegotiated() 162 | if err != nil { 163 | fmt.Println("Failed to get negotiated ALPN protocol:", err) 164 | } else { 165 | fmt.Println("Negotiated ALPN protocol:", negotiatedProto) 166 | } 167 | 168 | cipher, err := conn.CurrentCipher() 169 | if err != nil { 170 | panic(err) 171 | } 172 | 173 | ver, err := conn.GetVersion() 174 | if err != nil { 175 | panic(err) 176 | } 177 | 178 | fmt.Println("New connection: " + ver + ", cipher=" + cipher) 179 | 180 | reader := bufio.NewReader(os.Stdin) 181 | text, _ := reader.ReadString('\n') 182 | 183 | request := text + "\n" 184 | fmt.Println(">>>\n" + request) 185 | 186 | if _, err := conn.Write([]byte(request)); err != nil { 187 | panic(err) 188 | } 189 | 190 | buffer := make([]byte, 4096) 191 | 192 | n, err := conn.Read(buffer) 193 | if err != nil { 194 | fmt.Println("read error:", err) 195 | return 196 | } 197 | 198 | fmt.Println("<<<\n" + string(buffer[:n])) 199 | 200 | return 201 | } 202 | 203 | // Define a custom type to handle string slices in command line flags 204 | type stringSlice []string 205 | 206 | // String method returns the string representation of the stringSlice 207 | func (s *stringSlice) String() string { 208 | return fmt.Sprintf("%v", *s) 209 | } 210 | 211 | // Set method splits the input string by commas and assigns the result to the stringSlice 212 | func (s *stringSlice) Set(value string) error { 213 | *s = strings.Split(value, ",") 214 | return nil 215 | } 216 | -------------------------------------------------------------------------------- /crypto/shim.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | /* shim methods */ 31 | extern int X_tscrypto_init(); 32 | 33 | /* Library methods */ 34 | extern void X_OPENSSL_free(void *ref); 35 | extern void *X_OPENSSL_malloc(size_t size); 36 | 37 | /* BIO methods */ 38 | extern int X_BIO_get_flags(BIO *b); 39 | extern void X_BIO_set_flags(BIO *bio, int flags); 40 | extern void X_BIO_clear_flags(BIO *bio, int flags); 41 | extern void X_BIO_set_data(BIO *bio, void* data); 42 | extern void *X_BIO_get_data(BIO *bio); 43 | extern int X_BIO_read(BIO *b, void *buf, int len); 44 | extern int X_BIO_write(BIO *b, const void *buf, int len); 45 | extern BIO *X_BIO_new_write_bio(); 46 | extern BIO *X_BIO_new_read_bio(); 47 | extern long X_BIO_get_mem_data(BIO *b, char **pp); 48 | 49 | extern int X_BN_num_bytes(const BIGNUM *a); 50 | 51 | /* EVP methods */ 52 | extern const int X_ED25519_SUPPORT; 53 | extern int X_EVP_PKEY_ED25519; 54 | extern const EVP_MD *X_EVP_get_digestbyname(const char *name); 55 | extern EVP_MD_CTX *X_EVP_MD_CTX_new(); 56 | extern int X_EVP_MD_CTX_copy_ex(EVP_MD_CTX *out, const EVP_MD_CTX *in); 57 | extern void X_EVP_MD_CTX_free(EVP_MD_CTX *ctx); 58 | extern const EVP_MD *X_EVP_md_null(); 59 | extern const EVP_MD *X_EVP_md5(); 60 | extern const EVP_MD *X_EVP_md4(); 61 | extern const EVP_MD *X_EVP_sha(); 62 | extern const EVP_MD *X_EVP_sha1(); 63 | extern const EVP_MD *X_EVP_dss(); 64 | extern const EVP_MD *X_EVP_dss1(); 65 | extern const EVP_MD *X_EVP_ripemd160(); 66 | extern const EVP_MD *X_EVP_sha224(); 67 | extern const EVP_MD *X_EVP_sha256(); 68 | extern const EVP_MD *X_EVP_sha384(); 69 | extern const EVP_MD *X_EVP_sha512(); 70 | extern const EVP_MD *X_EVP_sm3(); 71 | extern int X_EVP_MD_size(const EVP_MD *md); 72 | extern int X_EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl); 73 | extern int X_EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *d, size_t cnt); 74 | extern int X_EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *s); 75 | extern int X_EVP_DigestSignInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx, const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey); 76 | extern int X_EVP_DigestSignUpdate(EVP_MD_CTX *ctx, const void *d, size_t cnt); 77 | extern int X_EVP_DigestSignFinal(EVP_MD_CTX *ctx, unsigned char *sig, size_t *siglen); 78 | extern int X_EVP_DigestSign(EVP_MD_CTX *ctx, unsigned char *sigret, size_t *siglen, const unsigned char *tbs, size_t tbslen); 79 | extern int X_EVP_Digest(const void *data, size_t count, unsigned char *md, unsigned int *size, const EVP_MD *type, ENGINE *impl); 80 | extern EVP_PKEY *X_EVP_PKEY_new(void); 81 | extern void X_EVP_PKEY_free(EVP_PKEY *pkey); 82 | extern int X_EVP_PKEY_size(EVP_PKEY *pkey); 83 | extern struct rsa_st *X_EVP_PKEY_get1_RSA(EVP_PKEY *pkey); 84 | extern int X_EVP_PKEY_set1_RSA(EVP_PKEY *pkey, struct rsa_st *key); 85 | extern int X_EVP_PKEY_assign_charp(EVP_PKEY *pkey, int type, char *key); 86 | extern int X_EVP_DigestVerifyInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx, const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey); 87 | extern int X_EVP_DigestVerifyUpdate(EVP_MD_CTX *ctx, const void *d, size_t cnt); 88 | extern int X_EVP_DigestVerifyFinal(EVP_MD_CTX *ctx, const unsigned char *sig, size_t siglen); 89 | extern int X_EVP_DigestVerify(EVP_MD_CTX *ctx, const unsigned char *sigret, size_t siglen, const unsigned char *tbs, size_t tbslen); 90 | extern int X_EVP_CIPHER_block_size(EVP_CIPHER *c); 91 | extern int X_EVP_CIPHER_key_length(EVP_CIPHER *c); 92 | extern int X_EVP_CIPHER_iv_length(EVP_CIPHER *c); 93 | extern int X_EVP_CIPHER_nid(EVP_CIPHER *c); 94 | extern int X_EVP_CIPHER_CTX_block_size(EVP_CIPHER_CTX *ctx); 95 | extern int X_EVP_CIPHER_CTX_key_length(EVP_CIPHER_CTX *ctx); 96 | extern int X_EVP_CIPHER_CTX_iv_length(EVP_CIPHER_CTX *ctx); 97 | extern void X_EVP_CIPHER_CTX_set_padding(EVP_CIPHER_CTX *ctx, int padding); 98 | extern const EVP_CIPHER *X_EVP_CIPHER_CTX_cipher(EVP_CIPHER_CTX *ctx); 99 | extern int X_EVP_CIPHER_CTX_encrypting(const EVP_CIPHER_CTX *ctx); 100 | extern int X_EVP_PKEY_CTX_set_ec_paramgen_curve_nid(EVP_PKEY_CTX *ctx, int nid); 101 | extern int X_EVP_PKEY_CTX_set1_id(EVP_PKEY_CTX *ctx, void *id, int id_len); 102 | extern int X_EVP_PKEY_is_sm2(EVP_PKEY *pkey); 103 | 104 | /* HMAC methods */ 105 | extern size_t X_HMAC_size(const HMAC_CTX *e); 106 | extern HMAC_CTX *X_HMAC_CTX_new(void); 107 | extern void X_HMAC_CTX_free(HMAC_CTX *ctx); 108 | extern int X_HMAC_Init_ex(HMAC_CTX *ctx, const void *key, int len, const EVP_MD *md, ENGINE *impl); 109 | extern int X_HMAC_Update(HMAC_CTX *ctx, const unsigned char *data, size_t len); 110 | extern int X_HMAC_Final(HMAC_CTX *ctx, unsigned char *md, unsigned int *len); 111 | 112 | /* X509 methods */ 113 | extern const ASN1_TIME *X_X509_get0_notBefore(const X509 *x); 114 | extern const ASN1_TIME *X_X509_get0_notAfter(const X509 *x); 115 | extern long X_X509_get_version(const X509 *x); 116 | extern int X_X509_set_version(X509 *x, long version); 117 | 118 | /* PEM methods */ 119 | extern int X_PEM_write_bio_PrivateKey_traditional(BIO *bio, EVP_PKEY *key, const EVP_CIPHER *enc, unsigned char *kstr, int klen, pem_password_cb *cb, void *u); 120 | 121 | /* ASN.1 methods */ 122 | extern ECDSA_SIG *X_d2i_ECDSA_SIG(ECDSA_SIG **psig, const unsigned char **ppin, long len); 123 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: CI 5 | 6 | on: [push, pull_request] 7 | 8 | jobs: 9 | golang-lint: 10 | runs-on: ubuntu-22.04 11 | steps: 12 | - uses: actions/checkout@v3 13 | 14 | - name: Set up Go 15 | uses: actions/setup-go@v3 16 | with: 17 | go-version: 1.19 18 | 19 | - name: Go fmt Check 20 | uses: Jerome1337/gofmt-action@v1.0.5 21 | with: 22 | gofmt-path: './' 23 | gofmt-flags: '-l -d' 24 | 25 | - name: Go Mod 26 | run: go mod tidy 27 | 28 | - name: Clone Tongsuo 29 | uses: actions/checkout@v3 30 | with: 31 | repository: Tongsuo-Project/Tongsuo 32 | path: Tongsuo 33 | ref: 8.3-stable 34 | 35 | - name: Build Tongsuo 36 | run: | 37 | cd Tongsuo 38 | ./config --prefix=${RUNNER_TEMP}/tongsuo --libdir=${RUNNER_TEMP}/tongsuo/lib enable-ntls enable-export-sm4 39 | make -j4 40 | make install 41 | 42 | - name: Golang lint 43 | run: | 44 | curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b /usr/local/bin v1.61.0 45 | LD_LIBRARY_PATH=${RUNNER_TEMP}/tongsuo/lib CGO_CFLAGS="-Wall -I${RUNNER_TEMP}/tongsuo/include -Wno-deprecated-declarations" CGO_LDFLAGS="-L${RUNNER_TEMP}/tongsuo/lib" golangci-lint run ./... 46 | 47 | 48 | build-and-test: 49 | strategy: 50 | fail-fast: false 51 | matrix: 52 | os: [ubuntu-latest, macos-latest] 53 | runs-on: ${{matrix.os}} 54 | steps: 55 | - uses: actions/checkout@v3 56 | 57 | - name: Set up Go 58 | uses: actions/setup-go@v3 59 | with: 60 | go-version: 1.19 61 | 62 | - name: Clone Tongsuo 63 | uses: actions/checkout@v3 64 | with: 65 | repository: Tongsuo-Project/Tongsuo 66 | path: Tongsuo 67 | ref: 8.3-stable 68 | 69 | - name: Build Tongsuo 70 | run: | 71 | cd Tongsuo 72 | ./config --prefix=${RUNNER_TEMP}/tongsuo --libdir=${RUNNER_TEMP}/tongsuo/lib enable-ntls enable-export-sm4 73 | make -j4 74 | make install 75 | 76 | - name: Build 77 | run: CGO_CFLAGS="-Wall -I${RUNNER_TEMP}/tongsuo/include -Wno-deprecated-declarations" CGO_LDFLAGS="-L${RUNNER_TEMP}/tongsuo/lib" go build 78 | 79 | - name: Test on Ubuntu 80 | run: LD_LIBRARY_PATH=${RUNNER_TEMP}/tongsuo/lib CGO_CFLAGS="-Wall -I${RUNNER_TEMP}/tongsuo/include -Wno-deprecated-declarations" CGO_LDFLAGS="-L${RUNNER_TEMP}/tongsuo/lib" go test ./... 81 | if: matrix.os == 'ubuntu-latest' 82 | 83 | - name: Test on macOS 84 | run: DYLD_LIBRARY_PATH=${RUNNER_TEMP}/tongsuo/lib CGO_CFLAGS="-Wall -I${RUNNER_TEMP}/tongsuo/include -Wno-deprecated-declarations" CGO_LDFLAGS="-L${RUNNER_TEMP}/tongsuo/lib" go test ./... 85 | if: matrix.os == 'macos-latest' 86 | 87 | build-static: 88 | strategy: 89 | fail-fast: false 90 | matrix: 91 | os: [ubuntu-latest, macos-latest] 92 | runs-on: ${{matrix.os}} 93 | steps: 94 | - uses: actions/checkout@v3 95 | 96 | - name: Set up Go 97 | uses: actions/setup-go@v3 98 | with: 99 | go-version: 1.19 100 | 101 | - name: Clone Tongsuo 102 | uses: actions/checkout@v3 103 | with: 104 | repository: Tongsuo-Project/Tongsuo 105 | path: tongsuo 106 | ref: 8.3-stable 107 | 108 | - name: Build Tongsuo Static 109 | run: | 110 | cd tongsuo 111 | ./config --prefix=${RUNNER_TEMP}/tongsuo --libdir=${RUNNER_TEMP}/tongsuo/lib enable-ntls enable-export-sm4 no-shared 112 | make -j4 113 | make install 114 | 115 | - name: Build 116 | run: CGO_CFLAGS="-Wall -I${RUNNER_TEMP}/tongsuo/include -Wno-deprecated-declarations" CGO_LDFLAGS="-L${RUNNER_TEMP}/tongsuo/lib" go build 117 | 118 | - name: Test on Ubuntu 119 | run: LD_LIBRARY_PATH=${RUNNER_TEMP}/tongsuo/lib CGO_CFLAGS="-Wall -I${RUNNER_TEMP}/tongsuo/include -Wno-deprecated-declarations" CGO_LDFLAGS="-L${RUNNER_TEMP}/tongsuo/lib" go test ./... 120 | if: matrix.os == 'ubuntu-latest' 121 | 122 | - name: Test on macOS 123 | run: DYLD_LIBRARY_PATH=${RUNNER_TEMP}/tongsuo/lib CGO_CFLAGS="-Wall -I${RUNNER_TEMP}/tongsuo/include -Wno-deprecated-declarations" CGO_LDFLAGS="-L${RUNNER_TEMP}/tongsuo/lib" go test ./... 124 | if: matrix.os == 'macos-latest' 125 | 126 | build-on-windows: 127 | runs-on: windows-latest 128 | steps: 129 | - uses: actions/checkout@v3 130 | 131 | - name: Set up Go 132 | uses: actions/setup-go@v3 133 | with: 134 | go-version: 1.19 135 | 136 | - name: Clone Tongsuo 137 | uses: actions/checkout@v3 138 | with: 139 | repository: Tongsuo-Project/Tongsuo 140 | path: Tongsuo 141 | ref: 8.3-stable 142 | - uses: ilammy/msvc-dev-cmd@v1 143 | - uses: ilammy/setup-nasm@v1 144 | - uses: shogo82148/actions-setup-perl@v1 145 | - name: Build Tongsuo 146 | shell: cmd 147 | run: | 148 | mkdir _build 149 | cd _build 150 | perl ..\Configure VC-WIN64A no-makedepend --prefix=%RUNNER_TEMP%\tongsuo enable-ntls enable-export-sm4 151 | nmake /S 152 | nmake install 153 | working-directory: Tongsuo 154 | 155 | - name: Build 156 | shell: cmd 157 | run: | 158 | set CGO_CFLAGS=-Wall -I%RUNNER_TEMP%\tongsuo\include -Wno-deprecated-declarations 159 | set CGO_LDFLAGS=-L%RUNNER_TEMP%\tongsuo\lib" 160 | go build 161 | 162 | - name: Set PATH for go test runtime library search 163 | shell: perl {0} 164 | run: | 165 | use Actions::Core; 166 | add_path("$ENV{RUNNER_TEMP}\\tongsuo\\bin"); 167 | add_path("$ENV{RUNNER_TEMP}\\tongsuo\\lib"); 168 | 169 | - name: Test on Windows 170 | shell: cmd 171 | run: | 172 | copy /y %RUNNER_TEMP%\tongsuo\bin\*.dll "C:\Program Files\MySQL\MySQL Server 8.0\bin" 173 | copy /y %RUNNER_TEMP%\tongsuo\bin\*.dll "C:\Program Files\OpenSSL\bin" 174 | copy /y %RUNNER_TEMP%\tongsuo\bin\*.dll C:\Windows\system32 175 | copy /y %RUNNER_TEMP%\tongsuo\bin\*.dll C:\Strawberry\c\bin 176 | copy /y %RUNNER_TEMP%\tongsuo\bin\*.dll "C:\Program Files\Microsoft Service Fabric\bin\Fabric\Fabric.Code" 177 | copy /y %RUNNER_TEMP%\tongsuo\bin\*.dll "C:\Program Files\Git\mingw64\bin" 178 | copy /y %RUNNER_TEMP%\tongsuo\bin\*.dll c:\tools\php 179 | copy /y %RUNNER_TEMP%\tongsuo\bin\*.dll "C:\Program Files\Amazon\AWSCLIV2" 180 | set CGO_CFLAGS=-Wall -I%RUNNER_TEMP%\tongsuo\include -Wno-deprecated-declarations 181 | set CGO_LDFLAGS=-L%RUNNER_TEMP%\tongsuo\lib 182 | go env 183 | echo %PATH% 184 | go test ./... 185 | -------------------------------------------------------------------------------- /ssl.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package tongsuogo 16 | 17 | // #include "shim.h" 18 | import "C" 19 | 20 | import ( 21 | "os" 22 | "unsafe" 23 | ) 24 | 25 | type SSLTLSExtErr int 26 | 27 | const ( 28 | SSLTLSExtErrOK SSLTLSExtErr = C.SSL_TLSEXT_ERR_OK 29 | SSLTLSExtErrAlertWarning SSLTLSExtErr = C.SSL_TLSEXT_ERR_ALERT_WARNING 30 | SSLTLSExtErrAlertFatal SSLTLSExtErr = C.SSL_TLSEXT_ERR_ALERT_FATAL 31 | SSLTLSExtErrNoAck SSLTLSExtErr = C.SSL_TLSEXT_ERR_NOACK 32 | ) 33 | 34 | const ( 35 | NPNNegotiated C.int = C.OPENSSL_NPN_NEGOTIATED 36 | NPNNoOverlap C.int = C.OPENSSL_NPN_NO_OVERLAP 37 | ) 38 | 39 | var sslIdx = C.X_SSL_new_index() 40 | 41 | //export get_ssl_idx 42 | func get_ssl_idx() C.int { 43 | return sslIdx 44 | } 45 | 46 | type SSL struct { 47 | ssl *C.SSL 48 | verifyCb VerifyCallback 49 | } 50 | 51 | //export go_ssl_verify_cb_thunk 52 | func go_ssl_verify_cb_thunk(callback unsafe.Pointer, ok C.int, ctx *C.X509_STORE_CTX) C.int { 53 | defer func() { 54 | if err := recover(); err != nil { 55 | // logger.Critf("openssl: verify callback panic'd: %v", err) 56 | os.Exit(1) 57 | } 58 | }() 59 | verifyCb := (*SSL)(callback).verifyCb 60 | // set up defaults just in case verify_cb is nil 61 | if verifyCb != nil { 62 | store := &CertificateStoreCtx{ctx: ctx, sslCtx: nil} 63 | if verifyCb(ok == 1, store) { 64 | ok = 1 65 | } else { 66 | ok = 0 67 | } 68 | } 69 | return ok 70 | } 71 | 72 | // Wrapper around SSL_get_servername. Returns server name according to rfc6066 73 | // http://tools.ietf.org/html/rfc6066. 74 | func (s *SSL) GetServername() string { 75 | return C.GoString(C.SSL_get_servername(s.ssl, C.TLSEXT_NAMETYPE_host_name)) 76 | } 77 | 78 | // GetOptions returns SSL options. See 79 | // https://www.openssl.org/docs/ssl/SSL_CTX_set_options.html 80 | func (s *SSL) GetOptions() Options { 81 | return Options(C.X_SSL_get_options(s.ssl)) 82 | } 83 | 84 | // SetOptions sets SSL options. See 85 | // https://www.openssl.org/docs/ssl/SSL_CTX_set_options.html 86 | func (s *SSL) SetOptions(options Options) Options { 87 | return Options(C.X_SSL_set_options(s.ssl, C.long(options))) 88 | } 89 | 90 | // ClearOptions clear SSL options. See 91 | // https://www.openssl.org/docs/ssl/SSL_CTX_set_options.html 92 | func (s *SSL) ClearOptions(options Options) Options { 93 | return Options(C.X_SSL_clear_options(s.ssl, C.long(options))) 94 | } 95 | 96 | // SetVerify controls peer verification settings. See 97 | // http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html 98 | func (s *SSL) SetVerify(options VerifyOptions, verifyCb VerifyCallback) { 99 | s.verifyCb = verifyCb 100 | if verifyCb != nil { 101 | C.SSL_set_verify(s.ssl, C.int(options), (*[0]byte)(C.X_SSL_verify_cb)) 102 | } else { 103 | C.SSL_set_verify(s.ssl, C.int(options), nil) 104 | } 105 | } 106 | 107 | // SetVerifyMode controls peer verification setting. See 108 | // http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html 109 | func (s *SSL) SetVerifyMode(options VerifyOptions) { 110 | s.SetVerify(options, s.verifyCb) 111 | } 112 | 113 | // SetVerifyCallback controls peer verification setting. See 114 | // http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html 115 | func (s *SSL) SetVerifyCallback(verifyCb VerifyCallback) { 116 | s.SetVerify(s.VerifyMode(), verifyCb) 117 | } 118 | 119 | // GetVerifyCallback returns callback function. See 120 | // http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html 121 | func (s *SSL) GetVerifyCallback() VerifyCallback { 122 | return s.verifyCb 123 | } 124 | 125 | // VerifyMode returns peer verification setting. See 126 | // http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html 127 | func (s *SSL) VerifyMode() VerifyOptions { 128 | return VerifyOptions(C.SSL_get_verify_mode(s.ssl)) 129 | } 130 | 131 | // SetVerifyDepth controls how many certificates deep the certificate 132 | // verification logic is willing to follow a certificate chain. See 133 | // https://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html 134 | func (s *SSL) SetVerifyDepth(depth int) { 135 | C.SSL_set_verify_depth(s.ssl, C.int(depth)) 136 | } 137 | 138 | // GetVerifyDepth controls how many certificates deep the certificate 139 | // verification logic is willing to follow a certificate chain. See 140 | // https://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html 141 | func (s *SSL) GetVerifyDepth() int { 142 | return int(C.SSL_get_verify_depth(s.ssl)) 143 | } 144 | 145 | // SetSSLCtx changes context to new one. Useful for Server Name Indication (SNI) 146 | // rfc6066 http://tools.ietf.org/html/rfc6066. See 147 | // http://stackoverflow.com/questions/22373332/serving-multiple-domains-in-one-box-with-sni 148 | func (s *SSL) SetSSLCtx(ctx *Ctx) { 149 | /* 150 | * SSL_set_SSL_CTX() only changes certs as of 1.0.0d 151 | * adjust other things we care about 152 | */ 153 | C.SSL_set_SSL_CTX(s.ssl, ctx.ctx) 154 | } 155 | 156 | //export sniCbThunk 157 | func sniCbThunk(callback unsafe.Pointer, con *C.SSL, ad unsafe.Pointer, arg unsafe.Pointer) C.int { 158 | _, _ = ad, arg // unused 159 | 160 | defer func() { 161 | if err := recover(); err != nil { 162 | // logger.Critf("openssl: verify callback sni panic'd: %v", err) 163 | os.Exit(1) 164 | } 165 | }() 166 | 167 | sniCb := (*Ctx)(callback).sniCb 168 | 169 | s := &SSL{ssl: con, verifyCb: nil} 170 | // This attaches a pointer to our SSL struct into the SNI callback. 171 | C.SSL_set_ex_data(s.ssl, get_ssl_idx(), unsafe.Pointer(s.ssl)) 172 | 173 | // Note: this is ctx.sni_cb, not C.sni_cb 174 | return C.int(sniCb(s)) 175 | } 176 | 177 | //export alpn_cb_thunk 178 | func alpn_cb_thunk(callback unsafe.Pointer, con *C.SSL, out unsafe.Pointer, outlen unsafe.Pointer, in unsafe.Pointer, 179 | inlen uint, arg unsafe.Pointer, 180 | ) C.int { 181 | defer func() { 182 | if err := recover(); err != nil { 183 | // logger.Critf("openssl: verify callback alpn panic'd: %v", err) 184 | os.Exit(1) 185 | } 186 | }() 187 | 188 | alpnCb := (*Ctx)(callback).alpnCb 189 | 190 | s := &SSL{ssl: con, verifyCb: nil} 191 | // This attaches a pointer to our SSL struct into the ALPN callback. 192 | C.SSL_set_ex_data(s.ssl, get_ssl_idx(), unsafe.Pointer(s.ssl)) 193 | 194 | // Ensure the out parameter is treated as a pointer to const unsigned char 195 | return C.int(alpnCb(s, out, outlen, in, inlen, arg)) 196 | } 197 | -------------------------------------------------------------------------------- /shim.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Space Monkey, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | #include 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "_cgo_export.h" 30 | 31 | 32 | void X_tongsuogo_init(void) { 33 | SSL_load_error_strings(); 34 | SSL_library_init(); 35 | } 36 | 37 | long X_SSL_set_options(SSL* ssl, long options) { 38 | return SSL_set_options(ssl, options); 39 | } 40 | 41 | long X_SSL_get_options(SSL* ssl) { 42 | return SSL_get_options(ssl); 43 | } 44 | 45 | long X_SSL_clear_options(SSL* ssl, long options) { 46 | return SSL_clear_options(ssl, options); 47 | } 48 | 49 | long X_SSL_set_tlsext_host_name(SSL *ssl, const char *name) { 50 | return SSL_set_tlsext_host_name(ssl, name); 51 | } 52 | 53 | const char *X_SSL_get_cipher_name(const SSL *ssl) { 54 | return SSL_get_cipher_name(ssl); 55 | } 56 | 57 | const char *X_SSL_get_version(const SSL *ssl) { 58 | return SSL_get_version(ssl); 59 | } 60 | 61 | int X_SSL_session_reused(SSL *ssl) { 62 | return SSL_session_reused(ssl); 63 | } 64 | 65 | int X_SSL_new_index() { 66 | return SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); 67 | } 68 | 69 | int X_SSL_verify_cb(int ok, X509_STORE_CTX* store) { 70 | SSL* ssl = (SSL *)X509_STORE_CTX_get_ex_data(store, 71 | SSL_get_ex_data_X509_STORE_CTX_idx()); 72 | void* p = SSL_get_ex_data(ssl, get_ssl_idx()); 73 | // get the pointer to the go Ctx object and pass it back into the thunk 74 | return go_ssl_verify_cb_thunk(p, ok, store); 75 | } 76 | 77 | int X_SSL_CTX_set_max_proto_version(SSL_CTX *ctx, int version) { 78 | return SSL_CTX_set_max_proto_version(ctx, version); 79 | } 80 | 81 | int X_SSL_CTX_set_min_proto_version(SSL_CTX *ctx, int version) { 82 | return SSL_CTX_set_min_proto_version(ctx, version); 83 | } 84 | 85 | const SSL_METHOD *X_SSLv23_method() { 86 | return SSLv23_method(); 87 | } 88 | 89 | 90 | const SSL_METHOD *X_SSLv3_method() { 91 | #ifndef OPENSSL_NO_SSL3_METHOD 92 | return SSLv3_method(); 93 | #else 94 | return NULL; 95 | #endif 96 | } 97 | 98 | const SSL_METHOD *X_TLSv1_method() { 99 | return TLSv1_method(); 100 | } 101 | 102 | const SSL_METHOD *X_TLSv1_1_method() { 103 | #if defined(TLS1_1_VERSION) && !defined(OPENSSL_SYSNAME_MACOSX) 104 | return TLSv1_1_method(); 105 | #else 106 | return NULL; 107 | #endif 108 | } 109 | 110 | const SSL_METHOD *X_TLSv1_2_method() { 111 | #if defined(TLS1_2_VERSION) && !defined(OPENSSL_SYSNAME_MACOSX) 112 | return TLSv1_2_method(); 113 | #else 114 | return NULL; 115 | #endif 116 | } 117 | 118 | const SSL_METHOD *X_NTLS_method() { 119 | return NTLS_method(); 120 | } 121 | const SSL_METHOD *X_NTLS_client_method() { 122 | return NTLS_client_method(); 123 | } 124 | const SSL_METHOD *X_NTLS_server_method() { 125 | return NTLS_server_method(); 126 | } 127 | 128 | int X_SSL_CTX_new_index() { 129 | return SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL); 130 | } 131 | 132 | void X_SSL_CTX_enable_ntls(SSL_CTX* ctx) { 133 | return SSL_CTX_enable_ntls(ctx); 134 | } 135 | 136 | long X_SSL_CTX_set_options(SSL_CTX* ctx, long options) { 137 | return SSL_CTX_set_options(ctx, options); 138 | } 139 | 140 | long X_SSL_CTX_clear_options(SSL_CTX* ctx, long options) { 141 | return SSL_CTX_clear_options(ctx, options); 142 | } 143 | 144 | long X_SSL_CTX_get_options(SSL_CTX* ctx) { 145 | return SSL_CTX_get_options(ctx); 146 | } 147 | 148 | long X_SSL_CTX_set_mode(SSL_CTX* ctx, long modes) { 149 | return SSL_CTX_set_mode(ctx, modes); 150 | } 151 | 152 | long X_SSL_CTX_get_mode(SSL_CTX* ctx) { 153 | return SSL_CTX_get_mode(ctx); 154 | } 155 | 156 | long X_SSL_CTX_set_session_cache_mode(SSL_CTX* ctx, long modes) { 157 | return SSL_CTX_set_session_cache_mode(ctx, modes); 158 | } 159 | 160 | long X_SSL_CTX_sess_set_cache_size(SSL_CTX* ctx, long t) { 161 | return SSL_CTX_sess_set_cache_size(ctx, t); 162 | } 163 | 164 | long X_SSL_CTX_sess_get_cache_size(SSL_CTX* ctx) { 165 | return SSL_CTX_sess_get_cache_size(ctx); 166 | } 167 | 168 | long X_SSL_CTX_set_timeout(SSL_CTX* ctx, long t) { 169 | return SSL_CTX_set_timeout(ctx, t); 170 | } 171 | 172 | long X_SSL_CTX_get_timeout(SSL_CTX* ctx) { 173 | return SSL_CTX_get_timeout(ctx); 174 | } 175 | 176 | long X_SSL_CTX_add_extra_chain_cert(SSL_CTX* ctx, X509 *cert) { 177 | return SSL_CTX_add_extra_chain_cert(ctx, cert); 178 | } 179 | 180 | long X_SSL_CTX_set_tmp_ecdh(SSL_CTX* ctx, EC_KEY *key) { 181 | return SSL_CTX_set_tmp_ecdh(ctx, key); 182 | } 183 | 184 | long X_SSL_CTX_set_tlsext_servername_callback( 185 | SSL_CTX* ctx, int (*cb)(SSL *con, int *ad, void *args)) { 186 | return SSL_CTX_set_tlsext_servername_callback(ctx, cb); 187 | } 188 | 189 | int X_SSL_CTX_verify_cb(int ok, X509_STORE_CTX* store) { 190 | SSL* ssl = (SSL *)X509_STORE_CTX_get_ex_data(store, 191 | SSL_get_ex_data_X509_STORE_CTX_idx()); 192 | SSL_CTX* ssl_ctx = SSL_get_SSL_CTX(ssl); 193 | void* p = SSL_CTX_get_ex_data(ssl_ctx, get_ssl_ctx_idx()); 194 | // get the pointer to the go Ctx object and pass it back into the thunk 195 | return go_ssl_ctx_verify_cb_thunk(p, ok, store); 196 | } 197 | 198 | long X_SSL_CTX_set_tmp_dh(SSL_CTX* ctx, DH *dh) { 199 | return SSL_CTX_set_tmp_dh(ctx, dh); 200 | } 201 | 202 | long X_PEM_read_DHparams(SSL_CTX* ctx, DH *dh) { 203 | return SSL_CTX_set_tmp_dh(ctx, dh); 204 | } 205 | 206 | int X_SSL_CTX_set_tlsext_ticket_key_cb(SSL_CTX *sslctx, 207 | int (*cb)(SSL *s, unsigned char key_name[16], 208 | unsigned char iv[EVP_MAX_IV_LENGTH], 209 | EVP_CIPHER_CTX *ctx, HMAC_CTX *hctx, int enc)) { 210 | return SSL_CTX_set_tlsext_ticket_key_cb(sslctx, cb); 211 | } 212 | 213 | int X_SSL_CTX_ticket_key_cb(SSL *s, unsigned char key_name[16], 214 | unsigned char iv[EVP_MAX_IV_LENGTH], 215 | EVP_CIPHER_CTX *cctx, HMAC_CTX *hctx, int enc) { 216 | 217 | SSL_CTX* ssl_ctx = SSL_get_SSL_CTX(s); 218 | void* p = SSL_CTX_get_ex_data(ssl_ctx, get_ssl_ctx_idx()); 219 | // get the pointer to the go Ctx object and pass it back into the thunk 220 | return go_ticket_key_cb_thunk(p, key_name, cctx, hctx, enc); 221 | } 222 | 223 | int X_X509_add_ref(X509* x509) { 224 | return X509_up_ref(x509); 225 | } 226 | 227 | int X_sk_X509_num(STACK_OF(X509) *sk) { 228 | return sk_X509_num(sk); 229 | } 230 | 231 | X509 *X_sk_X509_value(STACK_OF(X509)* sk, int i) { 232 | return sk_X509_value(sk, i); 233 | } 234 | -------------------------------------------------------------------------------- /tickets.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package tongsuogo 16 | 17 | // #include "shim.h" 18 | import "C" 19 | 20 | import ( 21 | "os" 22 | "unsafe" 23 | 24 | "github.com/tongsuo-project/tongsuo-go-sdk/crypto" 25 | ) 26 | 27 | const ( 28 | KeyNameSize = 16 29 | ) 30 | 31 | // TicketCipherCtx describes the cipher that will be used by the ticket store 32 | // for encrypting the tickets. Engine may be nil if no engine is desired. 33 | type TicketCipherCtx struct { 34 | Cipher *crypto.Cipher 35 | Engine *crypto.Engine 36 | } 37 | 38 | // TicketDigestCtx describes the digest that will be used by the ticket store 39 | // to authenticate the data. Engine may be nil if no engine is desired. 40 | type TicketDigestCtx struct { 41 | Digest *crypto.Digest 42 | Engine *crypto.Engine 43 | } 44 | 45 | // TicketName is an identifier for the key material for a ticket. 46 | type TicketName [KeyNameSize]byte 47 | 48 | // TicketKey is the key material for a ticket. If this is lost, forward secrecy 49 | // is lost as it allows decrypting TLS sessions retroactively. 50 | type TicketKey struct { 51 | Name TicketName 52 | CipherKey []byte 53 | HMACKey []byte 54 | IV []byte 55 | } 56 | 57 | // TicketKeyManager is a manager for TicketKeys. It allows one to control the 58 | // lifetime of tickets, causing renewals and expirations for keys that are 59 | // created. Calls to the manager are serialized. 60 | type TicketKeyManager interface { 61 | // New should create a brand new TicketKey with a new name. 62 | New() *TicketKey 63 | 64 | // Current should return a key that is still valid. 65 | Current() *TicketKey 66 | 67 | // Lookup should return a key with the given name, or nil if no name 68 | // exists. 69 | Lookup(name TicketName) *TicketKey 70 | 71 | // Expired should return if the key with the given name is expired and 72 | // should not be used any more. 73 | Expired(name TicketName) bool 74 | 75 | // ShouldRenew should return if the key is still ok to use for the current 76 | // session, but we should send a new key for the client. 77 | ShouldRenew(name TicketName) bool 78 | } 79 | 80 | // TicketStore descibes the encryption and authentication methods the tickets 81 | // will use along with a key manager for generating and keeping track of the 82 | // secrets. 83 | type TicketStore struct { 84 | CipherCtx TicketCipherCtx 85 | DigestCtx TicketDigestCtx 86 | Keys TicketKeyManager 87 | } 88 | 89 | func (t *TicketStore) cipherEngine() *C.ENGINE { 90 | if t.CipherCtx.Engine == nil { 91 | return nil 92 | } 93 | return (*C.ENGINE)(t.CipherCtx.Engine.Engine()) 94 | } 95 | 96 | func (t *TicketStore) digestEngine() *C.ENGINE { 97 | if t.DigestCtx.Engine == nil { 98 | return nil 99 | } 100 | return (*C.ENGINE)(t.DigestCtx.Engine.Engine()) 101 | } 102 | 103 | const ( 104 | // instruct to do a handshake 105 | ticketRespRequireHandshake = 0 106 | // crypto context is set up correctly 107 | ticketRespSessionOk = 1 108 | // crypto context is ok, but the ticket should be reissued 109 | ticketRespRenewSession = 2 110 | // we had a problem that shouldn't fall back to doing a handshake 111 | ticketRespError = -1 112 | // asked to create session crypto context 113 | ticketReqNewSession = 1 114 | // asked to load crypto context for a previous session 115 | ticketReqLookupSession = 0 116 | ) 117 | 118 | //export go_ticket_key_cb_thunk 119 | func go_ticket_key_cb_thunk(pctx unsafe.Pointer, keyName *C.uchar, cctx *C.EVP_CIPHER_CTX, hctx *C.HMAC_CTX, enc C.int, 120 | ) C.int { 121 | // no panic's allowed. it's super hard to guarantee any state at this point 122 | // so just abort everything. 123 | defer func() { 124 | if err := recover(); err != nil { 125 | // logger.Critf("openssl: ticket key callback panic'd: %v", err) 126 | os.Exit(1) 127 | } 128 | }() 129 | 130 | ctx := (*Ctx)(pctx) 131 | store := ctx.ticketStore 132 | if store == nil { 133 | // should this be an error condition? it doesn't make sense 134 | // to be called if we don't have a store I believe, but that's probably 135 | // not worth aborting the handshake which is what I believe returning 136 | // an error would do. 137 | return ticketRespRequireHandshake 138 | } 139 | 140 | ctx.ticketStoreMu.Lock() 141 | defer ctx.ticketStoreMu.Unlock() 142 | 143 | switch enc { 144 | case ticketReqNewSession: 145 | key := store.Keys.Current() 146 | if key == nil { 147 | key = store.Keys.New() 148 | if key == nil { 149 | return ticketRespRequireHandshake 150 | } 151 | } 152 | 153 | C.memcpy( 154 | unsafe.Pointer(keyName), 155 | unsafe.Pointer(&key.Name[0]), 156 | KeyNameSize) 157 | C.EVP_EncryptInit_ex( 158 | cctx, 159 | (*C.EVP_CIPHER)(store.CipherCtx.Cipher.Ptr()), 160 | store.cipherEngine(), 161 | (*C.uchar)(&key.CipherKey[0]), 162 | (*C.uchar)(&key.IV[0])) 163 | C.HMAC_Init_ex( 164 | hctx, 165 | unsafe.Pointer(&key.HMACKey[0]), 166 | C.int(len(key.HMACKey)), 167 | (*C.EVP_MD)(store.DigestCtx.Digest.Ptr()), 168 | store.digestEngine()) 169 | 170 | return ticketRespSessionOk 171 | 172 | case ticketReqLookupSession: 173 | var name TicketName 174 | C.memcpy( 175 | unsafe.Pointer(&name[0]), 176 | unsafe.Pointer(keyName), 177 | KeyNameSize) 178 | 179 | key := store.Keys.Lookup(name) 180 | if key == nil { 181 | return ticketRespRequireHandshake 182 | } 183 | if store.Keys.Expired(name) { 184 | return ticketRespRequireHandshake 185 | } 186 | 187 | C.EVP_DecryptInit_ex( 188 | cctx, 189 | (*C.EVP_CIPHER)(store.CipherCtx.Cipher.Ptr()), 190 | store.cipherEngine(), 191 | (*C.uchar)(&key.CipherKey[0]), 192 | (*C.uchar)(&key.IV[0])) 193 | C.HMAC_Init_ex( 194 | hctx, 195 | unsafe.Pointer(&key.HMACKey[0]), 196 | C.int(len(key.HMACKey)), 197 | (*C.EVP_MD)(store.DigestCtx.Digest.Ptr()), 198 | store.digestEngine()) 199 | 200 | if store.Keys.ShouldRenew(name) { 201 | return ticketRespRenewSession 202 | } 203 | 204 | return ticketRespSessionOk 205 | 206 | default: 207 | return ticketRespError 208 | } 209 | } 210 | 211 | // SetTicketStore sets the ticket store for the context so that clients can do 212 | // ticket based session resumption. If the store is nil, the 213 | func (c *Ctx) SetTicketStore(store *TicketStore) { 214 | c.ticketStore = store 215 | 216 | if store == nil { 217 | C.X_SSL_CTX_set_tlsext_ticket_key_cb(c.ctx, nil) 218 | } else { 219 | C.X_SSL_CTX_set_tlsext_ticket_key_cb(c.ctx, 220 | (*[0]byte)(C.X_SSL_CTX_ticket_key_cb)) 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /crypto/bio.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package crypto 16 | 17 | // #include "shim.h" 18 | import "C" 19 | 20 | import ( 21 | "fmt" 22 | "io" 23 | "sync" 24 | "unsafe" 25 | ) 26 | 27 | const ( 28 | SSLRecordSize = 16 * 1024 29 | ) 30 | 31 | func nonCopyGoBytes(ptr uintptr, length int) []byte { 32 | return unsafe.Slice((*byte)(unsafe.Pointer(ptr)), length) 33 | } 34 | 35 | func nonCopyCString(data *C.char, size C.int) []byte { 36 | return nonCopyGoBytes(uintptr(unsafe.Pointer(data)), int(size)) 37 | } 38 | 39 | var writeBioMapping = newMapping() 40 | 41 | type WriteBio struct { 42 | dataMtx sync.Mutex 43 | opMtx sync.Mutex 44 | buf []byte 45 | releaseBuffers bool 46 | } 47 | 48 | func loadWritePtr(b *C.BIO) *WriteBio { 49 | t := token(C.X_BIO_get_data(b)) 50 | 51 | return (*WriteBio)(writeBioMapping.Get(t)) 52 | } 53 | 54 | func bioClearRetryFlags(b *C.BIO) { 55 | C.X_BIO_clear_flags(b, C.BIO_FLAGS_RWS|C.BIO_FLAGS_SHOULD_RETRY) 56 | } 57 | 58 | func bioSetRetryRead(b *C.BIO) { 59 | C.X_BIO_set_flags(b, C.BIO_FLAGS_READ|C.BIO_FLAGS_SHOULD_RETRY) 60 | } 61 | 62 | //export go_write_bio_write 63 | func go_write_bio_write(bio *C.BIO, data *C.char, size C.int) C.int { 64 | var rc C.int 65 | 66 | defer func() { 67 | if err := recover(); err != nil { 68 | // logger.Critf("openssl: writeBioWrite panic'd: %v", err) 69 | rc = -1 70 | } 71 | }() 72 | ptr := loadWritePtr(bio) 73 | if ptr == nil || data == nil || size < 0 { 74 | return -1 75 | } 76 | ptr.dataMtx.Lock() 77 | defer ptr.dataMtx.Unlock() 78 | bioClearRetryFlags(bio) 79 | ptr.buf = append(ptr.buf, nonCopyCString(data, size)...) 80 | rc = size 81 | 82 | return rc 83 | } 84 | 85 | //export go_write_bio_ctrl 86 | func go_write_bio_ctrl(bio *C.BIO, cmd C.int, arg1 C.long, arg2 unsafe.Pointer) C.long { 87 | _, _ = arg1, arg2 // unused 88 | 89 | var rc C.long 90 | 91 | defer func() { 92 | if err := recover(); err != nil { 93 | // logger.Critf("openssl: writeBioCtrl panic'd: %v", err) 94 | rc = -1 95 | } 96 | }() 97 | switch cmd { 98 | case C.BIO_CTRL_WPENDING: 99 | rc = writeBioPending(bio) 100 | case C.BIO_CTRL_DUP, C.BIO_CTRL_FLUSH: 101 | rc = 1 102 | default: 103 | rc = 0 104 | } 105 | 106 | return rc 107 | } 108 | 109 | func writeBioPending(b *C.BIO) C.long { 110 | ptr := loadWritePtr(b) 111 | if ptr == nil { 112 | return 0 113 | } 114 | ptr.dataMtx.Lock() 115 | defer ptr.dataMtx.Unlock() 116 | 117 | return C.long(len(ptr.buf)) 118 | } 119 | 120 | func (bio *WriteBio) WriteTo(writer io.Writer) (int64, error) { 121 | bio.opMtx.Lock() 122 | defer bio.opMtx.Unlock() 123 | 124 | // write whatever data we currently have 125 | bio.dataMtx.Lock() 126 | data := bio.buf 127 | bio.dataMtx.Unlock() 128 | 129 | if len(data) == 0 { 130 | return 0, nil 131 | } 132 | n, err := writer.Write(data) 133 | 134 | // subtract however much data we wrote from the buffer 135 | bio.dataMtx.Lock() 136 | bio.buf = bio.buf[:copy(bio.buf, bio.buf[n:])] 137 | if bio.releaseBuffers && len(bio.buf) == 0 { 138 | bio.buf = nil 139 | } 140 | bio.dataMtx.Unlock() 141 | 142 | return int64(n), err 143 | } 144 | 145 | func (bio *WriteBio) SetRelease(flag bool) { 146 | bio.dataMtx.Lock() 147 | defer bio.dataMtx.Unlock() 148 | bio.releaseBuffers = flag 149 | } 150 | 151 | func (bio *WriteBio) Disconnect(b *C.BIO) { 152 | if loadWritePtr(b) == bio { 153 | writeBioMapping.Del(token(C.X_BIO_get_data(b))) 154 | C.X_BIO_set_data(b, nil) 155 | } 156 | } 157 | 158 | func (bio *WriteBio) MakeCBIO() *C.BIO { 159 | rv := C.X_BIO_new_write_bio() 160 | token := writeBioMapping.Add(unsafe.Pointer(bio)) 161 | C.X_BIO_set_data(rv, unsafe.Pointer(token)) 162 | 163 | return rv 164 | } 165 | 166 | var readBioMapping = newMapping() 167 | 168 | type ReadBio struct { 169 | dataMtx sync.Mutex 170 | opMtx sync.Mutex 171 | buf []byte 172 | eof bool 173 | releaseBuffers bool 174 | } 175 | 176 | func loadReadPtr(b *C.BIO) *ReadBio { 177 | return (*ReadBio)(readBioMapping.Get(token(C.X_BIO_get_data(b)))) 178 | } 179 | 180 | //export go_read_bio_read 181 | func go_read_bio_read(bio *C.BIO, data *C.char, size C.int) C.int { 182 | rc := 0 183 | 184 | defer func() { 185 | if err := recover(); err != nil { 186 | // logger.Critf("openssl: go_read_bio_read panic'd: %v", err) 187 | rc = -1 188 | } 189 | }() 190 | ptr := loadReadPtr(bio) 191 | if ptr == nil || size < 0 { 192 | return -1 193 | } 194 | ptr.dataMtx.Lock() 195 | defer ptr.dataMtx.Unlock() 196 | bioClearRetryFlags(bio) 197 | if len(ptr.buf) == 0 { 198 | if ptr.eof { 199 | return 0 200 | } 201 | bioSetRetryRead(bio) 202 | return -1 203 | } 204 | if size == 0 || data == nil { 205 | return C.int(len(ptr.buf)) 206 | } 207 | rc = copy(nonCopyCString(data, size), ptr.buf) 208 | ptr.buf = ptr.buf[:copy(ptr.buf, ptr.buf[rc:])] 209 | if ptr.releaseBuffers && len(ptr.buf) == 0 { 210 | ptr.buf = nil 211 | } 212 | return C.int(rc) 213 | } 214 | 215 | //export go_read_bio_ctrl 216 | func go_read_bio_ctrl(bio *C.BIO, cmd C.int, arg1 C.long, arg2 unsafe.Pointer) C.long { 217 | _, _ = arg1, arg2 // unused 218 | 219 | var rc C.long 220 | defer func() { 221 | if err := recover(); err != nil { 222 | // logger.Critf("openssl: readBioCtrl panic'd: %v", err) 223 | rc = -1 224 | } 225 | }() 226 | switch cmd { 227 | case C.BIO_CTRL_PENDING: 228 | rc = readBioPending(bio) 229 | case C.BIO_CTRL_DUP, C.BIO_CTRL_FLUSH: 230 | rc = 1 231 | default: 232 | rc = 0 233 | } 234 | 235 | return rc 236 | } 237 | 238 | func readBioPending(b *C.BIO) C.long { 239 | ptr := loadReadPtr(b) 240 | if ptr == nil { 241 | return 0 242 | } 243 | ptr.dataMtx.Lock() 244 | defer ptr.dataMtx.Unlock() 245 | return C.long(len(ptr.buf)) 246 | } 247 | 248 | func (bio *ReadBio) SetRelease(flag bool) { 249 | bio.dataMtx.Lock() 250 | defer bio.dataMtx.Unlock() 251 | bio.releaseBuffers = flag 252 | } 253 | 254 | func (bio *ReadBio) ReadFromOnce(r io.Reader) (int, error) { 255 | bio.opMtx.Lock() 256 | defer bio.opMtx.Unlock() 257 | 258 | // make sure we have a destination that fits at least one SSL record 259 | bio.dataMtx.Lock() 260 | if cap(bio.buf) < len(bio.buf)+SSLRecordSize { 261 | newBuf := make([]byte, len(bio.buf), len(bio.buf)+SSLRecordSize) 262 | copy(newBuf, bio.buf) 263 | bio.buf = newBuf 264 | } 265 | 266 | dst := bio.buf[len(bio.buf):cap(bio.buf)] 267 | dstSlice := bio.buf 268 | bio.dataMtx.Unlock() 269 | 270 | n, err := r.Read(dst) 271 | bio.dataMtx.Lock() 272 | defer bio.dataMtx.Unlock() 273 | if n > 0 { 274 | if len(dstSlice) != len(bio.buf) { 275 | // someone shrunk the buffer, so we read in too far ahead and we 276 | // need to slide backwards 277 | copy(bio.buf[len(bio.buf):len(bio.buf)+n], dst) 278 | } 279 | bio.buf = bio.buf[:len(bio.buf)+n] 280 | } 281 | 282 | if err != nil { 283 | return n, fmt.Errorf("read from once error: %w", err) 284 | } 285 | 286 | return n, nil 287 | } 288 | 289 | func (bio *ReadBio) MakeCBIO() *C.BIO { 290 | rv := C.X_BIO_new_read_bio() 291 | token := readBioMapping.Add(unsafe.Pointer(bio)) 292 | C.X_BIO_set_data(rv, unsafe.Pointer(token)) 293 | return rv 294 | } 295 | 296 | func (bio *ReadBio) Disconnect(b *C.BIO) { 297 | if loadReadPtr(b) == bio { 298 | readBioMapping.Del(token(C.X_BIO_get_data(b))) 299 | C.X_BIO_set_data(b, nil) 300 | } 301 | } 302 | 303 | func (bio *ReadBio) MarkEOF() { 304 | bio.dataMtx.Lock() 305 | defer bio.dataMtx.Unlock() 306 | bio.eof = true 307 | } 308 | 309 | type anyBio C.BIO 310 | 311 | func asAnyBio(b *C.BIO) *anyBio { return (*anyBio)(b) } 312 | 313 | func (bio *anyBio) Read(buf []byte) (int, error) { 314 | if len(buf) == 0 { 315 | return 0, nil 316 | } 317 | n := int(C.X_BIO_read((*C.BIO)(bio), unsafe.Pointer(&buf[0]), C.int(len(buf)))) 318 | if n <= 0 { 319 | return 0, io.EOF 320 | } 321 | return n, nil 322 | } 323 | 324 | func (bio *anyBio) Write(buf []byte) (int, error) { 325 | if len(buf) == 0 { 326 | return 0, nil 327 | } 328 | ret := int(C.X_BIO_write((*C.BIO)(bio), unsafe.Pointer(&buf[0]), 329 | C.int(len(buf)))) 330 | if ret < 0 { 331 | return 0, fmt.Errorf("BIO write failed: %w", PopError()) 332 | } 333 | if ret < len(buf) { 334 | return ret, fmt.Errorf("BIO write trucated: %w", ErrPartialWrite) 335 | } 336 | return ret, nil 337 | } 338 | -------------------------------------------------------------------------------- /crypto/sm4/sm4.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Tongsuo Project Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License 2.0 (the "License"). You may not use 4 | // this file except in compliance with the License. You can obtain a copy 5 | // in the file LICENSE in the source distribution or at 6 | // https://github.com/Tongsuo-Project/tongsuo-go-sdk/blob/main/LICENSE 7 | 8 | package sm4 9 | 10 | // #include "../shim.h" 11 | import "C" 12 | 13 | import ( 14 | "bytes" 15 | "crypto/cipher" 16 | "fmt" 17 | "unsafe" 18 | 19 | "github.com/tongsuo-project/tongsuo-go-sdk/crypto" 20 | ) 21 | 22 | const ( 23 | BlockSize = 16 24 | KeySize = 16 25 | ) 26 | 27 | type Encrypter interface { 28 | // crypto.EncryptionCipherCtx 29 | SetPadding(pad bool) 30 | EncryptAll(input []byte) ([]byte, error) 31 | SetAAD(aad []byte) 32 | SetTagLen(length int) 33 | GetTag() ([]byte, error) 34 | } 35 | 36 | type Decrypter interface { 37 | // crypto.DecryptionCipherCtx 38 | SetPadding(pad bool) 39 | DecryptAll(input []byte) ([]byte, error) 40 | SetAAD(aad []byte) 41 | SetTag(tag []byte) 42 | } 43 | 44 | type sm4Encrypter struct { 45 | cctx crypto.EncryptionCipherCtx 46 | key []byte 47 | iv []byte 48 | aad []byte 49 | tagLen int 50 | } 51 | 52 | type sm4Decrypter struct { 53 | cctx crypto.DecryptionCipherCtx 54 | key []byte 55 | iv []byte 56 | aad []byte 57 | tag []byte 58 | } 59 | 60 | type sm4Cipher struct { 61 | rk [32]uint32 62 | } 63 | 64 | func (c *sm4Cipher) BlockSize() int { 65 | return BlockSize 66 | } 67 | 68 | func NewCipher(key []byte) (cipher.Block, error) { 69 | if len(key) != KeySize { 70 | return nil, fmt.Errorf("invalid key size: %w", crypto.ErrInvalidKeySize) 71 | } 72 | 73 | cipher := &sm4Cipher{} 74 | ret := C.SM4_set_key((*C.uchar)(&key[0]), (*C.SM4_KEY)(unsafe.Pointer(&cipher.rk))) 75 | if ret != 1 { 76 | return nil, fmt.Errorf("failed to set key: %w", crypto.ErrInternalError) 77 | } 78 | 79 | return cipher, nil 80 | } 81 | 82 | func (c *sm4Cipher) Encrypt(dst, src []byte) { 83 | if len(src) < BlockSize { 84 | panic("sm4: input not full block") 85 | } 86 | if len(dst) < BlockSize { 87 | panic("sm4: output not full block") 88 | } 89 | 90 | C.SM4_encrypt((*C.uchar)(&src[0]), (*C.uchar)(&dst[0]), (*C.SM4_KEY)(unsafe.Pointer(&c.rk))) 91 | } 92 | 93 | func (c *sm4Cipher) Decrypt(dst, src []byte) { 94 | if len(src) < BlockSize { 95 | panic("sm4: input not full block") 96 | } 97 | if len(dst) < BlockSize { 98 | panic("sm4: output not full block") 99 | } 100 | 101 | C.SM4_decrypt((*C.uchar)(&src[0]), (*C.uchar)(&dst[0]), (*C.SM4_KEY)(unsafe.Pointer(&c.rk))) 102 | } 103 | 104 | func getSM4Cipher(mode int) (*crypto.Cipher, error) { 105 | var cipher *crypto.Cipher 106 | var err error 107 | 108 | switch mode { 109 | case crypto.CipherModeECB: 110 | cipher, err = crypto.GetCipherByName("SM4-ECB") 111 | case crypto.CipherModeCBC: 112 | cipher, err = crypto.GetCipherByName("SM4-CBC") 113 | case crypto.CipherModeCFB: 114 | cipher, err = crypto.GetCipherByName("SM4-CFB") 115 | case crypto.CipherModeOFB: 116 | cipher, err = crypto.GetCipherByName("SM4-OFB") 117 | case crypto.CipherModeCTR: 118 | cipher, err = crypto.GetCipherByName("SM4-CTR") 119 | case crypto.CipherModeGCM: 120 | cipher, err = crypto.GetCipherByName("SM4-GCM") 121 | case crypto.CipherModeCCM: 122 | cipher, err = crypto.GetCipherByName("SM4-CCM") 123 | default: 124 | return nil, fmt.Errorf("unsupported sm4 mode: %w", crypto.ErrUnsupportedMode) 125 | } 126 | 127 | if err != nil { 128 | return nil, fmt.Errorf("failed to get cipher: %w", err) 129 | } 130 | 131 | return cipher, nil 132 | } 133 | 134 | func NewDecrypter(mode int, key []byte, iv []byte) (Decrypter, error) { 135 | cipher, err := getSM4Cipher(mode) 136 | if err != nil { 137 | return nil, err 138 | } 139 | 140 | cctx, err := crypto.NewDecryptionCipherCtx(cipher, nil, nil, nil) 141 | if err != nil { 142 | return nil, fmt.Errorf("failed to create decryption cipher ctx %w", err) 143 | } 144 | 145 | if len(iv) > 0 { 146 | if mode == crypto.CipherModeGCM || mode == crypto.CipherModeCCM { 147 | err := cctx.SetCtrl(C.EVP_CTRL_AEAD_SET_IVLEN, len(iv)) 148 | if err != nil { 149 | return nil, fmt.Errorf("failed to set IV len to %d: %w", len(iv), err) 150 | } 151 | } 152 | } 153 | 154 | return &sm4Decrypter{cctx: cctx, key: key, iv: iv, aad: nil, tag: nil}, nil 155 | } 156 | 157 | func (ctx *sm4Decrypter) SetPadding(pad bool) { 158 | ctx.cctx.SetPadding(pad) 159 | } 160 | 161 | func NewEncrypter(mode int, key []byte, iv []byte) (Encrypter, error) { 162 | var tagLen int 163 | 164 | cipher, err := getSM4Cipher(mode) 165 | if err != nil { 166 | return nil, err 167 | } 168 | 169 | if mode == crypto.CipherModeGCM { 170 | tagLen = 16 171 | } 172 | if mode == crypto.CipherModeCCM { 173 | tagLen = 12 174 | } 175 | 176 | cctx, err := crypto.NewEncryptionCipherCtx(cipher, nil, nil, nil) 177 | if err != nil { 178 | return nil, fmt.Errorf("failed to create encryption cipher ctx %w", err) 179 | } 180 | 181 | if len(iv) > 0 { 182 | if mode == crypto.CipherModeGCM || mode == crypto.CipherModeCCM { 183 | err := cctx.SetCtrl(C.EVP_CTRL_AEAD_SET_IVLEN, len(iv)) 184 | if err != nil { 185 | return nil, fmt.Errorf("could not set IV len to %d: %w", len(iv), err) 186 | } 187 | } 188 | } 189 | 190 | return &sm4Encrypter{cctx: cctx, tagLen: tagLen, key: key, iv: iv, aad: nil}, nil 191 | } 192 | 193 | func (ctx *sm4Encrypter) GetTag() ([]byte, error) { 194 | tag, err := ctx.cctx.GetCtrlBytes(C.EVP_CTRL_AEAD_GET_TAG, ctx.tagLen, ctx.tagLen) 195 | if err != nil { 196 | return nil, fmt.Errorf("failed to get tag: %w", err) 197 | } 198 | 199 | return tag, nil 200 | } 201 | 202 | func (ctx *sm4Encrypter) SetTagLen(length int) { 203 | ctx.tagLen = length 204 | } 205 | 206 | func (ctx *sm4Encrypter) SetPadding(pad bool) { 207 | ctx.cctx.SetPadding(pad) 208 | } 209 | 210 | func (ctx *sm4Encrypter) SetAAD(aad []byte) { 211 | ctx.aad = aad 212 | } 213 | 214 | func (ctx *sm4Decrypter) SetAAD(aad []byte) { 215 | ctx.aad = aad 216 | } 217 | 218 | func (ctx *sm4Decrypter) SetTag(tag []byte) { 219 | ctx.tag = tag 220 | } 221 | 222 | func (ctx *sm4Decrypter) DecryptAll(src []byte) ([]byte, error) { 223 | if ctx.tag != nil { 224 | err := ctx.cctx.SetCtrlBytes(C.EVP_CTRL_AEAD_SET_TAG, len(ctx.tag), ctx.tag) 225 | if err != nil { 226 | return nil, fmt.Errorf("failed to set tag: %w", err) 227 | } 228 | } 229 | 230 | err := ctx.cctx.SetKeyAndIV(ctx.key, ctx.iv) 231 | if err != nil { 232 | return nil, fmt.Errorf("failed to set key or iv: %w", err) 233 | } 234 | 235 | var tmplen C.int 236 | if ctx.aad != nil { 237 | isCcm := (C.EVP_CIPHER_flags(C.X_EVP_CIPHER_CTX_cipher((*C.EVP_CIPHER_CTX)(ctx.cctx.Ctx()))) & 238 | C.EVP_CIPH_MODE) == C.EVP_CIPH_CCM_MODE 239 | 240 | if isCcm { 241 | res := C.EVP_DecryptUpdate((*C.EVP_CIPHER_CTX)(ctx.cctx.Ctx()), nil, &tmplen, nil, C.int(len(src))) 242 | if res != 1 { 243 | return nil, fmt.Errorf("failed to set CCM plain text length: %w", crypto.PopError()) 244 | } 245 | } 246 | 247 | res := C.EVP_DecryptUpdate((*C.EVP_CIPHER_CTX)(ctx.cctx.Ctx()), nil, &tmplen, (*C.uchar)(&ctx.aad[0]), 248 | C.int(len(ctx.aad))) 249 | if res != 1 { 250 | return nil, fmt.Errorf("failed to decrypt: %w", crypto.PopError()) 251 | } 252 | } 253 | 254 | res := new(bytes.Buffer) 255 | buf, err := ctx.cctx.DecryptUpdate(src) 256 | if err != nil { 257 | return nil, fmt.Errorf("failed to perform decryption: %w", err) 258 | } 259 | res.Write(buf) 260 | 261 | buf2, err := ctx.cctx.DecryptFinal() 262 | if err != nil { 263 | return nil, fmt.Errorf("failed to finalize decryption: %w", err) 264 | } 265 | res.Write(buf2) 266 | 267 | return res.Bytes(), nil 268 | } 269 | 270 | func (ctx *sm4Encrypter) EncryptAll(src []byte) ([]byte, error) { 271 | isCcm := (C.EVP_CIPHER_flags(C.X_EVP_CIPHER_CTX_cipher((*C.EVP_CIPHER_CTX)(ctx.cctx.Ctx()))) & C.EVP_CIPH_MODE) == 272 | C.EVP_CIPH_CCM_MODE 273 | 274 | if isCcm { 275 | err := ctx.cctx.SetCtrl(C.EVP_CTRL_AEAD_SET_TAG, ctx.tagLen) 276 | if err != nil { 277 | return nil, fmt.Errorf("failed to set CCM tag: %w", err) 278 | } 279 | } 280 | 281 | err := ctx.cctx.SetKeyAndIV(ctx.key, ctx.iv) 282 | if err != nil { 283 | return nil, fmt.Errorf("failed to set key or iv: %w", err) 284 | } 285 | 286 | var tmplen C.int 287 | if ctx.aad != nil { 288 | if isCcm { 289 | res := C.EVP_EncryptUpdate((*C.EVP_CIPHER_CTX)(ctx.cctx.Ctx()), nil, &tmplen, nil, C.int(len(src))) 290 | if res != 1 { 291 | return nil, fmt.Errorf("failed to set CCM plain text length: %w", crypto.PopError()) 292 | } 293 | } 294 | 295 | res := C.EVP_EncryptUpdate((*C.EVP_CIPHER_CTX)(ctx.cctx.Ctx()), nil, &tmplen, (*C.uchar)(&ctx.aad[0]), 296 | C.int(len(ctx.aad))) 297 | if res != 1 { 298 | return nil, fmt.Errorf("failed to set AAD: %w", crypto.PopError()) 299 | } 300 | } 301 | 302 | res := new(bytes.Buffer) 303 | buf, err := ctx.cctx.EncryptUpdate(src) 304 | if err != nil { 305 | return nil, fmt.Errorf("failed to perform encryption: %w", err) 306 | } 307 | res.Write(buf) 308 | 309 | buf2, err := ctx.cctx.EncryptFinal() 310 | if err != nil { 311 | return nil, fmt.Errorf("failed to finalize encryption: %w", err) 312 | } 313 | res.Write(buf2) 314 | 315 | return res.Bytes(), nil 316 | } 317 | -------------------------------------------------------------------------------- /crypto/cert_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package crypto_test 16 | 17 | import ( 18 | "math/big" 19 | "os" 20 | "path/filepath" 21 | "testing" 22 | "time" 23 | 24 | "github.com/tongsuo-project/tongsuo-go-sdk/crypto" 25 | ) 26 | 27 | func TestCertGenerate(t *testing.T) { 28 | t.Parallel() 29 | 30 | key, err := crypto.GenerateRSAKey(768) 31 | if err != nil { 32 | t.Fatal(err) 33 | } 34 | 35 | info := &crypto.CertificateInfo{ 36 | Serial: big.NewInt(int64(1)), 37 | Issued: 0, 38 | Expires: 24 * time.Hour, 39 | Country: "US", 40 | Organization: "Test", 41 | CommonName: "localhost", 42 | } 43 | 44 | cert, err := crypto.NewCertificate(info, key) 45 | if err != nil { 46 | t.Fatal(err) 47 | } 48 | 49 | if err := cert.Sign(key, crypto.DigestSHA256); err != nil { 50 | t.Fatal(err) 51 | } 52 | } 53 | 54 | func TestCertGenerateSM2(t *testing.T) { 55 | t.Parallel() 56 | 57 | key, err := crypto.GenerateECKey(crypto.SM2Curve) 58 | if err != nil { 59 | t.Fatal(err) 60 | } 61 | 62 | info := &crypto.CertificateInfo{ 63 | Serial: big.NewInt(int64(1)), 64 | Issued: 0, 65 | Expires: 24 * time.Hour, 66 | Country: "US", 67 | Organization: "Test", 68 | CommonName: "localhost", 69 | } 70 | 71 | cert, err := crypto.NewCertificate(info, key) 72 | if err != nil { 73 | t.Fatal(err) 74 | } 75 | 76 | if err := cert.Sign(key, crypto.DigestSM3); err != nil { 77 | t.Fatal(err) 78 | } 79 | } 80 | 81 | func TestCAGenerate(t *testing.T) { 82 | t.Parallel() 83 | 84 | cakey, err := crypto.GenerateRSAKey(768) 85 | if err != nil { 86 | t.Fatal(err) 87 | } 88 | 89 | info := &crypto.CertificateInfo{ 90 | Serial: big.NewInt(int64(1)), 91 | Issued: 0, 92 | Expires: 24 * time.Hour, 93 | Country: "US", 94 | Organization: "Test CA", 95 | CommonName: "CA", 96 | } 97 | 98 | ca, err := crypto.NewCertificate(info, cakey) 99 | if err != nil { 100 | t.Fatal(err) 101 | } 102 | 103 | if err := ca.AddExtensions(map[crypto.NID]string{ 104 | crypto.NidBasicConstraints: "critical,CA:TRUE", 105 | crypto.NidKeyUsage: "critical,keyCertSign,cRLSign", 106 | crypto.NidSubjectKeyIdentifier: "hash", 107 | crypto.NidNetscapeCertType: "sslCA", 108 | }); err != nil { 109 | t.Fatal(err) 110 | } 111 | 112 | if err := ca.Sign(cakey, crypto.DigestSHA256); err != nil { 113 | t.Fatal(err) 114 | } 115 | 116 | key, err := crypto.GenerateRSAKey(768) 117 | if err != nil { 118 | t.Fatal(err) 119 | } 120 | 121 | info = &crypto.CertificateInfo{ 122 | Serial: big.NewInt(int64(1)), 123 | Issued: 0, 124 | Expires: 24 * time.Hour, 125 | Country: "US", 126 | Organization: "Test", 127 | CommonName: "localhost", 128 | } 129 | 130 | cert, err := crypto.NewCertificate(info, key) 131 | if err != nil { 132 | t.Fatal(err) 133 | } 134 | 135 | if err := cert.AddExtensions(map[crypto.NID]string{ 136 | crypto.NidBasicConstraints: "critical,CA:FALSE", 137 | crypto.NidKeyUsage: "keyEncipherment", 138 | crypto.NidExtKeyUsage: "serverAuth", 139 | }); err != nil { 140 | t.Fatal(err) 141 | } 142 | 143 | if err := cert.SetIssuer(ca); err != nil { 144 | t.Fatal(err) 145 | } 146 | 147 | if err := cert.Sign(cakey, crypto.DigestSHA256); err != nil { 148 | t.Fatal(err) 149 | } 150 | } 151 | 152 | func generateSM2KeyAndSave(t *testing.T, filename string) crypto.PrivateKey { 153 | t.Helper() 154 | 155 | key, err := crypto.GenerateECKey(crypto.SM2Curve) 156 | if err != nil { 157 | t.Fatal(err) 158 | } 159 | 160 | pem, err := key.MarshalPKCS8PrivateKeyPEM() 161 | if err != nil { 162 | t.Fatal(err) 163 | } 164 | 165 | err = crypto.SavePEMToFile(pem, filename) 166 | if err != nil { 167 | t.Fatal(err) 168 | } 169 | 170 | return key 171 | } 172 | 173 | func TestCAGenerateSM2(t *testing.T) { 174 | t.Parallel() 175 | 176 | dirName := filepath.Join("test-runs", "TestCAGenerateSM2") 177 | _, err := os.Stat(dirName) 178 | 179 | if os.IsNotExist(err) { 180 | err := os.MkdirAll(dirName, 0o755) 181 | if err != nil { 182 | t.Logf("Failed to create the directory: %v\n", err) 183 | } 184 | } else if err != nil { 185 | t.Logf("Failed to check the directory: %v\n", err) 186 | } 187 | 188 | signAndSaveCert := func(cert *crypto.Certificate, caKey crypto.PrivateKey, filename string) { 189 | err := cert.Sign(caKey, crypto.DigestSM3) 190 | if err != nil { 191 | t.Fatal(err) 192 | } 193 | 194 | certPem, err := cert.MarshalPEM() 195 | if err != nil { 196 | t.Fatal(err) 197 | } 198 | 199 | err = crypto.SavePEMToFile(certPem, filename) 200 | if err != nil { 201 | t.Fatal(err) 202 | } 203 | } 204 | 205 | // Create CA certificate 206 | caKey, err := crypto.GenerateECKey(crypto.SM2Curve) 207 | if err != nil { 208 | t.Fatal(err) 209 | } 210 | 211 | caInfo := crypto.CertificateInfo{ 212 | big.NewInt(1), 213 | 0, 214 | 87600 * time.Hour, // 10 years 215 | "US", 216 | "Test CA", 217 | "CA", 218 | } 219 | caExtensions := map[crypto.NID]string{ 220 | crypto.NidBasicConstraints: "critical,CA:TRUE", 221 | crypto.NidKeyUsage: "critical,digitalSignature,keyCertSign,cRLSign", 222 | crypto.NidSubjectKeyIdentifier: "hash", 223 | crypto.NidAuthorityKeyIdentifier: "keyid:always,issuer", 224 | } 225 | 226 | ca, err := crypto.NewCertificate(&caInfo, caKey) 227 | if err != nil { 228 | t.Fatal(err) 229 | } 230 | 231 | err = ca.AddExtensions(caExtensions) 232 | if err != nil { 233 | t.Fatal(err) 234 | } 235 | 236 | caFile := filepath.Join(dirName, "chain-ca.crt") 237 | signAndSaveCert(ca, caKey, caFile) 238 | 239 | certInfos := []struct { 240 | name string 241 | keyUsage string 242 | }{ 243 | {"server_enc", "keyAgreement, keyEncipherment, dataEncipherment"}, 244 | {"server_sign", "nonRepudiation, digitalSignature"}, 245 | {"client_sign", "nonRepudiation, digitalSignature"}, 246 | {"client_enc", "keyAgreement, keyEncipherment, dataEncipherment"}, 247 | } 248 | 249 | for _, info := range certInfos { 250 | keyFile := filepath.Join(dirName, info.name+".key") 251 | key := generateSM2KeyAndSave(t, keyFile) 252 | certInfo := crypto.CertificateInfo{ 253 | Serial: big.NewInt(1), 254 | Issued: 0, 255 | Expires: 87600 * time.Hour, // 10 years 256 | Country: "US", 257 | Organization: "Test", 258 | CommonName: "localhost", 259 | } 260 | extensions := map[crypto.NID]string{ 261 | crypto.NidBasicConstraints: "critical,CA:FALSE", 262 | crypto.NidKeyUsage: info.keyUsage, 263 | } 264 | 265 | cert, err := crypto.NewCertificate(&certInfo, key) 266 | if err != nil { 267 | t.Fatal(err) 268 | } 269 | 270 | err = cert.AddExtensions(extensions) 271 | if err != nil { 272 | t.Fatal(err) 273 | } 274 | 275 | err = cert.SetIssuer(ca) 276 | if err != nil { 277 | t.Fatal(err) 278 | } 279 | 280 | certFile := filepath.Join(dirName, info.name+".crt") 281 | signAndSaveCert(cert, caKey, certFile) 282 | } 283 | } 284 | 285 | func TestCertGetNameEntry(t *testing.T) { 286 | t.Parallel() 287 | 288 | key, err := crypto.GenerateRSAKey(768) 289 | if err != nil { 290 | t.Fatal(err) 291 | } 292 | 293 | info := &crypto.CertificateInfo{ 294 | Serial: big.NewInt(int64(1)), 295 | Issued: 0, 296 | Expires: 24 * time.Hour, 297 | Country: "US", 298 | Organization: "Test", 299 | CommonName: "localhost", 300 | } 301 | 302 | cert, err := crypto.NewCertificate(info, key) 303 | if err != nil { 304 | t.Fatal(err) 305 | } 306 | 307 | name, err := cert.GetSubjectName() 308 | if err != nil { 309 | t.Fatal(err) 310 | } 311 | 312 | entry, ok := name.GetEntry(crypto.NidCommonName) 313 | if !ok { 314 | t.Fatal("no common name") 315 | } 316 | 317 | if entry != "localhost" { 318 | t.Fatalf("expected localhost; got %q", entry) 319 | } 320 | 321 | entry, ok = name.GetEntry(crypto.NidLocalityName) 322 | if ok { 323 | t.Fatal("did not expect a locality name") 324 | } 325 | 326 | if entry != "" { 327 | t.Fatalf("entry should be empty; got %q", entry) 328 | } 329 | } 330 | 331 | func TestCertVersion(t *testing.T) { 332 | t.Parallel() 333 | 334 | key, err := crypto.GenerateRSAKey(768) 335 | if err != nil { 336 | t.Fatal(err) 337 | } 338 | 339 | info := &crypto.CertificateInfo{ 340 | Serial: big.NewInt(int64(1)), 341 | Issued: 0, 342 | Expires: 24 * time.Hour, 343 | Country: "US", 344 | Organization: "Test", 345 | CommonName: "localhost", 346 | } 347 | 348 | cert, err := crypto.NewCertificate(info, key) 349 | if err != nil { 350 | t.Fatal(err) 351 | } 352 | 353 | if err := cert.SetVersion(crypto.X509V3); err != nil { 354 | t.Fatal(err) 355 | } 356 | 357 | if vers := cert.GetVersion(); vers != crypto.X509V3 { 358 | t.Fatalf("bad version: %d", vers) 359 | } 360 | } 361 | -------------------------------------------------------------------------------- /crypto/nid.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package crypto 16 | 17 | type NID int 18 | 19 | const ( 20 | NidUndef NID = 0 21 | NidRsadsi NID = 1 22 | NidPkcs NID = 2 23 | NidMd2 NID = 3 24 | NidMd5 NID = 4 25 | NidRc4 NID = 5 26 | NidRsaEncryption NID = 6 27 | NidMd2WithRSAEncryption NID = 7 28 | NidMd5WithRSAEncryption NID = 8 29 | NidPbeWithMD2AndDESCBC NID = 9 30 | NidPbeWithMD5AndDESCBC NID = 10 31 | NidX500 NID = 11 32 | NidX509 NID = 12 33 | NidCommonName NID = 13 34 | NidCountryName NID = 14 35 | NidLocalityName NID = 15 36 | NidStateOrProvinceName NID = 16 37 | NidOrganizationName NID = 17 38 | NidOrganizationalUnitName NID = 18 39 | NidRsa NID = 19 40 | NidPkcs7 NID = 20 41 | NidPkcs7Data NID = 21 42 | NidPkcs7Signed NID = 22 43 | NidPkcs7Enveloped NID = 23 44 | NidPkcs7SignedAndEnveloped NID = 24 45 | NidPkcs7Digest NID = 25 46 | NidPkcs7Encrypted NID = 26 47 | NidPkcs3 NID = 27 48 | NidDhKeyAgreement NID = 28 49 | NidDesEcb NID = 29 50 | NidDesCfb64 NID = 30 51 | NidDesCbc NID = 31 52 | NidDesEde NID = 32 53 | NidDesEde3 NID = 33 54 | NidIdeaCbc NID = 34 55 | NidIdeaCfb64 NID = 35 56 | NidIdeaEcb NID = 36 57 | NidRc2Cbc NID = 37 58 | NidRc2Ecb NID = 38 59 | NidRc2Cfb64 NID = 39 60 | NidRc2Ofb64 NID = 40 61 | NidSha NID = 41 62 | NidShaWithRSAEncryption NID = 42 63 | NidDesEdeCbc NID = 43 64 | NidDesEde3Cbc NID = 44 65 | NidDesOfb64 NID = 45 66 | NidIdeaOfb64 NID = 46 67 | NidPkcs9 NID = 47 68 | NidPkcs9EmailAddress NID = 48 69 | NidPkcs9UnstructuredName NID = 49 70 | NidPkcs9ContentType NID = 50 71 | NidPkcs9MessageDigest NID = 51 72 | NidPkcs9SigningTime NID = 52 73 | NidPkcs9Countersignature NID = 53 74 | NidPkcs9ChallengePassword NID = 54 75 | NidPkcs9UnstructuredAddress NID = 55 76 | NidPkcs9ExtCertAttributes NID = 56 77 | NidNetscape NID = 57 78 | NidNetscapeCertExtension NID = 58 79 | NidNetscapeDataType NID = 59 80 | NidDesEdeCfb64 NID = 60 81 | NidDesEde3Cfb64 NID = 61 82 | NidDesEdeOfb64 NID = 62 83 | NidDesEde3Ofb64 NID = 63 84 | NidSha1 NID = 64 85 | NidSha1WithRSAEncryption NID = 65 86 | NidDsaWithSHA NID = 66 87 | NidDsa2 NID = 67 88 | NidPbeWithSHA1AndRC2CBC NID = 68 89 | NidIDPbkdf2 NID = 69 90 | NidDsaWithSHA12 NID = 70 91 | NidNetscapeCertType NID = 71 92 | NidNetscapeBaseURL NID = 72 93 | NidNetscapeRevocationURL NID = 73 94 | NidNetscapeCaRevocationURL NID = 74 95 | NidNetscapeRenewalURL NID = 75 96 | NidNetscapeCaPolicyURL NID = 76 97 | NidNetscapeSslServerName NID = 77 98 | NidNetscapeComment NID = 78 99 | NidNetscapeCertSequence NID = 79 100 | NidDesxCbc NID = 80 101 | NidIDCe NID = 81 102 | NidSubjectKeyIdentifier NID = 82 103 | NidKeyUsage NID = 83 104 | NidPrivateKeyUsagePeriod NID = 84 105 | NidSubjectAltName NID = 85 106 | NidIssuerAltName NID = 86 107 | NidBasicConstraints NID = 87 108 | NidCrlNumber NID = 88 109 | NidCertificatePolicies NID = 89 110 | NidAuthorityKeyIdentifier NID = 90 111 | NidBfCbc NID = 91 112 | NidBfEcb NID = 92 113 | NidBfCfb64 NID = 93 114 | NidBfOfb64 NID = 94 115 | NidMdc2 NID = 95 116 | NidMdc2WithRSA NID = 96 117 | NidRc440 NID = 97 118 | NidRc240Cbc NID = 98 119 | NidGivenName NID = 99 120 | NidSurname NID = 100 121 | NidInitials NID = 101 122 | NidUniqueIdentifier NID = 102 123 | NidCrlDistributionPoints NID = 103 124 | NidMd5WithRSA NID = 104 125 | NidSerialNumber NID = 105 126 | NidTitle NID = 106 127 | NidDescription NID = 107 128 | NidCast5Cbc NID = 108 129 | NidCast5Ecb NID = 109 130 | NidCast5Cfb64 NID = 110 131 | NidCast5Ofb64 NID = 111 132 | NidPbeWithMD5AndCast5CBC NID = 112 133 | NidDsaWithSHA1 NID = 113 134 | NidMd5Sha1 NID = 114 135 | NidSha1WithRSA NID = 115 136 | NidDsa NID = 116 137 | NidRipemd160 NID = 117 138 | NidRipemd160WithRSA NID = 119 139 | NidRc5Cbc NID = 120 140 | NidRc5Ecb NID = 121 141 | NidRc5Cfb64 NID = 122 142 | NidRc5Ofb64 NID = 123 143 | NidRleCompression NID = 124 144 | NidZlibCompression NID = 125 145 | NidExtKeyUsage NID = 126 146 | NidIDPkix NID = 127 147 | NidIDKp NID = 128 148 | NidServerAuth NID = 129 149 | NidClientAuth NID = 130 150 | NidCodeSign NID = 131 151 | NidEmailProtect NID = 132 152 | NidTimeStamp NID = 133 153 | NidMsCodeInd NID = 134 154 | NidMsCodeCom NID = 135 155 | NidMsCtlSign NID = 136 156 | NidMsSgc NID = 137 157 | NidMsEfs NID = 138 158 | NidNsSgc NID = 139 159 | NidDeltaCrl NID = 140 160 | NidCrlReason NID = 141 161 | NidInvalidityDate NID = 142 162 | NidSxnet NID = 143 163 | NidPbeWithSHA1And128BitRC4 NID = 144 164 | NidPbeWithSHA1And40BitRC4 NID = 145 165 | NidPbeWithSHA1And3KeyTripleDESCBC NID = 146 166 | NidPbeWithSHA1And2KeyTripleDESCBC NID = 147 167 | NidPbeWithSHA1And128BitRC2CBC NID = 148 168 | NidPbeWithSHA1And40BitRC2CBC NID = 149 169 | NidKeyBag NID = 150 170 | NidPkcs8ShroudedKeyBag NID = 151 171 | NidCertBag NID = 152 172 | NidCrlBag NID = 153 173 | NidSecretBag NID = 154 174 | NidSafeContentsBag NID = 155 175 | NidFriendlyName NID = 156 176 | NidLocalKeyID NID = 157 177 | NidX509Certificate NID = 158 178 | NidSdsiCertificate NID = 159 179 | NidX509Crl NID = 160 180 | NidPbes2 NID = 161 181 | NidPbmac1 NID = 162 182 | NidHmacWithSHA1 NID = 163 183 | NidIDQtCps NID = 164 184 | NidIDQtUnotice NID = 165 185 | NidRc264Cbc NID = 166 186 | NidSMIMECapabilities NID = 167 187 | NidPbeWithMD2AndRC2CBC NID = 168 188 | NidPbeWithMD5AndRC2CBC NID = 169 189 | NidPbeWithSHA1AndDESCBC NID = 170 190 | NidMsExtReq NID = 171 191 | NidExtReq NID = 172 192 | NidName NID = 173 193 | NidDnQualifier NID = 174 194 | NidIDPe NID = 175 195 | NidIDAd NID = 176 196 | NidInfoAccess NID = 177 197 | NidAdOCSP NID = 178 198 | NidAdCaIssuers NID = 179 199 | NidOCSPSign NID = 180 200 | NidX962IdEcPublicKey NID = 408 201 | NidHmac NID = 855 202 | NidCmac NID = 894 203 | NidDhpublicnumber NID = 920 204 | NidTLS1Prf NID = 1021 205 | NidHkdf NID = 1036 206 | NidX25519 NID = 1034 207 | NidX448 NID = 1035 208 | NidEd25519 NID = 1087 209 | NidEd448 NID = 1088 210 | NidSM2 NID = 1172 211 | ) 212 | -------------------------------------------------------------------------------- /crypto/ciphers.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package crypto 16 | 17 | // #include "shim.h" 18 | import "C" 19 | 20 | import ( 21 | "fmt" 22 | "runtime" 23 | "unsafe" 24 | ) 25 | 26 | const ( 27 | GCMTagMaxLen = 16 28 | ) 29 | 30 | const ( 31 | CipherModeECB = 1 32 | CipherModeCBC = 2 33 | CipherModeCFB = 3 34 | CipherModeOFB = 4 35 | CipherModeCTR = 5 36 | CipherModeGCM = 6 37 | CipherModeCCM = 7 38 | ) 39 | 40 | type CipherCtx interface { 41 | Ctx() *C.EVP_CIPHER_CTX 42 | Cipher() *Cipher 43 | BlockSize() int 44 | KeySize() int 45 | IVSize() int 46 | SetKeyAndIV(key, iv []byte) error 47 | SetPadding(pad bool) 48 | SetCtrl(code, arg int) error 49 | SetCtrlBytes(code, arg int, value []byte) error 50 | GetCtrlInt(code, arg int) (int, error) 51 | GetCtrlBytes(code, arg, expectsize int) ([]byte, error) 52 | } 53 | 54 | type Cipher struct { 55 | ptr *C.EVP_CIPHER 56 | } 57 | 58 | func (c *Cipher) Ptr() *C.EVP_CIPHER { 59 | return c.ptr 60 | } 61 | 62 | func (c *Cipher) Nid() NID { 63 | return NID(C.X_EVP_CIPHER_nid(c.ptr)) 64 | } 65 | 66 | func (c *Cipher) ShortName() (string, error) { 67 | return Nid2ShortName(c.Nid()) 68 | } 69 | 70 | func (c *Cipher) BlockSize() int { 71 | return int(C.X_EVP_CIPHER_block_size(c.ptr)) 72 | } 73 | 74 | func (c *Cipher) KeySize() int { 75 | return int(C.X_EVP_CIPHER_key_length(c.ptr)) 76 | } 77 | 78 | func (c *Cipher) IVSize() int { 79 | return int(C.X_EVP_CIPHER_iv_length(c.ptr)) 80 | } 81 | 82 | func Nid2ShortName(nid NID) (string, error) { 83 | sn := C.OBJ_nid2sn(C.int(nid)) 84 | if sn == nil { 85 | return "", PopError() 86 | } 87 | return C.GoString(sn), nil 88 | } 89 | 90 | func GetCipherByName(name string) (*Cipher, error) { 91 | cname := C.CString(name) 92 | defer C.free(unsafe.Pointer(cname)) 93 | p := C.EVP_get_cipherbyname(cname) 94 | if p == nil { 95 | return nil, ErrCipherNotFound 96 | } 97 | // we can consider ciphers to use static mem; don't need to free 98 | return &Cipher{ptr: p}, nil 99 | } 100 | 101 | func GetCipherByNid(nid NID) (*Cipher, error) { 102 | sn, err := Nid2ShortName(nid) 103 | if err != nil { 104 | return nil, err 105 | } 106 | return GetCipherByName(sn) 107 | } 108 | 109 | type cipherCtx struct { 110 | ctx *C.EVP_CIPHER_CTX 111 | } 112 | 113 | func newCipherCtx() (*cipherCtx, error) { 114 | cctx := C.EVP_CIPHER_CTX_new() 115 | if cctx == nil { 116 | return nil, ErrMallocFailure 117 | } 118 | ctx := &cipherCtx{cctx} 119 | runtime.SetFinalizer(ctx, func(ctx *cipherCtx) { 120 | C.EVP_CIPHER_CTX_free(ctx.ctx) 121 | }) 122 | return ctx, nil 123 | } 124 | 125 | func (ctx *cipherCtx) SetKeyAndIV(key, iv []byte) error { 126 | var kptr, iptr *C.uchar 127 | if key != nil { 128 | if len(key) != ctx.KeySize() { 129 | return fmt.Errorf("bad key size (%d bytes instead of %d): %w", 130 | len(key), ctx.KeySize(), ErrBadKeySize) 131 | } 132 | kptr = (*C.uchar)(&key[0]) 133 | } 134 | if iv != nil { 135 | if len(iv) != ctx.IVSize() { 136 | return fmt.Errorf("bad IV size (%d bytes instead of %d): %w", 137 | len(iv), ctx.IVSize(), ErrBadIvSize) 138 | } 139 | iptr = (*C.uchar)(&iv[0]) 140 | } 141 | if kptr != nil || iptr != nil { 142 | var res C.int 143 | if C.X_EVP_CIPHER_CTX_encrypting(ctx.ctx) != 0 { 144 | res = C.EVP_EncryptInit_ex(ctx.ctx, nil, nil, kptr, iptr) 145 | } else { 146 | res = C.EVP_DecryptInit_ex(ctx.ctx, nil, nil, kptr, iptr) 147 | } 148 | if res != 1 { 149 | return PopError() 150 | } 151 | } 152 | return nil 153 | } 154 | 155 | func (ctx *cipherCtx) Ctx() *C.EVP_CIPHER_CTX { 156 | return ctx.ctx 157 | } 158 | 159 | func (ctx *cipherCtx) Cipher() *Cipher { 160 | return &Cipher{ptr: C.X_EVP_CIPHER_CTX_cipher(ctx.ctx)} 161 | } 162 | 163 | func (ctx *cipherCtx) BlockSize() int { 164 | return int(C.X_EVP_CIPHER_CTX_block_size(ctx.ctx)) 165 | } 166 | 167 | func (ctx *cipherCtx) KeySize() int { 168 | return int(C.X_EVP_CIPHER_CTX_key_length(ctx.ctx)) 169 | } 170 | 171 | func (ctx *cipherCtx) IVSize() int { 172 | return int(C.X_EVP_CIPHER_CTX_iv_length(ctx.ctx)) 173 | } 174 | 175 | func (ctx *cipherCtx) SetPadding(pad bool) { 176 | if pad { 177 | C.X_EVP_CIPHER_CTX_set_padding(ctx.ctx, 1) 178 | } else { 179 | C.X_EVP_CIPHER_CTX_set_padding(ctx.ctx, 0) 180 | } 181 | } 182 | 183 | func (ctx *cipherCtx) SetCtrl(code, arg int) error { 184 | res := C.EVP_CIPHER_CTX_ctrl(ctx.ctx, C.int(code), C.int(arg), nil) 185 | if res != 1 { 186 | return fmt.Errorf("failed to set code %d to %d [result %d]: %w", 187 | code, arg, res, PopError()) 188 | } 189 | return nil 190 | } 191 | 192 | func (ctx *cipherCtx) SetCtrlBytes(code, arg int, value []byte) error { 193 | res := C.EVP_CIPHER_CTX_ctrl(ctx.ctx, C.int(code), C.int(arg), 194 | unsafe.Pointer(&value[0])) 195 | if res != 1 { 196 | return fmt.Errorf("failed to set code %d with arg %d to %x [result %d]: %w", 197 | code, arg, value, res, PopError()) 198 | } 199 | return nil 200 | } 201 | 202 | func (ctx *cipherCtx) GetCtrlInt(code, arg int) (int, error) { 203 | var returnVal C.int 204 | res := C.EVP_CIPHER_CTX_ctrl(ctx.ctx, C.int(code), C.int(arg), 205 | unsafe.Pointer(&returnVal)) 206 | if res != 1 { 207 | return 0, fmt.Errorf("failed to get code %d with arg %d [result %d]: %w", 208 | code, arg, res, PopError()) 209 | } 210 | return int(returnVal), nil 211 | } 212 | 213 | func (ctx *cipherCtx) GetCtrlBytes(code, arg, expectsize int) ([]byte, error) { 214 | returnVal := make([]byte, expectsize) 215 | res := C.EVP_CIPHER_CTX_ctrl(ctx.ctx, C.int(code), C.int(arg), 216 | unsafe.Pointer(&returnVal[0])) 217 | if res != 1 { 218 | return nil, fmt.Errorf("failed to get code %d with arg %d [result %d]: %w", 219 | code, arg, res, PopError()) 220 | } 221 | return returnVal, nil 222 | } 223 | 224 | type EncryptionCipherCtx interface { 225 | CipherCtx 226 | 227 | // pass in plaintext, get back ciphertext. can be called 228 | // multiple times as needed 229 | EncryptUpdate(input []byte) ([]byte, error) 230 | 231 | // call after all plaintext has been passed in; may return 232 | // additional ciphertext if needed to finish off a block 233 | // or extra padding information 234 | EncryptFinal() ([]byte, error) 235 | } 236 | 237 | type DecryptionCipherCtx interface { 238 | CipherCtx 239 | 240 | // pass in ciphertext, get back plaintext. can be called 241 | // multiple times as needed 242 | DecryptUpdate(input []byte) ([]byte, error) 243 | 244 | // call after all ciphertext has been passed in; may return 245 | // additional plaintext if needed to finish off a block 246 | DecryptFinal() ([]byte, error) 247 | } 248 | 249 | type encryptionCipherCtx struct { 250 | *cipherCtx 251 | } 252 | 253 | type decryptionCipherCtx struct { 254 | *cipherCtx 255 | } 256 | 257 | func newEncryptionCipherCtx(cipher *Cipher, e *Engine, key, iv []byte) ( 258 | *encryptionCipherCtx, error, 259 | ) { 260 | if cipher == nil { 261 | return nil, ErrNilParameter 262 | } 263 | ctx, err := newCipherCtx() 264 | if err != nil { 265 | return nil, err 266 | } 267 | var eptr *C.ENGINE 268 | if e != nil { 269 | eptr = e.Engine() 270 | } 271 | if C.EVP_EncryptInit_ex(ctx.ctx, cipher.ptr, eptr, nil, nil) != 1 { 272 | return nil, PopError() 273 | } 274 | err = ctx.SetKeyAndIV(key, iv) 275 | if err != nil { 276 | return nil, err 277 | } 278 | return &encryptionCipherCtx{cipherCtx: ctx}, nil 279 | } 280 | 281 | func newDecryptionCipherCtx(cipher *Cipher, e *Engine, key, iv []byte) ( 282 | *decryptionCipherCtx, error, 283 | ) { 284 | if cipher == nil { 285 | return nil, ErrNilParameter 286 | } 287 | ctx, err := newCipherCtx() 288 | if err != nil { 289 | return nil, err 290 | } 291 | var eptr *C.ENGINE 292 | if e != nil { 293 | eptr = e.Engine() 294 | } 295 | if C.EVP_DecryptInit_ex(ctx.ctx, cipher.ptr, eptr, nil, nil) != 1 { 296 | return nil, PopError() 297 | } 298 | err = ctx.SetKeyAndIV(key, iv) 299 | if err != nil { 300 | return nil, err 301 | } 302 | return &decryptionCipherCtx{cipherCtx: ctx}, nil 303 | } 304 | 305 | func NewEncryptionCipherCtx(c *Cipher, e *Engine, key, iv []byte) ( 306 | EncryptionCipherCtx, error, 307 | ) { 308 | return newEncryptionCipherCtx(c, e, key, iv) 309 | } 310 | 311 | func NewDecryptionCipherCtx(c *Cipher, e *Engine, key, iv []byte) ( 312 | DecryptionCipherCtx, error, 313 | ) { 314 | return newDecryptionCipherCtx(c, e, key, iv) 315 | } 316 | 317 | func (ctx *encryptionCipherCtx) EncryptUpdate(input []byte) ([]byte, error) { 318 | if len(input) == 0 { 319 | return nil, nil 320 | } 321 | outbuf := make([]byte, len(input)+ctx.BlockSize()) 322 | outlen := C.int(len(outbuf)) 323 | res := C.EVP_EncryptUpdate(ctx.ctx, (*C.uchar)(&outbuf[0]), &outlen, 324 | (*C.uchar)(&input[0]), C.int(len(input))) 325 | if res != 1 { 326 | return nil, fmt.Errorf("failed to encrypt [result %d]: %w", res, PopError()) 327 | } 328 | return outbuf[:outlen], nil 329 | } 330 | 331 | func (ctx *decryptionCipherCtx) DecryptUpdate(input []byte) ([]byte, error) { 332 | if len(input) == 0 { 333 | return nil, nil 334 | } 335 | outbuf := make([]byte, len(input)+ctx.BlockSize()) 336 | outlen := C.int(len(outbuf)) 337 | res := C.EVP_DecryptUpdate(ctx.ctx, (*C.uchar)(&outbuf[0]), &outlen, 338 | (*C.uchar)(&input[0]), C.int(len(input))) 339 | if res != 1 { 340 | return nil, fmt.Errorf("failed to decrypt [result %d]: %w", res, PopError()) 341 | } 342 | return outbuf[:outlen], nil 343 | } 344 | 345 | func (ctx *encryptionCipherCtx) EncryptFinal() ([]byte, error) { 346 | outbuf := make([]byte, ctx.BlockSize()) 347 | var outlen C.int 348 | if C.EVP_EncryptFinal_ex(ctx.ctx, (*C.uchar)(&outbuf[0]), &outlen) != 1 { 349 | return nil, fmt.Errorf("encryption failed: %w", PopError()) 350 | } 351 | return outbuf[:outlen], nil 352 | } 353 | 354 | func (ctx *decryptionCipherCtx) DecryptFinal() ([]byte, error) { 355 | outbuf := make([]byte, ctx.BlockSize()) 356 | var outlen C.int 357 | if C.EVP_DecryptFinal_ex(ctx.ctx, (*C.uchar)(&outbuf[0]), &outlen) != 1 { 358 | // this may mean the tag failed to verify- all previous plaintext 359 | // returned must be considered faked and invalid 360 | return nil, fmt.Errorf("decryption failed: %w", PopError()) 361 | } 362 | return outbuf[:outlen], nil 363 | } 364 | --------------------------------------------------------------------------------