001// SPDX-FileCopyrightText: 2020 Paul Schaub <vanitasvitae@fsfe.org>, 2021 Flowcrypt a.s.
002//
003// SPDX-License-Identifier: Apache-2.0
004
005package org.pgpainless.key.info;
006
007import org.bouncycastle.asn1.ASN1ObjectIdentifier;
008import org.bouncycastle.asn1.gnu.GNUObjectIdentifiers;
009import org.bouncycastle.bcpg.ECDHPublicBCPGKey;
010import org.bouncycastle.bcpg.ECDSAPublicBCPGKey;
011import org.bouncycastle.bcpg.ECPublicBCPGKey;
012import org.bouncycastle.bcpg.EdDSAPublicBCPGKey;
013import org.bouncycastle.bcpg.S2K;
014import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
015import org.bouncycastle.openpgp.PGPPublicKey;
016import org.bouncycastle.openpgp.PGPSecretKey;
017import org.pgpainless.algorithm.PublicKeyAlgorithm;
018import org.pgpainless.key.generation.type.eddsa.EdDSACurve;
019
020public class KeyInfo {
021
022    private final PGPSecretKey secretKey;
023    private final PGPPublicKey publicKey;
024
025    public KeyInfo(PGPSecretKey secretKey) {
026        this.secretKey = secretKey;
027        this.publicKey = secretKey.getPublicKey();
028    }
029
030    public KeyInfo(PGPPublicKey publicKey) {
031        this.publicKey = publicKey;
032        this.secretKey = null;
033    }
034
035    public String getCurveName() {
036        return getCurveName(publicKey);
037    }
038
039    /**
040     * Returns indication that a contained secret key is encrypted.
041     *
042     * @return true if secret key is encrypted, false if secret key is not encrypted or there is public key only.
043     */
044    public boolean isEncrypted() {
045        return secretKey != null && isEncrypted(secretKey);
046    }
047
048    /**
049     * Returns indication that a contained secret key is not encrypted.
050     *
051     * @return true if secret key is not encrypted or there is public key only, false if secret key is encrypted.
052     */
053    public boolean isDecrypted() {
054        return secretKey == null || isDecrypted(secretKey);
055    }
056
057    /**
058     * Returns indication that a contained secret key has S2K of a type GNU_DUMMY_S2K.
059     *
060     * @return true if secret key has S2K of a type GNU_DUMMY_S2K, false if there is public key only,
061     *         or S2K on the secret key is absent or not of a type GNU_DUMMY_S2K.
062     */
063    public boolean hasDummyS2K() {
064        return secretKey != null && hasDummyS2K(secretKey);
065    }
066
067    public static String getCurveName(PGPPublicKey publicKey) {
068        PublicKeyAlgorithm algorithm = PublicKeyAlgorithm.fromId(publicKey.getAlgorithm());
069        ECPublicBCPGKey key;
070        switch (algorithm) {
071            case ECDSA: {
072                key = (ECDSAPublicBCPGKey) publicKey.getPublicKeyPacket().getKey();
073                break;
074            }
075            case ECDH: {
076                key = (ECDHPublicBCPGKey) publicKey.getPublicKeyPacket().getKey();
077                break;
078            }
079            case EDDSA: {
080                key = (EdDSAPublicBCPGKey) publicKey.getPublicKeyPacket().getKey();
081                break;
082            }
083            default:
084                throw new IllegalArgumentException("Not an elliptic curve public key (" + algorithm + ")");
085        }
086        return getCurveName(key);
087    }
088
089    public static String getCurveName(ECPublicBCPGKey key) {
090        ASN1ObjectIdentifier identifier = key.getCurveOID();
091
092        // Workaround for ECUtil not recognizing ed25519
093        if (identifier.equals(GNUObjectIdentifiers.Ed25519)) {
094            return EdDSACurve._Ed25519.getName();
095        }
096
097        return ECUtil.getCurveName(identifier);
098    }
099
100    /**
101     * Returns indication that a secret key is encrypted.
102     *
103     * @param secretKey A secret key to examine.
104     * @return true if secret key is encrypted, false otherwise.
105     */
106    public static boolean isEncrypted(PGPSecretKey secretKey) {
107        return secretKey.getS2KUsage() != 0;
108    }
109
110    /**
111     * Returns indication that a secret key is not encrypted.
112     *
113     * @param secretKey A secret key to examine.
114     * @return true if secret key is encrypted, false otherwise.
115     */
116    public static boolean isDecrypted(PGPSecretKey secretKey) {
117        return secretKey.getS2KUsage() == 0;
118    }
119
120    /**
121     * Returns indication that a secret key has S2K of a type GNU_DUMMY_S2K.
122     *
123     * @param secretKey A secret key to examine.
124     * @return true if secret key has S2K of a type GNU_DUMMY_S2K, false otherwise.
125     */
126    public static boolean hasDummyS2K(PGPSecretKey secretKey) {
127        final S2K s2k = secretKey.getS2K();
128        return s2k != null && s2k.getType() == S2K.GNU_DUMMY_S2K;
129    }
130}