first commit
This commit is contained in:
commit
a6c329054e
5
go.mod
Normal file
5
go.mod
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module git.purser.it/roypur/stoken
|
||||||
|
|
||||||
|
go 1.17
|
||||||
|
|
||||||
|
require github.com/ugorji/go/codec v1.2.6
|
4
go.sum
Normal file
4
go.sum
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E=
|
||||||
|
github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0=
|
||||||
|
github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
|
||||||
|
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
|
170
sign.go
Normal file
170
sign.go
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
package stoken
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/ed25519"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/ugorji/go/codec"
|
||||||
|
"hash/crc64"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
|
||||||
|
|
||||||
|
var b64 = base64.NewEncoding(alphabet).WithPadding(base64.NoPadding)
|
||||||
|
|
||||||
|
type linkData struct {
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTokenCoder(seed []byte, pubKeys ...[]byte) (tc TokenCoder, err error) {
|
||||||
|
tc.pubKeys = make([]ed25519.PublicKey, len(pubKeys)+1, len(pubKeys)+1)
|
||||||
|
if len(seed) == 0 {
|
||||||
|
seed = make([]byte, ed25519.SeedSize, ed25519.SeedSize)
|
||||||
|
_, err = rand.Read(seed)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(seed) != ed25519.SeedSize {
|
||||||
|
err = errors.New("Incorrect seed size")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTokenCoderHex(seed string, pubKeys ...string) (tc TokenCoder, err error) {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NewTokenCoder(rawSeed, rawPubKeys...)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
var ld linkData
|
||||||
|
ld.Signature = ed25519.Sign(tc.privKey, buf.Bytes())
|
||||||
|
ld.Payload = buf.Bytes()
|
||||||
|
buf.Reset()
|
||||||
|
enc.Encode(ld)
|
||||||
|
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)
|
||||||
|
|
||||||
|
var ld linkData
|
||||||
|
err = dec.Decode(&ld)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ok := false
|
||||||
|
for _, key := range tc.pubKeys {
|
||||||
|
if ed25519.Verify(key, ld.Payload, ld.Signature) {
|
||||||
|
ok = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
err = errors.New("Signature validation failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.Reset()
|
||||||
|
buf.Write(ld.Payload)
|
||||||
|
|
||||||
|
hash := crc64.New(crc64.MakeTable(crc64.ISO))
|
||||||
|
hash.Write(ld.Payload)
|
||||||
|
|
||||||
|
sum = hex.EncodeToString(hash.Sum(nil))
|
||||||
|
err = dec.Decode(&payload)
|
||||||
|
return
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user