org.bouncycastle.pqc.crypto.ntru.NTRUOWCPA Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of com.liferay.saml.opensaml.integration Show documentation
Show all versions of com.liferay.saml.opensaml.integration Show documentation
Liferay SAML OpenSAML Integration
package org.bouncycastle.pqc.crypto.ntru;
import org.bouncycastle.pqc.math.ntru.HPSPolynomial;
import org.bouncycastle.pqc.math.ntru.Polynomial;
import org.bouncycastle.pqc.math.ntru.parameters.NTRUHPSParameterSet;
import org.bouncycastle.pqc.math.ntru.parameters.NTRUHRSSParameterSet;
import org.bouncycastle.pqc.math.ntru.parameters.NTRUParameterSet;
import org.bouncycastle.util.Arrays;
/**
* An OW-CPA secure deterministic public key encryption scheme (DPKE).
*
* @see NTRU specification section 1.11
*/
class NTRUOWCPA
{
private final NTRUParameterSet params;
private final NTRUSampling sampling;
public NTRUOWCPA(NTRUParameterSet params)
{
this.params = params;
this.sampling = new NTRUSampling(params);
}
/**
* Generate a DPKE key pair.
*
* @param seed a random byte array
* @return DPKE key pair
* @see NTRU specification section 1.11.1
*/
public OWCPAKeyPair keypair(byte[] seed)
{
byte[] publicKey;
byte[] privateKey = new byte[this.params.owcpaSecretKeyBytes()];
int n = this.params.n();
int q = this.params.q();
int i;
PolynomialPair pair;
Polynomial x3, x4, x5;
x3 = this.params.createPolynomial();
x4 = this.params.createPolynomial();
x5 = this.params.createPolynomial();
Polynomial f, g, invfMod3 = x3;
Polynomial gf = x3, invgf = x4, tmp = x5;
Polynomial invh = x3, h = x3;
pair = sampling.sampleFg(seed);
f = pair.f();
g = pair.g();
invfMod3.s3Inv(f);
byte[] fs3ToBytes = f.s3ToBytes(params.owcpaMsgBytes());
System.arraycopy(fs3ToBytes, 0, privateKey, 0, fs3ToBytes.length);
byte[] s3Res = invfMod3.s3ToBytes(privateKey.length - this.params.packTrinaryBytes());
System.arraycopy(s3Res, 0, privateKey, this.params.packTrinaryBytes(), s3Res.length);
f.z3ToZq();
g.z3ToZq();
if (this.params instanceof NTRUHRSSParameterSet)
{
/* g = 3*(x-1)*g */
for (i = n - 1; i > 0; i--)
{
g.coeffs[i] = (short)(3 * (g.coeffs[i - 1] - g.coeffs[i]));
}
g.coeffs[0] = (short)-(3 * g.coeffs[0]);
}
else
{
for (i = 0; i < n; i++)
{
g.coeffs[i] = (short)(3 * g.coeffs[i]);
}
}
gf.rqMul(g, f);
invgf.rqInv(gf);
tmp.rqMul(invgf, f);
invh.sqMul(tmp, f);
byte[] sqRes = invh.sqToBytes(privateKey.length - 2 * this.params.packTrinaryBytes());
System.arraycopy(sqRes, 0, privateKey, 2 * this.params.packTrinaryBytes(), sqRes.length);
tmp.rqMul(invgf, g);
h.rqMul(tmp, g);
publicKey = h.rqSumZeroToBytes(this.params.owcpaPublicKeyBytes());
return new OWCPAKeyPair(publicKey, privateKey);
}
/**
* DPKE encryption.
*
* @param r
* @param m
* @param publicKey
* @return DPKE ciphertext
* @see NTRU specification section 1.11.3
*/
public byte[] encrypt(Polynomial r, Polynomial m, byte[] publicKey)
{
int i;
Polynomial x1 = params.createPolynomial(), x2 = params.createPolynomial();
Polynomial h = x1, liftm = x1;
Polynomial ct = x2;
h.rqSumZeroFromBytes(publicKey);
ct.rqMul(r, h);
liftm.lift(m);
for (i = 0; i < params.n(); i++)
{
ct.coeffs[i] += liftm.coeffs[i];
}
return ct.rqSumZeroToBytes(params.ntruCiphertextBytes());
}
/**
* DPKE decryption.
*
* @param ciphertext
* @param privateKey
* @return an instance of {@link OWCPADecryptResult} containing {@code packed_rm} and fail flag
* @see NTRU specification section 1.11.4
*/
public OWCPADecryptResult decrypt(byte[] ciphertext, byte[] privateKey)
{
byte[] sk = privateKey;
byte[] rm = new byte[params.owcpaMsgBytes()];
int i, fail;
Polynomial x1 = params.createPolynomial();
Polynomial x2 = params.createPolynomial();
Polynomial x3 = params.createPolynomial();
Polynomial x4 = params.createPolynomial();
Polynomial c = x1, f = x2, cf = x3;
Polynomial mf = x2, finv3 = x3, m = x4;
Polynomial liftm = x2, invh = x3, r = x4;
Polynomial b = x1;
c.rqSumZeroFromBytes(ciphertext);
f.s3FromBytes(sk);
f.z3ToZq();
cf.rqMul(c, f);
mf.rqToS3(cf);
finv3.s3FromBytes(Arrays.copyOfRange(sk, params.packTrinaryBytes(), sk.length));
m.s3Mul(mf, finv3);
byte[] arr1 = m.s3ToBytes(rm.length - params.packTrinaryBytes());
fail = 0;
/* Check that the unused bits of the last byte of the ciphertext are zero */
fail |= checkCiphertext(ciphertext);
/* For the IND-CCA2 KEM we must ensure that c = Enc(h, (r,m)). */
/* We can avoid re-computing r*h + Lift(m) as long as we check that */
/* r (defined as b/h mod (q, Phi_n)) and m are in the message space. */
/* (m can take any value in S3 in NTRU_HRSS) */
if (params instanceof NTRUHPSParameterSet)
{
fail |= checkM((HPSPolynomial)m);
}
/* b = c - Lift(m) mod (q, x^n - 1) */
liftm.lift(m);
for (i = 0; i < params.n(); i++)
{
b.coeffs[i] = (short)(c.coeffs[i] - liftm.coeffs[i]);
}
/* r = b / h mod (q, Phi_n) */
invh.sqFromBytes(Arrays.copyOfRange(sk, 2 * params.packTrinaryBytes(), sk.length));
r.sqMul(b, invh);
/* NOTE: Our definition of r as b/h mod (q, Phi_n) follows Figure 4 of */
/* [Sch18] https://eprint.iacr.org/2018/1174/20181203:032458. */
/* This differs from Figure 10 of Saito--Xagawa--Yamakawa */
/* [SXY17] https://eprint.iacr.org/2017/1005/20180516:055500 */
/* where r gets a final reduction modulo p. */
/* We need this change to use Proposition 1 of [Sch18]. */
/* Proposition 1 of [Sch18] shows that re-encryption with (r,m) yields c. */
/* if and only if fail==0 after the following call to owcpa_check_r */
/* The procedure given in Fig. 8 of [Sch18] can be skipped because we have */
/* c(1) = 0 due to the use of poly_Rq_sum_zero_{to,from}bytes. */
fail |= checkR(r);
r.trinaryZqToZ3();
byte[] arr2 = r.s3ToBytes(params.owcpaMsgBytes());
System.arraycopy(arr2, 0, rm, 0, arr2.length);
System.arraycopy(arr1, 0, rm, params.packTrinaryBytes(), arr1.length);
return new OWCPADecryptResult(rm, fail);
}
private int checkCiphertext(byte[] ciphertext)
{
/* A ciphertext is log2(q)*(n-1) bits packed into bytes. */
/* Check that any unused bits of the final byte are zero. */
short t;
t = ciphertext[params.ntruCiphertextBytes() - 1];
t &= 0xff << (8 - (7 & (params.logQ() * params.packDegree())));
/* We have 0 <= t < 256 */
/* Return 0 on success (t=0), 1 on failure */
return 1 & ((~t + 1) >>> 15);
}
private int checkR(Polynomial r)
{
/* A valid r has coefficients in {0,1,q-1} and has r[N-1] = 0 */
/* Note: We may assume that 0 <= r[i] <= q-1 for all i */
int i;
int t = 0; // unsigned
short c; // unsigned
for (i = 0; i < params.n() - 1; i++)
{
c = r.coeffs[i];
t |= (c + 1) & (params.q() - 4); /* 0 iff c is in {-1,0,1,2} */
t |= (c + 2) & 4; /* 1 if c = 2, 0 if c is in {-1,0,1} */
}
t |= r.coeffs[params.n() - 1];/* Coefficient n-1 must be zero */
/* We have 0 <= t < 2^16. */
/* Return 0 on success (t=0), 1 on failure */
return (1 & ((~t + 1) >>> 31));
}
/**
* Check that m is in message space, i.e.
* (1) |{i : m[i] = 1}| = |{i : m[i] = 2}|, and
* (2) |{i : m[i] != 0}| = NTRU_WEIGHT.
* Note: We may assume that m has coefficients in {0,1,2}.
*
* @param m
* @return 0 on success (t=0), 1 on failure
*/
private int checkM(HPSPolynomial m)
{
int i;
int t = 0; // unsigned
short ps = 0; // unsigned
short ms = 0; // unsigned
for (i = 0; i < params.n() - 1; i++)
{
ps += m.coeffs[i] & 1;
ms += m.coeffs[i] & 2;
}
t |= ps ^ (ms >>> 1);
t |= ms ^ ((NTRUHPSParameterSet)params).weight();
/* We have 0 <= t < 2^16. */
/* Return 0 on success (t=0), 1 on failure */
return (1 & ((~t + 1) >>> 31));
}
}