Merge branch 'transcript' of cwtch.im/tapir into master

This commit is contained in:
Dan Ballard 2019-08-26 12:43:32 -07:00 committad av Gogs
förälder f38efc6bf2 ce17c81d7a
incheckning ac6e44b09a
2 ändrade filer med 80 tillägg och 22 borttagningar

Visa fil

@ -8,8 +8,6 @@ import (
"git.openprivacy.ca/openprivacy/libricochet-go/log"
"git.openprivacy.ca/openprivacy/libricochet-go/utils"
"golang.org/x/crypto/ed25519"
"golang.org/x/crypto/sha3"
"time"
)
// AuthMessage is exchanged between peers to obtain the Auth Capability
@ -59,36 +57,45 @@ func (ea AuthApp) Init(connection tapir.Connection) {
key := primitives.Perform3DH(connection.ID(), &ephemeralIdentity, remoteAuthMessage.LongTermPublicKey, remoteAuthMessage.EphemeralPublicKey, connection.IsOutbound())
connection.SetEncryptionKey(key)
// Wait to Sync (we need to ensure that both the Local and Remote server have turned encryption on
// otherwise our next Send will fail.
time.Sleep(time.Second)
// We just successfully unmarshaled both of these, so we can safely ignore the err return from these functions.
challengeRemote, _ := json.Marshal(remoteAuthMessage)
challengeLocal, _ := json.Marshal(authMessage)
// TODO: Replace this with proper transcript primitive
challengeRemote, err := json.Marshal(remoteAuthMessage)
if err != nil {
connection.Close()
return
}
challengeLocal, err := json.Marshal(authMessage)
if err != nil {
connection.Close()
return
}
challenge := sha3.New512()
// Define canonical labels so both sides of the
var outboundAuthMessage []byte
var outboundHostname string
var inboundAuthMessage []byte
var inboundHostname string
if connection.IsOutbound() {
challenge.Write(challengeLocal)
challenge.Write(challengeRemote)
outboundHostname = connection.ID().Hostname()
inboundHostname = utils.GetTorV3Hostname(remoteAuthMessage.LongTermPublicKey)
outboundAuthMessage = challengeLocal
inboundAuthMessage = challengeRemote
} else {
challenge.Write(challengeRemote)
challenge.Write(challengeLocal)
outboundHostname = utils.GetTorV3Hostname(remoteAuthMessage.LongTermPublicKey)
inboundHostname = connection.ID().Hostname()
outboundAuthMessage = challengeRemote
inboundAuthMessage = challengeLocal
}
// Derive a challenge from the transcript of the public parameters of this authentication protocol
var transcript *primitives.Transcript
transcript = primitives.NewTranscript("tapir-auth-" + outboundHostname + "-" + inboundHostname)
transcript.AddToTranscript("outbound-challenge", outboundAuthMessage)
transcript.AddToTranscript("inbound-challenge", inboundAuthMessage)
challengeBytes := transcript.CommitToTranscript("3dh-auth-challenge")
// If debug is turned on we will dump the transcript to log.
// There is nothing sensitive in this transcript
log.Debugf("Transcript: %s", transcript.OutputTranscriptToAudit())
// Since we have set the encryption key on the connection the connection will encrypt any messages we send with that key
// To test that the remote peer has done the same we calculate a challenge hash based on the transcript so far and send it to them
// We expect the remote to do the same, and compare the two.
// If successful we extend our auth capability to the connection and reassert the hostname.
challengeBytes := challenge.Sum([]byte{})
// We note that the only successful scenario here requires that the remote peer have successfully derived the same
// encryption key and the same transcript challenge.
connection.Send(challengeBytes)
remoteChallenge := connection.Expect()
if subtle.ConstantTimeCompare(challengeBytes, remoteChallenge) == 1 {

51
primitives/transcript.go Normal file
Visa fil

@ -0,0 +1,51 @@
package primitives
import (
"fmt"
"golang.org/x/crypto/sha3"
"hash"
)
// Transcript implements a transcript of a public coin argument.
//
// We have the following goals:
// - Provide a consisted transcript API for our zero knowledge protocols
// - Allow sequential proofs over a common transcript (ensuring a single proof cannot be extracted standalone)
// - produce an auditable human-readable transcript.
//
// The design of this API was inspired by Merlin: https://docs.rs/crate/merlin/
//
// At some point we might want to extend this to be compatible with Merlin transcripts, built on STROBE
type Transcript struct {
hash hash.Hash
transcript string
}
// NewTranscript creates a new Transcript with the given Label, the label should be unique to the application
func NewTranscript(label string) *Transcript {
transcript := new(Transcript)
transcript.hash = sha3.New256()
transcript.AddToTranscript("protocol", []byte(label))
return transcript
}
// AddToTranscript appends a value to the transcript with the given label
// This binds the given data to the label.
func (t *Transcript) AddToTranscript(label string, b []byte) {
op := fmt.Sprintf("%s (%d) %x;", label, len(b), b)
t.transcript = fmt.Sprintf("%v\n%v", t.transcript, op)
t.hash.Write([]byte(op))
}
// OutputTranscriptToAudit outputs a human-readable copy of the transcript so far.
func (t Transcript) OutputTranscriptToAudit() string {
return t.transcript
}
// CommitToTranscript generates a challenge based on the current transcript, it also commits the challenge to the transcript.
func (t *Transcript) CommitToTranscript(label string) []byte {
t.AddToTranscript("commit", []byte(label))
b := t.hash.Sum([]byte{})
t.AddToTranscript(label, b)
return b
}