package barcode import ( "crypto/hmac" "crypto/sha512" "encoding/base64" "encoding/json" "fmt" "strings" "time" fHMAC "github.com/ory/fosite/token/hmac" "edge-infra.dev/pkg/edge/iam/config" iamErrors "edge-infra.dev/pkg/edge/iam/errors" ) type SignedStrategy struct { HMACStrategy *fHMAC.HMACStrategy } type Payload struct { Subject string `json:"sub"` IssuedBy string `json:"iby"` IssuedAt int64 `json:"iat"` ExpiresAt int64 `json:"exp"` } var b64 = base64.URLEncoding.WithPadding(base64.NoPadding) func (s *SignedStrategy) Generate(subjectAlias, issuerAlias string) (string, error) { var signingKey [32]byte copy(signingKey[:], s.HMACStrategy.GlobalSecret) payload := Payload{ Subject: subjectAlias, IssuedBy: issuerAlias, IssuedAt: time.Now().Unix(), ExpiresAt: time.Now().Add(config.GetEmergencyBarcodeLifeSpan()).Unix(), } payloadBytes, _ := json.Marshal(payload) signature := s.generateHMAC(payloadBytes, &signingKey) encodedSignature := b64.EncodeToString(signature) encodedToken := fmt.Sprintf("%s.%s.%s", config.BarcodePrefix(), b64.EncodeToString(payloadBytes), encodedSignature) return encodedToken, nil } func (s *SignedStrategy) Verify(signedBarcode string) (*Payload, error) { var parts = strings.Split(signedBarcode, ".") if len(parts) != 3 { return nil, iamErrors.ErrUnrecognisedBarcode } unprefixedBarcode := strings.Join(parts[1:], ".") err := s.HMACStrategy.Validate(unprefixedBarcode) if err != nil { return nil, err } payload, _ := decodeTokenPayload(parts[1]) expires := time.Unix(payload.ExpiresAt, 0) remainder := time.Until(expires) if remainder <= 0 { return nil, iamErrors.ErrExpiredEBC } return payload, nil } func decodeTokenPayload(b64Payload string) (*Payload, error) { var payload Payload decodedTokenPayload, err := b64.DecodeString(b64Payload) if err != nil { return nil, err } err = json.Unmarshal(decodedTokenPayload, &payload) if err != nil { return nil, err } return &payload, nil } func (s *SignedStrategy) generateHMAC(data []byte, key *[32]byte) []byte { hasher := sha512.New512_256 h := hmac.New(hasher, key[:]) // sha512.digest.Write() always returns nil for err, the panic should never happen _, err := h.Write(data) if err != nil { panic(err) } return h.Sum(nil) }