-
Notifications
You must be signed in to change notification settings - Fork 168
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
Changes from 9 commits
4e82c4b
170b62d
2719c48
47b558b
8e7607c
107f879
aa6c637
0807220
fda1a2c
857b1a9
4534eaf
65325b4
24119bd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,41 +12,262 @@ | |
// | ||
//===----------------------------------------------------------------------===// | ||
@_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(multiplying scalar: ArbitraryPrecisionInteger, on group: BoringSSLEllipticCurveGroup) throws { | ||
self._basePoint = try scalar.withUnsafeBignumPointer { scalarPtr in | ||
try group.withUnsafeGroupPointer { groupPtr in | ||
guard | ||
let pointPtr = CCryptoBoringSSL_EC_POINT_new(groupPtr), | ||
CCryptoBoringSSL_EC_POINT_mul(groupPtr, pointPtr, scalarPtr, nil, nil, nil) == 1 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not a new issue, but this leaks the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've addressed this by factoring out the |
||
else { throw CryptoBoringWrapperError.internalBoringSSLError() } | ||
return pointPtr | ||
} | ||
} | ||
} | ||
|
||
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() | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
try group.withUnsafeGroupPointer { groupPtr in | ||
try scalar.withUnsafeBignumPointer { bigNumPtr in | ||
guard CCryptoBoringSSL_EC_POINT_mul(groupPtr, self._basePoint, bigNumPtr, nil, nil, nil) != 0 else { | ||
@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() | ||
} | ||
} | ||
} | ||
} | ||
|
||
package init(copying pointer: OpaquePointer, on group: BoringSSLEllipticCurveGroup) throws { | ||
@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 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() | ||
} | ||
|
||
self._basePoint = try msg.withUnsafeBytes { msgPtr in | ||
try group.withUnsafeGroupPointer { groupPtr in | ||
try domainSeparationTag.withUnsafeBytes { dstPtr in | ||
guard | ||
let pointPtr = CCryptoBoringSSL_EC_POINT_new(groupPtr), | ||
hashToCurveFunction( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this fails, we leak the point. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Resolved in the same manner as https://github.com/apple/swift-crypto/pull/292/files/fda1a2c6df294e87b24b77ad28a7cd5950d74d25#r1850181741. |
||
groupPtr, | ||
pointPtr, | ||
dstPtr.baseAddress, | ||
dstPtr.count, | ||
msgPtr.baseAddress, | ||
msgPtr.count | ||
) == 1 | ||
else { throw CryptoBoringWrapperError.internalBoringSSLError() } | ||
return pointPtr | ||
} | ||
} | ||
} | ||
} | ||
|
||
@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: fatalError("Unexpected error testing EC point equality") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know that we need to be this strict. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that's not quite right since the docs say that
I think this can happen if you provide a point on a different group for an equality test, for instance. This function isn't throwing so the best we can do is fatal error, which seems to be in line with what we do elsewhere? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure we need the error to be this loud, though. Perhaps we drop this to an assertion and then return There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After further consideration, given we allow folks to pass in the I've updated the code to clear the BoringSSL error and return |
||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
@usableFromInline | ||
package init<Bytes: ContiguousBytes>(x962Representation bytes: Bytes, on group: BoringSSLEllipticCurveGroup) throws { | ||
self._basePoint = try group.withUnsafeGroupPointer { groupPtr in | ||
guard let basePoint = CCryptoBoringSSL_EC_POINT_dup(pointer, groupPtr) else { | ||
guard let basePoint = CCryptoBoringSSL_EC_POINT_new(groupPtr) else { | ||
throw CryptoBoringWrapperError.internalBoringSSLError() | ||
} | ||
return basePoint | ||
} | ||
|
||
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 | ||
} | ||
} | ||
|
||
deinit { | ||
CCryptoBoringSSL_EC_POINT_free(self._basePoint) | ||
@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 | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be worth not squishing this in,
get throws {
can be on its own line.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I moved this onto its own line.
Aside: I have a task to add the swift-format job to this repo soon, which will squash all these inconsistencies away.