diff --git a/Package.swift b/Package.swift index 16891dcf..774329d1 100644 --- a/Package.swift +++ b/Package.swift @@ -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 diff --git a/Sources/CCryptoBoringSSLShims/include/CCryptoBoringSSLShims.h b/Sources/CCryptoBoringSSLShims/include/CCryptoBoringSSLShims.h index 2af4f11d..c99ff507 100644 --- a/Sources/CCryptoBoringSSLShims/include/CCryptoBoringSSLShims.h +++ b/Sources/CCryptoBoringSSLShims/include/CCryptoBoringSSLShims.h @@ -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) diff --git a/Sources/CCryptoBoringSSLShims/shims.c b/Sources/CCryptoBoringSSLShims/shims.c index 71d0025f..df8178c4 100644 --- a/Sources/CCryptoBoringSSLShims/shims.c +++ b/Sources/CCryptoBoringSSLShims/shims.c @@ -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); +} diff --git a/Sources/CryptoBoringWrapper/EC/EllipticCurve.swift b/Sources/CryptoBoringWrapper/EC/EllipticCurve.swift index fb204d43..cf223ee7 100644 --- a/Sources/CryptoBoringWrapper/EC/EllipticCurve.swift +++ b/Sources/CryptoBoringWrapper/EC/EllipticCurve.swift @@ -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 @@ -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 @@ -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 { diff --git a/Sources/CryptoBoringWrapper/EC/EllipticCurvePoint.swift b/Sources/CryptoBoringWrapper/EC/EllipticCurvePoint.swift index e97f8806..25444f8a 100644 --- a/Sources/CryptoBoringWrapper/EC/EllipticCurvePoint.swift +++ b/Sources/CryptoBoringWrapper/EC/EllipticCurvePoint.swift @@ -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(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(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( + 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 } } diff --git a/Sources/_CryptoExtras/CMakeLists.txt b/Sources/_CryptoExtras/CMakeLists.txt index b2d05631..1229d508 100644 --- a/Sources/_CryptoExtras/CMakeLists.txt +++ b/Sources/_CryptoExtras/CMakeLists.txt @@ -15,12 +15,21 @@ add_library(_CryptoExtras "ChaCha20CTR/BoringSSL/ChaCha20CTR_boring.swift" "ChaCha20CTR/ChaCha20CTR.swift" + "ECToolbox/BoringSSL/ECToolbox_boring.swift" + "ECToolbox/ECToolbox.swift" + "H2G/HashToField.swift" "Key Derivation/KDF.swift" "Key Derivation/PBKDF2/BoringSSL/PBKDF2_boring.swift" "Key Derivation/PBKDF2/BoringSSL/PBKDF2_commoncrypto.swift" "Key Derivation/PBKDF2/PBKDF2.swift" "Key Derivation/Scrypt/BoringSSL/Scrypt_boring.swift" "Key Derivation/Scrypt/Scrypt.swift" + "OPRFs/OPRF.swift" + "OPRFs/OPRFClient.swift" + "OPRFs/OPRFServer.swift" + "OPRFs/VOPRF+API.swift" + "OPRFs/VOPRFClient.swift" + "OPRFs/VOPRFServer.swift" "RSA/RSA+BlindSigning.swift" "RSA/RSA.swift" "RSA/RSA_boring.swift" @@ -29,8 +38,11 @@ add_library(_CryptoExtras "Util/CryptoKitErrors_boring.swift" "Util/DigestType.swift" "Util/Error.swift" + "Util/I2OSP.swift" "Util/PEMDocument.swift" - "Util/SubjectPublicKeyInfo.swift") + "Util/PrettyBytes.swift" + "Util/SubjectPublicKeyInfo.swift" + "ZKPs/DLEQ.swift") target_include_directories(_CryptoExtras PRIVATE $ diff --git a/Sources/_CryptoExtras/ECToolbox/BoringSSL/ECToolbox_boring.swift b/Sources/_CryptoExtras/ECToolbox/BoringSSL/ECToolbox_boring.swift new file mode 100644 index 00000000..9326a49d --- /dev/null +++ b/Sources/_CryptoExtras/ECToolbox/BoringSSL/ECToolbox_boring.swift @@ -0,0 +1,276 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2024 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 Crypto +import CryptoBoringWrapper +import Foundation + +/// NOTE: This protocol is different from `Crypto.OpenSSLSupportedNISTCurve` module and has additional requirements to +/// support ECToolbox. It is (re-)defined here because its counterpart in the Crypto module is only conditionally +/// compiled on _non-Darwin_ platforms, but we implement ECToolbox on both Darwin and non-Darwin platforms. +@usableFromInline +protocol OpenSSLSupportedNISTCurve { + associatedtype H: HashFunction + + @inlinable + static var group: BoringSSLEllipticCurveGroup { get } + + // TODO: we could use EC_GROUP_get_cofactor for this and drop this requirement. + @inlinable + static var cofactor: Int { get } + + @inlinable + static var orderByteCount: Int { get } + + @inlinable + static var compressedx962PointByteCount: Int { get } + + // TODO: could this be moved to the group or to the HashFunction? + @inlinable + static var hashToFieldByteCount: Int { get } +} + +/// NOTE: This conformance applies to this type from the Crypto module even if it comes from the SDK. +extension P256: OpenSSLSupportedNISTCurve { + @usableFromInline + typealias H = SHA256 + + @inlinable + static var group: BoringSSLEllipticCurveGroup { try! BoringSSLEllipticCurveGroup(.p256) } + + @inlinable + static var cofactor: Int { 1 } + + @inlinable + static var orderByteCount: Int { 32 } + + @inlinable + static var compressedx962PointByteCount: Int { 33 } + + @inlinable + static var hashToFieldByteCount: Int { 48 } +} + +/// NOTE: This conformance applies to this type from the Crypto module even if it comes from the SDK. +extension P384: OpenSSLSupportedNISTCurve { + @usableFromInline + typealias H = SHA384 + + @inlinable + static var group: BoringSSLEllipticCurveGroup { try! BoringSSLEllipticCurveGroup(.p384) } + + @inlinable + static var cofactor: Int { 1 } + + @inlinable + static var orderByteCount: Int { 48 } + + @inlinable + static var compressedx962PointByteCount: Int { 49 } + + @inlinable + static var hashToFieldByteCount: Int { 72 } +} + +/// NOTE: This conformance applies to this type from the Crypto module even if it comes from the SDK. +extension P521: OpenSSLSupportedNISTCurve { + @usableFromInline + typealias H = SHA512 + + @inlinable + static var group: BoringSSLEllipticCurveGroup { try! BoringSSLEllipticCurveGroup(.p521) } + + @inlinable + static var cofactor: Int { 1 } + + @inlinable + static var orderByteCount: Int { 66 } + + @inlinable + static var compressedx962PointByteCount: Int { 67 } + + @inlinable + static var hashToFieldByteCount: Int { 98 } +} + +struct OpenSSLGroupScalar: GroupScalar, CustomStringConvertible { + var openSSLScalar: ArbitraryPrecisionInteger + + init(_ openSSLScalar: ArbitraryPrecisionInteger) { + self.openSSLScalar = openSSLScalar + } + + /// Deserializes a scalar from data. + /// - Parameters: + /// - data: The serialized scalar + /// - reductionIsModOrder: Resulting number is taken "mod q" (characteristic) by default. Override by setting true if "mod p" (order) is desired. + /// - Returns: The deserialized scalar + init(bytes: Data, reductionIsModOrder: Bool = false) throws { + if reductionIsModOrder { + self.init(try ArbitraryPrecisionInteger(bytes: bytes).modulo(C.group.weierstrassCoefficients.field)) + } else { + self.init(try ArbitraryPrecisionInteger(bytes: bytes).modulo(C.group.order)) + } + } + + static var random: Self { + // Force-try: Protocol requires non-throwing and this can only throw if bounds are invalid. + try! Self(.random(inclusiveMin: 0, exclusiveMax: C.group.order)) + } + + static func + (left: Self, right: Self) -> Self { + // Force-try: Protocol requires non-throwing and this can only throw if modulus is invalid. + try! Self(left.openSSLScalar.add(right.openSSLScalar, modulo: C.group.order)) + } + + static func - (left: Self, right: Self) -> Self { + // Force-try: Protocol requires non-throwing and this can only throw if modulus is invalid. + try! Self(left.openSSLScalar.sub(right.openSSLScalar, modulo: C.group.order)) + } + + static func ^ (left: Self, right: Int) -> Self { + precondition(right == -1, "Unimplemented arbitrary exponentiation") + // Force-try: Protocol requires non-throwing and this can only throw if modulus is invalid. + return try! Self(left.openSSLScalar.inverse(modulo: C.group.order)) + } + + static func * (left: Self, right: Self) -> Self { + // Force-try: Protocol requires non-throwing and this can only throw if modulus is invalid. + try! Self(left.openSSLScalar.mul(right.openSSLScalar, modulo: C.group.order)) + } + + static prefix func - (left: Self) -> Self { + // Force-try: Protocol requires non-throwing and this can only throw if modulus is invalid. + try! Self(.zero.sub(left.openSSLScalar, modulo: C.group.order)) + } + + static func == (left: Self, right: Self) -> Bool { + left.openSSLScalar == right.openSSLScalar + } + + var rawRepresentation: Data { + // Force-try: This can only throw if the requested size is not big enough to represent the point. + try! Data(bytesOf: self.openSSLScalar, paddedToSize: C.orderByteCount) + } + + var description: String { + self.rawRepresentation.hexString + } +} + +struct OpenSSLCurvePoint: GroupElement { + var ecPoint: EllipticCurvePoint + typealias Scalar = OpenSSLGroupScalar + + init(ecPoint: EllipticCurvePoint) { + self.ecPoint = ecPoint + } + + static var generator: Self { + // Force-try: Protocol requires non-throwing and this can only throw if group has no generator. + // TODO: `BoringSSLEllipticCurveGroup.generator` should probably be non-throwing, like `.order`. + try! Self(ecPoint: C.group.generator) + } + + static var random: Self { + let randomBytes = SystemRandomNumberGenerator.randomBytes(count: C.group.order.byteCount) + let dst = Data("Random EC Point Generation".utf8) + // Force-try: Protocol requires non-throwing and this can only throw if called with the wrong group. + let point = try! EllipticCurvePoint(hashing: randomBytes, to: C.group, domainSeparationTag: dst) + return Self(ecPoint: point) + } + + static func + (left: Self, right: Self) -> Self { + // Force-try: Protocol requires non-throwing. + try! Self(ecPoint: left.ecPoint.adding(right.ecPoint, on: C.group)) + } + + static func - (left: Self, right: Self) -> Self { + // Force-try: Protocol requires non-throwing. + try! Self(ecPoint: left.ecPoint.subtracting(right.ecPoint, on: C.group)) + } + + static prefix func - (left: Self) -> Self { + // Force-try: Protocol requires non-throwing. + try! Self(ecPoint: left.ecPoint.inverting(on: C.group)) + } + + static func * (left: Scalar, right: Self) -> Self { + // Force-try: Protocol requires non-throwing. + try! Self(ecPoint: .multiplying(right.ecPoint, by: left.openSSLScalar, on: C.group)) + } + + static func == (left: Self, right: Self) -> Bool { + left.ecPoint.isEqual(to: right.ecPoint, on: C.group) + } +} + +extension OpenSSLCurvePoint { + var compressedRepresentation: Data { + try! self.ecPoint.x962Representation(compressed: true, on: C.group) + } +} + +extension OpenSSLCurvePoint: OPRFGroupElement { + init(oprfRepresentation data: Data) throws { + let point = try EllipticCurvePoint(x962Representation: data, on: C.group) + self.init(ecPoint: point) + } + + var oprfRepresentation: Data { self.compressedRepresentation } +} + +struct OpenSSLGroup: Group { + typealias Element = OpenSSLCurvePoint + + static var cofactor: Int { + // NOTE: Practically, this is always 1, because this type is only generic over our NIST curves. + C.cofactor + } +} + +@available(macOS 10.15, iOS 13.2, tvOS 13.2, watchOS 6.1, *) +struct OpenSSLHashToCurve: HashToGroup { + typealias H = C.H + typealias GE = OpenSSLCurvePoint + typealias G = OpenSSLGroup + + static func hashToScalar(_ data: Data, domainSeparationString: Data) throws -> GE.Scalar { + // Force-unwrap: HashToField is guaranteed to produce one or more elements, so .first is always non-nil. + return try HashToField.hashToField( + data, + outputElementCount: 1, + dst: Data("HashToScalar-".utf8) + domainSeparationString, + outputSize: C.hashToFieldByteCount, + reductionIsModOrder: false + ).first! + } + + static func hashToGroup(_ data: Data, domainSeparationString: Data) -> GE { + precondition(G.cofactor == 1, "H2C doesn't have support for clearing co-factors.") + precondition(!domainSeparationString.isEmpty, "DST must be non-empty.") + switch C.self { + case is P256.Type: + let point = try! EllipticCurvePoint(hashing: data, to: P256.group, domainSeparationTag: domainSeparationString) + return OpenSSLCurvePoint(ecPoint: point) + case is P384.Type: + let point = try! EllipticCurvePoint(hashing: data, to: P384.group, domainSeparationTag: domainSeparationString) + return OpenSSLCurvePoint(ecPoint: point) + case is P521.Type: + // BoringSSL doesn't have implementation of P521_XMD:SHA-512_SSWU_RO_. + fatalError("HashToGroup not supported for type: \(C.self).") + default: + fatalError("HashToGroup not supported for type: \(C.self).") + } + } +} diff --git a/Sources/_CryptoExtras/ECToolbox/ECToolbox.swift b/Sources/_CryptoExtras/ECToolbox/ECToolbox.swift new file mode 100644 index 00000000..8d4366a6 --- /dev/null +++ b/Sources/_CryptoExtras/ECToolbox/ECToolbox.swift @@ -0,0 +1,88 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2024 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 Crypto +import Foundation + +#if canImport(Darwin) && !CRYPTO_IN_SWIFTPM +typealias SupportedCurveDetailsImpl = CorecryptoSupportedNISTCurve +typealias GroupImpl = CoreCryptoGroup +typealias HashToCurveImpl = CoreCryptoHashToCurve +#else +typealias SupportedCurveDetailsImpl = OpenSSLSupportedNISTCurve +typealias GroupImpl = OpenSSLGroup +@available(macOS 10.15, iOS 13.2, tvOS 13.2, watchOS 6.1, *) +typealias HashToCurveImpl = OpenSSLHashToCurve +#endif + +/// A prime-order group +protocol Group { + /// Group element + associatedtype Element: GroupElement + + /// Group scalar (mod p) where p is the order of the group + typealias Scalar = Element.Scalar + + /// Cofactor of the group + static var cofactor: Int { get } +} + +protocol HashToGroup { + associatedtype H: HashFunction + associatedtype G: Group where G.Element: OPRFGroupElement + + static func hashToScalar(_ data: Data, domainSeparationString: Data) throws -> G.Scalar + static func hashToGroup(_ data: Data, domainSeparationString: Data) -> G.Element +} + +protocol GroupScalar { + init(bytes: Data, reductionIsModOrder: Bool) throws + + var rawRepresentation: Data { get } + + // Generates a Random Scalar Element + static var random: Self { get } + + static func + (left: Self, right: Self) -> Self + + static func - (left: Self, right: Self) -> Self + + static func ^ (left: Self, right: Int) -> Self + + static func * (left: Self, right: Self) -> Self + + static prefix func - (left: Self) -> Self + + // Constant-time Comparison + static func == (left: Self, right: Self) -> Bool +} + +protocol GroupElement { + associatedtype Scalar: GroupScalar + + static var generator: Self { get } + + // Generates a Random Group Element + static var random: Self { get } + + static func + (left: Self, right: Self) -> Self + + static func - (left: Self, right: Self) -> Self + + static prefix func - (left: Self) -> Self + + // Group Point Multiplication + static func * (left: Scalar, right: Self) -> Self + // Constant-time Comparison + static func == (left: Self, right: Self) -> Bool +} diff --git a/Sources/_CryptoExtras/H2G/HashToField.swift b/Sources/_CryptoExtras/H2G/HashToField.swift new file mode 100644 index 00000000..9c8b0fd0 --- /dev/null +++ b/Sources/_CryptoExtras/H2G/HashToField.swift @@ -0,0 +1,76 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2024 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 Foundation +import Crypto + +extension Data { + static func ^ (left: Data, right: Data) -> Data { + precondition(left.count == right.count) + return Data(zip(left, right).compactMap { return $0 ^ $1 }) + } +} + +enum Hash2FieldErrors: Error { + case outputSizeIsTooLarge +} + +/// HashToField hashes a byte string msg of arbitrary length into one or more elements of a finite field +@available(macOS 10.15, iOS 13.2, tvOS 13.2, watchOS 6.1, *) +struct HashToField { + static func expandMessageXMD(_ msg: Data, DST: Data, outputByteCount L: Int) throws -> Data { + typealias H = C.H + let digestByteCount = H.Digest.byteCount + + let ell = Int(ceil(Double(L) / Double(digestByteCount))) + + if ell > 255 { + throw Hash2FieldErrors.outputSizeIsTooLarge + } + + let DST_prime = DST + I2OSP(value: DST.count, outputByteCount: 1) + let z_pad = Data(repeating: 0, count: H.blockByteCount) + let l_i_b_str = I2OSP(value: L, outputByteCount: 2) + let msg_prime = z_pad + msg + l_i_b_str + I2OSP(value: 0, outputByteCount: 1) + DST_prime + + let b0 = Data(H.hash(data: msg_prime)) + var bis = Data() + + for i in 1...ell { + let chaining = ((i == 1) ? b0 : (b0 ^ bis.suffix(digestByteCount))) + bis.append(Data(H.hash(data: (chaining + I2OSP(value: i, outputByteCount: 1) + DST_prime)))) + } + + return Data(bis.prefix(L)) + } + + static func hashToField(_ data: Data, outputElementCount: Int, dst: Data, outputSize L: Int, reductionIsModOrder: Bool) throws -> [GroupImpl.Scalar] { + precondition(outputElementCount > 0) + let byteCount = outputElementCount * L + let uniformBytes = try expandMessageXMD(data, + DST: dst, + outputByteCount: byteCount) + + var u_i = [GroupImpl.Scalar]() + u_i.reserveCapacity(outputElementCount) + + for i in 0...Scalar(bytes: tv, reductionIsModOrder: reductionIsModOrder)) + } + + precondition(u_i.count == outputElementCount) + return u_i + } +} diff --git a/Sources/_CryptoExtras/OPRFs/OPRF.swift b/Sources/_CryptoExtras/OPRFs/OPRF.swift new file mode 100644 index 00000000..e61dcaa1 --- /dev/null +++ b/Sources/_CryptoExtras/OPRFs/OPRF.swift @@ -0,0 +1,130 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2024 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 Foundation +import Crypto + +/// (Verifiable Partly-)Oblivious Pseudorandom Functions +/// https://cfrg.github.io/draft-irtf-cfrg-voprf/draft-irtf-cfrg-voprf.html +@available(macOS 10.15, iOS 13.2, tvOS 13.2, watchOS 6.1, *) +enum OPRF {} + +@available(macOS 10.15, iOS 13.2, tvOS 13.2, watchOS 6.1, *) +extension OPRF { + enum Errors: Error { + case invalidProof + case incorrectProofSize + case invalidModeForInfo + case incompatibleMode + } +} + +/// Defines the IETF Serializations for OPRFs +protocol OPRFGroupElement: GroupElement { + init(oprfRepresentation: Data) throws + var oprfRepresentation: Data { get } +} + +@available(macOS 10.15, iOS 13.2, tvOS 13.2, watchOS 6.1, *) +extension OPRF { + static func oprfVersion(v8CompatibilityMode: Bool) -> String { + if v8CompatibilityMode { + return "VOPRF08-" + } else { + return "OPRFV1-" + } + } + + /// OPRF Modes + enum Mode: Int, CaseIterable { + // Base mode corresponds to an OPRF + case base = 0 + // Verifiable mode corresponds to a V(erifiable)OPRF + case verifiable = 1 + // Partially-Oblivious verifiable OPRF + case partiallyOblivious = 2 + } + + /// IETF Ciphersuites defined for OPRFs + struct Ciphersuite { + let suiteID: Int + + init(_ h2g: H2G.Type) { + switch h2g.self { + case is HashToCurveImpl.Type: + suiteID = 3 + case is HashToCurveImpl.Type: + suiteID = 4 + case is HashToCurveImpl.Type: + suiteID = 5 + default: + fatalError("Unsupported H2G ciphersuite.") + } + } + + var stringIdentifier: String { + get { + switch suiteID { + case 3: + return "P256-SHA256" + case 4: + return "P384-SHA384" + case 5: + return "P521-SHA512" + default: + fatalError("Unsupported H2G ciphersuite.") + } + } + } + } + + internal static func suiteIdentifier(suite: Ciphersuite, v8CompatibilityMode: Bool) -> Data { + if v8CompatibilityMode { + return I2OSP(value: suite.suiteID, outputByteCount: 2) + } else { + return Data("-\(suite.stringIdentifier)".utf8) + } + } + + internal static func setupContext(mode: Mode, suite: Ciphersuite, v8CompatibilityMode: Bool) -> Data { + return oprfVersion(v8CompatibilityMode: v8CompatibilityMode).data(using: .utf8)! + + I2OSP(value: mode.rawValue, outputByteCount: 1) + + suiteIdentifier(suite: suite, v8CompatibilityMode: v8CompatibilityMode) + } + + internal static func composeFinalizeContext(message: Data, + info: Data?, + unblindedElement: H2G.G.Element, + ciphersuite: Ciphersuite, + mode: Mode, + v8CompatibilityMode: Bool) -> Data { + if v8CompatibilityMode { + let finalizeCTX = "Finalize-".data(using: .utf8)! + setupContext(mode: mode, suite: ciphersuite, v8CompatibilityMode: v8CompatibilityMode) + let hashInput = I2OSP(value: message.count, outputByteCount: 2) + message + + I2OSP(value: info?.count ?? 0, outputByteCount: 2) + (info ?? Data()) + + I2OSP(value: unblindedElement.oprfRepresentation.count, outputByteCount: 2) + unblindedElement.oprfRepresentation + + I2OSP(value: finalizeCTX.count, outputByteCount: 2) + finalizeCTX + + return hashInput + } else { + var hashInput = I2OSP(value: message.count, outputByteCount: 2) + message + + if mode == .partiallyOblivious { + hashInput = hashInput + I2OSP(value: info!.count, outputByteCount: 2) + info! + } + + hashInput = hashInput + I2OSP(value: unblindedElement.oprfRepresentation.count, outputByteCount: 2) + unblindedElement.oprfRepresentation + Data("Finalize".utf8) + return hashInput + } + } +} diff --git a/Sources/_CryptoExtras/OPRFs/OPRFClient.swift b/Sources/_CryptoExtras/OPRFs/OPRFClient.swift new file mode 100644 index 00000000..bf20458d --- /dev/null +++ b/Sources/_CryptoExtras/OPRFs/OPRFClient.swift @@ -0,0 +1,54 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2024 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 Crypto +import Foundation + +@available(macOS 10.15, iOS 13.2, tvOS 13.2, watchOS 6.1, *) +extension OPRF { + struct Client { + let mode: Mode + let ciphersuite: Ciphersuite + let v8CompatibilityMode: Bool + typealias G = H2G.G + + init(ciphersuite: Ciphersuite) { + self = Self(mode: .base, ciphersuite: ciphersuite) + } + + internal init(mode: Mode, ciphersuite: Ciphersuite, v8CompatibilityMode: Bool = false) { + self.mode = mode + self.ciphersuite = ciphersuite + self.v8CompatibilityMode = v8CompatibilityMode + } + + func blindMessage(_ message: Data, blind: G.Scalar = G.Scalar.random) -> (blind: G.Scalar, blindedElement: G.Element) { + let dst = "HashToGroup-".data(using: .utf8)! + setupContext(mode: mode, suite: ciphersuite, v8CompatibilityMode: self.v8CompatibilityMode) + let P: G.Element = H2G.hashToGroup(message, domainSeparationString: dst) + let blindedElement = blind * P + return (blind: blind, blindedElement: blindedElement) + } + + func unblind(blind: G.Scalar, evaluatedElement: G.Element) -> G.Element { + return (blind ^ (-1)) * evaluatedElement + } + + func finalize(message: Data, info: Data?, blind: G.Scalar, evaluatedElement: G.Element) throws -> Data { + let unblinded = unblind(blind: blind, + evaluatedElement: evaluatedElement) + + return Data(H2G.H.hash(data: composeFinalizeContext(message: message, info: info, unblindedElement: unblinded, ciphersuite: ciphersuite, mode: mode, v8CompatibilityMode: self.v8CompatibilityMode))) + } + + } +} diff --git a/Sources/_CryptoExtras/OPRFs/OPRFServer.swift b/Sources/_CryptoExtras/OPRFs/OPRFServer.swift new file mode 100644 index 00000000..cf858f7f --- /dev/null +++ b/Sources/_CryptoExtras/OPRFs/OPRFServer.swift @@ -0,0 +1,111 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2024 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 Foundation +import Crypto + +@available(macOS 10.15, iOS 13.2, tvOS 13.2, watchOS 6.1, *) +extension OPRF { + struct Server { + typealias G = H2G.G + let mode: Mode + let ciphersuite: Ciphersuite + let privateKey: G.Scalar + let v8CompatibilityMode: Bool + + init(ciphersuite: Ciphersuite, privateKey: G.Scalar = G.Scalar.random) { + self.init(mode: .base, ciphersuite: ciphersuite, privateKey: privateKey) + } + + internal init(mode: Mode, ciphersuite: Ciphersuite, privateKey: G.Scalar = G.Scalar.random, v8CompatibilityMode: Bool = false) { + self.mode = mode + self.ciphersuite = ciphersuite + self.privateKey = privateKey + self.v8CompatibilityMode = v8CompatibilityMode + } + + var publicKey: G.Element { + privateKey * G.Element.generator + } + + func evaluate(blindedElement: G.Element, info: Data? = nil, proofScalar: G.Scalar = G.Scalar.random) throws -> + (G.Element, DLEQProof?) { + let dst = setupContext(mode: mode, suite: ciphersuite, v8CompatibilityMode: self.v8CompatibilityMode) + + if v8CompatibilityMode { return try v8Evaluate(blindedElement: blindedElement, info: info, proofScalar: proofScalar) } + + if mode == .base || mode == .verifiable { + let evaluatedElement = self.privateKey * blindedElement + if mode == .base { return (evaluatedElement, nil) } + + let proof = try DLEQ.proveEquivalenceBetween(k: self.privateKey, + A: G.Element.generator, + B: (self.privateKey * G.Element.generator), + CDs: [(C: blindedElement, D: evaluatedElement)], + dst: dst, + proofScalar: proofScalar, v8CompatibilityMode: self.v8CompatibilityMode) + return (evaluatedElement, proof) + } + + precondition(mode == .partiallyOblivious) + let framedInfo = Data("Info".utf8) + I2OSP(value: info!.count, outputByteCount: 2) + info! + + let m = try H2G.hashToScalar(framedInfo, domainSeparationString: dst) + let t = privateKey + m + + let evaluatedElement = (t ^ (-1)) * blindedElement + let proof = try DLEQ.proveEquivalenceBetween(k: t, + A: G.Element.generator, + B: (t * G.Element.generator), + CDs: [(C: evaluatedElement, D: blindedElement)], + dst: dst, + proofScalar: proofScalar, v8CompatibilityMode: self.v8CompatibilityMode) + return (evaluatedElement, proof) + } + + internal func v8Evaluate(blindedElement: G.Element, info: Data? = nil, proofScalar: G.Scalar = G.Scalar.random) throws -> + (G.Element, DLEQProof?) { + precondition(self.mode == .verifiable || self.mode == .base) + let setupCtx = setupContext(mode: mode, suite: ciphersuite, v8CompatibilityMode: self.v8CompatibilityMode) + let contextDST = "Context-".data(using: .utf8)! + setupCtx + + let ctx = contextDST + I2OSP(value: (info?.count ?? 0), outputByteCount: 2) + (info ?? Data()) + + let m = try H2G.hashToScalar(ctx, domainSeparationString: setupCtx) + let t = privateKey + m + let evaluatedElement = (t ^ (-1)) * blindedElement + + guard self.mode != .base else { + return (evaluatedElement, nil) + } + + let proof = try DLEQ.proveEquivalenceBetween(k: t, + A: G.Element.generator, + B: (t * G.Element.generator), + CDs: [(C: evaluatedElement, D: blindedElement)], + dst: setupCtx, + proofScalar: proofScalar, v8CompatibilityMode: self.v8CompatibilityMode) + return (evaluatedElement, proof) + } + + internal func verifyFinalize(msg: Data, + output: Data, + info: Data?) throws -> Bool { + let dst = "HashToGroup-".data(using: .utf8)! + setupContext(mode: mode, suite: ciphersuite, v8CompatibilityMode: self.v8CompatibilityMode) + let t: H2G.G.Element = H2G.hashToGroup(msg, domainSeparationString: dst) + let (issuedElement, _): (H2G.G.Element, DLEQProof?) = try evaluate(blindedElement: t, info: info) + + return output == Data(H2G.H.hash(data: composeFinalizeContext(message: msg, info: info, unblindedElement: issuedElement, ciphersuite: ciphersuite, mode: mode, v8CompatibilityMode: v8CompatibilityMode))) + } + } +} diff --git a/Sources/_CryptoExtras/OPRFs/VOPRF+API.swift b/Sources/_CryptoExtras/OPRFs/VOPRF+API.swift new file mode 100644 index 00000000..f2285c77 --- /dev/null +++ b/Sources/_CryptoExtras/OPRFs/VOPRF+API.swift @@ -0,0 +1,449 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2024 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 Crypto +import Foundation + +// MARK: - P384 + VPORF (P384-SHA384) +extension P384 { + /// A mechanism to compute the output of a pseudorandom without the client learning the secret or the server + /// learning the input using the P384-SHA384 Verifiable Oblivious Pseudorandom Function (VOPRF). + /// + /// - Seealso: [RFC 9497: VOPRF Protocol](https://www.rfc-editor.org/rfc/rfc9497.html#name-voprf-protocol). + /// - Seealso: [RFC 9497: OPRF(P-384, SHA-384)](https://www.rfc-editor.org/rfc/rfc9497.html#name-oprfp-384-sha-384). + @available(iOS 16.0, macOS 13.0, watchOS 9.0, tvOS 16.0, *) + public enum _VOPRF { + typealias H2G = HashToCurveImpl + typealias Ciphersuite = OPRF.Ciphersuite + typealias Client = OPRF.VerifiableClient + typealias Server = OPRF.VerifiableServer + + static var ciphersuite: Ciphersuite { Ciphersuite(H2G.self) } + + /// A P-384 public key used to blind inputs and finalize blinded elements. + public struct PublicKey { + fileprivate typealias BackingPublicKey = P384.Signing.PublicKey + fileprivate var backingKey: BackingPublicKey + fileprivate var backingPoint: H2G.G.Element + fileprivate static var client: Client { try! Client(ciphersuite: P384._VOPRF.ciphersuite, mode: .verifiable) } + + fileprivate init(backingKey: BackingPublicKey) { + self.backingKey = backingKey + self.backingPoint = try! H2G.G.Element(oprfRepresentation: backingKey.compressedRepresentation) + } + + /// Creates a P-384 public key for VOPRF(P-384, SHA-384) from a collection of bytes. + /// + /// - Parameters: + /// - rawRepresentation: A raw representation of the key as a collection of contiguous bytes. + public init(rawRepresentation: D) throws { + self.init(backingKey: try BackingPublicKey(rawRepresentation: rawRepresentation)) + } + + /// Creates a P-384 public key for VOPRF(P-384, SHA-384) from a compact representation of the key. + /// + /// - Parameters: + /// - compactRepresentation: A compact representation of the key as a collection of contiguous bytes. + public init(compactRepresentation: Bytes) throws { + self.init(backingKey: try BackingPublicKey(compactRepresentation: compactRepresentation)) + } + + /// Creates a P-384 public key for VOPRF(P-384, SHA-384) from an ANSI x9.63 representation. + /// + /// - Parameters: + /// - x963Representation: An ANSI x9.63 representation of the key as collection of contiguous bytes. + public init(x963Representation: Bytes) throws { + self.init(backingKey: try BackingPublicKey(x963Representation: x963Representation)) + } + + /// Creates a P-384 public key for VOPRF(P-384, SHA-384) from a compressed representation of the key. + /// + /// - Parameters: + /// - compressedRepresentation: A compressed representation of the key as a collection of contiguous bytes. + public init(compressedRepresentation: Bytes) throws { + self.init(backingKey: try BackingPublicKey(compressedRepresentation: compressedRepresentation)) + } + + /// Creates a P-384 public key for VOPRF(P-384, SHA-384) from a Privacy-Enhanced Mail (PEM) representation. + /// + /// - Parameters: + /// - pemRepresentation: A PEM representation of the key. + public init(pemRepresentation: String) throws { + self.init(backingKey: try BackingPublicKey(pemRepresentation: pemRepresentation)) + } + + /// Creates a P-384 public key for VOPRF(P-384, SHA-384) from a Distinguished Encoding Rules (DER) encoded + /// representation. + /// + /// - Parameters: + /// - derRepresentation: A DER-encoded representation of the key. + public init(derRepresentation: Bytes) throws where Bytes.Element == UInt8 { + self.init(backingKey: try BackingPublicKey(derRepresentation: derRepresentation)) + } + + /// A compact representation of the public key. + public var compactRepresentation: Data? { self.backingKey.compactRepresentation } + + /// A full representation of the public key. + public var rawRepresentation: Data { self.backingKey.rawRepresentation } + + /// An ANSI x9.63 representation of the public key. + public var x963Representation: Data { self.backingKey.x963Representation } + + /// A compressed representation of the public key. + public var compressedRepresentation: Data { self.backingKey.compressedRepresentation } + + /// A Distinguished Encoding Rules (DER) encoded representation of the public key. + public var derRepresentation: Data { self.backingKey.derRepresentation } + + /// A Privacy-Enhanced Mail (PEM) representation of the public key. + public var pemRepresentation: String { self.backingKey.pemRepresentation } + + /// An RFC 9497 OPRF representation of the public key. + public var oprfRepresentation: Data { self.backingPoint.oprfRepresentation } + } + + /// A P-384 public key used to evaluate blinded inputs. + public struct PrivateKey { + fileprivate typealias BackingPrivateKey = P384.Signing.PrivateKey + fileprivate var backingKey: BackingPrivateKey + fileprivate var backingScalar: H2G.G.Scalar + fileprivate var server: Server + + fileprivate init(backingKey: BackingPrivateKey) { + self.backingKey = backingKey + self.backingScalar = try! H2G.G.Scalar(bytes: backingKey.rawRepresentation) + self.server = try! Server(ciphersuite: P384._VOPRF.ciphersuite, privateKey: self.backingScalar, mode: .verifiable) + } + + /// Creates a random P-384 private key for VOPRF(P-384, SHA-384). + /// + /// Keys that use a compact point encoding enable shorter public keys, but aren’t + /// compliant with FIPS certification. If your app requires FIPS certification, + /// create a key with ``init(rawRepresentation:)``. + /// + /// - Parameters: + /// - compactRepresentable: Determines whether to create a key that supports compact point encoding. + public init(compactRepresentable: Bool = true) { + self.init(backingKey: BackingPrivateKey(compactRepresentable: compactRepresentable)) + } + + /// Creates a P-384 private key for VOPRF(P-384, SHA-384) from an ANSI x9.63 representation. + /// + /// - Parameters: + /// - x963Representation: An ANSI x9.63 representation of the key. + public init(x963Representation: Bytes) throws { + self.init(backingKey: try BackingPrivateKey(x963Representation: x963Representation)) + } + + /// Creates a P-384 private key for VOPRF(P-384, SHA-384) from a collection of bytes. + /// + /// - Parameters: + /// - rawRepresentation: A raw representation of the key as a collection of + /// contiguous bytes. + public init(rawRepresentation: Bytes) throws { + self.init(backingKey: try BackingPrivateKey(rawRepresentation: rawRepresentation)) + } + + /// Creates a P-384 private key for VOPRF(P-384, SHA-384) from a Privacy-Enhanced Mail PEM) representation. + /// + /// - Parameters: + /// - pemRepresentation: A PEM representation of the key. + public init(pemRepresentation: String) throws { + self.init(backingKey: try BackingPrivateKey(pemRepresentation: pemRepresentation)) + } + + /// Creates a P-384 private key for VOPRF(P-384, SHA-384) from a Distinguished Encoding Rules (DER) encoded + /// representation. + /// + /// - Parameters: + /// - derRepresentation: A DER-encoded representation of the key. + public init(derRepresentation: Bytes) throws where Bytes.Element == UInt8 { + self.init(backingKey: try BackingPrivateKey(derRepresentation: derRepresentation)) + } + + /// The corresponding public key. + public var publicKey: P384._VOPRF.PublicKey { + PublicKey(backingKey: self.backingKey.publicKey) + } + + /// A data representation of the private key. + public var rawRepresentation: Data { self.backingKey.rawRepresentation } + + /// An ANSI x9.63 representation of the private key. + public var x963Representation: Data { self.backingKey.x963Representation } + + /// A Distinguished Encoding Rules (DER) encoded representation of the private key. + public var derRepresentation: Data { + self.backingKey.derRepresentation + } + + /// A Privacy-Enhanced Mail (PEM) representation of the private key. + public var pemRepresentation: String { + self.backingKey.pemRepresentation + } + } + } +} + +@available(iOS 16.0, macOS 13.0, watchOS 9.0, tvOS 16.0, *) +extension P384._VOPRF { + /// A blinding value, used to blind an input. + /// + /// Users cannot not create values of this type manually; it is created and returned by the blind operation. + public struct Blind { + fileprivate var backing: H2G.G.Scalar + + fileprivate init(backing: H2G.G.Scalar) { + self.backing = backing + } + } + + /// A blinded element, the result of blinding an input. + /// + /// Clients should not create values of this type manually; they are created and returned by the blind operation. + /// + /// Servers should reconstruct values of this type from the serialized blinded element bytes sent by the client. + public struct BlindedElement { + fileprivate var backing: H2G.G.Element + + fileprivate init(backing: H2G.G.Element) { + self.backing = backing + } + + /// Construct a blinded element from its OPRF representation. + /// + /// Clients should not create values of this type manually; they are created and returned by the blind operation. + /// + /// Servers should reconstruct values of this type from the serialized blinded element bytes sent by the client. + public init(oprfRepresentation: D) throws { + self.init(backing: try H2G.G.Element(oprfRepresentation: Data(oprfRepresentation))) + } + + /// The OPRF representation of the blinded element. + public var oprfRepresentation: Data { self.backing.oprfRepresentation } + } + + /// A blinded element and its blind for unblinding. + /// + /// Users cannot create values of this type manually; it is created and returned by the blind operation. + public struct BlindedInput { + var input: Data + var blind: Blind + + /// The element representing the blinded input to be sent to the server. + public var blindedElement: BlindedElement + } + + /// An evaluated element, the result of the blind evaluate operation. + /// + /// Users cannot create values of this type manually; it is created and returned by the evaluate operation. + public struct EvaluatedElement { + static var serializedByteCount: Int { P384.compressedx962PointByteCount } + + fileprivate var backing: H2G.G.Element + + fileprivate init(backing: H2G.G.Element) { + self.backing = backing + } + + internal init(oprfRepresentation: D) throws { + self.init(backing: try H2G.G.Element(oprfRepresentation: Data(oprfRepresentation))) + } + + /// The OPRF representation of the evaluated element to be sent to the client. + public var oprfRepresentation: Data { self.backing.oprfRepresentation } + } + + /// A proof that the evaluated element was computed using the agreed key pair. + /// + /// Users cannot create values of this type manually; it is created and returned by the evaluate operation. + public struct Proof { + static var serializedByteCount: Int { P384.orderByteCount * 2 } + fileprivate var backing: DLEQProof + + fileprivate init(backing: DLEQProof) { + self.backing = backing + } + + internal init(rawRepresentation: D) throws { + guard rawRepresentation.count == Self.serializedByteCount else { + throw CryptoKitError.incorrectParameterSize + } + + var remainingBytes = rawRepresentation[...] + + let challengeBytes = remainingBytes.prefix(P384.orderByteCount) + remainingBytes = remainingBytes.dropFirst(P384.orderByteCount) + + let responseBytes = remainingBytes.prefix(P384.orderByteCount) + remainingBytes = remainingBytes.dropFirst(P384.orderByteCount) + + precondition(remainingBytes.isEmpty) + + let challenge = try H2G.G.Scalar(bytes: Data(challengeBytes)) + let response = try H2G.G.Scalar(bytes: Data(responseBytes)) + self.init(backing: DLEQProof(c: challenge, s: response)) + } + + /// A serialized representation of the proof to send to the client. + public var rawRepresentation: Data { + var result = Data(capacity: Self.serializedByteCount) + result.append(self.backing.c.rawRepresentation) + result.append(self.backing.s.rawRepresentation) + return result + } + } + + /// The result of blind evaluation: the evaluated element and corresponding proof. + /// + /// Servers should not create values of this type manually; they are created and returned by the evaluate operation. + /// + /// Clients should reconstruct values of this type from the serialized blind evaluation bytes sent by the server. + public struct BlindEvaluation { + static var serializedByteCount: Int { EvaluatedElement.serializedByteCount + Proof.serializedByteCount } + + /// The evaluated element. + public private(set) var evaluatedElement: EvaluatedElement + + /// The proof. + public private(set) var proof: Proof + + fileprivate init(evaluatedElement: EvaluatedElement, proof: Proof) { + self.evaluatedElement = evaluatedElement + self.proof = proof + } + + /// Construct a blind evaluation from its serialized representation. + /// + /// Servers should not create values of this type manually; they are created and returned by the evaluate operation. + /// + /// Clients should reconstruct values of this type from the serialized blind evaluation bytes sent by the server. + public init(rawRepresentation: D) throws { + guard rawRepresentation.count == Self.serializedByteCount else { + throw CryptoKitError.incorrectParameterSize + } + + var remainingBytes = rawRepresentation[...] + + let evaluatedElementBytes = remainingBytes.prefix(EvaluatedElement.serializedByteCount) + remainingBytes = remainingBytes.dropFirst(EvaluatedElement.serializedByteCount) + + let proofBytes = remainingBytes.prefix(Proof.serializedByteCount) + remainingBytes = remainingBytes.dropFirst(Proof.serializedByteCount) + + precondition(remainingBytes.isEmpty) + + let evaluatedElement = try EvaluatedElement(oprfRepresentation: evaluatedElementBytes) + let proof = try Proof(rawRepresentation: proofBytes) + self.init(evaluatedElement: evaluatedElement, proof: proof) + } + + /// A serialized representation of the blind evaluation to send to the client. + public var rawRepresentation: Data { + var result = Data(capacity: Self.serializedByteCount) + result.append(self.evaluatedElement.oprfRepresentation) + result.append(self.proof.rawRepresentation) + return result + } + } +} + +@available(iOS 16.0, macOS 13.0, watchOS 9.0, tvOS 16.0, *) +extension P384._VOPRF.PublicKey { + internal func blind(_ input: D, with fixedBlind: P384._VOPRF.H2G.G.Scalar) throws -> P384._VOPRF.BlindedInput { + let input = Data(input) + let (blind, blindedElement) = Self.client.blindMessage(input, blind: fixedBlind) + return P384._VOPRF.BlindedInput( + input: input, + blind: P384._VOPRF.Blind(backing: blind), + blindedElement: P384._VOPRF.BlindedElement(backing: blindedElement) + ) + } + + /// Blind an input to be evaluated by the server using the VOPRF protocol. + /// + /// - Parameter input: The input to blind. + /// - Returns: The blinded input, and its blind for unblinding. + /// + /// - Seealso: [RFC 9497: VOPRF Protocol](https://www.rfc-editor.org/rfc/rfc9497.html#name-voprf-protocol). + public func blind(_ input: D) throws -> P384._VOPRF.BlindedInput { + try self.blind(input, with: .random) + } + + /// Compute the output of the VOPRF by verifying the server proof, and unblinding and hashing the evaluated element. + /// + /// - Parameter blindedInput: The blinded input from the blind operation, computed earlier by the client. + /// - Parameter blindEvaluation: The blind evaluation from the evaluate operation, received from the server. + /// - Returns: The PRF output. + /// + /// - Seealso: [RFC 9497: VOPRF Protocol](https://www.rfc-editor.org/rfc/rfc9497.html#name-voprf-protocol). + public func finalize(_ blindedInput: P384._VOPRF.BlindedInput, using blindEvaluation: P384._VOPRF.BlindEvaluation) throws -> Data { + try Self.client.finalize( + message: blindedInput.input, + info: nil, + blind: blindedInput.blind.backing, + evaluatedElement: blindEvaluation.evaluatedElement.backing, + proof: blindEvaluation.proof.backing, + publicKey: self.backingPoint + ) + } +} + +@available(iOS 16.0, macOS 13.0, watchOS 9.0, tvOS 16.0, *) +extension P384._VOPRF.PrivateKey { + static var hashToGroupDST: Data { + Data("HashToGroup-".utf8) + OPRF.setupContext(mode: .verifiable, suite: P384._VOPRF.ciphersuite, v8CompatibilityMode: false) + } + + internal func evaluate(_ blindedElement: P384._VOPRF.BlindedElement, using fixedProofScalar: P384._VOPRF.H2G.G.Scalar) throws -> P384._VOPRF.BlindEvaluation { + let (evaluatedElement, proof) = try self.server.evaluate(blindedElement: blindedElement.backing, proofScalar: fixedProofScalar) + return P384._VOPRF.BlindEvaluation( + evaluatedElement: P384._VOPRF.EvaluatedElement(backing: evaluatedElement), + proof: P384._VOPRF.Proof(backing: proof) + ) + } + + /// Compute the evaluated element and associated proof for verification by the client. + /// + /// - Parameter blindedElement: The blinded element from the blind operation, received from the client. + /// - Returns: The blind evaluation to be sent to the client. + /// + /// - Seealso: [RFC 9497: VOPRF Protocol](https://www.rfc-editor.org/rfc/rfc9497.html#name-voprf-protocol). + public func evaluate(_ blindedElement: P384._VOPRF.BlindedElement) throws -> P384._VOPRF.BlindEvaluation { + try self.evaluate(blindedElement, using: .random) + } + + /// Compute the PRF without blinding or proof. + /// + /// - Parameter input: The input message for which to compute the PRF. + /// - Returns: The computed PRF, the same as the VOPRF, without the blinding or proof. + /// + /// - Seealso: [RFC 9497: VOPRF Protocol - Evaluate]( https://cfrg.github.io/draft-irtf-cfrg-voprf/draft-irtf-cfrg-voprf.html#section-3.3.2-7). + public func evaluate(_ input: D) throws -> Data { + let inputElement = P384._VOPRF.H2G.hashToGroup( + Data(input), + domainSeparationString: Self.hashToGroupDST + ) + let evaluatedElement = self.backingScalar * inputElement + let finalizeContext = OPRF.composeFinalizeContext( + message: Data(input), + info: nil, + unblindedElement: evaluatedElement, + ciphersuite: P384._VOPRF.ciphersuite, + mode: .verifiable, + v8CompatibilityMode: false + ) + return Data(P384._VOPRF.H2G.H.hash(data: finalizeContext)) + } +} diff --git a/Sources/_CryptoExtras/OPRFs/VOPRFClient.swift b/Sources/_CryptoExtras/OPRFs/VOPRFClient.swift new file mode 100644 index 00000000..8687e3bb --- /dev/null +++ b/Sources/_CryptoExtras/OPRFs/VOPRFClient.swift @@ -0,0 +1,99 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2024 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 Crypto +import Foundation + +@available(macOS 10.15, iOS 13.2, tvOS 13.2, watchOS 6.1, *) +extension OPRF { + struct VerifiableClient { + fileprivate let client: OPRF.Client + typealias G = H2G.G + + init(ciphersuite: Ciphersuite, v8CompatibilityMode: Bool = false, mode: OPRF.Mode) throws { + if mode != .partiallyOblivious && mode != .verifiable { + throw OPRF.Errors.incompatibleMode + } + + self.client = .init(mode: mode, ciphersuite: ciphersuite, v8CompatibilityMode: v8CompatibilityMode) + } + + func blindMessage(_ message: Data, blind: G.Scalar = G.Scalar.random) -> (blind: G.Scalar, blindedElement: G.Element) { + self.client.blindMessage(message, blind: blind) + } + + fileprivate func v8Finalize(message: Data, info: Data?, blind: G.Scalar, evaluatedElement: G.Element, proof: DLEQProof, publicKey: G.Element) throws -> Data { + precondition(self.client.mode == .verifiable) + let setupCtx = setupContext(mode: client.mode, suite: client.ciphersuite, v8CompatibilityMode: self.client.v8CompatibilityMode) + let contextDST = "Context-".data(using: .utf8)! + setupCtx + + let ctx = contextDST + I2OSP(value: (info?.count ?? 0), outputByteCount: 2) + (info ?? Data()) + + let m = try H2G.hashToScalar(ctx, domainSeparationString: setupCtx) + let t = m * G.Element.generator + + let u = publicKey + t + + let blindedElement = self.blindMessage(message, blind: blind).blindedElement + guard try DLEQ.verifyProof(A: H2G.G.Element.generator, B: u, + CDs: [(C: evaluatedElement, D: blindedElement)], + proof: proof, + dst: setupContext(mode: client.mode, suite: client.ciphersuite, v8CompatibilityMode: self.client.v8CompatibilityMode), v8CompatibilityMode: self.client.v8CompatibilityMode) else { + throw OPRF.Errors.invalidProof + } + + return try self.client.finalize(message: message, info: info, blind: blind, evaluatedElement: evaluatedElement) + + } + + func finalize(message: Data, info: Data?, blind: G.Scalar, evaluatedElement: G.Element, proof: DLEQProof, publicKey: G.Element) throws -> Data { + if self.client.v8CompatibilityMode { return try v8Finalize(message: message, info: info, blind: blind, evaluatedElement: evaluatedElement, proof: proof, publicKey: publicKey) } + + let hasInfo = (info != nil) + if hasInfo && (self.client.mode == .verifiable) { + throw OPRF.Errors.invalidModeForInfo + } + + let setupCtx = setupContext(mode: client.mode, suite: client.ciphersuite, v8CompatibilityMode: self.client.v8CompatibilityMode) + let blindedElement = self.blindMessage(message, blind: blind).blindedElement + + if self.client.mode == .verifiable { + guard try DLEQ.verifyProof(A: H2G.G.Element.generator, B: publicKey, + CDs: [(C: blindedElement, D: evaluatedElement)], + proof: proof, + dst: setupContext(mode: client.mode, suite: client.ciphersuite, v8CompatibilityMode: self.client.v8CompatibilityMode), v8CompatibilityMode: self.client.v8CompatibilityMode) else { + throw OPRF.Errors.invalidProof + } + + return try self.client.finalize(message: message, info: info, blind: blind, evaluatedElement: evaluatedElement) + } + + precondition(self.client.mode == .partiallyOblivious) + let framedInfo = Data("Info".utf8) + I2OSP(value: info!.count, outputByteCount: 2) + info! + + let m = try H2G.hashToScalar(framedInfo, domainSeparationString: setupCtx) + let T = m * G.Element.generator + + let tweakedKey = T + publicKey + guard try DLEQ.verifyProof(A: H2G.G.Element.generator, B: tweakedKey, + CDs: [(C: evaluatedElement, D: blindedElement)], + proof: proof, + dst: setupContext(mode: client.mode, suite: client.ciphersuite, v8CompatibilityMode: self.client.v8CompatibilityMode), v8CompatibilityMode: self.client.v8CompatibilityMode) else { + throw OPRF.Errors.invalidProof + } + + return try self.client.finalize(message: message, info: info, blind: blind, evaluatedElement: evaluatedElement) + } + + } +} diff --git a/Sources/_CryptoExtras/OPRFs/VOPRFServer.swift b/Sources/_CryptoExtras/OPRFs/VOPRFServer.swift new file mode 100644 index 00000000..c5de9040 --- /dev/null +++ b/Sources/_CryptoExtras/OPRFs/VOPRFServer.swift @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2024 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 Foundation +import Crypto + +@available(macOS 10.15, iOS 13.2, tvOS 13.2, watchOS 6.1, *) +extension OPRF { + struct VerifiableServer { + typealias G = H2G.G + let server: OPRF.Server + + init(ciphersuite: Ciphersuite, privateKey: G.Scalar = G.Scalar.random, v8CompatibilityMode: Bool = false, mode: OPRF.Mode) throws { + if mode != .partiallyOblivious && mode != .verifiable { + throw OPRF.Errors.incompatibleMode + } + + self.server = .init(mode: mode, ciphersuite: ciphersuite, privateKey: privateKey, v8CompatibilityMode: v8CompatibilityMode) + } + + var publicKey: G.Element { + server.publicKey + } + + func evaluate(blindedElement: G.Element, info: Data? = nil, proofScalar: G.Scalar = G.Scalar.random) throws -> + (G.Element, DLEQProof) { + let hasInfo = (info != nil) + if hasInfo && self.server.mode == .verifiable && !server.v8CompatibilityMode { + throw OPRF.Errors.invalidModeForInfo + } + + let (evaluatedElement, proof) = try self.server.evaluate(blindedElement: blindedElement, + info: info, + proofScalar: proofScalar) + + return (evaluatedElement, proof!) + } + + internal func verifyFinalize(msg: Data, + output: Data, + info: Data?) throws -> Bool { + return try server.verifyFinalize(msg: msg, output: output, info: info) + } + } +} diff --git a/Sources/_CryptoExtras/Util/I2OSP.swift b/Sources/_CryptoExtras/Util/I2OSP.swift new file mode 100644 index 00000000..d3d20153 --- /dev/null +++ b/Sources/_CryptoExtras/Util/I2OSP.swift @@ -0,0 +1,30 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2024 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 Foundation + +func I2OSP(value: Int, outputByteCount: Int) -> Data { + precondition(outputByteCount > 0, "Cannot I2OSP with no output length.") + precondition(value >= 0, "I2OSP requires a non-null value.") + + let requiredBytes = Int(ceil(log2(Double(max(value, 1) + 1)) / 8)) + precondition(outputByteCount >= requiredBytes) + + var data = Data(repeating: 0, count: outputByteCount) + + for i in (outputByteCount - requiredBytes)...(outputByteCount - 1) { + data[i] = UInt8(truncatingIfNeeded: (value >> (8 * (outputByteCount - 1 - i)))) + } + + return data +} diff --git a/Sources/_CryptoExtras/Util/PrettyBytes.swift b/Sources/_CryptoExtras/Util/PrettyBytes.swift new file mode 100644 index 00000000..7610e5c0 --- /dev/null +++ b/Sources/_CryptoExtras/Util/PrettyBytes.swift @@ -0,0 +1,100 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2019-2020 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 Foundation + +enum ByteHexEncodingErrors: Error { + case incorrectHexValue + case incorrectString +} + +let charA = UInt8(UnicodeScalar("a").value) +let char0 = UInt8(UnicodeScalar("0").value) + +private func itoh(_ value: UInt8) -> UInt8 { + return (value > 9) ? (charA + value - 10) : (char0 + value) +} + +private func htoi(_ value: UInt8) throws -> UInt8 { + switch value { + case char0...char0 + 9: + return value - char0 + case charA...charA + 5: + return value - charA + 10 + default: + throw ByteHexEncodingErrors.incorrectHexValue + } +} + +extension DataProtocol { + var hexString: String { + let hexLen = self.count * 2 + var hexChars = [UInt8](repeating: 0, count: hexLen) + var offset = 0 + + self.regions.forEach { (_) in + for i in self { + hexChars[Int(offset * 2)] = itoh((i >> 4) & 0xF) + hexChars[Int(offset * 2 + 1)] = itoh(i & 0xF) + offset += 1 + } + } + + return String(bytes: hexChars, encoding: .utf8)! + } +} + +extension MutableDataProtocol { + mutating func appendByte(_ byte: UInt64) { + withUnsafePointer(to: byte.littleEndian, { self.append(contentsOf: UnsafeRawBufferPointer(start: $0, count: 8)) }) + } +} + +extension Data { + init(hexString: String) throws { + self.init() + + if hexString.count % 2 != 0 || hexString.count == 0 { + throw ByteHexEncodingErrors.incorrectString + } + + let stringBytes: [UInt8] = Array(hexString.lowercased().data(using: String.Encoding.utf8)!) + + for i in stride(from: stringBytes.startIndex, to: stringBytes.endIndex - 1, by: 2) { + let char1 = stringBytes[i] + let char2 = stringBytes[i + 1] + + try self.append(htoi(char1) << 4 + htoi(char2)) + } + } +} + +extension Array where Element == UInt8 { + init(hexString: String) throws { + self.init() + + guard hexString.count.isMultiple(of: 2), !hexString.isEmpty else { + throw ByteHexEncodingErrors.incorrectString + } + + let stringBytes: [UInt8] = Array(hexString.data(using: String.Encoding.utf8)!) + + for i in stride(from: stringBytes.startIndex, to: stringBytes.endIndex - 1, by: 2) { + let char1 = stringBytes[i] + let char2 = stringBytes[i + 1] + + try self.append(htoi(char1) << 4 + htoi(char2)) + } + } + +} diff --git a/Sources/_CryptoExtras/ZKPs/DLEQ.swift b/Sources/_CryptoExtras/ZKPs/DLEQ.swift new file mode 100644 index 00000000..ff748f85 --- /dev/null +++ b/Sources/_CryptoExtras/ZKPs/DLEQ.swift @@ -0,0 +1,149 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2024 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 Foundation +import Crypto + +/// A DLEQ Proof as described in https://cfrg.github.io/draft-irtf-cfrg-voprf/draft-irtf-cfrg-voprf.html#name-generateproof +struct DLEQProof { + var c: GS + var s: GS + + internal init(c: GS, s: GS) { + self.c = c + self.s = s + } +} + +// Discrete Log Equivalence Proof +// Proves that for a value kept secret k, the relation between B=k*A and D=k*C is such that log_A(B)==log_C(D) +struct DLEQ { + typealias GE = H2G.G.Element + + static func composites(k: GE.Scalar? = nil, B: GE, dst: Data, CDs: [(C: GE, D: GE)], v8CompatibilityMode: Bool) throws -> (M: GE, Z: GE) { + let seedDST = "Seed-".data(using: .utf8)! + dst + + let Bm = B.oprfRepresentation + + let h1Input = I2OSP(value: Bm.count, outputByteCount: 2) + Bm + + I2OSP(value: seedDST.count, outputByteCount: 2) + seedDST + let seed = Data(H2G.H.hash(data: h1Input)) + + var M: GE? + var Z: GE? + + for i in 0.. GE.Scalar { + let Bm = B.oprfRepresentation + let A0 = M.oprfRepresentation + let A1 = Z.oprfRepresentation + let A2 = T2.oprfRepresentation + let A3 = T3.oprfRepresentation + + var h2Input = I2OSP(value: Bm.count, outputByteCount: 2) + Bm + + I2OSP(value: A0.count, outputByteCount: 2) + A0 + + I2OSP(value: A1.count, outputByteCount: 2) + A1 + + I2OSP(value: A2.count, outputByteCount: 2) + A2 + + I2OSP(value: A3.count, outputByteCount: 2) + A3 + + if v8CompatibilityMode { + let challengeDST = "Challenge-".data(using: .utf8)! + dst + h2Input = h2Input + I2OSP(value: challengeDST.count, outputByteCount: 2) + challengeDST + + } else { + let challengeDST = "Challenge".data(using: .utf8)! + h2Input = h2Input + challengeDST + } + + return try H2G.hashToScalar(h2Input, domainSeparationString: dst) + } + + static func proveEquivalenceBetween(k: GE.Scalar, + A: GE, + B: GE, + CDs: [(C: GE, D: GE)], + dst: Data, + proofScalar: GE.Scalar, v8CompatibilityMode: Bool) throws -> DLEQProof { + var M: GE + var Z: GE + + let comp = try composites(k: k, B: B, dst: dst, CDs: CDs, v8CompatibilityMode: v8CompatibilityMode) + M = comp.M + Z = comp.Z + + let r = proofScalar + + let t2 = r * A + let t3 = r * M + + let c = try composeChallenge(dst: dst, B: B, M: M, Z: Z, T2: t2, T3: t3, v8CompatibilityMode: v8CompatibilityMode) + + let s = (r - c * k) + + return DLEQProof(c: c, s: s) + } + + static func verifyProof(A: GE, + B: GE, + CDs: [(C: GE, D: GE)], + proof: DLEQProof, dst: Data, v8CompatibilityMode: Bool) throws -> Bool { + let composites = try composites(B: B, dst: dst, CDs: CDs, v8CompatibilityMode: v8CompatibilityMode) + let t2 = (proof.s * A) + (proof.c * B) + let t3 = ((proof.s * composites.M) + (proof.c * composites.Z)) + + let c = try composeChallenge(dst: dst, B: B, M: composites.M, Z: composites.Z, T2: t2, T3: t3, v8CompatibilityMode: v8CompatibilityMode) + + return c == proof.c + } +} diff --git a/Tests/_CryptoExtrasTests/ECToolbox/H2CVectors/P256_XMD-SHA-256_SSWU_RO_.json b/Tests/_CryptoExtrasTests/ECToolbox/H2CVectors/P256_XMD-SHA-256_SSWU_RO_.json new file mode 100644 index 00000000..cf5736ad --- /dev/null +++ b/Tests/_CryptoExtrasTests/ECToolbox/H2CVectors/P256_XMD-SHA-256_SSWU_RO_.json @@ -0,0 +1,115 @@ +{ + "L": "0x30", + "Z": "0xffffffff00000001000000000000000000000000fffffffffffffffffffffff5", + "ciphersuite": "P256_XMD:SHA-256_SSWU_RO_", + "curve": "NIST P-256", + "dst": "QUUX-V01-CS02-with-P256_XMD:SHA-256_SSWU_RO_", + "expand": "XMD", + "field": { + "m": "0x1", + "p": "0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff" + }, + "hash": "sha256", + "k": "0x80", + "map": { + "name": "SSWU" + }, + "randomOracle": true, + "vectors": [ + { + "P": { + "x": "0x2c15230b26dbc6fc9a37051158c95b79656e17a1a920b11394ca91c44247d3e4", + "y": "0x8a7a74985cc5c776cdfe4b1f19884970453912e9d31528c060be9ab5c43e8415" + }, + "Q0": { + "x": "0xab640a12220d3ff283510ff3f4b1953d09fad35795140b1c5d64f313967934d5", + "y": "0xdccb558863804a881d4fff3455716c836cef230e5209594ddd33d85c565b19b1" + }, + "Q1": { + "x": "0x51cce63c50d972a6e51c61334f0f4875c9ac1cd2d3238412f84e31da7d980ef5", + "y": "0xb45d1a36d00ad90e5ec7840a60a4de411917fbe7c82c3949a6e699e5a1b66aac" + }, + "msg": "", + "u": [ + "0xad5342c66a6dd0ff080df1da0ea1c04b96e0330dd89406465eeba11582515009", + "0x8c0f1d43204bd6f6ea70ae8013070a1518b43873bcd850aafa0a9e220e2eea5a" + ] + }, + { + "P": { + "x": "0x0bb8b87485551aa43ed54f009230450b492fead5f1cc91658775dac4a3388a0f", + "y": "0x5c41b3d0731a27a7b14bc0bf0ccded2d8751f83493404c84a88e71ffd424212e" + }, + "Q0": { + "x": "0x5219ad0ddef3cc49b714145e91b2f7de6ce0a7a7dc7406c7726c7e373c58cb48", + "y": "0x7950144e52d30acbec7b624c203b1996c99617d0b61c2442354301b191d93ecf" + }, + "Q1": { + "x": "0x019b7cb4efcfeaf39f738fe638e31d375ad6837f58a852d032ff60c69ee3875f", + "y": "0x589a62d2b22357fed5449bc38065b760095ebe6aeac84b01156ee4252715446e" + }, + "msg": "abc", + "u": [ + "0xafe47f2ea2b10465cc26ac403194dfb68b7f5ee865cda61e9f3e07a537220af1", + "0x379a27833b0bfe6f7bdca08e1e83c760bf9a338ab335542704edcd69ce9e46e0" + ] + }, + { + "P": { + "x": "0x65038ac8f2b1def042a5df0b33b1f4eca6bff7cb0f9c6c1526811864e544ed80", + "y": "0xcad44d40a656e7aff4002a8de287abc8ae0482b5ae825822bb870d6df9b56ca3" + }, + "Q0": { + "x": "0xa17bdf2965eb88074bc01157e644ed409dac97cfcf0c61c998ed0fa45e79e4a2", + "y": "0x4f1bc80c70d411a3cc1d67aeae6e726f0f311639fee560c7f5a664554e3c9c2e" + }, + "Q1": { + "x": "0x7da48bb67225c1a17d452c983798113f47e438e4202219dd0715f8419b274d66", + "y": "0xb765696b2913e36db3016c47edb99e24b1da30e761a8a3215dc0ec4d8f96e6f9" + }, + "msg": "abcdef0123456789", + "u": [ + "0x0fad9d125a9477d55cf9357105b0eb3a5c4259809bf87180aa01d651f53d312c", + "0xb68597377392cd3419d8fcc7d7660948c8403b19ea78bbca4b133c9d2196c0fb" + ] + }, + { + "P": { + "x": "0x4be61ee205094282ba8a2042bcb48d88dfbb609301c49aa8b078533dc65a0b5d", + "y": "0x98f8df449a072c4721d241a3b1236d3caccba603f916ca680f4539d2bfb3c29e" + }, + "Q0": { + "x": "0xc76aaa823aeadeb3f356909cb08f97eee46ecb157c1f56699b5efebddf0e6398", + "y": "0x776a6f45f528a0e8d289a4be12c4fab80762386ec644abf2bffb9b627e4352b1" + }, + "Q1": { + "x": "0x418ac3d85a5ccc4ea8dec14f750a3a9ec8b85176c95a7022f391826794eb5a75", + "y": "0xfd6604f69e9d9d2b74b072d14ea13050db72c932815523305cb9e807cc900aff" + }, + "msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", + "u": [ + "0x3bbc30446f39a7befad080f4d5f32ed116b9534626993d2cc5033f6f8d805919", + "0x76bb02db019ca9d3c1e02f0c17f8baf617bbdae5c393a81d9ce11e3be1bf1d33" + ] + }, + { + "P": { + "x": "0x457ae2981f70ca85d8e24c308b14db22f3e3862c5ea0f652ca38b5e49cd64bc5", + "y": "0xecb9f0eadc9aeed232dabc53235368c1394c78de05dd96893eefa62b0f4757dc" + }, + "Q0": { + "x": "0xd88b989ee9d1295df413d4456c5c850b8b2fb0f5402cc5c4c7e815412e926db8", + "y": "0xbb4a1edeff506cf16def96afff41b16fc74f6dbd55c2210e5b8f011ba32f4f40" + }, + "Q1": { + "x": "0xa281e34e628f3a4d2a53fa87ff973537d68ad4fbc28d3be5e8d9f6a2571c5a4b", + "y": "0xf6ed88a7aab56a488100e6f1174fa9810b47db13e86be999644922961206e184" + }, + "msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "u": [ + "0x4ebc95a6e839b1ae3c63b847798e85cb3c12d3817ec6ebc10af6ee51adb29fec", + "0x4e21af88e22ea80156aff790750121035b3eefaa96b425a8716e0d20b4e269ee" + ] + } + ] +} diff --git a/Tests/_CryptoExtrasTests/ECToolbox/H2CVectors/P384_XMD-SHA-384_SSWU_RO_.json b/Tests/_CryptoExtrasTests/ECToolbox/H2CVectors/P384_XMD-SHA-384_SSWU_RO_.json new file mode 100644 index 00000000..bdd9cfaa --- /dev/null +++ b/Tests/_CryptoExtrasTests/ECToolbox/H2CVectors/P384_XMD-SHA-384_SSWU_RO_.json @@ -0,0 +1,115 @@ +{ + "L": "0x48", + "Z": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffff3", + "ciphersuite": "P384_XMD:SHA-384_SSWU_RO_", + "curve": "NIST P-384", + "dst": "QUUX-V01-CS02-with-P384_XMD:SHA-384_SSWU_RO_", + "expand": "XMD", + "field": { + "m": "0x1", + "p": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff" + }, + "hash": "sha384", + "k": "0xc0", + "map": { + "name": "SSWU" + }, + "randomOracle": true, + "vectors": [ + { + "P": { + "x": "0xeb9fe1b4f4e14e7140803c1d99d0a93cd823d2b024040f9c067a8eca1f5a2eeac9ad604973527a356f3fa3aeff0e4d83", + "y": "0x0c21708cff382b7f4643c07b105c2eaec2cead93a917d825601e63c8f21f6abd9abc22c93c2bed6f235954b25048bb1a" + }, + "Q0": { + "x": "0xe4717e29eef38d862bee4902a7d21b44efb58c464e3e1f0d03894d94de310f8ffc6de86786dd3e15a1541b18d4eb2846", + "y": "0x6b95a6e639822312298a47526bb77d9cd7bcf76244c991c8cd70075e2ee6e8b9a135c4a37e3c0768c7ca871c0ceb53d4" + }, + "Q1": { + "x": "0x509527cfc0750eedc53147e6d5f78596c8a3b7360e0608e2fab0563a1670d58d8ae107c9f04bcf90e89489ace5650efd", + "y": "0x33337b13cb35e173fdea4cb9e8cce915d836ff57803dbbeb7998aa49d17df2ff09b67031773039d09fbd9305a1566bc4" + }, + "msg": "", + "u": [ + "0x25c8d7dc1acd4ee617766693f7f8829396065d1b447eedb155871feffd9c6653279ac7e5c46edb7010a0e4ff64c9f3b4", + "0x59428be4ed69131df59a0c6a8e188d2d4ece3f1b2a3a02602962b47efa4d7905945b1e2cc80b36aa35c99451073521ac" + ] + }, + { + "P": { + "x": "0xe02fc1a5f44a7519419dd314e29863f30df55a514da2d655775a81d413003c4d4e7fd59af0826dfaad4200ac6f60abe1", + "y": "0x01f638d04d98677d65bef99aef1a12a70a4cbb9270ec55248c04530d8bc1f8f90f8a6a859a7c1f1ddccedf8f96d675f6" + }, + "Q0": { + "x": "0xfc853b69437aee9a19d5acf96a4ee4c5e04cf7b53406dfaa2afbdd7ad2351b7f554e4bbc6f5db4177d4d44f933a8f6ee", + "y": "0x7e042547e01834c9043b10f3a8221c4a879cb156f04f72bfccab0c047a304e30f2aa8b2e260d34c4592c0c33dd0c6482" + }, + "Q1": { + "x": "0x57912293709b3556b43a2dfb137a315d256d573b82ded120ef8c782d607c05d930d958e50cb6dc1cc480b9afc38c45f1", + "y": "0xde9387dab0eef0bda219c6f168a92645a84665c4f2137c14270fb424b7532ff84843c3da383ceea24c47fa343c227bb8" + }, + "msg": "abc", + "u": [ + "0x53350214cb6bef0b51abb791b1c4209a2b4c16a0c67e1ab1401017fad774cd3b3f9a8bcdf7f6229dd8dd5a075cb149a0", + "0xc0473083898f63e03f26f14877a2407bd60c75ad491e7d26cbc6cc5ce815654075ec6b6898c7a41d74ceaf720a10c02e" + ] + }, + { + "P": { + "x": "0xbdecc1c1d870624965f19505be50459d363c71a699a496ab672f9a5d6b78676400926fbceee6fcd1780fe86e62b2aa89", + "y": "0x57cf1f99b5ee00f3c201139b3bfe4dd30a653193778d89a0accc5e0f47e46e4e4b85a0595da29c9494c1814acafe183c" + }, + "Q0": { + "x": "0x0ceece45b73f89844671df962ad2932122e878ad2259e650626924e4e7f132589341dec1480ebcbbbe3509d11fb570b7", + "y": "0xfafd71a3115298f6be4ae5c6dfc96c400cfb55760f185b7b03f3fa45f3f91eb65d27628b3c705cafd0466fafa54883ce" + }, + "Q1": { + "x": "0xdea1be8d3f9be4cbf4fab9d71d549dde76875b5d9b876832313a083ec81e528cbc2a0a1d0596b3bcb0ba77866b129776", + "y": "0xeb15fe71662214fb03b65541f40d3eb0f4cf5c3b559f647da138c9f9b7484c48a08760e02c16f1992762cb7298fa52cf" + }, + "msg": "abcdef0123456789", + "u": [ + "0xaab7fb87238cf6b2ab56cdcca7e028959bb2ea599d34f68484139dde85ec6548a6e48771d17956421bdb7790598ea52e", + "0x26e8d833552d7844d167833ca5a87c35bcfaa5a0d86023479fb28e5cd6075c18b168bf1f5d2a0ea146d057971336d8d1" + ] + }, + { + "P": { + "x": "0x03c3a9f401b78c6c36a52f07eeee0ec1289f178adf78448f43a3850e0456f5dd7f7633dd31676d990eda32882ab486c0", + "y": "0xcc183d0d7bdfd0a3af05f50e16a3f2de4abbc523215bf57c848d5ea662482b8c1f43dc453a93b94a8026db58f3f5d878" + }, + "Q0": { + "x": "0x051a22105e0817a35d66196338c8d85bd52690d79bba373ead8a86dd9899411513bb9f75273f6483395a7847fb21edb4", + "y": "0xf168295c1bbcff5f8b01248e9dbc885335d6d6a04aea960f7384f746ba6502ce477e624151cc1d1392b00df0f5400c06" + }, + "Q1": { + "x": "0x6ad7bc8ed8b841efd8ad0765c8a23d0b968ec9aa360a558ff33500f164faa02bee6c704f5f91507c4c5aad2b0dc5b943", + "y": "0x47313cc0a873ade774048338fc34ca5313f96bbf6ae22ac6ef475d85f03d24792dc6afba8d0b4a70170c1b4f0f716629" + }, + "msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", + "u": [ + "0x04c00051b0de6e726d228c85bf243bf5f4789efb512b22b498cde3821db9da667199b74bd5a09a79583c6d353a3bb41c", + "0x97580f218255f899f9204db64cd15e6a312cb4d8182375d1e5157c8f80f41d6a1a4b77fb1ded9dce56c32058b8d5202b" + ] + }, + { + "P": { + "x": "0x7b18d210b1f090ac701f65f606f6ca18fb8d081e3bc6cbd937c5604325f1cdea4c15c10a54ef303aabf2ea58bd9947a4", + "y": "0xea857285a33abb516732915c353c75c576bf82ccc96adb63c094dde580021eddeafd91f8c0bfee6f636528f3d0c47fd2" + }, + "Q0": { + "x": "0x42e6666f505e854187186bad3011598d9278b9d6e3e4d2503c3d236381a56748dec5d139c223129b324df53fa147c4df", + "y": "0x8ee51dbda46413bf621838cc935d18d617881c6f33f3838a79c767a1e5618e34b22f79142df708d2432f75c7366c8512" + }, + "Q1": { + "x": "0x4ff01ceeba60484fa1bc0d825fe1e5e383d8f79f1e5bb78e5fb26b7a7ef758153e31e78b9d60ce75c5e32e43869d4e12", + "y": "0x0f84b978fac8ceda7304b47e229d6037d32062e597dc7a9b95bcd9af441f3c56c619a901d21635f9ec6ab4710b9fcd0e" + }, + "msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "u": [ + "0x480cb3ac2c389db7f9dac9c396d2647ae946db844598971c26d1afd53912a1491199c0a5902811e4b809c26fcd37a014", + "0xd28435eb34680e148bf3908536e42231cba9e1f73ae2c6902a222a89db5c49c97db2f8fa4d4cd6e424b17ac60bdb9bb6" + ] + } + ] +} diff --git a/Tests/_CryptoExtrasTests/ECToolbox/HashToCurveTests.swift b/Tests/_CryptoExtrasTests/ECToolbox/HashToCurveTests.swift new file mode 100644 index 00000000..22e4e026 --- /dev/null +++ b/Tests/_CryptoExtrasTests/ECToolbox/HashToCurveTests.swift @@ -0,0 +1,124 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2024 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 Crypto +@testable import _CryptoExtras +import XCTest + +struct HashToCurveTestVectorCurvePoint: Codable { + let x: String + let y: String + + var rawRepresentation: Data { + return try! Data(hexString: String(x.dropFirst(2))) + Data(hexString: String(y.dropFirst(2))) + } +} + +struct HashToCurveTestVectorFile: Codable { + let dst: String + let vectors: [HashToCurveTestVector] +} + +struct HashToCurveTestVector: Codable { + let P: HashToCurveTestVectorCurvePoint + let msg: String +} + +class HashToCurveTests: XCTestCase { + + func testVector(vectorFileName file: String, with h2c: HashToCurveImpl.Type) throws { + #if CRYPTO_IN_SWIFTPM + let bundle = Bundle.module + #else + let bundle = Bundle(for: type(of: self)) + #endif + let fileURL = bundle.url(forResource: file, withExtension: "json") + + let data = try Data(contentsOf: fileURL!) + let decoder = JSONDecoder() + let testVectorFile = try decoder.decode(HashToCurveTestVectorFile.self, from: data) + + let dst = testVectorFile.dst + + for vector in testVectorFile.vectors { + let msg = vector.msg.data(using: .ascii)! + + let point = h2c.hashToGroup(msg, domainSeparationString: Data(dst.utf8)) + + XCTAssert(point.oprfRepresentation.hexString.dropFirst(2) == vector.P.x.dropFirst(2)) + } + } + + func testVectors() throws { + try testVector(vectorFileName: "P256_XMD-SHA-256_SSWU_RO_", with: HashToCurveImpl.self) + try testVector(vectorFileName: "P384_XMD-SHA-384_SSWU_RO_", with: HashToCurveImpl.self) +// try testVector(vectorFileName: "P521_XMD-SHA-512_SSWU_RO_", with: HashToCurveImpl.self) + } + + func testH2F() throws { + let data = try! Data(hexString: "436f6e746578742d564f50524630372d00000300097465737420696e666f") + let dst = try! Data(hexString: "48617368546f5363616c61722d564f50524630372d000003") + + let scalar = try HashToField.hashToField(data, + outputElementCount: 1, + dst: dst, + outputSize: 48, reductionIsModOrder: false).first! + + let tv = try! Data(hexString: "5561bc4e7322a640b2ff6cb6aad96d1021f423233b858343caefa05abde7ef85") + XCTAssert(scalar.rawRepresentation == tv) + } + + func testExpandMessageXMD() throws { + let dst = "QUUX-V01-CS02-with-expander".data(using: .ascii)! + var msg = "".data(using: .ascii)! + var uniformBytes = try Data(hexString: "f659819a6473c1835b25ea59e3d38914c98b374f0970b7e4c92181df928fca88") + + try XCTAssert(uniformBytes == HashToField.expandMessageXMD(msg, DST: dst, outputByteCount: 32)) + + msg = "abc".data(using: .ascii)! + uniformBytes = try Data(hexString: "1c38f7c211ef233367b2420d04798fa4698080a8901021a795a1151775fe4da7") + + try XCTAssert(uniformBytes == HashToField.expandMessageXMD(msg, DST: dst, outputByteCount: 32)) + + msg = "abcdef0123456789".data(using: .ascii)! + uniformBytes = try Data(hexString: "8f7e7b66791f0da0dbb5ec7c22ec637f79758c0a48170bfb7c4611bd304ece89") + + try XCTAssert(uniformBytes == HashToField.expandMessageXMD(msg, DST: dst, outputByteCount: 32)) + + msg = "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".data(using: .ascii)! + uniformBytes = try Data(hexString: "396962db47f749ec3b5042ce2452b619607f27fd3939ece2746a7614fb83a1d097f554df3927b084e55de92c7871430d6b95c2a13896d8a33bc48587b1f66d21b128a1a8240d5b0c26dfe795a1a842a0807bb148b77c2ef82ed4b6c9f7fcb732e7f94466c8b51e52bf378fba044a31f5cb44583a892f5969dcd73b3fa128816e") + + try XCTAssert(uniformBytes == HashToField.expandMessageXMD(msg, DST: dst, outputByteCount: 128)) + } + + func testScalarSerialization() throws { + let serialized = try Data(hexString: "afe47f2ea2b10465cc26ac403194dfb68b7f5ee865cda61e9f3e07a537220af1") + + let scalar = try GroupImpl.Scalar(bytes: serialized) + + XCTAssertEqual(scalar.rawRepresentation, serialized) + } + + func testHash2Field() throws { + let dst = "QUUX-V01-CS02-with-P256_XMD:SHA-256_SSWU_RO_".data(using: .ascii)! + let msg = "abc".data(using: .ascii)! + + let elements = try HashToField.hashToField(msg, outputElementCount: 2, dst: dst, outputSize: 48, reductionIsModOrder: true) + + let u0 = elements.first! + let u1 = elements.last! + + XCTAssertEqual(u0.rawRepresentation.hexString, "afe47f2ea2b10465cc26ac403194dfb68b7f5ee865cda61e9f3e07a537220af1") + XCTAssertEqual(u1.rawRepresentation.hexString, "379a27833b0bfe6f7bdca08e1e83c760bf9a338ab335542704edcd69ce9e46e0") + } +} diff --git a/Tests/_CryptoExtrasTests/OPRFs/ECVOPRFTests.swift b/Tests/_CryptoExtrasTests/OPRFs/ECVOPRFTests.swift new file mode 100644 index 00000000..a873c1c2 --- /dev/null +++ b/Tests/_CryptoExtrasTests/OPRFs/ECVOPRFTests.swift @@ -0,0 +1,286 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2024 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 Crypto +@testable import _CryptoExtras +import XCTest + +struct OPRFSuite: Codable { + let groupDST: String + let suiteName: String? + let identifier: String? + let suiteID: Int? + let mode: Int + let skSm: String + let pkSm: String? + let vectors: [OPRFTestVector] +} + +struct DLEQProofVector: Codable { + let proof: String + let r: String +} + +struct OPRFTestVector: Codable { + let Batch: Int + let Blind: String + let BlindedElement: String + let EvaluationElement: String + let Info: String? + let Input: String + let Output: String + let Proof: DLEQProofVector? + let result: String? + let comment: String? +} + +enum Result: String { + case success = "success" + case invalidProof = "invalidProof" + case invalidOPRFOutput = "invalidOPRFOutput" +} + +class ECVOPRFTests: XCTestCase { + func testSuite(suite: OPRFSuite, C: G.Type, modeValue: Int, v8DraftCompatible: Bool) throws { + switch modeValue { + case OPRF.Mode.base.rawValue: + try testBaseSuite(suite: suite, C: C, v8DraftCompatible: v8DraftCompatible) + case OPRF.Mode.verifiable.rawValue: + try testVerifiableSuite(suite: suite, C: C, v8DraftCompatible: v8DraftCompatible, mode: .verifiable) + case OPRF.Mode.partiallyOblivious.rawValue: + try testVerifiableSuite(suite: suite, C: C, v8DraftCompatible: v8DraftCompatible, mode: .partiallyOblivious) + default: + fatalError("Unknown mode") + } + } + + func testBaseSuite(suite: OPRFSuite, C: G.Type, v8DraftCompatible: Bool) throws { + print("Testing \(suite.suiteName ?? suite.identifier!) in base mode.") + + let privateKey = try GroupImpl.Scalar(bytes: Data(hexString: suite.skSm)) + let ciphersuite = OPRF.Ciphersuite(HashToCurveImpl.self) + + for vector in suite.vectors { + if vector.Batch != 1 { + continue + } + + let client = OPRF.Client(mode: .base, ciphersuite: ciphersuite, v8CompatibilityMode: v8DraftCompatible) + let blindTestVector = try GroupImpl.Scalar(bytes: Data(hexString: vector.Blind)) + let input = try Data(hexString: vector.Input) + + let (blind, blindedElement) = client.blindMessage(input, blind: blindTestVector) + + XCTAssert(blindTestVector == blind) + XCTAssert(blindedElement.oprfRepresentation.hexString == vector.BlindedElement) + + let server = OPRF.Server(mode: .base, ciphersuite: ciphersuite, privateKey: privateKey, v8CompatibilityMode: v8DraftCompatible) + + var info = Data() + if vector.Info != nil { info = try Data(hexString: vector.Info!) } + + let evaluate = try server.evaluate(blindedElement: blindedElement, info: info) + + let evaluatedElementTv = try GroupImpl.Element(oprfRepresentation: Data(hexString: vector.EvaluationElement)) + XCTAssert(evaluate.0 == evaluatedElementTv) + + let finalized = try client.finalize(message: input, + info: info, + blind: blind, + evaluatedElement: evaluate.0) + + XCTAssert(finalized.hexString == vector.Output) + XCTAssert(try server.verifyFinalize(msg: input, output: finalized, info: info)) + } + } + + func testVerifiableSuite(suite: OPRFSuite, C: G.Type, v8DraftCompatible: Bool, mode: OPRF.Mode) throws { + print("Testing \(suite.suiteName ?? suite.identifier!) in \(mode) mode.") + + let privateKey = try GroupImpl.Scalar(bytes: Data(hexString: suite.skSm)) + let ciphersuite = OPRF.Ciphersuite(HashToCurveImpl.self) + + for vector in suite.vectors { + if vector.Batch != 1 { + continue + } + + let client = try! OPRF.VerifiableClient(ciphersuite: ciphersuite, v8CompatibilityMode: v8DraftCompatible, mode: mode) + let blindTestVector = try GroupImpl.Scalar(bytes: Data(hexString: vector.Blind)) + let input = try Data(hexString: vector.Input) + + let (blind, blindedElement) = client.blindMessage(input, blind: blindTestVector) + + XCTAssert(blindTestVector == blind) + XCTAssert(blindedElement.oprfRepresentation.hexString == vector.BlindedElement) + + let server = try! OPRF.VerifiableServer(ciphersuite: ciphersuite, privateKey: privateKey, v8CompatibilityMode: v8DraftCompatible, mode: mode) + let proofBlind = try GroupImpl.Scalar(bytes: Data(hexString: vector.Proof!.r)) + + var info: Data? + if (vector.Info?.count ?? 0) > 0 { info = try Data(hexString: vector.Info!) } + + let evaluate = try server.evaluate(blindedElement: blindedElement, info: info, proofScalar: proofBlind) + + let evaluatedElementTv = try GroupImpl.Element(oprfRepresentation: Data(hexString: vector.EvaluationElement)) + XCTAssert(evaluate.0 == evaluatedElementTv) + + let proof_tv = vector.Proof!.proof + let c_tv = try GroupImpl.Scalar(bytes: Data(hexString: String(proof_tv.prefix(proof_tv.count / 2)))) + let s_tv = try GroupImpl.Scalar(bytes: Data(hexString: String(proof_tv.suffix(proof_tv.count / 2)))) + let proof = DLEQProof(c: c_tv, s: s_tv) + + switch (vector.result) { + case .some(Result.invalidProof.rawValue): do { + XCTAssertThrowsError(try client.finalize(message: input, + info: info, + blind: blind, + evaluatedElement: evaluate.0, + proof: proof, + publicKey: server.publicKey), + error: OPRF.Errors.invalidProof) + } + case .some(Result.invalidOPRFOutput.rawValue): do { + XCTAssertFalse(try server.verifyFinalize(msg: input, output: Data(hexString: vector.Output), info: info)) + } + default: + XCTAssert(c_tv == evaluate.1.c) + XCTAssert(s_tv == evaluate.1.s) + let finalized = try client.finalize(message: input, + info: info, + blind: blind, + evaluatedElement: evaluate.0, + proof: proof, + publicKey: server.publicKey) + + XCTAssert(finalized.hexString == vector.Output) + XCTAssert(try server.verifyFinalize(msg: input, output: finalized, info: info)) + } + } + } + + func testVectors() throws { + print("Testing VOPRF Draft8 vectors.") + try testVectors(filename: "OPRFVectors-VOPRFDraft8", v8DraftCompatible: true) + print("Testing VOPRF Draft19 vectors.") + try testVectors(filename: "OPRFVectors-VOPRFDraft19", v8DraftCompatible: false) + print("Testing VOPRF edge case vectors.") + try testVectors(filename: "OPRFVectors-edgecases", v8DraftCompatible: false) + } + + func testVectors(filename: String, v8DraftCompatible: Bool) throws { + #if CRYPTO_IN_SWIFTPM + let bundle = Bundle.module + #else + let bundle = Bundle(for: type(of: self)) + #endif + + let fileURL = bundle.url(forResource: filename, withExtension: "json") + + let data = try Data(contentsOf: fileURL!) + let decoder = JSONDecoder() + let suites = try decoder.decode([OPRFSuite].self, from: data) + + if v8DraftCompatible { + for suite in suites { + switch (suite.suiteID, suite.mode) { + case (OPRF.Ciphersuite(HashToCurveImpl.self).suiteID, let modeValue): do { + try testSuite(suite: suite, C: P256.self, modeValue: modeValue, v8DraftCompatible: v8DraftCompatible) + } + case (OPRF.Ciphersuite(HashToCurveImpl.self).suiteID, let modeValue): do { + try testSuite(suite: suite, C: P384.self, modeValue: modeValue, v8DraftCompatible: v8DraftCompatible) + } +// case (OPRF.Ciphersuite(HashToCurveImpl.self).suiteID, let modeValue): do { +// try testSuite(suite: suite, C: P521.self, modeValue: modeValue, v8DraftCompatible: v8DraftCompatible) +// } + + default: + print("Unsupported Ciphersuite: \(suite.suiteName ?? suite.identifier!)") + } + } + } else { + for suite in suites { + switch (suite.identifier, suite.mode) { + case (OPRF.Ciphersuite(HashToCurveImpl.self).stringIdentifier, let modeValue): do { + try testSuite(suite: suite, C: P256.self, modeValue: modeValue, v8DraftCompatible: v8DraftCompatible) + } + case (OPRF.Ciphersuite(HashToCurveImpl.self).stringIdentifier, let modeValue): do { + try testSuite(suite: suite, C: P384.self, modeValue: modeValue, v8DraftCompatible: v8DraftCompatible) + } +// case (OPRF.Ciphersuite(HashToCurveImpl.self).stringIdentifier, let modeValue): do { +// try testSuite(suite: suite, C: P521.self, modeValue: modeValue, v8DraftCompatible: v8DraftCompatible) +// } + + default: + print("Unsupported Ciphersuite: \(suite.suiteName ?? suite.identifier!)") + } + } + } + } + + func testDistributivity() throws { + let r = GroupImpl.Scalar.random + + let a = GroupImpl.Scalar.random + let b = GroupImpl.Scalar.random + + let ab = (a + b) + let ar = a * r + let br = b * r + + XCTAssert(ab - b == a) + XCTAssert(ab - a == b) + + let arbr = (ab) * r + + XCTAssert(arbr - br == ar) + XCTAssert(arbr - ar == br) + } + + func testMath() throws { + let r = GroupImpl.Scalar.random + let k = GroupImpl.Scalar.random + let c = GroupImpl.Scalar.random + + let A = GroupImpl.Element.generator + let B = k * A + + let m = GroupImpl.Scalar.random + let z = k * m + + let t2 = r * A + let t3 = r * m + + let s = (r - c * k) + let t2_recontructed = (s * A) + (c * B) + let t3_reconstructed = ((s * m) + (c * z)) + + XCTAssert(t2 == t2_recontructed) + XCTAssert(t3.rawRepresentation == t3_reconstructed.rawRepresentation) + } + + func testDLEQProver() throws { + let k = GroupImpl.Scalar.random + let A = GroupImpl.Element.generator + let B = k * A + + let C = GroupImpl.Element.random + let D = k * C + + let CDs = [(C: C, D: D)] + + let proof = try DLEQ>.proveEquivalenceBetween(k: k, A: A, B: B, CDs: CDs, dst: Data(), proofScalar: .random, v8CompatibilityMode: false) + + XCTAssert(try DLEQ>.verifyProof(A: A, B: B, CDs: CDs, proof: proof, dst: Data(), v8CompatibilityMode: false)) + } +} diff --git a/Tests/_CryptoExtrasTests/OPRFs/OPRFVectors/OPRFVectors-VOPRFDraft19.json b/Tests/_CryptoExtrasTests/OPRFs/OPRFVectors/OPRFVectors-VOPRFDraft19.json new file mode 100644 index 00000000..4b215426 --- /dev/null +++ b/Tests/_CryptoExtrasTests/OPRFs/OPRFVectors/OPRFVectors-VOPRFDraft19.json @@ -0,0 +1,632 @@ +[ + { + "groupDST": "48617368546f47726f75702d4f50524656312d002d72697374726574746f3235352d534841353132", + "hash": "SHA512", + "identifier": "ristretto255-SHA512", + "keyInfo": "74657374206b6579", + "mode": 0, + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "5ebcea5ee37023ccb9fc2d2019f9d7737be85591ae8652ffa9ef0f4d37063b0e", + "vectors": [ + { + "Batch": 1, + "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f6706", + "BlindedElement": "609a0ae68c15a3cf6903766461307e5c8bb2f95e7e6550e1ffa2dc99e412803c", + "EvaluationElement": "7ec6578ae5120958eb2db1745758ff379e77cb64fe77b0b2d8cc917ea0869c7e", + "Input": "00", + "Output": "527759c3d9366f277d8c6020418d96bb393ba2afb20ff90df23fb7708264e2f3ab9135e3bd69955851de4b1f9fe8a0973396719b7912ba9ee8aa7d0b5e24bcf6" + }, + { + "Batch": 1, + "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f6706", + "BlindedElement": "da27ef466870f5f15296299850aa088629945a17d1f5b7f5ff043f76b3c06418", + "EvaluationElement": "b4cbf5a4f1eeda5a63ce7b77c7d23f461db3fcab0dd28e4e17cecb5c90d02c25", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "f4a74c9c592497375e796aa837e907b1a045d34306a749db9f34221f7e750cb4f2a6413a6bf6fa5e19ba6348eb673934a722a7ede2e7621306d18951e7cf2c73" + } + ] + }, + { + "groupDST": "48617368546f47726f75702d4f50524656312d012d72697374726574746f3235352d534841353132", + "hash": "SHA512", + "identifier": "ristretto255-SHA512", + "keyInfo": "74657374206b6579", + "mode": 1, + "pkSm": "c803e2cc6b05fc15064549b5920659ca4a77b2cca6f04f6b357009335476ad4e", + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "e6f73f344b79b379f1a0dd37e07ff62e38d9f71345ce62ae3a9bc60b04ccd909", + "vectors": [ + { + "Batch": 1, + "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f6706", + "BlindedElement": "863f330cc1a1259ed5a5998a23acfd37fb4351a793a5b3c090b642ddc439b945", + "EvaluationElement": "aa8fa048764d5623868679402ff6108d2521884fa138cd7f9c7669a9a014267e", + "Input": "00", + "Output": "b58cfbe118e0cb94d79b5fd6a6dafb98764dff49c14e1770b566e42402da1a7da4d8527693914139caee5bd03903af43a491351d23b430948dd50cde10d32b3c", + "Proof": { + "proof": "ddef93772692e535d1a53903db24367355cc2cc78de93b3be5a8ffcc6985dd066d4346421d17bf5117a2a1ff0fcb2a759f58a539dfbe857a40bce4cf49ec600d", + "r": "222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d9881aa6f61d645fc0e" + } + }, + { + "Batch": 1, + "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f6706", + "BlindedElement": "cc0b2a350101881d8a4cba4c80241d74fb7dcbfde4a61fde2f91443c2bf9ef0c", + "EvaluationElement": "60a59a57208d48aca71e9e850d22674b611f752bed48b36f7a91b372bd7ad468", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "8a9a2f3c7f085b65933594309041fc1898d42d0858e59f90814ae90571a6df60356f4610bf816f27afdd84f47719e480906d27ecd994985890e5f539e7ea74b6", + "Proof": { + "proof": "401a0da6264f8cf45bb2f5264bc31e109155600babb3cd4e5af7d181a2c9dc0a67154fabf031fd936051dec80b0b6ae29c9503493dde7393b722eafdf5a50b02", + "r": "222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d9881aa6f61d645fc0e" + } + }, + { + "Batch": 2, + "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f6706,222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d9881aa6f61d645fc0e", + "BlindedElement": "863f330cc1a1259ed5a5998a23acfd37fb4351a793a5b3c090b642ddc439b945,90a0145ea9da29254c3a56be4fe185465ebb3bf2a1801f7124bbbadac751e654", + "EvaluationElement": "aa8fa048764d5623868679402ff6108d2521884fa138cd7f9c7669a9a014267e,cc5ac221950a49ceaa73c8db41b82c20372a4c8d63e5dded2db920b7eee36a2a", + "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "b58cfbe118e0cb94d79b5fd6a6dafb98764dff49c14e1770b566e42402da1a7da4d8527693914139caee5bd03903af43a491351d23b430948dd50cde10d32b3c,8a9a2f3c7f085b65933594309041fc1898d42d0858e59f90814ae90571a6df60356f4610bf816f27afdd84f47719e480906d27ecd994985890e5f539e7ea74b6", + "Proof": { + "proof": "cc203910175d786927eeb44ea847328047892ddf8590e723c37205cb74600b0a5ab5337c8eb4ceae0494c2cf89529dcf94572ed267473d567aeed6ab873dee08", + "r": "419c4f4f5052c53c45f3da494d2b67b220d02118e0857cdbcf037f9ea84bbe0c" + } + } + ] + }, + { + "groupDST": "48617368546f47726f75702d4f50524656312d022d72697374726574746f3235352d534841353132", + "hash": "SHA512", + "identifier": "ristretto255-SHA512", + "keyInfo": "74657374206b6579", + "mode": 2, + "pkSm": "c647bef38497bc6ec077c22af65b696efa43bff3b4a1975a3e8e0a1c5a79d631", + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "145c79c108538421ac164ecbe131942136d5570b16d8bf41a24d4337da981e07", + "vectors": [ + { + "Batch": 1, + "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f6706", + "BlindedElement": "c8713aa89241d6989ac142f22dba30596db635c772cbf25021fdd8f3d461f715", + "EvaluationElement": "1a4b860d808ff19624731e67b5eff20ceb2df3c3c03b906f5693e2078450d874", + "Info": "7465737420696e666f", + "Input": "00", + "Output": "ca688351e88afb1d841fde4401c79efebb2eb75e7998fa9737bd5a82a152406d38bd29f680504e54fd4587eddcf2f37a2617ac2fbd2993f7bdf45442ace7d221", + "Proof": { + "proof": "41ad1a291aa02c80b0915fbfbb0c0afa15a57e2970067a602ddb9e8fd6b7100de32e1ecff943a36f0b10e3dae6bd266cdeb8adf825d86ef27dbc6c0e30c52206", + "r": "222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d9881aa6f61d645fc0e" + } + }, + { + "Batch": 1, + "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f6706", + "BlindedElement": "f0f0b209dd4d5f1844dac679acc7761b91a2e704879656cb7c201e82a99ab07d", + "EvaluationElement": "8c3c9d064c334c6991e99f286ea2301d1bde170b54003fb9c44c6d7bd6fc1540", + "Info": "7465737420696e666f", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "7c6557b276a137922a0bcfc2aa2b35dd78322bd500235eb6d6b6f91bc5b56a52de2d65612d503236b321f5d0bebcbc52b64b92e426f29c9b8b69f52de98ae507", + "Proof": { + "proof": "4c39992d55ffba38232cdac88fe583af8a85441fefd7d1d4a8d0394cd1de77018bf135c174f20281b3341ab1f453fe72b0293a7398703384bed822bfdeec8908", + "r": "222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d9881aa6f61d645fc0e" + } + }, + { + "Batch": 2, + "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f6706,222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d9881aa6f61d645fc0e", + "BlindedElement": "c8713aa89241d6989ac142f22dba30596db635c772cbf25021fdd8f3d461f715,423a01c072e06eb1cce96d23acce06e1ea64a609d7ec9e9023f3049f2d64e50c", + "EvaluationElement": "1a4b860d808ff19624731e67b5eff20ceb2df3c3c03b906f5693e2078450d874,aa1f16e903841036e38075da8a46655c94fc92341887eb5819f46312adfc0504", + "Info": "7465737420696e666f", + "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "ca688351e88afb1d841fde4401c79efebb2eb75e7998fa9737bd5a82a152406d38bd29f680504e54fd4587eddcf2f37a2617ac2fbd2993f7bdf45442ace7d221,7c6557b276a137922a0bcfc2aa2b35dd78322bd500235eb6d6b6f91bc5b56a52de2d65612d503236b321f5d0bebcbc52b64b92e426f29c9b8b69f52de98ae507", + "Proof": { + "proof": "43fdb53be399cbd3561186ae480320caa2b9f36cca0e5b160c4a677b8bbf4301b28f12c36aa8e11e5a7ef551da0781e863a6dc8c0b2bf5a149c9e00621f02006", + "r": "419c4f4f5052c53c45f3da494d2b67b220d02118e0857cdbcf037f9ea84bbe0c" + } + } + ] + }, + { + "groupDST": "48617368546f47726f75702d4f50524656312d002d64656361663434382d5348414b45323536", + "hash": "SHAKE_256", + "identifier": "decaf448-SHAKE256", + "keyInfo": "74657374206b6579", + "mode": 0, + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "e8b1375371fd11ebeb224f832dcc16d371b4188951c438f751425699ed29ecc80c6c13e558ccd67634fd82eac94aa8d1f0d7fee990695d1e", + "vectors": [ + { + "Batch": 1, + "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa3833a26e9388336361686ff1f83df55046504dfecad8549ba112", + "BlindedElement": "e0ae01c4095f08e03b19baf47ffdc19cb7d98e583160522a3c7d6a0b2111cd93a126a46b7b41b730cd7fc943d4e28e590ed33ae475885f6c", + "EvaluationElement": "50ce4e60eed006e22e7027454b5a4b8319eb2bc8ced609eb19eb3ad42fb19e06ba12d382cbe7ae342a0cad6ead0ef8f91f00bb7f0cd9c0a2", + "Input": "00", + "Output": "37d3f7922d9388a15b561de5829bbf654c4089ede89c0ce0f3f85bcdba09e382ce0ab3507e021f9e79706a1798ffeac68ebd5cf62e5eb9838c7068351d97ae37" + }, + { + "Batch": 1, + "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa3833a26e9388336361686ff1f83df55046504dfecad8549ba112", + "BlindedElement": "86a88dc5c6331ecfcb1d9aacb50a68213803c462e377577cacc00af28e15f0ddbc2e3d716f2f39ef95f3ec1314a2c64d940a9f295d8f13bb", + "EvaluationElement": "162e9fa6e9d527c3cd734a31bf122a34dbd5bcb7bb23651f1768a7a9274cc116c03b58afa6f0dede3994a60066c76370e7328e7062fd5819", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "a2a652290055cb0f6f8637a249ee45e32ef4667db0b4c80c0a70d2a64164d01525cfdad5d870a694ec77972b9b6ec5d2596a5223e5336913f945101f0137f55e" + } + ] + }, + { + "groupDST": "48617368546f47726f75702d4f50524656312d012d64656361663434382d5348414b45323536", + "hash": "SHAKE_256", + "identifier": "decaf448-SHAKE256", + "keyInfo": "74657374206b6579", + "mode": 1, + "pkSm": "945fc518c47695cf65217ace04b86ac5e4cbe26ca649d52854bb16c494ce09069d6add96b20d4b0ae311a87c9a73e3a146b525763ab2f955", + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "e3c01519a076a326a0eb566343e9b21c115fa18e6e85577ddbe890b33104fcc2835ddfb14a928dc3f5d79b936e17c76b99e0bf6a1680930e", + "vectors": [ + { + "Batch": 1, + "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa3833a26e9388336361686ff1f83df55046504dfecad8549ba112", + "BlindedElement": "7261bbc335c664ba788f1b1a1a4cd5190cc30e787ef277665ac1d314f8861e3ec11854ce3ddd42035d9e0f5cddde324c332d8c880abc00eb", + "EvaluationElement": "ca1491a526c28d880806cf0fb0122222392cf495657be6e4c9d203bceffa46c86406caf8217859d3fb259077af68e5d41b3699410781f467", + "Input": "00", + "Output": "e2ac40b634f36cccd8262b285adff7c9dcc19cd308564a5f4e581d1a8535773b86fa4fc9f2203c370763695c5093aea4a7aedec4488b1340ba3bf663a23098c1", + "Proof": { + "proof": "f84bbeee47aedf43558dae4b95b3853635a9fc1a9ea7eac9b454c64c66c4f49cd1c72711c7ac2e06c681e16ea693d5500bbd7b56455df52f69e00b76b4126961e1562fdbaaac40b7701065cbeece3febbfe09e00160f81775d36daed99d8a2a10be0759e01b7ee81217203416c9db208", + "r": "b1b748135d405ce48c6973401d9455bb8ccd18b01d0295c0627f67661200dbf9569f73fbb3925daa043a070e5f953d80bb464ea369e5522b" + } + }, + { + "Batch": 1, + "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa3833a26e9388336361686ff1f83df55046504dfecad8549ba112", + "BlindedElement": "88287e553939090b888ddc15913e1807dc4757215555e1c3a79488ef311594729c7fa74c772a732b78440b7d66d0aa35f3bb316f1d93e1b2", + "EvaluationElement": "c00978c73e8e4ee1d447ab0d3ad1754055e72cc85c08e3a0db170909a9c61cbff1f1e7015f289e3038b0f341faea5d7780c130106065c231", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "862952380e07ec840d9f6e6f909c5a25d16c3dacb586d89a181b4aa7380c959baa8c480fe8e6c64e089d68ea7aeeb5817bd524d7577905b5bab487690048c941", + "Proof": { + "proof": "7a2831a6b237e11ac1657d440df93bc5ce00f552e6020a99d5c956ffc4d07b5ade3e82ecdc257fd53d76239e733e0a1313e84ce16cc0d82734806092a693d7e8d3c420c2cb6ccd5d0ca32514fb78e9ad0973ebdcb52eba438fc73948d76339ee710121d83e2fe6f001cfdf551aff9f36", + "r": "b1b748135d405ce48c6973401d9455bb8ccd18b01d0295c0627f67661200dbf9569f73fbb3925daa043a070e5f953d80bb464ea369e5522b" + } + }, + { + "Batch": 2, + "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa3833a26e9388336361686ff1f83df55046504dfecad8549ba112,b1b748135d405ce48c6973401d9455bb8ccd18b01d0295c0627f67661200dbf9569f73fbb3925daa043a070e5f953d80bb464ea369e5522b", + "BlindedElement": "7261bbc335c664ba788f1b1a1a4cd5190cc30e787ef277665ac1d314f8861e3ec11854ce3ddd42035d9e0f5cddde324c332d8c880abc00eb,2e15f393c035492a1573627a3606e528c6294c767c8d43b8c691ef70a52cc7dc7d1b53fe458350a270abb7c231b87ba58266f89164f714d9", + "EvaluationElement": "ca1491a526c28d880806cf0fb0122222392cf495657be6e4c9d203bceffa46c86406caf8217859d3fb259077af68e5d41b3699410781f467,8ec68e9871b296e81c55647ce64a04fe75d19932f1400544cd601468c60f998408bbb546601d4a636e8be279e558d70b95c8d4a4f61892be", + "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "e2ac40b634f36cccd8262b285adff7c9dcc19cd308564a5f4e581d1a8535773b86fa4fc9f2203c370763695c5093aea4a7aedec4488b1340ba3bf663a23098c1,862952380e07ec840d9f6e6f909c5a25d16c3dacb586d89a181b4aa7380c959baa8c480fe8e6c64e089d68ea7aeeb5817bd524d7577905b5bab487690048c941", + "Proof": { + "proof": "167d922f0a6ffa845eed07f8aa97b6ac746d902ecbeb18f49c009adc0521eab1e4d275b74a2dc266b7a194c854e85e7eb54a9a36376dfc04ec7f3bd55fc9618c3970cb548e064f8a2f06183a5702933dbc3e4c25a73438f2108ee1981c306181003c7ea92fce963ec7b4ba4f270e6d38", + "r": "63798726803c9451ba405f00ef3acb633ddf0c420574a2ec6cbf28f840800e355c9fbaac10699686de2724ed22e797a00f3bd93d105a7f23" + } + } + ] + }, + { + "groupDST": "48617368546f47726f75702d4f50524656312d022d64656361663434382d5348414b45323536", + "hash": "SHAKE_256", + "identifier": "decaf448-SHAKE256", + "keyInfo": "74657374206b6579", + "mode": 2, + "pkSm": "6c9d12723a5bbcf305522cc04b4a34d9ced2e12831826018ea7b5dcf5452647ad262113059bf0f6e4354319951b9d513c74f29cb0eec38c1", + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "792a10dcbd3ba4a52a054f6f39186623208695301e7adb9634b74709ab22de402990eb143fd7c67ac66be75e0609705ecea800992aac8e19", + "vectors": [ + { + "Batch": 1, + "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa3833a26e9388336361686ff1f83df55046504dfecad8549ba112", + "BlindedElement": "161183c13c6cb33b0e4f9b7365f8c5c12d13c72f8b62d276ca09368d093dce9b42198276b9e9d870ac392dda53efd28d1b7e6e8c060cdc42", + "EvaluationElement": "06ec89dfde25bb2a6f0145ac84b91ac277b35de39ad1d6f402a8e46414952ce0d9ea1311a4ece283e2b01558c7078b040cfaa40dd63b3e6c", + "Info": "7465737420696e666f", + "Input": "00", + "Output": "4423f6dcc1740688ea201de57d76824d59cd6b859e1f9884b7eebc49b0b971358cf9cb075df1536a8ea31bcf55c3e31c2ba9cfa8efe54448d17091daeb9924ed", + "Proof": { + "proof": "66caee75bf2460429f620f6ad3e811d524cb8ddd848a435fc5d89af48877abf6506ee341a0b6f67c2d76cd021e5f3d1c9abe5aa9f0dce016da746135fedba2af41ed1d01659bfd6180d96bc1b7f320c0cb6926011ce392ecca748662564892bae66516acaac6ca39aadf6fcca95af406", + "r": "b1b748135d405ce48c6973401d9455bb8ccd18b01d0295c0627f67661200dbf9569f73fbb3925daa043a070e5f953d80bb464ea369e5522b" + } + }, + { + "Batch": 1, + "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa3833a26e9388336361686ff1f83df55046504dfecad8549ba112", + "BlindedElement": "12082b6a381c6c51e85d00f2a3d828cdeab3f5cb19a10b9c014c33826764ab7e7cfb8b4ff6f411bddb2d64e62a472af1cd816e5b712790c6", + "EvaluationElement": "f2919b7eedc05ab807c221fce2b12c4ae9e19e6909c4784564b690d1972d2994ca623f273afc67444d84ea40cbc58fcdab7945f321a52848", + "Info": "7465737420696e666f", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "8691905500510843902c44bdd9730ab9dc3925aa58ff9dd42765a2baf633126de0c3adb93bef5652f38e5827b6396e87643960163a560fc4ac9738c8de4e4a8d", + "Proof": { + "proof": "a295677c54d1bc4286330907fc2490a7de163da26f9ce03a462a452fea422b19ade296ba031359b3b6841e48455d20519ad01b4ac4f0b92e76d3cf16fbef0a3f72791a8401ef2d7081d361e502e96b2c60608b9fa566f43d4611c2f161d83aabef7f8017332b26ed1daaf80440772022", + "r": "b1b748135d405ce48c6973401d9455bb8ccd18b01d0295c0627f67661200dbf9569f73fbb3925daa043a070e5f953d80bb464ea369e5522b" + } + }, + { + "Batch": 2, + "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa3833a26e9388336361686ff1f83df55046504dfecad8549ba112,b1b748135d405ce48c6973401d9455bb8ccd18b01d0295c0627f67661200dbf9569f73fbb3925daa043a070e5f953d80bb464ea369e5522b", + "BlindedElement": "161183c13c6cb33b0e4f9b7365f8c5c12d13c72f8b62d276ca09368d093dce9b42198276b9e9d870ac392dda53efd28d1b7e6e8c060cdc42,fc8847d43fb4cea4e408f585661a8f2867533fa91d22155d3127a22f18d3b007add480f7d300bca93fa47fe87ae06a57b7d0f0d4c30b12f0", + "EvaluationElement": "06ec89dfde25bb2a6f0145ac84b91ac277b35de39ad1d6f402a8e46414952ce0d9ea1311a4ece283e2b01558c7078b040cfaa40dd63b3e6c,2e74c626d07de49b1c8c21d87120fd78105f485e36816af9bde3e3efbeef76815326062fd333925b66c5ce5a20f100bf01770c16609f990a", + "Info": "7465737420696e666f", + "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "4423f6dcc1740688ea201de57d76824d59cd6b859e1f9884b7eebc49b0b971358cf9cb075df1536a8ea31bcf55c3e31c2ba9cfa8efe54448d17091daeb9924ed,8691905500510843902c44bdd9730ab9dc3925aa58ff9dd42765a2baf633126de0c3adb93bef5652f38e5827b6396e87643960163a560fc4ac9738c8de4e4a8d", + "Proof": { + "proof": "fd94db736f97ea4efe9d0d4ad2933072697a6bbeb32834057b23edf7c7009f011dfa72157f05d2a507c2bbf0b54cad99ab99de05921c021fda7d70e65bcecdb05f9a30154127ace983c74d10fd910b554c5e95f6bd1565fd1f3dbbe3c523ece5c72d57a559b7be1368c4786db4a3c910", + "r": "63798726803c9451ba405f00ef3acb633ddf0c420574a2ec6cbf28f840800e355c9fbaac10699686de2724ed22e797a00f3bd93d105a7f23" + } + } + ] + }, + { + "groupDST": "48617368546f47726f75702d4f50524656312d002d503235362d534841323536", + "hash": "SHA256", + "identifier": "P256-SHA256", + "keyInfo": "74657374206b6579", + "mode": 0, + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "159749d750713afe245d2d39ccfaae8381c53ce92d098a9375ee70739c7ac0bf", + "vectors": [ + { + "Batch": 1, + "Blind": "3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "03723a1e5c09b8b9c18d1dcbca29e8007e95f14f4732d9346d490ffc195110368d", + "EvaluationElement": "030de02ffec47a1fd53efcdd1c6faf5bdc270912b8749e783c7ca75bb412958832", + "Input": "00", + "Output": "a0b34de5fa4c5b6da07e72af73cc507cceeb48981b97b7285fc375345fe495dd" + }, + { + "Batch": 1, + "Blind": "3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "03cc1df781f1c2240a64d1c297b3f3d16262ef5d4cf102734882675c26231b0838", + "EvaluationElement": "03a0395fe3828f2476ffcd1f4fe540e5a8489322d398be3c4e5a869db7fcb7c52c", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "c748ca6dd327f0ce85f4ae3a8cd6d4d5390bbb804c9e12dcf94f853fece3dcce" + } + ] + }, + { + "groupDST": "48617368546f47726f75702d4f50524656312d012d503235362d534841323536", + "hash": "SHA256", + "identifier": "P256-SHA256", + "keyInfo": "74657374206b6579", + "mode": 1, + "pkSm": "03e17e70604bcabe198882c0a1f27a92441e774224ed9c702e51dd17038b102462", + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "ca5d94c8807817669a51b196c34c1b7f8442fde4334a7121ae4736364312fca6", + "vectors": [ + { + "Batch": 1, + "Blind": "3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "02dd05901038bb31a6fae01828fd8d0e49e35a486b5c5d4b4994013648c01277da", + "EvaluationElement": "0209f33cab60cf8fe69239b0afbcfcd261af4c1c5632624f2e9ba29b90ae83e4a2", + "Input": "00", + "Output": "0412e8f78b02c415ab3a288e228978376f99927767ff37c5718d420010a645a1", + "Proof": { + "proof": "e7c2b3c5c954c035949f1f74e6bce2ed539a3be267d1481e9ddb178533df4c2664f69d065c604a4fd953e100b856ad83804eb3845189babfa5a702090d6fc5fa", + "r": "f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" + } + }, + { + "Batch": 1, + "Blind": "3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "03cd0f033e791c4d79dfa9c6ed750f2ac009ec46cd4195ca6fd3800d1e9b887dbd", + "EvaluationElement": "030d2985865c693bf7af47ba4d3a3813176576383d19aff003ef7b0784a0d83cf1", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "771e10dcd6bcd3664e23b8f2a710cfaaa8357747c4a8cbba03133967b5c24f18", + "Proof": { + "proof": "2787d729c57e3d9512d3aa9e8708ad226bc48e0f1750b0767aaff73482c44b8d2873d74ec88aebd3504961acea16790a05c542d9fbff4fe269a77510db00abab", + "r": "f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" + } + }, + { + "Batch": 2, + "Blind": "3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364,f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1", + "BlindedElement": "02dd05901038bb31a6fae01828fd8d0e49e35a486b5c5d4b4994013648c01277da,03462e9ae64cae5b83ba98a6b360d942266389ac369b923eb3d557213b1922f8ab", + "EvaluationElement": "0209f33cab60cf8fe69239b0afbcfcd261af4c1c5632624f2e9ba29b90ae83e4a2,02bb24f4d838414aef052a8f044a6771230ca69c0a5677540fff738dd31bb69771", + "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "0412e8f78b02c415ab3a288e228978376f99927767ff37c5718d420010a645a1,771e10dcd6bcd3664e23b8f2a710cfaaa8357747c4a8cbba03133967b5c24f18", + "Proof": { + "proof": "bdcc351707d02a72ce49511c7db990566d29d6153ad6f8982fad2b435d6ce4d60da1e6b3fa740811bde34dd4fe0aa1b5fe6600d0440c9ddee95ea7fad7a60cf2", + "r": "350e8040f828bf6ceca27405420cdf3d63cb3aef005f40ba51943c8026877963" + } + } + ] + }, + { + "groupDST": "48617368546f47726f75702d4f50524656312d022d503235362d534841323536", + "hash": "SHA256", + "identifier": "P256-SHA256", + "keyInfo": "74657374206b6579", + "mode": 2, + "pkSm": "030d7ff077fddeec965db14b794f0cc1ba9019b04a2f4fcc1fa525dedf72e2a3e3", + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "6ad2173efa689ef2c27772566ad7ff6e2d59b3b196f00219451fb2c89ee4dae2", + "vectors": [ + { + "Batch": 1, + "Blind": "3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "031563e127099a8f61ed51eeede05d747a8da2be329b40ba1f0db0b2bd9dd4e2c0", + "EvaluationElement": "02c5e5300c2d9e6ba7f3f4ad60500ad93a0157e6288eb04b67e125db024a2c74d2", + "Info": "7465737420696e666f", + "Input": "00", + "Output": "193a92520bd8fd1f37accb918040a57108daa110dc4f659abe212636d245c592", + "Proof": { + "proof": "f8a33690b87736c854eadfcaab58a59b8d9c03b569110b6f31f8bf7577f3fbb85a8a0c38468ccde1ba942be501654adb106167c8eb178703ccb42bccffb9231a", + "r": "f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" + } + }, + { + "Batch": 1, + "Blind": "3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "021a440ace8ca667f261c10ac7686adc66a12be31e3520fca317643a1eee9dcd4d", + "EvaluationElement": "0208ca109cbae44f4774fc0bdd2783efdcb868cb4523d52196f700210e777c5de3", + "Info": "7465737420696e666f", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "1e6d164cfd835d88a31401623549bf6b9b306628ef03a7962921d62bc5ffce8c", + "Proof": { + "proof": "043a8fb7fc7fd31e35770cabda4753c5bf0ecc1e88c68d7d35a62bf2631e875af4613641be2d1875c31d1319d191c4bbc0d04875f4fd03c31d3d17dd8e069b69", + "r": "f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" + } + }, + { + "Batch": 2, + "Blind": "3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364,f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1", + "BlindedElement": "031563e127099a8f61ed51eeede05d747a8da2be329b40ba1f0db0b2bd9dd4e2c0,03ca4ff41c12fadd7a0bc92cf856732b21df652e01a3abdf0fa8847da053db213c", + "EvaluationElement": "02c5e5300c2d9e6ba7f3f4ad60500ad93a0157e6288eb04b67e125db024a2c74d2,02f0b6bcd467343a8d8555a99dc2eed0215c71898c5edb77a3d97ddd0dbad478e8", + "Info": "7465737420696e666f", + "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "193a92520bd8fd1f37accb918040a57108daa110dc4f659abe212636d245c592,1e6d164cfd835d88a31401623549bf6b9b306628ef03a7962921d62bc5ffce8c", + "Proof": { + "proof": "8fbd85a32c13aba79db4b42e762c00687d6dbf9c8cb97b2a225645ccb00d9d7580b383c885cdfd07df448d55e06f50f6173405eee5506c0ed0851ff718d13e68", + "r": "350e8040f828bf6ceca27405420cdf3d63cb3aef005f40ba51943c8026877963" + } + } + ] + }, + { + "groupDST": "48617368546f47726f75702d4f50524656312d002d503338342d534841333834", + "hash": "SHA384", + "identifier": "P384-SHA384", + "keyInfo": "74657374206b6579", + "mode": 0, + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "dfe7ddc41a4646901184f2b432616c8ba6d452f9bcd0c4f75a5150ef2b2ed02ef40b8b92f60ae591bcabd72a6518f188", + "vectors": [ + { + "Batch": 1, + "Blind": "504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "02a36bc90e6db34096346eaf8b7bc40ee1113582155ad3797003ce614c835a874343701d3f2debbd80d97cbe45de6e5f1f", + "EvaluationElement": "03af2a4fc94770d7a7bf3187ca9cc4faf3732049eded2442ee50fbddda58b70ae2999366f72498cdbc43e6f2fc184afe30", + "Input": "00", + "Output": "ed84ad3f31a552f0456e58935fcc0a3039db42e7f356dcb32aa6d487b6b815a07d5813641fb1398c03ddab5763874357" + }, + { + "Batch": 1, + "Blind": "504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "02def6f418e3484f67a124a2ce1bfb19de7a4af568ede6a1ebb2733882510ddd43d05f2b1ab5187936a55e50a847a8b900", + "EvaluationElement": "034e9b9a2960b536f2ef47d8608b21597ba400d5abfa1825fd21c36b75f927f396bf3716c96129d1fa4a77fa1d479c8d7b", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "dd4f29da869ab9355d60617b60da0991e22aaab243a3460601e48b075859d1c526d36597326f1b985778f781a1682e75" + } + ] + }, + { + "groupDST": "48617368546f47726f75702d4f50524656312d012d503338342d534841333834", + "hash": "SHA384", + "identifier": "P384-SHA384", + "keyInfo": "74657374206b6579", + "mode": 1, + "pkSm": "031d689686c611991b55f1a1d8f4305ccd6cb719446f660a30db61b7aa87b46acf59b7c0d4a9077b3da21c25dd482229a0", + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "051646b9e6e7a71ae27c1e1d0b87b4381db6d3595eeeb1adb41579adbf992f4278f9016eafc944edaa2b43183581779d", + "vectors": [ + { + "Batch": 1, + "Blind": "504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "02d338c05cbecb82de13d6700f09cb61190543a7b7e2c6cd4fca56887e564ea82653b27fdad383995ea6d02cf26d0e24d9", + "EvaluationElement": "02a7bba589b3e8672aa19e8fd258de2e6aae20101c8d761246de97a6b5ee9cf105febce4327a326255a3c604f63f600ef6", + "Input": "00", + "Output": "3333230886b562ffb8329a8be08fea8025755372817ec969d114d1203d026b4a622beab60220bf19078bca35a529b35c", + "Proof": { + "proof": "bfc6cf3859127f5fe25548859856d6b7fa1c7459f0ba5712a806fc091a3000c42d8ba34ff45f32a52e40533efd2a03bc87f3bf4f9f58028297ccb9ccb18ae7182bcd1ef239df77e3be65ef147f3acf8bc9cbfc5524b702263414f043e3b7ca2e", + "r": "803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" + } + }, + { + "Batch": 1, + "Blind": "504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "02f27469e059886f221be5f2cca03d2bdc61e55221721c3b3e56fc012e36d31ae5f8dc058109591556a6dbd3a8c69c433b", + "EvaluationElement": "03f16f903947035400e96b7f531a38d4a07ac89a80f89d86a1bf089c525a92c7f4733729ca30c56ce78b1ab4f7d92db8b4", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "b91c70ea3d4d62ba922eb8a7d03809a441e1c3c7af915cbc2226f485213e895942cd0f8580e6d99f82221e66c40d274f", + "Proof": { + "proof": "d005d6daaad7571414c1e0c75f7e57f2113ca9f4604e84bc90f9be52da896fff3bee496dcde2a578ae9df315032585f801fb21c6080ac05672b291e575a40295b306d967717b28e08fcc8ad1cab47845d16af73b3e643ddcc191208e71c64630", + "r": "803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" + } + }, + { + "Batch": 2, + "Blind": "504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364,803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1", + "BlindedElement": "02d338c05cbecb82de13d6700f09cb61190543a7b7e2c6cd4fca56887e564ea82653b27fdad383995ea6d02cf26d0e24d9,02fa02470d7f151018b41e82223c32fad824de6ad4b5ce9f8e9f98083c9a726de9a1fc39d7a0cb6f4f188dd9cea01474cd", + "EvaluationElement": "02a7bba589b3e8672aa19e8fd258de2e6aae20101c8d761246de97a6b5ee9cf105febce4327a326255a3c604f63f600ef6,028e9e115625ff4c2f07bf87ce3fd73fc77994a7a0c1df03d2a630a3d845930e2e63a165b114d98fe34e61b68d23c0b50a", + "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "3333230886b562ffb8329a8be08fea8025755372817ec969d114d1203d026b4a622beab60220bf19078bca35a529b35c,b91c70ea3d4d62ba922eb8a7d03809a441e1c3c7af915cbc2226f485213e895942cd0f8580e6d99f82221e66c40d274f", + "Proof": { + "proof": "6d8dcbd2fc95550a02211fb78afd013933f307d21e7d855b0b1ed0af78076d8137ad8b0a1bfa05676d325249c1dbb9a52bd81b1c2b7b0efc77cf7b278e1c947f6283f1d4c513053fc0ad19e026fb0c30654b53d9cea4b87b037271b5d2e2d0ea", + "r": "a097e722ed2427de86966910acba9f5c350e8040f828bf6ceca27405420cdf3d63cb3aef005f40ba51943c8026877963" + } + } + ] + }, + { + "groupDST": "48617368546f47726f75702d4f50524656312d022d503338342d534841333834", + "hash": "SHA384", + "identifier": "P384-SHA384", + "keyInfo": "74657374206b6579", + "mode": 2, + "pkSm": "02f00f0f1de81e5d6cf18140d4926ffdc9b1898c48dc49657ae36eb1e45deb8b951aaf1f10c82d2eaa6d02aafa3f10d2b6", + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "5b2690d6954b8fbb159f19935d64133f12770c00b68422559c65431942d721ff79d47d7a75906c30b7818ec0f38b7fb2", + "vectors": [ + { + "Batch": 1, + "Blind": "504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "03859b36b95e6564faa85cd3801175eda2949707f6aa0640ad093cbf8ad2f58e762f08b56b2a1b42a64953aaf49cbf1ae3", + "EvaluationElement": "0220710e2e00306453f5b4f574cb6a512453f35c45080d09373e190c19ce5b185914fbf36582d7e0754bb7c8b683205b91", + "Info": "7465737420696e666f", + "Input": "00", + "Output": "0188653cfec38119a6c7dd7948b0f0720460b4310e40824e048bf82a16527303ed449a08caf84272c3bbc972ede797df", + "Proof": { + "proof": "82a17ef41c8b57f1e3122311b4d5cd39a63df0f67443ef18d961f9b659c1601ced8d3c64b294f604319ca80230380d437a49c7af0d620e22116669c008ebb767d90283d573b49cdb49e3725889620924c2c4b047a2a6225a3ba27e640ebddd33", + "r": "803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" + } + }, + { + "Batch": 1, + "Blind": "504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "03f7efcb4aaf000263369d8a0621cb96b81b3206e99876de2a00699ed4c45acf3969cd6e2319215395955d3f8d8cc1c712", + "EvaluationElement": "034993c818369927e74b77c400376fd1ae29b6ac6c6ddb776cf10e4fbc487826531b3cf0b7c8ca4d92c7af90c9def85ce6", + "Info": "7465737420696e666f", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "ff2a527a21cc43b251a567382677f078c6e356336aec069dea8ba36995343ca3b33bb5d6cf15be4d31a7e6d75b30d3f5", + "Proof": { + "proof": "693471b5dff0cd6a5c00ea34d7bf127b2795164e3bdb5f39a1e5edfbd13e443bc516061cd5b8449a473c2ceeccada9f3e5b57302e3d7bc5e28d38d6e3a3056e1e73b6cc030f5180f8a1ffa45aa923ee66d2ad0a07b500f2acc7fb99b5506465c", + "r": "803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" + } + }, + { + "Batch": 2, + "Blind": "504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364,803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1", + "BlindedElement": "03859b36b95e6564faa85cd3801175eda2949707f6aa0640ad093cbf8ad2f58e762f08b56b2a1b42a64953aaf49cbf1ae3,021a65d618d645f1a20bc33b06deaa7e73d6d634c8a56a3d02b53a732b69a5c53c5a207ea33d5afdcde9a22d59726bce51", + "EvaluationElement": "0220710e2e00306453f5b4f574cb6a512453f35c45080d09373e190c19ce5b185914fbf36582d7e0754bb7c8b683205b91,02017657b315ec65ef861505e596c8645d94685dd7602cdd092a8f1c1c0194a5d0485fe47d071d972ab514370174cc23f5", + "Info": "7465737420696e666f", + "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "0188653cfec38119a6c7dd7948b0f0720460b4310e40824e048bf82a16527303ed449a08caf84272c3bbc972ede797df,ff2a527a21cc43b251a567382677f078c6e356336aec069dea8ba36995343ca3b33bb5d6cf15be4d31a7e6d75b30d3f5", + "Proof": { + "proof": "4a0b2fe96d5b2a046a0447fe079b77859ef11a39a3520d6ff7c626aad9b473b724fb0cf188974ec961710a62162a83e97e0baa9eeada73397032d928b3e97b1ea92ad9458208302be3681b8ba78bcc17745bac00f84e0fdc98a6a8cba009c080", + "r": "a097e722ed2427de86966910acba9f5c350e8040f828bf6ceca27405420cdf3d63cb3aef005f40ba51943c8026877963" + } + } + ] + }, + { + "groupDST": "48617368546f47726f75702d4f50524656312d002d503532312d534841353132", + "hash": "SHA512", + "identifier": "P521-SHA512", + "keyInfo": "74657374206b6579", + "mode": 0, + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "0153441b8faedb0340439036d6aed06d1217b34c42f17f8db4c5cc610a4a955d698a688831b16d0dc7713a1aa3611ec60703bffc7dc9c84e3ed673b3dbe1d5fccea6", + "vectors": [ + { + "Batch": 1, + "Blind": "00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "0300e78bf846b0e1e1a3c320e353d758583cd876df56100a3a1e62bacba470fa6e0991be1be80b721c50c5fd0c672ba764457acc18c6200704e9294fbf28859d916351", + "EvaluationElement": "030166371cf827cb2fb9b581f97907121a16e2dc5d8b10ce9f0ede7f7d76a0d047657735e8ad07bcda824907b3e5479bd72cdef6b839b967ba5c58b118b84d26f2ba07", + "Input": "00", + "Output": "26232de6fff83f812adadadb6cc05d7bbeee5dca043dbb16b03488abb9981d0a1ef4351fad52dbd7e759649af393348f7b9717566c19a6b8856284d69375c809" + }, + { + "Batch": 1, + "Blind": "00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "0300c28e57e74361d87e0c1874e5f7cc1cc796d61f9cad50427cf54655cdb455613368d42b27f94bf66f59f53c816db3e95e68e1b113443d66a99b3693bab88afb556b", + "EvaluationElement": "0301ad453607e12d0cc11a3359332a40c3a254eaa1afc64296528d55bed07ba322e72e22cf3bcb50570fd913cb54f7f09c17aff8787af75f6a7faf5640cbb2d9620a6e", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "ad1f76ef939042175e007738906ac0336bbd1d51e287ebaa66901abdd324ea3ffa40bfc5a68e7939c2845e0fd37a5a6e76dadb9907c6cc8579629757fd4d04ba" + } + ] + }, + { + "groupDST": "48617368546f47726f75702d4f50524656312d012d503532312d534841353132", + "hash": "SHA512", + "identifier": "P521-SHA512", + "keyInfo": "74657374206b6579", + "mode": 1, + "pkSm": "0301505d646f6e4c9102451eb39730c4ba1c4087618641edbdba4a60896b07fd0c9414ce553cbf25b81dfcca50a8f6724ab7a2bc4d0cf736967a287bb6084cc0678ac0", + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "015c7fc1b4a0b1390925bae915bd9f3d72009d44d9241b962428aad5d13f22803311e7102632a39addc61ea440810222715c9d2f61f03ea424ec9ab1fe5e31cf9238", + "vectors": [ + { + "Batch": 1, + "Blind": "00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "0301d6e4fb545e043ddb6aee5d5ceeee1b44102615ab04430c27dd0f56988dedcb1df32ef384f160e0e76e718605f14f3f582f9357553d153b996795b4b3628a4f6380", + "EvaluationElement": "03013fdeaf887f3d3d283a79e696a54b66ff0edcb559265e204a958acf840e0930cc147e2a6835148d8199eebc26c03e9394c9762a1c991dde40bca0f8ca003eefb045", + "Input": "00", + "Output": "5e003d9b2fb540b3d4bab5fedd154912246da1ee5e557afd8f56415faa1a0fadff6517da802ee254437e4f60907b4cda146e7ba19e249eef7be405549f62954b", + "Proof": { + "proof": "0077fcc8ec6d059d7759b0a61f871e7c1dadc65333502e09a51994328f79e5bda3357b9a4f410a1760a3612c2f8f27cb7cb032951c047cc66da60da583df7b247edd0188e5eb99c71799af1d80d643af16ffa1545acd9e9233fbb370455b10eb257ea12a1667c1b4ee5b0ab7c93d50ae89602006960f083ca9adc4f6276c0ad60440393c", + "r": "015e80ae32363b32cb76ad4b95a5a34e46bb803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" + } + }, + { + "Batch": 1, + "Blind": "00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "03005b05e656cb609ce5ff5faf063bb746d662d67bbd07c062638396f52f0392180cf2365cabb0ece8e19048961d35eeae5d5fa872328dce98df076ee154dd191c615e", + "EvaluationElement": "0301b19fcf482b1fff04754e282292ed736c5f0aa080d4f42663cd3a416c6596f03129e8e096d8671fe5b0d19838312c511d2ce08d431e43e3ef06199d8cab7426238d", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "fa15eebba81ecf40954f7135cb76f69ef22c6bae394d1a4362f9b03066b54b6604d39f2e53369ca6762a3d9787e230e832aa85955af40ecb8deebb009a8cf474", + "Proof": { + "proof": "01ec9fece444caa6a57032e8963df0e945286f88fbdf233fb5101f0924f7ea89c47023f5f72f240e61991fd33a299b5b38c45a5e2dd1a67b072e59dfe86708a359c701e38d383c60cf6969463bcf13251bedad47b7941f52e409a3591398e27924410b18a301c0e19f527cad504fa08388050ac634e1b05c5216d337742f2754e1fc502f", + "r": "015e80ae32363b32cb76ad4b95a5a34e46bb803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" + } + }, + { + "Batch": 2, + "Blind": "00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364,015e80ae32363b32cb76ad4b95a5a34e46bb803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1", + "BlindedElement": "0301d6e4fb545e043ddb6aee5d5ceeee1b44102615ab04430c27dd0f56988dedcb1df32ef384f160e0e76e718605f14f3f582f9357553d153b996795b4b3628a4f6380,0301403b597538b939b450c93586ba275f9711ba07e42364bac1d5769c6824a8b55be6f9a536df46d952b11ab2188363b3d6737635d9543d4dba14a6e19421b9245bf5", + "EvaluationElement": "03013fdeaf887f3d3d283a79e696a54b66ff0edcb559265e204a958acf840e0930cc147e2a6835148d8199eebc26c03e9394c9762a1c991dde40bca0f8ca003eefb045,03001f96424497e38c46c904978c2fa1636c5c3dd2e634a85d8a7265977c5dce1f02c7e6c118479f0751767b91a39cce6561998258591b5d7c1bb02445a9e08e4f3e8d", + "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "5e003d9b2fb540b3d4bab5fedd154912246da1ee5e557afd8f56415faa1a0fadff6517da802ee254437e4f60907b4cda146e7ba19e249eef7be405549f62954b,fa15eebba81ecf40954f7135cb76f69ef22c6bae394d1a4362f9b03066b54b6604d39f2e53369ca6762a3d9787e230e832aa85955af40ecb8deebb009a8cf474", + "Proof": { + "proof": "00b4d215c8405e57c7a4b53398caf55f1f1623aaeb22408ddb9ea29130909b3f95dbb1ff366e81e86e918f9f2fd8b80dbb344cd498c9499d112905e585417e0068c600fe5dea18b389ef6c4cc062935607b8ccbbb9a84fba3143868a3e8a58efa0bf6ca642804d09dc06e980f64837811227c4267b217f1099a4e28b0854f4e5ee659796", + "r": "01ec21c7bb69b0734cb48dfd68433dd93b0fa097e722ed2427de86966910acba9f5c350e8040f828bf6ceca27405420cdf3d63cb3aef005f40ba51943c8026877963" + } + } + ] + }, + { + "groupDST": "48617368546f47726f75702d4f50524656312d022d503532312d534841353132", + "hash": "SHA512", + "identifier": "P521-SHA512", + "keyInfo": "74657374206b6579", + "mode": 2, + "pkSm": "0301de8ceb9ffe9237b1bba87c320ea0bebcfc3447fe6f278065c6c69886d692d1126b79b6844f829940ace9b52a5e26882cf7cbc9e57503d4cca3cd834584729f812a", + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "014893130030ce69cf714f536498a02ff6b396888f9bb507985c32928c4427d6d39de10ef509aca4240e8569e3a88debc0d392e3361bcd934cb9bdd59e339dff7b27", + "vectors": [ + { + "Batch": 1, + "Blind": "00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "020095cff9d7ecf65bdfee4ea92d6e748d60b02de34ad98094f82e25d33a8bf50138ccc2cc633556f1a97d7ea9438cbb394df612f041c485a515849d5ebb2238f2f0e2", + "EvaluationElement": "0301408e9c5be3ffcc1c16e5ae8f8aa68446223b0804b11962e856af5a6d1c65ebbb5db7278c21db4e8cc06d89a35b6804fb1738a295b691638af77aa1327253f26d01", + "Info": "7465737420696e666f", + "Input": "00", + "Output": "808ae5b87662eaaf0b39151dd85991b94c96ef214cb14a68bf5c143954882d330da8953a80eea20788e552bc8bbbfff3100e89f9d6e341197b122c46a208733b", + "Proof": { + "proof": "0106a89a61eee9dd2417d2849a8e2167bc5f56e3aed5a3ff23e22511fa1b37a29ed44d1bbfd6907d99cfbc558a56aec709282415a864a281e49dc53792a4a638a0660034306d64be12a94dcea5a6d664cf76681911c8b9a84d49bf12d4893307ec14436bd05f791f82446c0de4be6c582d373627b51886f76c4788256e3da7ec8fa18a86", + "r": "015e80ae32363b32cb76ad4b95a5a34e46bb803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" + } + }, + { + "Batch": 1, + "Blind": "00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "030112ea89cf9cf589496189eafc5f9eb13c9f9e170d6ecde7c5b940541cb1a9c5cfeec908b67efe16b81ca00d0ce216e34b3d5f46a658d3fd8573d671bdb6515ed508", + "EvaluationElement": "0200ebc49df1e6fa61f412e6c391e6f074400ecdd2f56c4a8c03fe0f91d9b551f40d4b5258fd891952e8c9b28003bcfa365122e54a5714c8949d5d202767b31b4bf1f6", + "Info": "7465737420696e666f", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "27032e24b1a52a82ab7f4646f3c5df0f070f499db98b9c5df33972bd5af5762c3638afae7912a6c1acdb1ae2ab2fa670bd5486c645a0e55412e08d33a4a0d6e3", + "Proof": { + "proof": "0082162c71a7765005cae202d4bd14b84dae63c29067e886b82506992bd994a1c3aac0c1c5309222fe1af8287b6443ed6df5c2e0b0991faddd3564c73c7597aecd9a003b1f1e3c65f28e58ab4e767cfb4adbcaf512441645f4c2aed8bf67d132d966006d35fa71a34145414bf3572c1de1a46c266a344dd9e22e7fb1e90ffba1caf556d9", + "r": "015e80ae32363b32cb76ad4b95a5a34e46bb803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" + } + }, + { + "Batch": 2, + "Blind": "00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364,015e80ae32363b32cb76ad4b95a5a34e46bb803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1", + "BlindedElement": "020095cff9d7ecf65bdfee4ea92d6e748d60b02de34ad98094f82e25d33a8bf50138ccc2cc633556f1a97d7ea9438cbb394df612f041c485a515849d5ebb2238f2f0e2,0201a328cf9f3fdeb86b6db242dd4cbb436b3a488b70b72d2fbbd1e5f50d7b0878b157d6f278c6a95c488f3ad52d6898a421658a82fe7ceb000b01aedea7967522d525", + "EvaluationElement": "0301408e9c5be3ffcc1c16e5ae8f8aa68446223b0804b11962e856af5a6d1c65ebbb5db7278c21db4e8cc06d89a35b6804fb1738a295b691638af77aa1327253f26d01,020062ab51ac3aa829e0f5b7ae50688bcf5f63a18a83a6e0da538666b8d50c7ea2b4ef31f4ac669302318dbebe46660acdda695da30c22cee7ca21f6984a720504502e", + "Info": "7465737420696e666f", + "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "808ae5b87662eaaf0b39151dd85991b94c96ef214cb14a68bf5c143954882d330da8953a80eea20788e552bc8bbbfff3100e89f9d6e341197b122c46a208733b,27032e24b1a52a82ab7f4646f3c5df0f070f499db98b9c5df33972bd5af5762c3638afae7912a6c1acdb1ae2ab2fa670bd5486c645a0e55412e08d33a4a0d6e3", + "Proof": { + "proof": "00731738844f739bca0cca9d1c8bea204bed4fd00285785738b985763741de5cdfa275152d52b6a2fdf7792ef3779f39ba34581e56d62f78ecad5b7f8083f384961501cd4b43713253c022692669cf076b1d382ecd8293c1de69ea569737f37a24772ab73517983c1e3db5818754ba1f008076267b8058b6481949ae346cdc17a8455fe2", + "r": "01ec21c7bb69b0734cb48dfd68433dd93b0fa097e722ed2427de86966910acba9f5c350e8040f828bf6ceca27405420cdf3d63cb3aef005f40ba51943c8026877963" + } + } + ] + } +] diff --git a/Tests/_CryptoExtrasTests/OPRFs/OPRFVectors/OPRFVectors-VOPRFDraft8.json b/Tests/_CryptoExtrasTests/OPRFs/OPRFVectors/OPRFVectors-VOPRFDraft8.json new file mode 100644 index 00000000..34b9819f --- /dev/null +++ b/Tests/_CryptoExtrasTests/OPRFs/OPRFVectors/OPRFVectors-VOPRFDraft8.json @@ -0,0 +1,402 @@ +[ + { + "groupDST": "48617368546f47726f75702d564f50524630382d000001", + "hash": "SHA512", + "mode": 0, + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "74db8e13d2c5148a1181d57cc06debd730da4df1978b72ac18bc48992a0d2c0f", + "suiteID": 1, + "suiteName": "OPRF(ristretto255, SHA-512)", + "vectors": [ + { + "Batch": 1, + "Blind": "c604c785ada70d77a5256ae21767de8c3304115237d262134f5e46e512cf8e03", + "BlindedElement": "744441a5d3ee12571a84d34812443eba2b6521a47265ad655f01e759b3dd7d35", + "EvaluationElement": "4254c503ee2013262473eec926b109b018d699b8dd954ee878bc17b159696353", + "Info": "7465737420696e666f", + "Input": "00", + "Output": "9aef8983b729baacb7ecf1be98d1276ca29e7d62dbf39bc595be018b66b199119f18579a9ae96a39d7d506c9e00f75b433a870d76ba755a3e7196911fff89ff3" + }, + { + "Batch": 1, + "Blind": "5ed895206bfc53316d307b23e46ecc6623afb3086da74189a416012be037e50b", + "BlindedElement": "f4eeea4e1bcb2ec818ee2d5c1fcec56c24064a9ff4bea5b3dd6877800fc28e4d", + "EvaluationElement": "185dae43b6209dacbc41a62fd4889700d11eeeff4e83ffbc72d54daee7e25659", + "Info": "7465737420696e666f", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "f556e2d83e576b4edc890472572d08f0d90d2ecc52a73b35b2a8416a72ff676549e3a83054fdf4fd16fe03e03bee7bb32cbd83c7ca212ea0d03b8996c2c268b2" + } + ] + }, + { + "groupDST": "48617368546f47726f75702d564f50524630382d010001", + "hash": "SHA512", + "mode": 1, + "pkSm": "7a5627aec2f2209a2fc62f39f57a8f5ffc4bbfd679d0273e6081b2b621ee3b52", + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "ad08ad9c7107691d792d346d743e8a79b8f6ae0673d58cbf7389d7003598c903", + "suiteID": 1, + "suiteName": "OPRF(ristretto255, SHA-512)", + "vectors": [ + { + "Batch": 1, + "Blind": "ed8366feb6b1d05d1f46acb727061e43aadfafe9c10e5a64e7518d63e3263503", + "BlindedElement": "56c6926e940df23d5dfe6a48949c5a9e5b503df3bff36454ba4821afa1528718", + "EvaluationElement": "523774950001072a4fb1f1f3300f7feb1eeddb5b8304baa9c3d463c11e7f0509", + "Info": "7465737420696e666f", + "Input": "00", + "Output": "2d9ed987fdfa623a5b4d5e445b127e86212b7c8f2567c175b424c59602fbba7c36975df5e4ecdf060430c8b1b581fc97e953535fd82089e15afbafcf310b3399", + "Proof": { + "proof": "c973c8cfbcdbb12a09e7640e44e45d85d420ed0539a18dc6c67c189b4f28c70dd32f9b13717ee073e1e73333a7cb17545dd42ed8a2008c5dae11a3bd7e70260d", + "r": "019cbd1d7420292528f8cdd62f339fdabb602f04a95dac9dbcec831b8c681a09" + } + }, + { + "Batch": 1, + "Blind": "e6d0f1d89ad552e383d6c6f4e8598cc3037d6e274d22da3089e7afbd4171ea02", + "BlindedElement": "5cd133d03df2e1ff919ed85501319c2039853dd7dc59da73605fd5791b835d23", + "EvaluationElement": "c0ba1012cbfb0338dadb435ef1d910eb179dc18c0d0a341f0249a3a9ff03b06e", + "Info": "7465737420696e666f", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "f5da1276b5ca3de4591534cf2d96f7bb49059bd374f40259f42dca89d723cac69ed3ae567128aaa2dfdf777f333615524aec24bc77b0a38e200e6a07b6c638eb", + "Proof": { + "proof": "156761aee4eb6a5e1e32bc0adb56ea46d65883777e152d4c607a3a3b8abf3b036ecebae005d3f26222a8da0a3924cceed8a1a7c707ef4ba077456c3e80f8c40f", + "r": "74ae06fd50d5f26c2519bd7b184f45dd3ef2cb50197d42df9d013f7d6c312a0b" + } + }, + { + "Batch": 2, + "Blind": "80513e77795feeec6d2c450589b0e1b178febd5c193a9fcba0d27f0a06e0d50f,533c2e6d91c934f919ac218973be55ba0d7b234160a0d4cf3bddafbda99e2e0c", + "BlindedElement": "1c7ee9c1b4145dabeba9ad159531432a20718cb44a86f79dc73f6f8671c9bf5e,7c1ef37881602cb6d3cf995e6ee310ed51e39b80ce0a825a316bc621d0580a14", + "EvaluationElement": "a8a66348d351408cb7e2d26341a1258ba91c1a7d1b380f6215bdfc242500991b,5a4b72bee9d2ca80ea220571690e2f92fadd0c13635b2888bc1ff255f8fee975", + "Info": "7465737420696e666f", + "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "2d9ed987fdfa623a5b4d5e445b127e86212b7c8f2567c175b424c59602fbba7c36975df5e4ecdf060430c8b1b581fc97e953535fd82089e15afbafcf310b3399,f5da1276b5ca3de4591534cf2d96f7bb49059bd374f40259f42dca89d723cac69ed3ae567128aaa2dfdf777f333615524aec24bc77b0a38e200e6a07b6c638eb", + "Proof": { + "proof": "caad28bac17ce71d59b43956e8d80f3edde3d0c317144bef3d10d9733ef1cf09fd910c663ea85ad7cfaf641d73314694fe18d3f6b89cfe001b18163ff908d10a", + "r": "3af5aec325791592eee4a8860522f8444c8e71ac33af5186a9706137886dce08" + } + } + ] + }, + { + "groupDST": "48617368546f47726f75702d564f50524630382d000002", + "hash": "SHAKE_256", + "mode": 0, + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "82c2a6492e1792e6ccdf1d7cff410c717681bd53ad47da7646b14ebd0588553e4c034e02b3ae5e724600a17a638ad528c04f793df56c2618", + "suiteID": 2, + "suiteName": "OPRF(decaf448, SHAKE-256)", + "vectors": [ + { + "Batch": 1, + "Blind": "d1080372f0fcf8c5eace50914e7127f576725f215cc7c111673c635ce668bbbb9b50601ad89b358ab8c23ed0b6c9d040365ec9d060868714", + "BlindedElement": "1c354d6d31500c7c5ae6fb10901ac87552ea3af1824e79871e2596ef537f86abac64859cf6f35911ab74f0b09a06ecc757a65a104e9e49fb", + "EvaluationElement": "9e5bbf27b2312a493b2f2f1d051b7cdf3801769ec5dc072451b68c4d0d4ed9303979ec4798261a01fabd8d25540f48a11dd8342fded95383", + "Info": "7465737420696e666f", + "Input": "00", + "Output": "5f8c28d5e760786cbd000ac58444bd216141472b9370b058408a714da5e3dd51fc572f96c99a9338bc8569abc991bc1523fa1467cd3a0de3aef7f154bd65d92e" + }, + { + "Batch": 1, + "Blind": "aed1ffa44fd8f0ed16373606a3cf7df589cca86d8ea1abbf5768771dbef3d401c74ae55ba1e28b9565e1e4018eb261a14134a4ce60c1c718", + "BlindedElement": "e8111f22d50595f68f01a6a9135f50e8702c90794c2637fbe009046f0c455884cc77ee7a87f3abf494afe780b3620ab0e7fb65c65ba902b2", + "EvaluationElement": "0ec625f99914ba702f0e6bc5d0f837cb4deaf7ab3ac554587182c3dfe1dad6d1540964f9581d26e8ef0a47b61c5f145109a5fffe04ad528e", + "Info": "7465737420696e666f", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "7f0e40c08d8220f88c0961925f764ee0e4e08909d497f462a97a2030b40b44986fa76d344efb9b0acab23db81356fc8c380b80701a61a5fa76097a5d2ea7aa9e" + } + ] + }, + { + "groupDST": "48617368546f47726f75702d564f50524630382d010002", + "hash": "SHAKE_256", + "mode": 1, + "pkSm": "8e623ef9b65ef2ce148ce56249ee5e69ed6acd3e504a07905cc4c093125518d30ae7d6de274438b822d5a55a4365216ac588a4c400fbf6ff", + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "5d295b55d1d6e46411bbb4151d154dc61711012ff2390255b3345988f8e3c458089d52e9b1d837049898f9e4e63a4534f0ed3b3a47c7051c", + "suiteID": 2, + "suiteName": "OPRF(decaf448, SHAKE-256)", + "vectors": [ + { + "Batch": 1, + "Blind": "4c936db1779a621b6c71475ac3111fd5703a59b713929f36dfd1e892a7fe814479c93d8b4b6e11d1f6fe5351e51457b665fa7b76074e531f", + "BlindedElement": "74bb2406b15a86ba94b0686901545f8ddc23e64918de47c76fa0bf812387021392c73e01068ac9cc07c7647b3d0d4e648c27bb3880ddb8e5", + "EvaluationElement": "90997b495c19f16561a3286a7bcba9a4ee6e12bab4d580d5004ae5064d90a389124e81066f3f1dbf9a729ab46ed674c3292f56d54a0d5641", + "Info": "7465737420696e666f", + "Input": "00", + "Output": "7db8c49354861f2d71c8175681c9cc930a00251330b2acc5c321f9833fed4113a1cb3e05a3840082c24e8d49470474dd1c7586f3663f32f66dc3888c63dc0e6e", + "Proof": { + "proof": "668f6ef88b249d51b6c94bfe82f2bec35ab7386bc9f3d14209d0247a5b6ebedec4c333947fff96d322f516f4674cc07638b8e854c52be7045d83d65aff51810460ec43417a6c6efbfb67ba7b0257b1237c64e6792195e338474d09df32b076c0b702ec8c639b34c29878b87aad70d63c", + "r": "1b3f5a55b2f18f8c53d4ecf2e1c27e1028f1c345bb5044864aa9dd8439d7520a7ba6183d50ef08bdf6c781aa465660c93e8195a8d231b62f" + } + }, + { + "Batch": 1, + "Blind": "15b3355179392f40c3d5a15f0d5ffc354e340454ec779f575e4573a3886ab5e57e4da2985cea9e32f6d95539ce2c7189e1bd7462e8c5483a", + "BlindedElement": "ea3418614d71144ac4ecbd2c63c30ce34718b739ba0a5dd3585efd9800b9debdad4cffc25dcc39b4691aaffba19ead8a425d7d50f016f57e", + "EvaluationElement": "7e12ab491c3787a1f17118f7a0308f8c41f4cd6e850cf7faba030b6c1bf1888337149e7c2fc88068626a0107be18e8b9e29f41c8d1510049", + "Info": "7465737420696e666f", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "66125718c5d651c88ab57dda67c52a506d436600f1521b7684c869b9a2b3e67d1b41c47593e79fd6b70aaae8d3689536897ae8964ffcd433c0884c12c94929dc", + "Proof": { + "proof": "91ed184bf518a155749a99d39bed3f9dc9895054e55fab0ebd0ce4270e8452fcc8da055e8c2f75f2306ecacaa594de592e0d0b059b8eb30e15d5c3132b71ebc4933596c563ee8ce8681e0e40534e92ce487a0e33e341f02a9aaa1f750d9efa7545a0008b2f8dde5047ce68d00c2e962e", + "r": "2f2e9955be83a4b25743ebd3618d4fad8b7288477da50bed9befa58af639ddd950fec34205f8a4f166fadcb8fa71a3ffdd2e98f4c8ef5e26" + } + }, + { + "Batch": 2, + "Blind": "614bb578f29cc677ea9e7aea3e4839413997e020f9377b63c13584156a09a46dd2a425c41eac0e313a47e99d05df72c6e1d58e6592577a0d,4c115060bca87db7d73e00cbb8559f84cb7a221b235b0950a0ab553f03f10e1386abe954011b7da62bb6599418ef90b5d4ea98cc28aff517", + "BlindedElement": "909b0b8bcb900bd9e70f27258d7264015c50f3717361afff22d16ad84758d2c6b7a1963263d0d035f63b88df8b473f9365c53abcec34b201,726315ee47e217344da7036a24f806177e221c9f6eae5763f9089b16bada69b85aec56c3ca83b6f5f1091640ea3fe3e9429ff2aa7772efef", + "EvaluationElement": "46d8dec85a27698b4b69a67299eab1da0ec2bbed013a3a59b932e2938e2e2c5bcc8274febf49b7903419c18b895f17c4a9a504737d7a3fdc,fe47eca9d06b400c80cc2b749284312c6f97c7b5d88055fe56b068c441e053fe909c6c22bb7cd646a932e2d3838b7b3e2e883cfe0ed1a2a1", + "Info": "7465737420696e666f", + "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "7db8c49354861f2d71c8175681c9cc930a00251330b2acc5c321f9833fed4113a1cb3e05a3840082c24e8d49470474dd1c7586f3663f32f66dc3888c63dc0e6e,66125718c5d651c88ab57dda67c52a506d436600f1521b7684c869b9a2b3e67d1b41c47593e79fd6b70aaae8d3689536897ae8964ffcd433c0884c12c94929dc", + "Proof": { + "proof": "1f63637de4f945f5937ac015a508420f119f7b6a8e001439a1923a1705ceee704ad17664ff4c72f89566f83ceccee3001d44d849ac4dad2bc05b9bc718ba787fc3c5b09198c4ab244455bac64a9a231b18c4682c0e6e30ae5398f5c041ee2c5b02c619b7497c5bf070fdb4656353de1d", + "r": "a614f1894bcf6a1c7cef33909b794fe6e69a642b20f4c9118febffaf6b6a31471fe7794aa77ced123f07e56cc27de60b0ab106c0b8eab127" + } + } + ] + }, + { + "groupDST": "48617368546f47726f75702d564f50524630382d000003", + "hash": "SHA256", + "mode": 0, + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "c15d9e9ab36d495d9d62954db6aafe06d3edabf41600d58f9be0737af2719e97", + "suiteID": 3, + "suiteName": "OPRF(P-256, SHA-256)", + "vectors": [ + { + "Batch": 1, + "Blind": "5d9e7f6efd3093c32ecceabd57fb03cf760c926d2a7bfa265babf29ec98af0d0", + "BlindedElement": "03e9097c54d2ea05f99424bdf984ea30ecc3614029bd5f1139e70c4e1ae3bdbd92", + "EvaluationElement": "0202e4d1a338659c211900c39855f30025359928d261e6c9558d667b3fbbc811cd", + "Info": "7465737420696e666f", + "Input": "00", + "Output": "15b96275d06b85741f491fe0cad5cb835baa6c39066cbea73132dcf95e858e1c" + }, + { + "Batch": 1, + "Blind": "825155ab61f17605af2ae2e935c78d857c9407bcd45128d57d338f1671b5fcbe", + "BlindedElement": "03fa1ea45dd58d6b516c1252f2791610bf5ff1828c93be8af66786f45fb4d14db5", + "EvaluationElement": "02657822553416d91bb3d707040fd0d5a0555f5cbae7519df3a297747a3ad1dd57", + "Info": "7465737420696e666f", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "e97f3f451f3cfce45a530dec0a0dec934cd78c5b656771549072ee236ce070b9" + } + ] + }, + { + "groupDST": "48617368546f47726f75702d564f50524630382d010003", + "hash": "SHA256", + "mode": 1, + "pkSm": "03d6c3f69cfa683418a533fc52143a377f166e571ae50581abcb97ffd4e7124395", + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "7f62054fcd598b5e023c08ef0f04e05e26867438d5e355e846c9d8788d5c7a12", + "suiteID": 3, + "suiteName": "OPRF(P-256, SHA-256)", + "vectors": [ + { + "Batch": 1, + "Blind": "cee64d86fd20ab4caa264a26c0e3d42fb773b3173ba76f9588c9b14779bd8d91", + "BlindedElement": "029e103c4003ab9bf4a42e2003dd180922c8517927a68320058178fee56c6ac8a0", + "EvaluationElement": "02856ac0748085d250d842b8b8fff6c1a9f688c961de52c4a1e6c004c48196a123", + "Info": "7465737420696e666f", + "Input": "00", + "Output": "14afc50acf64589445991da5b60add8b3f71205d53a983023d3cdaf8c95c300d", + "Proof": { + "proof": "2a95bd827cf47873c886967ef6c17fe0e46efddd3b5f639927215cb7592a4bf12a29117174a1af5899d64855352690e416b37f2a95580846a6bec445d82364fc", + "r": "70a5204b2b606f5a28328916e1e5ea5a17862d7a261fdd6d959759758d5e34ac" + } + }, + { + "Batch": 1, + "Blind": "5c4b401063eff0bf242b4cd534a79bacfc2e715b2db1e7a3ad4ff8af1b24daa2", + "BlindedElement": "0323aabcfa93e9570524253671b3ce083144b183cecb562ec8f8a8472fc8cf341b", + "EvaluationElement": "03087bc7e00b8ad80b8a27484b91f8bf824a5d896a7031354edfa3269866493d9f", + "Info": "7465737420696e666f", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "533c79459ee0ffa8844ac37572f3616e10a1074dcbf945ce37b0c651cbb5775f", + "Proof": { + "proof": "fe55ecc9a92f940d4a56207a58e5554c6976b9425c917d24237b0a35c312bdcdea778a5c56690309ff28f26cc8bc5994e85868e3c870e5a32c0a559d80deccb8", + "r": "3b9217801b5d51cef66d9fdbd94a53533e7c5057e09e220065ea8c257c0dd606" + } + }, + { + "Batch": 2, + "Blind": "f0c7822ba317fb5e86028c44b92bd3aedcf6744d388ca013ef33edd369304eda,3b9631be9f8b274d9aaf671bfb6a775229bf435021b89c683259773bc686956b", + "BlindedElement": "021af4563c31cf1513bc5ae0b89c5b527c7ac70614b9d31c44ceb292ab49c91cc4,03f7e7ebe5610710c360df40cbd90dc52c2da500664e879f2afb78e71f815abee1", + "EvaluationElement": "03c8678cdb95e2f0eac027932c51893a20326b774ef23531bcd95def84060d240d,02b68c3891314a9696b5dff5df4b4e5b325938e2c5cb90f5fb9ba6a1133aa4dd14", + "Info": "7465737420696e666f", + "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "14afc50acf64589445991da5b60add8b3f71205d53a983023d3cdaf8c95c300d,533c79459ee0ffa8844ac37572f3616e10a1074dcbf945ce37b0c651cbb5775f", + "Proof": { + "proof": "6efbde69d36e3f9d53a79a73ce46d5d8ef31f0df2fb3f6f2c882b21fdf0ed76dcd755e42f35f00daaa6e964f48125cf1d642b1cea2e5faa2fb868584a8752bf2", + "r": "8306b863276ae74049615162a416d507a6532c99c1ea3f03d05f6e78dc1edabe" + } + } + ] + }, + { + "groupDST": "48617368546f47726f75702d564f50524630382d000004", + "hash": "SHA384", + "mode": 0, + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "b9ff42e68ef6f8eaa3b4d15d15ceb6f3f36b9dc332a3473d64840fc7b44626c6e70336bdecbe01d9c512b7e7d7e6af21", + "suiteID": 4, + "suiteName": "OPRF(P-384, SHA-384)", + "vectors": [ + { + "Batch": 1, + "Blind": "359073c015b92d15450f7fb395bf52c6ea98384c491fe4e4d423b59de7b0df382902c13bdc9993d3717bda68fc080b99", + "BlindedElement": "0285d803c65fda56993a296b99e8f4944e45cccb9b322bbc265c91a21d2c9cd146212aefbf3126ed59d84c32d6ab823b66", + "EvaluationElement": "026061a4ccfe38777e725855c96570fe85303cd70567007e489d0aa8bfced0e47579ecbc290e5150b9e84bf25188294f7e", + "Info": "7465737420696e666f", + "Input": "00", + "Output": "bc2c3c895f96d769703aec18359cbc0e84b41248559f0bd44f1e54675223c77e00874bbe61c1c320d3c95aee5a8c752f" + }, + { + "Batch": 1, + "Blind": "21ece4f9b6ffd01ce82082545413bd9bb5e8f3c63b86ae88d9ce0530b01cb1c23382c7ec9bdd6e75898e4877d8e2bc17", + "BlindedElement": "0211dd06e40b902006c33a92dc476a7c708b6b46c990656239cd6867ff0be5867d859517eaf7ea9bad10702b80a9dc6bdc", + "EvaluationElement": "03a1d34b657f6267b29338592e3c769db5d3fc8713bf2eb7238efb8138d5af8c56f9437315a5c58761b35cbfc0e1d2511d", + "Info": "7465737420696e666f", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "ee37530d0d7b20635fbc476317343b257750ffb3e83a2865ce2a46e59591f854b8301d6ca7d063322314a33b953c8bd5" + } + ] + }, + { + "groupDST": "48617368546f47726f75702d564f50524630382d010004", + "hash": "SHA384", + "mode": 1, + "pkSm": "0389ad5e50eebf9617ae3a5778e4f0665b56aa9066919e1fa5580d8dd781d560824e0e78aae816af6eff8abe2ad0585a0d", + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "42c4d1c15d27be015844404088967afe48c8ae96d4f00ce48e4d38ecabfb8feb5b748de625cdf81ab076745d6211be95", + "suiteID": 4, + "suiteName": "OPRF(P-384, SHA-384)", + "vectors": [ + { + "Batch": 1, + "Blind": "102f6338df84c9602bfa9e7d690b1f7a173d07e6d54a419db4a6308f8b09589e4283efb9cd1ee4061c6bf884e60a8774", + "BlindedElement": "02ae8990d580dcd52b6bc273bc6d0fd25be50b057511b953d9cc95bb27cb3e1fd3249ae19744ed496c6e4104ebc1ed48f1", + "EvaluationElement": "024cffdae0cae5fa4d6a68246ae797dbe06508284b65e0f09046977ab5d52a8b38f0245607db74979e5276fc636332cdee", + "Info": "7465737420696e666f", + "Input": "00", + "Output": "8a0b4829bc8422b1a2301d5471256892883c5e3fe27b998d1010225a706545637336a20a76f842d8a22e591d382c77e4", + "Proof": { + "proof": "128ad4f987ce1e3a9aab1e487df15d8c8000d5c4c9f14bd7fd699fabdb8da3f577d91625fabb0d9cf6069f8af6d9cc232dd63cd161be84a1e146e0110dc741e626a082193aa0a26e03118b662f1b903667f6e6fba51d69a2d65982a3b64ecb35", + "r": "90f67cafc0ffaa7a1e1d1ced3c477fea691e696032c8709c86cbcda2b184ad0029d29abeabede9788d11782429bff297" + } + }, + { + "Batch": 1, + "Blind": "8aec1d0c3d16afd032da7ba961449a56cec6fb918e932b06d5778ac7f67becfb3e3869237f74106241777f230582e84a", + "BlindedElement": "02a384f2d9635adffcc5482344c519036c019f3cc0918ec737c67cdda10ac0f73a9fe348835531f1900ea2c1f06dacdce4", + "EvaluationElement": "0306f0f71b58d53ae0973538a7bf2ce8fba7143efc88d2efca6cf1f98fb8399b16840d1fbbe7897807db930f67916418ae", + "Info": "7465737420696e666f", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "8c52d40c1f6cc80208bd610178a5034d6c4a05584e19b69617f846b09a8545443c63c8aa4d85bf0aad368e0591b1216a", + "Proof": { + "proof": "2c47297ee0093061ca2c87b430b2851a860aaae76c2bdba48779ba4294e7de0556ede3e6b881a04970b68a6126e2fa197d69e6784fbbd173604501c0edd21696628f0fd7cb13be28f94e5e15c042ffccadd780b2448d7d9d528e9615e4e70539", + "r": "bb1876a7f7165ac7ec79bfd5213ea2e374252f29a6e19915f81b0c7dcea93ce6580e089ede31c1b6b5b33494581b4868" + } + }, + { + "Batch": 2, + "Blind": "41fabd4722d92472d858051ce9ad1a533176a862c697b2c392aff2aeb77eb20c2ae6ba52fe31e13e03bf1d9f39878b23,51171628f1d28bb7402ca4aea6465e267b7f977a1fb71593281099ef2625644aee0b6c5f5e6e01a2b052b3bd4caf539b", + "BlindedElement": "02d4e6186c9ffa92565055f43f27bb1e2c4103c3325bba0b499adb99a157987d20fb374096814e438a6b483efa8f2a3307,033a3b052416a8a6d842a0baea6f5fab99d36645a70c89897a536970d34038eca35afac24906294cb7925b1b05e4327c8f", + "EvaluationElement": "037bf8e28a0607b1f8aa59363380b5a7450b66b98017cf033797f6c6c74e7625a445f71ace1bea7836ea5baa75d54eb5bd,03b793a9cb2d76991f1d6cd822abfbfa89fdfa1a06ef42b0bc8ade161e1996ed08c288a08366d4140c7627bba4e3472bcf", + "Info": "7465737420696e666f", + "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "8a0b4829bc8422b1a2301d5471256892883c5e3fe27b998d1010225a706545637336a20a76f842d8a22e591d382c77e4,8c52d40c1f6cc80208bd610178a5034d6c4a05584e19b69617f846b09a8545443c63c8aa4d85bf0aad368e0591b1216a", + "Proof": { + "proof": "27240901b6855d2b58ce84afefa91dd11819d7d5df73f94865a9d7e1902041200eb732b60b57fa0daf6e456402bb1ccb1aed901af35d3d790cd7c618604b766bb9271010354da9e4e5507e0468adf177977143db2ddb94d9b70e837ad7578275", + "r": "1b538ff23749be19e92df82df1acd3f606cc9faa9dc7ab251997738a3a232f352c2059c25684e6ccea420f8d0c793fa0" + } + } + ] + }, + { + "groupDST": "48617368546f47726f75702d564f50524630382d000005", + "hash": "SHA512", + "mode": 0, + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "00a2f8572ee764d2ec34363fb62ef9e8ff48883b5357b6802f43fffe5c5fd0d11f766bf7086aab33e2dce02cc71d77250ef6ed360a3fd56244abb6bdbc3aa6534da1", + "suiteID": 5, + "suiteName": "OPRF(P-521, SHA-512)", + "vectors": [ + { + "Batch": 1, + "Blind": "01b983705fcc9a39607288b935b0797ac6b3c4b2e848823ac9ae16b3a3b5816be03432370deb7c3c17d9fc7cb4e0ce646e04e42d638e0fa7a434ed340772a8b5d626", + "BlindedElement": "03006ce4a27e778a624d943cf4db48f9d393d3d4dd9cd44b78acf2d5b668a12f0ca587962de8c82b5aaa1f0166eb60d511f060aaab895fc6c51933277bc945add6d74a", + "EvaluationElement": "030055f7cd3ee3b1734e73ad8bbd4baca72ae8d051160c277ee329f23fa2365f9f138b38e6e2c59cc287242eeca01fae83d0c7cc3bb19724ac598a188816e7cfe1ca88", + "Info": "7465737420696e666f", + "Input": "00", + "Output": "aa59060a41ec8ca7b6c47f9c5a31883a44ffd95869a09dbe845ea8ce20cb290dba0b57c505824a0dcf6f961a2baeb8e6b49df8c158761a3fdb46f39e8e7fcb8b" + }, + { + "Batch": 1, + "Blind": "01a03b1096b0316bc8567c89bd70267d35c8ddcb2be2cdc867089a2eb5cf471b1e6eb4b043b9644c8539857abe3a2022e9c9fd6a1695bbabe8add48bcd149ff3b841", + "BlindedElement": "0201459ba64ad0e0f9f689f0ad5ab29ca5b960f5c9da3aef4126d2d547b871e754b17971fd45e0d64bdcfc8d256c342a141f04e2640705c38936c8cf53c22ea6b13966", + "EvaluationElement": "030094036457e8e5bf77719b11f01dd4aa2959efdb3329c3e3b25493efc3ab572c2e7db104cd5922645320ef51bbb282f84e5f6b08e9b49354f9d6a9f3a4327a1de6e4", + "Info": "7465737420696e666f", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "5efe6f00f45ec4e87e4c9b89aeaec61313c15c0a0a21ee2e41362d6af54536adf2f68d23c729b92b6fa8d5611764b0272be6cc153d47a0256c8cb44bd740037a" + } + ] + }, + { + "groupDST": "48617368546f47726f75702d564f50524630382d010005", + "hash": "SHA512", + "mode": 1, + "pkSm": "03013e587a7750213bb7c2b338a4507635f1ba60ece346de32ad975373e56fbabd878f9956996aac83a550ed5f5ba98fcc56817f6230cc7e84cb7eb2a1e1db51dbfc1b", + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "0064799c2f9c0f9e6b9ac2aca5c42687cf15742fb73e086c4954aa0bdc8b825911ff03712e8d308c0a6ff5435375036f189391234bf21aac57fa73df155d70da47bd", + "suiteID": 5, + "suiteName": "OPRF(P-521, SHA-512)", + "vectors": [ + { + "Batch": 1, + "Blind": "00bbb82117c88bbd91b8954e16c0b9ceed3ce992b198be1ebfba9ba970dbd75beefbfc6d056b7f7ba1ef79f4facbf2d912c26ce2ecc5bb8d66419b379952e96bd6f5", + "BlindedElement": "02002ff3ef3f2411aa0358936f852be710af790c9affbced8c39b018fd97de0a45d80c66cbf0dbda690ee4f594e0795627e6c6f37a500f223c30f31c24e73501532e7c", + "EvaluationElement": "0300769fd56c5174c4e3922900fcefdd5a89c9592f4d8e8f2396678fa72c01d4f8551ec92d4b5287ca673dc29d8db9bb05d2396121a6b8732b68ebf310fc2620059d67", + "Info": "7465737420696e666f", + "Input": "00", + "Output": "a647c5a940aa19d767ab0e163d1357ca068206b2b78f9e8e1021c0bb0f327d20cb8fadf996199d86d4cc0a08ac314493319979e1c2a98a96085b8fabff9f0d07", + "Proof": { + "proof": "011fd92f54f6a955a333648d843807bd88f644d235a7d592189da42d721ea6f7b55ec813146f35982487910aa15bbf5ce90653edb6a1b48c0bfd15758e9358aa731601baa67a3a59db301f41caa020986ae9e93a80d6c06d92e8c5eef6056fa6f3426b6054d118dc9fecb77fdcb4fc86b9857ada6de18394ff7d6c574cbd08d746b9dde0", + "r": "00ce4f0d824939827888f4c28773466f3c0a05741260040bc9f302a4fea13f1d8f2f6b92a02a32d5eb06f81de7960470f06169bee12cf47965b72a59946ca3879670" + } + }, + { + "Batch": 1, + "Blind": "009055c99bf9591cb0eab2a72d044c05ca2cc2ef9b609a38546f74b6d688f70cf205f782fa11a0d61b2f5a8a2a1143368327f3077c68a1545e9aafbba6a90dc0d40a", + "BlindedElement": "0301e2ecf7313820e9d47763e12633ce6acf9b3dec89928c83bde1ede2180dc73553af1317408846af5c53ebfed00d19a4125f4ffb7df9f4260ccc084a6f7482414a9d", + "EvaluationElement": "02000e69591ab605652cb3310e774edf79417e102cf89005c2c7f2bd3a06060d740817802f2cf484748d93df5b281a4bd835617a97ec9809519d474ca53bba15cdf014", + "Info": "7465737420696e666f", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "8d109503ccced41cbec087dab86c607763020be93bdd5ec8508cb0786071a2b22a7b06150242bcaf6ea1b555a994e0266647eb72914caf73cabe53ddfb0f940d", + "Proof": { + "proof": "0076fa4275414acb9f87dc9e4f20971d51fcd0d38a980854ac2ad1bd5737eec23bfb4599d021881f7b3872d2e90d9b47e4219f490cf7f0235b2f0859cb2ef15ddfd401acb6b0844edf066a5767b4b85536bfee69bdf472acf7a59254cf6578f9f35eba51bb58c6428d6b7c9e5c9af97edc66d98886fda9544048bf9ceea6fc745bf970da", + "r": "00b5dfc19eb96faba6382ec845097904db87240b9dd47b1e487ec625f11a7ba2cc3de74c5078a81806f74dd65065273c5bd886c7f87ff8c5f39f90320718eff747e3" + } + }, + { + "Batch": 2, + "Blind": "01c6cf092d80c7cf2cb55388d899515238094c800bdd9c65f71780ba85f5ae9b4703e17e559ca3ccd1944f9a70536c175f11a827452672b60d4e9f89eba281046e29,00cba1ba1a337759061965a423d9d3d6e1e1006dc8984ad28a4c93ecfc36fc2171046b3c4284855cfa2434ed98db9e68a597db2c14728fade716a6a82d600444b26e", + "BlindedElement": "0201e22c01df5ac0502842fad603f7a1e1183bcc79a5cb04bb7befdea870a9a6ea96fbccd752ea9927a9e1e28438098f693461e81832a3f690616bf983fced079f3a33,0300b49216dd8ba5ba1275d8345679f70fbc6baf4f4b32a03e917165a18afa9fad849c48eecb4bae965057ef7c215b52b42ca53c8d5f650633e0bb7097f2bd809d09ea", + "EvaluationElement": "03002949c2478249b918a0cf2cd870226541a81d2f3e88c47119f732301e749c3dea317c11174a18b89d1b9d2aa4f6ae92ae724e03a4800a26b7c827b00199f1114bcd,0300924ab017ea6e6328a0b0f341bbeb7d209c67ac169fa4ef7b04055c66b92aa9657f5d83b0b1ee9c79f3f0198519c97fef07dbecf3f6d4777550242a1c87953f9461", + "Info": "7465737420696e666f", + "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "a647c5a940aa19d767ab0e163d1357ca068206b2b78f9e8e1021c0bb0f327d20cb8fadf996199d86d4cc0a08ac314493319979e1c2a98a96085b8fabff9f0d07,8d109503ccced41cbec087dab86c607763020be93bdd5ec8508cb0786071a2b22a7b06150242bcaf6ea1b555a994e0266647eb72914caf73cabe53ddfb0f940d", + "Proof": { + "proof": "01f5d3c3f835d91aa88202f0fe8728180eeffe7fbc66ffe3f7a7dd958696a7cd3d47b3c0ec6cd59e9ee23090137293e6f42269923f3d4a1659bc706fd97620707d230028cd4b0aa237b91a352fce81248936826ba99e7bd5103a871715126014b8d47447e5f20192ed377a7431516fbd82763098ba23f9d15b84fe24fb1126beb0d46f03", + "r": "00d47b0d4ca4c64825ba085de242042b84d9ebe3b2e9de07678ff96713dfe16f40f2c662a56ed2db95e1e7bf2dea02bd1fa76e953a630772f68b53baade9962d1646" + } + } + ] + } +] diff --git a/Tests/_CryptoExtrasTests/OPRFs/OPRFVectors/OPRFVectors-edgecases.json b/Tests/_CryptoExtrasTests/OPRFs/OPRFVectors/OPRFVectors-edgecases.json new file mode 100644 index 00000000..5242ef47 --- /dev/null +++ b/Tests/_CryptoExtrasTests/OPRFs/OPRFVectors/OPRFVectors-edgecases.json @@ -0,0 +1,248 @@ +[ + { + "groupDST": "48617368546f47726f75702d4f50524656312d022d503235362d534841323536", + "hash": "SHA256", + "identifier": "P256-SHA256", + "keyInfo": "74657374206b6579", + "mode": 1, + "pkSm": "030d7ff077fddeec965db14b794f0cc1ba9019b04a2f4fcc1fa525dedf72e2a3e3", + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "ca5d94c8807817669a51b196c34c1b7f8442fde4334a7121ae4736364312fca6", + "vectors": [ + { + "Batch": 1, + "Blind": "3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "02dd05901038bb31a6fae01828fd8d0e49e35a486b5c5d4b4994013648c01277da", + "EvaluationElement": "0209f33cab60cf8fe69239b0afbcfcd261af4c1c5632624f2e9ba29b90ae83e4a2", + "Input": "00", + "Output": "0412e8f78b02c415ab3a288e228978376f99927767ff37c5718d420010a645a1", + "Proof": { + "proof": "3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad3643338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "r": "f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" + }, + "result": "invalidProof", + "comment": "proof invalid: c, s random scalars", + }, + { + "Batch": 1, + "Blind": "3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "03cd0f033e791c4d79dfa9c6ed750f2ac009ec46cd4195ca6fd3800d1e9b887dbd", + "EvaluationElement": "030d2985865c693bf7af47ba4d3a3813176576383d19aff003ef7b0784a0d83cf1", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "03cd0f033e791c4d79dfa9c6ed750f2ac009ec46cd4195ca6fd3800d1e9b887dbd", + "Proof": { + "proof": "2787d729c57e3d9512d3aa9e8708ad226bc48e0f1750b0767aaff73482c44b8d2873d74ec88aebd3504961acea16790a05c542d9fbff4fe269a77510db00abab", + "r": "f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" + }, + "result": "invalidOPRFOutput", + "comment": "Output element is invalid (not a valid OPRF evaluation)", + }, + ], + }, + { + "groupDST": "48617368546f47726f75702d4f50524656312d022d503235362d534841323536", + "hash": "SHA256", + "identifier": "P256-SHA256", + "keyInfo": "74657374206b6579", + "mode": 2, + "pkSm": "030d7ff077fddeec965db14b794f0cc1ba9019b04a2f4fcc1fa525dedf72e2a3e3", + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "6ad2173efa689ef2c27772566ad7ff6e2d59b3b196f00219451fb2c89ee4dae2", + "vectors": [ + { + "Batch": 1, + "Blind": "3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "031563e127099a8f61ed51eeede05d747a8da2be329b40ba1f0db0b2bd9dd4e2c0", + "EvaluationElement": "02c5e5300c2d9e6ba7f3f4ad60500ad93a0157e6288eb04b67e125db024a2c74d2", + "Info": "7465737420696e666f", + "Input": "00", + "Output": "193a92520bd8fd1f37accb918040a57108daa110dc4f659abe212636d245c592", + "Proof": { + "proof": "3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad3643338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "r": "f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" + }, + "result": "invalidProof", + "comment": "proof invalid: c, s random scalars", + }, + { + "Batch": 1, + "Blind": "3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "021a440ace8ca667f261c10ac7686adc66a12be31e3520fca317643a1eee9dcd4d", + "EvaluationElement": "0208ca109cbae44f4774fc0bdd2783efdcb868cb4523d52196f700210e777c5de3", + "Info": "7465737420696e666f", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "021a440ace8ca667f261c10ac7686adc66a12be31e3520fca317643a1eee9dcd4d", + "Proof": { + "proof": "043a8fb7fc7fd31e35770cabda4753c5bf0ecc1e88c68d7d35a62bf2631e875af4613641be2d1875c31d1319d191c4bbc0d04875f4fd03c31d3d17dd8e069b69", + "r": "f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" + }, + "result": "invalidOPRFOutput", + "comment": "Output element is invalid (not a valid OPRF evaluation)", + } + ], + }, + { + "groupDST": "48617368546f47726f75702d4f50524656312d012d503338342d534841333834", + "hash": "SHA384", + "identifier": "P384-SHA384", + "keyInfo": "74657374206b6579", + "mode": 1, + "pkSm": "031d689686c611991b55f1a1d8f4305ccd6cb719446f660a30db61b7aa87b46acf59b7c0d4a9077b3da21c25dd482229a0", + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "051646b9e6e7a71ae27c1e1d0b87b4381db6d3595eeeb1adb41579adbf992f4278f9016eafc944edaa2b43183581779d", + "vectors": [ + { + "Batch": 1, + "Blind": "504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "02d338c05cbecb82de13d6700f09cb61190543a7b7e2c6cd4fca56887e564ea82653b27fdad383995ea6d02cf26d0e24d9", + "EvaluationElement": "02a7bba589b3e8672aa19e8fd258de2e6aae20101c8d761246de97a6b5ee9cf105febce4327a326255a3c604f63f600ef6", + "Input": "00", + "Output": "3333230886b562ffb8329a8be08fea8025755372817ec969d114d1203d026b4a622beab60220bf19078bca35a529b35c", + "Proof": { + "proof": "504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "r": "803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" + }, + "result": "invalidProof", + "comment": "proof invalid: c, s random scalars", + }, + { + "Batch": 1, + "Blind": "504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "02f27469e059886f221be5f2cca03d2bdc61e55221721c3b3e56fc012e36d31ae5f8dc058109591556a6dbd3a8c69c433b", + "EvaluationElement": "03f16f903947035400e96b7f531a38d4a07ac89a80f89d86a1bf089c525a92c7f4733729ca30c56ce78b1ab4f7d92db8b4", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "02f27469e059886f221be5f2cca03d2bdc61e55221721c3b3e56fc012e36d31ae5f8dc058109591556a6dbd3a8c69c433b", + "Proof": { + "proof": "d005d6daaad7571414c1e0c75f7e57f2113ca9f4604e84bc90f9be52da896fff3bee496dcde2a578ae9df315032585f801fb21c6080ac05672b291e575a40295b306d967717b28e08fcc8ad1cab47845d16af73b3e643ddcc191208e71c64630", + "r": "803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" + }, + "result": "invalidOPRFOutput", + "comment": "Output element is invalid (not a valid OPRF evaluation)", + }, + ], + }, + { + "groupDST": "48617368546f47726f75702d4f50524656312d022d503338342d534841333834", + "hash": "SHA384", + "identifier": "P384-SHA384", + "keyInfo": "74657374206b6579", + "mode": 2, + "pkSm": "02f00f0f1de81e5d6cf18140d4926ffdc9b1898c48dc49657ae36eb1e45deb8b951aaf1f10c82d2eaa6d02aafa3f10d2b6", + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "5b2690d6954b8fbb159f19935d64133f12770c00b68422559c65431942d721ff79d47d7a75906c30b7818ec0f38b7fb2", + "vectors": [ + { + "Batch": 1, + "Blind": "504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "03859b36b95e6564faa85cd3801175eda2949707f6aa0640ad093cbf8ad2f58e762f08b56b2a1b42a64953aaf49cbf1ae3", + "EvaluationElement": "0220710e2e00306453f5b4f574cb6a512453f35c45080d09373e190c19ce5b185914fbf36582d7e0754bb7c8b683205b91", + "Info": "7465737420696e666f", + "Input": "00", + "Output": "0188653cfec38119a6c7dd7948b0f0720460b4310e40824e048bf82a16527303ed449a08caf84272c3bbc972ede797df", + "Proof": { + "proof": "504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "r": "803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" + }, + "result": "invalidProof", + "comment": "proof invalid: c, s random scalars", + }, + { + "Batch": 1, + "Blind": "504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "03f7efcb4aaf000263369d8a0621cb96b81b3206e99876de2a00699ed4c45acf3969cd6e2319215395955d3f8d8cc1c712", + "EvaluationElement": "034993c818369927e74b77c400376fd1ae29b6ac6c6ddb776cf10e4fbc487826531b3cf0b7c8ca4d92c7af90c9def85ce6", + "Info": "7465737420696e666f", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "03f7efcb4aaf000263369d8a0621cb96b81b3206e99876de2a00699ed4c45acf3969cd6e2319215395955d3f8d8cc1c712", + "Proof": { + "proof": "693471b5dff0cd6a5c00ea34d7bf127b2795164e3bdb5f39a1e5edfbd13e443bc516061cd5b8449a473c2ceeccada9f3e5b57302e3d7bc5e28d38d6e3a3056e1e73b6cc030f5180f8a1ffa45aa923ee66d2ad0a07b500f2acc7fb99b5506465c", + "r": "803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" + }, + "result": "invalidOPRFOutput", + "comment": "Output element is invalid (not a valid OPRF evaluation)", + }, + ], + }, + { + "groupDST": "48617368546f47726f75702d4f50524656312d012d503532312d534841353132", + "hash": "SHA512", + "identifier": "P521-SHA512", + "keyInfo": "74657374206b6579", + "mode": 1, + "pkSm": "0301505d646f6e4c9102451eb39730c4ba1c4087618641edbdba4a60896b07fd0c9414ce553cbf25b81dfcca50a8f6724ab7a2bc4d0cf736967a287bb6084cc0678ac0", + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "015c7fc1b4a0b1390925bae915bd9f3d72009d44d9241b962428aad5d13f22803311e7102632a39addc61ea440810222715c9d2f61f03ea424ec9ab1fe5e31cf9238", + "vectors": [ + { + "Batch": 1, + "Blind": "00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "0301d6e4fb545e043ddb6aee5d5ceeee1b44102615ab04430c27dd0f56988dedcb1df32ef384f160e0e76e718605f14f3f582f9357553d153b996795b4b3628a4f6380", + "EvaluationElement": "03013fdeaf887f3d3d283a79e696a54b66ff0edcb559265e204a958acf840e0930cc147e2a6835148d8199eebc26c03e9394c9762a1c991dde40bca0f8ca003eefb045", + "Input": "00", + "Output": "5e003d9b2fb540b3d4bab5fedd154912246da1ee5e557afd8f56415faa1a0fadff6517da802ee254437e4f60907b4cda146e7ba19e249eef7be405549f62954b", + "Proof": { + "proof": "00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad36400d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "r": "015e80ae32363b32cb76ad4b95a5a34e46bb803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" + }, + "result": "invalidProof", + "comment": "proof invalid: c, s random scalars", + }, + { + "Batch": 1, + "Blind": "00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "03005b05e656cb609ce5ff5faf063bb746d662d67bbd07c062638396f52f0392180cf2365cabb0ece8e19048961d35eeae5d5fa872328dce98df076ee154dd191c615e", + "EvaluationElement": "0301b19fcf482b1fff04754e282292ed736c5f0aa080d4f42663cd3a416c6596f03129e8e096d8671fe5b0d19838312c511d2ce08d431e43e3ef06199d8cab7426238d", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "03005b05e656cb609ce5ff5faf063bb746d662d67bbd07c062638396f52f0392180cf2365cabb0ece8e19048961d35eeae5d5fa872328dce98df076ee154dd191c615e", + "Proof": { + "proof": "01ec9fece444caa6a57032e8963df0e945286f88fbdf233fb5101f0924f7ea89c47023f5f72f240e61991fd33a299b5b38c45a5e2dd1a67b072e59dfe86708a359c701e38d383c60cf6969463bcf13251bedad47b7941f52e409a3591398e27924410b18a301c0e19f527cad504fa08388050ac634e1b05c5216d337742f2754e1fc502f", + "r": "015e80ae32363b32cb76ad4b95a5a34e46bb803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" + }, + "result": "invalidOPRFOutput", + "comment": "Output element is invalid (not a valid OPRF evaluation)", + }, + ], + }, + { + "groupDST": "48617368546f47726f75702d4f50524656312d022d503532312d534841353132", + "hash": "SHA512", + "identifier": "P521-SHA512", + "keyInfo": "74657374206b6579", + "mode": 2, + "pkSm": "0301de8ceb9ffe9237b1bba87c320ea0bebcfc3447fe6f278065c6c69886d692d1126b79b6844f829940ace9b52a5e26882cf7cbc9e57503d4cca3cd834584729f812a", + "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", + "skSm": "014893130030ce69cf714f536498a02ff6b396888f9bb507985c32928c4427d6d39de10ef509aca4240e8569e3a88debc0d392e3361bcd934cb9bdd59e339dff7b27", + "vectors": [ + { + "Batch": 1, + "Blind": "00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "020095cff9d7ecf65bdfee4ea92d6e748d60b02de34ad98094f82e25d33a8bf50138ccc2cc633556f1a97d7ea9438cbb394df612f041c485a515849d5ebb2238f2f0e2", + "EvaluationElement": "0301408e9c5be3ffcc1c16e5ae8f8aa68446223b0804b11962e856af5a6d1c65ebbb5db7278c21db4e8cc06d89a35b6804fb1738a295b691638af77aa1327253f26d01", + "Info": "7465737420696e666f", + "Input": "00", + "Output": "808ae5b87662eaaf0b39151dd85991b94c96ef214cb14a68bf5c143954882d330da8953a80eea20788e552bc8bbbfff3100e89f9d6e341197b122c46a208733b", + "Proof": { + "proof": "00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad36400d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "r": "015e80ae32363b32cb76ad4b95a5a34e46bb803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" + }, + "result": "invalidProof", + "comment": "proof invalid: c, s random scalars", + }, + { + "Batch": 1, + "Blind": "00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "030112ea89cf9cf589496189eafc5f9eb13c9f9e170d6ecde7c5b940541cb1a9c5cfeec908b67efe16b81ca00d0ce216e34b3d5f46a658d3fd8573d671bdb6515ed508", + "EvaluationElement": "0200ebc49df1e6fa61f412e6c391e6f074400ecdd2f56c4a8c03fe0f91d9b551f40d4b5258fd891952e8c9b28003bcfa365122e54a5714c8949d5d202767b31b4bf1f6", + "Info": "7465737420696e666f", + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Output": "030112ea89cf9cf589496189eafc5f9eb13c9f9e170d6ecde7c5b940541cb1a9c5cfeec908b67efe16b81ca00d0ce216e34b3d5f46a658d3fd8573d671bdb6515ed508", + "Proof": { + "proof": "0082162c71a7765005cae202d4bd14b84dae63c29067e886b82506992bd994a1c3aac0c1c5309222fe1af8287b6443ed6df5c2e0b0991faddd3564c73c7597aecd9a003b1f1e3c65f28e58ab4e767cfb4adbcaf512441645f4c2aed8bf67d132d966006d35fa71a34145414bf3572c1de1a46c266a344dd9e22e7fb1e90ffba1caf556d9", + "r": "015e80ae32363b32cb76ad4b95a5a34e46bb803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" + }, + "result": "invalidOPRFOutput", + "comment": "Output element is invalid (not a valid OPRF evaluation)", + }, + ], + }, +] diff --git a/Tests/_CryptoExtrasTests/OPRFs/VOPRFAPITests.swift b/Tests/_CryptoExtrasTests/OPRFs/VOPRFAPITests.swift new file mode 100644 index 00000000..0a8323c9 --- /dev/null +++ b/Tests/_CryptoExtrasTests/OPRFs/VOPRFAPITests.swift @@ -0,0 +1,107 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2024 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 Crypto +@testable import _CryptoExtras // NOTE: @testable import, to inject fixed values from test vectors. +import XCTest + +extension OPRFSuite { + static func load(from fileURL: URL) throws -> [Self] { + let json = try Data(contentsOf: fileURL) + let decoder = JSONDecoder() + return try decoder.decode([Self].self, from: json) + } + + static let allValues: [Self] = try! OPRFSuite.load(from: URL( + fileURLWithPath: "OPRFVectors/OPRFVectors-VOPRFDraft19.json", + relativeTo: URL(fileURLWithPath: #file) + )) + + static var P384_SHA384_VORPF: Self { + self.allValues.filter { $0.identifier == "P384-SHA384" && $0.mode == 1 }.first! + } +} + +@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) +final class VOPRFAPITests: XCTestCase { + func testVectors() throws { + try testVectorsVOPRF(suite: .P384_SHA384_VORPF) + try testVectorsPRF(suite: .P384_SHA384_VORPF) + } + + func testVectorsVOPRF(suite: OPRFSuite) throws { + for vector in suite.vectors.filter({ $0.Batch == 1 }) { + // [Server] Create the key-pair. + let privateKey = try P384._VOPRF.PrivateKey(rawRepresentation: Data(hexString: suite.skSm)) + + // [Client] Obtain public key. + let publicKey = privateKey.publicKey + + // [Client] Have a private input they wish to use. + let privateInput = try Data(hexString: vector.Input) + + // [Client] Blind the private input and send the blinded element to the server. + // TODO: should we make this the nomminal type? + let fixedBlind = try P384._VOPRF.H2G.G.Scalar(bytes: Data(hexString: vector.Blind)) + let blindedInput = try publicKey.blind(privateInput, with: fixedBlind) + + // [Client -> Server] Send the blinded element. + let blindedElementBytes = blindedInput.blindedElement.oprfRepresentation + + // [CHECK] Blinded element matches test vector. + XCTAssertEqual(blindedElementBytes.hexString, vector.BlindedElement) + + // [Server] Receive the blinded element. + let blindedElememt = try P384._VOPRF.BlindedElement(oprfRepresentation: blindedElementBytes) + + // [Server] Blind evaluate the blinded element and send the evaluation, along with the proof, to the client. + let fixedProofScalar = try P384._VOPRF.H2G.G.Scalar(bytes: Data(hexString: vector.Proof!.r)) + XCTAssertNil(vector.Info, "VOPRF mode shouldn't have info.") + let blindEvaluation = try privateKey.evaluate(blindedElememt, using: fixedProofScalar) + + // [CHECK] Evaluated element matches test vector. + XCTAssertEqual(blindEvaluation.evaluatedElement.oprfRepresentation.hexString, vector.EvaluationElement) + + // [CHECK] Proof matches test vector. + XCTAssertEqual(blindEvaluation.proof.rawRepresentation.hexString, vector.Proof?.proof) + + // [Server -> Client] Send the serialized blind evaluation. + let blindEvaluationBytes = blindEvaluation.rawRepresentation + + // [Client] Receive the blind evaluation. + let deserializedBlindEvaluation = try P384._VOPRF.BlindEvaluation(rawRepresentation: blindEvaluationBytes) + + // [Client] Finalize the evaluation by verifying the proof and unblinding to produce the output. + let output = try publicKey.finalize(blindedInput, using: deserializedBlindEvaluation) + + // [CHECK] Final output matches test vector. + XCTAssertEqual(output.hexString, vector.Output) + } + } + + func testVectorsPRF(suite: OPRFSuite) throws { + for vector in suite.vectors.filter({ $0.Batch == 1 }) { + // [Server] Create the key-pair. + let privateKey = try P384._VOPRF.PrivateKey(rawRepresentation: Data(hexString: suite.skSm)) + + // [Server] Have an input they wish to use. + let input = try Data(hexString: vector.Input) + + // [Server] Compute the PRF for the input, without blinding or proof generation. + let output = try privateKey.evaluate(input) + + // [CHECK] Final output matches test vector. + XCTAssertEqual(output.hexString, vector.Output) + } + } +} diff --git a/Tests/_CryptoExtrasTests/OPRFs/VOPRFPublicAPITests.swift b/Tests/_CryptoExtrasTests/OPRFs/VOPRFPublicAPITests.swift new file mode 100644 index 00000000..1ec31835 --- /dev/null +++ b/Tests/_CryptoExtrasTests/OPRFs/VOPRFPublicAPITests.swift @@ -0,0 +1,82 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2024 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 Crypto +import _CryptoExtras // NOTE: No @testable import, because we want to test the public API. +import XCTest + +@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) +final class VOPRFPublicAPITests: XCTestCase { + + func testEndToEndVOPRF() throws { + // [Server] Create the key-pair (other initializers are available). + let privateKey = P384._VOPRF.PrivateKey() + + // [Client] Obtain public key (other initializers are available). + let publicKey = privateKey.publicKey + + // [Client] Have a private input they wish to use. + let privateInput = Data("This is some input data".utf8) + + // [Client] Blind the private input and send the blinded element to the server. + let blindedInput = try publicKey.blind(privateInput) + + // [Client -> Server] Send the blinded element. + let blindedElementBytes = blindedInput.blindedElement.oprfRepresentation + + // [Server] Receive the blinded element. + let blindedElememt = try P384._VOPRF.BlindedElement(oprfRepresentation: blindedElementBytes) + + // [Server] Blind evaluate the blinded element and send the evaluation, along with the proof, to the client. + let blindEvaluation = try privateKey.evaluate(blindedElememt) + + // [Server -> Client] Send the serialized blind evaluation. + let blindEvaluationBytes = blindEvaluation.rawRepresentation + + // [Client] Receive the blind evaluation. + let deserializedBlindEvaluation = try P384._VOPRF.BlindEvaluation(rawRepresentation: blindEvaluationBytes) + + // [Client] Finalize the evaluation by verifying the proof and unblinding to produce the output. + let _: Data = try publicKey.finalize(blindedInput, using: deserializedBlindEvaluation) + } + + func testAccessToEvaluatedElementAndProof() throws { + /// In RFC 9497, the `BlindEvaluate` routine returns both `evaluatedElement` and `proof`, which are both later + /// provided to `Finalize`. + /// + /// For our API, these are bundled together into a `BlindEvaluation`, and since both are used in the final step, + /// our `finalize` API takes the composite type too, to guide correct usage. + /// + /// However, for use cases that require distinct access to the evaluated element and the proof we also expose + /// these properties as API. + /// + /// - See: https://www.rfc-editor.org/rfc/rfc9497.html#section-3.3.2-2 + let vector = OPRFSuite.P384_SHA384_VORPF.vectors.first! + let evaluatedElement = try Data(hexString: vector.EvaluationElement) + let proof = try Data(hexString: vector.Proof!.proof) + let blindEvaluation = try P384._VOPRF.BlindEvaluation(rawRepresentation: evaluatedElement + proof) + XCTAssertEqual(blindEvaluation.evaluatedElement.oprfRepresentation, evaluatedElement) + XCTAssertEqual(blindEvaluation.proof.rawRepresentation, proof) + } + + func testEndToEndPRF() throws { + // [Server] Create the key-pair (other initializers are available). + let privateKey = P384._VOPRF.PrivateKey() + + // [Server] Have an input they wish to use. + let input = Data("This is some input data".utf8) + + // [Server] Compute the PRF for the input, without blinding or proof generation. + let _: Data = try privateKey.evaluate(input) + } +} diff --git a/Tests/_CryptoExtrasTests/Utils/XCTestUtils.swift b/Tests/_CryptoExtrasTests/Utils/XCTestUtils.swift index 15cb3073..9f0878e6 100644 --- a/Tests/_CryptoExtrasTests/Utils/XCTestUtils.swift +++ b/Tests/_CryptoExtrasTests/Utils/XCTestUtils.swift @@ -32,3 +32,14 @@ func orFail(file: StaticString = #file, line: UInt = #line, _ closure: () thr return try wrapper(closure, file: file, line: line) } } + +func XCTAssertThrowsError( + _ expression: @autoclosure () throws -> T, + error expectedError: E, + _ message: @autoclosure () -> String = "", + file: StaticString = #file, + line: UInt = #line) { + XCTAssertThrowsError(try expression(), message(), file: file, line: line) { error in + XCTAssertEqual(error as? E, expectedError, message(), file: file, line: line) + } +}