65 lines
1.9 KiB
Go
65 lines
1.9 KiB
Go
|
|
package signing
|
||
|
|
|
||
|
|
import (
|
||
|
|
"crypto/hmac"
|
||
|
|
"crypto/sha256"
|
||
|
|
"crypto/subtle"
|
||
|
|
"encoding/hex"
|
||
|
|
"fmt"
|
||
|
|
"strconv"
|
||
|
|
"strings"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"git.apinb.com/ops/files/internal/storage"
|
||
|
|
)
|
||
|
|
|
||
|
|
func SignUpload(secret string, request storage.UploadRequest) string {
|
||
|
|
canonical := strings.Join([]string{
|
||
|
|
"PUT",
|
||
|
|
request.FileID,
|
||
|
|
strconv.FormatInt(request.ExpiresAt.Unix(), 10),
|
||
|
|
strconv.FormatInt(request.ExpectedSize, 10),
|
||
|
|
request.ContentType,
|
||
|
|
}, "\n")
|
||
|
|
return sign(secret, canonical)
|
||
|
|
}
|
||
|
|
|
||
|
|
func VerifyUpload(secret, signature string, request storage.UploadRequest, now time.Time) bool {
|
||
|
|
return request.ExpiresAt.After(now) && verify(secret, SignUpload(secret, request), signature)
|
||
|
|
}
|
||
|
|
|
||
|
|
func SignAccess(secret, fileID string, expiresAt time.Time, disposition string) string {
|
||
|
|
canonical := strings.Join([]string{"GET", fileID, strconv.FormatInt(expiresAt.Unix(), 10), disposition}, "\n")
|
||
|
|
return sign(secret, canonical)
|
||
|
|
}
|
||
|
|
|
||
|
|
func VerifyAccess(secret, fileID string, expiresAt time.Time, disposition, signature string, now time.Time) bool {
|
||
|
|
return expiresAt.After(now) && verify(secret, SignAccess(secret, fileID, expiresAt, disposition), signature)
|
||
|
|
}
|
||
|
|
|
||
|
|
func sign(secret, canonical string) string {
|
||
|
|
mac := hmac.New(sha256.New, []byte(secret))
|
||
|
|
_, _ = mac.Write([]byte(canonical))
|
||
|
|
return hex.EncodeToString(mac.Sum(nil))
|
||
|
|
}
|
||
|
|
|
||
|
|
func verify(secret, expected, actual string) bool {
|
||
|
|
if strings.TrimSpace(secret) == "" || len(actual) != sha256.Size*2 {
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
expectedBytes, expectedErr := hex.DecodeString(expected)
|
||
|
|
actualBytes, actualErr := hex.DecodeString(actual)
|
||
|
|
if expectedErr != nil || actualErr != nil {
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
return subtle.ConstantTimeCompare(expectedBytes, actualBytes) == 1
|
||
|
|
}
|
||
|
|
|
||
|
|
func UploadNonce(secret string, request storage.UploadRequest) (string, error) {
|
||
|
|
signature := SignUpload(secret, request)
|
||
|
|
if len(signature) < 16 {
|
||
|
|
return "", fmt.Errorf("上传签名长度无效")
|
||
|
|
}
|
||
|
|
return signature[:16], nil
|
||
|
|
}
|