mirror of
https://github.com/schollz/croc.git
synced 2025-10-11 13:21:00 +02:00
add pake
This commit is contained in:
parent
283bf704a2
commit
f6751dadb9
5 changed files with 332 additions and 104 deletions
|
@ -1,8 +1,6 @@
|
|||
package croc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"net/url"
|
||||
"os"
|
||||
|
@ -19,11 +17,15 @@ func (c *Croc) client(role int) (err error) {
|
|||
|
||||
// initialize the channel data for this client
|
||||
c.cs.Lock()
|
||||
c.cs.channel = newChannelData("")
|
||||
c.cs.channel.codePhrase = codePhrase
|
||||
c.cs.channel.Channel = codePhrase[:3]
|
||||
channel := codePhrase[:3]
|
||||
c.cs.channel.secret["pw"] = []byte(codePhrase[3:])
|
||||
if len(codePhrase) > 0 {
|
||||
if len(codePhrase) < 4 {
|
||||
err = errors.New("code phrase must be more than 4 characters")
|
||||
return
|
||||
}
|
||||
c.cs.channel.Channel = codePhrase[:3]
|
||||
c.cs.channel.passPhrase = codePhrase[3:]
|
||||
}
|
||||
c.cs.Unlock()
|
||||
|
||||
interrupt := make(chan os.Signal, 1)
|
||||
|
@ -136,30 +138,12 @@ func (c *Croc) processState(cd channelData) (err error) {
|
|||
c.cs.channel.TransferReady = true
|
||||
}
|
||||
c.cs.channel.Ports = cd.Ports
|
||||
for key := range cd.State {
|
||||
c.cs.channel.State[key] = cd.State[key]
|
||||
}
|
||||
// update the curve
|
||||
_, c.cs.channel.curve = getCurve(string(c.cs.channel.State["curve"]))
|
||||
|
||||
// TODO:
|
||||
// process the client state
|
||||
log.Debugf("processing client state: %+v", c.cs.channel.String2())
|
||||
if c.cs.channel.Role == 0 {
|
||||
// processing for sender
|
||||
|
||||
// *Does X not exist?*
|
||||
// - Generates X from pw.
|
||||
// - Update relay with X.
|
||||
if bytes.Equal(c.cs.channel.State["Xᵤ"], []byte{}) {
|
||||
random1 := make([]byte, 8)
|
||||
rand.Read(random1)
|
||||
random2 := make([]byte, 8)
|
||||
rand.Read(random2)
|
||||
c.cs.channel.State["Uᵤ"], c.cs.channel.State["Uᵥ"] = []byte(c.cs.channel.curve.ScalarBaseMult(random1))
|
||||
c.cs.channel.State["Vᵤ"], c.cs.channel.State["Vᵥ"] = []byte(c.cs.channel.curve.ScalarBaseMult(random2))
|
||||
}
|
||||
|
||||
} else if c.cs.channel.Role == 1 {
|
||||
// processing for recipient
|
||||
}
|
||||
|
|
|
@ -2,13 +2,13 @@ package croc
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/elliptic"
|
||||
"encoding/json"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/schollz/croc/src/pake"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -16,17 +16,6 @@ const (
|
|||
bufferSize = 1024
|
||||
)
|
||||
|
||||
var (
|
||||
// TODO:
|
||||
// MAKE EVERYTHING HERE PART OF THE CHANNELDATA!
|
||||
|
||||
// see PAKE setup for more info: https://play.golang.org/p/Sd0eTuuEIWu
|
||||
// availableStates are the varaibles available to the parties involved
|
||||
availableStates = []string{"curve", "Xᵤ", "Xᵥ", "Yᵤ", "Yᵥ", "Uᵤ", "Uᵥ", "Vᵤ", "Vᵥ", "Bcrypt(Ak)", "Bcrypt(Bk)"}
|
||||
// availableSecrets are the variables available only to a specific client, and not shared
|
||||
availableSecrets = []string{"pw", "Upwᵤ", "Upwᵥ", "α", "αᵤ", "αᵥ", "Vpwᵤ", "Vpwᵥ", "β", "gβᵤ", "gβᵥ", "BZᵤ", "BZᵥ", "BZᵤ", "BZᵥ", "AZᵤ", "AZᵥ", "AZᵤ", "AZᵥ", "Bk", "Ak"}
|
||||
)
|
||||
|
||||
type Croc struct {
|
||||
TcpPorts []string
|
||||
ServerPort string
|
||||
|
@ -74,9 +63,9 @@ type channelData struct {
|
|||
// Public
|
||||
// Channel is the name of the channel
|
||||
Channel string `json:"channel,omitempty"`
|
||||
// State contains state variables that are public to both parties
|
||||
// contains "curve", "h_k", "hh_k", "x", "y"
|
||||
State map[string][]byte `json:"state"`
|
||||
// Pake contains the information for
|
||||
// generating the session key over an insecure channel
|
||||
Pake pake.Pake
|
||||
// TransferReady is set by the relaying when both parties have connected
|
||||
// with their credentials
|
||||
TransferReady bool `json:"transfer_ready"`
|
||||
|
@ -97,11 +86,10 @@ type channelData struct {
|
|||
// codePhrase uses the first 3 characters to establish a channel, and the rest
|
||||
// to form the passphrase
|
||||
codePhrase string
|
||||
// secret are the computed secretes
|
||||
// contains "curve", "h_k", "hh_k", "x", "y"
|
||||
secret map[string][]byte
|
||||
// curve is the type of elliptic curve used for PAKE
|
||||
curve elliptic.Curve
|
||||
// passPhrase is used to generate a session key
|
||||
passPhrase string
|
||||
// sessionKey
|
||||
sessionKey []byte
|
||||
|
||||
// relay parameters
|
||||
// isopen determine whether or not the channel has been opened
|
||||
|
@ -152,17 +140,3 @@ type payload struct {
|
|||
// Close set to true when closing:
|
||||
Close bool `json:"close"`
|
||||
}
|
||||
|
||||
func newChannelData(name string) (cd *channelData) {
|
||||
cd = new(channelData)
|
||||
cd.Channel = name
|
||||
cd.State = make(map[string][]byte)
|
||||
for _, s := range availableStates {
|
||||
cd.State[s] = []byte{}
|
||||
}
|
||||
cd.secret = make(map[string][]byte)
|
||||
for _, s := range availableSecrets {
|
||||
cd.secret[s] = []byte{}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
261
src/pake/pake.go
Normal file
261
src/pake/pake.go
Normal file
|
@ -0,0 +1,261 @@
|
|||
package pake
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// Pake keeps public and private variables by
|
||||
// only transmitting between parties after marshaling
|
||||
//
|
||||
// This method follows
|
||||
// https://crypto.stanford.edu/~dabo/cryptobook/BonehShoup_0_4.pdf
|
||||
// Figure 21/15
|
||||
// http://www.lothar.com/~warner/MagicWormhole-PyCon2016.pdf
|
||||
// Slide 11
|
||||
|
||||
type Pake struct {
|
||||
// Public variables
|
||||
Role int
|
||||
Uᵤ, Uᵥ *big.Int
|
||||
Vᵤ, Vᵥ *big.Int
|
||||
Xᵤ, Xᵥ *big.Int
|
||||
Yᵤ, Yᵥ *big.Int
|
||||
HkA, HkB []byte
|
||||
|
||||
// Private variables
|
||||
curve elliptic.Curve
|
||||
pw []byte
|
||||
vpwᵤ, vpwᵥ *big.Int
|
||||
upwᵤ, upwᵥ *big.Int
|
||||
α []byte
|
||||
αᵤ, αᵥ *big.Int
|
||||
zᵤ, zᵥ *big.Int
|
||||
k []byte
|
||||
|
||||
isVerified bool
|
||||
}
|
||||
|
||||
func Init(pw []byte, role int, curve elliptic.Curve) (p *Pake, err error) {
|
||||
p = new(Pake)
|
||||
if role == 1 {
|
||||
p.Role = 1
|
||||
p.curve = curve
|
||||
p.pw = pw
|
||||
} else {
|
||||
p.Role = 0
|
||||
p.curve = curve
|
||||
p.pw = pw
|
||||
rand1 := make([]byte, 8)
|
||||
rand2 := make([]byte, 8)
|
||||
rand.Read(rand1)
|
||||
rand.Read(rand2)
|
||||
p.Uᵤ, p.Uᵥ = p.curve.ScalarBaseMult(rand1)
|
||||
p.Vᵤ, p.Vᵥ = p.curve.ScalarBaseMult(rand2)
|
||||
if !p.curve.IsOnCurve(p.Uᵤ, p.Uᵥ) {
|
||||
err = errors.New("U values not on curve")
|
||||
return
|
||||
}
|
||||
if !p.curve.IsOnCurve(p.Vᵤ, p.Vᵥ) {
|
||||
err = errors.New("V values not on curve")
|
||||
return
|
||||
}
|
||||
|
||||
// STEP: A computes X
|
||||
p.vpwᵤ, p.vpwᵥ = p.curve.ScalarMult(p.Vᵤ, p.Vᵥ, p.pw)
|
||||
p.upwᵤ, p.upwᵥ = p.curve.ScalarMult(p.Uᵤ, p.Uᵥ, p.pw)
|
||||
p.α = make([]byte, 8) // randomly generated secret
|
||||
rand.Read(p.α)
|
||||
p.αᵤ, p.αᵥ = p.curve.ScalarBaseMult(p.α)
|
||||
p.Xᵤ, p.Xᵥ = p.curve.Add(p.upwᵤ, p.upwᵥ, p.αᵤ, p.αᵥ) // "X"
|
||||
// now X should be sent to B
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Pake) Bytes() []byte {
|
||||
b, _ := json.Marshal(p)
|
||||
return b
|
||||
}
|
||||
|
||||
// Update will update itself
|
||||
func (p *Pake) Update(qBytes []byte) (err error) {
|
||||
var q *Pake
|
||||
err = json.Unmarshal(qBytes, &q)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if p.Role == q.Role {
|
||||
err = errors.New("can't have its own role")
|
||||
return
|
||||
}
|
||||
|
||||
if p.Role == 1 {
|
||||
// initial step for B
|
||||
if p.Uᵤ == nil && q.Uᵤ != nil {
|
||||
// copy over public variables
|
||||
p.Uᵤ, p.Uᵥ = q.Uᵤ, q.Uᵥ
|
||||
p.Vᵤ, p.Vᵥ = q.Vᵤ, q.Vᵥ
|
||||
p.Xᵤ, p.Xᵥ = q.Xᵤ, q.Xᵥ
|
||||
|
||||
// confirm that U,V are on curve
|
||||
if !p.curve.IsOnCurve(p.Uᵤ, p.Uᵥ) {
|
||||
err = errors.New("U values not on curve")
|
||||
return
|
||||
}
|
||||
if !p.curve.IsOnCurve(p.Vᵤ, p.Vᵥ) {
|
||||
err = errors.New("V values not on curve")
|
||||
return
|
||||
}
|
||||
|
||||
// STEP: B computes Y
|
||||
p.vpwᵤ, p.vpwᵥ = p.curve.ScalarMult(p.Vᵤ, p.Vᵥ, p.pw)
|
||||
p.upwᵤ, p.upwᵥ = p.curve.ScalarMult(p.Uᵤ, p.Uᵥ, p.pw)
|
||||
p.α = make([]byte, 8) // randomly generated secret
|
||||
rand.Read(p.α)
|
||||
p.αᵤ, p.αᵥ = p.curve.ScalarBaseMult(p.α)
|
||||
p.Yᵤ, p.Yᵥ = p.curve.Add(p.vpwᵤ, p.vpwᵥ, p.αᵤ, p.αᵥ) // "Y"
|
||||
// STEP: B computes Z
|
||||
p.zᵤ, p.zᵥ = p.curve.Add(p.Xᵤ, p.Xᵥ, p.upwᵤ, new(big.Int).Neg(p.upwᵥ))
|
||||
p.zᵤ, p.zᵥ = p.curve.ScalarMult(p.zᵤ, p.zᵥ, p.α)
|
||||
// STEP: B computes k
|
||||
// H(pw,id_P,id_Q,X,Y,Z)
|
||||
HB := sha256.New()
|
||||
HB.Write(p.pw)
|
||||
HB.Write(p.Xᵤ.Bytes())
|
||||
HB.Write(p.Xᵥ.Bytes())
|
||||
HB.Write(p.Yᵤ.Bytes())
|
||||
HB.Write(p.Yᵥ.Bytes())
|
||||
HB.Write(p.zᵤ.Bytes())
|
||||
HB.Write(p.zᵥ.Bytes())
|
||||
// STEP: B computes k
|
||||
p.k = HB.Sum(nil)
|
||||
p.HkB, err = hashK(p.k)
|
||||
} else if p.HkA == nil && q.HkA != nil {
|
||||
p.HkA = q.HkA
|
||||
// verify
|
||||
err = checkKHash(p.HkA, p.k)
|
||||
if err == nil {
|
||||
p.isVerified = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if p.HkB == nil && q.HkB != nil {
|
||||
p.HkB = q.HkB
|
||||
p.Yᵤ, p.Yᵥ = q.Yᵤ, q.Yᵥ
|
||||
|
||||
// STEP: A computes Z
|
||||
p.zᵤ, p.zᵥ = p.curve.Add(p.Yᵤ, p.Yᵥ, p.vpwᵤ, new(big.Int).Neg(p.vpwᵥ))
|
||||
p.zᵤ, p.zᵥ = p.curve.ScalarMult(p.zᵤ, p.zᵥ, p.α)
|
||||
// STEP: A computes k
|
||||
// H(pw,id_P,id_Q,X,Y,Z)
|
||||
HA := sha256.New()
|
||||
HA.Write(p.pw)
|
||||
HA.Write(p.Xᵤ.Bytes())
|
||||
HA.Write(p.Xᵥ.Bytes())
|
||||
HA.Write(p.Yᵤ.Bytes())
|
||||
HA.Write(p.Yᵥ.Bytes())
|
||||
HA.Write(p.zᵤ.Bytes())
|
||||
HA.Write(p.zᵥ.Bytes())
|
||||
p.k = HA.Sum(nil)
|
||||
p.HkA, err = hashK(p.k)
|
||||
|
||||
// STEP: A verifies that its session key matches B's
|
||||
// session key
|
||||
err = checkKHash(p.HkB, p.k)
|
||||
if err == nil {
|
||||
p.isVerified = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// hashK generates a bcrypt hash of the password using work factor 14.
|
||||
func hashK(k []byte) ([]byte, error) {
|
||||
return bcrypt.GenerateFromPassword(k, 14)
|
||||
}
|
||||
|
||||
// checkKHash securely compares a bcrypt hashed password with its possible
|
||||
// plaintext equivalent. Returns nil on success, or an error on failure.
|
||||
func checkKHash(hash, k []byte) error {
|
||||
return bcrypt.CompareHashAndPassword(hash, k)
|
||||
}
|
||||
|
||||
// IsVerified returns whether or not the k has been
|
||||
// generated AND it confirmed to be the same as partner
|
||||
func (p *Pake) IsVerified() bool {
|
||||
return p.isVerified
|
||||
}
|
||||
|
||||
// SessionKey is returned, unless it is not generated
|
||||
// in which is returns an error. This function does
|
||||
// not check if it is verifies.
|
||||
func (p *Pake) SessionKey() ([]byte, error) {
|
||||
var err error
|
||||
if p.k == nil {
|
||||
err = errors.New("session key not generated")
|
||||
}
|
||||
return p.k, err
|
||||
}
|
||||
|
||||
// func main() {
|
||||
// // PUBLIC PARAMETERS (computed once)
|
||||
// p256 := elliptic.P256()
|
||||
// Uᵤ, Uᵥ := p256.ScalarBaseMult([]byte{1, 2, 3, 4})
|
||||
// Vᵤ, Vᵥ := p256.ScalarBaseMult([]byte{1, 2, 3, 4})
|
||||
// // PRIVATE PARAMATERS
|
||||
// // pw
|
||||
// pw := []byte{1, 1} // shared weak secret
|
||||
// // PROTOCOL
|
||||
// // STEP: A computes X
|
||||
// upwᵤ, upwᵥ := p256.ScalarMult(Uᵤ, Uᵥ, pw)
|
||||
// α := []byte{1, 2, 3, 4} // randomly generated secret
|
||||
// αᵤ, αᵥ := p256.ScalarBaseMult(α)
|
||||
// Xᵤ, Xᵥ := p256.Add(upwᵤ, upwᵥ, αᵤ, αᵥ) // "X"
|
||||
// // STEP: A sends X
|
||||
// // STEP: B computes Y
|
||||
// vpwᵤ, vpwᵥ := p256.ScalarMult(Vᵤ, Vᵥ, pw)
|
||||
// β := []byte{1, 2, 3, 4} // randomly generated secret
|
||||
// gβᵤ, gβᵥ := p256.ScalarBaseMult(β)
|
||||
// Yᵤ, Yᵥ := p256.Add(vpwᵤ, vpwᵥ, gβᵤ, gβᵥ) // "Y"
|
||||
// // STEP: B computes Z
|
||||
// BZᵤ, BZᵥ := p256.Add(Xᵤ, Xᵥ, upwᵤ, new(big.Int).Neg(upwᵥ))
|
||||
// BZᵤ, BZᵥ = p256.ScalarMult(BZᵤ, BZᵥ, β)
|
||||
// // STEP: B computes k
|
||||
// // H(pw,id_P,id_Q,X,Y,Z)
|
||||
// HB := sha256.New()
|
||||
// HB.Write(pw)
|
||||
// HB.Write(Xᵤ.Bytes())
|
||||
// HB.Write(Xᵥ.Bytes())
|
||||
// HB.Write(Yᵤ.Bytes())
|
||||
// HB.Write(Yᵥ.Bytes())
|
||||
// HB.Write(BZᵤ.Bytes())
|
||||
// HB.Write(BZᵥ.Bytes())
|
||||
// Bk := HB.Sum(nil)
|
||||
// // STEP: B sends Y
|
||||
// // STEP: A computes Z
|
||||
// AZᵤ, AZᵥ := p256.Add(Yᵤ, Yᵥ, vpwᵤ, new(big.Int).Neg(vpwᵥ))
|
||||
// AZᵤ, AZᵥ = p256.ScalarMult(AZᵤ, AZᵥ, α)
|
||||
// // STEP: A computes k
|
||||
// // H(pw,id_P,id_Q,X,Y,Z)
|
||||
// HA := sha256.New()
|
||||
// HA.Write(pw)
|
||||
// HA.Write(Xᵤ.Bytes())
|
||||
// HA.Write(Xᵥ.Bytes())
|
||||
// HA.Write(Yᵤ.Bytes())
|
||||
// HA.Write(Yᵥ.Bytes())
|
||||
// HA.Write(AZᵤ.Bytes())
|
||||
// HA.Write(AZᵥ.Bytes())
|
||||
// Ak := HA.Sum(nil)
|
||||
// // END
|
||||
// // verify
|
||||
// fmt.Println(Ak)
|
||||
// fmt.Println(Bk)
|
||||
// }
|
55
src/pake/pake_test.go
Normal file
55
src/pake/pake_test.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
package pake
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPake(t *testing.T) {
|
||||
// successful (both have same k)
|
||||
// initialize A
|
||||
A, err := Init([]byte{1, 2, 3}, 0, elliptic.P256())
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, A.IsVerified())
|
||||
// initialize B
|
||||
B, err := Init([]byte{1, 2, 3}, 1, elliptic.P256())
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, B.IsVerified())
|
||||
// send A's stuff to B
|
||||
err = B.Update(A.Bytes())
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, B.IsVerified())
|
||||
// send B's stuff to A
|
||||
err = A.Update(B.Bytes())
|
||||
assert.Nil(t, err) // A validates
|
||||
assert.True(t, A.IsVerified())
|
||||
// send A's stuff back to B
|
||||
err = B.Update(A.Bytes())
|
||||
assert.Nil(t, err) // B validates
|
||||
assert.True(t, B.IsVerified())
|
||||
|
||||
// failure (both have different k)
|
||||
// initialize A
|
||||
A, err = Init([]byte{1, 2, 3}, 0, elliptic.P256())
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, A.IsVerified())
|
||||
// initialize B
|
||||
B, err = Init([]byte{4, 5, 6}, 1, elliptic.P256())
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, B.IsVerified())
|
||||
// send A's stuff to B
|
||||
err = B.Update(A.Bytes())
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, B.IsVerified())
|
||||
// send B's stuff to A
|
||||
err = A.Update(B.Bytes())
|
||||
assert.NotNil(t, err) // A validates
|
||||
assert.False(t, A.IsVerified())
|
||||
// send A's stuff back to B
|
||||
err = B.Update(A.Bytes())
|
||||
assert.NotNil(t, err)
|
||||
assert.False(t, B.IsVerified())
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue