| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110 |
- package service
- import (
- "context"
- "crypto/aes"
- "crypto/cipher"
- "crypto/rand"
- "crypto/sha256"
- "encoding/base64"
- "errors"
- "fmt"
- "io"
- "strings"
- "github.com/Wei-Shaw/sub2api/internal/config"
- )
- const liveAttestationHeader = "x-oai-attestation"
- type liveAttestationAES struct {
- key [32]byte
- }
- func newLiveAttestationCipher(cfg *config.Config) SecretEncryptor {
- if cfg == nil || strings.TrimSpace(cfg.JWT.Secret) == "" {
- return nil
- }
- return &liveAttestationAES{
- key: sha256.Sum256([]byte("sub2api/live-attestation/v1\x00" + cfg.JWT.Secret)),
- }
- }
- func (c *liveAttestationAES) Encrypt(plaintext string) (string, error) {
- block, err := aes.NewCipher(c.key[:])
- if err != nil {
- return "", err
- }
- gcm, err := cipher.NewGCM(block)
- if err != nil {
- return "", err
- }
- nonce := make([]byte, gcm.NonceSize())
- if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
- return "", fmt.Errorf("generate Live attestation nonce: %w", err)
- }
- encrypted := gcm.Seal(nonce, nonce, []byte(plaintext), nil)
- return base64.RawStdEncoding.EncodeToString(encrypted), nil
- }
- func (c *liveAttestationAES) Decrypt(ciphertext string) (string, error) {
- encrypted, err := base64.RawStdEncoding.DecodeString(ciphertext)
- if err != nil {
- return "", fmt.Errorf("decode Live attestation: %w", err)
- }
- block, err := aes.NewCipher(c.key[:])
- if err != nil {
- return "", err
- }
- gcm, err := cipher.NewGCM(block)
- if err != nil {
- return "", err
- }
- if len(encrypted) < gcm.NonceSize() {
- return "", errors.New("encrypted Live attestation is too short")
- }
- plaintext, err := gcm.Open(nil, encrypted[:gcm.NonceSize()], encrypted[gcm.NonceSize():], nil)
- if err != nil {
- return "", fmt.Errorf("decrypt Live attestation: %w", err)
- }
- return string(plaintext), nil
- }
- func (s *OpenAIGatewayService) prepareLiveAttestation(ctx context.Context) (string, string, error) {
- if s == nil || s.liveAttestation == nil {
- return "", "", &LiveAttestationUnavailableError{
- Reason: "Sub2API has no platform DeviceCheck provider",
- }
- }
- if s.liveAttestationCipher == nil {
- return "", "", &LiveAttestationUnavailableError{
- Reason: "JWT secret is required to protect the Sideband attestation",
- }
- }
- header, err := s.liveAttestation.Generate(ctx)
- if err != nil {
- return "", "", &LiveAttestationUnavailableError{Reason: err.Error()}
- }
- ciphertext, err := s.liveAttestationCipher.Encrypt(header)
- if err != nil {
- return "", "", &LiveAttestationUnavailableError{
- Reason: "failed to protect the generated DeviceCheck attestation",
- }
- }
- return header, ciphertext, nil
- }
- func (s *OpenAIGatewayService) decryptLiveAttestation(record *LiveCallRecord) (string, error) {
- if record == nil || strings.TrimSpace(record.AttestationCiphertext) == "" || s.liveAttestationCipher == nil {
- return "", &LiveAttestationUnavailableError{
- Reason: "the Live call has no reusable DeviceCheck attestation",
- }
- }
- header, err := s.liveAttestationCipher.Decrypt(record.AttestationCiphertext)
- if err != nil {
- return "", &LiveAttestationUnavailableError{
- Reason: "the Live call DeviceCheck attestation cannot be decrypted on this instance",
- }
- }
- return header, nil
- }
|