-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4 from xconnio/joiner
Client: Add authenticators and session joiner
- Loading branch information
Showing
10 changed files
with
487 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,41 @@ | ||
package auth | ||
|
||
import ( | ||
"errors" | ||
|
||
"github.com/xconnio/wampproto-go/messages" | ||
) | ||
|
||
const MethodAnonymous = "anonymous" | ||
|
||
type anonymousAuthenticator struct { | ||
authID string | ||
authExtra map[string]any | ||
} | ||
|
||
func NewAnonymousAuthenticator(authID string, authExtra map[string]any) ClientAuthenticator { | ||
if authExtra == nil { | ||
authExtra = map[string]any{} | ||
} | ||
|
||
return &anonymousAuthenticator{ | ||
authID: authID, | ||
authExtra: authExtra, | ||
} | ||
} | ||
|
||
func (a *anonymousAuthenticator) AuthMethod() string { | ||
return MethodAnonymous | ||
} | ||
|
||
func (a *anonymousAuthenticator) AuthID() string { | ||
return a.authID | ||
} | ||
|
||
func (a *anonymousAuthenticator) AuthExtra() map[string]any { | ||
return a.authExtra | ||
} | ||
|
||
func (a *anonymousAuthenticator) Authenticate(_ messages.Challenge) (messages.Authenticate, error) { | ||
return nil, errors.New("func Authenticate() must not be called for anonymous authentication") | ||
} |
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,10 @@ | ||
package auth | ||
|
||
import "github.com/xconnio/wampproto-go/messages" | ||
|
||
type ClientAuthenticator interface { | ||
AuthMethod() string | ||
AuthID() string | ||
AuthExtra() map[string]any | ||
Authenticate(challenge messages.Challenge) (messages.Authenticate, error) | ||
} |
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,97 @@ | ||
package auth | ||
|
||
import ( | ||
"crypto/hmac" | ||
"crypto/sha256" | ||
"encoding/base64" | ||
|
||
"golang.org/x/crypto/pbkdf2" | ||
|
||
"github.com/xconnio/wampproto-go/messages" | ||
) | ||
|
||
const MethodCRA = "wampcra" | ||
|
||
type craAuthenticator struct { | ||
authID string | ||
authExtra map[string]any | ||
|
||
secret string | ||
} | ||
|
||
func NewCRAAuthenticator(authID string, authExtra map[string]any, secret string) ClientAuthenticator { | ||
return &craAuthenticator{ | ||
authID: authID, | ||
authExtra: authExtra, | ||
secret: secret, | ||
} | ||
} | ||
|
||
func (a *craAuthenticator) AuthMethod() string { | ||
return MethodCRA | ||
} | ||
|
||
func (a *craAuthenticator) AuthID() string { | ||
return a.authID | ||
} | ||
|
||
func (a *craAuthenticator) AuthExtra() map[string]any { | ||
return a.authExtra | ||
} | ||
|
||
func (a *craAuthenticator) Authenticate(challenge messages.Challenge) (messages.Authenticate, error) { | ||
ch, _ := challenge.Extra()["challenge"].(string) | ||
// If the client needed to look up a user's key, this would require decoding | ||
// the JSON-encoded challenge string and getting the authid. For this | ||
// example assume that client only operates as one user and knows the key | ||
// to use. | ||
|
||
var rawSecret []byte | ||
saltStr, _ := challenge.Extra()["salt"].(string) | ||
// If no salt given, use raw password as key. | ||
if saltStr != "" { | ||
// If salting info give, then compute a derived key using PBKDF2. | ||
iters, _ := messages.AsInt64(challenge.Extra()["iterations"]) | ||
keylen, _ := messages.AsInt64(challenge.Extra()["keylen"]) | ||
|
||
rawSecret = DeriveCRAKey(saltStr, a.secret, int(iters), int(keylen)) | ||
} else { | ||
rawSecret = []byte(a.secret) | ||
} | ||
|
||
challengeStr := SignCRAChallenge(ch, rawSecret) | ||
return messages.NewAuthenticate(challengeStr, map[string]any{}), nil | ||
} | ||
|
||
// SignCRAChallengeBytes computes the HMAC-SHA256, using the given key, over the | ||
// challenge string, and returns the result. | ||
func SignCRAChallengeBytes(ch string, key []byte) []byte { | ||
sig := hmac.New(sha256.New, key) | ||
sig.Write([]byte(ch)) | ||
return sig.Sum(nil) | ||
} | ||
|
||
// SignCRAChallenge computes the HMAC-SHA256, using the given key, over the | ||
// challenge string, and returns the result as a base64-encoded string. | ||
func SignCRAChallenge(ch string, key []byte) string { | ||
return base64.StdEncoding.EncodeToString(SignCRAChallengeBytes(ch, key)) | ||
} | ||
|
||
func DeriveCRAKey(saltStr string, secret string, iterations int, keyLength int) []byte { | ||
// If salting info give, then compute a derived key using PBKDF2. | ||
salt := []byte(saltStr) | ||
|
||
if iterations == 0 { | ||
iterations = 1000 | ||
} | ||
if keyLength == 0 { | ||
keyLength = 32 | ||
} | ||
|
||
// Compute derived key. | ||
dk := pbkdf2.Key([]byte(secret), salt, iterations, keyLength, sha256.New) | ||
// Get base64 bytes. see https://github.com/gammazero/nexus/issues/252 | ||
derivedKey := []byte(base64.StdEncoding.EncodeToString(dk)) | ||
|
||
return derivedKey | ||
} |
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,80 @@ | ||
package auth | ||
|
||
import ( | ||
"crypto/ed25519" | ||
"encoding/hex" | ||
"errors" | ||
"fmt" | ||
|
||
"github.com/xconnio/wampproto-go/messages" | ||
) | ||
|
||
const MethodCryptoSign = "cryptosign" | ||
|
||
type cryptoSignAuthenticator struct { | ||
authID string | ||
authExtra map[string]any | ||
|
||
privateKey ed25519.PrivateKey | ||
} | ||
|
||
func NewCryptoSignAuthenticator(authID string, authExtra map[string]any, | ||
privateKeyHex string) (ClientAuthenticator, error) { | ||
|
||
privateKeyRaw, err := hex.DecodeString(privateKeyHex) | ||
if err != nil { | ||
return nil, errors.New("invalid private key") | ||
} | ||
|
||
privateKey := ed25519.NewKeyFromSeed(privateKeyRaw) | ||
publicKey := privateKey.Public().(ed25519.PublicKey) | ||
publicKeyHex := hex.EncodeToString(publicKey) | ||
|
||
if authExtra == nil { | ||
authExtra = map[string]any{"pubkey": publicKeyHex} | ||
} else if val, ok := authExtra["pubkey"].(string); ok { | ||
if val != publicKeyHex { | ||
return nil, errors.New("provided pubkey does not correspond to the private key") | ||
} | ||
} | ||
|
||
return &cryptoSignAuthenticator{ | ||
authID: authID, | ||
authExtra: authExtra, | ||
privateKey: privateKey, | ||
}, nil | ||
} | ||
|
||
func (a *cryptoSignAuthenticator) AuthMethod() string { | ||
return MethodCryptoSign | ||
} | ||
|
||
func (a *cryptoSignAuthenticator) AuthID() string { | ||
return a.authID | ||
} | ||
|
||
func (a *cryptoSignAuthenticator) AuthExtra() map[string]any { | ||
return a.authExtra | ||
} | ||
|
||
func (a *cryptoSignAuthenticator) Authenticate(challenge messages.Challenge) (messages.Authenticate, error) { | ||
challengeHex, _ := challenge.Extra()["challenge"].(string) | ||
result, err := SignCryptoSignChallenge(challengeHex, a.privateKey) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to sign challenge") | ||
} | ||
|
||
return messages.NewAuthenticate(result, map[string]any{}), nil | ||
} | ||
|
||
func SignCryptoSignChallenge(challenge string, privateKey ed25519.PrivateKey) (string, error) { | ||
challengeRaw, err := hex.DecodeString(challenge) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to decode challenge: %w", err) | ||
} | ||
|
||
signedRaw := ed25519.Sign(privateKey, challengeRaw) | ||
signedHex := hex.EncodeToString(signedRaw) | ||
|
||
return signedHex + challenge, nil | ||
} |
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,38 @@ | ||
package auth | ||
|
||
import ( | ||
"github.com/xconnio/wampproto-go/messages" | ||
) | ||
|
||
const MethodTicket = "ticket" | ||
|
||
type ticketAuthenticator struct { | ||
authID string | ||
authExtra map[string]any | ||
|
||
ticket string | ||
} | ||
|
||
func NewTicketAuthenticator(authID string, authExtra map[string]any, ticket string) ClientAuthenticator { | ||
return &ticketAuthenticator{ | ||
authID: authID, | ||
authExtra: authExtra, | ||
ticket: ticket, | ||
} | ||
} | ||
|
||
func (a *ticketAuthenticator) AuthMethod() string { | ||
return MethodTicket | ||
} | ||
|
||
func (a *ticketAuthenticator) AuthID() string { | ||
return a.authID | ||
} | ||
|
||
func (a *ticketAuthenticator) AuthExtra() map[string]any { | ||
return a.authExtra | ||
} | ||
|
||
func (a *ticketAuthenticator) Authenticate(_ messages.Challenge) (messages.Authenticate, error) { | ||
return messages.NewAuthenticate(a.ticket, map[string]any{}), nil | ||
} |
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
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
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,37 @@ | ||
package wampproto | ||
|
||
import ( | ||
"sync" | ||
"time" | ||
|
||
"golang.org/x/exp/rand" | ||
) | ||
|
||
const maxID int64 = 1 << 53 | ||
|
||
func init() { | ||
source := rand.NewSource(uint64(time.Now().UnixNano())) | ||
rand.New(source) | ||
} | ||
|
||
// GenerateID generates a random WAMP ID. | ||
func GenerateID() int64 { | ||
return rand.Int63n(maxID) | ||
} | ||
|
||
type SessionScopeIDGenerator struct { | ||
id int64 | ||
sync.Mutex | ||
} | ||
|
||
func (s *SessionScopeIDGenerator) NextID() int64 { | ||
s.Lock() | ||
defer s.Unlock() | ||
|
||
if s.id == maxID { | ||
s.id = 0 | ||
} | ||
|
||
s.id++ | ||
return s.id | ||
} |
Oops, something went wrong.