545 lines
13 KiB
Go
545 lines
13 KiB
Go
![]() |
package ccxt
|
||
|
|
||
|
import (
|
||
|
"crypto"
|
||
|
"crypto/ecdsa"
|
||
|
"crypto/elliptic"
|
||
|
"crypto/hmac"
|
||
|
md5Hash "crypto/md5"
|
||
|
"crypto/rand"
|
||
|
rsaHash "crypto/rsa"
|
||
|
sha1Hash "crypto/sha1"
|
||
|
sha256Hash "crypto/sha256"
|
||
|
sha512Hash "crypto/sha512"
|
||
|
"crypto/x509"
|
||
|
"encoding/base64"
|
||
|
"encoding/hex"
|
||
|
"encoding/pem"
|
||
|
"hash/crc32"
|
||
|
"math/big"
|
||
|
"strings"
|
||
|
|
||
|
"golang.org/x/crypto/sha3"
|
||
|
// "golang.org/x/crypto/sha3"
|
||
|
)
|
||
|
|
||
|
func Sha1() string { return "sha1" }
|
||
|
func Sha256() string { return "sha256" }
|
||
|
func sha256() string { return "sha256" }
|
||
|
func Sha384() string { return "sha384" }
|
||
|
func sha384() string { return "sha384" }
|
||
|
func Sha512() string { return "sha512" }
|
||
|
func sha512() string { return "sha512" }
|
||
|
func Md5() string { return "md5" }
|
||
|
func md5() string { return "md5" }
|
||
|
func Ed25519() string { return "ed25519" }
|
||
|
func ed25519() string { return "ed25519" }
|
||
|
func Keccak() string { return "keccak" }
|
||
|
func Secp256k1() string { return "secp256k1" }
|
||
|
func P256() string { return "p256" }
|
||
|
func keccak() string { return "keccak" }
|
||
|
func secp256k1() string { return "secp256k1" }
|
||
|
|
||
|
func (this *Exchange) Hmac(request2 interface{}, secret2 interface{}, algorithm2 func() string, args ...interface{}) string {
|
||
|
digest := GetArg(args, 0, "hex").(string)
|
||
|
return Hmac(request2, secret2, algorithm2, digest)
|
||
|
}
|
||
|
|
||
|
func Hmac(request2 interface{}, secret2 interface{}, algorithm2 func() string, digest string) string {
|
||
|
var request []byte
|
||
|
switch v := request2.(type) {
|
||
|
case string:
|
||
|
request = []byte(v)
|
||
|
case []byte:
|
||
|
request = v
|
||
|
}
|
||
|
|
||
|
var secretBytes []byte
|
||
|
switch v := secret2.(type) {
|
||
|
case string:
|
||
|
secretBytes = []byte(v)
|
||
|
case []byte:
|
||
|
secretBytes = v
|
||
|
}
|
||
|
|
||
|
algorithm := "md5"
|
||
|
if algorithm2 != nil {
|
||
|
algorithm = algorithm2()
|
||
|
}
|
||
|
|
||
|
var signature []byte
|
||
|
switch algorithm {
|
||
|
case "sha256":
|
||
|
signature = signHMACSHA256(request, secretBytes)
|
||
|
case "sha512":
|
||
|
signature = signHMACSHA512(request, secretBytes)
|
||
|
case "sha384":
|
||
|
signature = signHMACSHA384(request, secretBytes)
|
||
|
case "md5":
|
||
|
signature = signHMACMD5(request, secretBytes)
|
||
|
}
|
||
|
|
||
|
if digest == "hex" {
|
||
|
return hex.EncodeToString(signature)
|
||
|
}
|
||
|
return base64.StdEncoding.EncodeToString(signature)
|
||
|
}
|
||
|
|
||
|
func signHMACSHA256(data, secret []byte) []byte {
|
||
|
h := hmac.New(sha256Hash.New, secret)
|
||
|
h.Write([]byte(data))
|
||
|
return h.Sum(nil)
|
||
|
}
|
||
|
|
||
|
func signHMACSHA512(data, secret []byte) []byte {
|
||
|
h := hmac.New(sha512Hash.New, secret)
|
||
|
h.Write([]byte(data))
|
||
|
return h.Sum(nil)
|
||
|
}
|
||
|
|
||
|
func signHMACSHA384(data, secret []byte) []byte {
|
||
|
h := hmac.New(sha512Hash.New, secret)
|
||
|
h.Write([]byte(data))
|
||
|
return h.Sum(nil)
|
||
|
}
|
||
|
|
||
|
func signHMACMD5(data, secret []byte) []byte {
|
||
|
h := hmac.New(md5Hash.New, secret)
|
||
|
h.Write(data)
|
||
|
return h.Sum(nil)
|
||
|
}
|
||
|
|
||
|
func (this *Exchange) Hash(request2 interface{}, hash func() string, args ...interface{}) interface{} {
|
||
|
digest2 := GetArg(args, 0, "hex")
|
||
|
return Hash(request2, hash, digest2)
|
||
|
}
|
||
|
|
||
|
func Hash(request2 interface{}, hash func() string, digest2 interface{}) interface{} {
|
||
|
var request string
|
||
|
switch v := request2.(type) {
|
||
|
case string:
|
||
|
request = v
|
||
|
}
|
||
|
|
||
|
algorithm := hash()
|
||
|
digest := "hex"
|
||
|
if digest2 != nil {
|
||
|
digest = digest2.(string)
|
||
|
}
|
||
|
|
||
|
var signature []byte
|
||
|
switch algorithm {
|
||
|
case "sha256":
|
||
|
signature = signSHA256(request)
|
||
|
case "sha512":
|
||
|
signature = signSHA512(request)
|
||
|
case "sha384":
|
||
|
signature = signSHA384(request)
|
||
|
case "sha1":
|
||
|
signature = signSHA1(request)
|
||
|
case "md5":
|
||
|
signature = signMD5(request)
|
||
|
case "keccak":
|
||
|
signature = signKeccak(request2)
|
||
|
case "sha3":
|
||
|
signature = signKeccak(request)
|
||
|
}
|
||
|
|
||
|
if digest == "binary" {
|
||
|
return signature
|
||
|
}
|
||
|
if digest == "hex" {
|
||
|
return hex.EncodeToString(signature)
|
||
|
}
|
||
|
return base64.StdEncoding.EncodeToString(signature)
|
||
|
}
|
||
|
|
||
|
func (this *Exchange) Axolotl(a interface{}, b interface{}, c interface{}) string {
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
func signSHA256(data string) []byte {
|
||
|
h := sha256Hash.New()
|
||
|
h.Write([]byte(data))
|
||
|
return h.Sum(nil)
|
||
|
}
|
||
|
|
||
|
func signSHA512(data string) []byte {
|
||
|
h := sha512Hash.New()
|
||
|
h.Write([]byte(data))
|
||
|
return h.Sum(nil)
|
||
|
}
|
||
|
|
||
|
func signSHA384(data string) []byte {
|
||
|
h := sha512Hash.New384()
|
||
|
h.Write([]byte(data))
|
||
|
return h.Sum(nil)
|
||
|
}
|
||
|
|
||
|
func signSHA1(data string) []byte {
|
||
|
h := sha1Hash.New()
|
||
|
h.Write([]byte(data))
|
||
|
return h.Sum(nil)
|
||
|
}
|
||
|
|
||
|
func signMD5(data string) []byte {
|
||
|
h := md5Hash.New()
|
||
|
h.Write([]byte(data))
|
||
|
return h.Sum(nil)
|
||
|
}
|
||
|
|
||
|
func signKeccak(data interface{}) []byte {
|
||
|
var input []byte
|
||
|
|
||
|
switch v := data.(type) {
|
||
|
case string:
|
||
|
input = []byte(v)
|
||
|
case []byte:
|
||
|
input = v
|
||
|
default:
|
||
|
return []byte{}
|
||
|
}
|
||
|
|
||
|
hash := sha3.NewLegacyKeccak256()
|
||
|
hash.Write(input)
|
||
|
return hash.Sum(nil)
|
||
|
}
|
||
|
|
||
|
func Jwt(data interface{}, secret interface{}, hash func() string, optionalArgs ...interface{}) string {
|
||
|
isRsa := GetArg(optionalArgs, 0, false).(bool)
|
||
|
params := GetArg(optionalArgs, 2, map[string]interface{}{}).(map[string]interface{})
|
||
|
return JwtFull(data, secret, hash, isRsa, params)
|
||
|
}
|
||
|
|
||
|
func JwtFull(data interface{}, secret interface{}, hash func() string, isRsa bool, options map[string]interface{}) string {
|
||
|
if options == nil {
|
||
|
options = make(map[string]interface{})
|
||
|
}
|
||
|
algorithm := hash()
|
||
|
algPrefix := "HS"
|
||
|
if isRsa {
|
||
|
algPrefix = "RS"
|
||
|
}
|
||
|
alg := algPrefix + strings.ToUpper(algorithm[3:])
|
||
|
if algOpt, ok := options["alg"]; ok {
|
||
|
alg = algOpt.(string)
|
||
|
}
|
||
|
header := map[string]interface{}{
|
||
|
"alg": alg,
|
||
|
"typ": "JWT",
|
||
|
}
|
||
|
for k, v := range options {
|
||
|
header[k] = v
|
||
|
}
|
||
|
|
||
|
if iat, ok := header["iat"]; ok {
|
||
|
if dataMap, ok := data.(map[string]interface{}); ok {
|
||
|
dataMap["iat"] = iat
|
||
|
}
|
||
|
delete(header, "iat")
|
||
|
}
|
||
|
|
||
|
encodedHeader := base64.RawURLEncoding.EncodeToString([]byte(Json(header)))
|
||
|
encodedData := base64.RawURLEncoding.EncodeToString([]byte(Json(data)))
|
||
|
token := encodedHeader + "." + encodedData
|
||
|
|
||
|
var signature string
|
||
|
if isRsa {
|
||
|
signature = Rsa(token, secret, hash)
|
||
|
} else if alg[:2] == "ES" {
|
||
|
// Ecdsa signing logic here (omitted for simplicity)
|
||
|
} else {
|
||
|
signature = base64.RawURLEncoding.EncodeToString(signHMACSHA256([]byte(token), []byte(secret.(string))))
|
||
|
}
|
||
|
// dirty quicky
|
||
|
signature = strings.Replace(signature, "+", "-", -1)
|
||
|
signature = strings.Replace(signature, "/", "_", -1)
|
||
|
signature = strings.TrimRight(signature, "==")
|
||
|
return token + "." + signature
|
||
|
}
|
||
|
|
||
|
func Rsa(data2 interface{}, privateKey2 interface{}, algorithm2 func() string) string {
|
||
|
data := data2.(string)
|
||
|
publicKey := privateKey2.(string)
|
||
|
// hashAlgorithm := hashAlgorithm2.(string)
|
||
|
hashAlgorithm := algorithm2()
|
||
|
// Remove PEM headers
|
||
|
// pkParts := strings.Split(publicKey, "\n")
|
||
|
// pkParts = pkParts[1 : len(pkParts)-1]
|
||
|
// newPk := strings.Join(pkParts, "")
|
||
|
|
||
|
// Decode base64 public key
|
||
|
// privateKey, err := base64.StdEncoding.DecodeString(newPk)
|
||
|
// if err != nil {
|
||
|
// panic(err)
|
||
|
// }
|
||
|
privateKey := []byte(publicKey)
|
||
|
|
||
|
// Parse the private key
|
||
|
block, _ := pem.Decode(privateKey)
|
||
|
if block == nil || block.Type != "RSA PRIVATE KEY" {
|
||
|
panic("RSA PRIVATE KEY")
|
||
|
}
|
||
|
|
||
|
parsedKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
|
||
|
// Hash the data
|
||
|
var hashedData []byte
|
||
|
var hash crypto.Hash
|
||
|
|
||
|
switch hashAlgorithm {
|
||
|
case "sha1":
|
||
|
hash = crypto.SHA1
|
||
|
h := sha1Hash.New()
|
||
|
h.Write([]byte(data))
|
||
|
hashedData = h.Sum(nil)
|
||
|
case "sha256":
|
||
|
hash = crypto.SHA256
|
||
|
h := sha256Hash.New()
|
||
|
h.Write([]byte(data))
|
||
|
hashedData = h.Sum(nil)
|
||
|
case "sha384":
|
||
|
hash = crypto.SHA384
|
||
|
h := sha512Hash.New384()
|
||
|
h.Write([]byte(data))
|
||
|
hashedData = h.Sum(nil)
|
||
|
case "sha512":
|
||
|
hash = crypto.SHA512
|
||
|
h := sha512Hash.New()
|
||
|
h.Write([]byte(data))
|
||
|
hashedData = h.Sum(nil)
|
||
|
case "md5":
|
||
|
hash = crypto.MD5
|
||
|
h := md5Hash.New()
|
||
|
h.Write([]byte(data))
|
||
|
hashedData = h.Sum(nil)
|
||
|
default:
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
// Sign the data
|
||
|
signData, err := rsaHash.SignPKCS1v15(rand.Reader, parsedKey, hash, hashedData)
|
||
|
if err != nil {
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
// Return base64 encoded signature
|
||
|
return base64.StdEncoding.EncodeToString(signData)
|
||
|
}
|
||
|
|
||
|
func Eddsa(data2 interface{}, publicKey2 interface{}, hashAlgorithm2 interface{}) string {
|
||
|
return "" // to do
|
||
|
}
|
||
|
|
||
|
// func Ecdsa(request interface{}, secret interface{}, alg interface{}, hash interface{}) string {
|
||
|
// return "" // to do
|
||
|
// }
|
||
|
|
||
|
func stringToPrivateKey(privKeyStr string) *ecdsa.PrivateKey {
|
||
|
// Decode PEM formatted private key
|
||
|
block, _ := pem.Decode([]byte(privKeyStr))
|
||
|
if block == nil {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Parse the ECDSA private key
|
||
|
privKey, err := x509.ParseECPrivateKey(block.Bytes)
|
||
|
if err != nil {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
return privKey
|
||
|
}
|
||
|
|
||
|
var secp256k1Curve = &elliptic.CurveParams{
|
||
|
Name: "secp256k1",
|
||
|
BitSize: 256,
|
||
|
P: fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F"),
|
||
|
N: fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"),
|
||
|
B: fromHex("07"),
|
||
|
Gx: fromHex("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798"),
|
||
|
Gy: fromHex("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8"),
|
||
|
}
|
||
|
|
||
|
// Helper function to convert hex string to big.Int
|
||
|
func fromHex(s string) *big.Int {
|
||
|
b, _ := new(big.Int).SetString(s, 16)
|
||
|
return b
|
||
|
}
|
||
|
|
||
|
// Helper function to convert a string hex to byte array
|
||
|
func hexToBytes(hexStr string) ([]byte, bool) {
|
||
|
bytes, err := hex.DecodeString(hexStr)
|
||
|
if err != nil {
|
||
|
return nil, false
|
||
|
}
|
||
|
return bytes, true
|
||
|
}
|
||
|
|
||
|
// Helper function to convert byte array to hex string
|
||
|
func toHex(bytes []byte) string {
|
||
|
return hex.EncodeToString(bytes)
|
||
|
}
|
||
|
|
||
|
func enforceLowS(s *big.Int) *big.Int {
|
||
|
// secp256k1 curve order
|
||
|
curveOrder := fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141")
|
||
|
|
||
|
// If s is greater than curveOrder / 2, calculate the new s as curveOrder - s
|
||
|
halfOrder := new(big.Int).Div(curveOrder, big.NewInt(2))
|
||
|
if s.Cmp(halfOrder) > 0 {
|
||
|
s.Sub(curveOrder, s)
|
||
|
}
|
||
|
return s
|
||
|
}
|
||
|
|
||
|
// Sign the message with secp256k1 curve using Go's native ecdsa package
|
||
|
|
||
|
// func signSecp256k1(message []byte, seckey []byte) ([]byte, int, bool) {
|
||
|
// // Sign the message with the secp256k1 private key
|
||
|
// // return nil, 0, false
|
||
|
// signature, err := secp256k1Hash.Sign(message, seckey)
|
||
|
// if err != nil {
|
||
|
// return nil, 0, false
|
||
|
// }
|
||
|
|
||
|
// recoveryID := int(signature[64])
|
||
|
|
||
|
// // // Split the signature into r and s components
|
||
|
// r := new(big.Int).SetBytes(signature[:32])
|
||
|
// s := new(big.Int).SetBytes(signature[32:64])
|
||
|
|
||
|
// // // Enforce low-s rule on the 's' value
|
||
|
// s = enforceLowS(s)
|
||
|
|
||
|
// // // Convert r and s back to byte slices
|
||
|
// rBytes := r.FillBytes(make([]byte, 32))
|
||
|
// sBytes := s.FillBytes(make([]byte, 32))
|
||
|
|
||
|
// // // Reconstruct the signature with the adjusted low-s value
|
||
|
// signature = append(rBytes, sBytes...)
|
||
|
|
||
|
// // The recovery ID is the last byte in the original signature
|
||
|
|
||
|
// return signature, recoveryID, true
|
||
|
// }
|
||
|
|
||
|
// Helper function to sign with P256 (Go's native implementation)
|
||
|
func signP256(message []byte, seckey []byte) ([]byte, int, bool) {
|
||
|
curve := elliptic.P256()
|
||
|
privKey := new(ecdsa.PrivateKey)
|
||
|
privKey.PublicKey.Curve = curve
|
||
|
privKey.D = new(big.Int).SetBytes(seckey)
|
||
|
|
||
|
r, s, err := ecdsa.Sign(rand.Reader, privKey, message)
|
||
|
if err != nil {
|
||
|
return nil, 0, false
|
||
|
}
|
||
|
|
||
|
rBytes := r.Bytes()
|
||
|
sBytes := s.Bytes()
|
||
|
signature := append(rBytes, sBytes...)
|
||
|
|
||
|
return signature, 0, true // P256 does not need a recovery ID
|
||
|
}
|
||
|
|
||
|
// Main Ecdsa function
|
||
|
func Ecdsa(request interface{}, secret interface{}, curveFunc func() string, hashFunc func() string) map[string]interface{} {
|
||
|
// Initialize return structure
|
||
|
result := map[string]interface{}{
|
||
|
"r": "",
|
||
|
"s": "",
|
||
|
"v": 0,
|
||
|
}
|
||
|
|
||
|
// Determine the curve
|
||
|
curveName := "secp256k1"
|
||
|
if curveFunc != nil {
|
||
|
curveName = curveFunc()
|
||
|
}
|
||
|
if curveName != "secp256k1" && curveName != "p256" {
|
||
|
return result
|
||
|
}
|
||
|
|
||
|
// Hash the message if needed
|
||
|
var messageHash []byte
|
||
|
requestStr, ok := request.(string)
|
||
|
if !ok {
|
||
|
return result
|
||
|
}
|
||
|
|
||
|
if hashFunc != nil {
|
||
|
hashName := hashFunc()
|
||
|
if hashName == "sha256" {
|
||
|
h := sha256Hash.New()
|
||
|
h.Write([]byte(requestStr))
|
||
|
messageHash = h.Sum(nil)
|
||
|
} else {
|
||
|
return result
|
||
|
}
|
||
|
} else {
|
||
|
messageHash, ok = hexToBytes(requestStr)
|
||
|
if !ok {
|
||
|
return result
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Convert secret key to bytes
|
||
|
secretStr, ok := secret.(string)
|
||
|
if !ok {
|
||
|
return result
|
||
|
}
|
||
|
secretKeyBytes, ok := hexToBytes(secretStr)
|
||
|
if !ok {
|
||
|
return result
|
||
|
}
|
||
|
|
||
|
// Sign the message depending on the curve
|
||
|
var signature []byte
|
||
|
var recoveryId int
|
||
|
success := false
|
||
|
if curveName == "secp256k1" {
|
||
|
signature, recoveryId, success = signSecp256k1(messageHash, secretKeyBytes)
|
||
|
} else {
|
||
|
signature, recoveryId, success = signP256(messageHash, secretKeyBytes)
|
||
|
}
|
||
|
if !success {
|
||
|
return result
|
||
|
}
|
||
|
|
||
|
// Extract r and s components
|
||
|
rBytes := signature[:32]
|
||
|
sBytes := signature[32:]
|
||
|
|
||
|
rHex := toHex(rBytes)
|
||
|
sHex := toHex(sBytes)
|
||
|
|
||
|
// Update the result map with signature components
|
||
|
result["r"] = rHex
|
||
|
result["s"] = sHex
|
||
|
result["v"] = recoveryId
|
||
|
|
||
|
return result
|
||
|
}
|
||
|
|
||
|
func Crc32(str string, signed2 ...bool) int64 {
|
||
|
signed := false
|
||
|
if len(signed2) > 0 {
|
||
|
signed = signed2[0]
|
||
|
}
|
||
|
|
||
|
// Compute the CRC32 checksum using IEEE polynomial
|
||
|
crc := crc32.ChecksumIEEE([]byte(str))
|
||
|
|
||
|
if signed {
|
||
|
// Convert unsigned CRC32 to signed 32-bit integer
|
||
|
return int64(int32(crc))
|
||
|
}
|
||
|
|
||
|
// Return unsigned 32-bit integer as int64
|
||
|
return int64(crc)
|
||
|
}
|