jws.go 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  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. package jose
  17. import (
  18. "encoding/base64"
  19. "errors"
  20. "fmt"
  21. "strings"
  22. "gopkg.in/square/go-jose.v2/json"
  23. )
  24. // rawJSONWebSignature represents a raw JWS JSON object. Used for parsing/serializing.
  25. type rawJSONWebSignature struct {
  26. Payload *byteBuffer `json:"payload,omitempty"`
  27. Signatures []rawSignatureInfo `json:"signatures,omitempty"`
  28. Protected *byteBuffer `json:"protected,omitempty"`
  29. Header *rawHeader `json:"header,omitempty"`
  30. Signature *byteBuffer `json:"signature,omitempty"`
  31. }
  32. // rawSignatureInfo represents a single JWS signature over the JWS payload and protected header.
  33. type rawSignatureInfo struct {
  34. Protected *byteBuffer `json:"protected,omitempty"`
  35. Header *rawHeader `json:"header,omitempty"`
  36. Signature *byteBuffer `json:"signature,omitempty"`
  37. }
  38. // JSONWebSignature represents a signed JWS object after parsing.
  39. type JSONWebSignature struct {
  40. payload []byte
  41. // Signatures attached to this object (may be more than one for multi-sig).
  42. // Be careful about accessing these directly, prefer to use Verify() or
  43. // VerifyMulti() to ensure that the data you're getting is verified.
  44. Signatures []Signature
  45. }
  46. // Signature represents a single signature over the JWS payload and protected header.
  47. type Signature struct {
  48. // Merged header fields. Contains both protected and unprotected header
  49. // values. Prefer using Protected and Unprotected fields instead of this.
  50. // Values in this header may or may not have been signed and in general
  51. // should not be trusted.
  52. Header Header
  53. // Protected header. Values in this header were signed and
  54. // will be verified as part of the signature verification process.
  55. Protected Header
  56. // Unprotected header. Values in this header were not signed
  57. // and in general should not be trusted.
  58. Unprotected Header
  59. // The actual signature value
  60. Signature []byte
  61. protected *rawHeader
  62. header *rawHeader
  63. original *rawSignatureInfo
  64. }
  65. // ParseSigned parses a signed message in compact or full serialization format.
  66. func ParseSigned(input string) (*JSONWebSignature, error) {
  67. input = stripWhitespace(input)
  68. if strings.HasPrefix(input, "{") {
  69. return parseSignedFull(input)
  70. }
  71. return parseSignedCompact(input)
  72. }
  73. // Get a header value
  74. func (sig Signature) mergedHeaders() rawHeader {
  75. out := rawHeader{}
  76. out.merge(sig.protected)
  77. out.merge(sig.header)
  78. return out
  79. }
  80. // Compute data to be signed
  81. func (obj JSONWebSignature) computeAuthData(payload []byte, signature *Signature) []byte {
  82. var serializedProtected string
  83. if signature.original != nil && signature.original.Protected != nil {
  84. serializedProtected = signature.original.Protected.base64()
  85. } else if signature.protected != nil {
  86. serializedProtected = base64.RawURLEncoding.EncodeToString(mustSerializeJSON(signature.protected))
  87. } else {
  88. serializedProtected = ""
  89. }
  90. return []byte(fmt.Sprintf("%s.%s",
  91. serializedProtected,
  92. base64.RawURLEncoding.EncodeToString(payload)))
  93. }
  94. // parseSignedFull parses a message in full format.
  95. func parseSignedFull(input string) (*JSONWebSignature, error) {
  96. var parsed rawJSONWebSignature
  97. err := json.Unmarshal([]byte(input), &parsed)
  98. if err != nil {
  99. return nil, err
  100. }
  101. return parsed.sanitized()
  102. }
  103. // sanitized produces a cleaned-up JWS object from the raw JSON.
  104. func (parsed *rawJSONWebSignature) sanitized() (*JSONWebSignature, error) {
  105. if parsed.Payload == nil {
  106. return nil, fmt.Errorf("square/go-jose: missing payload in JWS message")
  107. }
  108. obj := &JSONWebSignature{
  109. payload: parsed.Payload.bytes(),
  110. Signatures: make([]Signature, len(parsed.Signatures)),
  111. }
  112. if len(parsed.Signatures) == 0 {
  113. // No signatures array, must be flattened serialization
  114. signature := Signature{}
  115. if parsed.Protected != nil && len(parsed.Protected.bytes()) > 0 {
  116. signature.protected = &rawHeader{}
  117. err := json.Unmarshal(parsed.Protected.bytes(), signature.protected)
  118. if err != nil {
  119. return nil, err
  120. }
  121. }
  122. // Check that there is not a nonce in the unprotected header
  123. if parsed.Header != nil && parsed.Header.getNonce() != "" {
  124. return nil, ErrUnprotectedNonce
  125. }
  126. signature.header = parsed.Header
  127. signature.Signature = parsed.Signature.bytes()
  128. // Make a fake "original" rawSignatureInfo to store the unprocessed
  129. // Protected header. This is necessary because the Protected header can
  130. // contain arbitrary fields not registered as part of the spec. See
  131. // https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-4
  132. // If we unmarshal Protected into a rawHeader with its explicit list of fields,
  133. // we cannot marshal losslessly. So we have to keep around the original bytes.
  134. // This is used in computeAuthData, which will first attempt to use
  135. // the original bytes of a protected header, and fall back on marshaling the
  136. // header struct only if those bytes are not available.
  137. signature.original = &rawSignatureInfo{
  138. Protected: parsed.Protected,
  139. Header: parsed.Header,
  140. Signature: parsed.Signature,
  141. }
  142. var err error
  143. signature.Header, err = signature.mergedHeaders().sanitized()
  144. if err != nil {
  145. return nil, err
  146. }
  147. if signature.header != nil {
  148. signature.Unprotected, err = signature.header.sanitized()
  149. if err != nil {
  150. return nil, err
  151. }
  152. }
  153. if signature.protected != nil {
  154. signature.Protected, err = signature.protected.sanitized()
  155. if err != nil {
  156. return nil, err
  157. }
  158. }
  159. // As per RFC 7515 Section 4.1.3, only public keys are allowed to be embedded.
  160. jwk := signature.Header.JSONWebKey
  161. if jwk != nil && (!jwk.Valid() || !jwk.IsPublic()) {
  162. return nil, errors.New("square/go-jose: invalid embedded jwk, must be public key")
  163. }
  164. obj.Signatures = append(obj.Signatures, signature)
  165. }
  166. for i, sig := range parsed.Signatures {
  167. if sig.Protected != nil && len(sig.Protected.bytes()) > 0 {
  168. obj.Signatures[i].protected = &rawHeader{}
  169. err := json.Unmarshal(sig.Protected.bytes(), obj.Signatures[i].protected)
  170. if err != nil {
  171. return nil, err
  172. }
  173. }
  174. // Check that there is not a nonce in the unprotected header
  175. if sig.Header != nil && sig.Header.getNonce() != "" {
  176. return nil, ErrUnprotectedNonce
  177. }
  178. var err error
  179. obj.Signatures[i].Header, err = obj.Signatures[i].mergedHeaders().sanitized()
  180. if err != nil {
  181. return nil, err
  182. }
  183. if obj.Signatures[i].header != nil {
  184. obj.Signatures[i].Unprotected, err = obj.Signatures[i].header.sanitized()
  185. if err != nil {
  186. return nil, err
  187. }
  188. }
  189. if obj.Signatures[i].protected != nil {
  190. obj.Signatures[i].Protected, err = obj.Signatures[i].protected.sanitized()
  191. if err != nil {
  192. return nil, err
  193. }
  194. }
  195. obj.Signatures[i].Signature = sig.Signature.bytes()
  196. // As per RFC 7515 Section 4.1.3, only public keys are allowed to be embedded.
  197. jwk := obj.Signatures[i].Header.JSONWebKey
  198. if jwk != nil && (!jwk.Valid() || !jwk.IsPublic()) {
  199. return nil, errors.New("square/go-jose: invalid embedded jwk, must be public key")
  200. }
  201. // Copy value of sig
  202. original := sig
  203. obj.Signatures[i].header = sig.Header
  204. obj.Signatures[i].original = &original
  205. }
  206. return obj, nil
  207. }
  208. // parseSignedCompact parses a message in compact format.
  209. func parseSignedCompact(input string) (*JSONWebSignature, error) {
  210. parts := strings.Split(input, ".")
  211. if len(parts) != 3 {
  212. return nil, fmt.Errorf("square/go-jose: compact JWS format must have three parts")
  213. }
  214. rawProtected, err := base64.RawURLEncoding.DecodeString(parts[0])
  215. if err != nil {
  216. return nil, err
  217. }
  218. payload, err := base64.RawURLEncoding.DecodeString(parts[1])
  219. if err != nil {
  220. return nil, err
  221. }
  222. signature, err := base64.RawURLEncoding.DecodeString(parts[2])
  223. if err != nil {
  224. return nil, err
  225. }
  226. raw := &rawJSONWebSignature{
  227. Payload: newBuffer(payload),
  228. Protected: newBuffer(rawProtected),
  229. Signature: newBuffer(signature),
  230. }
  231. return raw.sanitized()
  232. }
  233. // CompactSerialize serializes an object using the compact serialization format.
  234. func (obj JSONWebSignature) CompactSerialize() (string, error) {
  235. if len(obj.Signatures) != 1 || obj.Signatures[0].header != nil || obj.Signatures[0].protected == nil {
  236. return "", ErrNotSupported
  237. }
  238. serializedProtected := mustSerializeJSON(obj.Signatures[0].protected)
  239. return fmt.Sprintf(
  240. "%s.%s.%s",
  241. base64.RawURLEncoding.EncodeToString(serializedProtected),
  242. base64.RawURLEncoding.EncodeToString(obj.payload),
  243. base64.RawURLEncoding.EncodeToString(obj.Signatures[0].Signature)), nil
  244. }
  245. // FullSerialize serializes an object using the full JSON serialization format.
  246. func (obj JSONWebSignature) FullSerialize() string {
  247. raw := rawJSONWebSignature{
  248. Payload: newBuffer(obj.payload),
  249. }
  250. if len(obj.Signatures) == 1 {
  251. if obj.Signatures[0].protected != nil {
  252. serializedProtected := mustSerializeJSON(obj.Signatures[0].protected)
  253. raw.Protected = newBuffer(serializedProtected)
  254. }
  255. raw.Header = obj.Signatures[0].header
  256. raw.Signature = newBuffer(obj.Signatures[0].Signature)
  257. } else {
  258. raw.Signatures = make([]rawSignatureInfo, len(obj.Signatures))
  259. for i, signature := range obj.Signatures {
  260. raw.Signatures[i] = rawSignatureInfo{
  261. Header: signature.header,
  262. Signature: newBuffer(signature.Signature),
  263. }
  264. if signature.protected != nil {
  265. raw.Signatures[i].Protected = newBuffer(mustSerializeJSON(signature.protected))
  266. }
  267. }
  268. }
  269. return string(mustSerializeJSON(raw))
  270. }