diff --git a/Sources/CryptoBoringWrapper/EC/EllipticCurvePoint.swift b/Sources/CryptoBoringWrapper/EC/EllipticCurvePoint.swift index 5ca515aa4..266886237 100644 --- a/Sources/CryptoBoringWrapper/EC/EllipticCurvePoint.swift +++ b/Sources/CryptoBoringWrapper/EC/EllipticCurvePoint.swift @@ -17,100 +17,55 @@ import protocol Foundation.ContiguousBytes import struct Foundation.Data -/// A wrapper around BoringSSL's EC_POINT with some lifetime management. +/// A wrapper around BoringSSL's EC_POINT with some lifetime management and value semantics. @usableFromInline @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) -package final class EllipticCurvePoint { - @usableFromInline var _basePoint: OpaquePointer - +package struct EllipticCurvePoint: @unchecked Sendable { @usableFromInline - package init(copying pointer: OpaquePointer, on group: BoringSSLEllipticCurveGroup) throws { - self._basePoint = try group.withUnsafeGroupPointer { groupPtr in - guard let pointPtr = CCryptoBoringSSL_EC_POINT_dup(pointer, groupPtr) else { - throw CryptoBoringWrapperError.internalBoringSSLError() - } - return pointPtr - } - } + var backing: Backing @usableFromInline - package convenience init( - copying other: EllipticCurvePoint, - on group: BoringSSLEllipticCurveGroup - ) - throws - { - try self.init(copying: other._basePoint, on: group) + package init(copying pointer: OpaquePointer, on group: BoringSSLEllipticCurveGroup) throws { + self.backing = try .init(copying: pointer, 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 - } + self.backing = try .init(_pointAtInfinityOn: group) } @usableFromInline package init(_generatorOf groupPtr: OpaquePointer) throws { - guard - let generatorPtr = CCryptoBoringSSL_EC_GROUP_get0_generator(groupPtr), - let pointPtr = CCryptoBoringSSL_EC_POINT_dup(generatorPtr, groupPtr) - else { - throw CryptoBoringWrapperError.internalBoringSSLError() - } - self._basePoint = pointPtr + self.backing = try .init(_generatorOf: groupPtr) } @usableFromInline - package convenience init( + package init( multiplying scalar: ArbitraryPrecisionInteger, on group: BoringSSLEllipticCurveGroup, context: FiniteFieldArithmeticContext? = nil ) throws { - try self.init(_pointAtInfinityOn: group) - try group.withUnsafeGroupPointer { groupPtr in - try scalar.withUnsafeBignumPointer { scalarPtr in - guard CCryptoBoringSSL_EC_POINT_mul(groupPtr, self._basePoint, scalarPtr, nil, nil, context?.bnCtx) == 1 - else { - throw CryptoBoringWrapperError.internalBoringSSLError() - } - } - } - } - - deinit { - CCryptoBoringSSL_EC_POINT_free(self._basePoint) + self.backing = try .init(multiplying: scalar, on: group, context: context) } @usableFromInline - package func multiply( + package mutating func multiply( by rhs: ArbitraryPrecisionInteger, on group: BoringSSLEllipticCurveGroup, context: FiniteFieldArithmeticContext? = nil ) 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, context?.bnCtx) != 0 - else { - throw CryptoBoringWrapperError.internalBoringSSLError() - } - } - } - } + try self.cowIfNeeded(on: group) + try self.backing.multiply(by: rhs, on: group, context: context) } @usableFromInline - package convenience init( + package init( multiplying lhs: EllipticCurvePoint, by rhs: ArbitraryPrecisionInteger, on group: BoringSSLEllipticCurveGroup, context: FiniteFieldArithmeticContext? = nil ) throws { - try self.init(copying: lhs, on: group) + self = lhs try self.multiply(by: rhs, on: group, context: context) } @@ -120,48 +75,38 @@ package final class EllipticCurvePoint { on group: BoringSSLEllipticCurveGroup, context: FiniteFieldArithmeticContext? = nil ) throws -> EllipticCurvePoint { - guard isKnownUniquelyReferenced(&self) else { - return try EllipticCurvePoint(multiplying: self, by: rhs, on: group, context: context) - } try self.multiply(by: rhs, on: group, context: context) return self } @usableFromInline package static func multiplying( - _ lhs: EllipticCurvePoint, + _ lhs: consuming EllipticCurvePoint, by rhs: ArbitraryPrecisionInteger, on group: BoringSSLEllipticCurveGroup, context: FiniteFieldArithmeticContext? = nil ) throws -> EllipticCurvePoint { - try EllipticCurvePoint(multiplying: lhs, by: rhs, on: group, context: context) + try lhs.multiplying(by: rhs, on: group, context: context) } @usableFromInline - package func add( + package mutating func add( _ rhs: EllipticCurvePoint, on group: BoringSSLEllipticCurveGroup, context: FiniteFieldArithmeticContext? = nil ) 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, context?.bnCtx) != 0 else { - throw CryptoBoringWrapperError.internalBoringSSLError() - } - } - } - } + try self.cowIfNeeded(on: group) + try self.backing.add(rhs, on: group, context: context) } @usableFromInline - package convenience init( + package init( adding lhs: EllipticCurvePoint, _ rhs: EllipticCurvePoint, on group: BoringSSLEllipticCurveGroup, context: FiniteFieldArithmeticContext? = nil ) throws { - try self.init(copying: lhs, on: group) + self = lhs try self.add(rhs, on: group, context: context) } @@ -171,78 +116,77 @@ package final class EllipticCurvePoint { on group: BoringSSLEllipticCurveGroup, context: FiniteFieldArithmeticContext? = nil ) throws -> EllipticCurvePoint { - guard isKnownUniquelyReferenced(&self) else { - return try EllipticCurvePoint(adding: self, rhs, on: group, context: context) - } try self.add(rhs, on: group, context: context) return self } @usableFromInline package static func adding( - _ lhs: EllipticCurvePoint, + _ lhs: consuming EllipticCurvePoint, _ rhs: EllipticCurvePoint, on group: BoringSSLEllipticCurveGroup, context: FiniteFieldArithmeticContext? = nil ) throws -> EllipticCurvePoint { - try EllipticCurvePoint(adding: lhs, rhs, on: group, context: context) + try lhs.add(rhs, on: group, context: context) + return lhs } @usableFromInline - package func invert(on group: BoringSSLEllipticCurveGroup, context: FiniteFieldArithmeticContext? = nil) throws { - try self.withPointPointer { selfPtr in - try group.withUnsafeGroupPointer { groupPtr in - guard CCryptoBoringSSL_EC_POINT_invert(groupPtr, selfPtr, context?.bnCtx) != 0 else { - throw CryptoBoringWrapperError.internalBoringSSLError() - } - } - } + package mutating func invert( + on group: BoringSSLEllipticCurveGroup, + context: FiniteFieldArithmeticContext? = nil + ) throws { + try self.cowIfNeeded(on: group) + try self.backing.invert(on: group, context: context) } @usableFromInline - package convenience init( + package init( inverting point: EllipticCurvePoint, on group: BoringSSLEllipticCurveGroup, context: FiniteFieldArithmeticContext? = nil ) throws { - try self.init(copying: point, on: group) + self = point try self.invert(on: group, context: context) } @usableFromInline - package func inverting( + package consuming func inverting( on group: BoringSSLEllipticCurveGroup, context: FiniteFieldArithmeticContext? = nil ) throws -> EllipticCurvePoint { - try EllipticCurvePoint(inverting: self, on: group, context: context) + try self.invert(on: group, context: context) + return self } @usableFromInline package static func inverting( - _ point: EllipticCurvePoint, + _ point: consuming EllipticCurvePoint, on group: BoringSSLEllipticCurveGroup, context: FiniteFieldArithmeticContext? = nil ) throws -> EllipticCurvePoint { - try EllipticCurvePoint(inverting: point, on: group, context: context) + try point.invert(on: group, context: context) + return point } @usableFromInline - package func subtract( - _ rhs: EllipticCurvePoint, + package mutating func subtract( + _ rhs: consuming EllipticCurvePoint, on group: BoringSSLEllipticCurveGroup, context: FiniteFieldArithmeticContext? = nil ) throws { + try self.cowIfNeeded(on: group) try self.add(rhs.inverting(on: group), on: group, context: context) } @usableFromInline - package convenience init( - subtracting rhs: EllipticCurvePoint, - from lhs: EllipticCurvePoint, + package init( + subtracting rhs: consuming EllipticCurvePoint, + from lhs: consuming EllipticCurvePoint, on group: BoringSSLEllipticCurveGroup, context: FiniteFieldArithmeticContext? = nil ) throws { - try self.init(copying: lhs, on: group) + self = lhs try self.subtract(rhs, on: group, context: context) } @@ -252,54 +196,28 @@ package final class EllipticCurvePoint { on group: BoringSSLEllipticCurveGroup, context: FiniteFieldArithmeticContext? = nil ) throws -> EllipticCurvePoint { - guard isKnownUniquelyReferenced(&self) else { - return try EllipticCurvePoint(subtracting: rhs, from: self, on: group, context: context) - } try self.subtract(rhs, on: group, context: context) return self } @usableFromInline package static func subtracting( - _ rhs: EllipticCurvePoint, - from lhs: EllipticCurvePoint, + _ rhs: consuming EllipticCurvePoint, + from lhs: consuming EllipticCurvePoint, on group: BoringSSLEllipticCurveGroup, context: FiniteFieldArithmeticContext? = nil ) throws -> EllipticCurvePoint { - try EllipticCurvePoint(subtracting: rhs, from: lhs, on: group, context: context) + try lhs.subtract(rhs, on: group, context: context) + return lhs } @usableFromInline - package convenience init( + package init( 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 has no P521 hash_to_curve API. - 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() } - } - } - } + self.backing = try .init(hashing: msg, to: group, domainSeparationTag: domainSeparationTag) } @usableFromInline @@ -308,110 +226,289 @@ package final class EllipticCurvePoint { on group: BoringSSLEllipticCurveGroup, context: FiniteFieldArithmeticContext? = nil ) -> Bool { - self.withPointPointer { selfPtr in - group.withUnsafeGroupPointer { groupPtr in - rhs.withPointPointer { rhsPtr in - switch CCryptoBoringSSL_EC_POINT_cmp(groupPtr, selfPtr, rhsPtr, context?.bnCtx) { - 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 - } - } - } - } + self.backing.isEqual(to: rhs, on: group, context: context) } @usableFromInline - package convenience init( + package init( x962Representation bytes: Bytes, on group: BoringSSLEllipticCurveGroup, context: FiniteFieldArithmeticContext? = nil ) throws { - try self.init(_pointAtInfinityOn: group) - guard - group.withUnsafeGroupPointer({ groupPtr in - bytes.withUnsafeBytes { dataPtr in - CCryptoBoringSSL_EC_POINT_oct2point( - groupPtr, - self._basePoint, - dataPtr.baseAddress, - dataPtr.count, - context?.bnCtx - ) - } - }) == 1 - else { - throw CryptoBoringWrapperError.invalidParameter - } + self.backing = try .init(x962Representation: bytes, on: group, context: context) } @usableFromInline - package func x962RepresentationByteCount( + package func x962Representation( compressed: Bool, on group: BoringSSLEllipticCurveGroup, context: FiniteFieldArithmeticContext? = nil - ) throws -> Int { - let numBytesNeeded = group.withUnsafeGroupPointer { groupPtr in - CCryptoBoringSSL_EC_POINT_point2oct( - groupPtr, - self._basePoint, - compressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED, - nil, - 0, - context?.bnCtx - ) - } - guard numBytesNeeded != 0 else { - throw CryptoBoringWrapperError.internalBoringSSLError() + ) throws -> Data { + try self.backing.x962Representation(compressed: compressed, on: group, context: context) + } + + private mutating func cowIfNeeded(on group: BoringSSLEllipticCurveGroup) throws { + if !isKnownUniquelyReferenced(&self.backing) { + self.backing = try .init(copying: self.backing, on: group) } - return numBytesNeeded } +} +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) +extension EllipticCurvePoint { @usableFromInline - package func x962Representation( - compressed: Bool, - on group: BoringSSLEllipticCurveGroup, - context: FiniteFieldArithmeticContext? = nil - ) throws -> Data { - let numBytesNeeded = try self.x962RepresentationByteCount(compressed: compressed, on: group, context: context) + final class Backing { + @usableFromInline + let _basePoint: OpaquePointer - var buf = Data(repeating: 0, count: numBytesNeeded) + fileprivate init(copying pointer: OpaquePointer, on group: BoringSSLEllipticCurveGroup) throws { + self._basePoint = try group.withUnsafeGroupPointer { groupPtr in + guard let pointPtr = CCryptoBoringSSL_EC_POINT_dup(pointer, groupPtr) else { + throw CryptoBoringWrapperError.internalBoringSSLError() + } + return pointPtr + } + } - let numBytesWritten = group.withUnsafeGroupPointer { groupPtr in - buf.withUnsafeMutableBytes { bufPtr in - CCryptoBoringSSLShims_EC_POINT_point2oct( + fileprivate convenience init( + copying other: Backing, + on group: BoringSSLEllipticCurveGroup + ) + throws + { + try self.init(copying: other._basePoint, on: group) + } + + fileprivate 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 + } + } + + fileprivate init(_generatorOf groupPtr: OpaquePointer) throws { + guard + let generatorPtr = CCryptoBoringSSL_EC_GROUP_get0_generator(groupPtr), + let pointPtr = CCryptoBoringSSL_EC_POINT_dup(generatorPtr, groupPtr) + else { + throw CryptoBoringWrapperError.internalBoringSSLError() + } + self._basePoint = pointPtr + } + + fileprivate convenience init( + multiplying scalar: ArbitraryPrecisionInteger, + on group: BoringSSLEllipticCurveGroup, + context: FiniteFieldArithmeticContext? = nil + ) throws { + try self.init(_pointAtInfinityOn: group) + try group.withUnsafeGroupPointer { groupPtr in + try scalar.withUnsafeBignumPointer { scalarPtr in + guard + CCryptoBoringSSL_EC_POINT_mul(groupPtr, self._basePoint, scalarPtr, nil, nil, context?.bnCtx) + == 1 + else { + throw CryptoBoringWrapperError.internalBoringSSLError() + } + } + } + } + + deinit { + CCryptoBoringSSL_EC_POINT_free(self._basePoint) + } + + fileprivate func multiply( + by rhs: ArbitraryPrecisionInteger, + on group: BoringSSLEllipticCurveGroup, + context: FiniteFieldArithmeticContext? = nil + ) 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, context?.bnCtx) != 0 + else { + throw CryptoBoringWrapperError.internalBoringSSLError() + } + } + } + } + } + + fileprivate func add( + _ rhs: EllipticCurvePoint, + on group: BoringSSLEllipticCurveGroup, + context: FiniteFieldArithmeticContext? = nil + ) 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, context?.bnCtx) != 0 + else { + throw CryptoBoringWrapperError.internalBoringSSLError() + } + } + } + } + } + + internal func invert(on group: BoringSSLEllipticCurveGroup, context: FiniteFieldArithmeticContext? = nil) throws + { + try self.withPointPointer { selfPtr in + try group.withUnsafeGroupPointer { groupPtr in + guard CCryptoBoringSSL_EC_POINT_invert(groupPtr, selfPtr, context?.bnCtx) != 0 else { + throw CryptoBoringWrapperError.internalBoringSSLError() + } + } + } + } + + fileprivate convenience init( + 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 + // BoringSSL has no P521 hash_to_curve API. + case .p521: throw CryptoBoringWrapperError.invalidParameter + 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() } + } + } + } + } + + fileprivate func isEqual( + to rhs: EllipticCurvePoint, + on group: BoringSSLEllipticCurveGroup, + context: FiniteFieldArithmeticContext? = nil + ) -> Bool { + self.withPointPointer { selfPtr in + group.withUnsafeGroupPointer { groupPtr in + rhs.withPointPointer { rhsPtr in + switch CCryptoBoringSSL_EC_POINT_cmp(groupPtr, selfPtr, rhsPtr, context?.bnCtx) { + 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 + } + } + } + } + } + + fileprivate convenience init( + x962Representation bytes: Bytes, + on group: BoringSSLEllipticCurveGroup, + context: FiniteFieldArithmeticContext? = nil + ) throws { + try self.init(_pointAtInfinityOn: group) + guard + group.withUnsafeGroupPointer({ groupPtr in + bytes.withUnsafeBytes { dataPtr in + CCryptoBoringSSL_EC_POINT_oct2point( + groupPtr, + self._basePoint, + dataPtr.baseAddress, + dataPtr.count, + context?.bnCtx + ) + } + }) == 1 + else { + throw CryptoBoringWrapperError.invalidParameter + } + } + + private func x962RepresentationByteCount( + compressed: Bool, + on group: BoringSSLEllipticCurveGroup, + context: FiniteFieldArithmeticContext? = nil + ) throws -> Int { + let numBytesNeeded = group.withUnsafeGroupPointer { groupPtr in + CCryptoBoringSSL_EC_POINT_point2oct( groupPtr, self._basePoint, compressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED, - bufPtr.baseAddress, - numBytesNeeded, + nil, + 0, context?.bnCtx ) } + guard numBytesNeeded != 0 else { + throw CryptoBoringWrapperError.internalBoringSSLError() + } + return numBytesNeeded } - guard numBytesWritten == numBytesNeeded else { - throw CryptoBoringWrapperError.internalBoringSSLError() - } - return buf + fileprivate func x962Representation( + compressed: Bool, + on group: BoringSSLEllipticCurveGroup, + context: FiniteFieldArithmeticContext? = nil + ) throws -> Data { + let numBytesNeeded = try self.x962RepresentationByteCount( + compressed: compressed, + on: group, + context: context + ) + + 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, + context?.bnCtx + ) + } + } + guard numBytesWritten == numBytesNeeded else { + throw CryptoBoringWrapperError.internalBoringSSLError() + } + + return buf + } } } // MARK: - Helpers @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) -extension EllipticCurvePoint { +extension EllipticCurvePoint.Backing { @inlinable package func withPointPointer(_ body: (OpaquePointer) throws -> T) rethrows -> T { try body(self._basePoint) } - @usableFromInline - package func affineCoordinates( + fileprivate func affineCoordinates( group: BoringSSLEllipticCurveGroup ) throws -> ( x: ArbitraryPrecisionInteger, y: ArbitraryPrecisionInteger @@ -440,3 +537,20 @@ extension EllipticCurvePoint { return (x: x, y: y) } } + +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) +extension EllipticCurvePoint { + @inlinable + package func withPointPointer(_ body: (OpaquePointer) throws -> T) rethrows -> T { + try self.backing.withPointPointer(body) + } + + @usableFromInline + package func affineCoordinates( + group: BoringSSLEllipticCurveGroup + ) throws -> ( + x: ArbitraryPrecisionInteger, y: ArbitraryPrecisionInteger + ) { + try self.backing.affineCoordinates(group: group) + } +} diff --git a/Sources/CryptoBoringWrapper/Util/ArbitraryPrecisionInteger.swift b/Sources/CryptoBoringWrapper/Util/ArbitraryPrecisionInteger.swift index 41f53a9fb..e0eee9fff 100644 --- a/Sources/CryptoBoringWrapper/Util/ArbitraryPrecisionInteger.swift +++ b/Sources/CryptoBoringWrapper/Util/ArbitraryPrecisionInteger.swift @@ -22,7 +22,7 @@ import Foundation /// and that provides better Swift types for this object. @usableFromInline @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) -package struct ArbitraryPrecisionInteger { +package struct ArbitraryPrecisionInteger: @unchecked Sendable { private var _backing: BackingStorage @usableFromInline diff --git a/Tests/CryptoBoringWrapperTests/EllipticCurvePointTests.swift b/Tests/CryptoBoringWrapperTests/EllipticCurvePointTests.swift new file mode 100644 index 000000000..4c08fd9a9 --- /dev/null +++ b/Tests/CryptoBoringWrapperTests/EllipticCurvePointTests.swift @@ -0,0 +1,60 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2025 Apple Inc. and the SwiftCrypto project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftCrypto project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import CryptoBoringWrapper +import XCTest + +final class EllipticCurvePointTests: XCTestCase { + static let p256 = try! BoringSSLEllipticCurveGroup(.p256) + + func testRepeatedMultiplyHasValueSemantics() throws { + let point = Self.p256.generator + var copy = point + try copy.multiply(by: 2, on: Self.p256) + try copy.multiply(by: 2, on: Self.p256) + + XCTAssertTrue(!copy.isEqual(to: point, on: Self.p256)) + XCTAssertTrue(try copy.isEqual(to: point.multiplying(by: 4, on: Self.p256), on: Self.p256)) + } + + func testAddHasValueSemantics() throws { + let point = Self.p256.generator + var other = point + try other.add(point, on: Self.p256) + + XCTAssertTrue(!other.isEqual(to: point, on: Self.p256)) + XCTAssertTrue(try other.isEqual(to: point.adding(point, on: Self.p256), on: Self.p256)) + } + + func testInvertingHasValueSemantics() throws { + let point = try Self.p256.generator.multiplying( + by: 2, + on: Self.p256 + ) + var other = point + try other.invert(on: Self.p256) + + XCTAssertTrue(!other.isEqual(to: point, on: Self.p256)) + XCTAssertTrue(try other.isEqual(to: point.inverting(on: Self.p256), on: Self.p256)) + } + + func testSubtractHasValueSemantics() throws { + let point = Self.p256.generator + var other = point + try other.subtract(point, on: Self.p256) + + XCTAssertTrue(!other.isEqual(to: point, on: Self.p256)) + XCTAssertTrue(try other.isEqual(to: point.subtracting(point, on: Self.p256), on: Self.p256)) + } +}