...

Source file src/gopkg.in/go-jose/go-jose.v2/jwe.go

Documentation: gopkg.in/go-jose/go-jose.v2

     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  	"gopkg.in/go-jose/go-jose.v2/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  	if obj.original != nil && obj.original.Protected != nil {
    90  		protected = obj.original.Protected.base64()
    91  	} else if obj.protected != nil {
    92  		protected = base64.RawURLEncoding.EncodeToString(mustSerializeJSON((obj.protected)))
    93  	} else {
    94  		protected = ""
    95  	}
    96  
    97  	output := []byte(protected)
    98  	if obj.aad != nil {
    99  		output = append(output, '.')
   100  		output = append(output, []byte(base64.RawURLEncoding.EncodeToString(obj.aad))...)
   101  	}
   102  
   103  	return output
   104  }
   105  
   106  // ParseEncrypted parses an encrypted message in compact or full serialization format.
   107  func ParseEncrypted(input string) (*JSONWebEncryption, error) {
   108  	input = stripWhitespace(input)
   109  	if strings.HasPrefix(input, "{") {
   110  		return parseEncryptedFull(input)
   111  	}
   112  
   113  	return parseEncryptedCompact(input)
   114  }
   115  
   116  // parseEncryptedFull parses a message in compact format.
   117  func parseEncryptedFull(input string) (*JSONWebEncryption, error) {
   118  	var parsed rawJSONWebEncryption
   119  	err := json.Unmarshal([]byte(input), &parsed)
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  
   124  	return parsed.sanitized()
   125  }
   126  
   127  // sanitized produces a cleaned-up JWE object from the raw JSON.
   128  func (parsed *rawJSONWebEncryption) sanitized() (*JSONWebEncryption, error) {
   129  	obj := &JSONWebEncryption{
   130  		original:    parsed,
   131  		unprotected: parsed.Unprotected,
   132  	}
   133  
   134  	// Check that there is not a nonce in the unprotected headers
   135  	if parsed.Unprotected != nil {
   136  		if nonce := parsed.Unprotected.getNonce(); nonce != "" {
   137  			return nil, ErrUnprotectedNonce
   138  		}
   139  	}
   140  	if parsed.Header != nil {
   141  		if nonce := parsed.Header.getNonce(); nonce != "" {
   142  			return nil, ErrUnprotectedNonce
   143  		}
   144  	}
   145  
   146  	if parsed.Protected != nil && len(parsed.Protected.bytes()) > 0 {
   147  		err := json.Unmarshal(parsed.Protected.bytes(), &obj.protected)
   148  		if err != nil {
   149  			return nil, fmt.Errorf("go-jose/go-jose: invalid protected header: %s, %s", err, parsed.Protected.base64())
   150  		}
   151  	}
   152  
   153  	// Note: this must be called _after_ we parse the protected header,
   154  	// otherwise fields from the protected header will not get picked up.
   155  	var err error
   156  	mergedHeaders := obj.mergedHeaders(nil)
   157  	obj.Header, err = mergedHeaders.sanitized()
   158  	if err != nil {
   159  		return nil, fmt.Errorf("go-jose/go-jose: cannot sanitize merged headers: %v (%v)", err, mergedHeaders)
   160  	}
   161  
   162  	if len(parsed.Recipients) == 0 {
   163  		obj.recipients = []recipientInfo{
   164  			{
   165  				header:       parsed.Header,
   166  				encryptedKey: parsed.EncryptedKey.bytes(),
   167  			},
   168  		}
   169  	} else {
   170  		obj.recipients = make([]recipientInfo, len(parsed.Recipients))
   171  		for r := range parsed.Recipients {
   172  			encryptedKey, err := base64.RawURLEncoding.DecodeString(parsed.Recipients[r].EncryptedKey)
   173  			if err != nil {
   174  				return nil, err
   175  			}
   176  
   177  			// Check that there is not a nonce in the unprotected header
   178  			if parsed.Recipients[r].Header != nil && parsed.Recipients[r].Header.getNonce() != "" {
   179  				return nil, ErrUnprotectedNonce
   180  			}
   181  
   182  			obj.recipients[r].header = parsed.Recipients[r].Header
   183  			obj.recipients[r].encryptedKey = encryptedKey
   184  		}
   185  	}
   186  
   187  	for _, recipient := range obj.recipients {
   188  		headers := obj.mergedHeaders(&recipient)
   189  		if headers.getAlgorithm() == "" || headers.getEncryption() == "" {
   190  			return nil, fmt.Errorf("go-jose/go-jose: message is missing alg/enc headers")
   191  		}
   192  	}
   193  
   194  	obj.iv = parsed.Iv.bytes()
   195  	obj.ciphertext = parsed.Ciphertext.bytes()
   196  	obj.tag = parsed.Tag.bytes()
   197  	obj.aad = parsed.Aad.bytes()
   198  
   199  	return obj, nil
   200  }
   201  
   202  // parseEncryptedCompact parses a message in compact format.
   203  func parseEncryptedCompact(input string) (*JSONWebEncryption, error) {
   204  	parts := strings.Split(input, ".")
   205  	if len(parts) != 5 {
   206  		return nil, fmt.Errorf("go-jose/go-jose: compact JWE format must have five parts")
   207  	}
   208  
   209  	rawProtected, err := base64.RawURLEncoding.DecodeString(parts[0])
   210  	if err != nil {
   211  		return nil, err
   212  	}
   213  
   214  	encryptedKey, err := base64.RawURLEncoding.DecodeString(parts[1])
   215  	if err != nil {
   216  		return nil, err
   217  	}
   218  
   219  	iv, err := base64.RawURLEncoding.DecodeString(parts[2])
   220  	if err != nil {
   221  		return nil, err
   222  	}
   223  
   224  	ciphertext, err := base64.RawURLEncoding.DecodeString(parts[3])
   225  	if err != nil {
   226  		return nil, err
   227  	}
   228  
   229  	tag, err := base64.RawURLEncoding.DecodeString(parts[4])
   230  	if err != nil {
   231  		return nil, err
   232  	}
   233  
   234  	raw := &rawJSONWebEncryption{
   235  		Protected:    newBuffer(rawProtected),
   236  		EncryptedKey: newBuffer(encryptedKey),
   237  		Iv:           newBuffer(iv),
   238  		Ciphertext:   newBuffer(ciphertext),
   239  		Tag:          newBuffer(tag),
   240  	}
   241  
   242  	return raw.sanitized()
   243  }
   244  
   245  // CompactSerialize serializes an object using the compact serialization format.
   246  func (obj JSONWebEncryption) CompactSerialize() (string, error) {
   247  	if len(obj.recipients) != 1 || obj.unprotected != nil ||
   248  		obj.protected == nil || obj.recipients[0].header != nil {
   249  		return "", ErrNotSupported
   250  	}
   251  
   252  	serializedProtected := mustSerializeJSON(obj.protected)
   253  
   254  	return fmt.Sprintf(
   255  		"%s.%s.%s.%s.%s",
   256  		base64.RawURLEncoding.EncodeToString(serializedProtected),
   257  		base64.RawURLEncoding.EncodeToString(obj.recipients[0].encryptedKey),
   258  		base64.RawURLEncoding.EncodeToString(obj.iv),
   259  		base64.RawURLEncoding.EncodeToString(obj.ciphertext),
   260  		base64.RawURLEncoding.EncodeToString(obj.tag)), nil
   261  }
   262  
   263  // FullSerialize serializes an object using the full JSON serialization format.
   264  func (obj JSONWebEncryption) FullSerialize() string {
   265  	raw := rawJSONWebEncryption{
   266  		Unprotected:  obj.unprotected,
   267  		Iv:           newBuffer(obj.iv),
   268  		Ciphertext:   newBuffer(obj.ciphertext),
   269  		EncryptedKey: newBuffer(obj.recipients[0].encryptedKey),
   270  		Tag:          newBuffer(obj.tag),
   271  		Aad:          newBuffer(obj.aad),
   272  		Recipients:   []rawRecipientInfo{},
   273  	}
   274  
   275  	if len(obj.recipients) > 1 {
   276  		for _, recipient := range obj.recipients {
   277  			info := rawRecipientInfo{
   278  				Header:       recipient.header,
   279  				EncryptedKey: base64.RawURLEncoding.EncodeToString(recipient.encryptedKey),
   280  			}
   281  			raw.Recipients = append(raw.Recipients, info)
   282  		}
   283  	} else {
   284  		// Use flattened serialization
   285  		raw.Header = obj.recipients[0].header
   286  		raw.EncryptedKey = newBuffer(obj.recipients[0].encryptedKey)
   287  	}
   288  
   289  	if obj.protected != nil {
   290  		raw.Protected = newBuffer(mustSerializeJSON(obj.protected))
   291  	}
   292  
   293  	return string(mustSerializeJSON(raw))
   294  }
   295  

View as plain text