Skip to content
Snippets Groups Projects
identity.go 3.14 KiB
package identity

import (
	"crypto/ed25519"
	"crypto/sha256"
	"encoding/hex"

	"github.com/iotaledger/autopeering-sim/peer"
)

type Identity struct {
	Identifier       peer.ID
	StringIdentifier string
	PublicKey        ed25519.PublicKey
	privateKey       ed25519.PrivateKey
}

// Creates a new identity based on the given public key.
func NewPublicIdentity(publicKey ed25519.PublicKey) *Identity {
	identifier := sha256.Sum256(publicKey)

	return &Identity{
		Identifier:       identifier,
		StringIdentifier: hex.EncodeToString(identifier[:8]),
		PublicKey:        publicKey,
		privateKey:       nil,
	}
}

// Generates a identity based on a newly generated public/private key pair.
// It will panic if no such pair could be generated.
func GeneratePrivateIdentity() *Identity {
	publicKey, privateKey, err := ed25519.GenerateKey(nil)
	if err != nil {
		panic("identity: failed generating key: " + err.Error())
	}

	return newPrivateIdentity(publicKey, privateKey)
}

// Sign signs the message with privateKey and returns the message plus the signature.
func (id *Identity) AddSignature(msg []byte) []byte {
	signatureStart := len(msg)

	signature := ed25519.Sign(id.privateKey, msg)

	data := make([]byte, signatureStart+SIGNATURE_BYTE_LENGTH)

	copy(data[:signatureStart], msg)

	// add public key and signature
	copy(data[signatureStart:signatureStart+ed25519.PublicKeySize], id.PublicKey)
	copy(data[signatureStart+ed25519.PublicKeySize:], signature)

	return data
}

// Verifies whether the data contains a valid signature of the message.
func (id *Identity) VerifySignature(data []byte) error {
	signatureStart := len(data) - SIGNATURE_BYTE_LENGTH
	if signatureStart <= 0 {
		return ErrInvalidDataLen
	}

	msg := data[:signatureStart]

	// ignore the public key
	sig := data[signatureStart+ed25519.PublicKeySize:]

	if !ed25519.Verify(id.PublicKey, msg, sig) {
		return ErrInvalidSignature
	}

	return nil
}

// Returns the identitiy derived from the signed message.
func FromSignedData(data []byte) (*Identity, error) {
	signatureStart := len(data) - SIGNATURE_BYTE_LENGTH
	if signatureStart <= 0 {
		return nil, ErrInvalidDataLen
	}

	pubKey := data[signatureStart : signatureStart+ed25519.PublicKeySize]

	identity := NewPublicIdentity(pubKey)
	if err := identity.VerifySignature(data); err != nil {
		return nil, err
	}

	return identity, nil
}

func (id *Identity) Marshal() []byte {
	data := make([]byte, MARSHALED_IDENTITY_TOTAL_SIZE)

	copy(data[MARSHALED_IDENTITY_PUBLIC_KEY_START:MARSHALED_IDENTITY_PUBLIC_KEY_END], id.PublicKey)
	copy(data[MARSHALED_IDENTITY_PRIVATE_KEY_START:MARSHALED_IDENTITY_PRIVATE_KEY_END], id.privateKey)

	return data
}

func Unmarshal(data []byte) (*Identity, error) {
	if len(data) != MARSHALED_IDENTITY_TOTAL_SIZE {
		return nil, ErrInvalidDataLen
	}

	publicKey := data[MARSHALED_IDENTITY_PUBLIC_KEY_START:MARSHALED_IDENTITY_PUBLIC_KEY_END]
	privateKey := data[MARSHALED_IDENTITY_PRIVATE_KEY_START:MARSHALED_IDENTITY_PRIVATE_KEY_END]

	return newPrivateIdentity(publicKey, privateKey), nil
}

func newPrivateIdentity(publicKey []byte, privateKey []byte) *Identity {

	identity := NewPublicIdentity(publicKey)
	identity.privateKey = privateKey

	return identity
}