0
0
Fork 0
mirror of https://github.com/schollz/croc.git synced 2025-10-11 13:21:00 +02:00
This commit is contained in:
Zack Scholl 2018-06-29 17:29:11 -07:00
parent 283bf704a2
commit f6751dadb9
5 changed files with 332 additions and 104 deletions

View file

@ -1,46 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at zack.scholl@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View file

@ -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
}

View file

@ -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
View 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
View 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())
}