org.bouncycastle.bcpg.S2K Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bcpg-jdk14 Show documentation
Show all versions of bcpg-jdk14 Show documentation
The Bouncy Castle Java API for handling the OpenPGP protocol. This jar contains the OpenPGP API for JDK 1.4. The APIs can be used in conjunction with a JCE/JCA provider such as the one provided with the Bouncy Castle Cryptography APIs.
The newest version!
package org.bouncycastle.bcpg;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.SecureRandom;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
/**
* Parameter specifier for the PGP string-to-key password based key derivation function.
* There are different S2K modes:
*
*
* In {@link #SIMPLE} mode, a single iteration of the hash algorithm is performed to derived a key
* from the given passphrase.
* This mode is deprecated and MUST NOT be generated.
*
*
* The {@link #SALTED} mode is like {@link #SIMPLE}, but uses an additional salt value.
* This mode is deprecated and MUST NOT be generated.
*
*
* In {@link #SALTED_AND_ITERATED} mode, S2K takes a single byte iteration count specifier, which is converted to an
* actual iteration count using a formula that grows the iteration count exponentially as the byte
* value increases.
* e.g. 0x01
== 1088 iterations, and 0xFF == 65,011,712 iterations.
*
*
* The {@link #SALTED_AND_ITERATED} mode uses both iteration and a salt value.
* This mode is recommended for applications that want to stay backwards compatible.
*
*
* The new {@link #ARGON_2} mode does key derivation using salted Argon2, which is a memory-hard hash algorithm.
* This mode is generally recommended over {@link #SALTED_AND_ITERATED}.
*
*
*
* @see
* RFC4880 - String-to-Key (S2K) Specifiers
* @see
* RFC9580 - String-to-Key (S2K) Specifier
* @see
* LibrePGP - String-to-Key (S2K) Specifiers
*/
public class S2K
extends BCPGObject
{
private static final int EXPBIAS = 6;
/**
* Simple key generation. A single non-salted iteration of a hash function.
* This method is deprecated to use, since it can be brute-forced when used
* with a low-entropy string, such as those typically provided by users.
* Additionally, the usage of Simple S2K can lead to key and IV reuse.
* Therefore, in OpenPGP v6, Therefore, when generating an S2K specifier,
* an implementation MUST NOT use Simple S2K.
*
* @deprecated use {@link #SALTED_AND_ITERATED} or {@link #ARGON_2} instead.
*/
public static final int SIMPLE = 0;
/**
* Salted key generation. A single iteration of a hash function with a (unique) salt.
* This method is deprecated to use, since it can be brute-forced when used
* with a low-entropy string, such as those typically provided by users.
* Therefore, in OpenPGP v6, an implementation SHOULD NOT generate a Salted S2K,
* unless the implementation knows that the input string is high-entropy.
*
* @deprecated use {@link #SALTED_AND_ITERATED} or {@link #ARGON_2} instead.
*/
public static final int SALTED = 1;
/**
* Salted and iterated key generation. Multiple iterations of a hash function, with a salt.
* This method MAY be used if {@link #ARGON_2} is not available.
*/
public static final int SALTED_AND_ITERATED = 3;
/**
* Memory-hard, salted key generation using Argon2 hash algorithm.
* @see Argon2Params
*/
public static final int ARGON_2 = 4;
/**
* GNU S2K extension.
* @see GNUDummyParams
*/
public static final int GNU_DUMMY_S2K = 101;
/**
* Do not store the secret part at all.
* @see GNUDummyParams
*/
public static final int GNU_PROTECTION_MODE_NO_PRIVATE_KEY = 1;
/**
* A stub to access smartcards.
* @see GNUDummyParams
*/
public static final int GNU_PROTECTION_MODE_DIVERT_TO_CARD = 2;
/**
* The (GnuPG) internal representation of a private key.
* @see GNUDummyParams
*/
public static final int GNU_PROTECTION_MODE_INTERNAL = 3;
int type;
int algorithm;
byte[] iv;
int itCount = -1;
int passes = -1;
int protectionMode = -1;
int parallelism;
int memorySizeExponent;
/**
* Parse an S2K specifier from an OpenPGP packet input stream.
* @param in packet input stream
* @throws IOException
* @throws UnsupportedPacketVersionException if an unsupported S2K type is encountered
*/
S2K(
InputStream in)
throws IOException
{
DataInputStream dIn = new DataInputStream(in);
type = dIn.read();
switch (type)
{
case SIMPLE:
algorithm = dIn.read();
break;
case SALTED:
algorithm = dIn.read();
iv = new byte[8];
dIn.readFully(iv, 0, iv.length);
break;
case SALTED_AND_ITERATED:
algorithm = dIn.read();
iv = new byte[8];
dIn.readFully(iv, 0, iv.length);
itCount = dIn.read();
break;
case ARGON_2:
iv = new byte[16];
dIn.readFully(iv);
passes = dIn.read();
parallelism = dIn.read();
memorySizeExponent = dIn.read();
break;
case GNU_DUMMY_S2K:
algorithm = dIn.read();
dIn.read(); // G
dIn.read(); // N
dIn.read(); // U
protectionMode = dIn.read(); // protection mode
break;
default:
throw new UnsupportedPacketVersionException("Invalid S2K type: " + type);
}
}
/**
* Constructs a specifier for a {@link #SIMPLE simple} S2K generation.
*
* @param algorithm the {@link HashAlgorithmTags digest algorithm} to use.
*/
public S2K(
int algorithm)
{
this.type = SIMPLE;
this.algorithm = algorithm;
}
/**
* Constructs a specifier for a {@link #SALTED salted} S2K generation.
*
* @param algorithm the {@link HashAlgorithmTags digest algorithm} to use.
* @param iv the salt to apply to input to the key generation.
*/
public S2K(
int algorithm,
byte[] iv)
{
this.type = SALTED;
this.algorithm = algorithm;
this.iv = iv;
}
/**
* Constructs a specifier for a {@link #SALTED_AND_ITERATED salted and iterated} S2K generation.
*
* @param algorithm the {@link HashAlgorithmTags digest algorithm} to iterate.
* @param iv the salt to apply to input to the key generation.
* @param itCount the single byte iteration count specifier.
*/
public S2K(
int algorithm,
byte[] iv,
int itCount)
{
this.type = SALTED_AND_ITERATED;
this.algorithm = algorithm;
this.iv = iv;
if (itCount >= 256 && itCount <= 65536)
{
throw new IllegalArgumentException("invalid itCount");
}
this.itCount = itCount;
}
/**
* Constructs a specifier for an {@link #ARGON_2 S2K method using Argon2}.
*
* @param argon2Params argon2 parameters
*/
public S2K(Argon2Params argon2Params)
{
this.type = ARGON_2;
this.iv = argon2Params.getSalt();
this.passes = argon2Params.getPasses();
this.parallelism = argon2Params.getParallelism();
this.memorySizeExponent = argon2Params.getMemSizeExp();
}
/**
* Construct a specifier for an S2K using the {@link #GNU_DUMMY_S2K} method.
*
* @param gnuDummyParams GNU_DUMMY_S2K parameters
*/
public S2K(GNUDummyParams gnuDummyParams)
{
this.type = GNU_DUMMY_S2K;
this.protectionMode = gnuDummyParams.getProtectionMode();
}
/**
* Return a new S2K instance using the {@link #SIMPLE} method, using the given hash algorithm
.
*
* @param algorithm hash algorithm tag
* @return S2K
*/
public static S2K simpleS2K(int algorithm)
{
return new S2K(algorithm);
}
/**
* Return a new S2K instance using the {@link #SALTED} method, using the given hash algorithm
* and salt.
*
* @param algorithm hash algorithm tag
* @param salt salt
* @return S2K
*/
public static S2K saltedS2K(int algorithm, byte[] salt)
{
return new S2K(algorithm, salt);
}
/**
* Return a new S2K instance using the {@link #SALTED_AND_ITERATED} method, using the given hash algorithm,
* salt and iterationCount.
*
* @param algorithm hash algorithm tag
* @param salt salt
* @param iterationCount number of iterations
* @return S2K
*/
public static S2K saltedAndIteratedS2K(int algorithm, byte[] salt, int iterationCount)
{
return new S2K(algorithm, salt, iterationCount);
}
/**
* Return a new S2K instance using the {@link #ARGON_2} method, using the given argon2 parameters.
*
* @param parameters argon2 parameters
* @return S2K
*/
public static S2K argon2S2K(Argon2Params parameters)
{
return new S2K(parameters);
}
/**
* Return a new S2K instance using the {@link #GNU_DUMMY_S2K} method, using the given GNU Dummy S2K parameters.
*
* @param parameters GNU Dummy S2K parameters
* @return S2K
*/
public static S2K gnuDummyS2K(GNUDummyParams parameters)
{
return new S2K(parameters);
}
/**
* Gets the S2K specifier type.
*
* @see #SIMPLE
* @see #SALTED
* @see #SALTED_AND_ITERATED
* @see #ARGON_2
*
* @return type
*/
public int getType()
{
return type;
}
/**
* Gets the {@link HashAlgorithmTags hash algorithm} for this S2K.
* Only used for {@link #SIMPLE}, {@link #SALTED}, {@link #SALTED_AND_ITERATED}
*
* @return hash algorithm
*/
public int getHashAlgorithm()
{
return algorithm;
}
/**
* Gets the iv/salt to use for the key generation.
* The value of this field depends on the S2K {@link #type}:
*
* {@link #SIMPLE}: null
* {@link #SALTED}: 8 octets
* {@link #SALTED_AND_ITERATED}: 8 octets
* {@link #ARGON_2}: 16 octets
*
*
* @return IV
*/
public byte[] getIV()
{
return iv;
}
/**
* Gets the actual (expanded) iteration count.
* Only used for {@link #SALTED_AND_ITERATED}.
*
* @return iteration count
*/
public long getIterationCount()
{
if (itCount >= 256)
{
return itCount;
}
return (16 + (itCount & 15)) << ((itCount >> 4) + EXPBIAS);
}
/**
* Return the number of passes - only Argon2.
*
* @return number of passes
*/
public int getPasses()
{
return passes;
}
/**
* Gets the protection mode - only if GNU_DUMMY_S2K.
*
* @see #GNU_PROTECTION_MODE_NO_PRIVATE_KEY
* @see #GNU_PROTECTION_MODE_DIVERT_TO_CARD
*
* @return GNU dummy-s2k protection mode
*/
public int getProtectionMode()
{
return protectionMode;
}
/**
* Gets the degree of parallelism - only if ARGON_2.
*
* @return parallelism
*/
public int getParallelism()
{
return parallelism;
}
/**
* Gets the memory size exponent - only if ARGON_2.
*
* @return memory size exponent
*/
public int getMemorySizeExponent()
{
return memorySizeExponent;
}
/**
* Encode the packet into the given {@link BCPGOutputStream}.
* @param out packet output stream
* @throws IOException
*/
public void encode(
BCPGOutputStream out)
throws IOException
{
switch (type)
{
case SIMPLE:
out.write(type);
out.write(algorithm);
break;
case SALTED:
out.write(type);
out.write(algorithm);
out.write(iv);
break;
case SALTED_AND_ITERATED:
out.write(type);
out.write(algorithm);
out.write(iv);
writeOneOctetOrThrow(out, itCount, "Iteration count");
break;
case ARGON_2:
out.write(type);
out.write(iv);
writeOneOctetOrThrow(out, passes, "Passes");
writeOneOctetOrThrow(out, parallelism, "Parallelism");
writeOneOctetOrThrow(out, memorySizeExponent, "Memory size exponent");
break;
case GNU_DUMMY_S2K:
out.write(type);
out.write(algorithm);
out.write('G');
out.write('N');
out.write('U');
out.write(protectionMode);
break;
default:
throw new IllegalStateException("Unknown S2K type " + type);
}
}
/**
* Throw an {@link IllegalArgumentException} if the value cannot be encoded,
* otherwise write the value to the output stream.
*
* @param out output stream
* @param val value
* @param valName name of the value for the error message
* @throws IllegalArgumentException if the value cannot be encoded
* @throws IOException potentially thrown by {@link BCPGOutputStream#write(int)}
*/
private void writeOneOctetOrThrow(BCPGOutputStream out, int val, String valName)
throws IOException
{
if (val >= 256)
{
throw new IllegalStateException(valName + " not encodable");
}
out.write(val);
}
/**
* Parameters for Argon2 S2K.
* @see
* OpenPGP - Argon2
*/
public static class Argon2Params
{
private final byte[] salt;
private final int passes;
private final int parallelism;
private final int memSizeExp;
/**
* Uniformly safe and recommended parameters not tailored to any hardware.
* Uses Argon2id, 1 pass, 4 parallelism, 2 GiB RAM.
*
* @see RFC 9106: §4. Parameter Choice
*/
public Argon2Params()
{
this(CryptoServicesRegistrar.getSecureRandom());
}
/**
* Uniformly safe and recommended parameters not tailored to any hardware.
* Uses Argon2id, 1 pass, 4 parallelism, 2 GiB RAM.
*
* @see RFC 9106: §4. Parameter Choice
*/
public Argon2Params(SecureRandom secureRandom)
{
this(1, 4, 21, secureRandom);
}
/**
* Create customized Argon2 S2K parameters.
*
* @param passes number of iterations, must be greater than 0
* @param parallelism number of lanes, must be greater 0
* @param memSizeExp exponent for memory consumption, must be between 3 + ⌈log₂p⌉ and 31
* @param secureRandom secure random generator to initialize the salt vector
*/
public Argon2Params(int passes, int parallelism, int memSizeExp, SecureRandom secureRandom)
{
this(mineSalt(secureRandom), passes, parallelism, memSizeExp);
}
/**
* Create customized Argon2 S2K parameters.
*
* @param salt 16 bytes of random salt
* @param passes number of iterations, must be greater than 0
* @param parallelism number of lanes, must be greater 0
* @param memSizeExp exponent for memory consumption, must be between 3 + ⌈log₂p⌉ and 31
*/
public Argon2Params(byte[] salt, int passes, int parallelism, int memSizeExp)
{
if (salt.length != 16)
{
throw new IllegalArgumentException("Argon2 uses 16 bytes of salt");
}
this.salt = salt;
if (passes < 1)
{
throw new IllegalArgumentException("Number of passes MUST be positive, non-zero");
}
this.passes = passes;
if (parallelism < 1)
{
throw new IllegalArgumentException("Parallelism MUST be positive, non-zero.");
}
this.parallelism = parallelism;
// log₂p = logₑp / logₑ2
double log2_p = Math.log(parallelism) / Math.log(2);
// see https://www.rfc-editor.org/rfc/rfc9580.html#section-3.7.1.4-5
if (memSizeExp < (3 + Math.ceil(log2_p)) || memSizeExp > 31)
{
throw new IllegalArgumentException("Memory size exponent MUST be between 3 + ⌈log₂(parallelism)⌉ and 31");
}
this.memSizeExp = memSizeExp;
}
/**
* Uniformly safe and recommended parameters not tailored to any hardware.
* Uses Argon2id, 1 pass, 4 parallelism, 2 GiB RAM.
*
* @see RFC 9106: §4. Parameter Choice
*/
public static Argon2Params universallyRecommendedParameters()
{
return new Argon2Params(1, 4, 21, CryptoServicesRegistrar.getSecureRandom());
}
/**
* Recommended parameters for memory constrained environments (64MiB RAM).
* Uses Argon2id with 3 passes, 4 lanes and 64 MiB RAM.
*
* @return safe parameters for memory constrained environments
* @see RFC9106: §4. Parameter Choice
*/
public static Argon2Params memoryConstrainedParameters()
{
return new Argon2Params(3, 4, 16, CryptoServicesRegistrar.getSecureRandom());
}
/**
* Generate 16 bytes of random salt.
*
* @param secureRandom random number generator instance
* @return salt
*/
private static byte[] mineSalt(SecureRandom secureRandom)
{
byte[] salt = new byte[16];
secureRandom.nextBytes(salt);
return salt;
}
/**
* Return a 16-byte byte array containing the salt S.
*
* @return salt
*/
public byte[] getSalt()
{
return salt;
}
/**
* Return the number of passes t.
*
* @return number of passes
*/
public int getPasses()
{
return passes;
}
/**
* Return the factor of parallelism p.
*
* @return parallelism
*/
public int getParallelism()
{
return parallelism;
}
/**
* Return the exponent indicating the memory size m.
*
* @return memory size exponent
*/
public int getMemSizeExp()
{
return memSizeExp;
}
}
/**
* Parameters for the {@link #GNU_DUMMY_S2K} method.
*
* @see
* GNU extensions to the S2K algorithm
*/
public static class GNUDummyParams
{
private final int protectionMode;
private GNUDummyParams(int protectionMode)
{
this.protectionMode = protectionMode;
}
/**
* Factory method for a GNU Dummy S2K indicating a missing private key.
*
* @return params
*/
public static GNUDummyParams noPrivateKey()
{
return new GNUDummyParams(GNU_PROTECTION_MODE_NO_PRIVATE_KEY);
}
/**
* Factory method for a GNU Dummy S2K indicating a private key located on a smart card.
*
* @return params
*/
public static GNUDummyParams divertToCard()
{
return new GNUDummyParams(GNU_PROTECTION_MODE_DIVERT_TO_CARD);
}
/**
* Factory method for a GNU Dummy S2K indicating an internal private key.
*
* @return params
*/
public static GNUDummyParams internal()
{
return new GNUDummyParams(GNU_PROTECTION_MODE_INTERNAL);
}
/**
* Return the GNU Dummy S2K protection method.
*
* @return protection method
*/
public int getProtectionMode()
{
return protectionMode;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy