This repository has been archived by the owner on May 12, 2018. It is now read-only.
Parameterize max hops and payload size - WIP #1
Merged
david415
merged 3 commits into
applied-mixnetworks:master
from
david415:parameterize_max_hops_payload_size.0
Jan 14, 2017
Merged
Changes from 1 commit
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,37 +16,27 @@ import ( | |
"github.com/david415/go-lioness" | ||
) | ||
|
||
const ( | ||
// The number of bytes produced by our CSPRG for the key stream | ||
// implementing our stream cipher to encrypt/decrypt the mix header. The | ||
// last 2 * securityParameter bytes are only used in order to generate/check | ||
// the MAC over the header. | ||
numStreamBytes = (2*NumMaxHops + 3) * securityParameter | ||
|
||
// NumMaxHops is the maximum path length. | ||
NumMaxHops = 5 | ||
|
||
// Fixed size of the the routing info. This consists of a 16 | ||
// byte address and a 16 byte HMAC for each hop of the route, | ||
// the first pair in cleartext and the following pairs | ||
// increasingly obfuscated. In case fewer than numMaxHops are | ||
// used, then the remainder is padded with null-bytes, also | ||
// obfuscated. | ||
routingInfoSize = pubKeyLen + (2*NumMaxHops-1)*securityParameter | ||
|
||
// HopPayloadSize is the per-hop payload size in the header | ||
HopPayloadSize = 32 | ||
type SphinxParams struct { | ||
// PayloadSize is the packet payload size | ||
PayloadSize = 1024 | ||
) | ||
PayloadSize int | ||
// NumMaxHops is the maximum path length. | ||
MaxHops int | ||
} | ||
|
||
func NewSphinxParams(maxHops, payloadSize int) *SphinxParams { | ||
return &SphinxParams{ | ||
PayloadSize: payloadSize, | ||
MaxHops: maxHops, | ||
} | ||
} | ||
|
||
// MixHeader contains the sphinx header but not the payload. | ||
// A version number is also included; TODO: make the version | ||
// number do something useful. | ||
type MixHeader struct { | ||
Version byte | ||
EphemeralKey [32]byte // alpha | ||
RoutingInfo [routingInfoSize]byte // beta | ||
RoutingInfo []byte // beta | ||
HeaderMAC [securityParameter]byte // gamma | ||
} | ||
|
||
|
@@ -65,6 +55,7 @@ func EncodeDestination(destination []byte) []byte { | |
|
||
// MixHeaderFactory builds mix headers | ||
type MixHeaderFactory struct { | ||
params *SphinxParams | ||
group *GroupCurve25519 | ||
blockCipher BlockCipher | ||
streamCipher StreamCipher | ||
|
@@ -74,8 +65,9 @@ type MixHeaderFactory struct { | |
} | ||
|
||
// NewMixHeaderFactory creates a new mix header factory | ||
func NewMixHeaderFactory(pki SphinxPKI, randReader io.Reader) *MixHeaderFactory { | ||
func NewMixHeaderFactory(params *SphinxParams, pki SphinxPKI, randReader io.Reader) *MixHeaderFactory { | ||
factory := MixHeaderFactory{ | ||
params: params, | ||
group: NewGroupCurve25519(), | ||
blockCipher: NewLionessBlockCipher(), | ||
streamCipher: &Chacha20Stream{}, | ||
|
@@ -92,8 +84,8 @@ func NewMixHeaderFactory(pki SphinxPKI, randReader io.Reader) *MixHeaderFactory | |
// a slice of 32byte shared secrets for each mix hop. | ||
func (f *MixHeaderFactory) BuildHeader(route [][16]byte, destination []byte, messageID [16]byte) (*MixHeader, [][32]byte, error) { | ||
routeLen := len(route) | ||
if routeLen > NumMaxHops { | ||
return nil, nil, fmt.Errorf("route length %d exceeds max hops %d", routeLen, NumMaxHops) | ||
if routeLen > f.params.MaxHops { | ||
return nil, nil, fmt.Errorf("route length %d exceeds max hops %d", routeLen, f.params.MaxHops) | ||
} | ||
var secretPoint [32]byte | ||
var err error | ||
|
@@ -102,7 +94,7 @@ func (f *MixHeaderFactory) BuildHeader(route [][16]byte, destination []byte, mes | |
return nil, nil, fmt.Errorf("faileed to generate curve25519 secret: %s", err) | ||
} | ||
|
||
paddingLen := (2*(NumMaxHops-routeLen)+2)*securityParameter - len(destination) | ||
paddingLen := (2*(f.params.MaxHops-routeLen)+2)*securityParameter - len(destination) | ||
padding := make([]byte, paddingLen) | ||
_, err = f.randReader.Read(padding) | ||
if err != nil { | ||
|
@@ -131,8 +123,9 @@ func (f *MixHeaderFactory) BuildHeader(route [][16]byte, destination []byte, mes | |
// compute the filler strings | ||
hopSize := 2 * securityParameter | ||
filler := make([]byte, (numHops-1)*hopSize) | ||
numStreamBytes := uint((2*f.params.MaxHops + 3) * securityParameter) | ||
for i := 1; i < numHops; i++ { | ||
min := (2*(NumMaxHops-i) + 3) * securityParameter | ||
min := (2*(f.params.MaxHops-i) + 3) * securityParameter | ||
streamKey := f.digest.DeriveStreamCipherKey(hopSharedSecrets[i-1]) | ||
streamBytes, err := f.streamCipher.GenerateStream(streamKey, numStreamBytes) | ||
if err != nil { | ||
|
@@ -147,7 +140,7 @@ func (f *MixHeaderFactory) BuildHeader(route [][16]byte, destination []byte, mes | |
copy(beta[len(destination):], messageID[:]) | ||
copy(beta[len(destination)+len(messageID):], padding) | ||
|
||
betaLen := uint((2*(NumMaxHops-routeLen) + 3) * securityParameter) | ||
betaLen := uint((2*(f.params.MaxHops-routeLen) + 3) * securityParameter) | ||
rhoKey := f.digest.DeriveStreamCipherKey(hopSharedSecrets[routeLen-1]) | ||
cipherStream, err := f.streamCipher.GenerateStream(rhoKey, betaLen) | ||
if err != nil { | ||
|
@@ -175,10 +168,10 @@ func (f *MixHeaderFactory) BuildHeader(route [][16]byte, destination []byte, mes | |
newBeta = []byte{} | ||
newBeta = append(newBeta, mixID[:]...) | ||
newBeta = append(newBeta, gamma[:]...) | ||
betaSlice := uint((2*NumMaxHops - 1) * securityParameter) | ||
betaSlice := uint((2*f.params.MaxHops - 1) * securityParameter) | ||
newBeta = append(newBeta, prevBeta[:betaSlice]...) | ||
rhoKey := f.digest.DeriveStreamCipherKey(hopSharedSecrets[i]) | ||
streamSlice := uint((2*NumMaxHops + 1) * securityParameter) | ||
streamSlice := uint((2*f.params.MaxHops + 1) * securityParameter) | ||
cipherStream, err := f.streamCipher.GenerateStream(rhoKey, streamSlice) | ||
if err != nil { | ||
return nil, nil, fmt.Errorf("stream cipher failure: %s", err) | ||
|
@@ -194,12 +187,10 @@ func (f *MixHeaderFactory) BuildHeader(route [][16]byte, destination []byte, mes | |
} | ||
prevBeta = newBeta | ||
} | ||
finalBeta := [routingInfoSize]byte{} | ||
copy(finalBeta[:], newBeta[:]) | ||
header := &MixHeader{ | ||
Version: 0x01, | ||
EphemeralKey: hopEphemeralPubKeys[0], | ||
RoutingInfo: finalBeta, | ||
RoutingInfo: newBeta, | ||
HeaderMAC: gamma, | ||
} | ||
return header, hopSharedSecrets, nil | ||
|
@@ -210,12 +201,12 @@ func (f *MixHeaderFactory) BuildHeader(route [][16]byte, destination []byte, mes | |
// addressed to the final destination. | ||
type SphinxPacket struct { | ||
Header *MixHeader | ||
Payload [PayloadSize]byte // delta | ||
Payload []byte // delta | ||
} | ||
|
||
// NewOnionReply is used to create an SphinxPacket with a specified header and payload. | ||
// This is used by the WrapReply to create Single Use Reply Blocks | ||
func NewOnionReply(header *MixHeader, payload [PayloadSize]byte) *SphinxPacket { | ||
func NewOnionReply(header *MixHeader, payload []byte) *SphinxPacket { | ||
return &SphinxPacket{ | ||
Header: header, | ||
Payload: payload, | ||
|
@@ -224,6 +215,7 @@ func NewOnionReply(header *MixHeader, payload [PayloadSize]byte) *SphinxPacket { | |
|
||
// SphinxPacketFactory builds onion packets | ||
type SphinxPacketFactory struct { | ||
params *SphinxParams | ||
group *GroupCurve25519 | ||
blockCipher BlockCipher | ||
pki SphinxPKI | ||
|
@@ -232,29 +224,31 @@ type SphinxPacketFactory struct { | |
} | ||
|
||
// NewSphinxPacketFactory creates a new onion packet factory | ||
func NewSphinxPacketFactory(pki SphinxPKI, randReader io.Reader) *SphinxPacketFactory { | ||
func NewSphinxPacketFactory(params *SphinxParams, pki SphinxPKI, randReader io.Reader) *SphinxPacketFactory { | ||
factory := SphinxPacketFactory{ | ||
params: params, | ||
group: NewGroupCurve25519(), | ||
blockCipher: NewLionessBlockCipher(), | ||
pki: pki, | ||
randReader: randReader, | ||
mixHeaderFactory: NewMixHeaderFactory(pki, randReader), | ||
mixHeaderFactory: NewMixHeaderFactory(params, pki, randReader), | ||
} | ||
return &factory | ||
} | ||
|
||
// BuildForwardSphinxPacket builds a forward oniion packet | ||
func (f *SphinxPacketFactory) BuildForwardSphinxPacket(route [][16]byte, destination [16]byte, payload []byte) (*SphinxPacket, error) { | ||
|
||
if len(payload)+1+len(destination) > PayloadSize-2 { // XXX AddPadding has a 2 byte overhead | ||
return nil, fmt.Errorf("wrong sized payload %d > %d", len(payload), PayloadSize) | ||
// AddPadding has a 2 byte overhead | ||
if len(payload)+1+len(destination) > f.params.PayloadSize-2 { | ||
return nil, fmt.Errorf("wrong sized payload %d > %d", len(payload), f.params.PayloadSize) | ||
} | ||
addrPayload := []byte{} | ||
addrPayload = append(addrPayload, bytes.Repeat([]byte{0}, 16)...) | ||
encodedDest := EncodeDestination(destination[:]) | ||
addrPayload = append(addrPayload, encodedDest...) | ||
addrPayload = append(addrPayload, payload[:]...) | ||
paddedPayload, err := AddPadding(addrPayload, PayloadSize) | ||
paddedPayload, err := AddPadding(addrPayload, f.params.PayloadSize) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
@@ -287,11 +281,9 @@ func (f *SphinxPacketFactory) BuildForwardSphinxPacket(route [][16]byte, destina | |
return nil, err | ||
} | ||
} | ||
newPayload := [PayloadSize]byte{} | ||
copy(newPayload[:], delta) | ||
return &SphinxPacket{ | ||
Header: mixHeader, | ||
Payload: newPayload, | ||
Payload: delta, | ||
}, nil | ||
} | ||
|
||
|
@@ -307,6 +299,7 @@ type ReplyBlock struct { | |
|
||
// SphinxClient is used for sending and receiving messages | ||
type SphinxClient struct { | ||
params *SphinxParams | ||
id []byte | ||
keysmap map[[16]byte][][]byte | ||
pki SphinxPKI | ||
|
@@ -316,7 +309,7 @@ type SphinxClient struct { | |
} | ||
|
||
// NewSphinxClient creates a new SphinxClient | ||
func NewSphinxClient(pki SphinxPKI, id []byte, randReader io.Reader) (*SphinxClient, error) { | ||
func NewSphinxClient(params *SphinxParams, pki SphinxPKI, id []byte, randReader io.Reader) (*SphinxClient, error) { | ||
var newID [4]byte | ||
if id == nil { | ||
_, err := randReader.Read(newID[:]) | ||
|
@@ -326,12 +319,13 @@ func NewSphinxClient(pki SphinxPKI, id []byte, randReader io.Reader) (*SphinxCli | |
id = []byte(fmt.Sprintf("Client %x", newID)) | ||
} | ||
return &SphinxClient{ | ||
params: params, | ||
id: id, | ||
keysmap: make(map[[16]byte][][]byte), | ||
pki: pki, | ||
randReader: randReader, | ||
blockCipher: NewLionessBlockCipher(), | ||
mixHeaderFactory: NewMixHeaderFactory(pki, randReader), | ||
mixHeaderFactory: NewMixHeaderFactory(params, pki, randReader), | ||
}, nil | ||
} | ||
|
||
|
@@ -422,16 +416,14 @@ func (c *SphinxClient) WrapReply(surb *ReplyBlock, message []byte) ([]byte, *Sph | |
} | ||
prefixedMessage := make([]byte, securityParameter) | ||
prefixedMessage = append(prefixedMessage, message...) | ||
paddedPayload, err := AddPadding(prefixedMessage, PayloadSize) | ||
paddedPayload, err := AddPadding(prefixedMessage, c.params.PayloadSize) | ||
if err != nil { | ||
return nil, nil, fmt.Errorf("WrapReply failed to add padding: %v", err) | ||
} | ||
ciphertextPayload, err := c.blockCipher.Encrypt(key, paddedPayload) | ||
if err != nil { | ||
return nil, nil, fmt.Errorf("WrapReply failed to encrypt payload: %v", err) | ||
} | ||
var payload [PayloadSize]byte | ||
copy(payload[:], ciphertextPayload) | ||
onionPacket := NewOnionReply(surb.Header, payload) | ||
return surb.FirstHop[:], onionPacket, nil | ||
sphinxPacket := NewOnionReply(surb.Header, ciphertextPayload) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Worth keeping an assertion that the payload is the expected There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm yeah... somewhere there should be such an assertion |
||
return surb.FirstHop[:], sphinxPacket, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needed? can't clients directly just
&SphinxParams{payloadSize, maxHops}
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indeed. perhaps that's better.