mirror of
https://github.com/vanitasvitae/Smack.git
synced 2025-09-12 10:39:38 +02:00
Add support for XEP-XXXX: OMEMO Media Sharing
This commit is contained in:
parent
8a74aec32a
commit
9a635e7a25
11 changed files with 678 additions and 6 deletions
|
@ -21,16 +21,22 @@ import java.io.File;
|
|||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.CipherInputStream;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
@ -43,13 +49,14 @@ import org.jivesoftware.smack.SmackException;
|
|||
import org.jivesoftware.smack.XMPPConnection;
|
||||
import org.jivesoftware.smack.XMPPConnectionRegistry;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
|
||||
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
||||
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
|
||||
import org.jivesoftware.smackx.httpfileupload.UploadService.Version;
|
||||
import org.jivesoftware.smackx.httpfileupload.element.Slot;
|
||||
import org.jivesoftware.smackx.httpfileupload.element.SlotRequest;
|
||||
import org.jivesoftware.smackx.httpfileupload.element.SlotRequest_V0_2;
|
||||
import org.jivesoftware.smackx.omemo_media_sharing.AesgcmUrl;
|
||||
import org.jivesoftware.smackx.omemo_media_sharing.OmemoMediaSharingUtils;
|
||||
import org.jivesoftware.smackx.xdata.FormField;
|
||||
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||
|
||||
|
@ -57,10 +64,13 @@ import org.jxmpp.jid.DomainBareJid;
|
|||
|
||||
/**
|
||||
* A manager for XEP-0363: HTTP File Upload.
|
||||
* This manager is also capable of XEP-XXXX: OMEMO Media Sharing.
|
||||
*
|
||||
* @author Grigory Fedorov
|
||||
* @author Florian Schmaus
|
||||
* @author Paul Schaub
|
||||
* @see <a href="http://xmpp.org/extensions/xep-0363.html">XEP-0363: HTTP File Upload</a>
|
||||
* @see <a href="https://xmpp.org/extensions/inbox/omemo-media-sharing.html">XEP-XXXX: OMEMO Media Sharing</a>
|
||||
*/
|
||||
public final class HttpFileUploadManager extends Manager {
|
||||
|
||||
|
@ -245,7 +255,7 @@ public final class HttpFileUploadManager extends Manager {
|
|||
* Note that this is a synchronous call -- Smack must wait for the server response.
|
||||
*
|
||||
* @param file file to be uploaded
|
||||
* @param listener upload progress listener of null
|
||||
* @param listener upload progress listener or null
|
||||
* @return public URL for sharing uploaded file
|
||||
*
|
||||
* @throws InterruptedException
|
||||
|
@ -265,6 +275,74 @@ public final class HttpFileUploadManager extends Manager {
|
|||
return slot.getGetUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload a file encrypted using the scheme described in OMEMO Media Sharing.
|
||||
* The file is being encrypted using a random 256 bit AES key in Galois Counter Mode using a random 16 byte IV and
|
||||
* then uploaded to the server.
|
||||
* The URL that is returned has a modified scheme (aesgcm:// instead of https://) and has the IV and key attached
|
||||
* as ref part.
|
||||
*
|
||||
* Note: The URL contains the used key and IV in plain text. Keep in mind to only share this URL though a secured
|
||||
* channel (i.e. end-to-end encrypted message), as anybody who can read the URL can also decrypt the file.
|
||||
*
|
||||
* Note: This method uses a IV of length 16 instead of 12. Although not specified in the ProtoXEP, 16 byte IVs are
|
||||
* currently used by most implementations. This implementation also supports 12 byte IVs when decrypting.
|
||||
*
|
||||
* @param file file
|
||||
* @return AESGCM URL which contains the key and IV of the encrypted file.
|
||||
*
|
||||
* @see <a href="https://xmpp.org/extensions/inbox/omemo-media-sharing.html">XEP-XXXX: OMEMO Media Sharing</a>
|
||||
*/
|
||||
public AesgcmUrl uploadFileEncrypted(File file) throws InterruptedException, IOException,
|
||||
XMPPException.XMPPErrorException, SmackException, InvalidAlgorithmParameterException,
|
||||
NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException {
|
||||
return uploadFileEncrypted(file, null);
|
||||
}
|
||||
/**
|
||||
* Upload a file encrypted using the scheme described in OMEMO Media Sharing.
|
||||
* The file is being encrypted using a random 256 bit AES key in Galois Counter Mode using a random 16 byte IV and
|
||||
* then uploaded to the server.
|
||||
* The URL that is returned has a modified scheme (aesgcm:// instead of https://) and has the IV and key attached
|
||||
* as ref part.
|
||||
*
|
||||
* Note: The URL contains the used key and IV in plain text. Keep in mind to only share this URL though a secured
|
||||
* channel (i.e. end-to-end encrypted message), as anybody who can read the URL can also decrypt the file.
|
||||
*
|
||||
* Note: This method uses a IV of length 16 instead of 12. Although not specified in the ProtoXEP, 16 byte IVs are
|
||||
* currently used by most implementations. This implementation also supports 12 byte IVs when decrypting.
|
||||
*
|
||||
* @param file file
|
||||
* @param listener progress listener or null
|
||||
* @return AESGCM URL which contains the key and IV of the encrypted file.
|
||||
*
|
||||
* @see <a href="https://xmpp.org/extensions/inbox/omemo-media-sharing.html">XEP-XXXX: OMEMO Media Sharing</a>
|
||||
*/
|
||||
public AesgcmUrl uploadFileEncrypted(File file, UploadProgressListener listener) throws IOException,
|
||||
InterruptedException, XMPPException.XMPPErrorException, SmackException, NoSuchPaddingException,
|
||||
NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException {
|
||||
if (!file.isFile()) {
|
||||
throw new FileNotFoundException("The path " + file.getAbsolutePath() + " is not a file");
|
||||
}
|
||||
|
||||
// The encrypted file will contain an extra block with the AEAD MAC.
|
||||
long cipherFileLength = file.length() + 16;
|
||||
|
||||
final Slot slot = requestSlot(file.getName(), cipherFileLength, "application/octet-stream");
|
||||
URL slotUrl = slot.getGetUrl();
|
||||
|
||||
// fresh AES key + iv
|
||||
byte[] key = OmemoMediaSharingUtils.generateRandomKey();
|
||||
byte[] iv = OmemoMediaSharingUtils.generateRandomIV();
|
||||
Cipher cipher = OmemoMediaSharingUtils.encryptionCipherFrom(key, iv);
|
||||
|
||||
FileInputStream fis = new FileInputStream(file);
|
||||
// encrypt the file on the fly - encryption actually happens below in uploadFile()
|
||||
CipherInputStream cis = new CipherInputStream(fis, cipher);
|
||||
|
||||
uploadFile(cis, cipherFileLength, slot, listener);
|
||||
|
||||
return new AesgcmUrl(slotUrl, key, iv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Request a new upload slot from default upload service (if discovered). When you get slot you should upload file
|
||||
|
@ -391,10 +469,13 @@ public final class HttpFileUploadManager extends Manager {
|
|||
if (fileSize >= Integer.MAX_VALUE) {
|
||||
throw new IllegalArgumentException("File size " + fileSize + " must be less than " + Integer.MAX_VALUE);
|
||||
}
|
||||
final int fileSizeInt = (int) fileSize;
|
||||
|
||||
// Construct the FileInputStream first to make sure we can actually read the file.
|
||||
final FileInputStream fis = new FileInputStream(file);
|
||||
uploadFile(fis, fileSize, slot, listener);
|
||||
}
|
||||
|
||||
private void uploadFile(final InputStream fis, long fileSize, final Slot slot, UploadProgressListener listener) throws IOException {
|
||||
|
||||
final URL putUrl = slot.getPutUrl();
|
||||
|
||||
|
@ -404,7 +485,7 @@ public final class HttpFileUploadManager extends Manager {
|
|||
urlConnection.setUseCaches(false);
|
||||
urlConnection.setDoOutput(true);
|
||||
// TODO Change to using fileSize once Smack's minimum Android API level is 19 or higher.
|
||||
urlConnection.setFixedLengthStreamingMode(fileSizeInt);
|
||||
urlConnection.setFixedLengthStreamingMode((int) fileSize);
|
||||
urlConnection.setRequestProperty("Content-Type", "application/octet-stream;");
|
||||
for (Entry<String, String> header : slot.getHeaders().entrySet()) {
|
||||
urlConnection.setRequestProperty(header.getKey(), header.getValue());
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
/**
|
||||
*
|
||||
* Copyright © 2019 Paul Schaub
|
||||
*
|
||||
* 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 org.jivesoftware.smackx.omemo_media_sharing;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
|
||||
import org.jivesoftware.smack.util.Objects;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
import org.jivesoftware.smackx.httpfileupload.element.Slot;
|
||||
|
||||
/**
|
||||
* This class represents a aesgcm URL as described in XEP-XXXX: OMEMO Media Sharing.
|
||||
* As the builtin {@link URL} class cannot handle the aesgcm protocol identifier, this class
|
||||
* is used as a utility class that bundles together a {@link URL}, key and IV.
|
||||
*
|
||||
* @see <a href="https://xmpp.org/extensions/inbox/omemo-media-sharing.html">XEP-XXXX: OMEMO Media Sharing</a>
|
||||
*/
|
||||
public class AesgcmUrl {
|
||||
|
||||
public static final String PROTOCOL = "aesgcm";
|
||||
|
||||
private final URL httpsUrl;
|
||||
private final byte[] keyBytes;
|
||||
private final byte[] ivBytes;
|
||||
|
||||
/**
|
||||
* Private constructor that constructs the {@link AesgcmUrl} from a normal https {@link URL}, a key and iv.
|
||||
*
|
||||
* @param httpsUrl normal https url as given by the {@link Slot}.
|
||||
* @param key byte array of an encoded 256 bit aes key
|
||||
* @param iv 16 or 12 byte initialization vector
|
||||
*/
|
||||
public AesgcmUrl(URL httpsUrl, byte[] key, byte[] iv) {
|
||||
this.httpsUrl = Objects.requireNonNull(httpsUrl);
|
||||
this.keyBytes = Objects.requireNonNull(key);
|
||||
this.ivBytes = Objects.requireNonNull(iv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a {@link AesgcmUrl} from a {@link String}.
|
||||
* The parsed object will provide a normal {@link URL} under which the offered file can be downloaded,
|
||||
* as well as a {@link Cipher} that can be used to decrypt it.
|
||||
*
|
||||
* @param aesgcmUrlString aesgcm URL as a {@link String}
|
||||
*/
|
||||
public AesgcmUrl(String aesgcmUrlString) {
|
||||
if (!aesgcmUrlString.startsWith(PROTOCOL)) {
|
||||
throw new IllegalArgumentException("Provided String does not resemble a aesgcm URL.");
|
||||
}
|
||||
|
||||
// Convert aesgcm Url to https URL
|
||||
this.httpsUrl = extractHttpsUrl(aesgcmUrlString);
|
||||
|
||||
// Extract IV and Key
|
||||
byte[][] ivAndKey = extractIVAndKey(aesgcmUrlString);
|
||||
this.ivBytes = ivAndKey[0];
|
||||
this.keyBytes = ivAndKey[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a https {@link URL} under which the file can be downloaded.
|
||||
*
|
||||
* @return https URL
|
||||
*/
|
||||
public URL getDownloadUrl() {
|
||||
return httpsUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link String} representation of this aesgcm URL.
|
||||
*
|
||||
* @return aesgcm URL with key and IV.
|
||||
*/
|
||||
public String getAesgcmUrl() {
|
||||
String aesgcmUrl = httpsUrl.toString().replaceFirst(httpsUrl.getProtocol(), PROTOCOL);
|
||||
return aesgcmUrl + "#" + StringUtils.encodeHex(ivBytes) + StringUtils.encodeHex(keyBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Cipher} in decryption mode, which can be used to decrypt the offered file.
|
||||
*
|
||||
* @return cipher
|
||||
*
|
||||
* @throws NoSuchPaddingException if the JVM cannot provide the specified cipher mode
|
||||
* @throws NoSuchAlgorithmException if the JVM cannot provide the specified cipher mode
|
||||
* @throws InvalidAlgorithmParameterException if the JVM cannot provide the specified cipher
|
||||
* (eg. if no BC provider is added)
|
||||
* @throws InvalidKeyException if the provided key is invalid
|
||||
*/
|
||||
public Cipher getDecryptionCipher() throws NoSuchPaddingException, NoSuchAlgorithmException,
|
||||
InvalidAlgorithmParameterException, InvalidKeyException {
|
||||
return OmemoMediaSharingUtils.decryptionCipherFrom(keyBytes, ivBytes);
|
||||
}
|
||||
|
||||
private static URL extractHttpsUrl(String aesgcmUrlString) {
|
||||
// aesgcm -> https
|
||||
String httpsUrlString = aesgcmUrlString.replaceFirst(PROTOCOL, "https");
|
||||
// remove #ref
|
||||
httpsUrlString = httpsUrlString.substring(0, httpsUrlString.indexOf("#"));
|
||||
|
||||
try {
|
||||
return new URL(httpsUrlString);
|
||||
} catch (MalformedURLException e) {
|
||||
throw new AssertionError("Failed to convert aesgcm URL to https URL: '" + aesgcmUrlString + "'", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[][] extractIVAndKey(String aesgcmUrlString) {
|
||||
int startOfRef = aesgcmUrlString.lastIndexOf("#");
|
||||
if (startOfRef == -1) {
|
||||
throw new IllegalArgumentException("The provided aesgcm Url does not have a ref part which is " +
|
||||
"supposed to contain the encryption key for file encryption.");
|
||||
}
|
||||
|
||||
String ref = aesgcmUrlString.substring(startOfRef + 1);
|
||||
byte[] refBytes = StringUtils.hexStringToByteArray(ref);
|
||||
|
||||
byte[] key = new byte[32];
|
||||
byte[] iv;
|
||||
int ivLen;
|
||||
// determine the length of the initialization vector part
|
||||
switch (refBytes.length) {
|
||||
// 32 bytes key + 16 bytes IV
|
||||
case 48:
|
||||
ivLen = 16;
|
||||
break;
|
||||
|
||||
// 32 bytes key + 12 bytes IV
|
||||
case 44:
|
||||
ivLen = 12;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Provided URL has an invalid ref tag (" + ref.length() + "): '" + ref + "'");
|
||||
}
|
||||
iv = new byte[ivLen];
|
||||
System.arraycopy(refBytes, 0, iv, 0, ivLen);
|
||||
System.arraycopy(refBytes, ivLen, key, 0, 32);
|
||||
|
||||
return new byte[][] {iv, key};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
/**
|
||||
*
|
||||
* Copyright © 2019 Paul Schaub
|
||||
*
|
||||
* 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 org.jivesoftware.smackx.omemo_media_sharing;
|
||||
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import org.jivesoftware.smack.util.RandomUtils;
|
||||
|
||||
/**
|
||||
* Utility code for XEP-XXXX: OMEMO Media Sharing.
|
||||
*
|
||||
* @see <a href="https://xmpp.org/extensions/inbox/omemo-media-sharing.html">XEP-XXXX: OMEMO Media Sharing</a>
|
||||
*/
|
||||
public class OmemoMediaSharingUtils {
|
||||
|
||||
private static final String KEYTYPE = "AES";
|
||||
private static final String CIPHERMODE = "AES/GCM/NoPadding";
|
||||
// 256 bit = 32 byte
|
||||
private static final int LEN_KEY = 32;
|
||||
private static final int LEN_KEY_BITS = LEN_KEY * 8;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final int LEN_IV_12 = 12;
|
||||
private static final int LEN_IV_16 = 16;
|
||||
// Note: Contrary to what the ProtoXEP states, 16 byte IV length is used in the wild instead of 12.
|
||||
// At some point we should switch to 12 bytes though.
|
||||
private static final int LEN_IV = LEN_IV_16;
|
||||
|
||||
public static byte[] generateRandomIV() {
|
||||
return generateRandomIV(LEN_IV);
|
||||
}
|
||||
|
||||
public static byte[] generateRandomIV(int len) {
|
||||
return RandomUtils.secureRandomBytes(len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random 256 bit AES key.
|
||||
*
|
||||
* @return encoded AES key
|
||||
* @throws NoSuchAlgorithmException if the JVM doesn't provide the given key type.
|
||||
*/
|
||||
public static byte[] generateRandomKey() throws NoSuchAlgorithmException {
|
||||
KeyGenerator generator = KeyGenerator.getInstance(KEYTYPE);
|
||||
generator.init(LEN_KEY_BITS);
|
||||
return generator.generateKey().getEncoded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link Cipher} from a given key and iv which is in encryption mode.
|
||||
*
|
||||
* @param key aes encryption key
|
||||
* @param iv initialization vector
|
||||
*
|
||||
* @return cipher in encryption mode
|
||||
*
|
||||
* @throws NoSuchPaddingException if the JVM doesn't provide the padding specified in the ciphermode.
|
||||
* @throws NoSuchAlgorithmException if the JVM doesn't provide the encryption method specified in the ciphermode.
|
||||
* @throws InvalidAlgorithmParameterException if the cipher cannot be initiated.
|
||||
* @throws InvalidKeyException if the key is invalid.
|
||||
*/
|
||||
public static Cipher encryptionCipherFrom(byte[] key, byte[] iv)
|
||||
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException,
|
||||
InvalidKeyException {
|
||||
SecretKey secretKey = new SecretKeySpec(key, KEYTYPE);
|
||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
||||
Cipher cipher = Cipher.getInstance(CIPHERMODE);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
|
||||
return cipher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link Cipher} from a given key and iv which is in decryption mode.
|
||||
*
|
||||
* @param key aes encryption key
|
||||
* @param iv initialization vector
|
||||
*
|
||||
* @return cipher in decryption mode
|
||||
*
|
||||
* @throws NoSuchPaddingException if the JVM doesn't provide the padding specified in the ciphermode.
|
||||
* @throws NoSuchAlgorithmException if the JVM doesn't provide the encryption method specified in the ciphermode.
|
||||
* @throws InvalidAlgorithmParameterException if the cipher cannot be initiated.
|
||||
* @throws InvalidKeyException if the key is invalid.
|
||||
*/
|
||||
public static Cipher decryptionCipherFrom(byte[] key, byte[] iv)
|
||||
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException,
|
||||
InvalidKeyException {
|
||||
SecretKey secretKey = new SecretKeySpec(key, KEYTYPE);
|
||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
||||
Cipher cipher = Cipher.getInstance(CIPHERMODE);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
|
||||
return cipher;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
*
|
||||
* Copyright © 2017 Grigory Fedorov
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Smack's API for XEP-XXXX: OMEMO Media Sharing.
|
||||
*
|
||||
* @author Paul Schaub
|
||||
* @see <a href="https://xmpp.org/extensions/inbox/omemo-media-sharing.html">XEP-XXXX: OMEMO Media Sharing</a>
|
||||
*/
|
||||
package org.jivesoftware.smackx.omemo_media_sharing;
|
|
@ -0,0 +1,71 @@
|
|||
/**
|
||||
*
|
||||
* Copyright © 2019 Paul Schaub
|
||||
*
|
||||
* 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 org.jivesoftware.smackx.omemo_media_sharing;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
||||
public class OmemoMediaSharingUtilsTest {
|
||||
|
||||
private static final String iv_12 = "8c3d050e9386ec173861778f";
|
||||
private static final String iv_16 = "1ad857dcbb119e2642e4f8f7c137819e";
|
||||
private static final String key = "4f15af8f1a28100d0101fb1c2e119b0c18c34396c68ad379f5912ee21dca6b0b";
|
||||
private static final String key_iv_12 = iv_12 + key;
|
||||
private static final String key_iv_16 = iv_16 + key;
|
||||
|
||||
private static final String file = "download.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/tr%C3%A8s%20cool.jpg";
|
||||
private static final String file_https = "https://" + file;
|
||||
private static final String file_aesgcm_12 = "aesgcm://" + file + "#" + key_iv_12;
|
||||
private static final String file_aesgcm_16 = "aesgcm://" + file + "#" + key_iv_16;
|
||||
|
||||
@Test
|
||||
public void test12byteIvVariant() throws MalformedURLException {
|
||||
AesgcmUrl aesgcm = new AesgcmUrl(file_aesgcm_12);
|
||||
|
||||
// Make sure, that parsed aesgcm url still equals input string
|
||||
assertEquals(file_aesgcm_12, aesgcm.getAesgcmUrl());
|
||||
assertEquals(file_https, aesgcm.getDownloadUrl().toString());
|
||||
|
||||
URL url = new URL(file_https);
|
||||
aesgcm = new AesgcmUrl(url, StringUtils.hexStringToByteArray(key),
|
||||
StringUtils.hexStringToByteArray(iv_12));
|
||||
assertEquals(file_aesgcm_12, aesgcm.getAesgcmUrl());
|
||||
assertEquals(file_https, aesgcm.getDownloadUrl().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test16byteIvVariant() throws MalformedURLException {
|
||||
AesgcmUrl aesgcm = new AesgcmUrl(file_aesgcm_16);
|
||||
|
||||
// Make sure, that parsed aesgcm url still equals input string
|
||||
assertEquals(file_aesgcm_16, aesgcm.getAesgcmUrl());
|
||||
assertEquals(file_https, aesgcm.getDownloadUrl().toString());
|
||||
|
||||
URL url = new URL(file_https);
|
||||
aesgcm = new AesgcmUrl(url, StringUtils.hexStringToByteArray(key),
|
||||
StringUtils.hexStringToByteArray(iv_16));
|
||||
assertEquals(file_aesgcm_16, aesgcm.getAesgcmUrl());
|
||||
assertEquals(file_https, aesgcm.getDownloadUrl().toString());
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue