Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

extras: Add EC toolbox abstractions and OPRF(P-384, SHA-384) VOPRF API #292

Merged
merged 13 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,18 @@ let package = Package(
],
swiftSettings: swiftSettings
),
.testTarget(name: "_CryptoExtrasTests", dependencies: ["_CryptoExtras"]),
.testTarget(
name: "_CryptoExtrasTests",
dependencies: ["_CryptoExtras"],
resources: [
.copy("ECToolbox/H2CVectors/P256_XMD-SHA-256_SSWU_RO_.json"),
.copy("ECToolbox/H2CVectors/P384_XMD-SHA-384_SSWU_RO_.json"),
.copy("OPRFs/OPRFVectors/OPRFVectors-VOPRFDraft8.json"),
.copy("OPRFs/OPRFVectors/OPRFVectors-VOPRFDraft19.json"),
.copy("OPRFs/OPRFVectors/OPRFVectors-edgecases.json"),
],
swiftSettings: swiftSettings
),
.testTarget(name: "CryptoBoringWrapperTests", dependencies: ["CryptoBoringWrapper"]),
],
cxxLanguageStandard: .cxx11
Expand Down
14 changes: 14 additions & 0 deletions Sources/CCryptoBoringSSLShims/include/CCryptoBoringSSLShims.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,20 @@ int CCryptoBoringSSLShims_EVP_PKEY_decrypt(EVP_PKEY_CTX *ctx, void *out,
size_t *out_len, const void *in,
size_t in_len);

int CCryptoBoringSSLShims_EC_hash_to_curve_p256_xmd_sha256_sswu(const EC_GROUP *group, EC_POINT *out,
const void *dst, size_t dst_len,
const void *msg, size_t msg_len);

int CCryptoBoringSSLShims_EC_hash_to_curve_p384_xmd_sha384_sswu(const EC_GROUP *group, EC_POINT *out,
const void *dst, size_t dst_len,
const void *msg, size_t msg_len);

size_t CCryptoBoringSSLShims_EC_POINT_point2oct(const EC_GROUP *group,
const EC_POINT *point,
point_conversion_form_t form,
void *buf, size_t max_out,
BN_CTX *ctx);

#if defined(__cplusplus)
}
#endif // defined(__cplusplus)
Expand Down
20 changes: 20 additions & 0 deletions Sources/CCryptoBoringSSLShims/shims.c
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,23 @@ int CCryptoBoringSSLShims_EVP_PKEY_decrypt(EVP_PKEY_CTX *ctx, void *out,
size_t in_len) {
return CCryptoBoringSSL_EVP_PKEY_decrypt(ctx, out, out_len, in, in_len);
}

int CCryptoBoringSSLShims_EC_hash_to_curve_p256_xmd_sha256_sswu(const EC_GROUP *group, EC_POINT *out,
const void *dst, size_t dst_len,
const void *msg, size_t msg_len) {
return CCryptoBoringSSL_EC_hash_to_curve_p256_xmd_sha256_sswu(group, out, dst, dst_len, msg, msg_len);
}

int CCryptoBoringSSLShims_EC_hash_to_curve_p384_xmd_sha384_sswu(const EC_GROUP *group, EC_POINT *out,
const void *dst, size_t dst_len,
const void *msg, size_t msg_len) {
return CCryptoBoringSSL_EC_hash_to_curve_p384_xmd_sha384_sswu(group, out, dst, dst_len, msg, msg_len);
}

