// DecodeAnnouncement decodes announcement bytes into a host announcement, // verifying the prefix and the signature. func DecodeAnnouncement(fullAnnouncement []byte) (na NetAddress, spk types.SiaPublicKey, err error) { // Read the first part of the announcement to get the intended host // announcement. var ha HostAnnouncement dec := encoding.NewDecoder(bytes.NewReader(fullAnnouncement)) err = dec.Decode(&ha) if err != nil { return "", types.SiaPublicKey{}, err } // Check that the announcement was registered as a host announcement. if ha.Specifier != PrefixHostAnnouncement { return "", types.SiaPublicKey{}, ErrAnnNotAnnouncement } // Check that the public key is a recognized type of public key. if ha.PublicKey.Algorithm != types.SignatureEd25519 { return "", types.SiaPublicKey{}, ErrAnnUnrecognizedSignature } // Read the signature out of the reader. var sig crypto.Signature err = dec.Decode(&sig) if err != nil { return "", types.SiaPublicKey{}, err } // Verify the signature. var pk crypto.PublicKey copy(pk[:], ha.PublicKey.Key) annHash := crypto.HashObject(ha) err = crypto.VerifyHash(annHash, pk, sig) if err != nil { return "", types.SiaPublicKey{}, err } return ha.NetAddress, ha.PublicKey, nil }
// managedVerifyChallengeResponse will verify that the renter's response to the // challenge provided by the host is correct. In the process, the storage // obligation and file contract revision will be loaded and returned. // // The storage obligation is returned under a storage obligation lock. func (h *Host) managedVerifyChallengeResponse(fcid types.FileContractID, challenge crypto.Hash, challengeResponse crypto.Signature) (so storageObligation, recentRevision types.FileContractRevision, revisionSigs []types.TransactionSignature, err error) { // Grab a lock before it is possible to perform any operations on the // storage obligation. Defer a call to unlock in the event of an error. If // there is no error, the storage obligation will be returned with a lock. err = h.managedTryLockStorageObligation(fcid) if err != nil { err = extendErr("could not get "+fcid.String()+" lock: ", ErrorInternal(err.Error())) return storageObligation{}, types.FileContractRevision{}, nil, err } defer func() { if err != nil { h.managedUnlockStorageObligation(fcid) } }() // Fetch the storage obligation, which has the revision, which has the // renter's public key. h.mu.RLock() defer h.mu.RUnlock() err = h.db.View(func(tx *bolt.Tx) error { so, err = getStorageObligation(tx, fcid) return err }) if err != nil { err = extendErr("could not fetch "+fcid.String()+": ", ErrorInternal(err.Error())) return storageObligation{}, types.FileContractRevision{}, nil, err } // Pull out the file contract revision and the revision's signatures from // the transaction. revisionTxn := so.RevisionTransactionSet[len(so.RevisionTransactionSet)-1] recentRevision = revisionTxn.FileContractRevisions[0] for _, sig := range revisionTxn.TransactionSignatures { // Checking for just the parent id is sufficient, an over-signed file // contract is invalid. if sig.ParentID == crypto.Hash(fcid) { revisionSigs = append(revisionSigs, sig) } } // Verify that the challegne response matches the public key. var renterPK crypto.PublicKey // Sanity check - there should be two public keys. if len(recentRevision.UnlockConditions.PublicKeys) != 2 { // The error has to be set here so that the defered error check will // unlock the storage obligation. h.log.Critical("wrong public key count in file contract revision") err = errRevisionWrongPublicKeyCount err = extendErr("wrong public key count for "+fcid.String()+": ", ErrorInternal(err.Error())) return storageObligation{}, types.FileContractRevision{}, nil, err } copy(renterPK[:], recentRevision.UnlockConditions.PublicKeys[0].Key) err = crypto.VerifyHash(challenge, renterPK, challengeResponse) if err != nil { err = extendErr("bad signature from renter: ", ErrorCommunication(err.Error())) return storageObligation{}, types.FileContractRevision{}, nil, err } return so, recentRevision, revisionSigs, nil }
// verifyChallengeResponse will verify that the renter's response to the // challenge provided by the host is correct. In the process, the storage // obligation and file contract revision will be loaded and returned. func (h *Host) verifyChallengeResponse(fcid types.FileContractID, challenge crypto.Hash, challengeResponse crypto.Signature) (*storageObligation, types.FileContractRevision, []types.TransactionSignature, error) { // Fetch the storage obligation, which has the revision, which has the // renter's public key. var so *storageObligation err := h.db.Update(func(tx *bolt.Tx) error { fso, err := getStorageObligation(tx, fcid) so = &fso return err }) if err != nil { return nil, types.FileContractRevision{}, nil, err } // Pull out the file contract revision and the revision's signatures from // the transaction. revisionTxn := so.RevisionTransactionSet[len(so.RevisionTransactionSet)-1] recentRevision := revisionTxn.FileContractRevisions[0] var revisionSigs []types.TransactionSignature for _, sig := range revisionTxn.TransactionSignatures { // Checking for just the parent id is sufficient, an over-signed file // contract is invalid. if sig.ParentID == crypto.Hash(fcid) { revisionSigs = append(revisionSigs, sig) } } // Verify that the challegne response matches the public key. var renterPK crypto.PublicKey // Sanity check - there should be two public keys. if len(recentRevision.UnlockConditions.PublicKeys) != 2 { h.log.Critical("found a revision with too few public keys") return nil, types.FileContractRevision{}, nil, errRevisionFewPublicKeys } copy(renterPK[:], recentRevision.UnlockConditions.PublicKeys[0].Key) err = crypto.VerifyHash(challenge, renterPK, challengeResponse) if err != nil { return nil, types.FileContractRevision{}, nil, err } return so, recentRevision, revisionSigs, nil }
// validSignatures checks the validaty of all signatures in a transaction. func (t *Transaction) validSignatures(currentHeight BlockHeight) error { // Check that all covered fields objects follow the rules. err := t.validCoveredFields() if err != nil { return err } // Create the inputSignatures object for each input. sigMap := make(map[crypto.Hash]*inputSignatures) for i, input := range t.SiacoinInputs { id := crypto.Hash(input.ParentID) _, exists := sigMap[id] if exists { return ErrDoubleSpend } sigMap[id] = &inputSignatures{ remainingSignatures: input.UnlockConditions.SignaturesRequired, possibleKeys: input.UnlockConditions.PublicKeys, usedKeys: make(map[uint64]struct{}), index: i, } } for i, revision := range t.FileContractRevisions { id := crypto.Hash(revision.ParentID) _, exists := sigMap[id] if exists { return ErrDoubleSpend } sigMap[id] = &inputSignatures{ remainingSignatures: revision.UnlockConditions.SignaturesRequired, possibleKeys: revision.UnlockConditions.PublicKeys, usedKeys: make(map[uint64]struct{}), index: i, } } for i, input := range t.SiafundInputs { id := crypto.Hash(input.ParentID) _, exists := sigMap[id] if exists { return ErrDoubleSpend } sigMap[id] = &inputSignatures{ remainingSignatures: input.UnlockConditions.SignaturesRequired, possibleKeys: input.UnlockConditions.PublicKeys, usedKeys: make(map[uint64]struct{}), index: i, } } // Check all of the signatures for validity. for i, sig := range t.TransactionSignatures { // Check that sig corresponds to an entry in sigMap. inSig, exists := sigMap[crypto.Hash(sig.ParentID)] if !exists || inSig.remainingSignatures == 0 { return ErrFrivilousSignature } // Check that sig's key hasn't already been used. _, exists = inSig.usedKeys[sig.PublicKeyIndex] if exists { return ErrPublicKeyOveruse } // Check that the public key index refers to an existing public key. if sig.PublicKeyIndex >= uint64(len(inSig.possibleKeys)) { return ErrInvalidPubKeyIndex } // Check that the timelock has expired. if sig.Timelock > currentHeight { return ErrPrematureSignature } // Check that the signature verifies. Multiple signature schemes are // supported. publicKey := inSig.possibleKeys[sig.PublicKeyIndex] switch publicKey.Algorithm { case SignatureEntropy: // Entropy cannot ever be used to sign a transaction. return ErrEntropyKey case SignatureEd25519: // Decode the public key and signature. var edPK crypto.PublicKey err := encoding.Unmarshal([]byte(publicKey.Key), &edPK) if err != nil { return err } var edSig [crypto.SignatureSize]byte err = encoding.Unmarshal([]byte(sig.Signature), &edSig) if err != nil { return err } cryptoSig := crypto.Signature(edSig) sigHash := t.SigHash(i) err = crypto.VerifyHash(sigHash, edPK, cryptoSig) if err != nil { return err } default: // If the identifier is not recognized, assume that the signature // is valid. This allows more signature types to be added via soft // forking. } inSig.usedKeys[sig.PublicKeyIndex] = struct{}{} inSig.remainingSignatures-- } // Check that all inputs have been sufficiently signed. for _, reqSigs := range sigMap { if reqSigs.remainingSignatures != 0 { return ErrMissingSignatures } } return nil }