001// SPDX-FileCopyrightText: 2018 Paul Schaub <vanitasvitae@fsfe.org>
002//
003// SPDX-License-Identifier: Apache-2.0
004
005package org.pgpainless.key;
006
007import java.net.URI;
008import java.net.URISyntaxException;
009import java.nio.Buffer;
010import java.nio.ByteBuffer;
011import javax.annotation.Nonnull;
012
013import org.bouncycastle.openpgp.PGPKeyRing;
014import org.bouncycastle.openpgp.PGPPublicKey;
015import org.bouncycastle.openpgp.PGPPublicKeyRing;
016import org.bouncycastle.openpgp.PGPSecretKey;
017import org.bouncycastle.openpgp.PGPSecretKeyRing;
018import org.bouncycastle.util.encoders.Hex;
019
020/**
021 * This class represents a hex encoded, uppercase OpenPGP v4 fingerprint.
022 */
023public class OpenPgpV4Fingerprint extends OpenPgpFingerprint {
024
025    public static final String SCHEME = "openpgp4fpr";
026
027    /**
028     * Create an {@link OpenPgpV4Fingerprint}.
029     * @see <a href="https://xmpp.org/extensions/xep-0373.html#annoucning-pubkey">
030     *     XEP-0373 ยง4.1: The OpenPGP Public-Key Data Node about how to obtain the fingerprint</a>
031     * @param fingerprint hexadecimal representation of the fingerprint.
032     */
033    public OpenPgpV4Fingerprint(@Nonnull String fingerprint) {
034        super(fingerprint);
035    }
036
037    @Override
038    public int getVersion() {
039        return 4;
040    }
041
042    @Override
043    protected boolean isValid(@Nonnull String fp) {
044        return fp.matches("[0-9A-F]{40}");
045    }
046
047    @Override
048    public long getKeyId() {
049        byte[] bytes = Hex.decode(toString().getBytes(utf8));
050        ByteBuffer buf = ByteBuffer.wrap(bytes);
051
052        // We have to cast here in order to be compatible with java 8
053        // https://github.com/eclipse/jetty.project/issues/3244
054        ((Buffer) buf).position(12);
055
056        return buf.getLong();
057    }
058
059    @Override
060    public String prettyPrint() {
061        String fp = toString();
062        StringBuilder pretty = new StringBuilder();
063        for (int i = 0; i < 5; i++) {
064            pretty.append(fp, i * 4, (i + 1) * 4).append(' ');
065        }
066        pretty.append(' ');
067        for (int i = 5; i < 9; i++) {
068            pretty.append(fp, i * 4, (i + 1) * 4).append(' ');
069        }
070        pretty.append(fp, 36, 40);
071        return pretty.toString();
072    }
073
074    public OpenPgpV4Fingerprint(@Nonnull byte[] bytes) {
075        super(bytes);
076    }
077
078    public OpenPgpV4Fingerprint(@Nonnull PGPPublicKey key) {
079        super(key);
080    }
081
082    public OpenPgpV4Fingerprint(@Nonnull PGPSecretKey key) {
083        this(key.getPublicKey());
084    }
085
086    public OpenPgpV4Fingerprint(@Nonnull PGPPublicKeyRing ring) {
087        super(ring);
088    }
089
090    public OpenPgpV4Fingerprint(@Nonnull PGPSecretKeyRing ring) {
091        super(ring);
092    }
093
094    public OpenPgpV4Fingerprint(@Nonnull PGPKeyRing ring) {
095        super(ring);
096    }
097
098    @Override
099    public boolean equals(Object other) {
100        if (other == null) {
101            return false;
102        }
103
104        if (!(other instanceof CharSequence)) {
105            return false;
106        }
107
108        return this.toString().equals(other.toString());
109    }
110
111    @Override
112    public int hashCode() {
113        return toString().hashCode();
114    }
115
116    /**
117     * Return the fingerprint as an openpgp4fpr {@link URI}.
118     * An example would be 'openpgp4fpr:7F9116FEA90A5983936C7CFAA027DB2F3E1E118A'.
119     *
120     * @return openpgp4fpr fingerprint uri
121     * @see <a href="https://metacode.biz/openpgp/openpgp4fpr">openpgp4fpr URI scheme</a>
122     */
123    public URI toUri() {
124        try {
125            return new URI(OpenPgpV4Fingerprint.SCHEME, toString(), null);
126        } catch (URISyntaxException e) {
127            throw new AssertionError(e);
128        }
129    }
130
131    /**
132     * Convert an openpgp4fpr URI to an {@link OpenPgpV4Fingerprint}.
133     *
134     * @param uri {@link URI} with scheme 'openpgp4fpr'
135     * @return fingerprint parsed from the uri
136     * @see <a href="https://metacode.biz/openpgp/openpgp4fpr">openpgp4fpr URI scheme</a>
137     */
138    public static OpenPgpV4Fingerprint fromUri(URI uri) {
139        if (!SCHEME.equals(uri.getScheme())) {
140            throw new IllegalArgumentException("URI scheme MUST equal '" + SCHEME + "'");
141        }
142        return new OpenPgpV4Fingerprint(uri.getSchemeSpecificPart());
143    }
144
145    @Override
146    public int compareTo(@Nonnull OpenPgpFingerprint openPgpFingerprint) {
147        return toString().compareTo(openPgpFingerprint.toString());
148    }
149}