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

Initial implementation #3

Merged
merged 79 commits into from
Jan 27, 2024
Merged

Initial implementation #3

merged 79 commits into from
Jan 27, 2024

Conversation

stevefan1999-personal
Copy link
Contributor

@stevefan1999-personal stevefan1999-personal commented Sep 23, 2023

I think I made it straight in the Zulip chat that I should try and see if this could be merged.

As I previously said in the chat, I used a lot of Rust type gymnastics which may look clever on the surface but not only it is very mentally challenging (it took me a few nights to sort them out actually), and worse, it induces a lot of cognitive loads for future maintenances, so what if we want to scale it out to support other TLS compatible algorithms and crypto suites, that would be pretty hard because of all those generic trait bounds and type information which took a lot of time to put the puzzle together.

But at the same time, as a competent programmer, you should not repeat yourself (at least DRY is a very important principle to me), and this is a pretty conflicting philosophy for me. This is the very reason I'm reluctant to make a PR in the first place.

But then I have an eureka moment: why don't I just rewrite the egregious generics parts with macros? Annnnnnnd boom! I decided to take some time to do it, and it definitely looked cleaner and easier to understand now, with the added benefit of easier to debug, much better documentations thanks to concrete implementations and having concise information for tracing in the future.

Meanwhile only the AES-GCM part for AEAD has not yet finished with the macro transition. I have also added some integration tests using various example sites in BadSSL as an example, but it would be better if we can host those test sites locally, particularly for air-gapped environments where they may even control the test environments. I have also added some generic websites as test vectors, but we are yet to have crypto suites control to have a more precise test against a specific crypto suites (say for example, TLS1.3 predominately uses ChaCha20-Poly1305 and so AES-GCM is rarely used and less coverage is reported in the integration tests).

Still, I would continue regarding this code as highly experimental, and potentially bug-ridden. In particular, the ECC256 BadSSL test is still failing while others looked...fine. My old repo will be archived and cease to update, and the efforts are all scoped under my fork now.

Fixes #2 and #1

@tarcieri
Copy link
Member

Did a pass over the cryptographic parts of this PR at least

@stevefan1999-personal
Copy link
Contributor Author

stevefan1999-personal commented Nov 13, 2023

Did a pass over the cryptographic parts of this PR at least

Thanks for reviewing!

To say if this is mergeable atm, hmm no. Basic client-side supports are supposedly fine, but server hosting is clearly devastating, so I think this is not in a really good shape.

Until the integration tests to make private keys and certs for different TLS schemes are made with automated GH Actions to check for them, I don't want to give this a pass, sadly I'm pretty preoccupated with other things in life, I can only offer limited supports.

@tarcieri
Copy link
Member

I meant that to say I took a look over the cryptographic parts of the PR

@stevefan1999-personal
Copy link
Contributor Author

Thanks. I can confirm at least the public keys and signatures superficially look well-formed.

I tried to verify the first one using ring and got an error as well:

let public_key_bytes = [4, 251, 69, 233, 72, 245, 113, 144, 25, 161, 81, 74, 19, 138, 202, 46, 50, 125, 130, 112, 237, 18, 117, 248, 231, 15, 1, 83, 48, 4, 14, 81, 65, 121, 253, 167, 198, 154, 106, 48, 128, 89, 222, 18, 200, 112, 249, 66, 54, 150, 154, 17, 1, 103, 129, 221, 163, 130, 12, 27, 139, 101, 93, 76, 106];
let message_bytes = [67, 184, 212, 248, 245, 187, 214, 44, 247, 201, 174, 102, 32, 105, 114, 147, 161, 63, 157, 102, 98, 97, 50, 138, 199, 45, 92, 42, 105, 181, 32, 122, 38, 93, 170, 69, 187, 5, 226, 255, 72, 235, 207, 131, 109, 90, 106, 192, 111, 11, 58, 251, 30, 179, 180, 179, 35, 59, 87, 181, 162, 178, 45, 179, 3, 0, 23, 65, 4, 77, 15, 150, 21, 247, 86, 155, 131, 57, 159, 238, 185, 1, 209, 179, 174, 156, 133, 61, 102, 173, 100, 204, 115, 34, 71, 213, 121, 200, 93, 238, 238, 124, 218, 40, 70, 77, 219, 188, 206, 43, 123, 133, 132, 68, 148, 216, 124, 156, 235, 102, 188, 123, 142, 163, 231, 166, 16, 63, 210, 54, 170, 138, 174];
let signature_bytes = [48, 69, 2, 33, 0, 209, 42, 230, 169, 214, 117, 224, 61, 169, 95, 103, 5, 226, 244, 194, 73, 166, 187, 33, 14, 181, 60, 212, 16, 188, 186, 182, 176, 9, 7, 212, 88, 2, 32, 113, 65, 112, 126, 96, 0, 200, 226, 74, 81, 7, 32, 10, 214, 135, 30, 87, 32, 1, 189, 137, 98, 173, 245, 88, 39, 132, 20, 2, 73, 253, 168];

