blob: 15a49ded46d0492c1cb1cea689030cd7e6802a1c [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CRYPTOHOME_CRYPTO_ELLIPTIC_CURVE_H_
#define CRYPTOHOME_CRYPTO_ELLIPTIC_CURVE_H_
#include <string>
#include <base/optional.h>
#include <brillo/secure_blob.h>
#include <crypto/scoped_openssl_types.h>
#include <openssl/bn.h>
#include <openssl/ec.h>
namespace cryptohome {
// A light-weight C++ wrapper for OpenSSL Elliptic Curve primitives.
class EllipticCurve final {
public:
// Currently only most common prime curves are supported, but the interface
// can be extended to any OpenSSL supported curve if needed.
enum class CurveType { kPrime256, kPrime384, kPrime521 };
// Creates an elliptic curve. Returns `base::nullopt` if error occurred.
// Context is only used during creation of the curve and does not have to
// outlive the curve instance (it is not stored in a curve object).
static base::Optional<EllipticCurve> Create(CurveType curve, BN_CTX* context);
// Non-copyable, but movable.
EllipticCurve(EllipticCurve&& other) = default;
EllipticCurve& operator=(EllipticCurve&& other) = default;
~EllipticCurve();
// Returns true if point is on a curve (including point at infinity).
bool IsPointValid(const EC_POINT& point, BN_CTX* context) const;
// Returns true if point is at infinity.
bool IsPointAtInfinity(const EC_POINT& point) const;
// Returns true if point is on a curve and finite (not at infinity).
bool IsPointValidAndFinite(const EC_POINT& point, BN_CTX* context) const;
// Calculate the inverse of the supplied `point`. The result is placed back in
// `point`.
bool InvertPoint(EC_POINT* point, BN_CTX* context) const;
// Returns scalar size in bytes of a curve order.
int ScalarSizeInBytes() const;
// Returns field element (affine coordinate) size in bytes.
int FieldElementSizeInBytes() const;
// Returns affine X coordinate of a given `point` or nullptr if error
// occurred.
crypto::ScopedBIGNUM GetAffineCoordinateX(const EC_POINT& point,
BN_CTX* context) const;
// Generates random non-zero scalar of the elliptic curve order. Returns
// nullptr if error occurred.
crypto::ScopedBIGNUM RandomNonZeroScalar(BN_CTX* context) const;
// Performs addition modulo order. Returns nullptr if error occurred.
crypto::ScopedBIGNUM ModAdd(const BIGNUM& a,
const BIGNUM& b,
BN_CTX* context) const;
// Returns true if two points are equal.
bool AreEqual(const EC_POINT& point1,
const EC_POINT& point2,
BN_CTX* context) const;
// Performs point by scalar multiplication. Input point must be on a curve.
// It is required that scalar is in the range of [0..curve order-1].
// Returns nullptr if error occurred.
crypto::ScopedEC_POINT Multiply(const EC_POINT& point,
const BIGNUM& scalar,
BN_CTX* context) const;
// Performs multiplication with generator. Expects scalar to be in the range
// of [-curve order..curve order-1]. Return nullptr if error occurred.
crypto::ScopedEC_POINT MultiplyWithGenerator(const BIGNUM& scalar,
BN_CTX* context) const;
// Performs point addition. Input points must be on a curve.
// If two points are equal, the addition will perform doubling: P + P = 2P.
// The result is a point on a curve or point at infinity e.g. P+(-P) = inf.
// Returns nullptr if error occurred.
crypto::ScopedEC_POINT Add(const EC_POINT& point1,
const EC_POINT& point2,
BN_CTX* context) const;
// Converts SecureBlob to EC_POINT. Returns nullptr if error occurred.
// The expected format of SecureBlob is OpenSSL octet form (a binary encoding
// of the EC_POINT structure as defined in RFC5480).
// The method returns an error if resulting point is not on a curve or at
// infinity.
crypto::ScopedEC_POINT SecureBlobToPoint(const brillo::SecureBlob& blob,
BN_CTX* context) const;
// Converts EC_POINT to SecureBlob. Input point must be finite and on a curve.
// Returns false if error occurred, otherwise stores resulting blob in
// `result`. The output blob is a point converted to OpenSSL uncompressed
// octet form (a binary encoding of the EC_POINT structure as defined in
// RFC5480).
bool PointToSecureBlob(const EC_POINT& point,
brillo::SecureBlob* result,
BN_CTX* context) const;
// Generates EC_KEY. This method should be preferred over generating private
// and public key separately, that is, private key using `RandomNonZeroScalar`
// and public key by multiplying private key with generator, but the result
// should be equivalent. Returns nullptr if error occurred.
crypto::ScopedEC_KEY GenerateKey(BN_CTX* context) const;
// Generates pair EC_KEY and converts a pair of public and them to secure
// blobs, Returns false if error occurred.
bool GenerateKeysAsSecureBlobs(brillo::SecureBlob* public_key,
brillo::SecureBlob* private_key,
BN_CTX* context) const;
// Returns curve order. This should be used only for testing.
const BIGNUM* GetOrderForTesting() const { return order_.get(); }
// Returns point at infinity or nullptr if error occurred.
// This should be used only for testing.
crypto::ScopedEC_POINT PointAtInfinityForTesting() const;
// Returns group. This should be used only for testing.
const EC_GROUP* GetGroupForTesting() const { return group_.get(); }
private:
// Constructor is private. A user of the class should use `Create` method
// instead.
explicit EllipticCurve(crypto::ScopedEC_GROUP group,
crypto::ScopedBIGNUM order);
// Converts point to buffer of bytes in OpenSSL octet form.
// Returns length of buffer stored in `ret_buf` or zero if error occurred.
size_t PointToBuf(const EC_POINT& point,
crypto::ScopedOpenSSLBytes* ret_buf,
BN_CTX* context) const;
crypto::ScopedEC_GROUP group_;
crypto::ScopedBIGNUM order_;
};
} // namespace cryptohome
#endif // CRYPTOHOME_CRYPTO_ELLIPTIC_CURVE_H_