• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

vocdoni / saas-backend / 17773047125

16 Sep 2025 04:47PM UTC coverage: 57.214% (+0.1%) from 57.118%
17773047125

Pull #245

github

emmdim
refactor(csp): Added MaxOverwritesPerProcess constant to define the limit of vote changes.
Change terminology from "consumed" to "used" for voting
Pull Request #245: refactor(csp): Added MaxVoteOverwritesPerProcess constant to define the limit of vote changes

18 of 22 new or added lines in 2 files covered. (81.82%)

3 existing lines in 2 files now uncovered.

5655 of 9884 relevant lines covered (57.21%)

31.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.95
/csp/sign.go
1
package csp
2

3
import (
4
        "crypto/sha256"
5
        "errors"
6
        "fmt"
7

8
        "github.com/vocdoni/saas-backend/csp/signers"
9
        "github.com/vocdoni/saas-backend/csp/signers/saltedkey"
10
        "github.com/vocdoni/saas-backend/db"
11
        "github.com/vocdoni/saas-backend/internal"
12
        "go.vocdoni.io/dvote/log"
13
        "go.vocdoni.io/proto/build/go/models"
14
        "google.golang.org/protobuf/proto"
15
)
16

17
// Sign method signs a message with the given token, address and processID. It
18
// returns the signature as HexBytes or an error if the signer type is invalid
19
// or the signature fails.
20
func (c *CSP) Sign(token, address, processID internal.HexBytes, signType signers.SignerType) (internal.HexBytes, error) {
7✔
21
        switch signType {
7✔
22
        case signers.SignerTypeECDSASalted:
6✔
23
                userID, salt, message, err := c.prepareSaltedKeySigner(token, address, processID)
6✔
24
                defer c.unlock(userID, processID)
6✔
25
                if err != nil {
6✔
UNCOV
26
                        return nil, err
×
UNCOV
27
                }
×
28
                signature, err := c.Signer.SignECDSA(*salt, message)
6✔
29
                if err != nil {
6✔
30
                        return nil, errors.Join(ErrSign, err)
×
31
                }
×
32
                if err := c.finishSaltedKeySigner(token, address, processID); err != nil {
7✔
33
                        return nil, err
1✔
34
                }
1✔
35
                return signature, nil
5✔
36
        default:
1✔
37
                return nil, ErrInvalidSignerType
1✔
38
        }
39
}
40

41
// prepareSaltedKeySigner method prepares the data for the Ethereum signer.
42
// It ensures the following conditions:
43
// - The auth token is valid and it is already verified.
44
// - The user belongs to the bundle.
45
// - The user belongs to the process.
46
// - The process has not been consumed yet.
47
// Then generates a bundle CA and encodes it to be signed. It returns userID,
48
// the salt as nil and the encoded CA as a message to sign.
49
//
50
//revive:disable:function-result-limit
51
func (c *CSP) prepareSaltedKeySigner(token, address, processID internal.HexBytes) (
52
        internal.HexBytes, *[saltedkey.SaltSize]byte, internal.HexBytes, error,
53
) {
13✔
54
        // get the data of the auth token and the user from the storage
13✔
55
        authTokenData, err := c.Storage.CSPAuth(token)
13✔
56
        if err != nil {
14✔
57
                return nil, nil, nil, ErrInvalidAuthToken
1✔
58
        }
1✔
59
        // check if the user is already signing
60
        if c.isLocked(authTokenData.UserID, processID) {
13✔
61
                return nil, nil, nil, ErrUserAlreadySigning
1✔
62
        }
1✔
63
        // check if the process is already consumed for this user
64
        if consumed, err := c.Storage.IsCSPProcessConsumed(authTokenData.UserID, processID); err != nil {
12✔
65
                log.Warn(err)
1✔
66
                switch err {
1✔
67
                case db.ErrTokenNotVerified:
1✔
68
                        return nil, nil, nil, ErrAuthTokenNotVerified
1✔
69
                default:
×
70
                        return nil, nil, nil, ErrStorageFailure
×
71
                }
72
        } else if consumed {
11✔
73
                return nil, nil, nil, ErrProcessAlreadyConsumed
1✔
74
        }
1✔
75
        // lock the user data to avoid concurrent signing
76
        c.lock(authTokenData.UserID, processID)
9✔
77
        // ensure that the auth token has been verified
9✔
78
        if !authTokenData.Verified {
9✔
79
                return nil, nil, nil, ErrAuthTokenNotVerified
×
80
        }
×
81
        // prepare the data for the signature
82
        caBundle := &models.CAbundle{
9✔
83
                ProcessId: processID,
9✔
84
                Address:   address,
9✔
85
        }
9✔
86
        // encode the data to sign
9✔
87
        signatureMsg, err := proto.Marshal(caBundle)
9✔
88
        if err != nil {
9✔
89
                return nil, nil, nil, ErrPrepareSignature
×
90
        }
×
91
        // generate the salt
92
        salt := [saltedkey.SaltSize]byte{}
9✔
93
        if len(processID) < saltedkey.SaltSize {
10✔
94
                return nil, nil, nil, ErrInvalidSalt
1✔
95
        }
1✔
96
        copy(salt[:], processID[:saltedkey.SaltSize])
8✔
97
        return authTokenData.UserID, &salt, signatureMsg, nil
8✔
98
}
99

100
func (c *CSP) finishSaltedKeySigner(token, address, processID internal.HexBytes) error {
10✔
101
        // get the data of the auth token and the user from the storage
10✔
102
        authTokenData, err := c.Storage.CSPAuth(token)
10✔
103
        if err != nil {
11✔
104
                return ErrInvalidAuthToken
1✔
105
        }
1✔
106
        // ensure that the auth token has been verified
107
        if !authTokenData.Verified {
10✔
108
                return ErrAuthTokenNotVerified
1✔
109
        }
1✔
110
        if !c.isLocked(authTokenData.UserID, processID) {
9✔
111
                return ErrUserIsNotAlreadySigning
1✔
112
        }
1✔
113
        // check if the process is already consumed for this user
114
        if consumed, err := c.Storage.IsCSPProcessConsumed(authTokenData.UserID, processID); err != nil {
7✔
115
                fmt.Println(err)
×
116
                return ErrStorageFailure
×
117
        } else if consumed {
7✔
118
                return ErrProcessAlreadyConsumed
×
119
        }
×
120
        // update the process data to mark it as consumed, and set the token used
121
        if err := c.Storage.ConsumeCSPProcess(token, processID, address); err != nil {
8✔
122
                log.Warn(err)
1✔
123
                return ErrStorageFailure
1✔
124
        }
1✔
125
        return nil
6✔
126
}
127

128
func (c *CSP) lock(userID, processID internal.HexBytes) {
10✔
129
        id := sha256.Sum256(append(userID, processID...))
10✔
130
        c.signerLock.Store(id, struct{}{})
10✔
131
}
10✔
132

133
func (c *CSP) isLocked(userID, processID internal.HexBytes) bool {
22✔
134
        id := sha256.Sum256(append(userID, processID...))
22✔
135
        _, ok := c.signerLock.Load(id)
22✔
136
        return ok
22✔
137
}
22✔
138

139
func (c *CSP) unlock(userID, processID internal.HexBytes) {
14✔
140
        id := sha256.Sum256(append(userID, processID...))
14✔
141
        c.signerLock.Delete(id)
14✔
142
}
14✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc