blob: 41bb88e7901c88e8cfae4dbca2b5fba7a9568ea6 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.sshd.common.signature;
import java.io.StreamCorruptedException;
import java.math.BigInteger;
import org.apache.sshd.common.cipher.ECCurves;
import org.apache.sshd.common.util.Pair;
import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.common.util.buffer.Buffer;
import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
import org.apache.sshd.common.util.io.DERParser;
import org.apache.sshd.common.util.io.DERWriter;
/**
* Signature algorithm for EC keys using ECDSA.
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
* @see <A HREF="http://tools.ietf.org/html/rfc3278#section-8.2">RFC3278 section 8.2</A>
*/
public class SignatureECDSA extends AbstractSignature {
public static class SignatureECDSA256 extends SignatureECDSA {
public static final String DEFAULT_ALGORITHM = "SHA256withECDSA";
public SignatureECDSA256() {
super(DEFAULT_ALGORITHM);
}
}
public static class SignatureECDSA384 extends SignatureECDSA {
public static final String DEFAULT_ALGORITHM = "SHA384withECDSA";
public SignatureECDSA384() {
super(DEFAULT_ALGORITHM);
}
}
public static class SignatureECDSA521 extends SignatureECDSA {
public static final String DEFAULT_ALGORITHM = "SHA512withECDSA";
public SignatureECDSA521() {
super(DEFAULT_ALGORITHM);
}
}
protected SignatureECDSA(String algo) {
super(algo);
}
@Override
public byte[] sign() throws Exception {
java.security.Signature signature = ValidateUtils.checkNotNull(getSignature(), "Signature not initialized");
byte[] sig = signature.sign();
try (DERParser parser = new DERParser(sig)) {
int type = parser.read();
if (type != 0x30) {
throw new StreamCorruptedException("Invalid signature format - not a DER SEQUENCE: 0x" + Integer.toHexString(type));
}
// length of remaining encoding of the 2 integers
int remainLen = parser.readLength();
/*
* There are supposed to be 2 INTEGERs, each encoded with:
*
* - one byte representing the fact that it is an INTEGER
* - one byte of the integer encoding length
* - at least one byte of integer data (zero length is not an option)
*/
if (remainLen < (2 * 3)) {
throw new StreamCorruptedException("Invalid signature format - not enough encoded data length: " + remainLen);
}
BigInteger r = parser.readBigInteger();
BigInteger s = parser.readBigInteger();
// Write the <r,s> to its own types writer.
Buffer rsBuf = new ByteArrayBuffer();
rsBuf.putMPInt(r);
rsBuf.putMPInt(s);
return rsBuf.getCompactData();
}
}
@Override
public boolean verify(byte[] sig) throws Exception {
byte[] data = sig;
Pair<String, byte[]> encoding = extractEncodedSignature(data);
if (encoding != null) {
String keyType = encoding.getFirst();
ECCurves curve = ECCurves.fromKeyType(keyType);
ValidateUtils.checkNotNull(curve, "Unknown curve type: %s", keyType);
data = encoding.getSecond();
}
Buffer rsBuf = new ByteArrayBuffer(data);
byte[] rArray = rsBuf.getMPIntAsBytes();
byte[] rEncoding;
try (DERWriter w = new DERWriter(rArray.length + 4)) { // in case length > 0x7F
w.writeBigInteger(rArray);
rEncoding = w.toByteArray();
}
byte[] sArray = rsBuf.getMPIntAsBytes();
byte[] sEncoding;
try (DERWriter w = new DERWriter(sArray.length + 4)) { // in case length > 0x7F
w.writeBigInteger(sArray);
sEncoding = w.toByteArray();
}
int remaining = rsBuf.available();
if (remaining != 0) {
throw new StreamCorruptedException("Signature had padding - remaining=" + remaining);
}
int length = rEncoding.length + sEncoding.length;
byte[] encoded;
try (DERWriter w = new DERWriter(1 + length + 4)) { // in case length > 0x7F
w.write(0x30); // SEQUENCE
w.writeLength(length);
w.write(rEncoding);
w.write(sEncoding);
encoded = w.toByteArray();
}
return doVerify(encoded);
}
}