| // 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); | |
| } | |
| } |