LCOV - code coverage report
Current view: top level - core/crypto - SPJsonWebToken.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 242 274 88.3 %
Date: 2024-05-12 00:16:13 Functions: 30 45 66.7 %

          Line data    Source code
       1             : /**
       2             : Copyright (c) 2022 Roman Katuntsev <sbkarr@stappler.org>
       3             : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
       4             : 
       5             : Permission is hereby granted, free of charge, to any person obtaining a copy
       6             : of this software and associated documentation files (the "Software"), to deal
       7             : in the Software without restriction, including without limitation the rights
       8             : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       9             : copies of the Software, and to permit persons to whom the Software is
      10             : furnished to do so, subject to the following conditions:
      11             : 
      12             : The above copyright notice and this permission notice shall be included in
      13             : all copies or substantial portions of the Software.
      14             : 
      15             : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      16             : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      17             : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      18             : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      19             : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      20             : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      21             : THE SOFTWARE.
      22             : **/
      23             : 
      24             : #ifndef STAPPLER_CRYPTO_SPJSONWEBTOKEN_H_
      25             : #define STAPPLER_CRYPTO_SPJSONWEBTOKEN_H_
      26             : 
      27             : #include "SPCommon.h"
      28             : 
      29             : #if MODULE_STAPPLER_DATA
      30             : 
      31             : #include "SPString.h"
      32             : #include "SPTime.h"
      33             : #include "SPData.h"
      34             : #include "SPDataWrapper.h"
      35             : #include "SPCrypto.h"
      36             : 
      37             : namespace STAPPLER_VERSIONIZED stappler {
      38             : 
      39             : template <typename Interface>
      40             : struct JsonWebToken {
      41             :         using String = typename Interface::StringType;
      42             :         using Bytes = typename Interface::BytesType;
      43             :         using Value = data::ValueTemplate<Interface>;
      44             : 
      45             :         enum SigAlg {
      46             :                 None,
      47             :                 HS256,
      48             :                 HS512,
      49             :                 RS256,
      50             :                 RS512,
      51             :                 ES256,
      52             :                 ES512,
      53             :                 GS256,
      54             :                 GS512
      55             :         };
      56             : 
      57             :         static SigAlg getAlg(StringView);
      58             :         static StringView getAlgName(const SigAlg &);
      59             : 
      60             :         static JsonWebToken make(StringView iss, StringView aud, TimeInterval maxage = TimeInterval(), StringView sub = StringView());
      61             : 
      62             :         bool validate(StringView key) const;
      63             :         bool validate(BytesView key) const;
      64             :         bool validate(const crypto::PublicKey &key) const;
      65             : 
      66             :         bool validatePayload(StringView issuer, StringView aud) const;
      67             :         bool validatePayload() const;
      68             : 
      69             :         void setMaxAge(TimeInterval maxage);
      70             : 
      71             :         Value data() const;
      72             : 
      73             :         String exportPlain(data::EncodeFormat = data::EncodeFormat::Json) const;
      74             : 
      75             :         String exportSigned(SigAlg, StringView key,
      76             :                         const CoderSource &passwd = CoderSource(), data::EncodeFormat = data::EncodeFormat::Json) const;
      77             :         String exportSigned(SigAlg, BytesView key,
      78             :                         const CoderSource &passwd = CoderSource(), data::EncodeFormat = data::EncodeFormat::Json) const;
      79             :         String exportSigned(SigAlg, const crypto::PrivateKey &key, data::EncodeFormat = data::EncodeFormat::Json) const;
      80             :         String exportSigned(const crypto::PrivateKey &key, data::EncodeFormat = data::EncodeFormat::Json) const;
      81             : 
      82             :         JsonWebToken(Value &&payload, TimeInterval maxage = TimeInterval());
      83             :         JsonWebToken(const StringView &);
      84             : 
      85             :         String message;
      86             :         Value header;
      87             :         Value payload;
      88             :         Bytes sig;
      89             : 
      90             :         SigAlg alg = None;
      91             :         String kid;
      92             : };
      93             : 
      94             : template <typename Interface>
      95             : class AesToken : public data::WrapperTemplate<Interface> {
      96             : public:
      97             :         using String = typename Interface::StringType;
      98             :         using Bytes = typename Interface::BytesType;
      99             :         using Value = data::ValueTemplate<Interface>;
     100             : 
     101             :         template <typename T>
     102             :         using Function = typename Interface::template FunctionType<T>;
     103             : 
     104             :         struct Keys {
     105             :                 crypto::PublicKey *pub;
     106             :                 crypto::PrivateKey *priv;
     107             :                 BytesView secret;
     108             :         };
     109             : 
     110             :         // struct to pass identity data to fingerprinting algorithm
     111             :         struct Fingerprint {
     112             :                 crypto::HashFunction func = crypto::HashFunction::GOST_3411;
     113             :                 BytesView fpb;
     114             :                 Function<void(const crypto::HashCoderCallback &)> cb;
     115             : 
     116         100 :                 Fingerprint(crypto::HashFunction fn, BytesView v) : func(fn), fpb(v) { }
     117         100 :                 Fingerprint(crypto::HashFunction fn, Function<void(const crypto::HashCoderCallback &)> &&cb) : func(fn), cb(move(cb)) { }
     118             :         };
     119             : 
     120             :         // parse from JsonWebToken source
     121             :         static AesToken parse(StringView token, const Fingerprint &, StringView iss, StringView aud = StringView(), Keys = Keys());
     122             : 
     123             :         // parse from data::Value source
     124             :         static AesToken parse(const Value &, const Fingerprint &, Keys = Keys());
     125             : 
     126             :         static AesToken create(Keys = Keys());
     127             : 
     128             :         explicit operator bool () const;
     129             : 
     130             :         String exportToken(StringView iss, const Fingerprint &fpb, TimeInterval maxage, StringView sub) const;
     131             :         Value exportData(const Fingerprint &fpb) const;
     132             : 
     133             : protected:
     134             :         static std::array<uint8_t, 64> getFingerprint(const Fingerprint &, Time t, BytesView secret);
     135             : 
     136             :         Bytes encryptAes(const crypto::BlockKey256 &, const Value &) const;
     137             :         static Value decryptAes(const crypto::BlockKey256 &, BytesView);
     138             : 
     139             :         AesToken();
     140             :         AesToken(Keys keys);
     141             :         AesToken(Value &&, Keys keys);
     142             : 
     143             :         Keys _keys;
     144             : };
     145             : 
     146             : template <typename Interface>
     147         250 : auto JsonWebToken<Interface>::getAlg(StringView name) -> typename JsonWebToken<Interface>::SigAlg  {
     148         250 :         if (name == "HS256") {
     149          25 :                 return HS256;
     150         225 :         } else if (name == "HS512") {
     151          25 :                 return HS512;
     152         200 :         } else if (name == "RS256") {
     153          25 :                 return RS256;
     154         175 :         } else if (name == "RS512") {
     155          25 :                 return RS512;
     156         150 :         } else if (name == "ES256") {
     157          25 :                 return ES256;
     158         125 :         } else if (name == "ES512") {
     159          25 :                 return ES512;
     160         100 :         } else if (name == "GS256") {
     161          50 :                 return GS256;
     162          50 :         } else if (name == "GS512") {
     163          50 :                 return GS512;
     164             :         }
     165           0 :         return JsonWebToken::None;
     166             : }
     167             : 
     168             : template <typename Interface>
     169         375 : StringView JsonWebToken<Interface>::getAlgName(const SigAlg &alg) {
     170         375 :         switch (alg) {
     171           0 :         case None: return "none"; break;
     172          50 :         case HS256: return "HS256"; break;
     173          50 :         case HS512: return "HS512"; break;
     174          25 :         case RS256: return "RS256"; break;
     175          50 :         case RS512: return "RS512"; break;
     176          25 :         case ES256: return "ES256"; break;
     177          25 :         case ES512: return "ES512"; break;
     178         100 :         case GS256: return "GS256"; break;
     179          50 :         case GS512: return "GS512"; break;
     180             :         }
     181           0 :         return StringView();
     182             : }
     183             : 
     184             : template <typename Interface>
     185          75 : JsonWebToken<Interface> JsonWebToken<Interface>::make(StringView iss, StringView aud, TimeInterval maxage, StringView sub) {
     186          75 :         Value payload;
     187          75 :         payload.setString(iss, "iss");
     188          75 :         if (!sub.empty()) {
     189          75 :                 payload.setString(sub, "sub");
     190             :         }
     191             : 
     192          75 :         if (!aud.empty()) {
     193          75 :                 payload.setString(aud, "aud");
     194             :         }
     195             : 
     196         150 :         return JsonWebToken(move(payload), maxage);
     197          75 : }
     198             : 
     199             : template <typename Interface>
     200         175 : JsonWebToken<Interface>::JsonWebToken(Value &&val, TimeInterval maxage) : payload(move(val)) {
     201         175 :         if (maxage != nullptr) {
     202          75 :                 payload.setInteger((Time::now() + maxage).toSeconds(), "exp");
     203             :         }
     204         175 : }
     205             : 
     206             : template <typename Interface>
     207         250 : JsonWebToken<Interface>::JsonWebToken(const StringView &token) {
     208         250 :         StringView r(token);
     209         250 :         auto head = r.readUntil<StringView::Chars<'.'>>();
     210         250 :         if (r.is('.')) {
     211         250 :                 ++ r;
     212             :         }
     213             : 
     214         250 :         header = data::read<Interface>(base64::decode<Interface>(head));
     215         500 :         for (auto &it : header.asDict()) {
     216         250 :                 if (it.first == "alg") {
     217         250 :                         alg = getAlg(it.second.getString());
     218           0 :                 } else if (it.first == "kid") {
     219           0 :                         kid = it.second.asString();
     220             :                 }
     221             :         }
     222             : 
     223         250 :         auto pl = r.readUntil<StringView::Chars<'.'>>();
     224             : 
     225         250 :         message = String(token.data(), token.size() - r.size());
     226             : 
     227         250 :         if (r.is('.')) {
     228         250 :                 ++ r;
     229             :         }
     230             : 
     231         250 :         payload = data::read<Interface>(base64::decode<Interface>(pl));
     232         250 :         sig = base64::decode<Interface>(r);
     233         250 : }
     234             : 
     235             : template <typename Interface>
     236             : void JsonWebToken<Interface>::setMaxAge(TimeInterval maxage) {
     237             :         payload.setInteger((Time::now() + maxage).toSeconds(), "exp");
     238             : }
     239             : 
     240             : template <typename Interface>
     241          50 : bool JsonWebToken<Interface>::validate(StringView key) const {
     242          50 :         return validate(BytesView((const uint8_t *)key.data(), key.size() + 1));
     243             : }
     244             : 
     245             : template <typename Interface>
     246         100 : bool JsonWebToken<Interface>::validate(BytesView key) const {
     247         100 :         if (key.empty()) {
     248           0 :                 return false;
     249             :         }
     250             : 
     251         100 :         switch (alg) {
     252          25 :         case HS256: {
     253          25 :                 auto keySig = string::Sha256::hmac(message, key);
     254          25 :                 if (sig.size() == keySig.size() && memcmp(sig.data(), keySig.data(), sig.size()) == 0) {
     255          25 :                         return true;
     256             :                 }
     257           0 :                 break;
     258             :         }
     259          25 :         case HS512: {
     260          25 :                 auto keySig = string::Sha512::hmac(message, key);
     261          25 :                 if (sig.size() == keySig.size() && memcmp(sig.data(), keySig.data(), sig.size()) == 0) {
     262          25 :                         return true;
     263             :                 }
     264           0 :                 break;
     265             :         }
     266          50 :         default: {
     267          50 :                 crypto::PublicKey pk(key);
     268             : 
     269          50 :                 if (pk) {
     270          50 :                         return validate(pk);
     271             :                 }
     272           0 :                 break;
     273          50 :         }
     274             :         }
     275             : 
     276           0 :         return false;
     277             : }
     278             : 
     279             : template <typename Interface>
     280         200 : bool JsonWebToken<Interface>::validate(const crypto::PublicKey &pk) const {
     281         200 :         if (!pk) {
     282           0 :                 return false;
     283             :         }
     284             : 
     285         200 :         crypto::SignAlgorithm algo = crypto::SignAlgorithm::RSA_SHA512;
     286         200 :         switch (alg) {
     287          25 :         case RS256: algo = crypto::SignAlgorithm::RSA_SHA256; break;
     288          25 :         case ES256: algo = crypto::SignAlgorithm::ECDSA_SHA256; break;
     289          50 :         case GS256: algo = crypto::SignAlgorithm::GOST_256; break;
     290          25 :         case RS512: algo = crypto::SignAlgorithm::RSA_SHA512; break;
     291          25 :         case ES512: algo = crypto::SignAlgorithm::ECDSA_SHA512; break;
     292          50 :         case GS512: algo = crypto::SignAlgorithm::GOST_512; break;
     293           0 :         default: return false; break;
     294             :         }
     295             : 
     296         200 :         if (pk.verify(BytesView((const uint8_t *)message.data(), message.size()), sig, algo)) {
     297         200 :                 return true;
     298             :         }
     299             : 
     300           0 :         return false;
     301             : }
     302             : 
     303             : template <typename Interface>
     304          50 : bool JsonWebToken<Interface>::validatePayload(StringView issuer, StringView aud) const {
     305          50 :         auto exp = payload.getInteger("exp");
     306         100 :         if (issuer == payload.getString("iss") && aud == payload.getString("aud")
     307         100 :                         && (exp == 0 || exp > int64_t(Time::now().toSeconds()))) {
     308          50 :                 return true;
     309             :         }
     310           0 :         return false;
     311             : }
     312             : 
     313             : template <typename Interface>
     314             : bool JsonWebToken<Interface>::validatePayload() const {
     315             :         auto exp = payload.getInteger("exp");
     316             :         if (exp == 0 || exp > int64_t(Time::now().toSeconds())) {
     317             :                 return true;
     318             :         }
     319             :         return false;
     320             : }
     321             : 
     322             : template <typename Interface>
     323             : auto JsonWebToken<Interface>::data() const -> Value {
     324             :         Value data;
     325             :         data.setValue(header, "header");
     326             :         data.setValue(payload, "payload");
     327             :         data.setBytes(sig, "sig");
     328             :         return data;
     329             : }
     330             : 
     331             : template <typename Interface>
     332             : auto JsonWebToken<Interface>::exportPlain(data::EncodeFormat format) const -> String {
     333             :         return string::toString<Interface>(
     334             :                         base64url::encode(data::write(header, format)), ".", base64url::encode(data::write(payload, format)));
     335             : }
     336             : 
     337             : template <typename Interface>
     338          50 : auto JsonWebToken<Interface>::exportSigned(SigAlg alg, StringView key,
     339             :                 const CoderSource &passwd, data::EncodeFormat format) const -> String {
     340          50 :         return exportSigned(alg, BytesView((const uint8_t *)key.data(), key.size()), passwd, format);
     341             : }
     342             : 
     343             : template <typename Interface>
     344         150 : auto JsonWebToken<Interface>::exportSigned(SigAlg alg, BytesView key,
     345             :                 const CoderSource &passwd, data::EncodeFormat format) const -> String {
     346             : 
     347         150 :         switch (alg) {
     348          50 :         case HS256: {
     349          50 :                 Value hederData(header);
     350          50 :                 hederData.setString(getAlgName(alg), "alg");
     351             : 
     352         150 :                 auto data =  string::toString<Interface>(
     353             :                                 base64url::encode<Interface>(data::write<Interface>(hederData, format)),
     354          50 :                                 ".", base64url::encode<Interface>(data::write<Interface>(payload, format)));
     355             : 
     356             :                 return string::toString<Interface>(
     357          50 :                                 data, ".", base64url::encode<Interface>(string::Sha256::hmac(data, key)));
     358             :                 break;
     359          50 :         }
     360          50 :         case HS512: {
     361          50 :                 Value hederData(header);
     362          50 :                 hederData.setString(getAlgName(alg), "alg");
     363             : 
     364         150 :                 auto data =  string::toString<Interface>(
     365             :                                 base64url::encode<Interface>(data::write<Interface>(hederData, format)),
     366          50 :                                 ".", base64url::encode<Interface>(data::write<Interface>(payload, format)));
     367             : 
     368             :                 return string::toString<Interface>(
     369          50 :                                 data, ".", base64url::encode<Interface>(string::Sha512::hmac(data, key)));
     370             :                 break;
     371          50 :         }
     372          50 :         default: {
     373          50 :                 crypto::PrivateKey pk(key, passwd);
     374          50 :                 if (pk) {
     375          50 :                         return exportSigned(alg, pk, format);
     376             :                 }
     377          50 :         }
     378             :         }
     379             : 
     380           0 :         return String();
     381             : }
     382             : 
     383             : template <typename Interface>
     384         275 : auto JsonWebToken<Interface>::exportSigned(SigAlg alg, const crypto::PrivateKey &pk, data::EncodeFormat format) const -> String {
     385         275 :         if (!pk) {
     386           0 :                 return String();
     387             :         }
     388             : 
     389         275 :         Value hederData(header);
     390         275 :         hederData.setString(getAlgName(alg), "alg");
     391             : 
     392         825 :         auto data =  string::toString<Interface>(
     393             :                         base64url::encode<Interface>(data::write<Interface>(hederData, format)),
     394         275 :                         ".", base64url::encode<Interface>(data::write<Interface>(payload, format)));
     395             : 
     396         275 :         crypto::SignAlgorithm algo = crypto::SignAlgorithm::RSA_SHA512;
     397         275 :         switch (alg) {
     398          25 :         case RS256: algo = crypto::SignAlgorithm::RSA_SHA256; break;
     399          25 :         case ES256: algo = crypto::SignAlgorithm::ECDSA_SHA256; break;
     400         100 :         case GS256: algo = crypto::SignAlgorithm::GOST_256; break;
     401          50 :         case RS512: algo = crypto::SignAlgorithm::RSA_SHA512; break;
     402          25 :         case ES512: algo = crypto::SignAlgorithm::ECDSA_SHA512; break;
     403          50 :         case GS512: algo = crypto::SignAlgorithm::GOST_512; break;
     404           0 :         default: break;
     405             :         }
     406             : 
     407         275 :         if (pk.sign([&] (BytesView sign) {
     408         275 :                 data = string::toString<Interface>(data, ".", base64url::encode<Interface>(sign));
     409             :         }, data, algo)) {
     410         275 :                 return data;
     411             :         }
     412             : 
     413           0 :         return String();
     414         275 : }
     415             : 
     416             : template <typename Interface>
     417         200 : auto JsonWebToken<Interface>::exportSigned(const crypto::PrivateKey &key, data::EncodeFormat fmt) const -> String {
     418         200 :         switch (key.getType()) {
     419          25 :         case crypto::KeyType::RSA:
     420          25 :                 return exportSigned(RS512, key, fmt);
     421             :                 break;
     422          25 :         case crypto::KeyType::ECDSA:
     423             :         case crypto::KeyType::EDDSA_ED448:
     424          25 :                 return exportSigned(ES512, key, fmt);
     425             :                 break;
     426         100 :         case crypto::KeyType::GOST3410_2012_256:
     427         100 :                 return exportSigned(GS256, key, fmt);
     428             :                 break;
     429          50 :         case crypto::KeyType::GOST3410_2012_512:
     430          50 :                 return exportSigned(GS512, key, fmt);
     431             :                 break;
     432           0 :         default:
     433           0 :                 break;
     434             :         }
     435           0 :         return String();
     436             : }
     437             : 
     438             : template <typename Interface>
     439          50 : AesToken<Interface> AesToken<Interface>::parse(StringView token, const Fingerprint &fpb, StringView iss, StringView aud, Keys keys) {
     440          50 :         if (aud.empty()) {
     441           0 :                 aud = iss;
     442             :         }
     443             : 
     444          50 :         JsonWebToken<Interface> input(token);
     445          50 :         if (input.validate(*keys.pub) && input.validatePayload(iss, aud)) {
     446          50 :                 Time tf = Time::microseconds(input.payload.getInteger("tf"));
     447          50 :                 auto fp = getFingerprint(fpb, tf, keys.secret);
     448             : 
     449          50 :                 if (BytesView(fp.data(), fp.size()) == BytesView(input.payload.getBytes("fp"))) {
     450          50 :                         auto v = crypto::getBlockInfo(input.payload.getBytes("p"));
     451          50 :                         crypto::BlockKey256 aesKey;
     452          50 :                         if (keys.priv) {
     453          50 :                                 aesKey = crypto::makeBlockKey(*keys.priv, BytesView(fp.data(), fp.size()), v.cipher, v.version);
     454             :                         } else {
     455           0 :                                 aesKey = crypto::makeBlockKey(keys.secret, BytesView(fp.data(), fp.size()), v.cipher, v.version);
     456             :                         }
     457             : 
     458          50 :                         auto p = decryptAes(aesKey, input.payload.getBytes("p"));
     459          50 :                         if (p) {
     460          50 :                                 return AesToken(move(p), keys);
     461             :                         }
     462          50 :                 }
     463             :         }
     464           0 :         return AesToken();
     465          50 : }
     466             : 
     467             : template <typename Interface>
     468          75 : AesToken<Interface> AesToken<Interface>::parse(const Value &payload, const Fingerprint &fpb, Keys keys) {
     469          75 :         Time tf = Time::microseconds(payload.getInteger("tf"));
     470          75 :         auto fp = getFingerprint(fpb, tf, keys.secret);
     471             : 
     472          75 :         if (BytesView(fp.data(), fp.size()) == BytesView(payload.getBytes("fp"))) {
     473          75 :                 auto v = crypto::getBlockInfo(payload.getBytes("p"));
     474             : 
     475          75 :                 crypto::BlockKey256 aesKey;
     476          75 :                 if (keys.priv) {
     477          75 :                         aesKey = crypto::makeBlockKey(*keys.priv, BytesView(fp.data(), fp.size()), v.cipher, v.version);
     478             :                 } else {
     479           0 :                         aesKey = crypto::makeBlockKey(keys.secret, BytesView(fp.data(), fp.size()), v.cipher, v.version);
     480             :                 }
     481             : 
     482          75 :                 auto p = decryptAes(aesKey, payload.getBytes("p"));
     483          75 :                 if (p) {
     484          75 :                         return AesToken(move(p), keys);
     485             :                 }
     486          75 :         }
     487           0 :         return AesToken();
     488             : }
     489             : 
     490             : template <typename Interface>
     491          75 : AesToken<Interface> AesToken<Interface>::create(Keys keys) {
     492          75 :         return AesToken(keys);
     493             : }
     494             : 
     495             : template <typename Interface>
     496          25 : AesToken<Interface>::operator bool () const {
     497          25 :         return !this->_data.isNull() && _keys.priv && *_keys.priv && !_keys.secret.empty();
     498             : }
     499             : 
     500             : template <typename Interface>
     501          75 : auto AesToken<Interface>::exportToken(StringView iss, const Fingerprint &fpb, TimeInterval maxage, StringView sub) const -> String {
     502          75 :         auto t = Time::now();
     503             : 
     504          75 :         JsonWebToken<Interface> token = JsonWebToken<Interface>::make(iss, iss, maxage, sub);
     505          75 :         auto fp = getFingerprint(fpb, t, _keys.secret);
     506             : 
     507          75 :         token.payload.setBytes(BytesView(fp.data(), fp.size()), "fp");
     508          75 :         token.payload.setInteger(t.toMicros(), "tf");
     509             : 
     510          75 :         crypto::BlockKey256 aesKey;
     511          75 :         if (_keys.priv) {
     512          75 :                 switch (_keys.priv->getType()) {
     513          50 :                 case crypto::KeyType::GOST3410_2012_256:
     514             :                 case crypto::KeyType::GOST3410_2012_512:
     515          50 :                         aesKey = crypto::makeBlockKey(*_keys.priv, BytesView(fp.data(), fp.size()), crypto::BlockCipher::Gost3412_2015_CTR_ACPKM);
     516          50 :                         break;
     517          25 :                 default:
     518          25 :                         aesKey = crypto::makeBlockKey(*_keys.priv, BytesView(fp.data(), fp.size()));
     519          25 :                         break;
     520             :                 }
     521             :         } else {
     522           0 :                 aesKey = crypto::makeBlockKey(_keys.secret, BytesView(fp.data(), fp.size()));
     523             :         }
     524             : 
     525          75 :         token.payload.setBytes(encryptAes(aesKey, this->_data), "p");
     526         150 :         return token.exportSigned(*_keys.priv, data::EncodeFormat::Cbor);
     527          75 : }
     528             : 
     529             : template <typename Interface>
     530         200 : auto AesToken<Interface>::exportData(const Fingerprint &fpb) const -> Value {
     531         200 :         auto t = Time::now();
     532         200 :         auto fp = getFingerprint(fpb, t, _keys.secret);
     533             : 
     534         200 :         Value payload;
     535         200 :         payload.setBytes(BytesView(fp.data(), fp.size()), "fp");
     536         200 :         payload.setInteger(t.toMicros(), "tf");
     537             : 
     538         200 :         crypto::BlockKey256 aesKey;
     539         200 :         if (_keys.priv) {
     540         200 :                 switch (_keys.priv->getType()) {
     541          50 :                 case crypto::KeyType::GOST3410_2012_256:
     542             :                 case crypto::KeyType::GOST3410_2012_512:
     543          50 :                         aesKey = crypto::makeBlockKey(*_keys.priv, BytesView(fp.data(), fp.size()), crypto::BlockCipher::Gost3412_2015_CTR_ACPKM);
     544          50 :                         break;
     545         150 :                 default:
     546         150 :                         aesKey = crypto::makeBlockKey(*_keys.priv, BytesView(fp.data(), fp.size()));
     547         150 :                         break;
     548             :                 }
     549             :         } else {
     550           0 :                 aesKey = crypto::makeBlockKey(_keys.secret, BytesView(fp.data(), fp.size()));
     551             :         }
     552             : 
     553         200 :         payload.setBytes(encryptAes(aesKey, this->_data), "p");
     554         400 :         return payload;
     555           0 : }
     556             : 
     557             : template <typename Interface>
     558         400 : std::array<uint8_t, 64> AesToken<Interface>::getFingerprint(const Fingerprint &fp, Time t, BytesView secret) {
     559         400 :         auto v = byteorder::HostToBig(t.toMicros());
     560         400 :         if (!fp.fpb.empty()) {
     561         300 :                 switch (fp.func) {
     562          25 :                 case crypto::HashFunction::SHA_2:
     563          25 :                         return crypto::Sha512().update(secret).update(fp.fpb).update(CoderSource((const uint8_t *)&v, sizeof(v))) .final();
     564             :                         break;
     565         275 :                 case crypto::HashFunction::GOST_3411:
     566         275 :                         return crypto::Gost3411_512().update(secret).update(fp.fpb).update(CoderSource((const uint8_t *)&v, sizeof(v))) .final();
     567             :                         break;
     568             :                 }
     569         100 :         } else if (fp.cb) {
     570         100 :                 return crypto::hash512([&] (const crypto::HashCoderCallback &cb) {
     571          50 :                         fp.cb(cb);
     572         100 :                 }, fp.func);
     573             :         } else {
     574          50 :                 switch (fp.func) {
     575          25 :                 case crypto::HashFunction::SHA_2:
     576          25 :                         return crypto::Sha512().update(secret).update(CoderSource((const uint8_t *)&v, sizeof(v))).final();
     577             :                         break;
     578          25 :                 case crypto::HashFunction::GOST_3411:
     579          25 :                         return crypto::Gost3411_512().update(secret).update(CoderSource((const uint8_t *)&v, sizeof(v))).final();
     580             :                         break;
     581             :                 }
     582             :         }
     583           0 :         return std::array<uint8_t, 64>();
     584             : }
     585             : 
     586             : template <typename Interface>
     587         275 : auto AesToken<Interface>::encryptAes(const crypto::BlockKey256 &key, const Value &val) const -> Bytes {
     588         275 :         auto d = data::write<Interface>(val, data::EncodeFormat::CborCompressed);
     589         275 :         Bytes out;
     590         275 :         crypto::encryptBlock(key, d, [&] (BytesView data) {
     591         275 :                 out = data.bytes<Interface>();
     592             :         });
     593         550 :         return out;
     594         275 : }
     595             : 
     596             : template <typename Interface>
     597         125 : auto AesToken<Interface>::decryptAes(const crypto::BlockKey256 &key, BytesView val) -> Value {
     598         125 :         Value out;
     599         125 :         crypto::decryptBlock(key, val, [&] (BytesView data) {
     600         125 :                 out = data::read<Interface>(data);
     601             :         });
     602         125 :         return out;
     603           0 : }
     604             : 
     605             : template <typename Interface>
     606           0 : AesToken<Interface>::AesToken() { }
     607             : 
     608             : template <typename Interface>
     609          75 : AesToken<Interface>::AesToken(Keys keys) : _keys(keys) { }
     610             : 
     611             : template <typename Interface>
     612         125 : AesToken<Interface>::AesToken(Value &&v, Keys keys) : data::WrapperTemplate<Interface>(std::move(v)), _keys(keys) { }
     613             : 
     614             : }
     615             : 
     616             : #else
     617             : 
     618             : #warning "Enable module stappler_data to use JsonWebToken"
     619             : 
     620             : #endif // MODULE_STAPPLER_DATA
     621             : 
     622             : #endif /* STAPPLER_CRYPTO_SPJSONWEBTOKEN_H_ */

Generated by: LCOV version 1.14