Redesign authentication protocol

This commit is contained in:
2019-02-22 17:35:49 +01:00
parent 4a69555578
commit 19732daaf5
33 changed files with 791 additions and 413 deletions

View File

@ -2,10 +2,10 @@ package client
import (
"bytes"
"crypto/rsa"
"crypto/sha256"
"encoding/json"
"errors"
"fmt"
"net/http"
"time"
@ -25,6 +25,7 @@ var (
ErrUnexpectedResponse = errors.New("unexpected response")
ErrUnauthorized = errors.New("unauthorized")
ErrRejected = errors.New("rejected")
ErrInvalidServerToken = errors.New("invalid server token")
)
type Client struct {
@ -40,18 +41,15 @@ func (c *Client) Advertise(attrs peering.PeerAttributes) error {
}
data := &peering.AdvertisingRequest{
ID: c.options.PeerID,
Attributes: attrs,
PublicKey: publicKey,
}
req, _, err := c.createPostRequest(url, data)
res, err := c.Post(url, data)
if err != nil {
return err
}
res, err := c.options.HTTPClient.Do(req)
switch res.StatusCode {
case http.StatusCreated:
return nil
@ -111,7 +109,7 @@ func (c *Client) Get(url string) (*http.Response, error) {
if err != nil {
return nil, err
}
if err := c.signRequest(req, nil); err != nil {
if err := c.addClientToken(req, nil); err != nil {
return nil, err
}
return c.options.HTTPClient.Do(req)
@ -122,7 +120,7 @@ func (c *Client) Post(url string, data interface{}) (*http.Response, error) {
if err != nil {
return nil, err
}
if err := c.signRequest(req, body); err != nil {
if err := c.addClientToken(req, body); err != nil {
return nil, err
}
return c.options.HTTPClient.Do(req)
@ -144,16 +142,22 @@ func (c *Client) createPostRequest(url string, data interface{}) (*http.Request,
return req, body, nil
}
func (c *Client) signRequest(r *http.Request, body []byte) error {
func (c *Client) addServerToken(r *http.Request) {
r.Header.Set(
server.ServerTokenHeader,
c.options.ServerToken,
)
}
func (c *Client) addClientToken(r *http.Request, body []byte) error {
bodySum, err := c.createBodySum(body)
if err != nil {
return err
}
token := jwt.NewWithClaims(jwt.SigningMethodRS256, peering.PeerClaims{
token := jwt.NewWithClaims(jwt.SigningMethodRS256, peering.ClientTokenClaims{
StandardClaims: jwt.StandardClaims{
NotBefore: time.Now().Unix(),
Issuer: string(c.options.PeerID),
ExpiresAt: time.Now().Add(time.Minute * 10).Unix(),
},
BodySum: bodySum,
@ -164,7 +168,9 @@ func (c *Client) signRequest(r *http.Request, body []byte) error {
return err
}
r.Header.Set("Authorization", fmt.Sprintf("%s %s", server.AuthorizationType, tokenStr))
r.Header.Set(server.ClientTokenHeader, tokenStr)
c.addServerToken(r)
return nil
}
@ -181,6 +187,27 @@ func (c *Client) createBodySum(body []byte) ([]byte, error) {
return sha.Sum(nil), nil
}
func (c *Client) PeerID(serverPublicKey *rsa.PublicKey) (peering.PeerID, error) {
token, err := jwt.ParseWithClaims(
c.options.ServerToken,
&peering.ServerTokenClaims{},
func(token *jwt.Token) (interface{}, error) {
return serverPublicKey, nil
},
)
if err != nil {
return "", err
}
if !token.Valid {
return "", ErrInvalidServerToken
}
serverClaims, ok := token.Claims.(*peering.ServerTokenClaims)
if !ok {
return "", ErrInvalidServerToken
}
return serverClaims.PeerID, nil
}
func New(funcs ...OptionFunc) *Client {
options := createOptions(funcs...)
return &Client{options}

44
client/client_test.go Normal file
View File

@ -0,0 +1,44 @@
package client
import (
"crypto/rand"
"crypto/rsa"
"testing"
peeringCrypto "forge.cadoles.com/wpetit/go-http-peering/crypto"
peering "forge.cadoles.com/wpetit/go-http-peering"
)
func TestClientPeerID(t *testing.T) {
serverPK := mustGeneratePrivateKey()
peerID := peering.NewPeerID()
serverToken, err := peeringCrypto.CreateServerToken(serverPK, "test", peerID)
if err != nil {
t.Fatal(err)
}
client := New(
WithServerToken(serverToken),
)
clientPeerID, err := client.PeerID(&serverPK.PublicKey)
if err != nil {
t.Fatal(err)
}
if g, e := clientPeerID, peerID; g != e {
t.Errorf("client.PeerID(): got '%v', expected '%v'", g, e)
}
}
func mustGeneratePrivateKey() *rsa.PrivateKey {
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
panic(err)
}
return privateKey
}

View File

@ -3,22 +3,24 @@ package client
import (
"crypto/rsa"
"net/http"
peering "forge.cadoles.com/wpetit/go-http-peering"
)
type HTTPClient interface {
Do(*http.Request) (*http.Response, error)
}
type Options struct {
PeerID peering.PeerID
HTTPClient *http.Client
BaseURL string
PrivateKey *rsa.PrivateKey
HTTPClient HTTPClient
BaseURL string
PrivateKey *rsa.PrivateKey
ServerToken string
}
type OptionFunc func(*Options)
func WithPeerID(id peering.PeerID) OptionFunc {
func WithServerToken(token string) OptionFunc {
return func(opts *Options) {
opts.PeerID = id
opts.ServerToken = token
}
}
@ -28,7 +30,7 @@ func WithPrivateKey(pk *rsa.PrivateKey) OptionFunc {
}
}
func WithHTTPClient(client *http.Client) OptionFunc {
func WithHTTPClient(client HTTPClient) OptionFunc {
return func(opts *Options) {
opts.HTTPClient = client
}
@ -43,7 +45,6 @@ func WithBaseURL(url string) OptionFunc {
func defaultOptions() *Options {
return &Options{
HTTPClient: http.DefaultClient,
PeerID: peering.NewPeerID(),
}
}