package pubsub

import (
	

	pb 

	
	
)

// MessageSignaturePolicy describes if signatures are produced, expected, and/or verified.
type MessageSignaturePolicy uint8

// LaxSign and LaxNoSign are deprecated. In the future msgSigning and msgVerification can be unified.
const (
	// msgSigning is set when the locally produced messages must be signed
	msgSigning MessageSignaturePolicy = 1 << iota
	// msgVerification is set when external messages must be verfied
	msgVerification
)

const (
	// StrictSign produces signatures and expects and verifies incoming signatures
	StrictSign = msgSigning | msgVerification
	// StrictNoSign does not produce signatures and drops and penalises incoming messages that carry one
	StrictNoSign = msgVerification
	// LaxSign produces signatures and validates incoming signatures iff one is present
	// Deprecated: it is recommend to either strictly enable, or strictly disable, signatures.
	LaxSign = msgSigning
	// LaxNoSign does not produce signatures and validates incoming signatures iff one is present
	// Deprecated: it is recommend to either strictly enable, or strictly disable, signatures.
	LaxNoSign = 0
)

// mustVerify is true when a message signature must be verified.
// If signatures are not expected, verification checks if the signature is absent.
func ( MessageSignaturePolicy) () bool {
	return &msgVerification != 0
}

// mustSign is true when messages should be signed, and incoming messages are expected to have a signature.
func ( MessageSignaturePolicy) () bool {
	return &msgSigning != 0
}

const SignPrefix = "libp2p-pubsub:"

func verifyMessageSignature( *pb.Message) error {
	,  := messagePubKey()
	if  != nil {
		return 
	}

	 := *
	.Signature = nil
	.Key = nil
	,  := .Marshal()
	if  != nil {
		return 
	}

	 = withSignPrefix()

	,  := .Verify(, .Signature)
	if  != nil {
		return 
	}

	if ! {
		return fmt.Errorf("invalid signature")
	}

	return nil
}

func messagePubKey( *pb.Message) (crypto.PubKey, error) {
	var  crypto.PubKey

	,  := peer.IDFromBytes(.From)
	if  != nil {
		return nil, 
	}

	if .Key == nil {
		// no attached key, it must be extractable from the source ID
		,  = .ExtractPublicKey()
		if  != nil {
			return nil, fmt.Errorf("cannot extract signing key: %s", .Error())
		}
		if  == nil {
			return nil, fmt.Errorf("cannot extract signing key")
		}
	} else {
		,  = crypto.UnmarshalPublicKey(.Key)
		if  != nil {
			return nil, fmt.Errorf("cannot unmarshal signing key: %s", .Error())
		}

		// verify that the source ID matches the attached key
		if !.MatchesPublicKey() {
			return nil, fmt.Errorf("bad signing key; source ID %s doesn't match key", )
		}
	}

	return , nil
}

func signMessage( peer.ID,  crypto.PrivKey,  *pb.Message) error {
	,  := .Marshal()
	if  != nil {
		return 
	}

	 = withSignPrefix()

	,  := .Sign()
	if  != nil {
		return 
	}

	.Signature = 

	,  := .ExtractPublicKey()
	if  == nil {
		,  := crypto.MarshalPublicKey(.GetPublic())
		if  != nil {
			return 
		}
		.Key = 
	}

	return nil
}

func withSignPrefix( []byte) []byte {
	return append([]byte(SignPrefix), ...)
}