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