-
Notifications
You must be signed in to change notification settings - Fork 106
/
signature.go
130 lines (107 loc) · 3.17 KB
/
signature.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package signature
import (
"crypto/ecdsa"
"encoding/hex"
"encoding/json"
"fmt"
"time"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/gowebpki/jcs"
"github.com/storyicon/sigverify"
)
type SignatureEnvelope struct {
Data string
Signature string
}
type SignatureData struct {
UploadID string `json:"upload_id"`
Cid string `json:"cid"`
ShouldCache int `json:"shouldCache"`
Timestamp int64 `json:"timestamp"`
TrackId int64 `json:"trackId"`
UserID int `json:"userId"`
}
type RecoveredSignature struct {
DataHash common.Hash
Data SignatureData
SignerWallet string
}
type ListenTSSignature struct {
Signature string
Timestamp string
}
func (r *RecoveredSignature) String() string {
j, _ := json.Marshal(r)
return string(j)
}
func ParseFromQueryString(queryStringValue string) (*RecoveredSignature, error) {
var envelope *SignatureEnvelope
err := json.Unmarshal([]byte(queryStringValue), &envelope)
if err != nil {
return nil, err
}
// ensure json keys are sorted
inner, err := jcs.Transform([]byte(envelope.Data))
if err != nil {
return nil, err
}
hash := crypto.Keccak256Hash(inner)
signatureBytes, err := hex.DecodeString(envelope.Signature[2:])
if err != nil {
return nil, err
}
recoveredAddress, err := sigverify.EcRecoverEx(hash.Bytes(), signatureBytes)
if err != nil {
return nil, err
}
var data SignatureData
err = json.Unmarshal([]byte(envelope.Data), &data)
if err != nil {
return nil, err
}
recovered := &RecoveredSignature{
DataHash: hash,
Data: data,
SignerWallet: recoveredAddress.String(),
}
return recovered, nil
}
func GenerateListenTimestampAndSignature(privateKey *ecdsa.PrivateKey) (*ListenTSSignature, error) {
// based on: https://github.com/AudiusProject/audius-protocol/blob/main/creator-node/src/apiSigning.ts
// '{"data":"listen","timestamp":"2023-05-24T15:37:57.051Z"}'
timestamp := time.Now().UTC().Format(time.RFC3339)
data := fmt.Sprintf("{\"data\":\"listen\",\"timestamp\":\"%s\"}", timestamp)
signature, err := Sign(data, privateKey)
if err != nil {
fmt.Println("Error signing message:", err)
return nil, err
}
signatureHex := fmt.Sprintf("0x%s", hex.EncodeToString(signature))
return &ListenTSSignature{
Signature: signatureHex,
Timestamp: timestamp,
}, nil
}
// From https://github.com/AudiusProject/sig/blob/main/go/index.go
func Sign(input string, privateKey *ecdsa.PrivateKey) ([]byte, error) {
return SignBytes([]byte(input), privateKey)
}
func SignBytes(input []byte, privateKey *ecdsa.PrivateKey) ([]byte, error) {
// hash the input
hash := crypto.Keccak256Hash(input)
// TextHash will prepend Ethereum signed message prefix to the hash
// and hash that again
hash2 := accounts.TextHash(hash.Bytes())
signature, err := crypto.Sign(hash2, privateKey)
if err != nil {
return nil, err
}
return signature, nil
}
// From https://github.com/AudiusProject/sig/blob/main/go/index.go
func recover(input string, signature []byte) (common.Address, error) {
hash := crypto.Keccak256Hash([]byte(input))
return sigverify.EcRecoverEx(hash.Bytes(), signature)
}