github.com/decred/dcrlnd@v0.7.6/keychain/ecdh.go (about)

     1  package keychain
     2  
     3  import (
     4  	"crypto/sha256"
     5  	"fmt"
     6  
     7  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
     8  )
     9  
    10  // NewPubKeyECDH wraps the given key of the key ring so it adheres to the
    11  // SingleKeyECDH interface.
    12  func NewPubKeyECDH(keyDesc KeyDescriptor, ecdh ECDHRing) *PubKeyECDH {
    13  	return &PubKeyECDH{
    14  		keyDesc: keyDesc,
    15  		ecdh:    ecdh,
    16  	}
    17  }
    18  
    19  // PubKeyECDH is an implementation of the SingleKeyECDH interface. It wraps an
    20  // ECDH key ring so it can perform ECDH shared key generation against a single
    21  // abstracted away private key.
    22  type PubKeyECDH struct {
    23  	keyDesc KeyDescriptor
    24  	ecdh    ECDHRing
    25  }
    26  
    27  // PubKey returns the public key of the private key that is abstracted away by
    28  // the interface.
    29  //
    30  // NOTE: This is part of the SingleKeyECDH interface.
    31  func (p *PubKeyECDH) PubKey() *secp256k1.PublicKey {
    32  	return p.keyDesc.PubKey
    33  }
    34  
    35  // ECDH performs a scalar multiplication (ECDH-like operation) between the
    36  // abstracted private key and a remote public key. The output returned will be
    37  // the sha256 of the resulting shared point serialized in compressed format. If
    38  // k is our private key, and P is the public key, we perform the following
    39  // operation:
    40  //
    41  //	sx := k*P
    42  //	s := sha256(sx.SerializeCompressed())
    43  //
    44  // NOTE: This is part of the SingleKeyECDH interface.
    45  func (p *PubKeyECDH) ECDH(pubKey *secp256k1.PublicKey) ([32]byte, error) {
    46  	return p.ecdh.ECDH(p.keyDesc, pubKey)
    47  }
    48  
    49  // PrivKeyECDH is an implementation of the SingleKeyECDH in which we do have the
    50  // full private key. This can be used to wrap a temporary key to conform to the
    51  // SingleKeyECDH interface.
    52  type PrivKeyECDH struct {
    53  	// PrivKey is the private key that is used for the ECDH operation.
    54  	PrivKey *secp256k1.PrivateKey
    55  }
    56  
    57  // PubKey returns the public key of the private key that is abstracted away by
    58  // the interface.
    59  //
    60  // NOTE: This is part of the SingleKeyECDH interface.
    61  func (p *PrivKeyECDH) PubKey() *secp256k1.PublicKey {
    62  	return p.PrivKey.PubKey()
    63  }
    64  
    65  // ECDH performs a scalar multiplication (ECDH-like operation) between the
    66  // abstracted private key and a remote public key. The output returned will be
    67  // the sha256 of the resulting shared point serialized in compressed format. If
    68  // k is our private key, and P is the public key, we perform the following
    69  // operation:
    70  //
    71  //	sx := k*P
    72  //	s := sha256(sx.SerializeCompressed())
    73  //
    74  // NOTE: This is part of the SingleKeyECDH interface.
    75  func (p *PrivKeyECDH) ECDH(pub *secp256k1.PublicKey) ([32]byte, error) {
    76  
    77  	// Privkey to ModNScalar.
    78  	var privKeyModn secp256k1.ModNScalar
    79  	privKeyModn.SetByteSlice(p.PrivKey.Serialize())
    80  
    81  	// Pubkey to JacobianPoint.
    82  	var pubJacobian, res secp256k1.JacobianPoint
    83  	pub.AsJacobian(&pubJacobian)
    84  
    85  	// Calculate shared point and ensure it's on the curve.
    86  	secp256k1.ScalarMultNonConst(&privKeyModn, &pubJacobian, &res)
    87  	res.ToAffine()
    88  	sharedPub := secp256k1.NewPublicKey(&res.X, &res.Y)
    89  	if !sharedPub.IsOnCurve() {
    90  		return [32]byte{}, fmt.Errorf("Derived ECDH point is not on the secp256k1 curve")
    91  	}
    92  
    93  	// Hash of the serialized point is the shared secret.
    94  	h := sha256.Sum256(sharedPub.SerializeCompressed())
    95  	return h, nil
    96  }
    97  
    98  var _ SingleKeyECDH = (*PubKeyECDH)(nil)
    99  var _ SingleKeyECDH = (*PrivKeyECDH)(nil)