github.com/lestrrat-go/jwx/v2@v2.0.21/internal/base64/base64.go (about)

     1  package base64
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/base64"
     6  	"encoding/binary"
     7  	"fmt"
     8  	"sync"
     9  )
    10  
    11  type Decoder interface {
    12  	Decode([]byte) ([]byte, error)
    13  }
    14  
    15  type Encoder interface {
    16  	Encode([]byte, []byte)
    17  	EncodedLen(int) int
    18  	EncodeToString([]byte) string
    19  }
    20  
    21  var muEncoder sync.RWMutex
    22  var encoder Encoder = base64.RawURLEncoding
    23  var muDecoder sync.RWMutex
    24  var decoder Decoder = defaultDecoder{}
    25  
    26  func SetEncoder(enc Encoder) {
    27  	muEncoder.Lock()
    28  	defer muEncoder.Unlock()
    29  	encoder = enc
    30  }
    31  
    32  func getEncoder() Encoder {
    33  	muEncoder.RLock()
    34  	defer muEncoder.RUnlock()
    35  	return encoder
    36  }
    37  
    38  func SetDecoder(dec Decoder) {
    39  	muDecoder.Lock()
    40  	defer muDecoder.Unlock()
    41  	decoder = dec
    42  }
    43  
    44  func getDecoder() Decoder {
    45  	muDecoder.RLock()
    46  	defer muDecoder.RUnlock()
    47  	return decoder
    48  }
    49  
    50  func Encode(src []byte) []byte {
    51  	encoder := getEncoder()
    52  	dst := make([]byte, encoder.EncodedLen(len(src)))
    53  	encoder.Encode(dst, src)
    54  	return dst
    55  }
    56  
    57  func EncodeToString(src []byte) string {
    58  	return getEncoder().EncodeToString(src)
    59  }
    60  
    61  func EncodeUint64ToString(v uint64) string {
    62  	data := make([]byte, 8)
    63  	binary.BigEndian.PutUint64(data, v)
    64  
    65  	i := 0
    66  	for ; i < len(data); i++ {
    67  		if data[i] != 0x0 {
    68  			break
    69  		}
    70  	}
    71  
    72  	return EncodeToString(data[i:])
    73  }
    74  
    75  const (
    76  	InvalidEncoding = iota
    77  	Std
    78  	URL
    79  	RawStd
    80  	RawURL
    81  )
    82  
    83  func Guess(src []byte) int {
    84  	var isRaw = !bytes.HasSuffix(src, []byte{'='})
    85  	var isURL = !bytes.ContainsAny(src, "+/")
    86  	switch {
    87  	case isRaw && isURL:
    88  		return RawURL
    89  	case isURL:
    90  		return URL
    91  	case isRaw:
    92  		return RawStd
    93  	default:
    94  		return Std
    95  	}
    96  }
    97  
    98  // defaultDecoder is a Decoder that detects the encoding of the source and
    99  // decodes it accordingly. This shouldn't really be required per the spec, but
   100  // it exist because we have seen in the wild JWTs that are encoded using
   101  // various versions of the base64 encoding.
   102  type defaultDecoder struct{}
   103  
   104  func (defaultDecoder) Decode(src []byte) ([]byte, error) {
   105  	var enc *base64.Encoding
   106  
   107  	switch Guess(src) {
   108  	case RawURL:
   109  		enc = base64.RawURLEncoding
   110  	case URL:
   111  		enc = base64.URLEncoding
   112  	case RawStd:
   113  		enc = base64.RawStdEncoding
   114  	case Std:
   115  		enc = base64.StdEncoding
   116  	default:
   117  		return nil, fmt.Errorf(`invalid encoding`)
   118  	}
   119  
   120  	dst := make([]byte, enc.DecodedLen(len(src)))
   121  	n, err := enc.Decode(dst, src)
   122  	if err != nil {
   123  		return nil, fmt.Errorf(`failed to decode source: %w`, err)
   124  	}
   125  	return dst[:n], nil
   126  }
   127  
   128  func Decode(src []byte) ([]byte, error) {
   129  	return getDecoder().Decode(src)
   130  }
   131  
   132  func DecodeString(src string) ([]byte, error) {
   133  	return getDecoder().Decode([]byte(src))
   134  }