aboutsummaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit/src/org/eclipse/jgit/util/sha1/SHA1.java
blob: 56e90d0636fe42af5abc035be73b1e4e08257b88 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/*
 * Copyright (C) 2022, Matthias Sohn <matthias.sohn@sap.com> and others
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Distribution License v. 1.0 which is available at
 * https://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
package org.eclipse.jgit.util.sha1;

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

import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.MutableObjectId;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.util.SystemReader;

/**
 * SHA-1 interface from FIPS 180-1 / RFC 3174 with optional collision detection.
 * Some implementations may not support collision detection.
 * <p>
 * See <a href="https://tools.ietf.org/html/rfc3174">RFC 3174</a>.
 */
public abstract class SHA1 {
	/**
	 * SHA1 implementations available in JGit
	 *
	 * @since 5.13.2
	 */
	public enum Sha1Implementation {
		/**
		 * {@link SHA1Java} implemented in Java, supports collision detection.
		 */
		JAVA(SHA1Java.class),
		/**
		 * Native implementation based on JDK's {@link MessageDigest}.
		 */
		JDKNATIVE(SHA1Native.class);

		private final String implClassName;

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

		@Override
		public String toString() {
			return implClassName;
		}
	}

	private static final Sha1Implementation SHA1_IMPLEMENTATION = fromConfig();

	private static Sha1Implementation fromConfig() {
		try {
			return SystemReader.getInstance().getUserConfig().getEnum(
					ConfigConstants.CONFIG_CORE_SECTION, null,
					ConfigConstants.SHA1_IMPLEMENTATION,
					Sha1Implementation.JAVA);
		} catch (ConfigInvalidException | IOException e) {
			return Sha1Implementation.JAVA;
		}
	}

	private static Sha1Implementation getImplementation() {
		String fromSystemProperty = System
				.getProperty("org.eclipse.jgit.util.sha1.implementation"); //$NON-NLS-1$
		if (fromSystemProperty == null) {
			return SHA1_IMPLEMENTATION;
		}
		if (fromSystemProperty
				.equalsIgnoreCase(Sha1Implementation.JAVA.name())) {
			return Sha1Implementation.JAVA;
		}
		if (fromSystemProperty
				.equalsIgnoreCase(Sha1Implementation.JDKNATIVE.name())) {
			return Sha1Implementation.JDKNATIVE;
		}
		return SHA1_IMPLEMENTATION;
	}

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

	/**
	 * Update the digest computation by adding a byte.
	 *
	 * @param b a byte.
	 */
	public abstract void update(byte b);

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

	/**
	 * Update the digest computation by adding bytes to the message.
	 *
	 * @param in
	 *            input array of bytes.
	 * @param p
	 *            offset to start at from {@code in}.
	 * @param len
	 *            number of bytes to hash.
	 */
	public abstract void update(byte[] in, int p, int len);

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

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

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

	/**
	 * Reset this instance to compute another hash.
	 *
	 * @return {@code this}.
	 */
	public abstract SHA1 reset();

	/**
	 * Enable likely collision detection.
	 * <p>
	 * Default for implementations supporting collision detection is
	 * {@code true}.
	 * <p>
	 * Implementations not supporting collision detection ignore calls to this
	 * method.
	 *
	 * @param detect
	 *            a boolean.
	 * @return {@code this}
	 */
	public abstract SHA1 setDetectCollision(boolean detect);

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