Skip to content

Commit

Permalink
Merge pull request #30 from ZeroPass/develop
Browse files Browse the repository at this point in the history
Refactor `parseDateYYMMDD` to handle future dates correctly & Fix sm tests
  • Loading branch information
smlu authored May 29, 2024
2 parents bdc451c + 606bf1c commit 34bcd03
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 27 deletions.
21 changes: 14 additions & 7 deletions lib/src/extension/string_apis.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,34 @@ extension StringDecodeApis on String {
}

extension StringYYMMDDateApi on String {
DateTime parseDateYYMMDD() {
DateTime parseDateYYMMDD({bool futureDate = false}) {
if (length < 6) {
throw FormatException("invalid length of compact date string");
throw FormatException("Invalid length of compact date string");
}

int y = int.parse(substring(0, 2)) + 2000;
int m = int.parse(substring(2, 4));
int d = int.parse(substring(4, 6));

// Sub 100 years from parsed year if greater than 10 years and 5 months from now.
final now = DateTime.now();
final tenYearsFromNow = now.year + 10;
if (y > tenYearsFromNow || (y == tenYearsFromNow && now.month + 5 < m)) {
int maxYear = now.year;
int maxMonth = now.month;
if (futureDate) {
maxYear += 20; // cut off year 20 years from now
maxMonth += 5;
}

// If parsed year is greater than max wind back for 100 years
if (y > maxYear || ( y == maxYear && maxMonth < m)) {
y -= 100;
}

return DateTime(y, m, d);
}

DateTime parseDate() {
DateTime parseDate({bool futureDate = false}) {
if (length == 6) {
return this.parseDateYYMMDD();
return this.parseDateYYMMDD(futureDate: futureDate);
} else {
return DateTime.parse(this);
}
Expand Down
2 changes: 1 addition & 1 deletion lib/src/lds/df1/efdg12.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class EfDG12 extends DataGroup {

DateTime? get dateOfIssue => _dateOfIssue;
String? get issuingAuthority => _issuingAuthority;


EfDG12.fromBytes(Uint8List data) : super.fromBytes(data);

Expand Down
16 changes: 8 additions & 8 deletions lib/src/lds/mrz.dart
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,13 @@ class MRZ {
_docNum = _read(istream, 9);
final cdDocNum = _readWithPad(istream, 1);
_optData = _read(istream, 15);
dateOfBirth = _readDate(istream);
dateOfBirth = _readDate(istream, futureDate: false);

_assertCheckDigit(dateOfBirth.formatYYMMDD(), _readCD(istream),
"Data of Birth check digit mismatch");

gender = _read(istream, 1);
dateOfExpiry = _readDate(istream);
dateOfExpiry = _readDate(istream, futureDate: true);

_assertCheckDigit(dateOfExpiry.formatYYMMDD(), _readCD(istream),
"Data of Expiry check digit mismatch");
Expand Down Expand Up @@ -151,12 +151,12 @@ class MRZ {
final cdDocNum = _readWithPad(istream, 1);

nationality = _read(istream, 3);
dateOfBirth = _readDate(istream);
dateOfBirth = _readDate(istream, futureDate: false);
_assertCheckDigit(dateOfBirth.formatYYMMDD(), _readCD(istream),
"Data of Birth check digit mismatch");

gender = _read(istream, 1);
dateOfExpiry = _readDate(istream);
dateOfExpiry = _readDate(istream, futureDate: true);
_assertCheckDigit(dateOfExpiry.formatYYMMDD(), _readCD(istream),
"Data of Expiry check digit mismatch");

Expand Down Expand Up @@ -185,12 +185,12 @@ class MRZ {
_docNum, _readCD(istream), "Document Number check digit mismatch");

nationality = _read(istream, 3);
dateOfBirth = _readDate(istream);
dateOfBirth = _readDate(istream, futureDate: false);
_assertCheckDigit(dateOfBirth.formatYYMMDD(), _readCD(istream),
"Data of Birth check digit mismatch");

gender = _read(istream, 1);
dateOfExpiry = _readDate(istream);
dateOfExpiry = _readDate(istream, futureDate: true);
_assertCheckDigit(dateOfExpiry.formatYYMMDD(), _readCD(istream),
"Data of Expiry check digit mismatch");

Expand Down Expand Up @@ -240,8 +240,8 @@ class MRZ {
return _readWithPad(istream, maxLength).replaceAll(RegExp(r'<+$'), '');
}

static DateTime _readDate(InputStream istream) {
return _read(istream, 6).parseDateYYMMDD();
static DateTime _readDate(InputStream istream, {bool futureDate = false}) {
return _read(istream, 6).parseDateYYMMDD(futureDate: futureDate);
}

static int _readCD(InputStream istream) {
Expand Down
4 changes: 2 additions & 2 deletions lib/src/proto/dba_key.dart
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,10 @@ class DBAKey extends AccessKey {
String get mrtdNumber => _mrtdNum;

/// Returns passport owner's date of birth used for calculating key seed.
DateTime get dateOfBirth => _dob.parseDateYYMMDD();
DateTime get dateOfBirth => _dob.parseDateYYMMDD(futureDate: false);

/// Returns passport date of expiry used for calculating key seed.
DateTime get dateOfExpiry => _doe.parseDateYYMMDD();
DateTime get dateOfExpiry => _doe.parseDateYYMMDD(futureDate: true);

/// Very sensitive data. Do not use in production!
@override
Expand Down
2 changes: 1 addition & 1 deletion lib/src/proto/mrtd_sm.dart
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class MrtdSM extends SecureMessaging {
return rapdu;
}

//increment SSC should be made before decrypting data
// Increment SSC should be made before decrypting data
_ssc.increment();

_log.debug("Unprotecting RAPDU: $rapdu");
Expand Down
38 changes: 31 additions & 7 deletions test/extensions_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,19 +80,43 @@ void main() {
expect( '121212'.parseDateYYMMDD() , DateTime(2012, 12, 12) );
expect( '201212'.parseDateYYMMDD() , DateTime(2020, 12, 12) );

expect( '891109'.parseDateYYMMDD(futureDate: false) , DateTime(1989, 11, 9) );
expect( '760501'.parseDateYYMMDD(futureDate: false) , DateTime(1976, 05, 01) );
expect( '000215'.parseDateYYMMDD(futureDate: false) , DateTime(2000, 02, 15) );
expect( '111111'.parseDateYYMMDD(futureDate: false) , DateTime(2011, 11, 11) );
expect( '121212'.parseDateYYMMDD(futureDate: false) , DateTime(2012, 12, 12) );
expect( '201212'.parseDateYYMMDD(futureDate: false) , DateTime(2020, 12, 12) );

expect( '891109'.parseDateYYMMDD(futureDate: true) , DateTime(1989, 11, 9) );
expect( '760501'.parseDateYYMMDD(futureDate: true) , DateTime(1976, 05, 01) );
expect( '000215'.parseDateYYMMDD(futureDate: true) , DateTime(2000, 02, 15) );
expect( '111111'.parseDateYYMMDD(futureDate: true) , DateTime(2011, 11, 11) );
expect( '121212'.parseDateYYMMDD(futureDate: true) , DateTime(2012, 12, 12) );
expect( '201212'.parseDateYYMMDD(futureDate: true) , DateTime(2020, 12, 12) );

final now = DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day);
expect( now.formatYYMMDD().parseDateYYMMDD() , now );
expect( now.formatYYMMDD().parseDateYYMMDD(), now );
expect( now.formatYYMMDD().parseDateYYMMDD(futureDate: false), now );
expect( now.formatYYMMDD().parseDateYYMMDD(futureDate: true) , now );

// 1 month future date
final nextMonth = DateTime(now.year, now.month + 1, now.day);
expect( nextMonth.formatYYMMDD().parseDateYYMMDD(), nextMonth );
expect( nextMonth.formatYYMMDD().parseDateYYMMDD() != nextMonth, true ); // by default future date is not expected
expect( nextMonth.formatYYMMDD().parseDateYYMMDD(futureDate: false) != nextMonth, true );
expect( nextMonth.formatYYMMDD().parseDateYYMMDD(futureDate: true) == nextMonth, true );

// 10 years future date
final tenFromNow = DateTime(now.year + 10, now.month, now.day);
expect( tenFromNow.formatYYMMDD().parseDateYYMMDD(), tenFromNow );
expect( tenFromNow.formatYYMMDD().parseDateYYMMDD() != tenFromNow, true ); // by default future date is not expected
expect( tenFromNow.formatYYMMDD().parseDateYYMMDD(futureDate: false) != tenFromNow, true );
expect( tenFromNow.formatYYMMDD().parseDateYYMMDD(futureDate: true) == tenFromNow, true );

// 10 years and 6 months from now should wind date back for a century.
// 10 years and 6 months future date
final tenAnd6MonthsFromNow = DateTime(now.year + 10, now.month + 6, now.day);
final ninetyYearsAgo = DateTime(now.year - 90, now.month + 6, now.day);
expect( tenAnd6MonthsFromNow.formatYYMMDD().parseDateYYMMDD(), ninetyYearsAgo );
expect( tenAnd6MonthsFromNow.formatYYMMDD().parseDateYYMMDD() , ninetyYearsAgo );
expect( tenAnd6MonthsFromNow.formatYYMMDD().parseDateYYMMDD(futureDate: false), ninetyYearsAgo );
expect( tenAnd6MonthsFromNow.formatYYMMDD().parseDateYYMMDD(futureDate: true) , tenAnd6MonthsFromNow );
});
});

Expand Down Expand Up @@ -152,7 +176,7 @@ void main() {
Logger.root.level = Level.FINEST;
Logger.root.trace(traceMsg);
expect( logMsg, traceMsg );
expect( level , Level.FINEST );
expect( level , Level.FINEST );
});

test('Sensitive data log test', () {
Expand Down Expand Up @@ -404,7 +428,7 @@ void main() {
dlog.sdDebug(debugMsg);
expect( dlogMsg, debugMsg );
expect( dlevel , Level.FINE );

// Verbose test
Logger.root.logSensitiveData = false;
dlog.logSensitiveData = false;
Expand Down
4 changes: 3 additions & 1 deletion test/sm_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ void testProtecting(final CommandAPDU cmd, final SMCipher cipher, final SSC ssc,
final M = sm.generateM(cmd: pcmd, dataDO: dataDO, do97: do97);
expect( M, tvM );

ssc.increment(); // ssc has to be incremented prior to generating N
final N = sm.generateN(M: M);
expect( N, tvN );

Expand Down Expand Up @@ -64,6 +65,7 @@ void testUprotecting(final ResponseAPDU rapdu, final SMCipher cipher, final SSC
final do8E = sm.parseDO8EFromRAPDU(rapdu, do8EStart);
expect( TLV.encode(do8E.tag.value, do8E.value) , tvDO8E );

ssc.increment(); // ssc has to be incremented prior to generating K
final K = sm.generateK(data: rapdu.data!.sublist(0, do8EStart));
expect( K , tvK );

Expand All @@ -81,7 +83,7 @@ void testUprotecting(final ResponseAPDU rapdu, final SMCipher cipher, final SSC
void main() {
test('Testing MRTD Secure Messaging', () {

// Test vectors taken from Appendix D.4 to the Part 11 of ICAO 7816 p11 doc
// Test vectors taken from Appendix D.4 to the Part 11 of ICAO 9303 p11 doc
final tvKSEnc = "979EC13B1CBFE9DCD01AB0FED307EAE5".parseHex();
final tvKSMAC = "F1CB1F1FB5ADF208806B89DC579DC1F8".parseHex();
final smCipher = DES_SMCipher(tvKSEnc, tvKSMAC);
Expand Down

0 comments on commit 34bcd03

Please sign in to comment.