mirror of
https://git.numenor-labs.us/dsfx.git
synced 2025-04-29 08:10:34 +00:00
initial commit
This commit is contained in:
commit
dc892f6ac4
9
main.go
Normal file
9
main.go
Normal file
@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.Println("Hello, World!")
|
||||
}
|
58
pkg/dcrypto/aead.go
Normal file
58
pkg/dcrypto/aead.go
Normal file
@ -0,0 +1,58 @@
|
||||
package dcrypto
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Encrypt uses AES-GCM to encrypt the given plaintext with the given key.
|
||||
func Encrypt(key, plaintext []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nonce := make([]byte, gcm.NonceSize())
|
||||
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)
|
||||
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
// Decrypt uses AES-GCM to decrypt the given ciphertext with the given key.
|
||||
func Decrypt(key, ciphertext []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nonceSize := gcm.NonceSize()
|
||||
if len(ciphertext) < nonceSize {
|
||||
return nil, errors.New("ciphertext too short")
|
||||
}
|
||||
|
||||
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
|
||||
|
||||
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return plaintext, nil
|
||||
}
|
31
pkg/dcrypto/ecdh.go
Normal file
31
pkg/dcrypto/ecdh.go
Normal file
@ -0,0 +1,31 @@
|
||||
package dcrypto
|
||||
|
||||
import (
|
||||
"crypto/ecdh"
|
||||
"crypto/rand"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultDHCurve is the default elliptic curve used for signing.
|
||||
DefaultDHCurve = ecdh.P384
|
||||
)
|
||||
|
||||
// GenerateDHKey generates a new ECDH private key for key exchange.
|
||||
func GenerateDHKey() (*ecdh.PrivateKey, error) {
|
||||
return DefaultDHCurve().GenerateKey(rand.Reader)
|
||||
}
|
||||
|
||||
// ComputeDHSecret computes the shared secret from the private key and the public key.
|
||||
func ComputeDHSecret(priv *ecdh.PrivateKey, pub *ecdh.PublicKey) ([]byte, error) {
|
||||
return priv.ECDH(pub)
|
||||
}
|
||||
|
||||
// ExportDHPublicKey exports the public key as a byte slice.
|
||||
func ExportDHPublicKey(pub *ecdh.PublicKey) ([]byte, error) {
|
||||
return pub.Bytes(), nil
|
||||
}
|
||||
|
||||
// ImportDHPublicKey imports the public key from a byte slice.
|
||||
func ImportDHPublicKey(data []byte) (*ecdh.PublicKey, error) {
|
||||
return DefaultDHCurve().NewPublicKey(data)
|
||||
}
|
62
pkg/dcrypto/ecdsa.go
Normal file
62
pkg/dcrypto/ecdsa.go
Normal file
@ -0,0 +1,62 @@
|
||||
package dcrypto
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultSigningCurve is the default elliptic curve used for signing.
|
||||
DefaultSigningCurve = elliptic.P384
|
||||
)
|
||||
|
||||
// GenerateSigningKey generates a new ECDSA private key for signing.
|
||||
func GenerateSigningKey() (*ecdsa.PrivateKey, error) {
|
||||
return ecdsa.GenerateKey(DefaultSigningCurve(), rand.Reader)
|
||||
}
|
||||
|
||||
// Sign signs the data with the private key.
|
||||
func Sign(priv *ecdsa.PrivateKey, data []byte) ([]byte, error) {
|
||||
return ecdsa.SignASN1(rand.Reader, priv, data)
|
||||
}
|
||||
|
||||
// Verify verifies the signature of the data with the public key.
|
||||
func Verify(pub *ecdsa.PublicKey, data, signature []byte) bool {
|
||||
return ecdsa.VerifyASN1(pub, data, signature)
|
||||
}
|
||||
|
||||
// ExportPublicSigningKey exports the public key as a byte slice.
|
||||
func ExportPublicSigningKey(pub *ecdsa.PublicKey) ([]byte, error) {
|
||||
data := struct {
|
||||
N []byte `json:"n"`
|
||||
PubX []byte `json:"pub_x"`
|
||||
PubY []byte `json:"pub_y"`
|
||||
}{
|
||||
N: pub.Curve.Params().N.Bytes(),
|
||||
PubX: pub.X.Bytes(),
|
||||
PubY: pub.Y.Bytes(),
|
||||
}
|
||||
return json.Marshal(data)
|
||||
}
|
||||
|
||||
// ImportPublicSigningKey imports the public key from a byte slice.
|
||||
func ImportPublicSigningKey(pubBytes []byte) (*ecdsa.PublicKey, error) {
|
||||
var data struct {
|
||||
N []byte `json:"n"`
|
||||
PubX []byte `json:"pub_x"`
|
||||
PubY []byte `json:"pub_y"`
|
||||
}
|
||||
if err := json.Unmarshal(pubBytes, &data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params := new(ecdsa.PublicKey)
|
||||
params.Curve = DefaultSigningCurve()
|
||||
params.X = new(big.Int).SetBytes(data.PubX)
|
||||
params.Y = new(big.Int).SetBytes(data.PubY)
|
||||
|
||||
return params, nil
|
||||
}
|
11
pkg/dcrypto/hkdf.go
Normal file
11
pkg/dcrypto/hkdf.go
Normal file
@ -0,0 +1,11 @@
|
||||
package dcrypto
|
||||
|
||||
import (
|
||||
"crypto/hkdf"
|
||||
"crypto/sha256"
|
||||
)
|
||||
|
||||
// Key creates a key from the given secret and salt using HKDF.
|
||||
func Key(secret, salt []byte) ([]byte, error) {
|
||||
return hkdf.Key(sha256.New, secret, salt, "", 32)
|
||||
}
|
12
pkg/dcrypto/rand.go
Normal file
12
pkg/dcrypto/rand.go
Normal file
@ -0,0 +1,12 @@
|
||||
package dcrypto
|
||||
|
||||
import "crypto/rand"
|
||||
|
||||
// Challenge generates a random challenge of n bytes.
|
||||
func Challenge(n int) []byte {
|
||||
challenge := make([]byte, n)
|
||||
if _, err := rand.Read(challenge); err != nil || len(challenge) != n {
|
||||
return nil
|
||||
}
|
||||
return challenge
|
||||
}
|
55
pkg/encoding/vwb/vwb.go
Normal file
55
pkg/encoding/vwb/vwb.go
Normal file
@ -0,0 +1,55 @@
|
||||
// Package vwb provides utilities for working with variable-width byte slices,
|
||||
// usually in the context of network requests.
|
||||
package vwb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
func debugf(msg string, args ...any) {
|
||||
fmt.Printf("(encoding/vwb): "+msg, args...)
|
||||
}
|
||||
|
||||
// Encode ...
|
||||
func Encode(w io.Writer, input []byte) error {
|
||||
debugf("encoding %d bytes\n", len(input))
|
||||
buf := make([]byte, 0, len(input)+2)
|
||||
|
||||
// Write the length of the input
|
||||
buf = append(buf, byte(len(input)>>8), byte(len(input)))
|
||||
// Write the input
|
||||
buf = append(buf, input...)
|
||||
|
||||
debugf("writing %d bytes\n", len(buf))
|
||||
// Write the buffer to the writer
|
||||
if _, err := w.Write(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode ...
|
||||
func Decode(r io.Reader) ([]byte, error) {
|
||||
debugf("beginning decode\n")
|
||||
var lenbuf [2]byte
|
||||
// Read the length of the input
|
||||
if _, err := io.ReadFull(r, lenbuf[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
debugf("read %d bytes for length\n", lenbuf)
|
||||
// Calculate the length of the input
|
||||
length := int(lenbuf[0])<<8 | int(lenbuf[1])
|
||||
|
||||
debugf("reading %d byte sized message\n", length)
|
||||
// Read the input
|
||||
data := make([]byte, length)
|
||||
if _, err := io.ReadFull(r, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
debugf("done\n")
|
||||
return data, nil
|
||||
}
|
347
pkg/handshake/handshake.go
Normal file
347
pkg/handshake/handshake.go
Normal file
@ -0,0 +1,347 @@
|
||||
package handshake
|
||||
|
||||
import (
|
||||
"crypto/ecdh"
|
||||
"crypto/ecdsa"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"koti.casa/numenor-labs/dsfx/pkg/dcrypto"
|
||||
"koti.casa/numenor-labs/dsfx/pkg/encoding/vwb"
|
||||
)
|
||||
|
||||
func debugf(msg string, args ...any) {
|
||||
fmt.Printf("(debug): "+msg, args...)
|
||||
}
|
||||
|
||||
// An Actor represents an entity that can participate in the handshake process.
|
||||
// In this model, there are two actors: Alice and Bob. Alice represents the
|
||||
// client role and Bob represents the server role. Each actor has a ecdsa private
|
||||
// key used for identification, and a tcp address that they are reachable at.
|
||||
type Actor interface {
|
||||
// Identity represents the identity of the actor. It is used to sign messages
|
||||
// on behalf of the actor during the handshake process. The actor may choose
|
||||
// to use a well known key here in order to be recognized by other actors.
|
||||
// It is also valid to return an ephemeral key here if the actor wishes to
|
||||
// remain anonymous with the remote actor.
|
||||
IdentityKey() *ecdsa.PrivateKey
|
||||
|
||||
// Address returns the address of the actor. This is used when listening for
|
||||
// incoming connections, and when dialing out to other actors.
|
||||
Address() *net.TCPAddr
|
||||
}
|
||||
|
||||
// State represents the state of the handshake process. It should be passed to
|
||||
// and from the various functions in the handshake process.
|
||||
type State struct {
|
||||
EphemeralKey *ecdh.PrivateKey
|
||||
}
|
||||
|
||||
// InitiateHandshake initiates the handshake process between the given actor
|
||||
// and the remote actor.
|
||||
func InitiateHandshake(actor Actor, conn io.ReadWriter, rpub *ecdsa.PublicKey) ([]byte, error) {
|
||||
// ------------------------------------------------------------------------
|
||||
// Step 1: Ephemeral Key Exchange To Server
|
||||
|
||||
debugf("client: creating dh key\n")
|
||||
// Create a new ECDH private key for the actor.
|
||||
ourDHKey, err := dcrypto.GenerateDHKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
debugf("client: exporting dh key\n")
|
||||
// Export the public key of the actor's ECDH private key.
|
||||
ourDHKeyRaw, err := dcrypto.ExportDHPublicKey(ourDHKey.PublicKey())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
debugf("client: sending dh key\n")
|
||||
// Write the actor's public key to the connection.
|
||||
err = vwb.Encode(conn, ourDHKeyRaw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Step 2: Ephemeral Key Exchange From Server
|
||||
|
||||
debugf("client: waiting for server's dh key\n")
|
||||
// Read the remote actor's public key from the connection.
|
||||
remoteDHKeyRaw, err := vwb.Decode(conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
debugf("client: importing server's dh key\n")
|
||||
// Import the remote actor's public key.
|
||||
remoteDHKey, err := dcrypto.ImportDHPublicKey(remoteDHKeyRaw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Step 3: Client Authentication
|
||||
|
||||
debugf("client: exporting public signing key\n")
|
||||
// Export the public key of the actor's signing key.
|
||||
ourPublicKeyRaw, err := dcrypto.ExportPublicSigningKey(&actor.IdentityKey().PublicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
debugf("client: exporting remote public signing key\n")
|
||||
remotePublicKeyRaw, err := dcrypto.ExportPublicSigningKey(rpub)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
debugf("client: creating authentication message\n")
|
||||
// Construct the message that will be signed by the client.
|
||||
// This message is formatted as follows:
|
||||
// rlt + sha256(ae + be)
|
||||
// This binds both the client and server's long term public keys to the
|
||||
// ephemeral keys that were exchanged in the previous step. This creates a
|
||||
// verifiable link between both parties and the ephemeral keys used to
|
||||
// establish the shared secret.
|
||||
authMessage, err := buildMessage(ourDHKey.PublicKey(), remoteDHKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
debugf("client: signing authentication message\n")
|
||||
// Sign the message with the actor's private key.
|
||||
signature, err := dcrypto.Sign(actor.IdentityKey(), authMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
debugf("client: encoding %d bytes of public key\n", len(ourPublicKeyRaw))
|
||||
debugf("client: encoding %d bytes of signature\n", len(signature))
|
||||
|
||||
plaintext := make([]byte, 0, len(ourPublicKeyRaw)+len(signature))
|
||||
plaintext = append(plaintext, ourPublicKeyRaw...)
|
||||
plaintext = append(plaintext, signature...)
|
||||
|
||||
debugf("client: computing shared secret\n")
|
||||
// Compute the shared secret between the actor and the remote actor.
|
||||
sharedSecret, err := dcrypto.ComputeDHSecret(ourDHKey, remoteDHKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
debugf("client: deriving key from shared secret\n")
|
||||
// Derive a key from the shared secret using HKDF.
|
||||
derivedKey, err := dcrypto.Key(sharedSecret, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
debugf("client: encrypting authentication message\n")
|
||||
// Encrypt the message with the derived key.
|
||||
boxedMsg, err := dcrypto.Encrypt(derivedKey, plaintext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
debugf("client: sending authentication message\n")
|
||||
// Write the boxed message to the connection.
|
||||
err = vwb.Encode(conn, boxedMsg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Step 4: Server Authentication
|
||||
|
||||
debugf("client: waiting for server's authentication message\n")
|
||||
// Read the authentication message from the connection.
|
||||
authMessageRaw, err := vwb.Decode(conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
debugf("client: decrypting authentication message\n")
|
||||
// Decrypt the authentication message with the derived key.
|
||||
plaintext, err = dcrypto.Decrypt(derivedKey, authMessageRaw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
debugf("client: importing server's signing key\n")
|
||||
// The server authentication is just verifying the signature it created of
|
||||
// the client authentication message.
|
||||
remotePublicKey, err := dcrypto.ImportPublicSigningKey(remotePublicKeyRaw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
debugf("client: verifying server's signature\n")
|
||||
if !dcrypto.Verify(remotePublicKey, authMessage, plaintext) {
|
||||
return nil, errors.New("failed to verify server's signature")
|
||||
}
|
||||
|
||||
debugf("client: handshake complete\n")
|
||||
return sharedSecret, nil
|
||||
}
|
||||
|
||||
// AcceptHandshake accepts a handshake from the given actor and connection. It
|
||||
// returns the shared secret between the actor and the remote actor.
|
||||
func AcceptHandshake(actor Actor, conn io.ReadWriter) ([]byte, error) {
|
||||
// ------------------------------------------------------------------------
|
||||
// Step 1: Ephemeral Key Exchange From Client
|
||||
|
||||
debugf("server: starting handshake process\n")
|
||||
// Read the remote actor's public key from the connection.
|
||||
remoteDHKeyRaw, err := vwb.Decode(conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
debugf("server: importing client's dh key\n")
|
||||
// Import the remote actor's public key.
|
||||
remoteDHKey, err := dcrypto.ImportDHPublicKey(remoteDHKeyRaw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Step 2: Ephemeral Key Exchange To Client
|
||||
|
||||
debugf("server: creating dh key\n")
|
||||
// Create a new ECDH private key for the actor.
|
||||
ourDHKey, err := dcrypto.GenerateDHKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
debugf("server: exporting dh key\n")
|
||||
// Export the public key of the actor's ECDH private key.
|
||||
ourDHKeyRaw, err := dcrypto.ExportDHPublicKey(ourDHKey.PublicKey())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
debugf("server: sending dh key\n")
|
||||
// Write the actor's public key to the connection.
|
||||
err = vwb.Encode(conn, ourDHKeyRaw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Step 3: Server Authentication
|
||||
|
||||
debugf("server: waiting for client's authentication message\n")
|
||||
// Read the authentication message from the connection.
|
||||
authMessageRaw, err := vwb.Decode(conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
debugf("server: computing shared secret\n")
|
||||
// Decrypt the authentication message with the derived key.
|
||||
sharedSecret, err := dcrypto.ComputeDHSecret(ourDHKey, remoteDHKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
debugf("server: deriving key from shared secret\n")
|
||||
// Derive a key from the shared secret using HKDF.
|
||||
derivedKey, err := dcrypto.Key(sharedSecret, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
debugf("server: decrypting authentication message\n")
|
||||
plaintext, err := dcrypto.Decrypt(derivedKey, authMessageRaw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clientPublicKeyRaw := plaintext[:222]
|
||||
signature := plaintext[222:]
|
||||
|
||||
debugf("server: importing client's public signing key\n")
|
||||
// Verify the client's public key and signature.
|
||||
clientPublicKey, err := dcrypto.ImportPublicSigningKey(clientPublicKeyRaw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
debugf("server: verifying client's signature\n")
|
||||
// Construct the message that was signed by the client.
|
||||
// This message is formatted as follows:
|
||||
// rlt + sha256(ae + be)
|
||||
// This binds both the client and server's long term public keys to the
|
||||
// ephemeral keys that were exchanged in the previous step. This creates a
|
||||
// verifiable link between both parties and the ephemeral keys used to
|
||||
// establish the shared secret.
|
||||
authMessage, err := buildMessage(remoteDHKey, ourDHKey.PublicKey())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !dcrypto.Verify(clientPublicKey, authMessage, signature) {
|
||||
return nil, errors.New("failed to verify client's signature")
|
||||
}
|
||||
|
||||
debugf("server: creating authentication message\n")
|
||||
// Now we need to sign the authentication message with the server's private
|
||||
// key. This will be sent back to the client in the next step to authenticate
|
||||
// the server to the client.
|
||||
serverSignature, err := dcrypto.Sign(actor.IdentityKey(), authMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
boxedMsg, err := dcrypto.Encrypt(derivedKey, serverSignature)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
debugf("server: sending authentication message\n")
|
||||
// Send the server's signature back to the client.
|
||||
err = vwb.Encode(conn, boxedMsg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Step 4: Client Authentication
|
||||
|
||||
debugf("server: handshake complete\n")
|
||||
return sharedSecret, nil
|
||||
}
|
||||
|
||||
func buildMessage(clientPubKey *ecdh.PublicKey, serverPubKey *ecdh.PublicKey) ([]byte, error) {
|
||||
clientPubKeyRaw, err := dcrypto.ExportDHPublicKey(clientPubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serverPubKeyRaw, err := dcrypto.ExportDHPublicKey(serverPubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Construct the message that will be signed by the client.
|
||||
// This message is formatted as follows:
|
||||
// rlt + sha256(ae + be)
|
||||
// This binds both the client and server's long term public keys to the
|
||||
// ephemeral keys that were exchanged in the previous step. This creates a
|
||||
// verifiable link between both parties and the ephemeral keys used to
|
||||
// establish the shared secret.
|
||||
message := make([]byte, 0, len(clientPubKeyRaw)+len(serverPubKeyRaw))
|
||||
message = append(message, clientPubKeyRaw...)
|
||||
message = append(message, serverPubKeyRaw...)
|
||||
|
||||
messageChecksum := sha256.Sum256(message)
|
||||
authMessage := make([]byte, 0, len(serverPubKeyRaw)+sha256.Size)
|
||||
authMessage = append(authMessage, serverPubKeyRaw...)
|
||||
authMessage = append(authMessage, messageChecksum[:]...)
|
||||
|
||||
return authMessage, nil
|
||||
}
|
90
pkg/handshake/handshake_test.go
Normal file
90
pkg/handshake/handshake_test.go
Normal file
@ -0,0 +1,90 @@
|
||||
package handshake_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"log"
|
||||
"net"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"koti.casa/numenor-labs/dsfx/pkg/dcrypto"
|
||||
"koti.casa/numenor-labs/dsfx/pkg/handshake"
|
||||
)
|
||||
|
||||
func TestHandshake(t *testing.T) {
|
||||
var wg sync.WaitGroup
|
||||
|
||||
client, server := net.Pipe()
|
||||
defer client.Close()
|
||||
defer server.Close()
|
||||
|
||||
alice := newActor()
|
||||
bob := newActor()
|
||||
|
||||
var aliceSharedSecret []byte
|
||||
var aliceErr error
|
||||
var bobSharedSecret []byte
|
||||
var bobErr error
|
||||
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
aliceSharedSecret, aliceErr = handshake.InitiateHandshake(alice, client, &bob.IdentityKey().PublicKey)
|
||||
wg.Done()
|
||||
}()
|
||||
go func() {
|
||||
bobSharedSecret, bobErr = handshake.AcceptHandshake(bob, server)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
|
||||
if aliceErr != nil || bobErr != nil {
|
||||
if aliceErr != nil {
|
||||
t.Errorf("alice error: %v", aliceErr)
|
||||
return
|
||||
}
|
||||
t.Errorf("bob error: %v", bobErr)
|
||||
return
|
||||
}
|
||||
|
||||
if aliceSharedSecret == nil || bobSharedSecret == nil {
|
||||
t.Errorf("handshake failed: shared secret is nil")
|
||||
return
|
||||
}
|
||||
if len(aliceSharedSecret) == 0 || len(bobSharedSecret) == 0 {
|
||||
t.Errorf("handshake failed: shared secret is empty")
|
||||
}
|
||||
if !bytes.Equal(aliceSharedSecret, bobSharedSecret) {
|
||||
t.Errorf("handshake failed: shared secrets do not match")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// An actor represents an entity that can participate in the handshake process.
|
||||
type actor struct {
|
||||
identity *ecdsa.PrivateKey
|
||||
}
|
||||
|
||||
// newActor creates a new actor with a random identity key.
|
||||
func newActor() *actor {
|
||||
key, err := dcrypto.GenerateSigningKey()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
return &actor{
|
||||
identity: key,
|
||||
}
|
||||
}
|
||||
|
||||
// IdentityKey ...
|
||||
func (a *actor) IdentityKey() *ecdsa.PrivateKey {
|
||||
return a.identity
|
||||
}
|
||||
|
||||
// Address returns the address of the actor. This is used when listening for
|
||||
// incoming connections, and when dialing out to other actors.
|
||||
func (a *actor) Address() *net.TCPAddr {
|
||||
return nil
|
||||
}
|
41
revive.toml
Normal file
41
revive.toml
Normal file
@ -0,0 +1,41 @@
|
||||
ignoreGeneratedHeader = false
|
||||
severity = "warning"
|
||||
confidence = 0.8
|
||||
errorCode = 1
|
||||
warningCode = 1
|
||||
|
||||
[rule.bare-return]
|
||||
[rule.blank-imports]
|
||||
[rule.context-as-argument]
|
||||
[rule.context-keys-type]
|
||||
[rule.dot-imports]
|
||||
[rule.empty-block]
|
||||
[rule.empty-lines]
|
||||
[rule.enforce-map-style]
|
||||
[rule.enforce-slice-style]
|
||||
[rule.error-naming]
|
||||
[rule.error-return]
|
||||
[rule.error-strings]
|
||||
[rule.errorf]
|
||||
[rule.exported]
|
||||
[rule.filename-format]
|
||||
# Override the default pattern to forbid .go files with uppercase letters and dashes.
|
||||
arguments=["^[_a-z][_a-z0-9]*\\.go$"]
|
||||
[rule.increment-decrement]
|
||||
[rule.indent-error-flow]
|
||||
[rule.line-length-limit]
|
||||
arguments = [200]
|
||||
# [rule.package-comments]
|
||||
[rule.range]
|
||||
[rule.receiver-naming]
|
||||
[rule.redefines-builtin-id]
|
||||
[rule.superfluous-else]
|
||||
[rule.time-naming]
|
||||
[rule.unexported-naming]
|
||||
[rule.unexported-return]
|
||||
[rule.unreachable-code]
|
||||
[rule.unused-parameter]
|
||||
[rule.useless-break]
|
||||
[rule.use-any]
|
||||
[rule.var-declaration]
|
||||
[rule.var-naming]
|
Loading…
x
Reference in New Issue
Block a user