github.com/code-to-go/safepool.lib@v0.0.0-20221205180519-ee25e63c226e/security/signature.go (about)

     1  package security
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/ed25519"
     6  	"encoding/base64"
     7  
     8  	"github.com/code-to-go/safepool.lib/core"
     9  )
    10  
    11  type PublicKey ed25519.PublicKey
    12  type PrivateKey ed25519.PrivateKey
    13  
    14  const (
    15  	PublicKeySize  = ed25519.PublicKeySize
    16  	PrivateKeySize = ed25519.PrivateKeySize
    17  	SignatureSize  = ed25519.SignatureSize
    18  )
    19  
    20  type SignedData struct {
    21  	Signature [SignatureSize]byte
    22  	Signer    PublicKey
    23  }
    24  
    25  type Public struct {
    26  	Id    PublicKey
    27  	Nick  string
    28  	Email string
    29  }
    30  
    31  func Sign(identity Identity, data []byte) ([]byte, error) {
    32  	private := identity.SignatureKey.Private
    33  	return ed25519.Sign(ed25519.PrivateKey(private), data), nil
    34  }
    35  
    36  func Verify(id string, data []byte, sig []byte) bool {
    37  	public, err := base64.StdEncoding.DecodeString(id)
    38  	if core.IsErr(err, "invalid id '%s': %v", id) {
    39  		return false
    40  	}
    41  
    42  	for off := 0; off < len(sig); off += SignatureSize {
    43  		if func() bool {
    44  			defer func() { recover() }()
    45  			return ed25519.Verify(ed25519.PublicKey(public), data, sig[off:off+SignatureSize])
    46  		}() {
    47  			return true
    48  		}
    49  	}
    50  	return false
    51  }
    52  
    53  type SignedHashEvidence struct {
    54  	Key       []byte `json:"k"`
    55  	Signature []byte `json:"s"`
    56  }
    57  
    58  type SignedHash struct {
    59  	Hash      []byte               `json:"h"`
    60  	Evidences []SignedHashEvidence `json:"e"`
    61  }
    62  
    63  func NewSignedHash(hash []byte, i Identity) (SignedHash, error) {
    64  	signature, err := Sign(i, hash)
    65  	if core.IsErr(err, "cannot sign with identity %s: %v", base64.StdEncoding.EncodeToString(i.SignatureKey.Public)) {
    66  		return SignedHash{}, err
    67  	}
    68  
    69  	return SignedHash{
    70  		Hash: hash,
    71  		Evidences: []SignedHashEvidence{
    72  			{
    73  				Key:       i.SignatureKey.Public,
    74  				Signature: signature,
    75  			},
    76  		},
    77  	}, nil
    78  }
    79  
    80  func AppendToSignedHash(s SignedHash, i Identity) error {
    81  	signature, err := Sign(i, s.Hash)
    82  	if core.IsErr(err, "cannot sign with identity %s: %v", base64.StdEncoding.EncodeToString(i.SignatureKey.Public)) {
    83  		return err
    84  	}
    85  	s.Evidences = append(s.Evidences, SignedHashEvidence{
    86  		Key:       i.SignatureKey.Public,
    87  		Signature: signature,
    88  	})
    89  	return nil
    90  }
    91  
    92  func VerifySignedHash(s SignedHash, trusts []Identity, hash []byte) bool {
    93  	if !bytes.Equal(s.Hash, hash) {
    94  		return false
    95  	}
    96  
    97  	for _, e := range s.Evidences {
    98  		for _, t := range trusts {
    99  			if bytes.Equal(e.Key, t.SignatureKey.Public) {
   100  				if Verify(t.Id(), hash, e.Signature) {
   101  					return true
   102  				}
   103  			}
   104  		}
   105  	}
   106  	return false
   107  }