@ -23,6 +23,7 @@ import (
"crypto/sha512"
"crypto/sha512"
"crypto/rand"
"crypto/rand"
"crypto/x509"
"crypto/x509"
"github.com/google/uuid"
)
)
type AuthReqJSON struct {
type AuthReqJSON struct {
@ -65,26 +66,26 @@ type AuthRespJSON struct {
Signature [ ] byte ` json:"signature" `
Signature [ ] byte ` json:"signature" `
}
}
func NewAuthRespJSON ( thread * GQLThread , req AuthReqJSON ) ( AuthRespJSON , [ ] byte , error ) {
func NewAuthRespJSON ( thread * GQLThread , req AuthReqJSON ) ( AuthRespJSON , * ecdsa . PublicKey , [ ] byte , error ) {
// Check if req.Time is within +- 1 second of now
// Check if req.Time is within +- 1 second of now
now := time . Now ( )
now := time . Now ( )
earliest := now . Add ( - 1 * time . Second )
earliest := now . Add ( - 1 * time . Second )
latest := now . Add ( 1 * time . Second )
latest := now . Add ( 1 * time . Second )
// If req.Time is before the earliest acceptable time, or after the latest acceptible time
// If req.Time is before the earliest acceptable time, or after the latest acceptible time
if req . Time . Compare ( earliest ) == - 1 {
if req . Time . Compare ( earliest ) == - 1 {
return AuthRespJSON { } , nil , fmt . Errorf ( "GQL_AUTH_TIME_TOO_LATE: %s" , req . Time )
return AuthRespJSON { } , nil , nil , fmt . Errorf ( "GQL_AUTH_TIME_TOO_LATE: %s" , req . Time )
} else if req . Time . Compare ( latest ) == 1 {
} else if req . Time . Compare ( latest ) == 1 {
return AuthRespJSON { } , nil , fmt . Errorf ( "GQL_AUTH_TIME_TOO_EARLY: %s" , req . Time )
return AuthRespJSON { } , nil , nil , fmt . Errorf ( "GQL_AUTH_TIME_TOO_EARLY: %s" , req . Time )
}
}
x , y := elliptic . Unmarshal ( thread . Key . Curve , req . Pubkey )
x , y := elliptic . Unmarshal ( thread . Key . Curve , req . Pubkey )
if x == nil {
if x == nil {
return AuthRespJSON { } , nil , fmt . Errorf ( "GQL_AUTH_UNMARSHAL_FAIL: %+v" , req . Pubkey )
return AuthRespJSON { } , nil , nil , fmt . Errorf ( "GQL_AUTH_UNMARSHAL_FAIL: %+v" , req . Pubkey )
}
}
remote , err := thread . ECDH . NewPublicKey ( req . ECDHPubkey )
remote , err := thread . ECDH . NewPublicKey ( req . ECDHPubkey )
if err != nil {
if err != nil {
return AuthRespJSON { } , nil , err
return AuthRespJSON { } , nil , nil , err
}
}
// Verify the signature
// Verify the signature
@ -92,23 +93,25 @@ func NewAuthRespJSON(thread *GQLThread, req AuthReqJSON) (AuthRespJSON, []byte,
sig_data := append ( req . ECDHPubkey , time_bytes ... )
sig_data := append ( req . ECDHPubkey , time_bytes ... )
sig_hash := sha512 . Sum512 ( sig_data )
sig_hash := sha512 . Sum512 ( sig_data )
verified := ecdsa . VerifyASN1 (
remote_key := & ecdsa . PublicKey {
& ecdsa . PublicKey {
Curve : thread . Key . Curve ,
Curve : thread . Key . Curve ,
X : x ,
X : x ,
Y : y ,
Y : y ,
} ,
}
verified := ecdsa . VerifyASN1 (
remote_key ,
sig_hash [ : ] ,
sig_hash [ : ] ,
req . Signature ,
req . Signature ,
)
)
if verified == false {
if verified == false {
return AuthRespJSON { } , nil , fmt . Errorf ( "GQL_AUTH_VERIFY_FAIL: %+v" , req )
return AuthRespJSON { } , nil , nil , fmt . Errorf ( "GQL_AUTH_VERIFY_FAIL: %+v" , req )
}
}
ec_key , err := thread . ECDH . GenerateKey ( rand . Reader )
ec_key , err := thread . ECDH . GenerateKey ( rand . Reader )
if err != nil {
if err != nil {
return AuthRespJSON { } , nil , err
return AuthRespJSON { } , nil , nil , err
}
}
ec_key_pub := ec_key . PublicKey ( ) . Bytes ( )
ec_key_pub := ec_key . PublicKey ( ) . Bytes ( )
@ -120,35 +123,37 @@ func NewAuthRespJSON(thread *GQLThread, req AuthReqJSON) (AuthRespJSON, []byte,
resp_sig , err := ecdsa . SignASN1 ( rand . Reader , thread . Key , resp_sig_hash [ : ] )
resp_sig , err := ecdsa . SignASN1 ( rand . Reader , thread . Key , resp_sig_hash [ : ] )
if err != nil {
if err != nil {
return AuthRespJSON { } , nil , err
return AuthRespJSON { } , nil , nil , err
}
}
shared_secret , err := ec_key . ECDH ( remote )
shared_secret , err := ec_key . ECDH ( remote )
if err != nil {
if err != nil {
return AuthRespJSON { } , nil , err
return AuthRespJSON { } , nil , nil , err
}
}
return AuthRespJSON {
return AuthRespJSON {
Granted : granted ,
Granted : granted ,
ECDHPubkey : ec_key_pub ,
ECDHPubkey : ec_key_pub ,
Signature : resp_sig ,
Signature : resp_sig ,
} , shared_secret, nil
} , remote_key, shared_secret, nil
}
}
type AuthData struct {
type AuthData struct {
Granted time . Time
Granted time . Time
Pubkey ecdh . PublicKey
Pubkey * ecdsa . PublicKey
ECDHClient ecdh . PublicKey
Shared [ ] byte
}
}
type AuthDataJSON struct {
type AuthDataJSON struct {
Granted time . Time ` json:"granted" `
Granted time . Time ` json:"granted" `
Pubkey [ ] byte ` json:"p bkey"`
Pubkey [ ] byte ` json:"p u bkey"`
ECDHClient [ ] byte ` json:" ec dh_client "`
Shared [ ] byte ` json:" shar ed"`
}
}
func HashKey ( pub [ ] byte ) uint64 {
func KeyID ( pub * ecdsa . PublicKey ) NodeID {
return 0
ser := elliptic . Marshal ( pub . Curve , pub . X , pub . Y )
str := uuid . NewHash ( sha512 . New ( ) , ZeroUUID , ser , 3 )
return NodeID ( str )
}
}
func AuthHandler ( ctx * Context , server * GQLThread ) func ( http . ResponseWriter , * http . Request ) {
func AuthHandler ( ctx * Context , server * GQLThread ) func ( http . ResponseWriter , * http . Request ) {
@ -169,7 +174,7 @@ func AuthHandler(ctx *Context, server *GQLThread) func(http.ResponseWriter, *htt
return
return
}
}
resp , _, err := NewAuthRespJSON ( server , req )
resp , remote_id, _, err := NewAuthRespJSON ( server , req )
if err != nil {
if err != nil {
ctx . Log . Logf ( "gql" , "GQL_AUTH_VERIFY_ERROR: %e" , err )
ctx . Log . Logf ( "gql" , "GQL_AUTH_VERIFY_ERROR: %e" , err )
return
return
@ -192,13 +197,13 @@ func AuthHandler(ctx *Context, server *GQLThread) func(http.ResponseWriter, *htt
ctx . Log . Logf ( "gql" , "GQL_AUTH_VERIFY_SUCCESS: %s" , str )
ctx . Log . Logf ( "gql" , "GQL_AUTH_VERIFY_SUCCESS: %s" , str )
key_hash := HashKey( req . Pubkey )
key_hash := KeyID( remote_id )
_ , exists := server . AuthMap [ key_hash ]
_ , exists := server . AuthMap [ key_hash ]
if exists {
if exists {
// New user
ctx . Log . Logf ( "gql" , "REFRESHING AUTH FOR %+s" , req . Pubkey )
} else {
} else {
// Existing user
ctx . Log . Logf ( "gql" , "AUTHORIZING NEW USER %+s" , req . Pubkey )
}
}
}
}
@ -578,7 +583,7 @@ type GQLThread struct {
http_server * http . Server
http_server * http . Server
http_done * sync . WaitGroup
http_done * sync . WaitGroup
Listen string
Listen string
AuthMap map [ uint64 ] AuthData
AuthMap map [ NodeID ] AuthData
Key * ecdsa . PrivateKey
Key * ecdsa . PrivateKey
ECDH ecdh . Curve
ECDH ecdh . Curve
}
}
@ -604,7 +609,7 @@ func (thread * GQLThread) DeserializeInfo(ctx *Context, data []byte) (ThreadInfo
type GQLThreadJSON struct {
type GQLThreadJSON struct {
SimpleThreadJSON
SimpleThreadJSON
Listen string ` json:"listen" `
Listen string ` json:"listen" `
AuthMap map [ uint64] AuthData ` json:"auth_map" `
AuthMap map [ string] AuthDataJSON ` json:"auth_map" `
Key [ ] byte ` json:"key" `
Key [ ] byte ` json:"key" `
ECDH uint8 ` json:"ecdh_curve" `
ECDH uint8 ` json:"ecdh_curve" `
}
}
@ -633,10 +638,19 @@ func NewGQLThreadJSON(thread *GQLThread) GQLThreadJSON {
panic ( err )
panic ( err )
}
}
auth_map := map [ string ] AuthDataJSON { }
for id , data := range ( thread . AuthMap ) {
auth_map [ id . String ( ) ] = AuthDataJSON {
Granted : data . Granted ,
Pubkey : elliptic . Marshal ( data . Pubkey . Curve , data . Pubkey . X , data . Pubkey . Y ) ,
Shared : thread . AuthMap [ id ] . Shared ,
}
}
return GQLThreadJSON {
return GQLThreadJSON {
SimpleThreadJSON : thread_json ,
SimpleThreadJSON : thread_json ,
Listen : thread . Listen ,
Listen : thread . Listen ,
AuthMap : thread . AuthMap ,
AuthMap : auth_m ap,
Key : ser_key ,
Key : ser_key ,
ECDH : ecdh_curve_ids [ thread . ECDH ] ,
ECDH : ecdh_curve_ids [ thread . ECDH ] ,
}
}
@ -660,7 +674,26 @@ func LoadGQLThread(ctx *Context, id NodeID, data []byte, nodes NodeMap) (Node, e
}
}
thread := NewGQLThread ( id , j . Name , j . StateName , j . Listen , ecdh_curve , key )
thread := NewGQLThread ( id , j . Name , j . StateName , j . Listen , ecdh_curve , key )
thread . AuthMap = j . AuthMap
thread . AuthMap = map [ NodeID ] AuthData { }
for id_str , auth_json := range ( j . AuthMap ) {
id , err := ParseID ( id_str )
if err != nil {
return nil , err
}
x , y := elliptic . Unmarshal ( key . Curve , auth_json . Pubkey )
if x == nil {
return nil , fmt . Errorf ( "Failed to load public key for curve %+v from %+v" , key . Curve , auth_json . Pubkey )
}
thread . AuthMap [ id ] = AuthData {
Granted : auth_json . Granted ,
Pubkey : & ecdsa . PublicKey {
Curve : key . Curve ,
X : x ,
Y : y ,
} ,
Shared : auth_json . Shared ,
}
}
nodes [ id ] = & thread
nodes [ id ] = & thread
err = RestoreSimpleThread ( ctx , & thread , j . SimpleThreadJSON , nodes )
err = RestoreSimpleThread ( ctx , & thread , j . SimpleThreadJSON , nodes )
@ -675,7 +708,7 @@ func NewGQLThread(id NodeID, name string, state_name string, listen string, ecdh
return GQLThread {
return GQLThread {
SimpleThread : NewSimpleThread ( id , name , state_name , reflect . TypeOf ( ( * ParentThreadInfo ) ( nil ) ) , gql_actions , gql_handlers ) ,
SimpleThread : NewSimpleThread ( id , name , state_name , reflect . TypeOf ( ( * ParentThreadInfo ) ( nil ) ) , gql_actions , gql_handlers ) ,
Listen : listen ,
Listen : listen ,
AuthMap : map [ uint64 ] AuthData { } ,
AuthMap : map [ NodeID ] AuthData { } ,
http_done : & sync . WaitGroup { } ,
http_done : & sync . WaitGroup { } ,
Key : key ,
Key : key ,
ECDH : ecdh_curve ,
ECDH : ecdh_curve ,