// Tests whether a message can be signed, and wrapped in plain-text. func TestMessageCleartextSignRecover(t *testing.T) { key, err := crypto.GenerateKey() if err != nil { t.Fatalf("failed to create crypto key: %v", err) } payload := []byte("hello world") msg := NewMessage(payload) if _, err := msg.Wrap(DefaultPoW, Options{ From: key, }); err != nil { t.Fatalf("failed to sign message: %v", err) } if msg.Flags&signatureFlag != signatureFlag { t.Fatalf("signature flag mismatch: have %d, want %d", msg.Flags&signatureFlag, signatureFlag) } if bytes.Compare(msg.Payload, payload) != 0 { t.Fatalf("payload mismatch after signing: have 0x%x, want 0x%x", msg.Payload, payload) } pubKey := msg.Recover() if pubKey == nil { t.Fatalf("failed to recover public key") } p1 := elliptic.Marshal(crypto.S256(), key.PublicKey.X, key.PublicKey.Y) p2 := elliptic.Marshal(crypto.S256(), pubKey.X, pubKey.Y) if !bytes.Equal(p1, p2) { t.Fatalf("public key mismatch: have 0x%x, want 0x%x", p2, p1) } }
func (h *encHandshake) handleAuthMsg(msg *authMsgV4, prv *ecdsa.PrivateKey) error { // Import the remote identity. h.initNonce = msg.Nonce[:] h.remoteID = msg.InitiatorPubkey rpub, err := h.remoteID.Pubkey() if err != nil { return fmt.Errorf("bad remoteID: %#v", err) } h.remotePub = ecies.ImportECDSAPublic(rpub) // Generate random keypair for ECDH. // If a private key is already set, use it instead of generating one (for testing). if h.randomPrivKey == nil { h.randomPrivKey, err = ecies.GenerateKey(rand.Reader, crypto.S256(), nil) if err != nil { return err } } // Check the signature. token, err := h.staticSharedSecret(prv) if err != nil { return err } signedMsg := xor(token, h.initNonce) remoteRandomPub, err := secp256k1.RecoverPubkey(signedMsg, msg.Signature[:]) if err != nil { return err } h.remoteRandomPub, _ = importPublicKey(remoteRandomPub) return nil }
// makeAuthMsg creates the initiator handshake message. func (h *encHandshake) makeAuthMsg(prv *ecdsa.PrivateKey, token []byte) (*authMsgV4, error) { rpub, err := h.remoteID.Pubkey() if err != nil { return nil, fmt.Errorf("bad remoteID: %v", err) } h.remotePub = ecies.ImportECDSAPublic(rpub) // Generate random initiator nonce. h.initNonce = make([]byte, shaLen) if _, err := rand.Read(h.initNonce); err != nil { return nil, err } // Generate random keypair to for ECDH. h.randomPrivKey, err = ecies.GenerateKey(rand.Reader, crypto.S256(), nil) if err != nil { return nil, err } // Sign known message: static-shared-secret ^ nonce token, err = h.staticSharedSecret(prv) if err != nil { return nil, err } signed := xor(token, h.initNonce) signature, err := crypto.Sign(signed, h.randomPrivKey.ExportECDSA()) if err != nil { return nil, err } msg := new(authMsgV4) copy(msg.Signature[:], signature) copy(msg.InitiatorPubkey[:], crypto.FromECDSAPub(&prv.PublicKey)[1:]) copy(msg.Nonce[:], h.initNonce) msg.Version = 4 return msg, nil }
// Tests whether a message can be properly signed and encrypted. func TestMessageFullCrypto(t *testing.T) { fromKey, err := crypto.GenerateKey() if err != nil { t.Fatalf("failed to create sender crypto key: %v", err) } toKey, err := crypto.GenerateKey() if err != nil { t.Fatalf("failed to create recipient crypto key: %v", err) } payload := []byte("hello world") msg := NewMessage(payload) envelope, err := msg.Wrap(DefaultPoW, Options{ From: fromKey, To: &toKey.PublicKey, }) if err != nil { t.Fatalf("failed to encrypt message: %v", err) } if msg.Flags&signatureFlag != signatureFlag { t.Fatalf("signature flag mismatch: have %d, want %d", msg.Flags&signatureFlag, signatureFlag) } if len(msg.Signature) == 0 { t.Fatalf("no signature found for signed message") } out, err := envelope.Open(toKey) if err != nil { t.Fatalf("failed to open encrypted message: %v", err) } if !bytes.Equal(out.Payload, payload) { t.Error("payload mismatch: have 0x%x, want 0x%x", out.Payload, payload) } pubKey := out.Recover() if pubKey == nil { t.Fatalf("failed to recover public key") } p1 := elliptic.Marshal(crypto.S256(), fromKey.PublicKey.X, fromKey.PublicKey.Y) p2 := elliptic.Marshal(crypto.S256(), pubKey.X, pubKey.Y) if !bytes.Equal(p1, p2) { t.Fatalf("public key mismatch: have 0x%x, want 0x%x", p2, p1) } }
// Pubkey returns the public key represented by the node ID. // It returns an error if the ID is not a point on the curve. func (id NodeID) Pubkey() (*ecdsa.PublicKey, error) { p := &ecdsa.PublicKey{Curve: crypto.S256(), X: new(big.Int), Y: new(big.Int)} half := len(id) / 2 p.X.SetBytes(id[:half]) p.Y.SetBytes(id[half:]) if !p.Curve.IsOnCurve(p.X, p.Y) { return nil, errors.New("not a point on the S256 curve") } return p, nil }