/****************************************************************************** * * Description : * * Authentication * *****************************************************************************/ package main import ( "bytes" "crypto/hmac" "crypto/md5" "encoding/base64" "github.com/tinode/chat/server/logs" ) // Singned AppID. Composition: // // [1:algorithm version][4:appid][2:key sequence][1:isRoot][16:signature] = 24 bytes // // convertible to base64 without padding. All integers are little-endian. // Definitions for byte lengths of key's parts. const ( // apikeyVersion is the version of this API scheme. apikeyVersion = 1 // apikeyAppID is deprecated and will be removed in the future. apikeyAppID = 4 // apikeySequence is the serial number of the key. apikeySequence = 2 // apikeyWho indicates if the key grants root privileges. apikeyWho = 1 // apikeySignature is key's cryptographic (HMAC) signature. apikeySignature = 16 // apikeyLength is the length of the key in bytes. apikeyLength = apikeyVersion + apikeyAppID + apikeySequence + apikeyWho + apikeySignature ) // Client signature validation // // key: client's secret key // // Returns application id, key type. func checkAPIKey(apikey string) (isValid, isRoot bool) { if declen := base64.URLEncoding.DecodedLen(len(apikey)); declen != apikeyLength { return } data, err := base64.URLEncoding.DecodeString(apikey) if err != nil { logs.Warn.Println("failed to decode.base64 appid ", err) return } if data[0] != 1 { logs.Warn.Println("unknown appid signature algorithm ", data[0]) return } hasher := hmac.New(md5.New, globals.apiKeySalt) hasher.Write(data[:apikeyVersion+apikeyAppID+apikeySequence+apikeyWho]) check := hasher.Sum(nil) if !bytes.Equal(data[apikeyVersion+apikeyAppID+apikeySequence+apikeyWho:], check) { logs.Warn.Println("invalid apikey signature") return } isRoot = (data[apikeyVersion+apikeyAppID+apikeySequence] == 1) isValid = true return }