1// Package buffer provides a very thin wrapper around []byte buffer called
2// `Buffer`, to provide functionalities that are often used within the jwx
3// related packages
4package buffer
5
6import (
7	"encoding/base64"
8	"encoding/binary"
9	"encoding/json"
10
11	"github.com/pkg/errors"
12)
13
14// Buffer wraps `[]byte` and provides functions that are often used in
15// the jwx related packages. One notable difference is that while
16// encoding/json marshalls `[]byte` using base64.StdEncoding, this
17// module uses base64.RawURLEncoding as mandated by the spec
18type Buffer []byte
19
20// FromUint creates a `Buffer` from an unsigned int
21func FromUint(v uint64) Buffer {
22	data := make([]byte, 8)
23	binary.BigEndian.PutUint64(data, v)
24
25	i := 0
26	for ; i < len(data); i++ {
27		if data[i] != 0x0 {
28			break
29		}
30	}
31	return Buffer(data[i:])
32}
33
34// FromBase64 constructs a new Buffer from a base64 encoded data
35func FromBase64(v []byte) (Buffer, error) {
36	b := Buffer{}
37	if err := b.Base64Decode(v); err != nil {
38		return Buffer(nil), errors.Wrap(err, "failed to decode from base64")
39	}
40
41	return b, nil
42}
43
44// FromNData constructs a new Buffer from a "n:data" format
45// (I made that name up)
46func FromNData(v []byte) (Buffer, error) {
47	size := binary.BigEndian.Uint32(v)
48	buf := make([]byte, int(size))
49	copy(buf, v[4:4+size])
50	return Buffer(buf), nil
51}
52
53// Bytes returns the raw bytes that comprises the Buffer
54func (b Buffer) Bytes() []byte {
55	return []byte(b)
56}
57
58// NData returns Datalen || Data, where Datalen is a 32 bit counter for
59// the length of the following data, and Data is the octets that comprise
60// the buffer data
61func (b Buffer) NData() []byte {
62	buf := make([]byte, 4+b.Len())
63	binary.BigEndian.PutUint32(buf, uint32(b.Len()))
64
65	copy(buf[4:], b.Bytes())
66	return buf
67}
68
69// Len returns the number of bytes that the Buffer holds
70func (b Buffer) Len() int {
71	return len(b)
72}
73
74func (b *Buffer) SetBytes(b2 []byte) {
75	*b = make([]byte, len(b2))
76	copy(*b, b2)
77}
78
79// Base64Encode encodes the contents of the Buffer using base64.RawURLEncoding
80func (b Buffer) Base64Encode() ([]byte, error) {
81	enc := base64.RawURLEncoding
82	out := make([]byte, enc.EncodedLen(len(b)))
83	enc.Encode(out, b)
84	return out, nil
85}
86
87// Base64Decode decodes the contents of the Buffer using base64.RawURLEncoding
88func (b *Buffer) Base64Decode(v []byte) error {
89	enc := base64.RawURLEncoding
90	out := make([]byte, enc.DecodedLen(len(v)))
91	n, err := enc.Decode(out, v)
92	if err != nil {
93		return errors.Wrap(err, "failed to decode from base64")
94	}
95	out = out[:n]
96	*b = Buffer(out)
97	return nil
98}
99
100// MarshalJSON marshals the buffer into JSON format after encoding the buffer
101// with base64.RawURLEncoding
102func (b Buffer) MarshalJSON() ([]byte, error) {
103	v, err := b.Base64Encode()
104	if err != nil {
105		return nil, errors.Wrap(err, "failed to encode to base64")
106	}
107	return json.Marshal(string(v))
108}
109
110// UnmarshalJSON unmarshals from a JSON string into a Buffer, after decoding it
111// with base64.RawURLEncoding
112func (b *Buffer) UnmarshalJSON(data []byte) error {
113	var x string
114	if err := json.Unmarshal(data, &x); err != nil {
115		return errors.Wrap(err, "failed to unmarshal JSON")
116	}
117	return b.Base64Decode([]byte(x))
118}
119