// 
// Decompiled by Procyon v0.6.0
// 

package com.google.crypto.tink.jwt;

import com.google.errorprone.annotations.InlineMe;
import com.google.crypto.tink.tinkkey.KeyAccess;
import java.security.spec.ECPoint;
import com.google.crypto.tink.AccessesPartialKey;
import java.util.Optional;
import java.math.BigInteger;
import com.google.crypto.tink.subtle.Base64;
import com.google.crypto.tink.internal.BigIntegerEncoding;
import java.util.Iterator;
import com.google.crypto.tink.internal.JsonParser;
import java.io.IOException;
import com.google.crypto.tink.Key;
import com.google.gson.JsonObject;
import java.security.GeneralSecurityException;
import com.google.gson.JsonElement;
import com.google.crypto.tink.KeyStatus;
import com.google.gson.JsonArray;
import com.google.crypto.tink.KeysetHandle;

public final class JwkSetConverter
{
    public static String fromPublicKeysetHandle(KeysetHandle handle) throws IOException, GeneralSecurityException {
        handle = KeysetHandle.newBuilder(handle).build();
        final JsonArray keys = new JsonArray();
        for (int i = 0; i < handle.size(); ++i) {
            final KeysetHandle.Entry entry = handle.getAt(i);
            if (entry.getStatus() == KeyStatus.ENABLED) {
                final Key key = entry.getKey();
                if (key instanceof JwtEcdsaPublicKey) {
                    keys.add(convertJwtEcdsaKey((JwtEcdsaPublicKey)key));
                }
                else if (key instanceof JwtRsaSsaPkcs1PublicKey) {
                    keys.add(convertJwtRsaSsaPkcs1Key((JwtRsaSsaPkcs1PublicKey)key));
                }
                else {
                    if (!(key instanceof JwtRsaSsaPssPublicKey)) {
                        throw new GeneralSecurityException("unsupported key with parameters " + key.getParameters());
                    }
                    keys.add(convertJwtRsaSsaPssKey((JwtRsaSsaPssPublicKey)key));
                }
            }
        }
        final JsonObject jwkSet = new JsonObject();
        jwkSet.add("keys", keys);
        return jwkSet.toString();
    }
    
    public static KeysetHandle toPublicKeysetHandle(final String jwkSet) throws IOException, GeneralSecurityException {
        JsonObject jsonKeyset;
        try {
            jsonKeyset = JsonParser.parse(jwkSet).getAsJsonObject();
        }
        catch (final IllegalStateException | IOException ex) {
            throw new GeneralSecurityException("JWK set is invalid JSON", ex);
        }
        final KeysetHandle.Builder builder = KeysetHandle.newBuilder();
        final JsonArray jsonKeys = jsonKeyset.get("keys").getAsJsonArray();
        for (final JsonElement element : jsonKeys) {
            final JsonObject jsonKey = element.getAsJsonObject();
            final String substring;
            final String algPrefix = substring = getStringItem(jsonKey, "alg").substring(0, 2);
            switch (substring) {
                case "RS": {
                    builder.addEntry(KeysetHandle.importKey(convertToRsaSsaPkcs1Key(jsonKey)).withRandomId());
                    continue;
                }
                case "PS": {
                    builder.addEntry(KeysetHandle.importKey(convertToRsaSsaPssKey(jsonKey)).withRandomId());
                    continue;
                }
                case "ES": {
                    builder.addEntry(KeysetHandle.importKey(convertToEcdsaKey(jsonKey)).withRandomId());
                    continue;
                }
                default: {
                    throw new GeneralSecurityException("unexpected alg value: " + getStringItem(jsonKey, "alg"));
                }
            }
        }
        if (builder.size() <= 0) {
            throw new GeneralSecurityException("empty keyset");
        }
        builder.getAt(0).makePrimary();
        return builder.build();
    }
    
