apple / swift-crypto

Open-source implementation of a substantial portion of the API of Apple CryptoKit suitable for use on Linux platforms.
https://apple.github.io/swift-crypto
Apache License 2.0
1.46k stars 161 forks source link

Difference in RSA Signature on macOS vs Ubuntu #254

Closed dezinezync closed 1 month ago

dezinezync commented 1 month ago

New Issue Checklist

Expected behavior

When generating a signature using _RSA.Signing.PrivateKey(pemRepresentation:).signature(for:padding:), the method should generate identical signatures on macOS 14.6.1 (development machine) versus Ubuntu 22.04.4 LTS.

Actual behavior

When generating a signature using _RSA.Signing.PrivateKey(pemRepresentation:).signature(for:padding:), the method generates different outputs on macOS 14.6.1 (development machine) versus Ubuntu 22.04.4 LTS.

Steps to reproduce

Test case code for reproducing the behaviour:

final class SignatureValidatorTests: XCTestCase {

  func testGeneratingSignatureHeader() throws {
    let digest = "dJZMgTHg+E8GHSUfoZmlcNHmemgyb73xYXuSu6kuGS4="
    let date = "Wed, 11 Sep 2024 07:20:22 GMT"
    // what should be generated + is generated on macOS
    let expected = "Jk+0f4MPcFHewm7x+nHxYjCXNRoEwGVUz9SpKNFh9DWbQRQwy/e/SG5T1QKDnigvVI8tWDGFPTSS2AvLPud5KKzWFrps29AkRooc2bz1ONchCZRr4HC6bHi9LwALoZgnhzy04P0Esn4AfNRCi6KlFcJNi2jUDxNzgGpF5a1LHPmLmN5VldySzFmXMQe5M5MK7c9nImfhfgivr9CuxZxLnuHuotHhawn0Tv20bXvOWK1LJMLIw6UI2Ofq8Gfr3K2AYpoP/sJfETfw0kMorbj38tSjXgu4PVkL5FER3OZqNzU7i5HJEkjTdjusD7txkTP4/eO7oCoyD3pzwZ+xtknrYg=="
    // what gets generated on Ubuntu
    let actual = "dCPLJ7FOn9hkRuNOpCbM2RXrD/YOrbD4yhX7Ify3B9XrGZ/amrw/xnK6HS+W6/817iTbpi61G1NQAovi2wOVs7gR4DtP+fNxQv/3oaQmyN4PWnUBsOFnPRpISiy9b6SuEEba+se19GocZOnAtkCVHdAzvPLvX7xrPMBBmTaHfaiIDFapB8dW7zDX+7IrbleGRUwitwLH8bG8T9+SGmD48p7bFbqHiZ9B2VpV91HhhR7TJ5SzEDhHcrGRJxrXOZ1Tfftn3dJKYZIpQ8Cq61Ox65D99z6UpReav2nZK80+32c02Av95Nk22uzdiidPI0mNE/WIgOn3bkjSFLtBH4ws3A=="

    let stringToSign = """
(request-target): post /inbox
host: mastodon.social
date: \(date)
digest: SHA-256=\(digest)
"""

    let stsData = stringToSign.data(using: .utf8)!
    let privateKey = try _RSA.Signing.PrivateKey(pemRepresentation: privateKeyPEM)
    let signatureData = try privateKey.signature(for: stsData, padding: .insecurePKCS1v1_5)
    let signature = Data(signatureData.rawRepresentation).base64EncodedString()

    XCTAssertEqual(signature, actual)       // passes on macOS, fails on Ubuntu        
    XCTAssertEqual(signature, expected)  // passes on Ubuntu, fails on macOS  
  }

