me.footlights.core.crypto.Fingerprint.java Source code

Java tutorial

Introduction

Here is the source code for me.footlights.core.crypto.Fingerprint.java

Source

/*
 * Copyright 2011 Jonathan Anderson
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package me.footlights.core.crypto;

import java.net.URI;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.logging.Level;
import java.util.logging.Logger;

import scala.Option;

import org.apache.commons.codec.binary.Base32;
import org.apache.commons.codec.binary.Hex;

import me.footlights.core.HasBytes;
import me.footlights.core.Preconditions;
import me.footlights.core.Preferences;
import me.footlights.core.ConfigurationError;
import me.footlights.core.ProgrammerError;
import me.footlights.core.data.FormatException;

/** A fingerprint for a number of bytes. */
public class Fingerprint {
    public static Fingerprint of(byte[] bytes) {
        return Fingerprint.newBuilder().setContent(bytes).build();
    }

    public static Fingerprint of(ByteBuffer bytes) {
        return Fingerprint.newBuilder().setContent(bytes).build();
    }

    public static Option<Fingerprint> unapply(String s) {
        try {
            return Option.apply(decode(new URI(s)));
        } catch (FormatException e) {
            log.log(Level.INFO, e.getLocalizedMessage() + ": " + s);
        } catch (NoSuchAlgorithmException e) {
            log.log(Level.WARNING, "No such algorithm", e);
        } catch (URISyntaxException e) {
            log.log(Level.INFO, "Bad fingerprint syntax: " + s);
        }

        return Option.apply(null);
    }

    public static Fingerprint decode(URI uri) throws FormatException, NoSuchAlgorithmException, URISyntaxException {
        if ((uri.getScheme() == null) || !uri.getScheme().equals("urn"))
            throw new FormatException("Fingerprint must start with 'urn:'");

        // Recurse down into scheme-specific part, which is also a URI.
        URI data = new URI(uri.getSchemeSpecificPart());
        return decode(data.getScheme(), data.getSchemeSpecificPart());
    }

    public static Fingerprint decode(String name) throws NoSuchAlgorithmException {
        String parts[] = name.split(":");
        if (parts.length != 3)
            throw new IllegalArgumentException("Invalid fingerprint '" + name + "'");

        if (!parts[0].equals("urn"))
            throw new IllegalArgumentException("URNs must start with 'urn:'");

        return decode(parts[1].toLowerCase(), parts[2].toUpperCase());
    }

    public static Fingerprint decode(String algorithmName, String hash) throws NoSuchAlgorithmException {
        MessageDigest algorithm = MessageDigest.getInstance(algorithmName);
        final URI uri;
        try {
            uri = new URI("urn", algorithmName + ":" + hash, null);
        } catch (URISyntaxException e) {
            throw new IllegalArgumentException(e);
        }

        return new Fingerprint(algorithm, ByteBuffer.wrap(new Base32().decode(hash.getBytes())), uri);
    }

    public static Builder newBuilder() {
        return new Builder(Preferences.getDefaultPreferences());
    }

    public URI toURI() {
        return uri;
    }

    public String encode() {
        return uri.toString();
    }

    public MessageDigest getAlgorithm() {
        return algorithm;
    }

    public boolean matches(ByteBuffer b) {
        return (0 == bytes.compareTo(b));
    }

    public boolean matches(byte[] b) {
        return matches(ByteBuffer.wrap(b));
    }

    public ByteBuffer getBytes() {
        return bytes.asReadOnlyBuffer();
    }

    public byte[] copyBytes() {
        byte[] copy = new byte[bytes.remaining()];
        bytes.mark();
        bytes.get(copy);
        bytes.reset();
        return copy;
    }

    public static class Builder {
        public Fingerprint build() {
            Preconditions.notNull(bytes);

            ByteBuffer hash = ByteBuffer.wrap(algorithm.digest(bytes));

            final URI uri;
            try {
                uri = new URI("urn", algorithm.getAlgorithm().toLowerCase() + ":"
                        + new String(new Base32().encode(hash.array())), null);
            } catch (URISyntaxException e) {
                throw new ProgrammerError("Failed to make URI for Fingerprint", e);
            }

            return new Fingerprint(algorithm, hash, uri);
        }

        public Builder setAlgorithm(String a) throws NoSuchAlgorithmException {
            algorithm = MessageDigest.getInstance(a);
            return this;
        }

        public Builder setContent(byte[] b) {
            bytes = b;
            return this;
        }

        public Builder setContent(HasBytes h) {
            return setContent(h.getBytes());
        }

        public Builder setContent(ByteBuffer b) {
            if (b.isReadOnly()) {
                bytes = new byte[b.remaining()];
                b.get(bytes);
            } else
                bytes = b.array();

            return this;
        }

        private Builder(Preferences preferences) {
            try {
                algorithm = MessageDigest.getInstance(preferences.getString("crypto.hash.algorithm").get());
            } catch (NoSuchAlgorithmException e) {
                throw new ConfigurationError("Invalid hash algorithm: " + e);
            }
        }

        private MessageDigest algorithm;
        private byte[] bytes;
    }

    @Override
    public String toString() {
        return uri.toString();
    }

    @Override
    public int hashCode() {
        return bytes.duplicate().getInt();
    }

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof Fingerprint))
            return false;
        Fingerprint f = (Fingerprint) o;

        if (!algorithm.getAlgorithm().toLowerCase().equals(f.algorithm.getAlgorithm().toLowerCase()))
            return false;
        if (bytes.compareTo(f.bytes) != 0)
            return false;

        return true;
    }

    String hex() {
        return Hex.encodeHexString(bytes.array());
    }

    private Fingerprint(MessageDigest hashAlgorithm, ByteBuffer fingerprintBytes, URI uri) {
        Preconditions.check(uri.getScheme().equals("urn"));

        this.algorithm = hashAlgorithm;
        this.bytes = fingerprintBytes;
        this.uri = uri;
    }

    private MessageDigest algorithm;
    private ByteBuffer bytes;
    private URI uri;

    private static Logger log = Logger.getLogger(Fingerprint.class.getCanonicalName());
}