Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

extras: Add APIs for RSA Blind Signatures #232

Merged
merged 31 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
090eab8
API for RSA blind signing and verification from RFC 9474
simonjbeaumont May 14, 2024
e0bc477
Add blind signing backend for Security framework
simonjbeaumont May 21, 2024
b9f9d7f
Hide PSSZERO variants from public API for now
simonjbeaumont May 21, 2024
e5b4dd0
Update CMakeLists.txt
simonjbeaumont May 21, 2024
cb7a828
Verify signature produced within SecurityRSAPrivateKey.blindSignature…
simonjbeaumont May 21, 2024
d6c6dd5
Update API: bake params into key, use wrappers for intermidiate values
simonjbeaumont May 30, 2024
d2a28e5
Throw specific error when message too large for key modulus
simonjbeaumont May 31, 2024
56d0253
Drop Security framework backend and support all variants
simonjbeaumont May 31, 2024
60f26f0
Factor out withNewBignumContext helper
simonjbeaumont May 31, 2024
d239017
Align parameter names and labels in internal functions with public API
simonjbeaumont May 31, 2024
7c37d7a
Fix typo in doc comments: public -> private
simonjbeaumont May 31, 2024
ec9e122
Implement client operations for RSA Blind Signatures
simonjbeaumont May 31, 2024
925d7b9
Remove the BlindedMessage type
simonjbeaumont Jun 5, 2024
606f74d
Move prepare function to public key method
simonjbeaumont Jun 5, 2024
a8d0338
Throw (internal) errors defined in the RFC
simonjbeaumont Jun 5, 2024
bd1e490
Fix test function names for Linux test discovery
simonjbeaumont Jun 5, 2024
b9403e5
Factor saltLength into a computed property on Parameters
simonjbeaumont Jun 7, 2024
eec135e
Improve allocations for temporary bytes
simonjbeaumont Jun 7, 2024
616b027
Fixup documentation typos
simonjbeaumont Jun 7, 2024
ec0d4da
Parameters are Sendable, regardless of H type parameter
simonjbeaumont Jun 7, 2024
7500681
PublicKey and PrivateKey are also Sendable, regardless of H
simonjbeaumont Jun 7, 2024
8bf79bd
Wrap internal errors in most appropriate CryptoKitError
simonjbeaumont Jun 10, 2024
ae89be1
Factor code differently
simonjbeaumont Jun 10, 2024
c4f4a15
Tweak API to return BlindingResult nominal type instead of tuple
simonjbeaumont Jun 10, 2024
bda85da
Prefer withUnsafeTemporaryAllocation(of:capacity:_:) to Array.init(un…
simonjbeaumont Jun 17, 2024
97094e7
Move unsafe temporary allocations into BlindSigningHelpers methods
simonjbeaumont Jun 17, 2024
cc94704
Merge remote-tracking branch 'origin/main' into sb/rsa-blind-signing-…
simonjbeaumont Jun 21, 2024
e760d48
Replace hand-rolled BN ops with ArbitraryPrecisionInteger and FFAC
simonjbeaumont Jun 21, 2024
bb6c618
Fixup placement of paren in guards with closures
simonjbeaumont Jun 25, 2024
0ab2368
Replace closure invocation let binding with looped var
simonjbeaumont Jun 25, 2024
7a317cd
Use ERR_peek_last_error, not ERR_get_error
simonjbeaumont Jun 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Sources/CCryptoBoringSSLShims/include/CCryptoBoringSSLShims.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ BIGNUM *CCryptoBoringSSLShims_BN_bin2bn(const void *in, size_t len, BIGNUM *ret)

size_t CCryptoBoringSSLShims_BN_bn2bin(const BIGNUM *in, void *out);

int CCryptoBoringSSLShims_BN_mod(BIGNUM *rem, const BIGNUM *a, const BIGNUM *m, BN_CTX *ctx);

int CCryptoBoringSSLShims_RSA_verify(int hash_nid, const void *msg, size_t msg_len,
const void *sig, size_t sig_len, RSA *rsa);

Expand Down
4 changes: 4 additions & 0 deletions Sources/CCryptoBoringSSLShims/shims.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ size_t CCryptoBoringSSLShims_BN_bn2bin(const BIGNUM *in, void *out) {
return CCryptoBoringSSL_BN_bn2bin(in, out);
}

int CCryptoBoringSSLShims_BN_mod(BIGNUM *rem, const BIGNUM *a, const BIGNUM *m, BN_CTX *ctx) {
return BN_mod(rem, a, m, ctx);
}

int CCryptoBoringSSLShims_RSA_verify(int hash_nid, const void *msg, size_t msg_len,
const void *sig, size_t sig_len, RSA *rsa) {
return CCryptoBoringSSL_RSA_verify(hash_nid, msg, msg_len, sig, sig_len, rsa);
Expand Down
76 changes: 76 additions & 0 deletions Sources/CryptoBoringWrapper/ArbitraryPrecisionInteger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,14 @@ extension ArbitraryPrecisionInteger {
public init<Bytes: ContiguousBytes>(bytes: Bytes) throws {
self._backing = try BackingStorage(bytes: bytes)
}

/// Create an `ArbitraryPrecisionInteger` from a hex string.
///
/// - Parameter hexString: Hex byte string (big-endian, no `0x` prefix, may start with `-` for a negative number).
@inlinable
public init(hexString: String) throws {
self._backing = try BackingStorage(hexString: hexString)
}
}

extension ArbitraryPrecisionInteger.BackingStorage {
Expand All @@ -112,8 +120,27 @@ extension ArbitraryPrecisionInteger.BackingStorage {
throw CryptoBoringWrapperError.internalBoringSSLError()
}
}

@inlinable
convenience init(hexString: String) throws {
self.init()
try hexString.withCString { hexStringPtr in
/// `BN_hex2bin` takes a `BIGNUM **` so we need a double WUMP dance.
try withUnsafeMutablePointer(to: &self._backing) { backingPtr in
var backingPtr: UnsafeMutablePointer<BIGNUM>? = backingPtr
try withUnsafeMutablePointer(to: &backingPtr) { backingPtrPtr in
/// `BN_hex2bin` returns the number of bytes of `in` processed or zero on error.
guard CCryptoBoringSSL_BN_hex2bn(backingPtrPtr, hexStringPtr) == hexString.count else {
throw CryptoBoringWrapperError.incorrectParameterSize
}
}
}
}
}
}



// MARK: - Pointer helpers

extension ArbitraryPrecisionInteger {
Expand Down Expand Up @@ -381,6 +408,49 @@ extension ArbitraryPrecisionInteger: SignedNumeric {
}
}

// MARK: - Other arithmetic operations

extension ArbitraryPrecisionInteger {
@inlinable
public static func gcd(_ a: ArbitraryPrecisionInteger, _ b: ArbitraryPrecisionInteger) throws -> ArbitraryPrecisionInteger {
var result = ArbitraryPrecisionInteger()

guard result.withUnsafeMutableBignumPointer({ resultPtr in
a.withUnsafeBignumPointer { aPtr in
b.withUnsafeBignumPointer { bPtr in
ArbitraryPrecisionInteger.withUnsafeBN_CTX { bnCtx in
CCryptoBoringSSL_BN_gcd(resultPtr, aPtr, bPtr, bnCtx)
}
}
}
}) == 1 else {
throw CryptoBoringWrapperError.internalBoringSSLError()
}

return result
}

@inlinable
public func isCoprime(with other: ArbitraryPrecisionInteger) throws -> Bool {
try Self.gcd(self, other) == 1
}

@inlinable
public static func random(inclusiveMin: UInt64, exclusiveMax: ArbitraryPrecisionInteger) throws -> ArbitraryPrecisionInteger {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typically we'd spell this with a range, rather than the explicit bounds.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is an interesting one! I initially reached for a Range but a range needs to have a homogeneous type and the underlying call we're wrapping does not take two BIGNUM*, but instead one BN_ULONG and one BIGNUM*.

This left me with two options:

  1. Write it in terms of a Range<ArbitraryPrecisionInteger> and fail if the lower bound is too large.
  2. Write it like this.

When possible I thought we should be providing API that prohibits invalid use if we can leverage the type system to do so.

var result = ArbitraryPrecisionInteger()

guard result.withUnsafeMutableBignumPointer({ resultPtr in
exclusiveMax.withUnsafeBignumPointer { exclusiveMaxPtr in
CCryptoBoringSSL_BN_rand_range_ex(resultPtr, inclusiveMin, exclusiveMaxPtr)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't compile on Android armv7, a 32-bit platform, with the following error from my Android CI:

Sources/CryptoBoringWrapper/ArbitraryPrecisionInteger.swift:444:62: error: cannot convert value of type 'UInt64' to expected argument type 'BN_ULONG' (aka 'UInt32')
                CCryptoBoringSSL_BN_rand_range_ex(resultPtr, inclusiveMin, exclusiveMaxPtr)
                                                             ^
                                                             BN_ULONG(   )

Copy link
Contributor Author

@simonjbeaumont simonjbeaumont Jun 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@finagolfin sorry about that oversight. I'm sure the fix should be pretty minimal but unfortunately I don't have a way of validating it and it's not part of our CI. Presumably the fix here is to take a UInt instead of a UInt64.

Are you able to test this and open a PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I can reproduce this locally when building for watchOS simulator. Let me put up a patch.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}
}) == 1 else {
throw CryptoBoringWrapperError.internalBoringSSLError()
}

return result
}
}

// MARK: - Serializing

extension Data {
Expand Down Expand Up @@ -408,6 +478,12 @@ extension Data {

assert(written == byteCount)
}

@inlinable
public init(bytesOf integer: ArbitraryPrecisionInteger, paddedToSize paddingSize: Int) throws {
self.init(capacity: paddingSize)
try self.append(bytesOf: integer, paddedToSize: paddingSize)
}
}

// MARK: - Printing
Expand Down
92 changes: 92 additions & 0 deletions Sources/CryptoBoringWrapper/FiniteFieldArithmeticContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,24 @@ public final class FiniteFieldArithmeticContext {
// MARK: - Arithmetic operations

extension FiniteFieldArithmeticContext {
@inlinable
public func residue(_ x: ArbitraryPrecisionInteger) throws -> ArbitraryPrecisionInteger {
var result = ArbitraryPrecisionInteger()

guard x.withUnsafeBignumPointer({ xPtr in
self.fieldSize.withUnsafeBignumPointer { modPtr in
result.withUnsafeMutableBignumPointer { resultPtr in
CCryptoBoringSSL_BN_nnmod(resultPtr, xPtr, modPtr, self.bnCtx)
}
}
}) == 1 else {
throw CryptoBoringWrapperError.internalBoringSSLError()
}

return result

}

@inlinable
public func square(_ input: ArbitraryPrecisionInteger) throws -> ArbitraryPrecisionInteger {
var output = ArbitraryPrecisionInteger()
Expand Down Expand Up @@ -151,4 +169,78 @@ extension FiniteFieldArithmeticContext {

return try ArbitraryPrecisionInteger(copying: actualOutputPointer)
}

@inlinable
public func inverse(_ x: ArbitraryPrecisionInteger) throws -> ArbitraryPrecisionInteger? {
var result = ArbitraryPrecisionInteger()

guard result.withUnsafeMutableBignumPointer({ resultPtr in
x.withUnsafeBignumPointer { xPtr in
self.fieldSize.withUnsafeBignumPointer { modPtr in
CCryptoBoringSSL_BN_mod_inverse(resultPtr, xPtr , modPtr, self.bnCtx)
}
}
}) != nil else { return nil }

return result
}

@inlinable
public func pow(_ x: ArbitraryPrecisionInteger, _ p: ArbitraryPrecisionInteger) throws -> ArbitraryPrecisionInteger {
try self.pow(x, p) { r, x, p, m, ctx, _ in CCryptoBoringSSL_BN_mod_exp(r, x, p, m, ctx) }
}

@inlinable
public func pow(secret x: ArbitraryPrecisionInteger, _ p: ArbitraryPrecisionInteger) throws -> ArbitraryPrecisionInteger {
guard x < self.fieldSize else { throw CryptoBoringWrapperError.incorrectParameterSize }
return try self.pow(x, p, using: CCryptoBoringSSL_BN_mod_exp_mont)
}

@inlinable
public func pow(secret x: ArbitraryPrecisionInteger, secret p: ArbitraryPrecisionInteger) throws -> ArbitraryPrecisionInteger {
guard x < self.fieldSize else { throw CryptoBoringWrapperError.incorrectParameterSize }
return try self.pow(x, p, using: CCryptoBoringSSL_BN_mod_exp_mont_consttime)
}

/* private but @usableFromInline */ @usableFromInline func pow(
_ a: ArbitraryPrecisionInteger,
_ b: ArbitraryPrecisionInteger,
using method: (
_ rr: UnsafeMutablePointer<BIGNUM>?,
_ a: UnsafePointer<BIGNUM>?,
_ p: UnsafePointer<BIGNUM>?,
_ m: UnsafePointer<BIGNUM>?,
_ ctx: OpaquePointer?,
_ mont: UnsafePointer<BN_MONT_CTX>?
) -> Int32
) throws -> ArbitraryPrecisionInteger {
var result = ArbitraryPrecisionInteger()

guard result.withUnsafeMutableBignumPointer({ resultPtr in
a.withUnsafeBignumPointer { aPtr in
b.withUnsafeBignumPointer { bPtr in
self.fieldSize.withUnsafeBignumPointer { modPtr in
self.withUnsafeBN_MONT_CTX { montCtxPtr in
method(resultPtr, aPtr, bPtr, modPtr, self.bnCtx, montCtxPtr)
}
}
}
}
}) == 1 else {
throw CryptoBoringWrapperError.internalBoringSSLError()
}

return result
}

/// Some functions require a `BN_MONT_CTX` parameter: this obtains one for the field modulus with a scoped lifetime.
/* private but @usableFromInline */ @usableFromInline func withUnsafeBN_MONT_CTX<T>(_ body: (UnsafePointer<BN_MONT_CTX>) throws -> T) rethrows -> T {
try self.fieldSize.withUnsafeBignumPointer { modPtr in
// We force unwrap here because this call can only fail if the allocator is broken, and if
// the allocator fails we don't have long to live anyway.
let montCtx = CCryptoBoringSSL_BN_MONT_CTX_new_for_modulus(modPtr, self.bnCtx)!
defer { CCryptoBoringSSL_BN_MONT_CTX_free(montCtx) }
return try body(montCtx)
}
}
}
1 change: 1 addition & 0 deletions Sources/_CryptoExtras/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
add_library(_CryptoExtras
"ChaCha20CTR/BoringSSL/ChaCha20CTR_boring.swift"
"ChaCha20CTR/ChaCha20CTR.swift"
"RSA/RSA+BlindSigning.swift"
"RSA/RSA.swift"
"RSA/RSA_boring.swift"
"RSA/RSA_security.swift"
Expand Down
Loading