001// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
002//
003// SPDX-License-Identifier: Apache-2.0
004
005package org.pgpainless.key.info;
006
007import java.util.Set;
008
009import javax.annotation.Nonnull;
010
011import org.bouncycastle.openpgp.PGPSignature;
012import org.pgpainless.algorithm.CompressionAlgorithm;
013import org.pgpainless.algorithm.HashAlgorithm;
014import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
015import org.pgpainless.key.SubkeyIdentifier;
016import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
017
018public abstract class KeyAccessor {
019
020    protected final KeyRingInfo info;
021    protected final SubkeyIdentifier key;
022
023    KeyAccessor(KeyRingInfo info, SubkeyIdentifier key) {
024        this.info = info;
025        this.key = key;
026    }
027
028    /**
029     * Depending on the way we address the key (key-id or user-id), return the respective {@link PGPSignature}
030     * which contains the algorithm preferences we are going to use.
031     *
032     * If we address a key via its user-id, we want to rely on the algorithm preferences in the user-id certification,
033     * while we would instead rely on those in the direct-key signature if we'd address the key by key-id.
034     *
035     * @return signature
036     */
037    public abstract @Nonnull PGPSignature getSignatureWithPreferences();
038
039    /**
040     * Return preferred symmetric key encryption algorithms.
041     *
042     * @return preferred symmetric algorithms
043     */
044    public Set<SymmetricKeyAlgorithm> getPreferredSymmetricKeyAlgorithms() {
045        return SignatureSubpacketsUtil.parsePreferredSymmetricKeyAlgorithms(getSignatureWithPreferences());
046    }
047
048    /**
049     * Return preferred hash algorithms.
050     *
051     * @return preferred hash algorithms
052     */
053    public Set<HashAlgorithm> getPreferredHashAlgorithms() {
054        return SignatureSubpacketsUtil.parsePreferredHashAlgorithms(getSignatureWithPreferences());
055    }
056
057    /**
058     * Return preferred compression algorithms.
059     *
060     * @return preferred compression algorithms
061     */
062    public Set<CompressionAlgorithm> getPreferredCompressionAlgorithms() {
063        return SignatureSubpacketsUtil.parsePreferredCompressionAlgorithms(getSignatureWithPreferences());
064    }
065
066    /**
067     * Address the key via a user-id (e.g. "Alice &lt;alice@wonderland.lit&gt;").
068     * In this case we are sourcing preferred algorithms from the user-id certification first.
069     */
070    public static class ViaUserId extends KeyAccessor {
071
072        private final String userId;
073
074        /**
075         * Access a key via user-id.
076         *
077         * @param info info about a key at a given date
078         * @param key id of the subkey
079         * @param userId user-id
080         */
081        public ViaUserId(KeyRingInfo info, SubkeyIdentifier key, String userId) {
082            super(info, key);
083            this.userId = userId;
084        }
085
086        @Override
087        public @Nonnull PGPSignature getSignatureWithPreferences() {
088            PGPSignature signature = info.getLatestUserIdCertification(userId);
089            if (signature != null) {
090                return signature;
091            }
092            throw new IllegalStateException("No valid user-id certification signature found for '" + userId + "'.");
093        }
094    }
095
096    /**
097     * Address the key via key-id.
098     * In this case we are sourcing preferred algorithms from the keys direct-key signature first.
099     */
100    public static class ViaKeyId extends KeyAccessor {
101
102        /**
103         * Address the key via key-id.
104         * @param info info about the key at a given date
105         * @param key key-id
106         */
107        public ViaKeyId(KeyRingInfo info, SubkeyIdentifier key) {
108            super(info, key);
109        }
110
111        @Override
112        public @Nonnull PGPSignature getSignatureWithPreferences() {
113            PGPSignature signature;
114            if (key.getPrimaryKeyId() != key.getSubkeyId()) {
115                signature = info.getCurrentSubkeyBindingSignature(key.getSubkeyId());
116            } else {
117                signature = info.getLatestDirectKeySelfSignature();
118            }
119
120            if (signature != null) {
121                return signature;
122            }
123
124            signature = info.getLatestUserIdCertification(info.getPrimaryUserId());
125            if (signature == null) {
126                throw new IllegalStateException("No valid signature found.");
127            }
128            return signature;
129        }
130    }
131}