let pk = ring::signature::UnparsedPublicKey::new(&ring::signature::ECDSA_P256_SHA256_ASN1, public_key_bytes);
dbg!(pk.verify(&message_bytes, &signature_bytes));

...prints:

[src/main.rs:17] pk.verify(&message_bytes, &signature_bytes) = Err(
    Unspecified,
)

Any idea what the provenance of those examples happens to be? It'd be great to grab the original certificate.

I tried grabbing it with:

$ openssl s_client -connect ecc256.badssl.com:443 -servername ecc256.badssl.com

...and while the subject's public key appears to be NIST P-256, the root and intermediate certificates as well as the signature over the leaf all appear to be RSA, so I'm not sure where the ECDSA signature is coming from. Is it a transcript signature perhaps?

Are you sure SHA-256 is the correct digest algorithm?

Hmm this is my result:

(base) PS C:\Users\steve> openssl s_client -connect ecc256.badssl.com:443 -servername ecc256.badssl.com
CONNECTED(000001C4)
depth=2 C = US, O = Internet Security Research Group, CN = ISRG Root X1
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = R3
verify return:1
depth=0 CN = *.badssl.com
verify return:1
---
Certificate chain
 0 s:CN = *.badssl.com
   i:C = US, O = Let's Encrypt, CN = R3
 1 s:C = US, O = Let's Encrypt, CN = R3
   i:C = US, O = Internet Security Research Group, CN = ISRG Root X1
 2 s:C = US, O = Internet Security Research Group, CN = ISRG Root X1
   i:O = Digital Signature Trust Co., CN = DST Root CA X3
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIEJjCCAw6gAwIBAgISBLUhzN+vXhI4P/iz/HE4ZtudMA0GCSqGSIb3DQEBCwUA
MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD
EwJSMzAeFw0yMzEwMTkxNTQ4MzZaFw0yNDAxMTcxNTQ4MzVaMBcxFTATBgNVBAMM
DCouYmFkc3NsLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABD1A9AaTcG3k
4mmsmNJJZ7tvFz6nbtFHXmlo7qzp41wTerBEtr2LJdcfvTX+MMz5tHKK0r4x3eNf
z+alpf2xKiijggIaMIICFjAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0lBBYwFAYIKwYB
BQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFNN2dN1ahCaI
c01y7iA7kT4iyhtqMB8GA1UdIwQYMBaAFBQusxe3WFbLrlAJQOYfr52LFMLGMFUG
CCsGAQUFBwEBBEkwRzAhBggrBgEFBQcwAYYVaHR0cDovL3IzLm8ubGVuY3Iub3Jn
MCIGCCsGAQUFBzAChhZodHRwOi8vcjMuaS5sZW5jci5vcmcvMCMGA1UdEQQcMBqC
DCouYmFkc3NsLmNvbYIKYmFkc3NsLmNvbTATBgNVHSAEDDAKMAgGBmeBDAECATCC
AQQGCisGAQQB1nkCBAIEgfUEgfIA8AB3ADtTd3U+LbmAToswWwb+QDtn2E/D9Me9
AA0tcm/h+tQXAAABi0jWxw4AAAQDAEgwRgIhAM2LJVcsONJFXcI1AHwsSxL85mrT
BrHe1h01HK72FS5RAiEAn6hjtZ5PdiZ2X+RFeKdiQjIg5aqCDPRP2nms4vUrOW0A
dQB2/4g/Crb7lVHCYcz1h7o0tKTNuyncaEIKn+ZnTFo6dAAAAYtI1sc5AAAEAwBG
MEQCIAVZgT7zshKwH9Lc68ZyNN3VADGHzDvICx9kFhUlZsdRAiAT4G9NQx1XtXn3
W73s4t1N+ZXUOUFuG67asMpeYP/PmzANBgkqhkiG9w0BAQsFAAOCAQEAAzI7wsxI
4lu5C5IFPgvlZ3qxmMGfhgYKEYvbaYY2o1EGq6LmNuNUBs5DvtfM2FCxzBFgGbiU
8cHhwgjHLLL2N+pKL0Oh2colsN4CiPjBn0FaWs5Jaqki8SyQrOdAqMbZp11qPJ1k
Svpd8xGTHO1lwls4qmmK211cuRJ0e9vh9hcwfC/RiZ9kOMl6RlezhzhCdjsxO1kh
oT7JsygD4ceNDvAuKBIL4lMmGFXpugLE5R4NAxtXG5ElK2E41c4iuo3l5mFVe2tM
GWtFe4L0UZYtQV+MDojdfITr8O9RGn28VqmUnooO0MUQTTRfZppC9bZ4Yy32zFcL
aW1BKbupMs7oaw==
-----END CERTIFICATE-----
subject=CN = *.badssl.com

