Updates scrypt lib to support empty keys.

pull/775/head
Vitor Pamplona 2024-02-15 17:42:19 -05:00
rodzic 6553f3bec5
commit 52c6ce4849
6 zmienionych plików z 507 dodań i 4 usunięć

Wyświetl plik

@ -59,9 +59,6 @@ dependencies {
// immutable collections to avoid recomposition
api('org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7')
// scrypt for NIP-49
api('com.lambdaworks:scrypt:1.4.0')
// Parses URLs from Text:
api "io.github.url-detector:url-detector:0.1.23"

Wyświetl plik

@ -46,6 +46,10 @@ public class NIP49Test {
Nip49TestCase(".ksjabdk.aselqwe", "14c226dbdd865d5e1645e72c7470fd0a17feb42cc87b750bab6538171b3a3f8a", 7, 0x00),
Nip49TestCase("skjdaklrnçurbç l", "f7f2f77f98890885462764afb15b68eb5f69979c8046ecb08cad7c4ae6b221ab", 8, 0x01),
Nip49TestCase("777z7z7z7z7z7z7z", "11b25a101667dd9208db93c0827c6bdad66729a5b521156a7e9d3b22b3ae8944", 9, 0x02),
Nip49TestCase("", "f7f2f77f98890885462764afb15b68eb5f69979c8046ecb08cad7c4ae6b221ab", 4, 0x00),
Nip49TestCase("", "11b25a101667dd9208db93c0827c6bdad66729a5b521156a7e9d3b22b3ae8944", 5, 0x01),
Nip49TestCase("", "f7f2f77f98890885462764afb15b68eb5f69979c8046ecb08cad7c4ae6b221ab", 1, 0x00),
Nip49TestCase("", "11b25a101667dd9208db93c0827c6bdad66729a5b521156a7e9d3b22b3ae8944", 9, 0x01),
)
}

Wyświetl plik

@ -23,7 +23,6 @@ package com.vitorpamplona.quartz.crypto
import android.util.Log
import com.goterl.lazysodium.LazySodiumAndroid
import com.goterl.lazysodium.SodiumAndroid
import com.lambdaworks.crypto.SCrypt
import com.vitorpamplona.quartz.encoders.Bech32
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.encoders.bechToBytes

Wyświetl plik

@ -0,0 +1,87 @@
// Copyright (C) 2011 - Will Glozer. All rights reserved.
package com.vitorpamplona.quartz.crypto;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.GeneralSecurityException;
import static java.lang.System.arraycopy;
/**
* An implementation of the Password-Based Key Derivation Function as specified
* in RFC 2898.
*
* @author Will Glozer
*/
public class PBKDF {
/**
* Implementation of PBKDF2 (RFC2898).
*
* @param alg HMAC algorithm to use.
* @param P Password.
* @param S Salt.
* @param c Iteration count.
* @param dkLen Intended length, in octets, of the derived key.
*
* @return The derived key.
*
* @throws GeneralSecurityException
*/
public static byte[] pbkdf2(String alg, byte[] P, byte[] S, int c, int dkLen) throws GeneralSecurityException {
Mac mac = Mac.getInstance(alg);
mac.init(new SecretKeySpec(P, alg));
byte[] DK = new byte[dkLen];
pbkdf2(mac, S, c, DK, dkLen);
return DK;
}
/**
* Implementation of PBKDF2 (RFC2898).
*
* @param mac Pre-initialized {@link Mac} instance to use.
* @param S Salt.
* @param c Iteration count.
* @param DK Byte array that derived key will be placed in.
* @param dkLen Intended length, in octets, of the derived key.
*
* @throws GeneralSecurityException
*/
public static void pbkdf2(Mac mac, byte[] S, int c, byte[] DK, int dkLen) throws GeneralSecurityException {
int hLen = mac.getMacLength();
if (dkLen > (Math.pow(2, 32) - 1) * hLen) {
throw new GeneralSecurityException("Requested key length too long");
}
byte[] U = new byte[hLen];
byte[] T = new byte[hLen];
byte[] block1 = new byte[S.length + 4];
int l = (int) Math.ceil((double) dkLen / hLen);
int r = dkLen - (l - 1) * hLen;
arraycopy(S, 0, block1, 0, S.length);
for (int i = 1; i <= l; i++) {
block1[S.length + 0] = (byte) (i >> 24 & 0xff);
block1[S.length + 1] = (byte) (i >> 16 & 0xff);
block1[S.length + 2] = (byte) (i >> 8 & 0xff);
block1[S.length + 3] = (byte) (i >> 0 & 0xff);
mac.update(block1);
mac.doFinal(U, 0);
arraycopy(U, 0, T, 0, hLen);
for (int j = 1; j < c; j++) {
mac.update(U);
mac.doFinal(U, 0);
for (int k = 0; k < hLen; k++) {
T[k] ^= U[k];
}
}
arraycopy(T, 0, DK, (i - 1) * hLen, (i == l ? r : hLen));
}
}
}

