diff --git a/core/java/src/net/i2p/data/Certificate.java b/core/java/src/net/i2p/data/Certificate.java index 88b1f9d59f654b4b0b0ec15d47793fbc2c2424dd..afcfbf1b04e8099837c9be48b07876861e97798e 100644 --- a/core/java/src/net/i2p/data/Certificate.java +++ b/core/java/src/net/i2p/data/Certificate.java @@ -103,6 +103,8 @@ public class Certificate extends DataStructureImpl { throw new DataFormatException("Not enough bytes for the payload (read: " + read + " length: " + length + ')'); if (type == CERTIFICATE_TYPE_KEY) { if (length == 4) { + if (Arrays.equals(payload, KeyCertificate.X25519_Ed25519_PAYLOAD)) + return KeyCertificate.X25519_Ed25519_CERT; if (Arrays.equals(payload, KeyCertificate.Ed25519_PAYLOAD)) return KeyCertificate.ELG_Ed25519_CERT; if (Arrays.equals(payload, KeyCertificate.ECDSA256_PAYLOAD)) diff --git a/core/java/src/net/i2p/data/KeyCertificate.java b/core/java/src/net/i2p/data/KeyCertificate.java index 0aabb2c17f9c375e87b935ba44168888c13e1db5..c599d0f5433384f3a0c5d30b3f9b52a42799ddd9 100644 --- a/core/java/src/net/i2p/data/KeyCertificate.java +++ b/core/java/src/net/i2p/data/KeyCertificate.java @@ -20,16 +20,33 @@ public class KeyCertificate extends Certificate { public static final int HEADER_LENGTH = 4; - /** @since 0.9.22 pkg private for Certificate.create() */ + /** + * ElG + Ed25519 + * + * @since 0.9.22 pkg private for Certificate.create() + */ static final byte[] Ed25519_PAYLOAD = new byte[] { 0, (byte) (SigType.EdDSA_SHA512_Ed25519.getCode()), 0, 0 }; - /** @since 0.9.22 pkg private for Certificate.create() */ + /** + * ElG + P256 + * + * @since 0.9.22 pkg private for Certificate.create() + */ static final byte[] ECDSA256_PAYLOAD = new byte[] { 0, (byte) (SigType.ECDSA_SHA256_P256.getCode()), 0, 0 }; + /** + * X25519 + Ed25519 + * + * @since 0.9.54 + */ + static final byte[] X25519_Ed25519_PAYLOAD = new byte[] { + 0, (byte) (SigType.EdDSA_SHA512_Ed25519.getCode()), 0, (byte) (EncType.ECIES_X25519.getCode()) + }; + /** * An immutable ElG/ECDSA-P256 certificate. */ @@ -41,6 +58,12 @@ public class KeyCertificate extends Certificate { */ public static final KeyCertificate ELG_Ed25519_CERT; + /** + * An immutable X25519/Ed25519 certificate. + * @since 0.9.54 + */ + public static final KeyCertificate X25519_Ed25519_CERT; + static { KeyCertificate kc; try { @@ -55,6 +78,12 @@ public class KeyCertificate extends Certificate { throw new RuntimeException(dfe); // won't happen } ELG_Ed25519_CERT = kc; + try { + kc = new X25519_Ed25519Cert(); + } catch (DataFormatException dfe) { + throw new RuntimeException(dfe); // won't happen + } + X25519_Ed25519_CERT = kc; } /** @@ -401,4 +430,72 @@ public class KeyCertificate extends Certificate { return _hashcode; } } + + /** + * An immutable X25519/Ed25519 certificate. + * @since 0.9.54 + */ + private static final class X25519_Ed25519Cert extends KeyCertificate { + private static final byte[] ED_DATA = new byte[] { CERTIFICATE_TYPE_KEY, + 0, HEADER_LENGTH, + 0, (byte) SigType.EdDSA_SHA512_Ed25519.getCode(), + 0, (byte) EncType.ECIES_X25519.getCode() + }; + private static final int ED_LENGTH = ED_DATA.length; + private final int _hashcode; + + public X25519_Ed25519Cert() throws DataFormatException { + super(X25519_Ed25519_PAYLOAD); + _hashcode = super.hashCode(); + } + + /** @throws RuntimeException always */ + @Override + public void setCertificateType(int type) { + throw new RuntimeException("Data already set"); + } + + /** @throws RuntimeException always */ + @Override + public void setPayload(byte[] payload) { + throw new RuntimeException("Data already set"); + } + + /** @throws RuntimeException always */ + @Override + public void readBytes(InputStream in) throws DataFormatException, IOException { + throw new RuntimeException("Data already set"); + } + + /** Overridden for efficiency */ + @Override + public void writeBytes(OutputStream out) throws IOException { + out.write(ED_DATA); + } + + /** Overridden for efficiency */ + @Override + public int writeBytes(byte target[], int offset) { + System.arraycopy(ED_DATA, 0, target, offset, ED_LENGTH); + return ED_LENGTH; + } + + /** @throws RuntimeException always */ + @Override + public int readBytes(byte source[], int offset) throws DataFormatException { + throw new RuntimeException("Data already set"); + } + + /** Overridden for efficiency */ + @Override + public int size() { + return ED_LENGTH; + } + + /** Overridden for efficiency */ + @Override + public int hashCode() { + return _hashcode; + } + } }