issuer=C = US, O = Let's Encrypt, CN = R3

---
No client certificate CA names sent
Peer signing digest: SHA512
Peer signature type: ECDSA
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 4262 bytes and written 445 bytes
Verification error: unable to get local issuer certificate
---
New, TLSv1.2, Cipher is ECDHE-ECDSA-AES128-GCM-SHA256
Server public key is 256 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-ECDSA-AES128-GCM-SHA256
    Session-ID: E6B2C4E617682C635693C97CAF13D58DEF7480E5E6B3EBC998BBB9F6807E2333
    Session-ID-ctx:
    Master-Key: 9D4391155D7D9C2A8D6CB25CDAE8B7A3D460F3E244319E8B6A9D65E53993FC731B3FDBEB7C6F2728E65A5513990707F4
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 300 (seconds)
    TLS session ticket:
    0000 - e8 3b d9 b1 a8 8f 4b a4-0e 3e 1f 91 37 84 b0 6d   .;....K..>..7..m
    0010 - 81 18 f6 ce 06 28 43 6f-da 2a c4 47 7d a9 58 4a   .....(Co.*.G}.XJ
    0020 - 4e 45 72 b0 e6 c8 a5 bc-e5 35 cc cf 02 36 d6 57   NEr......5...6.W
    0030 - e2 3c c6 dc d4 92 a1 91-2b b2 c2 f4 2c 0b 1b 62   .<......+...,..b
    0040 - 45 77 a5 f0 9c a2 ea b1-ed 12 b4 8b 4c 31 a4 24   Ew..........L1.$
    0050 - 04 53 5d fd 89 0c ca c3-7e 51 d1 ae 2c 65 18 39   .S].....~Q..,e.9
    0060 - 39 11 1c fa 19 79 e4 37-e9 f9 99 7d 46 f8 30 b6   9....y.7...}F.0.
    0070 - be 8e eb 9a fa 3f 81 79-17 d6 a9 d6 4d b8 77 3c   .....?.y....M.w<
    0080 - b6 c3 f8 e6 96 68 a9 78-0c 92 b0 9f f6 2b e5 4a   .....h.x.....+.J
    0090 - 8b 2b 74 64 66 49 69 88-4b d1 61 44 10 7e 36 ce   .+tdfIi.K.aD.~6.
    00a0 - 2a de a4 3f 21 03 59 dd-56 db 7a e1 81 8b b9 0b   *..?!.Y.V.z.....
    00b0 - 1f f3 c3 f9 8e 86 51 5f-22 0b 0e 1d b8 2b 33 3f   ......Q_"....+3?

    Start Time: 1700200193
    Timeout   : 7200 (sec)
    Verify return code: 20 (unable to get local issuer certificate)
    Extended master secret: no
