github.com/core-coin/go-core/v2@v2.1.9/p2p/discover/v5wire/crypto.go (about)

     1  // Copyright 2020 by the Authors
     2  // This file is part of the go-core library.
     3  //
     4  // The go-core library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-core library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-core library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package v5wire
    18  
    19  import (
    20  	"crypto/aes"
    21  	"crypto/cipher"
    22  	"errors"
    23  	"fmt"
    24  	"hash"
    25  
    26  	"golang.org/x/crypto/hkdf"
    27  
    28  	"github.com/core-coin/go-core/v2/crypto"
    29  	"github.com/core-coin/go-core/v2/p2p/enode"
    30  )
    31  
    32  const (
    33  	// Encryption/authentication parameters.
    34  	aesKeySize   = 16
    35  	gcmNonceSize = 12
    36  )
    37  
    38  // Nonce represents a nonce used for AES/GCM.
    39  type Nonce [gcmNonceSize]byte
    40  
    41  // EncodePubkey encodes a public key.
    42  func EncodePubkey(key *crypto.PublicKey) []byte {
    43  	return key[:]
    44  }
    45  
    46  // DecodePubkey decodes a public key in compressed format.
    47  func DecodePubkey(e []byte) (*crypto.PublicKey, error) {
    48  	return crypto.UnmarshalPubKey(e)
    49  }
    50  
    51  // idNonceHash computes the ID signature hash used in the handshake.
    52  func idNonceHash(h hash.Hash, challenge, ephkey []byte, destID enode.ID) []byte {
    53  	h.Reset()
    54  	h.Write([]byte("discovery v5 identity proof"))
    55  	h.Write(challenge)
    56  	h.Write(ephkey)
    57  	h.Write(destID[:])
    58  	return h.Sum(nil)
    59  }
    60  
    61  // makeIDSignature creates the ID nonce signature.
    62  func makeIDSignature(hash hash.Hash, key *crypto.PrivateKey, challenge, ephkey []byte, destID enode.ID) ([]byte, error) {
    63  	input := idNonceHash(hash, challenge, ephkey, destID)
    64  	idsig, err := crypto.Sign(input, key)
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  	return idsig, nil
    69  }
    70  
    71  // ed448raw is an unparsed ed448 public key ENR entry.
    72  type ed448raw []byte
    73  
    74  func (ed448raw) ENRKey() string { return "secp256k1" }
    75  
    76  // verifyIDSignature checks that signature over idnonce was made by the given node.
    77  func verifyIDSignature(hash hash.Hash, sig []byte, n *enode.Node, challenge, ephkey []byte, destID enode.ID) error {
    78  	switch idscheme := n.Record().IdentityScheme(); idscheme {
    79  	case "v4":
    80  		var pubkey ed448raw
    81  		if n.Load(&pubkey) != nil {
    82  			return errors.New("no ed448 public key in record")
    83  		}
    84  		input := idNonceHash(hash, challenge, ephkey, destID)
    85  		if !crypto.VerifySignature(pubkey, input, sig) {
    86  			return errInvalidNonceSig
    87  		}
    88  		return nil
    89  	default:
    90  		return fmt.Errorf("can't verify ID nonce signature against scheme %q", idscheme)
    91  	}
    92  }
    93  
    94  type hashFn func() hash.Hash
    95  
    96  // deriveKeys creates the session keys.
    97  func deriveKeys(hash hashFn, priv *crypto.PrivateKey, pub *crypto.PublicKey, n1, n2 enode.ID, challenge []byte) *session {
    98  	const text = "discovery v5 key agreement"
    99  	var info = make([]byte, 0, len(text)+len(n1)+len(n2))
   100  	info = append(info, text...)
   101  	info = append(info, n1[:]...)
   102  	info = append(info, n2[:]...)
   103  
   104  	eph := ecdh(priv, pub)
   105  	if eph == nil {
   106  		return nil
   107  	}
   108  	kdf := hkdf.New(hash, eph, challenge, info)
   109  	sec := session{writeKey: make([]byte, aesKeySize), readKey: make([]byte, aesKeySize)}
   110  	kdf.Read(sec.writeKey)
   111  	kdf.Read(sec.readKey)
   112  	for i := range eph {
   113  		eph[i] = 0
   114  	}
   115  	return &sec
   116  }
   117  
   118  // ecdh creates a shared secret.
   119  func ecdh(privkey *crypto.PrivateKey, pubkey *crypto.PublicKey) []byte {
   120  	shared := crypto.ComputeSecret(privkey, pubkey)
   121  	return shared
   122  }
   123  
   124  // encryptGCM encrypts pt using AES-GCM with the given key and nonce. The ciphertext is
   125  // appended to dest, which must not overlap with plaintext. The resulting ciphertext is 16
   126  // bytes longer than plaintext because it contains an authentication tag.
   127  func encryptGCM(dest, key, nonce, plaintext, authData []byte) ([]byte, error) {
   128  	block, err := aes.NewCipher(key)
   129  	if err != nil {
   130  		panic(fmt.Errorf("can't create block cipher: %v", err))
   131  	}
   132  	aesgcm, err := cipher.NewGCMWithNonceSize(block, gcmNonceSize)
   133  	if err != nil {
   134  		panic(fmt.Errorf("can't create GCM: %v", err))
   135  	}
   136  	return aesgcm.Seal(dest, nonce, plaintext, authData), nil
   137  }
   138  
   139  // decryptGCM decrypts ct using AES-GCM with the given key and nonce.
   140  func decryptGCM(key, nonce, ct, authData []byte) ([]byte, error) {
   141  	block, err := aes.NewCipher(key)
   142  	if err != nil {
   143  		return nil, fmt.Errorf("can't create block cipher: %v", err)
   144  	}
   145  	if len(nonce) != gcmNonceSize {
   146  		return nil, fmt.Errorf("invalid GCM nonce size: %d", len(nonce))
   147  	}
   148  	aesgcm, err := cipher.NewGCMWithNonceSize(block, gcmNonceSize)
   149  	if err != nil {
   150  		return nil, fmt.Errorf("can't create GCM: %v", err)
   151  	}
   152  	pt := make([]byte, 0, len(ct))
   153  	return aesgcm.Open(pt, nonce, ct, authData)
   154  }