docs: public String createToken(String response)
        throws IllegalArgumentException, ProtocolVersionException {
      final Response decodedResponse;
      final Challenge challenge;
      decodedResponse = CrtAuthCodec.deserializeResponse(decode(response));
      challenge = CrtAuthCodec.deserializeChallengeAuthenticated(
          decodedResponse.getPayload(), secret);
  
      if (!challenge.getServerName().equals(serverName)) {
        throw new IllegalArgumentException("Got challenge with the wrong server_name encoded.");
      }
      PublicKey publicKey;
      try {
        publicKey = getKeyForUser(challenge.getUserName());
      } catch (KeyNotFoundException e) {
        // If the user requesting authentication doesn't have a public key,  we throw an
        // InvalidInputException. This normally shouldn't happen, since at this stage a challenge
        // should have already been sent, which in turn requires knowledge of the user's public key.
        throw new IllegalArgumentException(e);
      }
      boolean signatureVerified;
      try {
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(publicKey);
        signature.update(decodedResponse.getPayload());
        signatureVerified = signature.verify(decodedResponse.getSignature());
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
      if (challenge.isExpired(timeSupplier)) {
        throw new IllegalArgumentException("The challenge is out of its validity period");
      }
      if (!signatureVerified) {
        throw new IllegalArgumentException("Client did not provide proof that it controls the " +
                                           "secret key.");
      }
      UnsignedInteger validFrom = timeSupplier.getTime().minus(CLOCK_FUDGE);
      UnsignedInteger validTo = timeSupplier.getTime().plus(tokenLifetimeSeconds);
      Token token = new Token(validFrom.intValue(), validTo.intValue(), challenge.getUserName());
      return encode(CrtAuthCodec.serialize(token, secret));
    }