size_t CCryptoBoringSSLShims_EC_POINT_point2oct(const EC_GROUP *group,
const EC_POINT *point,
point_conversion_form_t form,
void *buf, size_t max_out,
BN_CTX *ctx) {
return CCryptoBoringSSL_EC_POINT_point2oct(group, point, form, buf, max_out, ctx);
}
26 changes: 25 additions & 1 deletion Sources/CryptoBoringWrapper/EC/EllipticCurve.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
/// A wrapper around BoringSSL's EC_GROUP object that handles reference counting and
/// liveness.
@usableFromInline
package class BoringSSLEllipticCurveGroup {
package final class BoringSSLEllipticCurveGroup {
/* private but usableFromInline */ @usableFromInline var _group: OpaquePointer

@usableFromInline
Expand Down Expand Up @@ -72,6 +72,16 @@ extension BoringSSLEllipticCurveGroup {
return try! ArbitraryPrecisionInteger(copying: baseOrder)
}

@usableFromInline
package var generator: EllipticCurvePoint {
get throws {
guard let generatorPtr = CCryptoBoringSSL_EC_GROUP_get0_generator(self._group) else {
throw CryptoBoringWrapperError.internalBoringSSLError()
}
return try EllipticCurvePoint(copying: generatorPtr, on: self)
}
}

/// An elliptic curve can be represented in a Weierstrass form: `y² = x³ + ax + b`. This
/// property provides the values of a and b on the curve.
@usableFromInline
Expand Down Expand Up @@ -102,6 +112,20 @@ extension BoringSSLEllipticCurveGroup {
case p384
case p521
}

@usableFromInline
var curveName: CurveName? {
switch CCryptoBoringSSL_EC_GROUP_get_curve_name(self._group) {
case NID_X9_62_prime256v1:
return .p256
case NID_secp384r1:
return .p384
case NID_secp521r1:
return .p521
default:
return nil
}
}
}

extension BoringSSLEllipticCurveGroup.CurveName {
Expand Down
251 changes: 238 additions & 13 deletions Sources/CryptoBoringWrapper/EC/EllipticCurvePoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,41 +12,266 @@
//
//===----------------------------------------------------------------------===//
@_implementationOnly import CCryptoBoringSSL
@_implementationOnly import CCryptoBoringSSLShims
import struct Foundation.Data
import protocol Foundation.ContiguousBytes

/// A wrapper around BoringSSL's EC_POINT with some lifetime management.
@usableFromInline
package class EllipticCurvePoint {
package final class EllipticCurvePoint {
/* private but @usableFromInline */ @usableFromInline var _basePoint: OpaquePointer

@usableFromInline
package init(multiplying scalar: ArbitraryPrecisionInteger, on group: BoringSSLEllipticCurveGroup) throws {
package init(copying pointer: OpaquePointer, on group: BoringSSLEllipticCurveGroup) throws {
self._basePoint = try group.withUnsafeGroupPointer { groupPtr in
guard let basePoint = CCryptoBoringSSL_EC_POINT_new(groupPtr) else {
guard let pointPtr = CCryptoBoringSSL_EC_POINT_dup(pointer, groupPtr) else {
throw CryptoBoringWrapperError.internalBoringSSLError()
}
return basePoint
return pointPtr
}
}

@usableFromInline
package convenience init(copying other: EllipticCurvePoint, on group: BoringSSLEllipticCurveGroup) throws {
try self.init(copying: other._basePoint, on: group)
}

@usableFromInline
package init(_pointAtInfinityOn group: BoringSSLEllipticCurveGroup) throws {
self._basePoint = try group.withUnsafeGroupPointer { groupPtr in
guard let pointPtr = CCryptoBoringSSL_EC_POINT_new(groupPtr) else {
throw CryptoBoringWrapperError.internalBoringSSLError()
}
return pointPtr
}
}

@usableFromInline
package convenience init(multiplying scalar: ArbitraryPrecisionInteger, on group: BoringSSLEllipticCurveGroup) throws {
try self.init(_pointAtInfinityOn: group)
try group.withUnsafeGroupPointer { groupPtr in
try scalar.withUnsafeBignumPointer { bigNumPtr in
guard CCryptoBoringSSL_EC_POINT_mul(groupPtr, self._basePoint, bigNumPtr, nil, nil, nil) != 0 else {
try scalar.withUnsafeBignumPointer { scalarPtr in
guard CCryptoBoringSSL_EC_POINT_mul(groupPtr, self._basePoint, scalarPtr, nil, nil, nil) == 1 else {
throw CryptoBoringWrapperError.internalBoringSSLError()
}
}
}
}

package init(copying pointer: OpaquePointer, on group: BoringSSLEllipticCurveGroup) throws {
self._basePoint = try group.withUnsafeGroupPointer { groupPtr in
guard let basePoint = CCryptoBoringSSL_EC_POINT_dup(pointer, groupPtr) else {
throw CryptoBoringWrapperError.internalBoringSSLError()
deinit {
CCryptoBoringSSL_EC_POINT_free(self._basePoint)
}

@usableFromInline
package func multiply(by rhs: ArbitraryPrecisionInteger, on group: BoringSSLEllipticCurveGroup) throws {
try self.withPointPointer { selfPtr in
try rhs.withUnsafeBignumPointer { rhsPtr in
try group.withUnsafeGroupPointer { groupPtr in
guard CCryptoBoringSSL_EC_POINT_mul(groupPtr, selfPtr, nil, selfPtr, rhsPtr, nil) != 0 else {
throw CryptoBoringWrapperError.internalBoringSSLError()
}
}
}
return basePoint
}
}

deinit {
CCryptoBoringSSL_EC_POINT_free(self._basePoint)
@usableFromInline
package convenience init(multiplying lhs: EllipticCurvePoint, by rhs: ArbitraryPrecisionInteger, on group: BoringSSLEllipticCurveGroup) throws {
try self.init(copying: lhs, on: group)
try self.multiply(by: rhs, on: group)
}

@usableFromInline
package func multiplying(by rhs: ArbitraryPrecisionInteger, on group: BoringSSLEllipticCurveGroup) throws -> EllipticCurvePoint {
try EllipticCurvePoint(multiplying: self, by: rhs, on: group)
}

@usableFromInline
package static func multiplying(_ lhs: EllipticCurvePoint, by rhs: ArbitraryPrecisionInteger, on group: BoringSSLEllipticCurveGroup) throws -> EllipticCurvePoint {
try EllipticCurvePoint(multiplying: lhs, by: rhs, on: group)
}

@usableFromInline
package func add(_ rhs: EllipticCurvePoint, on group: BoringSSLEllipticCurveGroup) throws {
try self.withPointPointer { selfPtr in
try group.withUnsafeGroupPointer { groupPtr in
try rhs.withPointPointer { rhsPtr in
guard CCryptoBoringSSL_EC_POINT_add(groupPtr, selfPtr, selfPtr, rhsPtr, nil) != 0 else {
throw CryptoBoringWrapperError.internalBoringSSLError()
}
}
}
}
}

@usableFromInline
package convenience init(adding lhs: EllipticCurvePoint, _ rhs: EllipticCurvePoint, on group: BoringSSLEllipticCurveGroup ) throws {
try self.init(copying: lhs, on: group)
try self.add(rhs, on: group)
}

@usableFromInline
package func adding(_ rhs: EllipticCurvePoint, on group: BoringSSLEllipticCurveGroup) throws -> EllipticCurvePoint {
try EllipticCurvePoint(adding: self, rhs, on: group)
}

@usableFromInline
package static func adding(_ lhs: EllipticCurvePoint, _ rhs: EllipticCurvePoint, on group: BoringSSLEllipticCurveGroup) throws -> EllipticCurvePoint {
try EllipticCurvePoint(adding: lhs, rhs, on: group)
}

@usableFromInline
package func invert(on group: BoringSSLEllipticCurveGroup) throws {
try self.withPointPointer { selfPtr in
try group.withUnsafeGroupPointer { groupPtr in
guard CCryptoBoringSSL_EC_POINT_invert(groupPtr, selfPtr, nil) != 0 else {
throw CryptoBoringWrapperError.internalBoringSSLError()
}
}
}
}

@usableFromInline
package convenience init(inverting point: EllipticCurvePoint, on group: BoringSSLEllipticCurveGroup) throws {
try self.init(copying: point, on: group)
try self.invert(on: group)
}

@usableFromInline
package func inverting(on group: BoringSSLEllipticCurveGroup) throws -> EllipticCurvePoint {
try EllipticCurvePoint(inverting: self, on: group)
}

@usableFromInline
package static func inverting(_ point: EllipticCurvePoint, on group: BoringSSLEllipticCurveGroup) throws -> EllipticCurvePoint {
try EllipticCurvePoint(inverting: point, on: group)
}

@usableFromInline
package func subtract(_ rhs: EllipticCurvePoint, on group: BoringSSLEllipticCurveGroup) throws {
try self.add(rhs.inverting(on: group), on: group)
}

@usableFromInline
package convenience init(subtracting rhs: EllipticCurvePoint, from lhs: EllipticCurvePoint, on group: BoringSSLEllipticCurveGroup) throws {
try self.init(copying: lhs, on: group)
try self.subtract(rhs, on: group)
}

@usableFromInline
package func subtracting(_ rhs: EllipticCurvePoint, on group: BoringSSLEllipticCurveGroup) throws -> EllipticCurvePoint {
try EllipticCurvePoint(subtracting: rhs, from: self, on: group)
}

@usableFromInline
package static func subtracting(_ rhs: EllipticCurvePoint, from lhs: EllipticCurvePoint, on group: BoringSSLEllipticCurveGroup) throws -> EllipticCurvePoint {
try EllipticCurvePoint(subtracting: rhs, from: lhs, on: group)
}

@usableFromInline
package convenience init<MessageBytes: ContiguousBytes, DSTBytes: ContiguousBytes>(hashing msg: MessageBytes, to group: BoringSSLEllipticCurveGroup, domainSeparationTag: DSTBytes) throws {
let hashToCurveFunction = switch group.curveName {
case .p256: CCryptoBoringSSLShims_EC_hash_to_curve_p256_xmd_sha256_sswu
case .p384: CCryptoBoringSSLShims_EC_hash_to_curve_p384_xmd_sha384_sswu
case .p521: throw CryptoBoringWrapperError.invalidParameter // BoringSSL doesn't have a hash_to_curve API for P521.
case .none: throw CryptoBoringWrapperError.internalBoringSSLError()
}

try self.init(_pointAtInfinityOn: group)
try msg.withUnsafeBytes { msgPtr in
try group.withUnsafeGroupPointer { groupPtr in
try domainSeparationTag.withUnsafeBytes { dstPtr in
guard hashToCurveFunction(
groupPtr,
self._basePoint,
dstPtr.baseAddress,
dstPtr.count,
msgPtr.baseAddress,
msgPtr.count
) == 1 else { throw CryptoBoringWrapperError.internalBoringSSLError() }
}
}
}
}

@usableFromInline
package func isEqual(to rhs: EllipticCurvePoint, on group: BoringSSLEllipticCurveGroup) -> Bool {
self.withPointPointer { selfPtr in
group.withUnsafeGroupPointer { groupPtr in
rhs.withPointPointer { rhsPtr in
switch CCryptoBoringSSL_EC_POINT_cmp(groupPtr, selfPtr, rhsPtr, nil) {
case 0: return true
case 1: return false
default:
// EC_POINT_cmp returns an error when comparing points on different groups.
// We treat that as not equal, so we'll just clear the error and return false.
CCryptoBoringSSL_ERR_clear_error()
return false
}
}
}
}
}

@usableFromInline
package convenience init<Bytes: ContiguousBytes>(x962Representation bytes: Bytes, on group: BoringSSLEllipticCurveGroup) throws {
try self.init(_pointAtInfinityOn: group)
guard group.withUnsafeGroupPointer({ groupPtr in
bytes.withUnsafeBytes { dataPtr in
CCryptoBoringSSL_EC_POINT_oct2point(
Lukasa marked this conversation as resolved.
Show resolved Hide resolved
groupPtr,
self._basePoint,
dataPtr.baseAddress,
dataPtr.count,
nil
)
}
}) == 1 else {
throw CryptoBoringWrapperError.invalidParameter
}
}

@usableFromInline
package func x962RepresentationByteCount(compressed: Bool, on group: BoringSSLEllipticCurveGroup) throws -> Int {
let numBytesNeeded = group.withUnsafeGroupPointer { groupPtr in
CCryptoBoringSSL_EC_POINT_point2oct(
groupPtr,
self._basePoint,
compressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED,
nil,
0,
nil
)
}
guard numBytesNeeded != 0 else {
throw CryptoBoringWrapperError.internalBoringSSLError()
}
return numBytesNeeded
}

@usableFromInline
package func x962Representation(compressed: Bool, on group: BoringSSLEllipticCurveGroup) throws -> Data {
let numBytesNeeded = try self.x962RepresentationByteCount(compressed: compressed, on: group)

var buf = Data(repeating: 0, count: numBytesNeeded)

let numBytesWritten = group.withUnsafeGroupPointer { groupPtr in
buf.withUnsafeMutableBytes { bufPtr in
CCryptoBoringSSLShims_EC_POINT_point2oct(
groupPtr,
self._basePoint,
compressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED,
bufPtr.baseAddress,
numBytesNeeded,
nil
)
}
}
guard numBytesWritten == numBytesNeeded else {
throw CryptoBoringWrapperError.internalBoringSSLError()
}

return buf
}
}

Expand Down
Loading
Loading