forked from ooni/minivpn
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
534 additions
and
0 deletions.
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 |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package optional | ||
|
||
import ( | ||
"reflect" | ||
|
||
"github.com/ooni/minivpn/internal/runtimex" | ||
) | ||
|
||
// Value is an optional value. The zero value of this structure | ||
// is equivalent to the one you get when calling [None]. | ||
type Value[T any] struct { | ||
// indirect is the indirect pointer to the value. | ||
indirect *T | ||
} | ||
|
||
// None constructs an empty value. | ||
func None[T any]() Value[T] { | ||
return Value[T]{nil} | ||
} | ||
|
||
// Some constructs a some value unless T is a pointer and points to | ||
// nil, in which case [Some] is equivalent to [None]. | ||
func Some[T any](value T) Value[T] { | ||
v := Value[T]{} | ||
maybeSetFromValue(&v, value) | ||
return v | ||
} | ||
|
||
// maybeSetFromValue sets the underlying value unless T is a pointer | ||
// and points to nil in which case we set the Value to be empty. | ||
func maybeSetFromValue[T any](v *Value[T], value T) { | ||
rv := reflect.ValueOf(value) | ||
if rv.Type().Kind() == reflect.Pointer && rv.IsNil() { | ||
v.indirect = nil | ||
return | ||
} | ||
v.indirect = &value | ||
} | ||
|
||
// IsNone returns whether this [Value] is empty. | ||
func (v Value[T]) IsNone() bool { | ||
return v.indirect == nil | ||
} | ||
|
||
// Unwrap returns the underlying value or panics. In case of | ||
// panic, the value passed to panic is an error. | ||
func (v Value[T]) Unwrap() T { | ||
runtimex.Assert(!v.IsNone(), "is none") | ||
return *v.indirect | ||
} | ||
|
||
// UnwrapOr returns the fallback if the [Value] is empty. | ||
func (v Value[T]) UnwrapOr(fallback T) T { | ||
if v.IsNone() { | ||
return fallback | ||
} | ||
return v.Unwrap() | ||
} |
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 |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package session | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"sync" | ||
) | ||
|
||
// DataChannelKey represents a pair of key sources that have been negotiated | ||
// over the control channel, and from which we will derive local and remote | ||
// keys for encryption and decrption over the data channel. The index refers to | ||
// the short key_id that is passed in the lower 3 bits if a packet header. | ||
// The setup of the keys for a given data channel (that is, for every key_id) | ||
// is made by expanding the keysources using the prf function. | ||
// Do note that we are not yet implementing key renegotiation - but the index | ||
// is provided for convenience when/if we support that in the future. | ||
type DataChannelKey struct { | ||
index uint32 | ||
ready bool | ||
local *KeySource | ||
remote *KeySource | ||
mu sync.Mutex | ||
} | ||
|
||
// errDayaChannelKey is a [DataChannelKey] error. | ||
var errDataChannelKey = errors.New("bad data-channel key") | ||
|
||
// Local returns the local [KeySource] | ||
func (dck *DataChannelKey) Local() *KeySource { | ||
return dck.local | ||
} | ||
|
||
// Remote returns the local [KeySource] | ||
func (dck *DataChannelKey) Remote() *KeySource { | ||
return dck.remote | ||
} | ||
|
||
// AddRemoteKey adds the server keySource to our dataChannelKey. This makes the | ||
// dataChannelKey ready to be used. | ||
func (dck *DataChannelKey) AddRemoteKey(k *KeySource) error { | ||
dck.mu.Lock() | ||
defer dck.mu.Unlock() | ||
if dck.ready { | ||
return fmt.Errorf("%w: %s", errDataChannelKey, "cannot overwrite remote key slot") | ||
} | ||
dck.remote = k | ||
dck.ready = true | ||
return nil | ||
} | ||
|
||
// Ready returns whether the [DataChannelKey] is ready. | ||
func (dck *DataChannelKey) Ready() bool { | ||
dck.mu.Lock() | ||
defer dck.mu.Unlock() | ||
return dck.ready | ||
} |
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 |
---|---|---|
@@ -0,0 +1,4 @@ | ||
// Package session keeps state for the application, including internal state | ||
// transitions for the OpenVPN protocol, data channel keys, and all the state | ||
// pertaining to the different packet counters. | ||
package session |
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 |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package session | ||
|
||
import ( | ||
"bytes" | ||
"errors" | ||
"fmt" | ||
|
||
"github.com/ooni/minivpn/internal/bytesx" | ||
) | ||
|
||
// randomFn mocks the function to generate random bytes. | ||
var randomFn = bytesx.GenRandomBytes | ||
|
||
// errRandomBytes is the error returned when we cannot generate random bytes. | ||
var errRandomBytes = errors.New("error generating random bytes") | ||
|
||
// KeySource contains random data to generate keys. | ||
type KeySource struct { | ||
R1 [32]byte | ||
R2 [32]byte | ||
PreMaster [48]byte | ||
} | ||
|
||
// Bytes returns the byte representation of a [KeySource]. | ||
func (k *KeySource) Bytes() []byte { | ||
buf := &bytes.Buffer{} | ||
buf.Write(k.PreMaster[:]) | ||
buf.Write(k.R1[:]) | ||
buf.Write(k.R2[:]) | ||
return buf.Bytes() | ||
} | ||
|
||
// NewKeySource constructs a new [KeySource]. | ||
func NewKeySource() (*KeySource, error) { | ||
random1, err := randomFn(32) | ||
if err != nil { | ||
return nil, fmt.Errorf("%w: %s", errRandomBytes, err.Error()) | ||
} | ||
|
||
var r1, r2 [32]byte | ||
var preMaster [48]byte | ||
copy(r1[:], random1) | ||
|
||
random2, err := randomFn(32) | ||
if err != nil { | ||
return nil, fmt.Errorf("%w: %s", errRandomBytes, err.Error()) | ||
} | ||
copy(r2[:], random2) | ||
|
||
random3, err := randomFn(48) | ||
if err != nil { | ||
return nil, fmt.Errorf("%w: %s", errRandomBytes, err.Error()) | ||
} | ||
copy(preMaster[:], random3) | ||
return &KeySource{ | ||
R1: r1, | ||
R2: r2, | ||
PreMaster: preMaster, | ||
}, nil | ||
} |
Oops, something went wrong.