/** * Copyright 2010 Google Inc. * * 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 org.waveprotocol.wave.federation; import com.google.protobuf.ByteString; import org.waveprotocol.wave.federation.Proto.ProtocolHashedVersion; import org.waveprotocol.wave.model.id.IdURIEncoderDecoder; import org.waveprotocol.wave.model.id.WaveletName; import org.waveprotocol.wave.model.version.HashedVersionFactory; import org.waveprotocol.wave.model.version.HashedVersionFactoryImpl; import org.waveprotocol.wave.util.escapers.jvm.JavaUrlCodec; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; /** * Utility class for creating ProtocolHashedVersion instances from bytes. * * @author thorogood@google.com (Sam Thorogood) * @author jochen@google.com (Jochen Bekmann) */ public class ProtocolHashedVersionFactory { private static final IdURIEncoderDecoder URI_CODEC = new IdURIEncoderDecoder(new JavaUrlCodec()); private static final HashedVersionFactory HASH_FACTORY = new HashedVersionFactoryImpl(URI_CODEC); /** The first N bits of a SHA-256 hash are stored in the hash. Must not exceed 256. */ private static final int HASH_SIZE_BITS = 160; /** * Utility class only, disallow construction. */ private ProtocolHashedVersionFactory() { } private static ByteString nextHash(ByteString historyHash, ByteString appliedDeltaBytes) { MessageDigest sha256; try { sha256 = MessageDigest.getInstance("SHA-256"); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } sha256.update(historyHash.toByteArray()); return ByteString.copyFrom( sha256.digest(appliedDeltaBytes.toByteArray()), 0, HASH_SIZE_BITS / 8); } /** * Return the version zero hash for the given wavelet name. */ public static ProtocolHashedVersion createVersionZero(WaveletName waveletName) { byte[] initialHash = HASH_FACTORY.createVersionZero(waveletName).getHistoryHash(); return ProtocolHashedVersion.newBuilder() .setVersion(0) .setHistoryHash(ByteString.copyFrom(initialHash)) .build(); } /** * Return the updated ProtocolHashedVersion for the given input. * * @param appliedDeltaBytes raw bytes of appliedDelta * @param appliedAt version delta was applied at, must match appliedDelta * @param operationsApplied number of operations applied, must match appliedDelta * @return new ProtocolHashedVersion */ public static ProtocolHashedVersion create(ByteString appliedDeltaBytes, ProtocolHashedVersion appliedAt, int operationsApplied) { // TODO(thorogood): verify appliedAt and operationsApplied against the raw bytes? return ProtocolHashedVersion.newBuilder() .setVersion(appliedAt.getVersion() + operationsApplied) .setHistoryHash(nextHash(appliedAt.getHistoryHash(), appliedDeltaBytes)) .build(); } }