package data import ( "time" "database/sql" "encoding/pem" "context" "errors" "fmt" "math/big" "crypto/rsa" "crypto/x509" ) type BlindSignRequest struct { UserID int64 `json:"user_id"` IssueID int64 `json:"issue_id"` Created time.Time `json:"created"` } type BlindSignRequestModel struct { DB *sql.DB } func (m BlindSignRequestModel) Insert(blind_sign *BlindSignRequest) error { query := ` INSERT INTO blind_sign_requests (user_id, issue_id) VALUES ($1, $2) RETURNING created` args := []interface{}{ blind_sign.UserID, blind_sign.IssueID, } return m.DB.QueryRow(query, args...).Scan( &blind_sign.Created, ) } func (m BlindSignRequestModel) BlindSign(issueID int64, blindedVoteBytes []byte) ([]byte, error) { if issueID < 1 { return nil, ErrRecordNotFound } query := `SELECT rsa_private_pem FROM issues WHERE id = $1` ctx, cancel := context.WithTimeout(context.Background(), 3 * time.Second) defer cancel() var pemBytes []byte err := m.DB.QueryRowContext(ctx, query, issueID).Scan(&pemBytes) if err != nil { switch { case errors.Is(err, sql.ErrNoRows): return nil, ErrRecordNotFound default: return nil, err } } key, err := parsePrivateKey(pemBytes) if err != nil { return nil, fmt.Errorf("parse private key: %w", err) } m_ := new(big.Int).SetBytes(blindedVoteBytes) // Validate range: m′ must be in [1, n-1] one := big.NewInt(1) if m_.Cmp(one) < 0 || m_.Cmp(key.N) >= 0 { return nil, ErrInvalidBlindedVote } // s′ = m′^d mod n sig := new(big.Int).Exp(m_, key.D, key.N) return sig.Bytes(), nil } func parsePrivateKey(pemBytes []byte) (*rsa.PrivateKey, error) { block, _ := pem.Decode(pemBytes) if block == nil { return nil, errors.New("failed to decode PEM block") } return x509.ParsePKCS1PrivateKey(block.Bytes) }