@ -2,6 +2,7 @@ package p2p
import (
import (
"crypto/ecdsa"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rand"
"errors"
"errors"
"fmt"
"fmt"
@ -27,25 +28,25 @@ const (
authRespLen = pubLen + shaLen + 1
authRespLen = pubLen + shaLen + 1
eciesBytes = 65 + 16 + 32
eciesBytes = 65 + 16 + 32
iHSLen = authMsgLen + eciesBytes // size of the final ECIES payload sent as initiator's handshake
encAuthMsgLen = authMsgLen + eciesBytes // size of the final ECIES payload sent as initiator's handshake
rHSLen = authRespLen + eciesBytes // size of the final ECIES payload sent as receiver's handshake
encAuthRespLen = authRespLen + eciesBytes // size of the final ECIES payload sent as receiver's handshake
)
)
// conn represents a remote connection after encryption handshake
// and protocol handshake have completed.
//
// The MsgReadWriter is usually layered as follows:
//
// lockedRW (thread-safety for ReadMsg, WriteMsg)
// rlpxFrameRW (message encoding, encryption, authentication)
// bufio.ReadWriter (buffering)
// net.Conn (network I/O)
//
type conn struct {
type conn struct {
MsgReadWriter
MsgReadWriter
* protoHandshake
* protoHandshake
}
}
// encHandshake contains the state of the encryption handshake.
type encHandshake struct {
remoteID discover . NodeID
initiator bool
initNonce , respNonce [ ] byte
dhSharedSecret [ ] byte
randomPrivKey * ecdsa . PrivateKey
remoteRandomPub * ecdsa . PublicKey
}
// secrets represents the connection secrets
// secrets represents the connection secrets
// which are negotiated during the encryption handshake.
// which are negotiated during the encryption handshake.
type secrets struct {
type secrets struct {
@ -64,34 +65,6 @@ type protoHandshake struct {
ID discover . NodeID
ID discover . NodeID
}
}
// secrets is called after the handshake is completed.
// It extracts the connection secrets from the handshake values.
func ( h * encHandshake ) secrets ( auth , authResp [ ] byte ) secrets {
sharedSecret := crypto . Sha3 ( h . dhSharedSecret , crypto . Sha3 ( h . respNonce , h . initNonce ) )
aesSecret := crypto . Sha3 ( h . dhSharedSecret , sharedSecret )
s := secrets {
RemoteID : h . remoteID ,
AES : aesSecret ,
MAC : crypto . Sha3 ( h . dhSharedSecret , aesSecret ) ,
Token : crypto . Sha3 ( sharedSecret ) ,
}
// setup sha3 instances for the MACs
mac1 := sha3 . NewKeccak256 ( )
mac1 . Write ( xor ( s . MAC , h . respNonce ) )
mac1 . Write ( auth )
mac2 := sha3 . NewKeccak256 ( )
mac2 . Write ( xor ( s . MAC , h . initNonce ) )
mac2 . Write ( authResp )
if h . initiator {
s . EgressMAC , s . IngressMAC = mac1 , mac2
} else {
s . EgressMAC , s . IngressMAC = mac2 , mac1
}
return s
}
// setupConn starts a protocol session on the given connection.
// setupConn starts a protocol session on the given connection.
// It runs the encryption handshake and the protocol handshake.
// It runs the encryption handshake and the protocol handshake.
// If dial is non-nil, the connection the local node is the initiator.
// If dial is non-nil, the connection the local node is the initiator.
@ -104,7 +77,7 @@ func setupConn(fd net.Conn, prv *ecdsa.PrivateKey, our *protoHandshake, dial *di
}
}
func setupInboundConn ( fd net . Conn , prv * ecdsa . PrivateKey , our * protoHandshake ) ( * conn , error ) {
func setupInboundConn ( fd net . Conn , prv * ecdsa . PrivateKey , our * protoHandshake ) ( * conn , error ) {
secrets , err := inbound EncHandshake( fd , prv , nil )
secrets , err := receiver EncHandshake( fd , prv , nil )
if err != nil {
if err != nil {
return nil , fmt . Errorf ( "encryption handshake failed: %v" , err )
return nil , fmt . Errorf ( "encryption handshake failed: %v" , err )
}
}
@ -124,7 +97,7 @@ func setupInboundConn(fd net.Conn, prv *ecdsa.PrivateKey, our *protoHandshake) (
}
}
func setupOutboundConn ( fd net . Conn , prv * ecdsa . PrivateKey , our * protoHandshake , dial * discover . Node ) ( * conn , error ) {
func setupOutboundConn ( fd net . Conn , prv * ecdsa . PrivateKey , our * protoHandshake , dial * discover . Node ) ( * conn , error ) {
secrets , err := outbound EncHandshake( fd , prv , dial . ID [ : ] , nil )
secrets , err := initiator EncHandshake( fd , prv , dial . ID , nil )
if err != nil {
if err != nil {
return nil , fmt . Errorf ( "encryption handshake failed: %v" , err )
return nil , fmt . Errorf ( "encryption handshake failed: %v" , err )
}
}
@ -145,14 +118,66 @@ func setupOutboundConn(fd net.Conn, prv *ecdsa.PrivateKey, our *protoHandshake,
return & conn { & lockedRW { wrapped : rw } , rhs } , nil
return & conn { & lockedRW { wrapped : rw } , rhs } , nil
}
}
// outboundEncHandshake negotiates a session token on conn.
// encHandshake contains the state of the encryption handshake.
type encHandshake struct {
initiator bool
remoteID discover . NodeID
remotePub * ecies . PublicKey // remote-pubk
initNonce , respNonce [ ] byte // nonce
randomPrivKey * ecies . PrivateKey // ecdhe-random
remoteRandomPub * ecies . PublicKey // ecdhe-random-pubk
}
// secrets is called after the handshake is completed.
// It extracts the connection secrets from the handshake values.
func ( h * encHandshake ) secrets ( auth , authResp [ ] byte ) ( secrets , error ) {
ecdheSecret , err := h . randomPrivKey . GenerateShared ( h . remoteRandomPub , sskLen , sskLen )
if err != nil {
return secrets { } , err
}
// derive base secrets from ephemeral key agreement
sharedSecret := crypto . Sha3 ( ecdheSecret , crypto . Sha3 ( h . respNonce , h . initNonce ) )
aesSecret := crypto . Sha3 ( ecdheSecret , sharedSecret )
s := secrets {
RemoteID : h . remoteID ,
AES : aesSecret ,
MAC : crypto . Sha3 ( ecdheSecret , aesSecret ) ,
Token : crypto . Sha3 ( sharedSecret ) ,
}
// setup sha3 instances for the MACs
mac1 := sha3 . NewKeccak256 ( )
mac1 . Write ( xor ( s . MAC , h . respNonce ) )
mac1 . Write ( auth )
mac2 := sha3 . NewKeccak256 ( )
mac2 . Write ( xor ( s . MAC , h . initNonce ) )
mac2 . Write ( authResp )
if h . initiator {
s . EgressMAC , s . IngressMAC = mac1 , mac2
} else {
s . EgressMAC , s . IngressMAC = mac2 , mac1
}
return s , nil
}
func ( h * encHandshake ) ecdhShared ( prv * ecdsa . PrivateKey ) ( [ ] byte , error ) {
return ecies . ImportECDSA ( prv ) . GenerateShared ( h . remotePub , sskLen , sskLen )
}
// initiatorEncHandshake negotiates a session token on conn.
// it should be called on the dialing side of the connection.
// it should be called on the dialing side of the connection.
//
//
// privateKey is the local client's private key
// prv is the local client's private key.
// remotePublicKey is the remote peer's node ID
// token is the token from a previous session with this node.
// sessionToken is the token from a previous session with this node.
func initiatorEncHandshake ( conn io . ReadWriter , prv * ecdsa . PrivateKey , remoteID discover . NodeID , token [ ] byte ) ( s secrets , err error ) {
func outboundEncHandshake ( conn io . ReadWriter , prvKey * ecdsa . PrivateKey , remotePublicKey [ ] byte , sessionToken [ ] byte ) ( s secrets , err error ) {
h , err := newInitiatorHandshake ( remoteID )
auth , initNonce , randomPrivKey , err := authMsg ( prvKey , remotePublicKey , sessionToken )
if err != nil {
return s , err
}
auth , err := h . authMsg ( prv , token )
if err != nil {
if err != nil {
return s , err
return s , err
}
}
@ -160,250 +185,189 @@ func outboundEncHandshake(conn io.ReadWriter, prvKey *ecdsa.PrivateKey, remotePu
return s , err
return s , err
}
}
response := make ( [ ] byte , rHS Len)
response := make ( [ ] byte , encAuthResp Len)
if _ , err = io . ReadFull ( conn , response ) ; err != nil {
if _ , err = io . ReadFull ( conn , response ) ; err != nil {
return s , err
return s , err
}
}
recNonce , remoteRandomPubKey , _ , err := completeHandshake ( response , prvKey )
if err := h . decodeAuthResp ( response , prv ) ; err != nil {
if err != nil {
return s , err
return s , err
}
}
return h . secrets ( auth , response )
}
func newInitiatorHandshake ( remoteID discover . NodeID ) ( * encHandshake , error ) {
// generate random initiator nonce
n := make ( [ ] byte , shaLen )
if _ , err := rand . Read ( n ) ; err != nil {
return nil , err
}
// generate random keypair to use for signing
randpriv , err := ecies . GenerateKey ( rand . Reader , crypto . S256 ( ) , nil )
if err != nil {
return nil , err
}
rpub , err := remoteID . Pubkey ( )
if err != nil {
return nil , fmt . Errorf ( "bad remoteID: %v" , err )
}
h := & encHandshake {
h := & encHandshake {
initiator : true ,
initiator : true ,
initNonce : initNonce ,
remoteID : remoteID ,
respNonce : recNonce ,
remotePub : ecies . ImportECDSAPublic ( rpub ) ,
randomPrivKey : randomPrivKey ,
initNonce : n ,
remoteRandomPub : remoteRandomPubKey ,
randomPrivKey : randpriv ,
}
}
copy ( h . remoteID [ : ] , remotePublicKey )
return h , nil
return h . secrets ( auth , response ) , nil
}
// authMsg creates the initiator handshake.
// TODO: change all the names
func authMsg ( prvKey * ecdsa . PrivateKey , remotePubKeyS , sessionToken [ ] byte ) (
auth , initNonce [ ] byte ,
randomPrvKey * ecdsa . PrivateKey ,
err error ,
) {
remotePubKey , err := importPublicKey ( remotePubKeyS )
if err != nil {
return
}
}
// authMsg creates an encrypted initiator handshake message.
func ( h * encHandshake ) authMsg ( prv * ecdsa . PrivateKey , token [ ] byte ) ( [ ] byte , error ) {
var tokenFlag byte
var tokenFlag byte
if sessionToken == nil {
if token == nil {
// no session token found means we need to generate shared secret.
// no session token found means we need to generate shared secret.
// ecies shared secret is used as initial session token for new peers
// ecies shared secret is used as initial session token for new peers
// generate shared key from prv and remote pubkey
// generate shared key from prv and remote pubkey
if sessionToken , err = ecies . ImportECDSA ( prvKey ) . GenerateShared ( ecies . ImportECDSAPublic ( remotePubKey ) , sskLen , sskLen ) ; err != nil {
var err error
return
if token , err = h . ecdhShared ( prv ) ; err != nil {
return nil , err
}
}
} else {
} else {
// for known peers, we use stored token from the previous session
// for known peers, we use stored token from the previous session
tokenFlag = 0x01
tokenFlag = 0x01
}
}
//E(remote-pubk, S(ecdhe-random, sha3(ecdh-shared-secret^nonce)) || H(ecdhe-random-pubk) || pubk || nonce || 0x0)
// sign known message:
// E(remote-pubk, S(ecdhe-random, sha3(token^nonce)) || H(ecdhe-random-pubk) || pubk || nonce || 0x1)
// allocate msgLen long message,
var msg [ ] byte = make ( [ ] byte , authMsgLen )
initNonce = msg [ authMsgLen - shaLen - 1 : authMsgLen - 1 ]
if _ , err = rand . Read ( initNonce ) ; err != nil {
return
}
// create known message
// ecdh-shared-secret^nonce for new peers
// ecdh-shared-secret^nonce for new peers
// token^nonce for old peers
// token^nonce for old peers
var sharedSecret = xor ( sessionToken , initNonce )
signed := xor ( token , h . initNonce )
signature , err := crypto . Sign ( signed , h . randomPrivKey . ExportECDSA ( ) )
// generate random keypair to use for signing
if err != nil {
if randomPrvKey , err = crypto . GenerateKey ( ) ; err != nil {
return nil , err
return
}
// sign shared secret (message known to both parties): shared-secret
var signature [ ] byte
// signature = sign(ecdhe-random, shared-secret)
// uses secp256k1.Sign
if signature , err = crypto . Sign ( sharedSecret , randomPrvKey ) ; err != nil {
return
}
// message
// signed-shared-secret || H(ecdhe-random-pubk) || pubk || nonce || 0x0
copy ( msg , signature ) // copy signed-shared-secret
// H(ecdhe-random-pubk)
var randomPubKey64 [ ] byte
if randomPubKey64 , err = exportPublicKey ( & randomPrvKey . PublicKey ) ; err != nil {
return
}
var pubKey64 [ ] byte
if pubKey64 , err = exportPublicKey ( & prvKey . PublicKey ) ; err != nil {
return
}
copy ( msg [ sigLen : sigLen + shaLen ] , crypto . Sha3 ( randomPubKey64 ) )
// pubkey copied to the correct segment.
copy ( msg [ sigLen + shaLen : sigLen + shaLen + pubLen ] , pubKey64 )
// nonce is already in the slice
// stick tokenFlag byte to the end
msg [ authMsgLen - 1 ] = tokenFlag
// encrypt using remote-pubk
// auth = eciesEncrypt(remote-pubk, msg)
if auth , err = crypto . Encrypt ( remotePubKey , msg ) ; err != nil {
return
}
return
}
}
// completeHandshake is called when the initiator receives an
// encode auth message
// authentication response (aka receiver handshake). It completes the
// signature || sha3(ecdhe-random-pubk) || pubk || nonce || token-flag
// handshake by reading off parameters the remote peer provides needed
msg := make ( [ ] byte , authMsgLen )
// to set up the secure session.
n := copy ( msg , signature )
func completeHandshake ( auth [ ] byte , prvKey * ecdsa . PrivateKey ) (
n += copy ( msg [ n : ] , crypto . Sha3 ( exportPubkey ( & h . randomPrivKey . PublicKey ) ) )
respNonce [ ] byte ,
n += copy ( msg [ n : ] , crypto . FromECDSAPub ( & prv . PublicKey ) [ 1 : ] )
remoteRandomPubKey * ecdsa . PublicKey ,
n += copy ( msg [ n : ] , h . initNonce )
tokenFlag bool ,
msg [ n ] = tokenFlag
err error ,
) {
// encrypt auth message using remote-pubk
var msg [ ] byte
return ecies . Encrypt ( rand . Reader , h . remotePub , msg , nil , nil )
// they prove that msg is meant for me,
// I prove I possess private key if i can read it
if msg , err = crypto . Decrypt ( prvKey , auth ) ; err != nil {
return
}
}
respNonce = msg [ pubLen : pubLen + shaLen ]
// decodeAuthResp decode an encrypted authentication response message.
var remoteRandomPubKeyS = msg [ : pubLen ]
func ( h * encHandshake ) decodeAuthResp ( auth [ ] byte , prv * ecdsa . PrivateKey ) error {
if remoteRandomPubKey , err = importPublicKey ( remoteRandomPubKeyS ) ; err != nil {
msg , err := crypto . Decrypt ( prv , auth )
return
if err != nil {
return fmt . Errorf ( "could not decrypt auth response (%v)" , err )
}
}
if msg [ authRespLen - 1 ] == 0x01 {
h . respNonce = msg [ pubLen : pubLen + shaLen ]
tokenFlag = true
h . remoteRandomPub , err = importPublicKey ( msg [ : pubLen ] )
if err != nil {
return err
}
}
return
// ignore token flag for now
return nil
}
}
// inbound EncHandshake negotiates a session token on conn.
// receiverEncHandshake negotiates a session token on conn.
// it should be called on the listening side of the connection.
// it should be called on the listening side of the connection.
//
//
// privateKey is the local client's private key
// prv is the local client's private key.
// sessionToken is the token from a previous session with this node.
// token is the token from a previous session with this node.
func inboundEncHandshake ( conn io . ReadWriter , prvKey * ecdsa . PrivateKey , sessionToken [ ] byte ) ( s secrets , err error ) {
func receiverEncHandshake ( conn io . ReadWriter , prv * ecdsa . PrivateKey , token [ ] byte ) ( s secrets , err error ) {
// we are listening connection. we are responders in the
// read remote auth sent by initiator.
// handshake. Extract info from the authentication. The initiator
auth := make ( [ ] byte , encAuthMsgLen )
// starts by sending us a handshake that we need to respond to. so
// we read auth message first, then respond.
auth := make ( [ ] byte , iHSLen )
if _ , err := io . ReadFull ( conn , auth ) ; err != nil {
if _ , err := io . ReadFull ( conn , auth ) ; err != nil {
return s , err
return s , err
}
}
response , recNonce , initNonce , remotePubKey , randomPrivKey , remoteRandomPubKey , err := authResp ( auth , sessionToken , prvKey )
h , err := decodeAuthMsg ( prv , token , auth )
if err != nil {
if err != nil {
return s , err
return s , err
}
}
if _ , err = conn . Write ( response ) ; err != nil {
// send auth response
resp , err := h . authResp ( prv , token )
if err != nil {
return s , err
return s , err
}
}
if _ , err = conn . Write ( resp ) ; err != nil {
h := & encHandshake {
return s , err
initiator : false ,
initNonce : initNonce ,
respNonce : recNonce ,
randomPrivKey : randomPrivKey ,
remoteRandomPub : remoteRandomPubKey ,
}
}
copy ( h . remoteID [ : ] , remotePubKey )
return h . secrets ( auth , response ) , err
return h . secrets ( auth , resp )
}
}
// authResp is called by peer if it accepted (but not
func decodeAuthMsg ( prv * ecdsa . PrivateKey , token [ ] byte , auth [ ] byte ) ( * encHandshake , error ) {
// initiated) the connection from the remote. It is passed the initiator
var err error
// handshake received and the session token belonging to the
h := new ( encHandshake )
// remote initiator.
// generate random keypair for session
//
h . randomPrivKey , err = ecies . GenerateKey ( rand . Reader , crypto . S256 ( ) , nil )
// The first return value is the authentication response (aka receiver
// handshake) that is to be sent to the remote initiator.
func authResp ( auth , sessionToken [ ] byte , prvKey * ecdsa . PrivateKey ) (
authResp , respNonce , initNonce , remotePubKeyS [ ] byte ,
randomPrivKey * ecdsa . PrivateKey ,
remoteRandomPubKey * ecdsa . PublicKey ,
err error ,
) {
// they prove that msg is meant for me,
// I prove I possess private key if i can read it
msg , err := crypto . Decrypt ( prvKey , auth )
if err != nil {
if err != nil {
return
return nil , err
}
// generate random nonce
h . respNonce = make ( [ ] byte , shaLen )
if _ , err = rand . Read ( h . respNonce ) ; err != nil {
return nil , err
}
}
remotePubKeyS = msg [ sigLen + shaLen : sigLen + shaLen + pubLen ]
msg , err := crypto . Decrypt ( prv , auth )
remotePubKey , _ := importPublicKey ( remotePubKeyS )
if err != nil {
return nil , fmt . Errorf ( "could not decrypt auth message (%v)" , err )
var tokenFlag byte
if sessionToken == nil {
// no session token found means we need to generate shared secret.
// ecies shared secret is used as initial session token for new peers
// generate shared key from prv and remote pubkey
if sessionToken , err = ecies . ImportECDSA ( prvKey ) . GenerateShared ( ecies . ImportECDSAPublic ( remotePubKey ) , sskLen , sskLen ) ; err != nil {
return
}
}
// tokenFlag = 0x00 // redundant
} else {
// decode message parameters
// for known peers, we use stored token from the previous session
// signature || sha3(ecdhe-random-pubk) || pubk || nonce || token-flag
tokenFlag = 0x01
h . initNonce = msg [ authMsgLen - shaLen - 1 : authMsgLen - 1 ]
copy ( h . remoteID [ : ] , msg [ sigLen + shaLen : sigLen + shaLen + pubLen ] )
rpub , err := h . remoteID . Pubkey ( )
if err != nil {
return nil , fmt . Errorf ( "bad remoteID: %#v" , err )
}
}
h . remotePub = ecies . ImportECDSAPublic ( rpub )
// recover remote random pubkey from signed message.
if token == nil {
// TODO: it is an error if the initiator has a token and we don't. check that.
// the initiator nonce is read off the end of the message
// no session token means we need to generate shared secret.
initNonce = msg [ authMsgLen - shaLen - 1 : authMsgLen - 1 ]
// ecies shared secret is used as initial session token for new peers.
// I prove that i own prv key (to derive shared secret, and read
// generate shared key from prv and remote pubkey.
// nonce off encrypted msg) and that I own shared secret they
if token , err = h . ecdhShared ( prv ) ; err != nil {
// prove they own the private key belonging to ecdhe-random-pubk
return nil , err
// we can now reconstruct the signed message and recover the peers
// pubkey
var signedMsg = xor ( sessionToken , initNonce )
var remoteRandomPubKeyS [ ] byte
if remoteRandomPubKeyS , err = secp256k1 . RecoverPubkey ( signedMsg , msg [ : sigLen ] ) ; err != nil {
return
}
}
// convert to ECDSA standard
if remoteRandomPubKey , err = importPublicKey ( remoteRandomPubKeyS ) ; err != nil {
return
}
}
signedMsg := xor ( token , h . initNonce )
// now we find ourselves a long task too, fill it random
remoteRandomPub , err := secp256k1 . RecoverPubkey ( signedMsg , msg [ : sigLen ] )
var resp = make ( [ ] byte , authRespLen )
if err != nil {
// generate shaLen long nonce
return nil , err
respNonce = resp [ pubLen : pubLen + shaLen ]
if _ , err = rand . Read ( respNonce ) ; err != nil {
return
}
}
// generate random keypair for session
h . remoteRandomPub , _ = importPublicKey ( remoteRandomPub )
if randomPrivKey , err = crypto . GenerateKey ( ) ; err != nil {
return h , nil
return
}
}
// authResp generates the encrypted authentication response message.
func ( h * encHandshake ) authResp ( prv * ecdsa . PrivateKey , token [ ] byte ) ( [ ] byte , error ) {
// responder auth message
// responder auth message
// E(remote-pubk, ecdhe-random-pubk || nonce || 0x0)
// E(remote-pubk, ecdhe-random-pubk || nonce || 0x0)
var randomPubKeyS [ ] byte
resp := make ( [ ] byte , authRespLen )
if randomPubKeyS , err = exportPublicKey ( & randomPrivKey . PublicKey ) ; err != nil {
n := copy ( resp , exportPubkey ( & h . randomPrivKey . PublicKey ) )
return
n += copy ( resp [ n : ] , h . respNonce )
if token == nil {
resp [ n ] = 0
} else {
resp [ n ] = 1
}
}
copy ( resp [ : pubLen ] , randomPubKeyS )
// nonce is already in the slice
resp [ authRespLen - 1 ] = tokenFlag
// encrypt using remote-pubk
// encrypt using remote-pubk
// auth = eciesEncrypt(remote-pubk, msg)
return ecies . Encrypt ( rand . Reader , h . remotePub , resp , nil , nil )
// why not encrypt with ecdhe-random-remote
if authResp , err = crypto . Encrypt ( remotePubKey , resp ) ; err != nil {
return
}
return
}
}
// importPublicKey unmarshals 512 bit public keys.
// importPublicKey unmarshals 512 bit public keys.
func importPublicKey ( pubKey [ ] byte ) ( pubKeyEC * ecdsa . PublicKey , err error ) {
func importPublicKey ( pubKey [ ] byte ) ( * ecies . PublicKey , error ) {
var pubKey65 [ ] byte
var pubKey65 [ ] byte
switch len ( pubKey ) {
switch len ( pubKey ) {
case 64 :
case 64 :
@ -414,14 +378,15 @@ func importPublicKey(pubKey []byte) (pubKeyEC *ecdsa.PublicKey, err error) {
default :
default :
return nil , fmt . Errorf ( "invalid public key length %v (expect 64/65)" , len ( pubKey ) )
return nil , fmt . Errorf ( "invalid public key length %v (expect 64/65)" , len ( pubKey ) )
}
}
return crypto . ToECDSAPub ( pubKey65 ) , nil
// TODO: fewer pointless conversions
return ecies . ImportECDSAPublic ( crypto . ToECDSAPub ( pubKey65 ) ) , nil
}
}
func exportPublicK ey ( pubKeyEC * ecdsa . PublicKey ) ( pubKey [ ] byte , err error ) {
func exportPubk ey ( pub * ecies . PublicKey ) [ ] byte {
if pubKeyEC == nil {
if pub == nil {
return nil , fmt . Errorf ( "no ECDSA public key given ")
panic ( "nil pubkey ")
}
}
return crypto . FromECDSAPub ( pubKeyEC ) [ 1 : ] , nil
return elliptic . Marshal ( pub . Curve , pub . X , pub . Y ) [ 1 : ]
}
}
func xor ( one , other [ ] byte ) ( xor [ ] byte ) {
func xor ( one , other [ ] byte ) ( xor [ ] byte ) {