1717import net .i2p .crypto .eddsa .Utils ;
1818import net .i2p .crypto .eddsa .spec .EdDSAPublicKeySpec ;
1919
20+ /**
21+ * A wrapper class which makes it easy to use Ed25519 public keys.
22+ * All verify methods are using the {@link HashCondenser} for efficiency.
23+ * Obtain an instance by loading a key file or using {@link Ed25519PrivateKey#derivePublicKey()}
24+ *
25+ * @author DevCybran
26+ *
27+ */
2028public class Ed25519PublicKey {
2129 private final EdDSAPublicKey key ;
2230
31+ /**
32+ * Loads a private key from the specified file.
33+ *
34+ * @see #loadFromString(String)
35+ * @param publicKeyFile the file to read the private key from
36+ * @return the read private key
37+ * @throws IOException if an IO error occurs while reading the file
38+ * @throws NoSuchAlgorithmException if SHA-512 is not present on this machine
39+ * @throws InvalidKeyException if the key file has an invalid format
40+ */
2341 public static Ed25519PublicKey loadFromFile (File publicKeyFile ) throws IOException , NoSuchAlgorithmException , InvalidKeyException {
2442 String key = new String (Files .readAllBytes (publicKeyFile .toPath ()), StandardCharsets .UTF_8 );
2543 return loadFromString (key );
2644 }
2745
46+ /**
47+ * Decodes a public key from the specified string.
48+ *
49+ * @param publicKeyString A hexadecimal encoded representation of the key, generated by {@link #saveAsString()}
50+ * @return the decoded public key
51+ * @throws InvalidKeyException if the passed publicKeyString has an invalid format
52+ * @throws NoSuchAlgorithmException if SHA-512 is not present on this machine
53+ */
2854 public static Ed25519PublicKey loadFromString (String publicKeyString ) throws InvalidKeyException , NoSuchAlgorithmException {
2955 if (publicKeyString .length () != (256 +512 )/8 *2 ) throw new InvalidKeyException ("the supplied key is not a valid public key" ); // key + hash
3056 byte [] publicKey , publicKeyHashStored , publicKeyHash ;
@@ -43,11 +69,25 @@ public static Ed25519PublicKey loadFromString(String publicKeyString) throws Inv
4369 this .key = key ;
4470 }
4571
72+ /**
73+ * Encodes and saves this key to the specified file.
74+ *
75+ * @see #saveAsString()
76+ * @param publicKeyFile the file to save the public key to
77+ * @throws IOException if an IO error occurs when writing the file
78+ * @throws NoSuchAlgorithmException if SHA-512 is not present on this machine
79+ */
4680 public void saveAsFile (File publicKeyFile ) throws IOException , NoSuchAlgorithmException {
4781 String key = this .saveAsString ();
4882 Files .write (publicKeyFile .toPath (), key .getBytes (StandardCharsets .UTF_8 ));
4983 }
5084
85+ /**
86+ * Encodes this key as a hexadecimal String.
87+ *
88+ * @return a hexadecimal encoded representation of this public key
89+ * @throws NoSuchAlgorithmException if SHA-512 is not present on this machine
90+ */
5191 public String saveAsString () throws NoSuchAlgorithmException {
5292 byte [] publicKey = this .key .getAbyte ();
5393 if (publicKey .length !=256 /8 ) throw new RuntimeException (new InvalidKeyException ("unexpected public key length" ));
@@ -72,18 +112,63 @@ private boolean verifyLow(byte[] data, String signature) throws SignatureExcepti
72112 }
73113 }
74114
115+ /**
116+ * Verifies the signature of the given byte array.
117+ * {@link HashCondenser#compute(byte[])} with default settings is preprocessing the array before verifying its signature.
118+ *
119+ * @param data
120+ * @param signature a hexadecimal representation of the signature (generated by {@link Ed25519PrivateKey#sign(byte[])}
121+ * @return true if the signature is correct. False otherwise.
122+ * @throws NoSuchAlgorithmException if the hash algorithm is not present on this machine
123+ * @throws SignatureException if the given signature has an invalid format
124+ */
75125 public boolean verify (byte [] data , String signature ) throws NoSuchAlgorithmException , SignatureException {
76126 return this .verifyLow (HashCondenser .getInstance ().compute (data ), signature );
77127 }
78128
129+ /**
130+ * Verifies the signature of the given String by converting it to bytes using the UTF-8 charset.
131+ *
132+ * @see #verify(byte[], String)
133+ * @param data
134+ * @param signature a hexadecimal representation of the signature (generated by {@link Ed25519PrivateKey#sign(String)}
135+ * @return true if the signature is correct. False otherwise.
136+ * @throws NoSuchAlgorithmException if the hash algorithm is not present on this machine
137+ * @throws SignatureException if the given signature has an invalid format
138+ */
79139 public boolean verify (String data , String signature ) throws NoSuchAlgorithmException , SignatureException {
80140 return this .verify (data .getBytes (StandardCharsets .UTF_8 ), signature );
81141 }
82142
143+ /**
144+ * Verifies that the signature for all data which can be read from the given InputStream is correct.
145+ * {@link HashCondenser#compute(byte[])} with default settings is preprocessing the stream before verifying its signature, which will allow processing huge files.
146+ *
147+ * @param source the data source
148+ * @param sourceSize the exact size of all data which will pass through the InputStream
149+ * @param signature a hexadecimal representation of the signature (generated by {@link Ed25519PrivateKey#sign(InputStream, long)}
150+ * @return true if the signature is correct. False otherwise.
151+ * @throws IllegalArgumentException if sourceSize is not the correct size
152+ * @throws NoSuchAlgorithmException if the hash algorithm is not present on this machine
153+ * @throws IOException if an IO error occurs while reading the stream
154+ * @throws SignatureException if the given signature has an invalid format
155+ */
83156 public boolean verify (InputStream source , long sourceSize , String signature ) throws IllegalArgumentException , NoSuchAlgorithmException , IOException , SignatureException {
84157 return this .verifyLow (HashCondenser .getInstance ().compute (source , sourceSize ), signature );
85158 }
86159
160+ /**
161+ * Verifies the signature of the content of the given file.
162+ *
163+ * @see #verify(InputStream, long, String)
164+ * @param source
165+ * @param signature a hexadecimal representation of the signature (generated by {@link Ed25519PrivateKey#sign(File)}
166+ * @return true if the signature is correct. False otherwise.
167+ * @throws IOException if an IO error occurs while reading the file
168+ * @throws IllegalArgumentException if the file's size changes during computation
169+ * @throws NoSuchAlgorithmException if the hash algorithm is not present on this machine
170+ * @throws SignatureException if the given signature has an invalid format
171+ */
87172 public boolean verify (File source , String signature ) throws IOException , IllegalArgumentException , NoSuchAlgorithmException , SignatureException {
88173 if (!source .isFile ()) throw new FileNotFoundException (source .getAbsolutePath ());
89174 long fileSize = source .length ();
@@ -92,10 +177,34 @@ public boolean verify(File source, String signature) throws IOException, Illegal
92177 }
93178 }
94179
180+ /**
181+ * Verifies the content of the given file against its signature, stored in a file of the same name which has a ".sig" extension appended
182+ * Example: If you verify "MyFile.dat", the signature will be read from "MyFile.dat.sig"
183+ *
184+ * @see #verifyFromFile(File, File)
185+ * @param source
186+ * @return true if the signature is correct. False otherwise.
187+ * @throws IOException if an IO error occurs while reading a file
188+ * @throws IllegalArgumentException if the file's size changes during computation
189+ * @throws NoSuchAlgorithmException if the hash algorithm is not present on this machine
190+ * @throws SignatureException if the signature in the .sig file has an invalid format
191+ */
95192 public boolean verifyFromFile (File source ) throws IOException , IllegalArgumentException , NoSuchAlgorithmException , SignatureException {
96193 return this .verifyFromFile (source , new File (source , ".sig" ));
97194 }
98195
196+ /**
197+ * Verifies the content of the given source file against the signature in the given signature file.
198+ *
199+ * @see #verify(File, String)
200+ * @param source
201+ * @param signatureFile the file to write the signature to
202+ * @return true if the signature is correct. False otherwise.
203+ * @throws IOException if an IO error occurs while reading a file
204+ * @throws IllegalArgumentException if the file's size changes during computation
205+ * @throws NoSuchAlgorithmException if the hash algorithm is not present on this machine
206+ * @throws SignatureException if the signature in the signature file has an invalid format
207+ */
99208 public boolean verifyFromFile (File source , File signatureFile ) throws IOException , IllegalArgumentException , NoSuchAlgorithmException , SignatureException {
100209 String signature = new String (Files .readAllBytes (signatureFile .toPath ()), StandardCharsets .UTF_8 );
101210 return this .verify (source , signature );
0 commit comments