5f16f22016-11-05Martin Nilsson #pike __REAL_VERSION__
2003262016-04-20Henrik Grubbström (Grubba) //! //! Modules implementing various web standards. //!
110b992017-03-22Henrik Grubbström (Grubba) //! Encode a JSON Web Signature (JWS). //! //! @param sign //! The asymetric private or MAC key to use for signing the result. //! //! @param tbs //! The value to sign. //!
c6c5872017-03-22Henrik Grubbström (Grubba) //! @param media_type //! The media type of @[tbs], cf @rfc{7515:4.1.9@}.
110b992017-03-22Henrik Grubbström (Grubba) //! //! @returns //! Returns @expr{0@} (zero) on encoding failure (usually //! that @[sign] doesn't support JWS. //! //! Returns a corresponding JWS on success. //! //! @seealso //! @[decode_jwt()], @rfc{7515@} string(7bit) encode_jws(Crypto.Sign.State|Crypto.MAC.State sign,
c6c5872017-03-22Henrik Grubbström (Grubba)  mixed tbs, string(7bit)|void media_type)
110b992017-03-22Henrik Grubbström (Grubba) { string json_tbs = Standards.JSON.encode(tbs);
c6c5872017-03-22Henrik Grubbström (Grubba)  mapping(string(7bit):string(7bit)) header = ([]); if (media_type) { header->typ = media_type; }
110b992017-03-22Henrik Grubbström (Grubba)  return sign->jose_sign &&
c6c5872017-03-22Henrik Grubbström (Grubba)  sign->jose_sign(string_to_utf8(json_tbs), header);
110b992017-03-22Henrik Grubbström (Grubba) } //! Decode a JSON Web Signature (JWS). //! //! @param sign //! The asymetric public or MAC key(s) to validate the jws against. //! //! @param jws //! A JWS as eg returned by @[encode_jws()]. //! //! @returns //! Returns @expr{0@} (zero) on validation failure. //! //! Returns an array with two elements on success: //! @array //! @elem mapping(string(7bit):string(7bit)|int) 0 //! The JOSE header. //! @elem mixed 1 //! The JWS payload. //! @endarray //! See @rfc{7515:3@}. //! //! @seealso //! @[encode_jws()], @[decode_jwt()], @[Crypto.Sign.State()->jose_decode()], //! @rfc{7515@} array decode_jws(array(Crypto.Sign.State|Crypto.MAC.State)| Crypto.Sign.State|Crypto.MAC.State sign, string(7bit) jws) { if (!arrayp(sign)) sign = ({ sign }); array(mapping(string(7bit):string(7bit)|int)|string(8bit)) decoded_jws; foreach(sign, Crypto.Sign s) { if (decoded_jws = s->jose_decode(jws)) { break; } } if (!decoded_jws) return 0; catch {
7dafa12018-02-20Henrik Grubbström (Grubba)  decoded_jws[1] = Standards.JSON.decode_utf8(decoded_jws[1]);
110b992017-03-22Henrik Grubbström (Grubba)  return decoded_jws; }; return 0; }
3cc5d02017-05-30Martin Nilsson //! Encode a JSON Web Token (JWT). Automatically adds the optional //! "issued at" timestamp and JWD ID (using UUID v4).
2003262016-04-20Henrik Grubbström (Grubba) //! //! @param sign
4878c92016-05-04Henrik Grubbström (Grubba) //! The asymetric private or MAC key to use for signing the result.
2003262016-04-20Henrik Grubbström (Grubba) //! //! @param claims //! The set of claims for the token. See @rfc{7519:4@}. //! //! @returns //! Returns @expr{0@} (zero) on encoding failure (usually //! that @[sign] doesn't support JWS. //! //! Returns a corresponding JWT on success. //! //! @note //! The claim @expr{"iat"@} (@rfc{7519:4.1.6@} is added unconditionally, //! and the claim @expr{"jti"@} (@rfc{7519:4.1.7@}) is added //! if not already present. //! //! @seealso //! @[decode_jwt()], @rfc{7519:4@}
4878c92016-05-04Henrik Grubbström (Grubba) string(7bit) encode_jwt(Crypto.Sign.State|Crypto.MAC.State sign, mapping(string:string|int) claims)
2003262016-04-20Henrik Grubbström (Grubba) { claims->iat = time(1); if (!claims->jti) claims->jti = (string)Standards.UUID.make_version4();
c6c5872017-03-22Henrik Grubbström (Grubba)  return encode_jws(sign, claims, "JWT");
2003262016-04-20Henrik Grubbström (Grubba) } //! Decode a JSON Web Token (JWT). //! //! @param sign
4878c92016-05-04Henrik Grubbström (Grubba) //! The asymetric public or MAC key(s) to validate the jwt against.
2003262016-04-20Henrik Grubbström (Grubba) //! //! @param jwt //! A JWT as eg returned by @[encode_jwt()]. //! //! @returns //! Returns @expr{0@} (zero) on validation failure (this //! includes validation of expiry times). //! //! Returns a mapping of the claims for the token on success. //! See @rfc{7519:4@}. //! //! @note //! The time check of the @expr{"nbf"@} value has a hard coded //! 60 second grace time (as allowed by @rfc{7519:4.1.5@}). //! //! @seealso
110b992017-03-22Henrik Grubbström (Grubba) //! @[encode_jwt()], @[decode_jws()], @rfc{7519:4@} mapping(string:string|int) decode_jwt(array(Crypto.Sign.State|Crypto.MAC.State)| Crypto.Sign.State|Crypto.MAC.State sign,
2003262016-04-20Henrik Grubbström (Grubba)  string(7bit) jwt) {
110b992017-03-22Henrik Grubbström (Grubba)  array(mapping(string(7bit):string(7bit)|int)|string(8bit)) jws = decode_jws(sign, jwt);
2003262016-04-20Henrik Grubbström (Grubba)  if (!jws) return 0; [mapping(string(7bit):string(7bit)|int) jose_header,
110b992017-03-22Henrik Grubbström (Grubba)  mapping(string:string|int) claims] = jws;
f09b862016-12-21Henrik Grubbström (Grubba)  if ((jose_header->typ || "JWT") != "JWT") return 0;
110b992017-03-22Henrik Grubbström (Grubba)  if (!mappingp(claims)) return 0; int now = time(1); if (!zero_type(claims->exp) && (claims->exp < now)) return 0; if (claims->nbf - 60 > now) return 0; return claims;
2003262016-04-20Henrik Grubbström (Grubba) }
8802f52016-12-29Henrik Grubbström (Grubba)  #if constant(Crypto.ECC.Curve) // NB: We index the Curve with the dot operator to get the Point. #pragma dynamic_dot protected mapping(string(7bit):Crypto.ECC.Curve) curve_lookup; #endif
a36a762017-03-20Henrik Grubbström (Grubba) protected mapping(string(7bit):Crypto.MAC) mac_lookup;
8802f52016-12-29Henrik Grubbström (Grubba) //! Decode a JSON Web Key (JWK). //! //! @returns
a36a762017-03-20Henrik Grubbström (Grubba) //! Returns an initialized @[Crypto.Sign.State] or @[Crypto.MAC.State] //! on success and @[UNDEFINED] on failure. Crypto.Sign.State|Crypto.MAC.State decode_jwk(mapping(string(7bit):string(7bit)) jwk)
8802f52016-12-29Henrik Grubbström (Grubba) { switch(jwk->kty) { case "RSA": return Crypto.RSA(jwk); #if constant(Crypto.ECC.Curve) case "EC":
a36a762017-03-20Henrik Grubbström (Grubba)  // RFC 7518:6.2
8802f52016-12-29Henrik Grubbström (Grubba)  if (!curve_lookup) { // NB: Thread safe. mapping(string(7bit):Crypto.ECC.Curve) m = ([]); foreach(values(Crypto.ECC), Crypto.ECC.Curve c) { string(7bit) n = objectp(c) && c->jose_name && c->jose_name(); if (n) { m[n] = c; } } curve_lookup = m; }
a36a762017-03-20Henrik Grubbström (Grubba)  // RFC 7518:6.2.1.1
8802f52016-12-29Henrik Grubbström (Grubba)  Crypto.ECC.Curve c = curve_lookup[jwk->crv]; if (!c) break;
8e3d622017-09-22Henrik Grubbström (Grubba)  if (jwk->d) { Crypto.ECC.Curve.ECDSA ecdsa = c.ECDSA(jwk); ecdsa->set_private_key(MIME.decode_base64url(jwk->d)); return ecdsa; }
8802f52016-12-29Henrik Grubbström (Grubba)  return c.Point(jwk); #endif /* constant(Crypto.ECC.Curve) */
a36a762017-03-20Henrik Grubbström (Grubba)  case "oct": // RFC 7518:6.4 if (!mac_lookup) { // NB: Thread safe. mapping(string(7bit):Crypto.MAC) m = ([]); foreach(values(Crypto), mixed x) { if (!objectp(x) || !objectp(x = x["HMAC"]) || !x->jwa) continue; string(7bit) jwa = x->jwa(); if (!jwa) continue; m[jwa] = x; } mac_lookup = m; } Crypto.MAC mac = mac_lookup[jwk->alg]; if (!mac) break; string(7bit) key = jwk->k; if (!key) break; return mac(MIME.decode_base64url(key));
8802f52016-12-29Henrik Grubbström (Grubba)  default: break; } return UNDEFINED; } #pragma no_dynamic_dot
a36a762017-03-20Henrik Grubbström (Grubba) //! Decode a JSON Web Key (JWK). //! //! @returns //! Returns an initialized @[Crypto.Sign.State] or @[Crypto.MAC.State] //! on success and @[UNDEFINED] on failure.
f8d0d82017-09-22Henrik Grubbström (Grubba) variant Crypto.Sign.State|Crypto.MAC.State decode_jwk(string(/*7bit*/8bit) jwk)
a36a762017-03-20Henrik Grubbström (Grubba) {
7dafa12018-02-20Henrik Grubbström (Grubba)  return decode_jwk(Standards.JSON.decode_utf8(MIME.decode_base64url(jwk)));
a36a762017-03-20Henrik Grubbström (Grubba) }
7f62b12017-09-21Henrik Grubbström (Grubba) //! Encode a JSON Web Key (JWK). string(7bit) encode_jwk(mapping(string(7bit):string(7bit)) jwk) { if (!mappingp(jwk)) return UNDEFINED;
7dafa12018-02-20Henrik Grubbström (Grubba)  return MIME.encode_base64url(string_to_utf8(Standards.JSON.encode(jwk)));
7f62b12017-09-21Henrik Grubbström (Grubba) } //! Encode a JSON Web Key (JWK). //! //! @param sign //! The initialized @[Crypto.Sign.State] or @[Crypto.MAC.State] //! for which a JWK is to be generated. //! //! @param private_key //! If true the private fields of @[sign] will also be encoded into //! the result. //! //! @returns //! Returns the corresponding JWK. variant string(7bit) encode_jwk(Crypto.Sign.State|Crypto.MAC.State sign, int(0..1)|void private_key) { mapping(string(7bit):string(7bit)) jwk = sign && sign->jwk(private_key); if (!jwk) return UNDEFINED; return encode_jwk(jwk); }
8802f52016-12-29Henrik Grubbström (Grubba) //! Decode a JSON Web Key (JWK) Set. //! //! @seealso //! @rfc{7517:5@}, @[decode_jwk()]
a36a762017-03-20Henrik Grubbström (Grubba) array(Crypto.Sign.State|Crypto.MAC.State)
8802f52016-12-29Henrik Grubbström (Grubba)  decode_jwk_set(mapping(string(8bit): array(mapping(string(7bit):string(7bit)))) jwk_set) { return filter(map(jwk_set->keys, decode_jwk), objectp); }
a36a762017-03-20Henrik Grubbström (Grubba)  //! Decode a JSON Web Key (JWK) Set. //! //! @seealso //! @rfc{7517:5@}, @[decode_jwk()] variant array(Crypto.Sign.State|Crypto.MAC.State) decode_jwk_set(string(7bit) jwk_set) {
7dafa12018-02-20Henrik Grubbström (Grubba)  return decode_jwk_set(Standards.JSON.decode_utf8(jwk_set));
a36a762017-03-20Henrik Grubbström (Grubba) }