SHA1.java

  1. /*
  2.  * Copyright (C) 2022, Matthias Sohn <matthias.sohn@sap.com> and others
  3.  *
  4.  * This program and the accompanying materials are made available under the
  5.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  6.  * https://www.eclipse.org/org/documents/edl-v10.php.
  7.  *
  8.  * SPDX-License-Identifier: BSD-3-Clause
  9.  */
  10. package org.eclipse.jgit.util.sha1;

  11. import java.io.IOException;
  12. import java.security.MessageDigest;

  13. import org.eclipse.jgit.errors.ConfigInvalidException;
  14. import org.eclipse.jgit.lib.ConfigConstants;
  15. import org.eclipse.jgit.lib.MutableObjectId;
  16. import org.eclipse.jgit.lib.ObjectId;
  17. import org.eclipse.jgit.util.SystemReader;

  18. /**
  19.  * SHA-1 interface from FIPS 180-1 / RFC 3174 with optional collision detection.
  20.  * Some implementations may not support collision detection.
  21.  * <p>
  22.  * See <a href="https://tools.ietf.org/html/rfc3174">RFC 3174</a>.
  23.  */
  24. public abstract class SHA1 {
  25.     /**
  26.      * SHA1 implementations available in JGit
  27.      *
  28.      * @since 5.13.2
  29.      */
  30.     public enum Sha1Implementation {
  31.         /**
  32.          * {@link SHA1Java} implemented in Java, supports collision detection.
  33.          */
  34.         JAVA(SHA1Java.class),
  35.         /**
  36.          * Native implementation based on JDK's {@link MessageDigest}.
  37.          */
  38.         JDKNATIVE(SHA1Native.class);

  39.         private final String implClassName;

  40.         private Sha1Implementation(Class implClass) {
  41.             this.implClassName = implClass.getName();
  42.         }

  43.         @Override
  44.         public String toString() {
  45.             return implClassName;
  46.         }
  47.     }

  48.     private static final Sha1Implementation SHA1_IMPLEMENTATION = fromConfig();

  49.     private static Sha1Implementation fromConfig() {
  50.         try {
  51.             return SystemReader.getInstance().getUserConfig().getEnum(
  52.                     ConfigConstants.CONFIG_CORE_SECTION, null,
  53.                     ConfigConstants.SHA1_IMPLEMENTATION,
  54.                     Sha1Implementation.JAVA);
  55.         } catch (ConfigInvalidException | IOException e) {
  56.             return Sha1Implementation.JAVA;
  57.         }
  58.     }

  59.     private static Sha1Implementation getImplementation() {
  60.         String fromSystemProperty = System
  61.                 .getProperty("org.eclipse.jgit.util.sha1.implementation"); //$NON-NLS-1$
  62.         if (fromSystemProperty == null) {
  63.             return SHA1_IMPLEMENTATION;
  64.         }
  65.         if (fromSystemProperty
  66.                 .equalsIgnoreCase(Sha1Implementation.JAVA.name())) {
  67.             return Sha1Implementation.JAVA;
  68.         }
  69.         if (fromSystemProperty
  70.                 .equalsIgnoreCase(Sha1Implementation.JDKNATIVE.name())) {
  71.             return Sha1Implementation.JDKNATIVE;
  72.         }
  73.         return SHA1_IMPLEMENTATION;
  74.     }

  75.     /**
  76.      * Create a new context to compute a SHA-1 hash of data.
  77.      * <p>
  78.      * If {@code core.sha1Implementation = jdkNative} in the user level global
  79.      * git configuration or the system property
  80.      * {@code org.eclipse.jgit.util.sha1.implementation = jdkNative} it will
  81.      * create an object using the implementation in the JDK. If both are set the
  82.      * system property takes precedence. Otherwise the pure Java implementation
  83.      * will be used which supports collision detection but is slower.
  84.      *
  85.      * @return a new context to compute a SHA-1 hash of data.
  86.      */
  87.     public static SHA1 newInstance() {
  88.         if (getImplementation() == Sha1Implementation.JDKNATIVE) {
  89.             return new SHA1Native();
  90.         }
  91.         return new SHA1Java();
  92.     }

  93.     /**
  94.      * Update the digest computation by adding a byte.
  95.      *
  96.      * @param b a byte.
  97.      */
  98.     public abstract void update(byte b);

  99.     /**
  100.      * Update the digest computation by adding bytes to the message.
  101.      *
  102.      * @param in
  103.      *            input array of bytes.
  104.      */
  105.     public abstract void update(byte[] in);

  106.     /**
  107.      * Update the digest computation by adding bytes to the message.
  108.      *
  109.      * @param in
  110.      *            input array of bytes.
  111.      * @param p
  112.      *            offset to start at from {@code in}.
  113.      * @param len
  114.      *            number of bytes to hash.
  115.      */
  116.     public abstract void update(byte[] in, int p, int len);

  117.     /**
  118.      * Finish the digest and return the resulting hash.
  119.      * <p>
  120.      * Once {@code digest()} is called, this instance should be discarded.
  121.      *
  122.      * @return the bytes for the resulting hash.
  123.      * @throws org.eclipse.jgit.util.sha1.Sha1CollisionException
  124.      *             if a collision was detected and safeHash is false.
  125.      */
  126.     public abstract byte[] digest() throws Sha1CollisionException;

  127.     /**
  128.      * Finish the digest and return the resulting hash.
  129.      * <p>
  130.      * Once {@code digest()} is called, this instance should be discarded.
  131.      *
  132.      * @return the ObjectId for the resulting hash.
  133.      * @throws org.eclipse.jgit.util.sha1.Sha1CollisionException
  134.      *             if a collision was detected and safeHash is false.
  135.      */
  136.     public abstract ObjectId toObjectId() throws Sha1CollisionException;

  137.     /**
  138.      * Finish the digest and return the resulting hash.
  139.      * <p>
  140.      * Once {@code digest()} is called, this instance should be discarded.
  141.      *
  142.      * @param id
  143.      *            destination to copy the digest to.
  144.      * @throws org.eclipse.jgit.util.sha1.Sha1CollisionException
  145.      *             if a collision was detected and safeHash is false.
  146.      */
  147.     public abstract void digest(MutableObjectId id)
  148.             throws Sha1CollisionException;

  149.     /**
  150.      * Reset this instance to compute another hash.
  151.      *
  152.      * @return {@code this}.
  153.      */
  154.     public abstract SHA1 reset();

  155.     /**
  156.      * Enable likely collision detection.
  157.      * <p>
  158.      * Default for implementations supporting collision detection is
  159.      * {@code true}.
  160.      * <p>
  161.      * Implementations not supporting collision detection ignore calls to this
  162.      * method.
  163.      *
  164.      * @param detect
  165.      *            a boolean.
  166.      * @return {@code this}
  167.      */
  168.     public abstract SHA1 setDetectCollision(boolean detect);

  169.     /**
  170.      * Check if a collision was detected. This method only returns an accurate
  171.      * result after the digest was obtained through {@link #digest()},
  172.      * {@link #digest(MutableObjectId)} or {@link #toObjectId()}, as the hashing
  173.      * function must finish processing to know the final state.
  174.      * <p>
  175.      * Implementations not supporting collision detection always return
  176.      * {@code false}.
  177.      * <p>
  178.      *
  179.      * @return {@code true} if a likely collision was detected.
  180.      */
  181.     public abstract boolean hasCollision();
  182. }