  // MARK: -
  let privateKeyPEM = """
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA0oZMFXQadnV7K03Dy3ZJBdYPb/8aJputrykCecEv+CRZIRyu
+v+F75nZx7aeu0Mz61onAINhIrQSwpT3HkqDB1XfNEwFE2VubgK/b8MvC07hXrVl
scYITyiDy2836bwvkr7w2T1jxCjBcrBKM+7/atFKuRRQhW2FXqfD/24UToo4nW3w
Qo/e8JlAhcq7y0Mx4KqU8zCw8f0VGl1FnaWpS0x1YRESgvet1vXtST/uAUz2SOV0
DyqpPHvhyQSr+4iwm+3WoSgKn67PQ4gxdIccqXNh+YnZAo0+nQ9rp+jlRfJEMnda
aTsWmCx2L40nWQUXd8mRnNiBMdZtqD3R+JTg9QIDAQABAoIBABobO04hwrJT6T06
fIA7n9yQagOqb9S/0274N+1fTpKH92GKzAwmKbbHrmD8vXxdXg/ecx22LRJ/oB/v
FyHzfYIoXPCASNTZ7DLKNnEmlCvj0UDMX9sGwrsGPCm7FgTvKDsbYISf/0IxB6Qw
RlNgXN4jLA/9e+q/5R0S0tRXEz+V3Lu09T59SzV9Yd2BOb5G2PbhIE81YCIjXwTA
g1cyHY+FLyn4GVQVGXpkgKk6c24V4ngagLm2jiUcdVX/N3MQt/ZGyw1TAZo3L372
q5ERCXeiihjdW2Tx9ap0U0HZQKg2IfoogQjM0gUKDt9y0km7GJgzyH+j2gxvViJ/
1vzmFiECgYEA9SpizlCaGxNWjdDfTyc2FBn073e/+22m7oEDDH8Cjp/uHjioIsaw
1ylvIPpTK3Vvy0TEQvYaHa+8WP8YKekfkYcbBcYFohLyw4U4qie+8GvT1jD/6DXk
kHCz3cIGQBddhQ74gTwLU9UF03sJXHUwEVTMlSKw8Y4Ohcbf1er4v60CgYEA29QC
kO/VJAJJLQZH9qBQzVV+9OILpX+wrTnT75ulYT2heg4RU2VCqbZArXJj50o1SZJK
Lbh6GxsDr6U1gsmb5gDsWhj3ig8GZRKRWMBWEC9CUIz9WSYzu7yYnDmYmr2pDuMo
T9vTqSAzBI86gB+LPali9gGf7qJyHQ6yQ1iDr2kCgYEAtrYyEOSNFSSiWuqRomHs
sapumpE6aN1djV5ksUlu1HBoe+lplRFS9eeRRJiKp1bMTEchpW3YJMu1qPXkQkfV
GRYBk5ny1MkeV4zPoDAi4vbZV0YJUjvn7aWwxtI06kr/jYyMs3PWVVHrF6MYZOmJ
O0Fhfrvt9vgR5q1CYCDP5bkCgYBh4yttXM6dZUMugHCFGuhANwT8iLfE73I+ANUm
Cjn/bhE8p40BhMLFjbmJQrnH4VyPMUxExbPh5AqlKT0Fyz7XkauthxabbItdcII2
chcgx9qQweeCzicVPyr5zg/tP+1LCs4FmY7L7eGp8xmKugEn6Qh/IUQVYFLOK6An
qv+gmQKBgQC5iHye6Tvv+wfZ0BntAM6yQbLhGLeOSzRRAd58pAN7uWugmZaAzQ7h
63ghGvEHjLVTthWcMWzpcuxNI2EE84sjYZg3noyYzYB5s9QFURft8ppu2b6zj5so
HvFrfdu4DUI4b5rTXLNRxDeDsYTwAUghy9Ds5V5qqoTepuDaPSgFdg==
-----END RSA PRIVATE KEY-----
"""

}

Swift Crypto version/commit hash

https://github.com/apple/swift-crypto/commit/ce204da6f612dbcf131afda1f208115202eae644

Environment

Swift version: Swift version 5.10 (swift-5.10-RELEASE)
Target: x86_64-unknown-linux-gnu
Unix version: Linux ip-172-30-3-16 6.5.0-1016-aws #16~22.04.1-Ubuntu SMP Wed Mar 13 18:54:49 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
Lukasa commented 1 month ago

Hrm, how confident are you of this? I can't reproduce this locally with a docker image: I see this passing in both environments.

dezinezync commented 1 month ago

I hit this bug on a production server.

So to verify, I tunnelled my dev setup to a temporary remote host to verify, and it works as expected, no changes in code.

I forgot to mention in the OP: the local code, running on macOS, is a debug build, while the server build is a release/production build, if that is of any consequence.

Lukasa commented 1 month ago

I'm really struggling to find a way to make this reproduce at all. Does your xctest actually fail on your Linux node? Are you actually running Crypto from main?

dezinezync commented 1 month ago

Does your xctest actually fail on your Linux node?

Yes

Are you actually running Crypto from main?

Yes, the specific commit mentioned in the OP.

I'll setup Ubuntu 22.04.4 LTS in UTM as a VM tomorrow morning to figure out if I can reproduce this locally as well, and get back to you with my findings.

Lukasa commented 1 month ago

Thank you, that would be helpful. I tried the 5.10 docker image, both arm64 and amd64, without success.

dezinezync commented 1 month ago

I can reproduce this on a Ubuntu 22.04.4 VM running in UTM (host: macOS 14.6.1 ARM) :

➜  testPKG ./.build/release/CryptoMe
Actual true
Expected false
➜  testPKG ./.build/checkouts/swift-crypto/scripts/environment.sh
This will replace your current pasteboard. Continue? [y/n]y
Swift version: Swift version 5.10.1 (swift-5.10.1-RELEASE)
Target: aarch64-unknown-linux-gnu
Unix version: Linux ubuntuutm 6.8.0-40-generic #40~22.04.3-Ubuntu SMP PREEMPT_DYNAMIC Tue Jul 30 17:53:10 UTC 2 aarch64 aarch64 aarch64 GNU/Linux

Your pasteboard now contains debug info, paste it on Github

Creating a debug build has no impact on this:

➜  testPKG ./.build/debug/CryptoMe
Actual true
Expected false
dezinezync commented 1 month ago

@Lukasa I've created a more condensed sample code and it validates correctly on all three platforms: macOS (local), Ubuntu (UTM VM), and the production server (UTM VM).

I apologise for the confusion, and I appreciate your patience.

I'm closing this thread, as I continue to investigate this further on my end.

Lukasa commented 1 month ago

No need to apologise, raising it here was absolutely the right thing to do. I hope you were able to get pointed in the right direction! Thanks for the issue. ✨