Wyświetl plik

@ -0,0 +1,188 @@
// Copyright (C) 2011 - Will Glozer. All rights reserved.
package com.vitorpamplona.quartz.crypto;
import javax.crypto.Mac;
import java.security.GeneralSecurityException;
import static java.lang.Integer.MAX_VALUE;
import static java.lang.System.arraycopy;
/**
* An implementation of the <a href="http://www.tarsnap.com/scrypt/scrypt.pdf"/>scrypt</a>
* key derivation function. This class will attempt to load a native library
* containing the optimized C implementation from
* <a href="http://www.tarsnap.com/scrypt.html">http://www.tarsnap.com/scrypt.html<a> and
* fall back to the pure Java version if that fails.
*
* @author Will Glozer
*/
public class SCrypt {
/**
* Implementation of the <a href="http://www.tarsnap.com/scrypt/scrypt.pdf"/>scrypt KDF</a>.
*
* @param passwd Password.
* @param salt Salt.
* @param N CPU cost parameter.
* @param r Memory cost parameter.
* @param p Parallelization parameter.
* @param dkLen Intended length of the derived key.
*
* @return The derived key.
*
* @throws GeneralSecurityException when HMAC_SHA256 is not available.
*/
public static byte[] scrypt(byte[] passwd, byte[] salt, int N, int r, int p, int dkLen) throws GeneralSecurityException {
return scryptJ(passwd, salt, N, r, p, dkLen);
}
/**
* Pure Java implementation of the <a href="http://www.tarsnap.com/scrypt/scrypt.pdf"/>scrypt KDF</a>.
*
* @param passwd Password.
* @param salt Salt.
* @param N CPU cost parameter.
* @param r Memory cost parameter.
* @param p Parallelization parameter.
* @param dkLen Intended length of the derived key.
*
* @return The derived key.
*
* @throws GeneralSecurityException when HMAC_SHA256 is not available.
*/
public static byte[] scryptJ(byte[] passwd, byte[] salt, int N, int r, int p, int dkLen) throws GeneralSecurityException {
if (N < 2 || (N & (N - 1)) != 0) throw new IllegalArgumentException("N must be a power of 2 greater than 1");
if (N > MAX_VALUE / 128 / r) throw new IllegalArgumentException("Parameter N is too large");
if (r > MAX_VALUE / 128 / p) throw new IllegalArgumentException("Parameter r is too large");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeyOrEmptySpec(passwd, "HmacSHA256"));
byte[] DK = new byte[dkLen];
byte[] B = new byte[128 * r * p];
byte[] XY = new byte[256 * r];
byte[] V = new byte[128 * r * N];
int i;
PBKDF.pbkdf2(mac, salt, 1, B, p * 128 * r);
for (i = 0; i < p; i++) {
smix(B, i * 128 * r, r, N, V, XY);
}
PBKDF.pbkdf2(mac, B, 1, DK, dkLen);
return DK;
}
public static void smix(byte[] B, int Bi, int r, int N, byte[] V, byte[] XY) {
int Xi = 0;
int Yi = 128 * r;
int i;
arraycopy(B, Bi, XY, Xi, 128 * r);
for (i = 0; i < N; i++) {
arraycopy(XY, Xi, V, i * (128 * r), 128 * r);
blockmix_salsa8(XY, Xi, Yi, r);
}
for (i = 0; i < N; i++) {
int j = integerify(XY, Xi, r) & (N - 1);
blockxor(V, j * (128 * r), XY, Xi, 128 * r);
blockmix_salsa8(XY, Xi, Yi, r);
}
arraycopy(XY, Xi, B, Bi, 128 * r);
}
public static void blockmix_salsa8(byte[] BY, int Bi, int Yi, int r) {
byte[] X = new byte[64];
int i;
arraycopy(BY, Bi + (2 * r - 1) * 64, X, 0, 64);
for (i = 0; i < 2 * r; i++) {
blockxor(BY, i * 64, X, 0, 64);
salsa20_8(X);
arraycopy(X, 0, BY, Yi + (i * 64), 64);
}
for (i = 0; i < r; i++) {
arraycopy(BY, Yi + (i * 2) * 64, BY, Bi + (i * 64), 64);
}
for (i = 0; i < r; i++) {
arraycopy(BY, Yi + (i * 2 + 1) * 64, BY, Bi + (i + r) * 64, 64);
}
}
public static int R(int a, int b) {
return (a << b) | (a >>> (32 - b));
}
public static void salsa20_8(byte[] B) {
int[] B32 = new int[16];
int[] x = new int[16];
int i;
for (i = 0; i < 16; i++) {
B32[i] = (B[i * 4 + 0] & 0xff) << 0;
B32[i] |= (B[i * 4 + 1] & 0xff) << 8;
B32[i] |= (B[i * 4 + 2] & 0xff) << 16;
B32[i] |= (B[i * 4 + 3] & 0xff) << 24;
}
arraycopy(B32, 0, x, 0, 16);
for (i = 8; i > 0; i -= 2) {
x[ 4] ^= R(x[ 0]+x[12], 7); x[ 8] ^= R(x[ 4]+x[ 0], 9);
x[12] ^= R(x[ 8]+x[ 4],13); x[ 0] ^= R(x[12]+x[ 8],18);
x[ 9] ^= R(x[ 5]+x[ 1], 7); x[13] ^= R(x[ 9]+x[ 5], 9);
x[ 1] ^= R(x[13]+x[ 9],13); x[ 5] ^= R(x[ 1]+x[13],18);
x[14] ^= R(x[10]+x[ 6], 7); x[ 2] ^= R(x[14]+x[10], 9);
x[ 6] ^= R(x[ 2]+x[14],13); x[10] ^= R(x[ 6]+x[ 2],18);
x[ 3] ^= R(x[15]+x[11], 7); x[ 7] ^= R(x[ 3]+x[15], 9);
x[11] ^= R(x[ 7]+x[ 3],13); x[15] ^= R(x[11]+x[ 7],18);
x[ 1] ^= R(x[ 0]+x[ 3], 7); x[ 2] ^= R(x[ 1]+x[ 0], 9);
x[ 3] ^= R(x[ 2]+x[ 1],13); x[ 0] ^= R(x[ 3]+x[ 2],18);
x[ 6] ^= R(x[ 5]+x[ 4], 7); x[ 7] ^= R(x[ 6]+x[ 5], 9);
x[ 4] ^= R(x[ 7]+x[ 6],13); x[ 5] ^= R(x[ 4]+x[ 7],18);
x[11] ^= R(x[10]+x[ 9], 7); x[ 8] ^= R(x[11]+x[10], 9);
x[ 9] ^= R(x[ 8]+x[11],13); x[10] ^= R(x[ 9]+x[ 8],18);
x[12] ^= R(x[15]+x[14], 7); x[13] ^= R(x[12]+x[15], 9);
x[14] ^= R(x[13]+x[12],13); x[15] ^= R(x[14]+x[13],18);
}
for (i = 0; i < 16; ++i) B32[i] = x[i] + B32[i];
for (i = 0; i < 16; i++) {
B[i * 4 + 0] = (byte) (B32[i] >> 0 & 0xff);
B[i * 4 + 1] = (byte) (B32[i] >> 8 & 0xff);
B[i * 4 + 2] = (byte) (B32[i] >> 16 & 0xff);
B[i * 4 + 3] = (byte) (B32[i] >> 24 & 0xff);
}
}
public static void blockxor(byte[] S, int Si, byte[] D, int Di, int len) {
for (int i = 0; i < len; i++) {
D[Di + i] ^= S[Si + i];
}
}
public static int integerify(byte[] B, int Bi, int r) {
int n;
Bi += (2 * r - 1) * 64;
n = (B[Bi + 0] & 0xff) << 0;
n |= (B[Bi + 1] & 0xff) << 8;
n |= (B[Bi + 2] & 0xff) << 16;
n |= (B[Bi + 3] & 0xff) << 24;
return n;
}
}

Wyświetl plik

@ -0,0 +1,228 @@
/*
* Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.vitorpamplona.quartz.crypto;
import java.security.MessageDigest;
import java.security.spec.KeySpec;
import java.util.Locale;
import javax.crypto.SecretKey;
/**
* This class specifies a secret key in a provider-independent fashion.
*
* <p>It can be used to construct a <code>SecretKey</code> from a byte array,
* without having to go through a (provider-based)
* <code>SecretKeyFactory</code>.
*
* <p>This class is only useful for raw secret keys that can be represented as
* a byte array and have no key parameters associated with them, e.g., DES or
* Triple DES keys.
*
* @author Jan Luehe
*
* @see javax.crypto.SecretKey
* @see javax.crypto.SecretKeyFactory
* @since 1.4
*/
public class SecretKeyOrEmptySpec implements KeySpec, SecretKey {
private static final long serialVersionUID = 6577238317307289933L;
/**
* The secret key.
*
* @serial
*/
private byte[] key;
/**
* The name of the algorithm associated with this key.
*
* @serial
*/
private String algorithm;
/**
* Constructs a secret key from the given byte array.
*
* <p>This constructor does not check if the given bytes indeed specify a
* secret key of the specified algorithm. For example, if the algorithm is
* DES, this constructor does not check if <code>key</code> is 8 bytes
* long, and also does not check for weak or semi-weak keys.
* In order for those checks to be performed, an algorithm-specific
* <i>key specification</i> class (in this case:
* {@link DESKeySpec DESKeySpec})
* should be used.
*
* @param key the key material of the secret key. The contents of
* the array are copied to protect against subsequent modification.
* @param algorithm the name of the secret-key algorithm to be associated
* with the given key material.
* See Appendix A in the <a href=
* "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html#AppA">
* Java Cryptography Architecture Reference Guide</a>
* for information about standard algorithm names.
* @exception IllegalArgumentException if <code>algorithm</code>
* is null or <code>key</code> is null or empty.
*/
public SecretKeyOrEmptySpec(byte[] key, String algorithm) {
if (key == null || algorithm == null) {
throw new IllegalArgumentException("Missing argument");
}
this.key = key.clone();
this.algorithm = algorithm;
}
/**
* Constructs a secret key from the given byte array, using the first
* <code>len</code> bytes of <code>key</code>, starting at
* <code>offset</code> inclusive.
*
* <p> The bytes that constitute the secret key are
* those between <code>key[offset]</code> and
* <code>key[offset+len-1]</code> inclusive.
*
* <p>This constructor does not check if the given bytes indeed specify a
* secret key of the specified algorithm. For example, if the algorithm is
* DES, this constructor does not check if <code>key</code> is 8 bytes
* long, and also does not check for weak or semi-weak keys.
* In order for those checks to be performed, an algorithm-specific key
* specification class (in this case:
* {@link DESKeySpec DESKeySpec})
* must be used.
*
* @param key the key material of the secret key. The first
* <code>len</code> bytes of the array beginning at
* <code>offset</code> inclusive are copied to protect
* against subsequent modification.
* @param offset the offset in <code>key</code> where the key material
* starts.
* @param len the length of the key material.
* @param algorithm the name of the secret-key algorithm to be associated
* with the given key material.
* See Appendix A in the <a href=
* "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html#AppA">
* Java Cryptography Architecture Reference Guide</a>
* for information about standard algorithm names.
* @exception IllegalArgumentException if <code>algorithm</code>
* is null or <code>key</code> is null, empty, or too short,
* i.e. {@code key.length-offset<len}.
* @exception ArrayIndexOutOfBoundsException is thrown if
* <code>offset</code> or <code>len</code> index bytes outside the
* <code>key</code>.
*/
public SecretKeyOrEmptySpec(byte[] key, int offset, int len, String algorithm) {
if (key == null || algorithm == null) {
throw new IllegalArgumentException("Missing argument");
}
if (key.length-offset < len) {
throw new IllegalArgumentException
("Invalid offset/length combination");
}
if (len < 0) {
throw new ArrayIndexOutOfBoundsException("len is negative");
}
this.key = new byte[len];
System.arraycopy(key, offset, this.key, 0, len);
this.algorithm = algorithm;
}
/**
* Returns the name of the algorithm associated with this secret key.
*
* @return the secret key algorithm.
*/
public String getAlgorithm() {
return this.algorithm;
}
/**
* Returns the name of the encoding format for this secret key.
*
* @return the string "RAW".
*/
public String getFormat() {
return "RAW";
}
/**
* Returns the key material of this secret key.
*
* @return the key material. Returns a new array
* each time this method is called.
*/
public byte[] getEncoded() {
return this.key.clone();
}
/**
* Calculates a hash code value for the object.
* Objects that are equal will also have the same hashcode.
*/
public int hashCode() {
int retval = 0;
for (int i = 1; i < this.key.length; i++) {
retval += this.key[i] * i;
}
if (this.algorithm.equalsIgnoreCase("TripleDES"))
return (retval ^= "desede".hashCode());
else
return (retval ^=
this.algorithm.toLowerCase(Locale.ENGLISH).hashCode());
}
/**
* Tests for equality between the specified object and this
* object. Two SecretKeySpec objects are considered equal if
* they are both SecretKey instances which have the
* same case-insensitive algorithm name and key encoding.
*
* @param obj the object to test for equality with this object.
*
* @return true if the objects are considered equal, false if
* <code>obj</code> is null or otherwise.
*/
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof SecretKey))
return false;
String thatAlg = ((SecretKey)obj).getAlgorithm();
if (!(thatAlg.equalsIgnoreCase(this.algorithm))) {
if ((!(thatAlg.equalsIgnoreCase("DESede"))
|| !(this.algorithm.equalsIgnoreCase("TripleDES")))
&& (!(thatAlg.equalsIgnoreCase("TripleDES"))
|| !(this.algorithm.equalsIgnoreCase("DESede"))))
return false;
}
byte[] thatKey = ((SecretKey)obj).getEncoded();
return MessageDigest.isEqual(this.key, thatKey);
}
}