package com.trilead.ssh2.signature; import com.trilead.ssh2.crypto.CertificateDecoder; import com.trilead.ssh2.packets.TypesReader; import com.trilead.ssh2.packets.TypesWriter; import net.i2p.crypto.eddsa.EdDSAPrivateKey; import net.i2p.crypto.eddsa.EdDSAPublicKey; import net.i2p.crypto.eddsa.EdDSASecurityProvider; import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable; import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec; import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec; import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec; import java.io.IOException; import java.security.GeneralSecurityException; import java.security.KeyFactory; import java.security.KeyPair; import java.security.PrivateKey; import java.security.PublicKey; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * @author Michael Clarke */ public class ED25519KeyAlgorithm extends KeyAlgorithm<EdDSAPublicKey, EdDSAPrivateKey> { private static final String ED25519_KEY_NAME = "ssh-ed25519"; private static final String ED25519_CURVE_NAME = "Ed25519"; protected ED25519KeyAlgorithm() { /*Whilst the signature is 'NoneWith', it actually uses a digest from the key's parameter specification * so is really SHA512WithEdDSA, but has to be looked up using what's in the Provider implementation. */ super("NoneWithEdDSA", ED25519_KEY_NAME, EdDSAPrivateKey.class, new EdDSASecurityProvider()); } @Override public byte[] encodeSignature(byte[] signature) throws IOException { TypesWriter signatureWriter = new TypesWriter(); signatureWriter.writeString(ED25519_KEY_NAME); signatureWriter.writeString(signature, 0, signature.length); return signatureWriter.getBytes(); } @Override public byte[] decodeSignature(byte[] encodedSignature) throws IOException { TypesReader typesReader = new TypesReader(encodedSignature); String signatureFormat = typesReader.readString(); if (!signatureFormat.equals(ED25519_KEY_NAME)) { throw new IOException("Invalid signature format"); } byte[] signature = typesReader.readByteString(); if (typesReader.remain() != 0) { throw new IOException("Unexpected padding in signature"); } return signature; } @Override public byte[] encodePublicKey(EdDSAPublicKey publicKey) throws IOException { byte[] encoded = publicKey.getAbyte(); TypesWriter typesWriter = new TypesWriter(); typesWriter.writeString(ED25519_KEY_NAME); typesWriter.writeString(encoded, 0, encoded.length); return typesWriter.getBytes(); } @Override public EdDSAPublicKey decodePublicKey(byte[] encodedPublicKey) throws IOException { TypesReader typesReader = new TypesReader(encodedPublicKey); String keyFormat = typesReader.readString(); if (!keyFormat.equals(ED25519_KEY_NAME)) { throw new IOException("Invalid key type"); } byte[] keyBytes = typesReader.readByteString(); if (0 != typesReader.remain()) { throw new IOException("Unexpected padding in public key"); } return new EdDSAPublicKey(new EdDSAPublicKeySpec(keyBytes, EdDSANamedCurveTable.getByName(ED25519_CURVE_NAME))); } @Override public List<CertificateDecoder> getCertificateDecoders() { return Collections.singletonList((CertificateDecoder) new OpenSshCertificateDecoder(ED25519KeyAlgorithm.ED25519_KEY_NAME) { @Override KeyPair generateKeyPair(TypesReader reader) throws GeneralSecurityException, IOException { EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName(ED25519KeyAlgorithm.ED25519_CURVE_NAME); byte[] publicKeyBytes = reader.readByteString(); byte[] privateKeyBytes = reader.readByteString(); EdDSAPublicKeySpec publicKeySpec = new EdDSAPublicKeySpec(publicKeyBytes, spec); EdDSAPrivateKeySpec privateKeySpec = new EdDSAPrivateKeySpec(Arrays.copyOfRange(privateKeyBytes, 0, 32), spec); KeyFactory factory = KeyFactory.getInstance("EdDSA", new EdDSASecurityProvider()); PublicKey publicKey = factory.generatePublic(publicKeySpec); PrivateKey privateKey = factory.generatePrivate(privateKeySpec); return new KeyPair(publicKey, privateKey); } }); } }