Initial release
This commit is contained in:
commit
2c6c508643
35
.editorconfig
Normal file
35
.editorconfig
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
|
||||||
|
end_of_line = LF
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.sh]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[{*.html,*.js,*.css,*.scss}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[Makefile]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 8
|
||||||
|
|
||||||
|
[{{*.,}[Dd]ockerfile{.*,},{*.,}[Cc]ontainerfile{.*,}}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.proto]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[{*.go,go.mod}]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 8
|
25
LICENSE
Normal file
25
LICENSE
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
BSD 2-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2024, some
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
106
authentication/session.go
Normal file
106
authentication/session.go
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
package authentication
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"somehole.com/common/security/identity"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
RetryCreateSessionToken = 3
|
||||||
|
MaxSessionAge = 1 * time.Hour
|
||||||
|
)
|
||||||
|
|
||||||
|
type SessionToken [8]byte
|
||||||
|
|
||||||
|
func NewSessionToken() (st SessionToken) {
|
||||||
|
var stBytes = make([]byte, 8)
|
||||||
|
rand.Read(stBytes)
|
||||||
|
st = SessionToken(stBytes)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type NextToken [4]byte
|
||||||
|
|
||||||
|
func NewNextToken() (nt NextToken) {
|
||||||
|
var ntBytes = make([]byte, 4)
|
||||||
|
rand.Read(ntBytes)
|
||||||
|
nt = NextToken(ntBytes)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type Session struct {
|
||||||
|
identity.Identity
|
||||||
|
nextTokens []NextToken
|
||||||
|
expiration time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSession(identity identity.Identity) (s *Session) {
|
||||||
|
s = &Session{
|
||||||
|
Identity: identity,
|
||||||
|
nextTokens: make([]NextToken, 0),
|
||||||
|
expiration: time.Now().Add(MaxSessionAge),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Session) Active() (ok bool) {
|
||||||
|
ok = time.Now().Before(s.expiration)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Session) NewNextToken() (nt NextToken) {
|
||||||
|
nt = NewNextToken()
|
||||||
|
s.nextTokens = append(s.nextTokens, nt)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Session) NextTokenIsCurrent(nt NextToken) (ok bool) {
|
||||||
|
ok = s.nextTokens[len(s.nextTokens)-1] == nt
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type SessionService struct {
|
||||||
|
sessions map[SessionToken]*Session
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSessionService() (srv *SessionService) {
|
||||||
|
srv = &SessionService{
|
||||||
|
sessions: make(map[SessionToken]*Session),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv *SessionService) NewSessionToken() (st SessionToken, err error) {
|
||||||
|
for i := 0; i < RetryCreateSessionToken; i++ {
|
||||||
|
st = NewSessionToken()
|
||||||
|
if _, exists := srv.sessions[st]; exists {
|
||||||
|
err = fmt.Errorf("could only creat colliding session tokens in %d tries", RetryCreateSessionToken)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err = nil
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv *SessionService) AddSession(st SessionToken, session *Session) (err error) {
|
||||||
|
if _, exists := srv.sessions[st]; exists {
|
||||||
|
err = fmt.Errorf("session already exists")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
srv.sessions[st] = session
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv *SessionService) GetSession(sessionToken SessionToken) (session *Session, err error) {
|
||||||
|
var exists bool
|
||||||
|
session, exists = srv.sessions[sessionToken]
|
||||||
|
if !exists {
|
||||||
|
err = fmt.Errorf("session not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
22
authorization/audit.go
Normal file
22
authorization/audit.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package authorization
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"somehole.com/common/security/identity"
|
||||||
|
)
|
||||||
|
|
||||||
|
type record struct {
|
||||||
|
Verb
|
||||||
|
Noun
|
||||||
|
time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuditService struct {
|
||||||
|
records map[identity.Id][]record
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAuditService() (srv *AuditService) {
|
||||||
|
srv = &AuditService{}
|
||||||
|
return
|
||||||
|
}
|
43
authorization/authorization.go
Normal file
43
authorization/authorization.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package authorization
|
||||||
|
|
||||||
|
import "somehole.com/common/security/identity"
|
||||||
|
|
||||||
|
type Verb int
|
||||||
|
|
||||||
|
const (
|
||||||
|
_ Verb = iota
|
||||||
|
Create
|
||||||
|
Read
|
||||||
|
Watch
|
||||||
|
Update
|
||||||
|
Patch
|
||||||
|
Delete
|
||||||
|
)
|
||||||
|
|
||||||
|
type Noun int
|
||||||
|
|
||||||
|
const (
|
||||||
|
_ Noun = iota
|
||||||
|
Identity
|
||||||
|
Command
|
||||||
|
Log
|
||||||
|
)
|
||||||
|
|
||||||
|
type AuthorizationService struct {
|
||||||
|
*AuditService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAuthorizationService(auditService *AuditService) (srv *AuthorizationService) {
|
||||||
|
if auditService == nil {
|
||||||
|
auditService = NewAuditService()
|
||||||
|
}
|
||||||
|
srv = &AuthorizationService{
|
||||||
|
AuditService: auditService,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv *AuthorizationService) Authorized(identity identity.Identity, verb Verb, noun Noun) (authorized bool, err error) {
|
||||||
|
authorized = true
|
||||||
|
return
|
||||||
|
}
|
10
go.mod
Normal file
10
go.mod
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
module somehole.com/common/security
|
||||||
|
|
||||||
|
go 1.23.1
|
||||||
|
|
||||||
|
require github.com/cloudflare/circl v1.4.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
golang.org/x/crypto v0.11.1-0.20230711161743-2e82bdd1719d // indirect
|
||||||
|
golang.org/x/sys v0.10.0 // indirect
|
||||||
|
)
|
6
go.sum
Normal file
6
go.sum
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
github.com/cloudflare/circl v1.4.0 h1:BV7h5MgrktNzytKmWjpOtdYrf0lkkbF8YMlBGPhJQrY=
|
||||||
|
github.com/cloudflare/circl v1.4.0/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
|
||||||
|
golang.org/x/crypto v0.11.1-0.20230711161743-2e82bdd1719d h1:LiA25/KWKuXfIq5pMIBq1s5hz3HQxhJJSu/SUGlD+SM=
|
||||||
|
golang.org/x/crypto v0.11.1-0.20230711161743-2e82bdd1719d/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||||
|
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
||||||
|
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
120
identity/identity.go
Normal file
120
identity/identity.go
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
package identity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"somehole.com/common/security/signature"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
RetryCreateId = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
type identity interface {
|
||||||
|
GetId() []byte
|
||||||
|
GetPublicKey() []byte
|
||||||
|
GetPublicKeySignature() []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type Id [8]byte
|
||||||
|
|
||||||
|
func (i Id) String() (out string) {
|
||||||
|
out = base64.StdEncoding.EncodeToString(i[:])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type Identity struct {
|
||||||
|
Id Id
|
||||||
|
PublicKey signature.PublicKey
|
||||||
|
PublicKeySignature signature.Signature
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIdentity(ident identity) (i Identity, err error) {
|
||||||
|
id := ident.GetId()
|
||||||
|
if len(id) != 8 {
|
||||||
|
err = errors.New("wrong size id")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pubKey := ident.GetPublicKey()
|
||||||
|
if len(pubKey) != 96 {
|
||||||
|
err = errors.New("wrong size public key")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pubKeySig := ident.GetPublicKeySignature()
|
||||||
|
if len(pubKeySig) != 48 {
|
||||||
|
err = errors.New("wrong size public key signature")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
i = Identity{
|
||||||
|
Id: Id(id),
|
||||||
|
PublicKey: signature.PublicKey(pubKey),
|
||||||
|
PublicKeySignature: signature.Signature(pubKeySig),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i Identity) String() (out string) {
|
||||||
|
b := make([]byte, 0)
|
||||||
|
b = append(b, i.Id[:]...)
|
||||||
|
b = append(b, i.PublicKey[:]...)
|
||||||
|
b = append(b, i.PublicKeySignature[:]...)
|
||||||
|
out = base64.StdEncoding.EncodeToString(b)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseIdentityString(in string) (i Identity, err error) {
|
||||||
|
var b []byte
|
||||||
|
b, err = base64.StdEncoding.DecodeString(in)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(b) != 8+96+48 {
|
||||||
|
err = errors.New("wrong for size identity data in string")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
i = Identity{
|
||||||
|
Id: Id(b[0:8]),
|
||||||
|
PublicKey: signature.PublicKey(b[8:104]),
|
||||||
|
PublicKeySignature: signature.Signature(b[104:152]),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type IdentityService struct {
|
||||||
|
identities map[Id]Identity
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIdentityService() (srv *IdentityService) {
|
||||||
|
srv = &IdentityService{
|
||||||
|
identities: make(map[Id]Identity),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv *IdentityService) NewId() (id Id, err error) {
|
||||||
|
var idBytes = make([]byte, 8)
|
||||||
|
for i := 0; i < RetryCreateId; i++ {
|
||||||
|
rand.Read(idBytes)
|
||||||
|
if _, exists := srv.identities[id]; exists {
|
||||||
|
err = fmt.Errorf("could only creat colliding ids in %d tries", RetryCreateId)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
id = Id(idBytes)
|
||||||
|
err = nil
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv *IdentityService) AddIdentity(id Id, identity Identity) (err error) {
|
||||||
|
_, exists := srv.identities[id]
|
||||||
|
if exists {
|
||||||
|
err = fmt.Errorf("failed to add identity")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
srv.identities[id] = identity
|
||||||
|
return
|
||||||
|
}
|
45
security.go
Normal file
45
security.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package security
|
||||||
|
|
||||||
|
import (
|
||||||
|
"somehole.com/common/security/authentication"
|
||||||
|
"somehole.com/common/security/authorization"
|
||||||
|
"somehole.com/common/security/identity"
|
||||||
|
"somehole.com/common/security/signature"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Signer signature.Keypair
|
||||||
|
}
|
||||||
|
|
||||||
|
type SecurityService struct {
|
||||||
|
config Config
|
||||||
|
*authentication.SessionService
|
||||||
|
*authorization.AuditService
|
||||||
|
*authorization.AuthorizationService
|
||||||
|
*identity.IdentityService
|
||||||
|
*signature.SignatureService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSecurityService(config ...Config) (srv *SecurityService) {
|
||||||
|
cfg := Config{}
|
||||||
|
if len(config) == 1 {
|
||||||
|
cfg = config[0]
|
||||||
|
}
|
||||||
|
signatureService, err := signature.NewSignatureService(&cfg.Signer)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
identityService := identity.NewIdentityService()
|
||||||
|
sessionService := authentication.NewSessionService()
|
||||||
|
auditService := authorization.NewAuditService()
|
||||||
|
authorizationService := authorization.NewAuthorizationService(auditService)
|
||||||
|
srv = &SecurityService{
|
||||||
|
config: cfg,
|
||||||
|
SessionService: sessionService,
|
||||||
|
AuditService: auditService,
|
||||||
|
AuthorizationService: authorizationService,
|
||||||
|
IdentityService: identityService,
|
||||||
|
SignatureService: signatureService,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
149
signature/signature.go
Normal file
149
signature/signature.go
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
package signature
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/cloudflare/circl/sign/bls"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PrivateKey [32]byte
|
||||||
|
|
||||||
|
func (pk PrivateKey) Bls() (privKey *bls.PrivateKey[bls.KeyG2SigG1], err error) {
|
||||||
|
privKey = &bls.PrivateKey[bls.KeyG2SigG1]{}
|
||||||
|
err = privKey.UnmarshalBinary(pk[:])
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pk PrivateKey) Sign(msg []byte) (sig Signature, err error) {
|
||||||
|
privKey, err := pk.Bls()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sig = Signature(bls.Sign(privKey, msg))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pk PrivateKey) SignKey(key PublicKey) (sig Signature, err error) {
|
||||||
|
pubKey := &bls.PublicKey[bls.KeyG2SigG1]{}
|
||||||
|
err = pubKey.UnmarshalBinary(key[:])
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return pk.Sign(key[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pk PrivateKey) String() (out string) {
|
||||||
|
out = base64.StdEncoding.EncodeToString(pk[:])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type PublicKey [96]byte
|
||||||
|
|
||||||
|
func (pk PublicKey) Bls() (pubKey *bls.PublicKey[bls.KeyG2SigG1], err error) {
|
||||||
|
pubKey = &bls.PublicKey[bls.KeyG2SigG1]{}
|
||||||
|
err = pubKey.UnmarshalBinary(pk[:])
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pk PublicKey) Verify(msg []byte, sig Signature) (ok bool, err error) {
|
||||||
|
pubKey, err := pk.Bls()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ok = bls.Verify(pubKey, msg, sig[:])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pk PublicKey) String() (out string) {
|
||||||
|
out = base64.StdEncoding.EncodeToString(pk[:])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type Signature [48]byte
|
||||||
|
|
||||||
|
type Keypair struct {
|
||||||
|
PrivateKey
|
||||||
|
PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKeypair() (kp *Keypair, err error) {
|
||||||
|
var (
|
||||||
|
ikm = make([]byte, 64)
|
||||||
|
salt = make([]byte, 32)
|
||||||
|
privKey *bls.PrivateKey[bls.KeyG2SigG1]
|
||||||
|
pubKey *bls.PublicKey[bls.KeyG2SigG1]
|
||||||
|
privKeyBytes = make([]byte, 32)
|
||||||
|
pubKeyBytes = make([]byte, 96)
|
||||||
|
)
|
||||||
|
_, err = rand.Read(ikm)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = rand.Read(salt)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
privKey, err = bls.KeyGen[bls.KeyG2SigG1](ikm, salt, []byte(""))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
privKeyBytes, err = privKey.MarshalBinary()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pubKey = privKey.PublicKey()
|
||||||
|
pubKeyBytes, err = pubKey.MarshalBinary()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
kp = &Keypair{
|
||||||
|
PrivateKey: PrivateKey(privKeyBytes),
|
||||||
|
PublicKey: PublicKey(pubKeyBytes),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kp *Keypair) validate() (ok bool, err error) {
|
||||||
|
var testMessage [128]byte
|
||||||
|
_, err = rand.Read(testMessage[:])
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sig, _ := kp.PrivateKey.Sign(testMessage[:])
|
||||||
|
ok, _ = kp.PublicKey.Verify(testMessage[:], sig)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type SignatureService struct {
|
||||||
|
*Keypair
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSignatureService(signer *Keypair) (srv *SignatureService, err error) {
|
||||||
|
if signer == nil {
|
||||||
|
var ok bool
|
||||||
|
signer, err = NewKeypair()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ok, err = signer.validate()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
err = errors.New("failed to validate new signer keypair")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
srv = &SignatureService{
|
||||||
|
Keypair: signer,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user