stoken/sign.go

220 lines
4.4 KiB
Go
Raw Normal View History

2022-02-20 19:01:20 +00:00
package stoken
import (
"bytes"
"crypto/ed25519"
"crypto/rand"
"encoding/base64"
"encoding/hex"
"errors"
"github.com/ugorji/go/codec"
"hash/crc64"
"strings"
)
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
var b64 = base64.NewEncoding(alphabet).WithPadding(base64.NoPadding)
2022-04-03 14:14:07 +00:00
type tokenData struct {
2022-02-20 19:01:20 +00:00
Signature []byte `codec:"s"`
Payload []byte `codec:"p"`
}
type TokenCoder struct {
valid bool
privKey ed25519.PrivateKey
pubKeys []ed25519.PublicKey
}
func (tc TokenCoder) PublicKey() ed25519.PublicKey {
if tc.valid {
pos := len(tc.pubKeys) - 1
return tc.pubKeys[pos]
}
return nil
}
func (tc TokenCoder) Seed() []byte {
if tc.valid {
return tc.privKey.Seed()
}
return nil
}
func (tc TokenCoder) PublicKeyHex() string {
return hex.EncodeToString(tc.PublicKey())
}
func (tc TokenCoder) SeedHex() string {
return hex.EncodeToString(tc.Seed())
}
2022-04-03 14:14:07 +00:00
func Format(token string) (txt string, err error) {
2022-04-03 14:27:14 +00:00
var payload interface{}
2022-04-03 14:14:07 +00:00
var data []byte
data, err = b64.DecodeString(strings.TrimFunc(token, trim))
if err != nil {
return
}
buf := bytes.NewBuffer(nil)
2022-04-03 14:52:26 +00:00
var cHandle codec.CborHandle
var jHandle codec.JsonHandle
jHandle.HTMLCharsAsIs = true
jHandle.MapKeyAsString = true
jHandle.Indent = 4
2022-04-03 14:14:07 +00:00
buf.Write(data)
2022-04-03 14:52:26 +00:00
dec := codec.NewDecoder(buf, &cHandle)
2022-04-03 14:14:07 +00:00
var td tokenData
err = dec.Decode(&td)
if err != nil {
return
}
buf.Reset()
buf.Write(td.Payload)
err = dec.Decode(&payload)
buf.Reset()
buf.Write([]byte("SIGNATURE=" + hex.EncodeToString(td.Signature) + "\n"))
2022-04-03 14:52:26 +00:00
enc := codec.NewEncoder(buf, &jHandle)
2022-04-03 14:14:07 +00:00
err = enc.Encode(payload)
txt = buf.String()
return
}
2022-02-27 19:21:58 +00:00
func NewTokenCoderWithSeed(seed []byte, pubKeys ...[]byte) (tc TokenCoder, err error) {
2022-02-20 19:01:20 +00:00
if len(seed) != ed25519.SeedSize {
err = errors.New("Incorrect seed size")
return
}
2022-02-27 19:21:58 +00:00
tc.pubKeys = make([]ed25519.PublicKey, len(pubKeys)+1, len(pubKeys)+1)
2022-02-20 19:01:20 +00:00
tc.privKey = ed25519.NewKeyFromSeed(seed)
tc.pubKeys[len(pubKeys)] = tc.privKey.Public().(ed25519.PublicKey)
for pos, key := range pubKeys {
if len(key) != ed25519.PublicKeySize {
err = errors.New("Incorrect public key size")
return
}
tc.pubKeys[pos] = ed25519.PublicKey(key)
}
tc.valid = true
return
}
2022-02-27 19:21:58 +00:00
func NewTokenCoder(pubKeys ...[]byte) (tc TokenCoder, err error) {
seed := make([]byte, ed25519.SeedSize, ed25519.SeedSize)
_, err = rand.Read(seed)
if err != nil {
return
}
return NewTokenCoderWithSeed(seed, pubKeys...)
}
func NewTokenCoderHexWithSeed(seed string, pubKeys ...string) (tc TokenCoder, err error) {
2022-02-20 19:01:20 +00:00
var rawSeed []byte
rawPubKeys := make([][]byte, len(pubKeys), len(pubKeys))
rawSeed, err = hex.DecodeString(seed)
if err != nil {
return
}
for pos, pubKey := range pubKeys {
rawPubKeys[pos], err = hex.DecodeString(pubKey)
if err != nil {
return
}
}
2022-02-27 19:21:58 +00:00
return NewTokenCoderWithSeed(rawSeed, rawPubKeys...)
}
func NewTokenCoderHex(pubKeys ...string) (tc TokenCoder, err error) {
rawPubKeys := make([][]byte, len(pubKeys), len(pubKeys))
for pos, pubKey := range pubKeys {
rawPubKeys[pos], err = hex.DecodeString(pubKey)
if err != nil {
return
}
}
return NewTokenCoder(rawPubKeys...)
2022-02-20 19:01:20 +00:00
}
func trim(r rune) bool {
return !strings.ContainsRune(alphabet, r)
}
func (tc TokenCoder) Encode(payload interface{}) (token string, err error) {
if !tc.valid {
err = errors.New("Invalid TokenCoder")
return
}
buf := bytes.NewBuffer(nil)
var handle codec.CborHandle
enc := codec.NewEncoder(buf, &handle)
enc.Encode(payload)
2022-04-03 14:14:07 +00:00
var td tokenData
td.Signature = ed25519.Sign(tc.privKey, buf.Bytes())
td.Payload = buf.Bytes()
2022-02-20 19:01:20 +00:00
buf.Reset()
2022-04-03 14:14:07 +00:00
enc.Encode(td)
2022-02-20 19:01:20 +00:00
token = b64.EncodeToString(buf.Bytes())
return
}
func (tc TokenCoder) Decode(token string, payload interface{}) (sum string, err error) {
if !tc.valid {
err = errors.New("Invalid TokenCoder")
return
}
var data []byte
data, err = b64.DecodeString(strings.TrimFunc(token, trim))
if err != nil {
return
}
buf := bytes.NewBuffer(nil)
var handle codec.CborHandle
buf.Write(data)
dec := codec.NewDecoder(buf, &handle)
2022-04-03 14:14:07 +00:00
var td tokenData
err = dec.Decode(&td)
2022-02-20 19:01:20 +00:00
if err != nil {
return
}
ok := false
for _, key := range tc.pubKeys {
2022-04-03 14:14:07 +00:00
if ed25519.Verify(key, td.Payload, td.Signature) {
2022-02-20 19:01:20 +00:00
ok = true
break
}
}
if !ok {
err = errors.New("Signature validation failed")
return
}
buf.Reset()
2022-04-03 14:14:07 +00:00
buf.Write(td.Payload)
2022-02-20 19:01:20 +00:00
hash := crc64.New(crc64.MakeTable(crc64.ISO))
2022-04-03 14:14:07 +00:00
hash.Write(td.Payload)
2022-02-20 19:01:20 +00:00
sum = hex.EncodeToString(hash.Sum(nil))
err = dec.Decode(&payload)
return
}