Skip to content

Commit

Permalink
change how we calculate inline peer IDs.
Browse files Browse the repository at this point in the history
Instead of trying to get fancy with multicodecs and such, just inline the public
key's protobuf.

Eventually, we can try to switch to true IPLD but this is, IMO, a step in the
right direction.
  • Loading branch information
Stebalien authored and rargulati committed Apr 13, 2018
1 parent 933e17b commit 278da24
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 122 deletions.
6 changes: 0 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,6 @@
"name": "go-multihash",
"version": "1.0.7"
},
{
"author": "whyrusleeping",
"hash": "QmNhVCV7kgAqW6oh6n8m9myxT2ksGPhVZnHkzkBvR5qg2d",
"name": "go-multicodec-packed",
"version": "0.1.0"
},
{
"author": "mr-tron",
"hash": "QmWFAMPqsEyUX7gDUsRVmMWz59FxSpJ1b2v6bJ1yYzo7jY",
Expand Down
110 changes: 24 additions & 86 deletions peer.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,28 @@
package peer

import (
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"strings"

logging "github.com/ipfs/go-log" // ID represents the identity of a peer.
ic "github.com/libp2p/go-libp2p-crypto"
b58 "github.com/mr-tron/base58/base58"
mc "github.com/multiformats/go-multicodec-packed"
mh "github.com/multiformats/go-multihash"
)

// MaxInlineKeyLength is the maximum length a key can be for it to be inlined in
// the peer ID.
//
// * When `len(pubKey.Bytes()) <= MaxInlineKeyLength`, the peer ID is the
// identity multihash hash of the public key.
// * When `len(pubKey.Bytes()) > MaxInlineKeyLength`, the peer ID is the
// sha2-256 multihash of the public key.
const MaxInlineKeyLength = 42

var log = logging.Logger("peer")

// ID is a libp2p peer identity.
type ID string

// Pretty returns a b58-encoded string of the ID
Expand Down Expand Up @@ -75,77 +82,23 @@ func (id ID) MatchesPublicKey(pk ic.PubKey) bool {
return oid == id
}

var MultihashDecodeErr = errors.New("unable to decode multihash")
var MultihashCodecErr = errors.New("unexpected multihash codec")
var MultihashLengthErr = errors.New("unexpected multihash length")
var CodePrefixErr = errors.New("unexpected code prefix")

func (id ID) ExtractEd25519PublicKey() (ic.PubKey, error) {
// ed25519 pubkey identity format
// <identity mc><length (2 + 32 = 34)><ed25519-pub mc><ed25519 pubkey>
// <0x00 ><0x22 ><0xed01 ><ed25519 pubkey>

var nilPubKey ic.PubKey

// Decode multihash
// ExtractPublicKey attempts to extract the public key from an ID
//
// This method returns nil, nil if the peer ID looks valid but it can't extract
// the public key.
func (id ID) ExtractPublicKey() (ic.PubKey, error) {
decoded, err := mh.Decode([]byte(id))
if err != nil {
return nilPubKey, MultihashDecodeErr
return nil, err
}

// Check ID multihash codec
if decoded.Code != mh.ID {
return nilPubKey, MultihashCodecErr
}

// Check multihash length
if decoded.Length != 2+32 {
return nilPubKey, MultihashLengthErr
}

// Split prefix
code, pubKeyBytes := mc.SplitPrefix(decoded.Digest)

// Check ed25519 code
if code != mc.Ed25519Pub {
return nilPubKey, CodePrefixErr
}

// Unmarshall public key
pubKey, err := ic.UnmarshalEd25519PublicKey(pubKeyBytes)
if err != nil {
// Should never occur because of the check decoded.Length != 2+32
return nilPubKey, fmt.Errorf("Unexpected error unmarshalling Ed25519 public key")
}

return pubKey, nil
}

// ExtractPublicKey attempts to extract the public key from an ID
func (id ID) ExtractPublicKey() ic.PubKey {
var pk ic.PubKey

// Try extract ed25519 pubkey
pk, err := id.ExtractEd25519PublicKey()
if err != nil {
log.Info(err, id)
}

if pk != nil {
return pk
return nil, nil
}

// Try extract other type of pubkey
/*pk, err = id.Extract...PublicKey()
pk, err := ic.UnmarshalPublicKey(decoded.Digest)
if err != nil {
log.Error(err, id)
return nil, err
}
if pk != nil {
return pk
}*/

return pk
return pk, nil
}

// IDFromString cast a string to ID type, and validate
Expand Down Expand Up @@ -200,26 +153,11 @@ func IDFromPublicKey(pk ic.PubKey) (ID, error) {
if err != nil {
return "", err
}
hash, _ := mh.Sum(b, mh.SHA2_256, -1)
return ID(hash), nil
}

// IDFromEd25519PublicKey returns the Peer ID corresponding to Id25519 pk
func IDFromEd25519PublicKey(pk ic.PubKey) (ID, error) {
b, err := pk.Bytes()
if err != nil {
return "", err
}

// Build the ed25519 public key multi-codec
Ed25519PubMultiCodec := make([]byte, 2)
binary.PutUvarint(Ed25519PubMultiCodec, uint64(mc.Ed25519Pub))

hash, err := mh.Sum(append(Ed25519PubMultiCodec, b[len(b)-32:]...), mh.ID, 34)
if err != nil {
return "", err
var alg uint64 = mh.SHA2_256
if len(b) <= MaxInlineKeyLength {
alg = mh.ID
}

hash, _ := mh.Sum(b, alg, -1)
return ID(hash), nil
}

Expand Down
56 changes: 26 additions & 30 deletions peer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,51 +160,47 @@ func TestPublicKeyExtraction(t *testing.T) {
t.Fatal(err)
}

id, err := IDFromEd25519PublicKey(originalPub)
id, err := IDFromPublicKey(originalPub)
if err != nil {
t.Fatal(err)
}

extractedPub := id.ExtractPublicKey()
if !originalPub.Equals(extractedPub) {
extractedPub, err := id.ExtractPublicKey()
if err != nil {
t.Fatal(err)
}
if extractedPub == nil {
t.Fatal("failed to extract public key")
}
if !originalPub.Equals(extractedPub) {
t.Fatal("extracted public key doesn't match")
}

// Test invalid multihash (invariant of the type of public key)
if ID("").ExtractPublicKey() != nil {
t.Fatal("Expecting a nil public key")
pk, err := ID("").ExtractPublicKey()
if err == nil {
t.Fatal("expected an error")
}
if pk != nil {
t.Fatal("expected a nil public key")
}
}

func TestEd25519PublicKeyExtraction(t *testing.T) {
randomKey := make([]byte, 32)
_, err := rand.Read(randomKey)
// Shouldn't work for, e.g. RSA keys (too large)

_, rsaPub, err := ic.GenerateKeyPair(ic.RSA, 2048)
if err != nil {
t.Fatal(err)
}

// Error case 1: Invalid multihash
_, err = ID("").ExtractEd25519PublicKey()
if err != MultihashDecodeErr {
t.Fatal("Error case 1: Expected an error")
}

// Error case 2: Non-ID multihash
_, err = ID(append([]byte{0x01 /* != 0x00 (id) */, 0x22, 0xed, 0x01}, randomKey...)).ExtractEd25519PublicKey()
if err != MultihashCodecErr {
t.Fatal("Error case 2: Expecting an error")
rsaId, err := IDFromPublicKey(rsaPub)
if err != nil {
t.Fatal(err)
}

// Error case 3: Non-34 multihash length
_, err = ID(append([]byte{0x00, 0x23 /* 35 = 34 + 1 != 35 */, 0xed, 0x01, 0x00 /* extra byte */}, randomKey...)).ExtractEd25519PublicKey()
if err != MultihashLengthErr {
t.Fatal("Error case 3: Expecting an error")
extractedRsaPub, err := rsaId.ExtractPublicKey()
if err != nil {
t.Fatal(err)
}

// Error case 4: Non-ed25519 code
_, err = ID(append([]byte{0x00, 0x22, 0xef /* != 0xed */, 0x01}, randomKey...)).ExtractEd25519PublicKey()
if err != CodePrefixErr {
t.Fatal("Error case 4: Expecting an error")
if extractedRsaPub != nil {
t.Fatal("expected to fail to extract public key from rsa ID")
}
}

Expand Down

0 comments on commit 278da24

Please sign in to comment.