    @AccessesPartialKey
    private static JsonObject convertJwtEcdsaKey(final JwtEcdsaPublicKey key) throws GeneralSecurityException {
        final JwtEcdsaParameters.Algorithm algorithm = key.getParameters().getAlgorithm();
        String alg;
        String crv;
        int encLength;
        if (algorithm.equals(JwtEcdsaParameters.Algorithm.ES256)) {
            alg = "ES256";
            crv = "P-256";
            encLength = 32;
        }
        else if (algorithm.equals(JwtEcdsaParameters.Algorithm.ES384)) {
            alg = "ES384";
            crv = "P-384";
            encLength = 48;
        }
        else {
            if (!algorithm.equals(JwtEcdsaParameters.Algorithm.ES512)) {
                throw new GeneralSecurityException("unknown algorithm");
            }
            alg = "ES512";
            crv = "P-521";
            encLength = 66;
        }
        final JsonObject jsonKey = new JsonObject();
        jsonKey.addProperty("kty", "EC");
        jsonKey.addProperty("crv", crv);
        final BigInteger x = key.getPublicPoint().getAffineX();
        final BigInteger y = key.getPublicPoint().getAffineY();
        jsonKey.addProperty("x", Base64.urlSafeEncode(BigIntegerEncoding.toBigEndianBytesOfFixedLength(x, encLength)));
        jsonKey.addProperty("y", Base64.urlSafeEncode(BigIntegerEncoding.toBigEndianBytesOfFixedLength(y, encLength)));
        jsonKey.addProperty("use", "sig");
        jsonKey.addProperty("alg", alg);
        final JsonArray keyOps = new JsonArray();
        keyOps.add("verify");
        jsonKey.add("key_ops", keyOps);
        final Optional<String> kid = key.getKid();
        if (kid.isPresent()) {
            jsonKey.addProperty("kid", kid.get());
        }
        return jsonKey;
    }
    
    private static byte[] base64urlUInt(final BigInteger n) {
        if (n.equals(BigInteger.ZERO)) {
            return new byte[] { 0 };
        }
        return BigIntegerEncoding.toUnsignedBigEndianBytes(n);
    }
    
    @AccessesPartialKey
    private static JsonObject convertJwtRsaSsaPkcs1Key(final JwtRsaSsaPkcs1PublicKey key) throws GeneralSecurityException {
        final String alg = key.getParameters().getAlgorithm().getStandardName();
        final JsonObject jsonKey = new JsonObject();
        jsonKey.addProperty("kty", "RSA");
        jsonKey.addProperty("n", Base64.urlSafeEncode(base64urlUInt(key.getModulus())));
        jsonKey.addProperty("e", Base64.urlSafeEncode(base64urlUInt(key.getParameters().getPublicExponent())));
        jsonKey.addProperty("use", "sig");
        jsonKey.addProperty("alg", alg);
        final JsonArray keyOps = new JsonArray();
        keyOps.add("verify");
        jsonKey.add("key_ops", keyOps);
        final Optional<String> kid = key.getKid();
        if (kid.isPresent()) {
            jsonKey.addProperty("kid", kid.get());
        }
        return jsonKey;
    }
    
    @AccessesPartialKey
    private static JsonObject convertJwtRsaSsaPssKey(final JwtRsaSsaPssPublicKey key) throws GeneralSecurityException {
        final String alg = key.getParameters().getAlgorithm().getStandardName();
        final JsonObject jsonKey = new JsonObject();
        jsonKey.addProperty("kty", "RSA");
        jsonKey.addProperty("n", Base64.urlSafeEncode(base64urlUInt(key.getModulus())));
        jsonKey.addProperty("e", Base64.urlSafeEncode(base64urlUInt(key.getParameters().getPublicExponent())));
        jsonKey.addProperty("use", "sig");
        jsonKey.addProperty("alg", alg);
        final JsonArray keyOps = new JsonArray();
        keyOps.add("verify");
        jsonKey.add("key_ops", keyOps);
        final Optional<String> kid = key.getKid();
        if (kid.isPresent()) {
            jsonKey.addProperty("kid", kid.get());
        }
        return jsonKey;
    }
    
    private static String getStringItem(final JsonObject obj, final String name) throws GeneralSecurityException {
        if (!obj.has(name)) {
            throw new GeneralSecurityException(name + " not found");
        }
        if (!obj.get(name).isJsonPrimitive() || !obj.get(name).getAsJsonPrimitive().isString()) {
            throw new GeneralSecurityException(name + " is not a string");
        }
        return obj.get(name).getAsString();
    }
    
    private static void expectStringItem(final JsonObject obj, final String name, final String expectedValue) throws GeneralSecurityException {
        final String value = getStringItem(obj, name);
        if (!value.equals(expectedValue)) {
            throw new GeneralSecurityException("unexpected " + name + " value: " + value);
        }
    }
    
    private static void validateUseIsSig(final JsonObject jsonKey) throws GeneralSecurityException {
        if (!jsonKey.has("use")) {
            return;
        }
        expectStringItem(jsonKey, "use", "sig");
    }
    
