forked from cwtch.im/tapir
Merge branch 'transcript' of cwtch.im/tapir into master
This commit is contained in:
commit
ac6e44b09a
|
@ -8,8 +8,6 @@ import (
|
||||||
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||||
"git.openprivacy.ca/openprivacy/libricochet-go/utils"
|
"git.openprivacy.ca/openprivacy/libricochet-go/utils"
|
||||||
"golang.org/x/crypto/ed25519"
|
"golang.org/x/crypto/ed25519"
|
||||||
"golang.org/x/crypto/sha3"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// AuthMessage is exchanged between peers to obtain the Auth Capability
|
// 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())
|
key := primitives.Perform3DH(connection.ID(), &ephemeralIdentity, remoteAuthMessage.LongTermPublicKey, remoteAuthMessage.EphemeralPublicKey, connection.IsOutbound())
|
||||||
connection.SetEncryptionKey(key)
|
connection.SetEncryptionKey(key)
|
||||||
|
|
||||||
// Wait to Sync (we need to ensure that both the Local and Remote server have turned encryption on
|
// We just successfully unmarshaled both of these, so we can safely ignore the err return from these functions.
|
||||||
// otherwise our next Send will fail.
|
challengeRemote, _ := json.Marshal(remoteAuthMessage)
|
||||||
time.Sleep(time.Second)
|
challengeLocal, _ := json.Marshal(authMessage)
|
||||||
|
|
||||||
// TODO: Replace this with proper transcript primitive
|
// Define canonical labels so both sides of the
|
||||||
challengeRemote, err := json.Marshal(remoteAuthMessage)
|
var outboundAuthMessage []byte
|
||||||
if err != nil {
|
var outboundHostname string
|
||||||
connection.Close()
|
var inboundAuthMessage []byte
|
||||||
return
|
var inboundHostname string
|
||||||
}
|
|
||||||
challengeLocal, err := json.Marshal(authMessage)
|
|
||||||
if err != nil {
|
|
||||||
connection.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
challenge := sha3.New512()
|
|
||||||
|
|
||||||
if connection.IsOutbound() {
|
if connection.IsOutbound() {
|
||||||
challenge.Write(challengeLocal)
|
outboundHostname = connection.ID().Hostname()
|
||||||
challenge.Write(challengeRemote)
|
inboundHostname = utils.GetTorV3Hostname(remoteAuthMessage.LongTermPublicKey)
|
||||||
|
outboundAuthMessage = challengeLocal
|
||||||
|
inboundAuthMessage = challengeRemote
|
||||||
} else {
|
} else {
|
||||||
challenge.Write(challengeRemote)
|
outboundHostname = utils.GetTorV3Hostname(remoteAuthMessage.LongTermPublicKey)
|
||||||
challenge.Write(challengeLocal)
|
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
|
// 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
|
// 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.
|
// 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.
|
// 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)
|
connection.Send(challengeBytes)
|
||||||
remoteChallenge := connection.Expect()
|
remoteChallenge := connection.Expect()
|
||||||
if subtle.ConstantTimeCompare(challengeBytes, remoteChallenge) == 1 {
|
if subtle.ConstantTimeCompare(challengeBytes, remoteChallenge) == 1 {
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
Loading…
Reference in New Issue