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

Get rid of openssl dependency #79

Closed
baltpeter opened this issue May 24, 2023 · 7 comments
Closed

Get rid of openssl dependency #79

baltpeter opened this issue May 24, 2023 · 7 comments
Assignees
Labels

Comments

@baltpeter
Copy link
Member

With #78, our last host dependency for Android is openssl1. We only need that to calculate the subject_hash_old for our CA:

execa('openssl', ['x509', '-inform', 'PEM', '-subject_hash_old', '-in', path]).then(

On Linux and macOS, that dependency isn't much of an issue, it's preinstalled almost everywhere anyway. But on Windows, you have to download a random .exe from some weird site.

Footnotes

  1. Apart from the udev rules, but those are only necessary for physical devices, and I really don't think we can do anything about that setup step since I'm pretty sure that requires root.

@baltpeter baltpeter added the UX label May 24, 2023
@baltpeter baltpeter self-assigned this May 24, 2023
@baltpeter
Copy link
Member Author

For the record: Official documentation that mentions that the files need to be named according to the subject_hash_old.

@baltpeter
Copy link
Member Author

https://stackoverflow.com/a/30265791 has some helpful background info and a snippet of the code that generates the hash in openssl.

@zner0L
Copy link
Contributor

zner0L commented May 24, 2023

I am afraid if I follow through with #44, I'll need openssl as a dependency as well. I don’t think we want to generate keys and certs ourselves…

@baltpeter
Copy link
Member Author

I am afraid if I follow through with #44, I'll need openssl as a dependency as well. I don’t think we want to generate keys and certs ourselves…

We're already depending on PKI.js for iOS. I'm reasonably confident that that should support everything you'll need (e.g. https://pkijs.org/examples/CertificateComplexExample/X509_cert_complex_example.html).

The problem here is only that Android uses this esoteric subject_hash_old that no one else seems to use…

@baltpeter
Copy link
Member Author

I pasted https://stackoverflow.com/a/30265791 into Bing Chat and asked it to implement that for Node. After a little bit of back and forth, it produced this code, which produces the correct hash!

import { createHash } from 'crypto';
import { readFile } from 'fs/promises';
import { Certificate } from 'pkijs';

// A helper function to convert a buffer to an unsigned long
function bufferToULong(buffer) {
    return (buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24)) >>> 0;
}

function subjectHashOld(cert) {
    // Get the subject name as a DER-encoded buffer
    const subject = cert.subject.valueBeforeDecode;

    // Create a hash object with MD5 algorithm
    const hash = createHash('md5');

    // Update the hash with the subject buffer
    hash.update(Buffer.from(subject));

    // Get the digest as a buffer
    const digest = hash.digest();

    // Truncate the digest to the first four bytes and convert to an unsigned long
    const truncated = digest.slice(0, 4);
    const ulong = bufferToULong(truncated);

    // Return the unsigned long as a hexadecimal string
    return ulong.toString(16);
}

(async () => {
    const path = process.argv[2];

    const certPem = await readFile(path, 'utf8');

    const certBase64 = certPem.replace(/(-----(BEGIN|END) CERTIFICATE-----|[\r\n])/g, '');
    const certDer = Buffer.from(certBase64, 'base64');

    const c = Certificate.fromBER(certDer);
    console.log(subjectHashOld(c));
})();

It would have taken me significantly longer to get there myself!

Transcript for reference: Conversation.pdf

@zner0L
Copy link
Contributor

zner0L commented May 24, 2023

Ah, yes. You are right!

@baltpeter
Copy link
Member Author

Simplified and cleaned up version of that:

async function subjectHashOld(path: string) {
    const { cert } = await parsePemCertificateFromFile(path);

    const hash = createHash('md5').update(Buffer.from(cert.subject.valueBeforeDecode)).digest();
    const truncated = hash.subarray(0, 4);
    const ulong = (truncated[0]! | (truncated[1]! << 8) | (truncated[2]! << 16) | (truncated[3]! << 24)) >>> 0;

    return ulong.toString(16);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants