Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
ueman committed Jul 21, 2024
1 parent 509343a commit ab05f47
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 43 deletions.
11 changes: 5 additions & 6 deletions passkit/lib/src/pkpass/pkpass.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class PkPass {
/// Parses bytes to a [PkPass] file.
/// Setting [skipVerification] to true disables any checksum or signature
/// verification and validation.
// TODO(ueman): Provide an async method for this.
// TODO(any): Provide an async method for this.
static PkPass fromBytes(
final List<int> bytes, {
bool skipVerification = false,
Expand All @@ -76,12 +76,11 @@ class PkPass {
final signatureContent =
archive.findFile('signature')!.content as List<int>;

final isValid = verifySignature(
Uint8List.fromList(signatureContent),
Uint8List.fromList(sha256.convert(manifestContent).bytes),
passData,
verifySignature(
signature: Uint8List.fromList(signatureContent),
manifestHash: Uint8List.fromList(sha256.convert(manifestContent).bytes),
pass: passData,
);
print(isValid);
}

return PkPass(
Expand Down
63 changes: 42 additions & 21 deletions passkit/lib/src/signature_verification.dart
Original file line number Diff line number Diff line change
@@ -1,44 +1,65 @@
import 'dart:typed_data';
import 'package:passkit/passkit.dart';
import 'package:pkcs7/pkcs7.dart';
import 'package:collection/collection.dart';

bool verifySignature(
Uint8List signature,
Uint8List manifestHash,
PassData pass,
) {
final wwdrG4 =
X509.fromDer(Uint8List.fromList(Worldwide_Developer_Relations_G4));
// What about old WWDR certs? Apple seemingly accepts them just fine?
// Only make sure the signing cert matches the pass' contents?
// Should pass updates just have valid certs, or is it fine
// as long as the contents match?
bool verifySignature({
required Uint8List signature,
required Uint8List manifestHash,
required PassData pass,
DateTime? now,
bool checkOutdatedIssuerCerts = false,
}) {
final pkcs7 = Pkcs7.fromDer(signature);

/*
final passKitIssuerCert = pkcs7.certificates.firstWhere((x509) {
return x509.serialNumber == BigInt.parse(pass.serialNumber);
if (checkOutdatedIssuerCerts) {
for (final cert in pkcs7.certificates) {
if (cert.notAfter.isBefore(now ?? DateTime.now())) {
throw Exception('Certificate of the PkPass expired');
}
}
}

final issuerCert = pkcs7.certificates.firstWhereOrNull((x509) {
return x509.subject.firstWhereOrNull((it) => it.key.name == 'UID')?.value ==
pass.passTypeIdentifier &&
x509.subject
.firstWhereOrNull(
(it) => it.key.name == 'organizationalUnitName',
)
?.value ==
pass.teamIdentifier;
});
passKitIssuerCert.subject.firstWhere((it) => it.key.name == 'UID').value ==
pass.passTypeIdentifier;
passKitIssuerCert.subject
.firstWhere((it) => it.key.name == 'organizationalUnitName')
.value ==
pass.passTypeIdentifier;
*/
if (issuerCert == null) {
throw Exception("Cert from issues doesn't match content of the pass");
}

// there must be a certificate in pkcs7.certificates which
// - has a subject with a UID which matches the passTypeIdentifier
// - has a organizationalUnitName which matches the teamIdentifier
// there must be a certificate in there which matches an APPLE WWDR certificate

// From https://developer.apple.com/documentation/walletpasses/building_a_pass
// Set the passTypeIdentifier of Pass in the pass.json file to the identifier. Set the serialNumber key to the unique serial number for that identifier.
// Set the passTypeIdentifier of Pass in the pass.json file to the identifier.
// Set the serialNumber key to the unique serial number for that identifier.

final si = pkcs7.verify([wwdrG4]);
final signerInfo = pkcs7.verify([_wwdrG4]);
// final algo = si.getDigest(si.digestAlgorithm); Calculate hash based on the algo?
return si.listEquality(manifestHash, si.messageDigest!);
return signerInfo.listEquality(manifestHash, signerInfo.messageDigest!);
}

X509 get wwdrG4 =>
X509 get _wwdrG4 =>
X509.fromDer(Uint8List.fromList(Worldwide_Developer_Relations_G4));

/// This is the content of https://www.apple.com/certificateauthority/AppleWWDRCAG4.cer .
/// It was basically read like this `File('AppleWWDRCAG4.cer').readAsBytesSync()` and then just pasted
/// here.
///
/// More info at:
/// https://developer.apple.com/help/account/reference/wwdr-intermediate-certificates/
/// https://www.apple.com/certificateauthority/
// ignore: constant_identifier_names
Expand Down
1 change: 1 addition & 0 deletions passkit/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ environment:

dependencies:
archive: ^3.6.0
collection: ^1.18.0
crypto: ^3.0.0
csslib: ^1.0.0
http: ^1.2.0
Expand Down
Binary file removed passkit/test/AppleWWDRMPCA1G1.cer
Binary file not shown.
15 changes: 0 additions & 15 deletions passkit/test/cert_test.dart

This file was deleted.

3 changes: 2 additions & 1 deletion passkit/test/pkpass/checksum_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ void main() {
});

test('does not throw for valid checksums', () async {
final bytes = File('test/sample_passes/kino.pkpass').readAsBytesSync();
final bytes =
File('test/sample_passes/coffee_club.pkpass').readAsBytesSync();

expect(
() => PkPass.fromBytes(bytes),
Expand Down
Binary file added passkit/test/sample_passes/coffee_club.pkpass
Binary file not shown.
48 changes: 48 additions & 0 deletions passkit_ui/example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.18.0"
convert:
dependency: transitive
description:
name: convert
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
crypto:
dependency: transitive
description:
Expand Down Expand Up @@ -131,6 +139,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.19.0"
js:
dependency: transitive
description:
name: js
sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
url: "https://pub.dev"
source: hosted
version: "0.7.1"
json_annotation:
dependency: transitive
description:
Expand Down Expand Up @@ -217,6 +233,38 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.9.0"
pem:
dependency: transitive
description:
name: pem
sha256: "3dfb24524f805ad694ba3cdbb6387ab31ab661fdb8ea873052ed88487fcfef86"
url: "https://pub.dev"
source: hosted
version: "2.0.5"
petitparser:
dependency: transitive
description:
name: petitparser
sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
url: "https://pub.dev"
source: hosted
version: "6.0.2"
pkcs7:
dependency: transitive
description:
name: pkcs7
sha256: "13c916f7577a4dbfc638af2266891a8f6bef06117ff78cff8982802419efbd37"
url: "https://pub.dev"
source: hosted
version: "1.0.3"
pointycastle:
dependency: transitive
description:
name: pointycastle
sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe"
url: "https://pub.dev"
source: hosted
version: "3.9.1"
qr:
dependency: transitive
description:
Expand Down

0 comments on commit ab05f47

Please sign in to comment.