---

@stevefan1999-personal
Copy link
Contributor Author

Earlier messages suggested it was ECDSA-P256-SHA384, which as I recall triggers the special case when the signing hash function output is longer than the group order -- the top bits of the hash output need to be used.

Aha! That did the trick.

@stevefan1999-personal for supporting multiple digests dynamically chosen at runtime you probably want to use the PrehashVerifier trait.

The following successfully verifies the first example:

use p256::ecdsa::{DerSignature, VerifyingKey, signature::hazmat::PrehashVerifier};
use sha2::{Sha384, Digest};

let public_key_bytes = [4, 251, 69, 233, 72, 245, 113, 144, 25, 161, 81, 74, 19, 138, 202, 46, 50, 125, 130, 112, 237, 18, 117, 248, 231, 15, 1, 83, 48, 4, 14, 81, 65, 121, 253, 167, 198, 154, 106, 48, 128, 89, 222, 18, 200, 112, 249, 66, 54, 150, 154, 17, 1, 103, 129, 221, 163, 130, 12, 27, 139, 101, 93, 76, 106];
let message_bytes = [67, 184, 212, 248, 245, 187, 214, 44, 247, 201, 174, 102, 32, 105, 114, 147, 161, 63, 157, 102, 98, 97, 50, 138, 199, 45, 92, 42, 105, 181, 32, 122, 38, 93, 170, 69, 187, 5, 226, 255, 72, 235, 207, 131, 109, 90, 106, 192, 111, 11, 58, 251, 30, 179, 180, 179, 35, 59, 87, 181, 162, 178, 45, 179, 3, 0, 23, 65, 4, 77, 15, 150, 21, 247, 86, 155, 131, 57, 159, 238, 185, 1, 209, 179, 174, 156, 133, 61, 102, 173, 100, 204, 115, 34, 71, 213, 121, 200, 93, 238, 238, 124, 218, 40, 70, 77, 219, 188, 206, 43, 123, 133, 132, 68, 148, 216, 124, 156, 235, 102, 188, 123, 142, 163, 231, 166, 16, 63, 210, 54, 170, 138, 174];
let signature_bytes = [48, 69, 2, 33, 0, 209, 42, 230, 169, 214, 117, 224, 61, 169, 95, 103, 5, 226, 244, 194, 73, 166, 187, 33, 14, 181, 60, 212, 16, 188, 186, 182, 176, 9, 7, 212, 88, 2, 32, 113, 65, 112, 126, 96, 0, 200, 226, 74, 81, 7, 32, 10, 214, 135, 30, 87, 32, 1, 189, 137, 98, 173, 245, 88, 39, 132, 20, 2, 73, 253, 168];

let vk = VerifyingKey::from_sec1_bytes(&public_key_bytes).unwrap();
let sig = DerSignature::try_from(signature_bytes.as_ref()).unwrap();

assert!(vk.verify_prehash(&Sha384::digest(&message_bytes), &sig).is_ok());

Ah, why do I forgot to read again...

Copy link
Member

@tarcieri tarcieri left a comment

Choose a reason for hiding this comment

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

At this point I see no major reason not to merge this

@tarcieri tarcieri changed the title Merge my own progress onto the official repo Initial implementation Jan 27, 2024
@tarcieri tarcieri merged commit e1f33ae into RustCrypto:master Jan 27, 2024
tarcieri added a commit that referenced this pull request Jan 28, 2024
As originally discussed in this issue:

#3 (comment)

The code was originally developed as Apache 2.0+MIT+ISC, though this
repo included only a MIT license in #3.

To my understanding there are three relevant contributors: myself,
@stevefan1999-personal, and @ctz.
@tarcieri tarcieri mentioned this pull request Jan 28, 2024
3 tasks
tarcieri added a commit that referenced this pull request Jan 28, 2024
As originally discussed in this issue:

#3 (comment)

The code was originally developed as Apache 2.0+MIT+ISC, though this
repo included only a MIT license in #3.

To my understanding there are three relevant contributors: myself,
@stevefan1999-personal, and @ctz.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Should this merge with my own code, or start anew with a new structure?
5 participants