// isPubKey returns whether or not the passed public key script is a standard // pay-to-pubkey script that pays to a valid compressed or uncompressed public // key along with the serialized pubkey it is paying to if it is. // // NOTE: This function ensures the public key is actually valid since the // compression algorithm requires valid pubkeys. It does not support hybrid // pubkeys. This means that even if the script has the correct form for a // pay-to-pubkey script, this function will only return true when it is paying // to a valid compressed or uncompressed pubkey. func isPubKey(script []byte) (bool, []byte) { // Pay-to-compressed-pubkey script. if len(script) == 35 && script[0] == txscript.OP_DATA_33 && script[34] == txscript.OP_CHECKSIG && (script[1] == 0x02 || script[1] == 0x03) { // Ensure the public key is valid. serializedPubKey := script[1:34] _, err := btcec.ParsePubKey(serializedPubKey, btcec.S256()) if err == nil { return true, serializedPubKey } } // Pay-to-uncompressed-pubkey script. if len(script) == 67 && script[0] == txscript.OP_DATA_65 && script[66] == txscript.OP_CHECKSIG && script[1] == 0x04 { // Ensure the public key is valid. serializedPubKey := script[1:66] _, err := btcec.ParsePubKey(serializedPubKey, btcec.S256()) if err == nil { return true, serializedPubKey } } return false, nil }
func fetchChanCommitKeys(nodeChanBucket *bolt.Bucket, channel *OpenChannel) error { // Construct the key which stores the commitment keys: ckk || channelID. // TODO(roasbeef): factor into func var bc bytes.Buffer if err := writeOutpoint(&bc, channel.ChanID); err != nil { return err } commitKey := make([]byte, len(commitKeys)+bc.Len()) copy(commitKey[:3], commitKeys) copy(commitKey[3:], bc.Bytes()) var err error keyBytes := nodeChanBucket.Get(commitKey) channel.TheirCommitKey, err = btcec.ParsePubKey(keyBytes[:33], btcec.S256()) if err != nil { return err } channel.OurCommitKey, err = btcec.ParsePubKey(keyBytes[33:], btcec.S256()) if err != nil { return err } return nil }
// TestSigCacheAddEvictEntry tests the eviction case where a new signature // triplet is added to a full signature cache which should trigger randomized // eviction, followed by adding the new element to the cache. func TestSigCacheAddEvictEntry(t *testing.T) { // Create a sigcache that can hold up to 100 entries. sigCacheSize := uint(100) sigCache := NewSigCache(sigCacheSize) // Fill the sigcache up with some random sig triplets. for i := uint(0); i < sigCacheSize; i++ { msg, sig, key, err := genRandomSig() if err != nil { t.Fatalf("unable to generate random signature test data") } sigCache.Add(*msg, sig, key) sigCopy, _ := btcec.ParseSignature(sig.Serialize(), btcec.S256()) keyCopy, _ := btcec.ParsePubKey(key.SerializeCompressed(), btcec.S256()) if !sigCache.Exists(*msg, sigCopy, keyCopy) { t.Errorf("previously added item not found in signature" + "cache") } } // The sigcache should now have sigCacheSize entries within it. if uint(len(sigCache.validSigs)) != sigCacheSize { t.Fatalf("sigcache should now have %v entries, instead it has %v", sigCacheSize, len(sigCache.validSigs)) } // Add a new entry, this should cause eviction of a randomly chosen // previous entry. msgNew, sigNew, keyNew, err := genRandomSig() if err != nil { t.Fatalf("unable to generate random signature test data") } sigCache.Add(*msgNew, sigNew, keyNew) // The sigcache should still have sigCache entries. if uint(len(sigCache.validSigs)) != sigCacheSize { t.Fatalf("sigcache should now have %v entries, instead it has %v", sigCacheSize, len(sigCache.validSigs)) } // The entry added above should be found within the sigcache. sigNewCopy, _ := btcec.ParseSignature(sigNew.Serialize(), btcec.S256()) keyNewCopy, _ := btcec.ParsePubKey(keyNew.SerializeCompressed(), btcec.S256()) if !sigCache.Exists(*msgNew, sigNewCopy, keyNewCopy) { t.Fatalf("previously added item not found in signature cache") } }
func fetchChanFundingInfo(nodeChanBucket *bolt.Bucket, channel *OpenChannel) error { var b bytes.Buffer if err := writeOutpoint(&b, channel.ChanID); err != nil { return err } fundTxnKey := make([]byte, len(fundingTxnKey)+b.Len()) copy(fundTxnKey[:3], fundingTxnKey) copy(fundTxnKey[3:], b.Bytes()) infoBytes := bytes.NewReader(nodeChanBucket.Get(fundTxnKey)) // TODO(roasbeef): can remove as channel ID *is* the funding point now. channel.FundingOutpoint = &wire.OutPoint{} if err := readOutpoint(infoBytes, channel.FundingOutpoint); err != nil { return err } ourKeyBytes, err := wire.ReadVarBytes(infoBytes, 0, 34, "") if err != nil { return err } channel.OurMultiSigKey, err = btcec.ParsePubKey(ourKeyBytes, btcec.S256()) if err != nil { return err } theirKeyBytes, err := wire.ReadVarBytes(infoBytes, 0, 34, "") if err != nil { return err } channel.TheirMultiSigKey, err = btcec.ParsePubKey(theirKeyBytes, btcec.S256()) if err != nil { return err } channel.FundingWitnessScript, err = wire.ReadVarBytes(infoBytes, 0, 520, "") if err != nil { return err } scratch := make([]byte, 8) if _, err := infoBytes.Read(scratch); err != nil { return err } unixSecs := byteOrder.Uint64(scratch) channel.CreationTime = time.Unix(int64(unixSecs), 0) return nil }
// RecvActTwo processes the second packet (act two) sent from the responder to // the initiator. A succesful processing of this packet authenticates the // initiator to the responder. func (b *BrontideMachine) RecvActTwo(actTwo [ActTwoSize]byte) error { var ( err error e [33]byte p [16]byte ) copy(e[:], actTwo[:33]) copy(p[:], actTwo[33:]) // e b.remoteEphemeral, err = btcec.ParsePubKey(e[:], btcec.S256()) if err != nil { return err } b.mixHash(b.remoteEphemeral.SerializeCompressed()) // ee s := btcec.GenerateSharedSecret(b.localEphemeral, b.remoteEphemeral) b.mixKey(s) if _, err := b.DecryptAndHash(p[:]); err != nil { return err } return nil }
// RecvActThree processes the final act (act three) sent from the initiator to // the responder. After processing this act, the responder learns of the // initiators's static public key. Decryption of the static key serves to // authenticate the initiator to the responder. func (b *BrontideMachine) RecvActThree(actThree [ActThreeSize]byte) error { var ( err error s [33 + 16]byte p [16]byte ) copy(s[:], actThree[:33+16]) copy(p[:], actThree[33+16:]) // s remotePub, err := b.DecryptAndHash(s[:]) if err != nil { return err } b.remoteStatic, err = btcec.ParsePubKey(remotePub, btcec.S256()) if err != nil { return err } // se se := btcec.GenerateSharedSecret(b.localEphemeral, b.remoteStatic) b.mixKey(se) if _, err := b.DecryptAndHash(p[:]); err != nil { return err } // With the final ECDH operation complete, derive the session sending // and receiving keys. b.split() return nil }
// Decode fully populates the target ForwardingMessage from the raw bytes // encoded within the io.Reader. In the case of any decoding errors, an error // will be returned. If the method successs, then the new OnionPacket is // ready to be processed by an instance of SphinxNode. func (f *OnionPacket) Decode(r io.Reader) error { var err error f.Header = &MixHeader{} var buf [1]byte if _, err := io.ReadFull(r, buf[:]); err != nil { return err } f.Header.Version = buf[0] var ephemeral [33]byte if _, err := io.ReadFull(r, ephemeral[:]); err != nil { return err } f.Header.EphemeralKey, err = btcec.ParsePubKey(ephemeral[:], btcec.S256()) if err != nil { return err } if _, err := io.ReadFull(r, f.Header.HeaderMAC[:]); err != nil { return err } if _, err := io.ReadFull(r, f.Header.RoutingInfo[:]); err != nil { return err } if _, err := io.ReadFull(r, f.Header.HopPayload[:]); err != nil { return err } return nil }
// RecvActOne processes the act one packet sent by the initiator. The responder // executes the mirroed actions to that of the initiator extending the // handshake digest and deriving a new shared secret based on a ECDH with the // initiator's ephemeral key and reponder's static key. func (b *BrontideMachine) RecvActOne(actOne [ActOneSize]byte) error { var ( err error e [33]byte p [16]byte ) copy(e[:], actOne[:33]) copy(p[:], actOne[33:]) // e b.remoteEphemeral, err = btcec.ParsePubKey(e[:], btcec.S256()) if err != nil { return err } b.mixHash(b.remoteEphemeral.SerializeCompressed()) // es s := btcec.GenerateSharedSecret(b.localStatic, b.remoteEphemeral) b.mixKey(s) // If the initiator doesn't know our static key, then this operation // will fail. if _, err := b.DecryptAndHash(p[:]); err != nil { return err } return nil }
// TestSigCacheAddMaxEntriesZeroOrNegative tests that if a sigCache is created // with a max size <= 0, then no entries are added to the sigcache at all. func TestSigCacheAddMaxEntriesZeroOrNegative(t *testing.T) { // Create a sigcache that can hold up to 0 entries. sigCache := NewSigCache(0) // Generate a random sigCache entry triplet. msg1, sig1, key1, err := genRandomSig() if err != nil { t.Errorf("unable to generate random signature test data") } // Add the triplet to the signature cache. sigCache.Add(*msg1, sig1, key1) // The generated triplet should not be found. sig1Copy, _ := btcec.ParseSignature(sig1.Serialize(), btcec.S256()) key1Copy, _ := btcec.ParsePubKey(key1.SerializeCompressed(), btcec.S256()) if sigCache.Exists(*msg1, sig1Copy, key1Copy) { t.Errorf("previously added signature found in sigcache, but" + "shouldn't have been") } // There shouldn't be any entries in the sigCache. if len(sigCache.validSigs) != 0 { t.Errorf("%v items found in sigcache, no items should have"+ "been added", len(sigCache.validSigs)) } }
// OpenChannel attempts to open a singly funded channel specified in the // request to a remote peer. func (r *rpcServer) OpenChannel(in *lnrpc.OpenChannelRequest, updateStream lnrpc.Lightning_OpenChannelServer) error { rpcsLog.Tracef("[openchannel] request to peerid(%v) "+ "allocation(us=%v, them=%v) numconfs=%v", in.TargetPeerId, in.LocalFundingAmount, in.RemoteFundingAmount, in.NumConfs) localFundingAmt := btcutil.Amount(in.LocalFundingAmount) remoteFundingAmt := btcutil.Amount(in.RemoteFundingAmount) nodepubKey, err := btcec.ParsePubKey(in.NodePubkey, btcec.S256()) if err != nil { return err } updateChan, errChan := r.server.OpenChannel(in.TargetPeerId, nodepubKey, localFundingAmt, remoteFundingAmt, in.NumConfs) var outpoint wire.OutPoint out: for { select { case err := <-errChan: rpcsLog.Errorf("unable to open channel to "+ "identityPub(%x) nor peerID(%v): %v", nodepubKey, in.TargetPeerId, err) return err case fundingUpdate := <-updateChan: rpcsLog.Tracef("[openchannel] sending update: %v", fundingUpdate) if err := updateStream.Send(fundingUpdate); err != nil { return err } // If a final channel open update is being sent, then // we can break out of our recv loop as we no longer // need to process any further updates. switch update := fundingUpdate.Update.(type) { case *lnrpc.OpenStatusUpdate_ChanOpen: chanPoint := update.ChanOpen.ChannelPoint h, _ := wire.NewShaHash(chanPoint.FundingTxid) outpoint = wire.OutPoint{ Hash: *h, Index: chanPoint.OutputIndex, } break out } case <-r.quit: return nil } } rpcsLog.Tracef("[openchannel] success peerid(%v), ChannelPoint(%v)", in.TargetPeerId, outpoint) return nil }
func deserializeLinkNode(r io.Reader) (*LinkNode, error) { var ( err error buf [8]byte ) node := &LinkNode{} if _, err := io.ReadFull(r, buf[:4]); err != nil { return nil, err } node.Network = wire.BitcoinNet(byteOrder.Uint32(buf[:4])) var pub [33]byte if _, err := io.ReadFull(r, pub[:]); err != nil { return nil, err } node.IdentityPub, err = btcec.ParsePubKey(pub[:], btcec.S256()) if err != nil { return nil, err } if _, err := io.ReadFull(r, buf[:]); err != nil { return nil, err } node.LastSeen = time.Unix(int64(byteOrder.Uint64(buf[:])), 0) if _, err := io.ReadFull(r, buf[:4]); err != nil { return nil, err } numAddrs := byteOrder.Uint32(buf[:4]) node.Addresses = make([]*net.TCPAddr, numAddrs) for i := uint32(0); i < numAddrs; i++ { addrString, err := wire.ReadVarString(r, 0) if err != nil { return nil, err } addr, err := net.ResolveTCPAddr("tcp", addrString) if err != nil { return nil, err } node.Addresses[i] = addr } return node, nil }
func fetchChanElkremState(nodeChanBucket *bolt.Bucket, channel *OpenChannel) error { var b bytes.Buffer if err := writeOutpoint(&b, channel.ChanID); err != nil { return err } elkremKey := make([]byte, len(elkremStateKey)+b.Len()) copy(elkremKey[:3], elkremStateKey) copy(elkremKey[3:], b.Bytes()) elkremStateBytes := bytes.NewReader(nodeChanBucket.Get(elkremKey)) revKeyBytes, err := wire.ReadVarBytes(elkremStateBytes, 0, 1000, "") if err != nil { return err } channel.TheirCurrentRevocation, err = btcec.ParsePubKey(revKeyBytes, btcec.S256()) if err != nil { return err } if _, err := elkremStateBytes.Read(channel.TheirCurrentRevocationHash[:]); err != nil { return err } // TODO(roasbeef): should be rederiving on fly, or encrypting on disk. senderBytes, err := wire.ReadVarBytes(elkremStateBytes, 0, 1000, "") if err != nil { return err } elkremRoot, err := wire.NewShaHash(senderBytes) if err != nil { return err } channel.LocalElkrem = elkrem.NewElkremSender(*elkremRoot) reciverBytes, err := wire.ReadVarBytes(elkremStateBytes, 0, 1000, "") if err != nil { return err } remoteE, err := elkrem.ElkremReceiverFromBytes(reciverBytes) if err != nil { return err } channel.RemoteElkrem = remoteE return nil }
// This example demonstrates encrypting a message for a public key that is first // parsed from raw bytes, then decrypting it using the corresponding private key. func Example_encryptMessage() { // Decode the hex-encoded pubkey of the recipient. pubKeyBytes, err := hex.DecodeString("04115c42e757b2efb7671c578530ec191a1" + "359381e6a71127a9d37c486fd30dae57e76dc58f693bd7e7010358ce6b165e483a29" + "21010db67ac11b1b51b651953d2") // uncompressed pubkey if err != nil { fmt.Println(err) return } pubKey, err := btcec.ParsePubKey(pubKeyBytes, btcec.S256()) if err != nil { fmt.Println(err) return } // Encrypt a message decryptable by the private key corresponding to pubKey message := "test message" ciphertext, err := btcec.Encrypt(pubKey, []byte(message)) if err != nil { fmt.Println(err) return } // Decode the hex-encoded private key. pkBytes, err := hex.DecodeString("a11b0a4e1a132305652ee7a8eb7848f6ad" + "5ea381e3ce20a2c086a2e388230811") if err != nil { fmt.Println(err) return } // note that we already have corresponding pubKey privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), pkBytes) // Try decrypting and verify if it's the same message. plaintext, err := btcec.Decrypt(privKey, ciphertext) if err != nil { fmt.Println(err) return } fmt.Println(string(plaintext)) // Output: // test message }
// TestSigCacheAddExists tests the ability to add, and later check the // existence of a signature triplet in the signature cache. func TestSigCacheAddExists(t *testing.T) { sigCache := NewSigCache(200) // Generate a random sigCache entry triplet. msg1, sig1, key1, err := genRandomSig() if err != nil { t.Errorf("unable to generate random signature test data") } // Add the triplet to the signature cache. sigCache.Add(*msg1, sig1, key1) // The previously added triplet should now be found within the sigcache. sig1Copy, _ := btcec.ParseSignature(sig1.Serialize(), btcec.S256()) key1Copy, _ := btcec.ParsePubKey(key1.SerializeCompressed(), btcec.S256()) if !sigCache.Exists(*msg1, sig1Copy, key1Copy) { t.Errorf("previously added item not found in signature cache") } }
// This example demonstrates verifying a secp256k1 signature against a public // key that is first parsed from raw bytes. The signature is also parsed from // raw bytes. func Example_verifySignature() { // Decode hex-encoded serialized public key. pubKeyBytes, err := hex.DecodeString("02a673638cb9587cb68ea08dbef685c" + "6f2d2a751a8b3c6f2a7e9a4999e6e4bfaf5") if err != nil { fmt.Println(err) return } pubKey, err := btcec.ParsePubKey(pubKeyBytes, btcec.S256()) if err != nil { fmt.Println(err) return } // Decode hex-encoded serialized signature. sigBytes, err := hex.DecodeString("30450220090ebfb3690a0ff115bb1b38b" + "8b323a667b7653454f1bccb06d4bbdca42c2079022100ec95778b51e707" + "1cb1205f8bde9af6592fc978b0452dafe599481c46d6b2e479") if err != nil { fmt.Println(err) return } signature, err := btcec.ParseSignature(sigBytes, btcec.S256()) if err != nil { fmt.Println(err) return } // Verify the signature for the message using the public key. message := "test message" messageHash := chainhash.DoubleHashB([]byte(message)) verified := signature.Verify(messageHash, pubKey) fmt.Println("Signature Verified?", verified) // Output: // Signature Verified? true }
func fetchChannelIDs(nodeChanBucket *bolt.Bucket, channel *OpenChannel) error { var ( err error b bytes.Buffer ) if err = writeOutpoint(&b, channel.ChanID); err != nil { return err } // Construct the id key: cid || channelID. idKey := make([]byte, len(chanIDKey)+b.Len()) copy(idKey[:3], chanIDKey) copy(idKey[3:], b.Bytes()) idBytes := nodeChanBucket.Get(idKey) channel.IdentityPub, err = btcec.ParsePubKey(idBytes, btcec.S256()) if err != nil { return err } return nil }
// ConnectPeer attempts to establish a connection to a remote peer. // TODO(roasbeef): also return pubkey and/or identity hash? func (r *rpcServer) ConnectPeer(ctx context.Context, in *lnrpc.ConnectPeerRequest) (*lnrpc.ConnectPeerResponse, error) { if in.Addr == nil { return nil, fmt.Errorf("need: lnc pubkeyhash@hostname") } pubkeyHex, err := hex.DecodeString(in.Addr.Pubkey) if err != nil { return nil, err } pubkey, err := btcec.ParsePubKey(pubkeyHex, btcec.S256()) if err != nil { return nil, err } host, err := net.ResolveTCPAddr("tcp", in.Addr.Host) if err != nil { return nil, err } peerAddr := &lnwire.NetAddress{ IdentityKey: pubkey, Address: host, ChainNet: activeNetParams.Net, } peerID, err := r.server.ConnectToPeer(peerAddr) if err != nil { rpcsLog.Errorf("(connectpeer): error connecting to peer: %v", err) return nil, err } // TODO(roasbeef): add pubkey return rpcsLog.Debugf("Connected to peer: %v", peerAddr.String()) return &lnrpc.ConnectPeerResponse{peerID}, nil }
// decompressScript returns the original script obtained by decompressing the // passed compressed script according to the domain specific compression // algorithm described above. // // NOTE: The script parameter must already have been proven to be long enough // to contain the number of bytes returned by decodeCompressedScriptSize or it // will panic. This is acceptable since it is only an internal function. func decompressScript(compressedPkScript []byte, version int32) []byte { // In practice this function will not be called with a zero-length or // nil script since the nil script encoding includes the length, however // the code below assumes the length exists, so just return nil now if // the function ever ends up being called with a nil script in the // future. if len(compressedPkScript) == 0 { return nil } // Decode the script size and examine it for the special cases. encodedScriptSize, bytesRead := deserializeVLQ(compressedPkScript) switch encodedScriptSize { // Pay-to-pubkey-hash script. The resulting script is: // <OP_DUP><OP_HASH160><20 byte hash><OP_EQUALVERIFY><OP_CHECKSIG> case cstPayToPubKeyHash: pkScript := make([]byte, 25) pkScript[0] = txscript.OP_DUP pkScript[1] = txscript.OP_HASH160 pkScript[2] = txscript.OP_DATA_20 copy(pkScript[3:], compressedPkScript[bytesRead:bytesRead+20]) pkScript[23] = txscript.OP_EQUALVERIFY pkScript[24] = txscript.OP_CHECKSIG return pkScript // Pay-to-script-hash script. The resulting script is: // <OP_HASH160><20 byte script hash><OP_EQUAL> case cstPayToScriptHash: pkScript := make([]byte, 23) pkScript[0] = txscript.OP_HASH160 pkScript[1] = txscript.OP_DATA_20 copy(pkScript[2:], compressedPkScript[bytesRead:bytesRead+20]) pkScript[22] = txscript.OP_EQUAL return pkScript // Pay-to-compressed-pubkey script. The resulting script is: // <OP_DATA_33><33 byte compressed pubkey><OP_CHECKSIG> case cstPayToPubKeyComp2, cstPayToPubKeyComp3: pkScript := make([]byte, 35) pkScript[0] = txscript.OP_DATA_33 pkScript[1] = byte(encodedScriptSize) copy(pkScript[2:], compressedPkScript[bytesRead:bytesRead+32]) pkScript[34] = txscript.OP_CHECKSIG return pkScript // Pay-to-uncompressed-pubkey script. The resulting script is: // <OP_DATA_65><65 byte uncompressed pubkey><OP_CHECKSIG> case cstPayToPubKeyUncomp4, cstPayToPubKeyUncomp5: // Change the leading byte to the appropriate compressed pubkey // identifier (0x02 or 0x03) so it can be decoded as a // compressed pubkey. This really should never fail since the // encoding ensures it is valid before compressing to this type. compressedKey := make([]byte, 33) compressedKey[0] = byte(encodedScriptSize - 2) copy(compressedKey[1:], compressedPkScript[1:]) key, err := btcec.ParsePubKey(compressedKey, btcec.S256()) if err != nil { return nil } pkScript := make([]byte, 67) pkScript[0] = txscript.OP_DATA_65 copy(pkScript[1:], key.SerializeUncompressed()) pkScript[66] = txscript.OP_CHECKSIG return pkScript } // When none of the special cases apply, the script was encoded using // the general format, so reduce the script size by the number of // special cases and return the unmodified script. scriptSize := int(encodedScriptSize - numSpecialScripts) pkScript := make([]byte, scriptSize) copy(pkScript, compressedPkScript[bytesRead:bytesRead+scriptSize]) return pkScript }
// generateSphinxPacket generates then encodes a sphinx packet which encodes // the onion route specified by the passed list of graph vertexes. The blob // returned from this function can immediately be included within an HTLC add // packet to be sent to the first hop within the route. func generateSphinxPacket(vertexes []graph.ID, paymentHash []byte) ([]byte, error) { // First convert all the vertexs from the routing table to in-memory // public key objects. These objects are necessary in order to perform // the series of ECDH operations required to construct the Sphinx // packet below. route := make([]*btcec.PublicKey, len(vertexes)) for i, vertex := range vertexes { vertexBytes, err := hex.DecodeString(vertex.String()) if err != nil { return nil, err } pub, err := btcec.ParsePubKey(vertexBytes, btcec.S256()) if err != nil { return nil, err } route[i] = pub } // Next we generate the per-hop payload which gives each node within // the route the necessary information (fees, CLTV value, etc) to // properly forward the payment. // TODO(roasbeef): properly set CLTV value, payment amount, and chain // within hop paylods. var hopPayloads [][]byte for i := 0; i < len(route); i++ { payload := bytes.Repeat([]byte{byte('A' + i)}, sphinx.HopPayloadSize) hopPayloads = append(hopPayloads, payload) } sessionKey, err := btcec.NewPrivateKey(btcec.S256()) if err != nil { return nil, err } // Next generate the onion routing packet which allows // us to perform privacy preserving source routing // across the network. sphinxPacket, err := sphinx.NewOnionPacket(route, sessionKey, hopPayloads, paymentHash) if err != nil { return nil, err } // Finally, encode Sphinx packet using it's wire represenation to be // included within the HTLC add packet. var onionBlob bytes.Buffer if err := sphinxPacket.Encode(&onionBlob); err != nil { return nil, err } rpcsLog.Tracef("[sendpayment] generated sphinx packet: %v", newLogClosure(func() string { // We unset the internal curve here in order to keep // the logs from getting noisy. sphinxPacket.Header.EphemeralKey.Curve = nil return spew.Sdump(sphinxPacket) })) return onionBlob.Bytes(), nil }
func TestChaining(t *testing.T) { tests := []struct { name string cc []byte origPrivateKey []byte nextPrivateKeyUncompressed []byte nextPrivateKeyCompressed []byte nextPublicKeyUncompressed []byte nextPublicKeyCompressed []byte }{ { name: "chaintest 1", cc: []byte("3318959fff419ab8b556facb3c429a86"), origPrivateKey: []byte("5ffc975976eaaa1f7b179f384ebbc053"), nextPrivateKeyUncompressed: []byte{ 0xd3, 0xfe, 0x2e, 0x96, 0x44, 0x12, 0x2d, 0xaa, 0x80, 0x8e, 0x36, 0x17, 0xb5, 0x9f, 0x8c, 0xd2, 0x72, 0x8c, 0xaf, 0xf1, 0xdb, 0xd6, 0x4a, 0x92, 0xd7, 0xc7, 0xee, 0x2b, 0x56, 0x34, 0xe2, 0x87, }, nextPrivateKeyCompressed: []byte{ 0x08, 0x56, 0x7a, 0x1b, 0x89, 0x56, 0x2e, 0xfa, 0xb4, 0x02, 0x59, 0x69, 0x10, 0xc3, 0x60, 0x1f, 0x34, 0xf0, 0x55, 0x02, 0x8a, 0xbf, 0x37, 0xf5, 0x22, 0x80, 0x9f, 0xd2, 0xe5, 0x42, 0x5b, 0x2d, }, nextPublicKeyUncompressed: []byte{ 0x04, 0xdd, 0x70, 0x31, 0xa5, 0xf9, 0x06, 0x70, 0xd3, 0x9a, 0x24, 0x5b, 0xd5, 0x73, 0xdd, 0xb6, 0x15, 0x81, 0x0b, 0x78, 0x19, 0xbc, 0xc8, 0x26, 0xc9, 0x16, 0x86, 0x73, 0xae, 0xe4, 0xc0, 0xed, 0x39, 0x81, 0xb4, 0x86, 0x2d, 0x19, 0x8c, 0x67, 0x9c, 0x93, 0x99, 0xf6, 0xd2, 0x3f, 0xd1, 0x53, 0x9e, 0xed, 0xbd, 0x07, 0xd6, 0x4f, 0xa9, 0x81, 0x61, 0x85, 0x46, 0x84, 0xb1, 0xa0, 0xed, 0xbc, 0xa7, }, nextPublicKeyCompressed: []byte{ 0x02, 0x2c, 0x48, 0x73, 0x37, 0x35, 0x74, 0x7f, 0x05, 0x58, 0xc1, 0x4e, 0x0d, 0x18, 0xc2, 0xbf, 0xcc, 0x83, 0xa2, 0x4d, 0x64, 0xab, 0xba, 0xea, 0xeb, 0x4c, 0xcd, 0x4c, 0x0c, 0x21, 0xc4, 0x30, 0x0f, }, }, } for _, test := range tests { // Create both uncompressed and compressed public keys for original // private key. origPubUncompressed := pubkeyFromPrivkey(test.origPrivateKey, false) origPubCompressed := pubkeyFromPrivkey(test.origPrivateKey, true) // Create next chained private keys, chained from both the uncompressed // and compressed pubkeys. nextPrivUncompressed, err := chainedPrivKey(test.origPrivateKey, origPubUncompressed, test.cc) if err != nil { t.Errorf("%s: Uncompressed chainedPrivKey failed: %v", test.name, err) return } nextPrivCompressed, err := chainedPrivKey(test.origPrivateKey, origPubCompressed, test.cc) if err != nil { t.Errorf("%s: Compressed chainedPrivKey failed: %v", test.name, err) return } // Verify that the new private keys match the expected values // in the test case. if !bytes.Equal(nextPrivUncompressed, test.nextPrivateKeyUncompressed) { t.Errorf("%s: Next private key (from uncompressed pubkey) does not match expected.\nGot: %s\nExpected: %s", test.name, spew.Sdump(nextPrivUncompressed), spew.Sdump(test.nextPrivateKeyUncompressed)) return } if !bytes.Equal(nextPrivCompressed, test.nextPrivateKeyCompressed) { t.Errorf("%s: Next private key (from compressed pubkey) does not match expected.\nGot: %s\nExpected: %s", test.name, spew.Sdump(nextPrivCompressed), spew.Sdump(test.nextPrivateKeyCompressed)) return } // Create the next pubkeys generated from the next private keys. nextPubUncompressedFromPriv := pubkeyFromPrivkey(nextPrivUncompressed, false) nextPubCompressedFromPriv := pubkeyFromPrivkey(nextPrivCompressed, true) // Create the next pubkeys by chaining directly off the original // pubkeys (without using the original's private key). nextPubUncompressedFromPub, err := chainedPubKey(origPubUncompressed, test.cc) if err != nil { t.Errorf("%s: Uncompressed chainedPubKey failed: %v", test.name, err) return } nextPubCompressedFromPub, err := chainedPubKey(origPubCompressed, test.cc) if err != nil { t.Errorf("%s: Compressed chainedPubKey failed: %v", test.name, err) return } // Public keys (used to generate the bitcoin address) MUST match. if !bytes.Equal(nextPubUncompressedFromPriv, nextPubUncompressedFromPub) { t.Errorf("%s: Uncompressed public keys do not match.", test.name) } if !bytes.Equal(nextPubCompressedFromPriv, nextPubCompressedFromPub) { t.Errorf("%s: Compressed public keys do not match.", test.name) } // Verify that all generated public keys match the expected // values in the test case. if !bytes.Equal(nextPubUncompressedFromPub, test.nextPublicKeyUncompressed) { t.Errorf("%s: Next uncompressed public keys do not match expected value.\nGot: %s\nExpected: %s", test.name, spew.Sdump(nextPubUncompressedFromPub), spew.Sdump(test.nextPublicKeyUncompressed)) return } if !bytes.Equal(nextPubCompressedFromPub, test.nextPublicKeyCompressed) { t.Errorf("%s: Next compressed public keys do not match expected value.\nGot: %s\nExpected: %s", test.name, spew.Sdump(nextPubCompressedFromPub), spew.Sdump(test.nextPublicKeyCompressed)) return } // Sign data with the next private keys and verify signature with // the next pubkeys. pubkeyUncompressed, err := btcec.ParsePubKey(nextPubUncompressedFromPub, btcec.S256()) if err != nil { t.Errorf("%s: Unable to parse next uncompressed pubkey: %v", test.name, err) return } pubkeyCompressed, err := btcec.ParsePubKey(nextPubCompressedFromPub, btcec.S256()) if err != nil { t.Errorf("%s: Unable to parse next compressed pubkey: %v", test.name, err) return } privkeyUncompressed := &btcec.PrivateKey{ PublicKey: *pubkeyUncompressed.ToECDSA(), D: new(big.Int).SetBytes(nextPrivUncompressed), } privkeyCompressed := &btcec.PrivateKey{ PublicKey: *pubkeyCompressed.ToECDSA(), D: new(big.Int).SetBytes(nextPrivCompressed), } data := "String to sign." sig, err := privkeyUncompressed.Sign([]byte(data)) if err != nil { t.Errorf("%s: Unable to sign data with next private key (chained from uncompressed pubkey): %v", test.name, err) return } ok := sig.Verify([]byte(data), privkeyUncompressed.PubKey()) if !ok { t.Errorf("%s: btcec signature verification failed for next keypair (chained from uncompressed pubkey).", test.name) return } sig, err = privkeyCompressed.Sign([]byte(data)) if err != nil { t.Errorf("%s: Unable to sign data with next private key (chained from compressed pubkey): %v", test.name, err) return } ok = sig.Verify([]byte(data), privkeyCompressed.PubKey()) if !ok { t.Errorf("%s: btcec signature verification failed for next keypair (chained from compressed pubkey).", test.name) return } } }
// readElement is a one-stop utility function to deserialize any datastructure // encoded using the serialization format of lnwire. func readElement(r io.Reader, element interface{}) error { var err error switch e := element.(type) { case *uint8: var b [1]uint8 if _, err := r.Read(b[:]); err != nil { return err } *e = b[0] case *uint16: var b [2]byte if _, err := io.ReadFull(r, b[:]); err != nil { return err } *e = binary.BigEndian.Uint16(b[:]) case *ErrorCode: var b [2]byte if _, err := io.ReadFull(r, b[:]); err != nil { return err } *e = ErrorCode(binary.BigEndian.Uint16(b[:])) case *CreditsAmount: var b [8]byte if _, err := io.ReadFull(r, b[:]); err != nil { return err } *e = CreditsAmount(int64(binary.BigEndian.Uint64(b[:]))) case *uint32: var b [4]byte if _, err := io.ReadFull(r, b[:]); err != nil { return err } *e = binary.BigEndian.Uint32(b[:]) case *uint64: var b [8]byte if _, err := io.ReadFull(r, b[:]); err != nil { return err } *e = binary.BigEndian.Uint64(b[:]) case *HTLCKey: var b [8]byte if _, err := io.ReadFull(r, b[:]); err != nil { return err } *e = HTLCKey(int64(binary.BigEndian.Uint64(b[:]))) case *btcutil.Amount: var b [8]byte if _, err := io.ReadFull(r, b[:]); err != nil { return err } *e = btcutil.Amount(int64(binary.BigEndian.Uint64(b[:]))) case **wire.ShaHash: var b wire.ShaHash if _, err := io.ReadFull(r, b[:]); err != nil { return err } *e = &b case **btcec.PublicKey: var b [33]byte if _, err = io.ReadFull(r, b[:]); err != nil { return err } pubKey, err := btcec.ParsePubKey(b[:], btcec.S256()) if err != nil { return err } *e = pubKey case *[]uint64: var numItems uint16 if err := readElement(r, &numItems); err != nil { return err } // if numItems > 65535 { // return fmt.Errorf("Too many items in []uint64") // } // Read the number of items var items []uint64 for i := uint16(0); i < numItems; i++ { var item uint64 err = readElement(r, &item) if err != nil { return err } items = append(items, item) } *e = items case *[]*btcec.Signature: var numSigs uint8 err = readElement(r, &numSigs) if err != nil { return err } if numSigs > 127 { return fmt.Errorf("Too many signatures!") } // Read that number of signatures var sigs []*btcec.Signature for i := uint8(0); i < numSigs; i++ { sig := new(btcec.Signature) err = readElement(r, &sig) if err != nil { return err } sigs = append(sigs, sig) } *e = sigs return nil case **btcec.Signature: sigBytes, err := wire.ReadVarBytes(r, 0, 73, "signature") if err != nil { return err } sig, err := btcec.ParseSignature(sigBytes, btcec.S256()) if err != nil { return err } *e = sig case *[][32]byte: // How many to read var sliceSize uint16 err = readElement(r, &sliceSize) if err != nil { return err } data := make([][32]byte, 0, sliceSize) // Append the actual for i := uint16(0); i < sliceSize; i++ { var element [32]byte err = readElement(r, &element) if err != nil { return err } data = append(data, element) } *e = data case *[32]byte: if _, err = io.ReadFull(r, e[:]); err != nil { return err } case *wire.BitcoinNet: var b [4]byte if _, err := io.ReadFull(r, b[:]); err != nil { return err } *e = wire.BitcoinNet(binary.BigEndian.Uint32(b[:])) return nil case *[]byte: bytes, err := wire.ReadVarBytes(r, 0, MaxSliceLength, "byte slice") if err != nil { return err } *e = bytes case *PkScript: pkScript, err := wire.ReadVarBytes(r, 0, 25, "pkscript") if err != nil { return err } *e = pkScript case *string: str, err := wire.ReadVarString(r, 0) if err != nil { return err } *e = str case *[]*wire.TxIn: // Read the size (1-byte number of txins) var numScripts uint8 if err := readElement(r, &numScripts); err != nil { return err } if numScripts > 127 { return fmt.Errorf("Too many txins") } // Append the actual TxIns txins := make([]*wire.TxIn, 0, numScripts) for i := uint8(0); i < numScripts; i++ { outpoint := new(wire.OutPoint) txin := wire.NewTxIn(outpoint, nil, nil) if err := readElement(r, &txin); err != nil { return err } txins = append(txins, txin) } *e = txins case **wire.TxIn: // Hash var h [32]byte if _, err = io.ReadFull(r, h[:]); err != nil { return err } hash, err := wire.NewShaHash(h[:]) if err != nil { return err } (*e).PreviousOutPoint.Hash = *hash // Index var idxBytes [4]byte _, err = io.ReadFull(r, idxBytes[:]) if err != nil { return err } (*e).PreviousOutPoint.Index = binary.BigEndian.Uint32(idxBytes[:]) return nil case **wire.OutPoint: // TODO(roasbeef): consolidate with above var h [32]byte if _, err = io.ReadFull(r, h[:]); err != nil { return err } hash, err := wire.NewShaHash(h[:]) if err != nil { return err } // Index var idxBytes [4]byte _, err = io.ReadFull(r, idxBytes[:]) if err != nil { return err } index := binary.BigEndian.Uint32(idxBytes[:]) *e = wire.NewOutPoint(hash, index) default: return fmt.Errorf("Unknown type in readElement: %T", e) } return nil }