GOST_3410_PublicKey::GOST_3410_PublicKey(const AlgorithmIdentifier& alg_id, const secure_vector<byte>& key_bits) { OID ecc_param_id; // The parameters also includes hash and cipher OIDs BER_Decoder(alg_id.parameters).start_cons(SEQUENCE).decode(ecc_param_id); domain_params = EC_Group(ecc_param_id); secure_vector<byte> bits; BER_Decoder(key_bits).decode(bits, OCTET_STRING); const size_t part_size = bits.size() / 2; // Keys are stored in little endian format (WTF) for(size_t i = 0; i != part_size / 2; ++i) { std::swap(bits[i], bits[part_size-1-i]); std::swap(bits[part_size+i], bits[2*part_size-1-i]); } BigInt x(bits.data(), part_size); BigInt y(&bits[part_size], part_size); public_key = PointGFp(domain().get_curve(), x, y); BOTAN_ASSERT(public_key.on_the_curve(), "Loaded GOST 34.10 public key is on the curve"); }
PointGFp operator*(const BigInt& scalar, const PointGFp& point) { //BOTAN_ASSERT(point.on_the_curve(), "Input is on the curve"); const CurveGFp& curve = point.get_curve(); const size_t scalar_bits = scalar.bits(); std::vector<BigInt> ws(9); PointGFp R[2] = { PointGFp(curve), point }; for(size_t i = scalar_bits; i > 0; i--) { const size_t b = scalar.get_bit(i - 1); R[b ^ 1].add(R[b], ws); R[b].mult2(ws); } if(scalar.is_negative()) R[0].negate(); //BOTAN_ASSERT(R[0].on_the_curve(), "Output is on the curve"); return R[0]; }
PointGFp& PointGFp::operator-=(const PointGFp& rhs) { PointGFp minus_rhs = PointGFp(rhs).negate(); if(is_zero()) *this = minus_rhs; else *this += minus_rhs; return *this; }
PointGFp OS2ECP(const byte data[], size_t data_len, const CurveGFp& curve) { if(data_len <= 1) return PointGFp(curve); // return zero const byte pc = data[0]; BigInt x, y; if(pc == 2 || pc == 3) { //compressed form x = BigInt::decode(&data[1], data_len - 1); const bool y_mod_2 = ((pc & 0x01) == 1); y = decompress_point(y_mod_2, x, curve); } else if(pc == 4) { const size_t l = (data_len - 1) / 2; // uncompressed form x = BigInt::decode(&data[1], l); y = BigInt::decode(&data[l+1], l); } else if(pc == 6 || pc == 7) { const size_t l = (data_len - 1) / 2; // hybrid form x = BigInt::decode(&data[1], l); y = BigInt::decode(&data[l+1], l); const bool y_mod_2 = ((pc & 0x01) == 1); if(decompress_point(y_mod_2, x, curve) != y) throw Illegal_Point("OS2ECP: Decoding error in hybrid format"); } else throw Invalid_Argument("OS2ECP: Unknown format type"); PointGFp result(curve, x, y); if(!result.on_the_curve()) throw Illegal_Point("OS2ECP: Decoded point was not on the curve"); return result; }
PointGFp OS2ECP(const uint8_t data[], size_t data_len, const CurveGFp& curve) { // Should we really be doing this? if(data_len <= 1) return PointGFp(curve); // return zero std::pair<BigInt, BigInt> xy = OS2ECP(data, data_len, curve.get_p(), curve.get_a(), curve.get_b()); PointGFp point(curve, xy.first, xy.second); if(!point.on_the_curve()) throw Illegal_Point("OS2ECP: Decoded point was not on the curve"); return point; }
PointGFp operator*(const BigInt& scalar, const PointGFp& point) { //BOTAN_ASSERT(point.on_the_curve(), "Input is on the curve"); const CurveGFp& curve = point.get_curve(); const size_t scalar_bits = scalar.bits(); std::vector<BigInt> ws(9); if(scalar_bits <= 2) { const byte abs_val = scalar.byte_at(0); if(abs_val == 0) return PointGFp::zero_of(curve); PointGFp result = point; if(abs_val == 2) result.mult2(ws); if(scalar.is_negative()) result.negate(); return result; } PointGFp R[2] = { PointGFp(curve), point }; for(size_t i = scalar_bits; i > 0; i--) { const size_t b = scalar.get_bit(i - 1); R[b ^ 1].add(R[b], ws); R[b].mult2(ws); } if(scalar.is_negative()) R[0].negate(); //BOTAN_ASSERT(R[0].on_the_curve(), "Output is on the curve"); return R[0]; }
// *this *= 2 void PointGFp::mult2(std::vector<BigInt>& ws_bn) { if(is_zero()) return; if(m_coord_y.is_zero()) { *this = PointGFp(m_curve); // setting myself to zero return; } /* https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-1986-cc */ const size_t cap_size = 2*m_curve.get_p_words() + 2; for(size_t i = 0; i != ws_bn.size(); ++i) ws_bn[i].ensure_capacity(cap_size); const BigInt& p = m_curve.get_p(); BigInt& y_2 = ws_bn[0]; BigInt& S = ws_bn[1]; BigInt& tmp = ws_bn[2]; BigInt& a_z4 = ws_bn[3]; BigInt& M = ws_bn[4]; BigInt& U = ws_bn[5]; BigInt& x = ws_bn[6]; BigInt& y = ws_bn[7]; BigInt& z = ws_bn[8]; secure_vector<word>& monty_ws = ws_bn[9].get_word_vector(); m_curve.sqr(y_2, m_coord_y, monty_ws); m_curve.mul(S, m_coord_x, y_2, monty_ws); S <<= 2; // * 4 while(S >= p) S -= p; m_curve.sqr(a_z4, m_coord_z, monty_ws); // z^2 m_curve.sqr(tmp, a_z4, monty_ws); // z^4 m_curve.mul(a_z4, m_curve.get_a_rep(), tmp, monty_ws); m_curve.sqr(M, m_coord_x, monty_ws); M *= 3; M += a_z4; while(M >= p) M -= p; m_curve.sqr(x, M, monty_ws); x -= (S << 1); while(x.is_negative()) x += p; m_curve.sqr(U, y_2, monty_ws); U <<= 3; while(U >= p) U -= p; S -= x; while(S.is_negative()) S += p; m_curve.mul(y, M, S, monty_ws); y -= U; if(y.is_negative()) y += p; m_curve.mul(z, m_coord_y, m_coord_z, monty_ws); z <<= 1; if(z >= p) z -= p; m_coord_x = x; m_coord_y = y; m_coord_z = z; }
// Point addition void PointGFp::add(const PointGFp& rhs, std::vector<BigInt>& ws_bn) { if(is_zero()) { coord_x = rhs.coord_x; coord_y = rhs.coord_y; coord_z = rhs.coord_z; return; } else if(rhs.is_zero()) return; const BigInt& p = curve.get_p(); BigInt& rhs_z2 = ws_bn[0]; BigInt& U1 = ws_bn[1]; BigInt& S1 = ws_bn[2]; BigInt& lhs_z2 = ws_bn[3]; BigInt& U2 = ws_bn[4]; BigInt& S2 = ws_bn[5]; BigInt& H = ws_bn[6]; BigInt& r = ws_bn[7]; monty_sqr(rhs_z2, rhs.coord_z); monty_mult(U1, coord_x, rhs_z2); monty_mult(S1, coord_y, monty_mult(rhs.coord_z, rhs_z2)); monty_sqr(lhs_z2, coord_z); monty_mult(U2, rhs.coord_x, lhs_z2); monty_mult(S2, rhs.coord_y, monty_mult(coord_z, lhs_z2)); H = U2; H -= U1; if(H.is_negative()) H += p; r = S2; r -= S1; if(r.is_negative()) r += p; if(H.is_zero()) { if(r.is_zero()) { mult2(ws_bn); return; } *this = PointGFp(curve); // setting myself to zero return; } monty_sqr(U2, H); monty_mult(S2, U2, H); U2 = monty_mult(U1, U2); monty_sqr(coord_x, r); coord_x -= S2; coord_x -= (U2 << 1); while(coord_x.is_negative()) coord_x += p; U2 -= coord_x; if(U2.is_negative()) U2 += p; monty_mult(coord_y, r, U2); coord_y -= monty_mult(S1, S2); if(coord_y.is_negative()) coord_y += p; monty_mult(coord_z, monty_mult(coord_z, rhs.coord_z), H); }
PointGFp operator*(const BigInt& scalar, const PointGFp& point) { const CurveGFp& curve = point.get_curve(); if(scalar.is_zero()) return PointGFp(curve); // zero point std::vector<BigInt> ws(9); if(scalar.abs() <= 2) // special cases for small values { byte value = scalar.abs().byte_at(0); PointGFp result = point; if(value == 2) result.mult2(ws); if(scalar.is_negative()) result.negate(); return result; } const size_t scalar_bits = scalar.bits(); #if 0 PointGFp x1 = PointGFp(curve); PointGFp x2 = point; size_t bits_left = scalar_bits; // Montgomery Ladder while(bits_left) { const bool bit_set = scalar.get_bit(bits_left - 1); if(bit_set) { x1.add(x2, ws); x2.mult2(ws); } else { x2.add(x1, ws); x1.mult2(ws); } --bits_left; } if(scalar.is_negative()) x1.negate(); return x1; #else const size_t window_size = 4; std::vector<PointGFp> Ps(1 << window_size); Ps[0] = PointGFp(curve); Ps[1] = point; for(size_t i = 2; i != Ps.size(); ++i) { Ps[i] = Ps[i-1]; Ps[i].add(point, ws); } PointGFp H(curve); // create as zero size_t bits_left = scalar_bits; while(bits_left >= window_size) { for(size_t i = 0; i != window_size; ++i) H.mult2(ws); const u32bit nibble = scalar.get_substring(bits_left - window_size, window_size); H.add(Ps[nibble], ws); bits_left -= window_size; } while(bits_left) { H.mult2(ws); if(scalar.get_bit(bits_left-1)) H.add(point, ws); --bits_left; } if(scalar.is_negative()) H.negate(); return H; #endif }
// *this *= 2 void PointGFp::mult2(std::vector<BigInt>& ws_bn) { if(is_zero()) return; else if(coord_y.is_zero()) { *this = PointGFp(curve); // setting myself to zero return; } const BigInt& p = curve.get_p(); BigInt& y_2 = ws_bn[0]; BigInt& S = ws_bn[1]; BigInt& z4 = ws_bn[2]; BigInt& a_z4 = ws_bn[3]; BigInt& M = ws_bn[4]; BigInt& U = ws_bn[5]; BigInt& x = ws_bn[6]; BigInt& y = ws_bn[7]; BigInt& z = ws_bn[8]; monty_sqr(y_2, coord_y); monty_mult(S, coord_x, y_2); S <<= 2; // * 4 while(S >= p) S -= p; monty_sqr(z4, monty_sqr(coord_z)); monty_mult(a_z4, curve.get_a_r(), z4); M = 3 * monty_sqr(coord_x); M += a_z4; while(M >= p) M -= p; monty_sqr(x, M); x -= (S << 1); while(x.is_negative()) x += p; monty_sqr(U, y_2); U <<= 3; while(U >= p) U -= p; S -= x; while(S.is_negative()) S += p; monty_mult(y, M, S); y -= U; if(y.is_negative()) y += p; monty_mult(z, coord_y, coord_z); z <<= 1; if(z >= p) z -= p; coord_x = x; coord_y = y; coord_z = z; }
// *this *= 2 void PointGFp::mult2(std::vector<BigInt>& ws_bn) { if(is_zero()) return; else if(m_coord_y.is_zero()) { *this = PointGFp(m_curve); // setting myself to zero return; } /* http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-1986-cc */ const BigInt& p = m_curve.get_p(); BigInt& y_2 = ws_bn[0]; BigInt& S = ws_bn[1]; BigInt& z4 = ws_bn[2]; BigInt& a_z4 = ws_bn[3]; BigInt& M = ws_bn[4]; BigInt& U = ws_bn[5]; BigInt& x = ws_bn[6]; BigInt& y = ws_bn[7]; BigInt& z = ws_bn[8]; curve_sqr(y_2, m_coord_y); curve_mult(S, m_coord_x, y_2); S <<= 2; // * 4 while(S >= p) S -= p; curve_sqr(z4, curve_sqr(m_coord_z)); curve_mult(a_z4, m_curve.get_a_rep(), z4); M = curve_sqr(m_coord_x); M *= 3; M += a_z4; while(M >= p) M -= p; curve_sqr(x, M); x -= (S << 1); while(x.is_negative()) x += p; curve_sqr(U, y_2); U <<= 3; while(U >= p) U -= p; S -= x; while(S.is_negative()) S += p; curve_mult(y, M, S); y -= U; if(y.is_negative()) y += p; curve_mult(z, m_coord_y, m_coord_z); z <<= 1; if(z >= p) z -= p; m_coord_x = x; m_coord_y = y; m_coord_z = z; }