    private static void validateKeyOpsIsVerify(final JsonObject jsonKey) throws GeneralSecurityException {
        if (!jsonKey.has("key_ops")) {
            return;
        }
        if (!jsonKey.get("key_ops").isJsonArray()) {
            throw new GeneralSecurityException("key_ops is not an array");
        }
        final JsonArray keyOps = jsonKey.get("key_ops").getAsJsonArray();
        if (keyOps.size() != 1) {
            throw new GeneralSecurityException("key_ops must contain exactly one element");
        }
        if (!keyOps.get(0).isJsonPrimitive() || !keyOps.get(0).getAsJsonPrimitive().isString()) {
            throw new GeneralSecurityException("key_ops is not a string");
        }
        if (!keyOps.get(0).getAsString().equals("verify")) {
            throw new GeneralSecurityException("unexpected keyOps value: " + keyOps.get(0).getAsString());
        }
    }
    
    @AccessesPartialKey
    private static JwtRsaSsaPkcs1PublicKey convertToRsaSsaPkcs1Key(final JsonObject jsonKey) throws GeneralSecurityException {
        final String stringItem = getStringItem(jsonKey, "alg");
        JwtRsaSsaPkcs1Parameters.Algorithm algorithm = null;
        switch (stringItem) {
            case "RS256": {
                algorithm = JwtRsaSsaPkcs1Parameters.Algorithm.RS256;
                break;
            }
            case "RS384": {
                algorithm = JwtRsaSsaPkcs1Parameters.Algorithm.RS384;
                break;
            }
            case "RS512": {
                algorithm = JwtRsaSsaPkcs1Parameters.Algorithm.RS512;
                break;
            }
            default: {
                throw new GeneralSecurityException("Unknown Rsa Algorithm: " + getStringItem(jsonKey, "alg"));
            }
        }
        if (jsonKey.has("p") || jsonKey.has("q") || jsonKey.has("dp") || jsonKey.has("dq") || jsonKey.has("d") || jsonKey.has("qi")) {
            throw new UnsupportedOperationException("importing RSA private keys is not implemented");
        }
        expectStringItem(jsonKey, "kty", "RSA");
        validateUseIsSig(jsonKey);
        validateKeyOpsIsVerify(jsonKey);
        final BigInteger publicExponent = new BigInteger(1, Base64.urlSafeDecode(getStringItem(jsonKey, "e")));
        final BigInteger modulus = new BigInteger(1, Base64.urlSafeDecode(getStringItem(jsonKey, "n")));
        if (jsonKey.has("kid")) {
            return JwtRsaSsaPkcs1PublicKey.builder().setParameters(JwtRsaSsaPkcs1Parameters.builder().setModulusSizeBits(modulus.bitLength()).setPublicExponent(publicExponent).setAlgorithm(algorithm).setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.CUSTOM).build()).setModulus(modulus).setCustomKid(getStringItem(jsonKey, "kid")).build();
        }
        return JwtRsaSsaPkcs1PublicKey.builder().setParameters(JwtRsaSsaPkcs1Parameters.builder().setModulusSizeBits(modulus.bitLength()).setPublicExponent(publicExponent).setAlgorithm(algorithm).setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.IGNORED).build()).setModulus(modulus).build();
    }
    
    @AccessesPartialKey
    private static JwtRsaSsaPssPublicKey convertToRsaSsaPssKey(final JsonObject jsonKey) throws GeneralSecurityException {
        final String stringItem = getStringItem(jsonKey, "alg");
        JwtRsaSsaPssParameters.Algorithm algorithm = null;
        switch (stringItem) {
            case "PS256": {
                algorithm = JwtRsaSsaPssParameters.Algorithm.PS256;
                break;
            }
            case "PS384": {
                algorithm = JwtRsaSsaPssParameters.Algorithm.PS384;
                break;
            }
            case "PS512": {
                algorithm = JwtRsaSsaPssParameters.Algorithm.PS512;
                break;
            }
            default: {
                throw new GeneralSecurityException("Unknown Rsa Algorithm: " + getStringItem(jsonKey, "alg"));
            }
        }
        if (jsonKey.has("p") || jsonKey.has("q") || jsonKey.has("dq") || jsonKey.has("dq") || jsonKey.has("d") || jsonKey.has("qi")) {
            throw new UnsupportedOperationException("importing RSA private keys is not implemented");
        }
        expectStringItem(jsonKey, "kty", "RSA");
        validateUseIsSig(jsonKey);
        validateKeyOpsIsVerify(jsonKey);
        final BigInteger publicExponent = new BigInteger(1, Base64.urlSafeDecode(getStringItem(jsonKey, "e")));
        final BigInteger modulus = new BigInteger(1, Base64.urlSafeDecode(getStringItem(jsonKey, "n")));
        if (jsonKey.has("kid")) {
            return JwtRsaSsaPssPublicKey.builder().setParameters(JwtRsaSsaPssParameters.builder().setModulusSizeBits(modulus.bitLength()).setPublicExponent(publicExponent).setAlgorithm(algorithm).setKidStrategy(JwtRsaSsaPssParameters.KidStrategy.CUSTOM).build()).setModulus(modulus).setCustomKid(getStringItem(jsonKey, "kid")).build();
        }
        return JwtRsaSsaPssPublicKey.builder().setParameters(JwtRsaSsaPssParameters.builder().setModulusSizeBits(modulus.bitLength()).setPublicExponent(publicExponent).setAlgorithm(algorithm).setKidStrategy(JwtRsaSsaPssParameters.KidStrategy.IGNORED).build()).setModulus(modulus).build();
    }
    
    @AccessesPartialKey
    private static JwtEcdsaPublicKey convertToEcdsaKey(final JsonObject jsonKey) throws GeneralSecurityException {
        final String stringItem = getStringItem(jsonKey, "alg");
        JwtEcdsaParameters.Algorithm algorithm = null;
        switch (stringItem) {
            case "ES256": {
                expectStringItem(jsonKey, "crv", "P-256");
                algorithm = JwtEcdsaParameters.Algorithm.ES256;
                break;
            }
            case "ES384": {
                expectStringItem(jsonKey, "crv", "P-384");
                algorithm = JwtEcdsaParameters.Algorithm.ES384;
                break;
            }
            case "ES512": {
                expectStringItem(jsonKey, "crv", "P-521");
                algorithm = JwtEcdsaParameters.Algorithm.ES512;
                break;
            }
            default: {
                throw new GeneralSecurityException("Unknown Ecdsa Algorithm: " + getStringItem(jsonKey, "alg"));
            }
        }
        if (jsonKey.has("d")) {
            throw new UnsupportedOperationException("importing ECDSA private keys is not implemented");
        }
        expectStringItem(jsonKey, "kty", "EC");
        validateUseIsSig(jsonKey);
        validateKeyOpsIsVerify(jsonKey);
        final BigInteger x = new BigInteger(1, Base64.urlSafeDecode(getStringItem(jsonKey, "x")));
        final BigInteger y = new BigInteger(1, Base64.urlSafeDecode(getStringItem(jsonKey, "y")));
        final ECPoint publicPoint = new ECPoint(x, y);
        if (jsonKey.has("kid")) {
            return JwtEcdsaPublicKey.builder().setParameters(JwtEcdsaParameters.builder().setKidStrategy(JwtEcdsaParameters.KidStrategy.CUSTOM).setAlgorithm(algorithm).build()).setPublicPoint(publicPoint).setCustomKid(getStringItem(jsonKey, "kid")).build();
        }
        return JwtEcdsaPublicKey.builder().setParameters(JwtEcdsaParameters.builder().setKidStrategy(JwtEcdsaParameters.KidStrategy.IGNORED).setAlgorithm(algorithm).build()).setPublicPoint(publicPoint).build();
    }
    
    @Deprecated
    @InlineMe(replacement = "JwkSetConverter.fromPublicKeysetHandle(handle)", imports = { "com.google.crypto.tink.jwt.JwkSetConverter" })
    public static String fromKeysetHandle(final KeysetHandle handle, final KeyAccess keyAccess) throws IOException, GeneralSecurityException {
        return fromPublicKeysetHandle(handle);
    }
    
    @Deprecated
    @InlineMe(replacement = "JwkSetConverter.toPublicKeysetHandle(jwkSet)", imports = { "com.google.crypto.tink.jwt.JwkSetConverter" })
    public static KeysetHandle toKeysetHandle(final String jwkSet, final KeyAccess keyAccess) throws IOException, GeneralSecurityException {
        return toPublicKeysetHandle(jwkSet);
    }
    
    private JwkSetConverter() {
    }
}
