53 lines
1.4 KiB
Go
53 lines
1.4 KiB
Go
package taskclaim
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"crypto/hmac"
|
||
|
|
"crypto/sha256"
|
||
|
|
"crypto/subtle"
|
||
|
|
"encoding/binary"
|
||
|
|
"encoding/hex"
|
||
|
|
"hash"
|
||
|
|
)
|
||
|
|
|
||
|
|
const tokenDomain = "cmbuyer/task-claim-token/v1\x00"
|
||
|
|
|
||
|
|
func deriveToken(secret []byte, deviceID, taskID, authorizationID, attemptID string, generation int, nonce []byte) []byte {
|
||
|
|
mac := hmac.New(sha256.New, secret)
|
||
|
|
_, _ = mac.Write([]byte(tokenDomain))
|
||
|
|
writeTokenField(mac, deviceID)
|
||
|
|
writeTokenField(mac, taskID)
|
||
|
|
writeTokenField(mac, authorizationID)
|
||
|
|
writeTokenField(mac, attemptID)
|
||
|
|
var number [8]byte
|
||
|
|
binary.BigEndian.PutUint64(number[:], uint64(generation))
|
||
|
|
_, _ = mac.Write(number[:])
|
||
|
|
writeTokenBytes(mac, nonce)
|
||
|
|
return mac.Sum(nil)
|
||
|
|
}
|
||
|
|
|
||
|
|
func writeTokenField(writer hash.Hash, value string) { writeTokenBytes(writer, []byte(value)) }
|
||
|
|
|
||
|
|
func writeTokenBytes(writer hash.Hash, value []byte) {
|
||
|
|
var size [4]byte
|
||
|
|
binary.BigEndian.PutUint32(size[:], uint32(len(value)))
|
||
|
|
_, _ = writer.Write(size[:])
|
||
|
|
_, _ = writer.Write(value)
|
||
|
|
}
|
||
|
|
|
||
|
|
func tokenHash(token []byte) []byte {
|
||
|
|
sum := sha256.Sum256(token)
|
||
|
|
return sum[:]
|
||
|
|
}
|
||
|
|
|
||
|
|
func matchingHash(left, right []byte) bool {
|
||
|
|
return len(left) == sha256.Size && len(right) == sha256.Size && subtle.ConstantTimeCompare(left, right) == 1
|
||
|
|
}
|
||
|
|
|
||
|
|
func decodeToken(value string) ([]byte, bool) {
|
||
|
|
if len(value) != sha256.Size*2 {
|
||
|
|
return nil, false
|
||
|
|
}
|
||
|
|
decoded, err := hex.DecodeString(value)
|
||
|
|
return decoded, err == nil && hex.EncodeToString(decoded) == value
|
||
|
|
}
|