blob: c12ff8b0aaed2c5dc8b40f5bf022365da4e1fe60 [file] [log] [blame]
// Copyright (C) 2015 The Android Open Source Project
//
// 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 com.google.gerrit.gpg;
import static com.google.common.base.Preconditions.checkArgument;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jgit.util.NB;
public class Fingerprint {
private final byte[] fp;
public static String toString(byte[] fp) {
checkLength(fp);
return String.format(
"%04X %04X %04X %04X %04X %04X %04X %04X %04X %04X",
NB.decodeUInt16(fp, 0),
NB.decodeUInt16(fp, 2),
NB.decodeUInt16(fp, 4),
NB.decodeUInt16(fp, 6),
NB.decodeUInt16(fp, 8),
NB.decodeUInt16(fp, 10),
NB.decodeUInt16(fp, 12),
NB.decodeUInt16(fp, 14),
NB.decodeUInt16(fp, 16),
NB.decodeUInt16(fp, 18));
}
public static long getId(byte[] fp) {
return NB.decodeInt64(fp, 12);
}
public static Map<Long, Fingerprint> byId(Iterable<Fingerprint> fps) {
Map<Long, Fingerprint> result = new HashMap<>();
for (Fingerprint fp : fps) {
result.put(fp.getId(), fp);
}
return Collections.unmodifiableMap(result);
}
private static byte[] checkLength(byte[] fp) {
checkArgument(fp.length == 20, "fingerprint must be 20 bytes, got %s", fp.length);
return fp;
}
/**
* Wrap a fingerprint byte array.
*
* <p>The newly created Fingerprint object takes ownership of the byte array, which must not be
* subsequently modified. (Most callers, such as hex decoders and {@code
* org.bouncycastle.openpgp.PGPPublicKey#getFingerprint()}, already produce fresh byte arrays).
*
* @param fp 20-byte fingerprint byte array to wrap.
*/
public Fingerprint(byte[] fp) {
this.fp = checkLength(fp);
}
/**
* Wrap a portion of a fingerprint byte array.
*
* <p>Unlike {@link #Fingerprint(byte[])}, creates a new copy of the byte array.
*
* @param buf byte array to wrap; must have at least {@code off + 20} bytes.
* @param off offset in buf.
*/
public Fingerprint(byte[] buf, int off) {
int expected = 20 + off;
checkArgument(
buf.length >= expected,
"fingerprint buffer must have at least %s bytes, got %s",
expected,
buf.length);
this.fp = new byte[20];
System.arraycopy(buf, off, fp, 0, 20);
}
public byte[] get() {
return fp;
}
public boolean equalsBytes(byte[] bytes) {
return Arrays.equals(fp, bytes);
}
@Override
public int hashCode() {
// Same hash code as ObjectId: second int word.
return NB.decodeInt32(fp, 4);
}
@Override
public boolean equals(Object o) {
return (o instanceof Fingerprint) && equalsBytes(((Fingerprint) o).fp);
}
@Override
public String toString() {
return toString(fp);
}
public long getId() {
return getId(fp);
}
}