gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

jwe.go (8686B)


      1 /*-
      2  * Copyright 2014 Square Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package jose
     18 
     19 import (
     20 	"encoding/base64"
     21 	"fmt"
     22 	"strings"
     23 
     24 	"github.com/go-jose/go-jose/v3/json"
     25 )
     26 
     27 // rawJSONWebEncryption represents a raw JWE JSON object. Used for parsing/serializing.
     28 type rawJSONWebEncryption struct {
     29 	Protected    *byteBuffer        `json:"protected,omitempty"`
     30 	Unprotected  *rawHeader         `json:"unprotected,omitempty"`
     31 	Header       *rawHeader         `json:"header,omitempty"`
     32 	Recipients   []rawRecipientInfo `json:"recipients,omitempty"`
     33 	Aad          *byteBuffer        `json:"aad,omitempty"`
     34 	EncryptedKey *byteBuffer        `json:"encrypted_key,omitempty"`
     35 	Iv           *byteBuffer        `json:"iv,omitempty"`
     36 	Ciphertext   *byteBuffer        `json:"ciphertext,omitempty"`
     37 	Tag          *byteBuffer        `json:"tag,omitempty"`
     38 }
     39 
     40 // rawRecipientInfo represents a raw JWE Per-Recipient header JSON object. Used for parsing/serializing.
     41 type rawRecipientInfo struct {
     42 	Header       *rawHeader `json:"header,omitempty"`
     43 	EncryptedKey string     `json:"encrypted_key,omitempty"`
     44 }
     45 
     46 // JSONWebEncryption represents an encrypted JWE object after parsing.
     47 type JSONWebEncryption struct {
     48 	Header                   Header
     49 	protected, unprotected   *rawHeader
     50 	recipients               []recipientInfo
     51 	aad, iv, ciphertext, tag []byte
     52 	original                 *rawJSONWebEncryption
     53 }
     54 
     55 // recipientInfo represents a raw JWE Per-Recipient header JSON object after parsing.
     56 type recipientInfo struct {
     57 	header       *rawHeader
     58 	encryptedKey []byte
     59 }
     60 
     61 // GetAuthData retrieves the (optional) authenticated data attached to the object.
     62 func (obj JSONWebEncryption) GetAuthData() []byte {
     63 	if obj.aad != nil {
     64 		out := make([]byte, len(obj.aad))
     65 		copy(out, obj.aad)
     66 		return out
     67 	}
     68 
     69 	return nil
     70 }
     71 
     72 // Get the merged header values
     73 func (obj JSONWebEncryption) mergedHeaders(recipient *recipientInfo) rawHeader {
     74 	out := rawHeader{}
     75 	out.merge(obj.protected)
     76 	out.merge(obj.unprotected)
     77 
     78 	if recipient != nil {
     79 		out.merge(recipient.header)
     80 	}
     81 
     82 	return out
     83 }
     84 
     85 // Get the additional authenticated data from a JWE object.
     86 func (obj JSONWebEncryption) computeAuthData() []byte {
     87 	var protected string
     88 
     89 	switch {
     90 	case obj.original != nil && obj.original.Protected != nil:
     91 		protected = obj.original.Protected.base64()
     92 	case obj.protected != nil:
     93 		protected = base64.RawURLEncoding.EncodeToString(mustSerializeJSON((obj.protected)))
     94 	default:
     95 		protected = ""
     96 	}
     97 
     98 	output := []byte(protected)
     99 	if obj.aad != nil {
    100 		output = append(output, '.')
    101 		output = append(output, []byte(base64.RawURLEncoding.EncodeToString(obj.aad))...)
    102 	}
    103 
    104 	return output
    105 }
    106 
    107 // ParseEncrypted parses an encrypted message in compact or JWE JSON Serialization format.
    108 func ParseEncrypted(input string) (*JSONWebEncryption, error) {
    109 	input = stripWhitespace(input)
    110 	if strings.HasPrefix(input, "{") {
    111 		return parseEncryptedFull(input)
    112 	}
    113 
    114 	return parseEncryptedCompact(input)
    115 }
    116 
    117 // parseEncryptedFull parses a message in compact format.
    118 func parseEncryptedFull(input string) (*JSONWebEncryption, error) {
    119 	var parsed rawJSONWebEncryption
    120 	err := json.Unmarshal([]byte(input), &parsed)
    121 	if err != nil {
    122 		return nil, err
    123 	}
    124 
    125 	return parsed.sanitized()
    126 }
    127 
    128 // sanitized produces a cleaned-up JWE object from the raw JSON.
    129 func (parsed *rawJSONWebEncryption) sanitized() (*JSONWebEncryption, error) {
    130 	obj := &JSONWebEncryption{
    131 		original:    parsed,
    132 		unprotected: parsed.Unprotected,
    133 	}
    134 
    135 	// Check that there is not a nonce in the unprotected headers
    136 	if parsed.Unprotected != nil {
    137 		if nonce := parsed.Unprotected.getNonce(); nonce != "" {
    138 			return nil, ErrUnprotectedNonce
    139 		}
    140 	}
    141 	if parsed.Header != nil {
    142 		if nonce := parsed.Header.getNonce(); nonce != "" {
    143 			return nil, ErrUnprotectedNonce
    144 		}
    145 	}
    146 
    147 	if parsed.Protected != nil && len(parsed.Protected.bytes()) > 0 {
    148 		err := json.Unmarshal(parsed.Protected.bytes(), &obj.protected)
    149 		if err != nil {
    150 			return nil, fmt.Errorf("go-jose/go-jose: invalid protected header: %s, %s", err, parsed.Protected.base64())
    151 		}
    152 	}
    153 
    154 	// Note: this must be called _after_ we parse the protected header,
    155 	// otherwise fields from the protected header will not get picked up.
    156 	var err error
    157 	mergedHeaders := obj.mergedHeaders(nil)
    158 	obj.Header, err = mergedHeaders.sanitized()
    159 	if err != nil {
    160 		return nil, fmt.Errorf("go-jose/go-jose: cannot sanitize merged headers: %v (%v)", err, mergedHeaders)
    161 	}
    162 
    163 	if len(parsed.Recipients) == 0 {
    164 		obj.recipients = []recipientInfo{
    165 			{
    166 				header:       parsed.Header,
    167 				encryptedKey: parsed.EncryptedKey.bytes(),
    168 			},
    169 		}
    170 	} else {
    171 		obj.recipients = make([]recipientInfo, len(parsed.Recipients))
    172 		for r := range parsed.Recipients {
    173 			encryptedKey, err := base64URLDecode(parsed.Recipients[r].EncryptedKey)
    174 			if err != nil {
    175 				return nil, err
    176 			}
    177 
    178 			// Check that there is not a nonce in the unprotected header
    179 			if parsed.Recipients[r].Header != nil && parsed.Recipients[r].Header.getNonce() != "" {
    180 				return nil, ErrUnprotectedNonce
    181 			}
    182 
    183 			obj.recipients[r].header = parsed.Recipients[r].Header
    184 			obj.recipients[r].encryptedKey = encryptedKey
    185 		}
    186 	}
    187 
    188 	for _, recipient := range obj.recipients {
    189 		headers := obj.mergedHeaders(&recipient)
    190 		if headers.getAlgorithm() == "" || headers.getEncryption() == "" {
    191 			return nil, fmt.Errorf("go-jose/go-jose: message is missing alg/enc headers")
    192 		}
    193 	}
    194 
    195 	obj.iv = parsed.Iv.bytes()
    196 	obj.ciphertext = parsed.Ciphertext.bytes()
    197 	obj.tag = parsed.Tag.bytes()
    198 	obj.aad = parsed.Aad.bytes()
    199 
    200 	return obj, nil
    201 }
    202 
    203 // parseEncryptedCompact parses a message in compact format.
    204 func parseEncryptedCompact(input string) (*JSONWebEncryption, error) {
    205 	parts := strings.Split(input, ".")
    206 	if len(parts) != 5 {
    207 		return nil, fmt.Errorf("go-jose/go-jose: compact JWE format must have five parts")
    208 	}
    209 
    210 	rawProtected, err := base64URLDecode(parts[0])
    211 	if err != nil {
    212 		return nil, err
    213 	}
    214 
    215 	encryptedKey, err := base64URLDecode(parts[1])
    216 	if err != nil {
    217 		return nil, err
    218 	}
    219 
    220 	iv, err := base64URLDecode(parts[2])
    221 	if err != nil {
    222 		return nil, err
    223 	}
    224 
    225 	ciphertext, err := base64URLDecode(parts[3])
    226 	if err != nil {
    227 		return nil, err
    228 	}
    229 
    230 	tag, err := base64URLDecode(parts[4])
    231 	if err != nil {
    232 		return nil, err
    233 	}
    234 
    235 	raw := &rawJSONWebEncryption{
    236 		Protected:    newBuffer(rawProtected),
    237 		EncryptedKey: newBuffer(encryptedKey),
    238 		Iv:           newBuffer(iv),
    239 		Ciphertext:   newBuffer(ciphertext),
    240 		Tag:          newBuffer(tag),
    241 	}
    242 
    243 	return raw.sanitized()
    244 }
    245 
    246 // CompactSerialize serializes an object using the compact serialization format.
    247 func (obj JSONWebEncryption) CompactSerialize() (string, error) {
    248 	if len(obj.recipients) != 1 || obj.unprotected != nil ||
    249 		obj.protected == nil || obj.recipients[0].header != nil {
    250 		return "", ErrNotSupported
    251 	}
    252 
    253 	serializedProtected := mustSerializeJSON(obj.protected)
    254 
    255 	return fmt.Sprintf(
    256 		"%s.%s.%s.%s.%s",
    257 		base64.RawURLEncoding.EncodeToString(serializedProtected),
    258 		base64.RawURLEncoding.EncodeToString(obj.recipients[0].encryptedKey),
    259 		base64.RawURLEncoding.EncodeToString(obj.iv),
    260 		base64.RawURLEncoding.EncodeToString(obj.ciphertext),
    261 		base64.RawURLEncoding.EncodeToString(obj.tag)), nil
    262 }
    263 
    264 // FullSerialize serializes an object using the full JSON serialization format.
    265 func (obj JSONWebEncryption) FullSerialize() string {
    266 	raw := rawJSONWebEncryption{
    267 		Unprotected:  obj.unprotected,
    268 		Iv:           newBuffer(obj.iv),
    269 		Ciphertext:   newBuffer(obj.ciphertext),
    270 		EncryptedKey: newBuffer(obj.recipients[0].encryptedKey),
    271 		Tag:          newBuffer(obj.tag),
    272 		Aad:          newBuffer(obj.aad),
    273 		Recipients:   []rawRecipientInfo{},
    274 	}
    275 
    276 	if len(obj.recipients) > 1 {
    277 		for _, recipient := range obj.recipients {
    278 			info := rawRecipientInfo{
    279 				Header:       recipient.header,
    280 				EncryptedKey: base64.RawURLEncoding.EncodeToString(recipient.encryptedKey),
    281 			}
    282 			raw.Recipients = append(raw.Recipients, info)
    283 		}
    284 	} else {
    285 		// Use flattened serialization
    286 		raw.Header = obj.recipients[0].header
    287 		raw.EncryptedKey = newBuffer(obj.recipients[0].encryptedKey)
    288 	}
    289 
    290 	if obj.protected != nil {
    291 		raw.Protected = newBuffer(mustSerializeJSON(obj.protected))
    292 	}
    293 
    294 	return string(mustSerializeJSON(raw))
    295 }