/* * Copyright 2012 Thomas Bocek * * 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 net.tomp2p.message; import io.netty.buffer.ByteBuf; import java.io.IOException; import net.tomp2p.peers.Number160; import net.tomp2p.utils.Utils; /** * Bare minimum ASN.1 encoder and decoder for the signature. * * @author Thomas Bocek * */ public class DSASignatureCodec implements SignatureCodec { public static final int SIGNATURE_SIZE = 2 * Number160.BYTE_ARRAY_SIZE; private final Number160 number1; private final Number160 number2; /** * Create a signature from an already encoded signature data * * @param encodedData the signature (encoded) * @throws IOException */ public DSASignatureCodec(byte[] encodedData) throws IOException { if (encodedData[0] != 0x30) { throw new IOException("expected sequence with value 48, but got " + encodedData[0]); } int seqLen = encodedData[1]; if (seqLen < 0) { throw new IOException("cannot handle seq length > than 127, got " + seqLen); } if (encodedData[2] != 0x02) { throw new IOException("expected sequence with value 2, but got " + encodedData[2]); } int intLen1 = encodedData[3]; if (intLen1 < 0) { throw new IOException("cannot handle int length > than 127, got " + intLen1); } number1 = encodeNumber(encodedData, 4, intLen1); if (encodedData[4 + intLen1] != 0x02) { throw new IOException("expected sequence with value 2, but got " + encodedData[4 + intLen1]); } int intLen2 = encodedData[5 + intLen1]; if (intLen2 < 0) { throw new IOException("cannot handle int length > than 127, got " + intLen2); } number2 = encodeNumber(encodedData, 6 + intLen1, intLen2); } /** * Create a codec from a buffer, where the signature data is encoded * @param buf the buffer holding the signature at the next reader index */ public DSASignatureCodec(ByteBuf buf) { byte[] me = new byte[Number160.BYTE_ARRAY_SIZE]; buf.readBytes(me); number1 = new Number160(me); buf.readBytes(me); number2 = new Number160(me); } private Number160 encodeNumber(byte[] encodedData, int offset, int len) throws IOException { if (len > 20) { int bias = len - 20; for (int i = 0; i < bias; i++) { if (encodedData[offset + i] != 0) { throw new IOException("we did not expect such a large number, it should be 160bit"); } } return new Number160(encodedData, offset + bias, 20); } else { return new Number160(encodedData, offset, len); } } /** * @return ASN1 encoded signature */ @Override public byte[] encode() { byte me[] = new byte[2 + (2 * (20 + 2))]; me[0] = 0x30; me[1] = 2 * (20 + 2); me[2] = 0x02; me[3] = 20; number1.encode(me, 4); me[24] = 0x02; me[25] = 20; number2.encode(me, 26); return me; } @Override public SignatureCodec write(ByteBuf buf) { buf.writeBytes(number1.toByteArray()); buf.writeBytes(number2.toByteArray()); return this; } public Number160 number1() { return number1; } public Number160 number2() { return number2; } @Override public String toString() { StringBuilder sb = new StringBuilder("sig["); sb.append(number1.toString()).append('/'); sb.append(number2.toString()).append(']'); return sb.toString(); } @Override public int hashCode() { return number1.hashCode() ^ number2.hashCode(); } @Override public boolean equals(Object obj) { if (!(obj instanceof DSASignatureCodec)) { return false; } if (obj == this) { return true; } DSASignatureCodec s = (DSASignatureCodec) obj; return Utils.equals(number1, s.number1) && Utils.equals(number2, s.number2); } @Override public int signatureSize() { return SIGNATURE_SIZE; } }