ampelmaennchen.git

ref: master

accounts/subscriptions.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
package accounts

import (
	"bytes"
	"crypto"
	"crypto/ed25519"
	"encoding/base64"
	"errors"
	"fmt"
	"time"

	"git.sr.ht/~sircmpwn/go-bare"
	"github.com/google/uuid"
)

type Seat string

func ParseSeat(s string) (Seat, error) {
	switch s {
	case string(SEAT_BACK):
		return SEAT_BACK, nil
	case string(SEAT_WINDOW):
		return SEAT_WINDOW, nil
	case string(SEAT_FRONT):
		return SEAT_FRONT, nil
	case string(SEAT_DRIVER):
		return SEAT_DRIVER, nil
	case string(SEAT_PILOT):
		return SEAT_PILOT, nil
	case string(SEAT_GARAGE):
		return SEAT_GARAGE, nil
	default:
		return SEAT_BACK, errors.New("No such seat: " + s)
	}
}

func (s Seat) String() string {
	return string(s)
}

const (
	SEAT_BACK   Seat = "back"
	SEAT_WINDOW Seat = "window"
	SEAT_FRONT  Seat = "front"
	SEAT_DRIVER Seat = "driver"
	SEAT_PILOT  Seat = "pilot"
	SEAT_GARAGE Seat = "garage"
)

type Subscription struct {
	ID         string
	Plan       Seat
	ValidSince time.Time
	Validity   int    // months
	Creator    string // user ID
	Signature  []byte
	// TODO payment ID
}

type SubscriptionTicket struct {
	ID        string
	Plan      string
	Validity  int    // months
	Creator   string // user ID
	Signature []byte
}

func (s SubscriptionTicket) Marshal() ([]byte, error) {
	bytes, err := bare.Marshal(&s)
	return bytes, err
}

func ParseSubscription(s string) (SubscriptionTicket, error) {
	t := SubscriptionTicket{}
	bytes, err := base64.StdEncoding.DecodeString(s)
	if err != nil {
		return SubscriptionTicket{}, fmt.Errorf("while decoding base64: %w", err)
	}
	err = bare.Unmarshal(bytes, &t)
	if err != nil {
		return t, fmt.Errorf("while unmarshalling: %w", err)
	}
	return t, nil
}

func CreateSubscription(plan Seat, validity int, keySeed []byte) error {
	subscription := SubscriptionTicket{
		ID:       uuid.NewString(),
		Plan:     plan.String(),
		Validity: validity,
		Creator:  "cli",
	}

	signed, err := subscription.Marshal()
	if err != nil {
		return fmt.Errorf("while marshalling signed subscription: %w", err)
	}

	fmt.Println(base64.StdEncoding.EncodeToString(signed))

	return nil
}

func sign(subscription SubscriptionTicket, keySeed []byte) (SubscriptionTicket, error) {
	privateKey := ed25519.NewKeyFromSeed(keySeed)

	unsigned, err := subscription.Marshal()
	if err != nil {
		return subscription, fmt.Errorf("while marshalling unsigned subscription: %w", err)
	}

	signature, err := privateKey.Sign(nil, unsigned, crypto.Hash(0))
	if err != nil {
		return subscription, fmt.Errorf("while signing subscription: %w", err)
	}
	subscription.Signature = signature

	return subscription, nil
}

func CheckSignature(s SubscriptionTicket, keySeed []byte) (bool, error) {
	signature := s.Signature
	s.Signature = []byte{}
	signed, err := sign(s, keySeed)
	if err != nil {
		return false, err
	}
	return bytes.Compare(signature, signed.Signature) == 0, nil
}