Skip to content

Commit 349d2ed

Browse files
RulerOfCakestargos
authored andcommitted
crypto: add Date fields for validTo and validFrom
Added equivalent fields to `X509Certificate` in Date form. PR-URL: #54159 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Tim Perry <pimterry@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Richard Lau <rlau@redhat.com> Reviewed-By: Daeyeon Jeong <daeyeon.dev@gmail.com>
1 parent 23b628d commit 349d2ed

File tree

6 files changed

+197
-0
lines changed

6 files changed

+197
-0
lines changed

deps/ncrypto/ncrypto.cc

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,35 @@ int PasswordCallback(char* buf, int size, int rwflag, void* u) {
356356
return -1;
357357
}
358358

359+
// Algorithm: http://howardhinnant.github.io/date_algorithms.html
360+
constexpr int days_from_epoch(int y, unsigned m, unsigned d)
361+
{
362+
y -= m <= 2;
363+
const int era = (y >= 0 ? y : y - 399) / 400;
364+
const unsigned yoe = static_cast<unsigned>(y - era * 400); // [0, 399]
365+
const unsigned doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1; // [0, 365]
366+
const unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; // [0, 146096]
367+
return era * 146097 + static_cast<int>(doe) - 719468;
368+
}
369+
370+
// tm must be in UTC
371+
// using time_t causes problems on 32-bit systems and windows x64.
372+
int64_t PortableTimeGM(struct tm* t) {
373+
int year = t->tm_year + 1900;
374+
int month = t->tm_mon;
375+
if (month > 11) {
376+
year += month / 12;
377+
month %= 12;
378+
} else if (month < 0) {
379+
int years_diff = (11 - month) / 12;
380+
year -= years_diff;
381+
month += 12 * years_diff;
382+
}
383+
int days_since_epoch = days_from_epoch(year, month + 1, t->tm_mday);
384+
385+
return 60 * (60 * (24LL * static_cast<int64_t>(days_since_epoch) + t->tm_hour) + t->tm_min) + t->tm_sec;
386+
}
387+
359388
// ============================================================================
360389
// SPKAC
361390

@@ -826,6 +855,18 @@ BIOPointer X509View::getValidTo() const {
826855
return bio;
827856
}
828857

858+
int64_t X509View::getValidToTime() const {
859+
struct tm tp;
860+
ASN1_TIME_to_tm(X509_get0_notAfter(cert_), &tp);
861+
return PortableTimeGM(&tp);
862+
}
863+
864+
int64_t X509View::getValidFromTime() const {
865+
struct tm tp;
866+
ASN1_TIME_to_tm(X509_get0_notBefore(cert_), &tp);
867+
return PortableTimeGM(&tp);
868+
}
869+
829870
DataPointer X509View::getSerialNumber() const {
830871
ClearErrorOnReturn clearErrorOnReturn;
831872
if (cert_ == nullptr) return {};

deps/ncrypto/ncrypto.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,8 @@ class X509View final {
462462
BIOPointer getInfoAccess() const;
463463
BIOPointer getValidFrom() const;
464464
BIOPointer getValidTo() const;
465+
int64_t getValidFromTime() const;
466+
int64_t getValidToTime() const;
465467
DataPointer getSerialNumber() const;
466468
Result<EVPKeyPointer, int> getPublicKey() const;
467469
StackOfASN1 getKeyUsage() const;

doc/api/crypto.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2863,6 +2863,16 @@ added: v15.6.0
28632863

28642864
The date/time from which this certificate is valid.
28652865

2866+
### `x509.validFromDate`
2867+
2868+
<!-- YAML
2869+
added: REPLACEME
2870+
-->
2871+
2872+
* Type: {Date}
2873+
2874+
The date/time from which this certificate is valid, encapsulated in a `Date` object.
2875+
28662876
### `x509.validTo`
28672877

28682878
<!-- YAML
@@ -2873,6 +2883,16 @@ added: v15.6.0
28732883

28742884
The date/time until which this certificate is valid.
28752885

2886+
### `x509.validToDate`
2887+
2888+
<!-- YAML
2889+
added: REPLACEME
2890+
-->
2891+
2892+
* Type: {Date}
2893+
2894+
The date/time until which this certificate is valid, encapsulated in a `Date` object.
2895+
28762896
### `x509.verify(publicKey)`
28772897

28782898
<!-- YAML

lib/internal/crypto/x509.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ class X509Certificate {
135135
infoAccess: this.infoAccess,
136136
validFrom: this.validFrom,
137137
validTo: this.validTo,
138+
validFromDate: this.validFromDate,
139+
validToDate: this.validToDate,
138140
fingerprint: this.fingerprint,
139141
fingerprint256: this.fingerprint256,
140142
fingerprint512: this.fingerprint512,
@@ -220,6 +222,24 @@ class X509Certificate {
220222
return value;
221223
}
222224

225+
get validFromDate() {
226+
let value = this[kInternalState].get('validFromDate');
227+
if (value === undefined) {
228+
value = this[kHandle].validFromDate();
229+
this[kInternalState].set('validFromDate', value);
230+
}
231+
return value;
232+
}
233+
234+
get validToDate() {
235+
let value = this[kInternalState].get('validToDate');
236+
if (value === undefined) {
237+
value = this[kHandle].validToDate();
238+
this[kInternalState].set('validToDate', value);
239+
}
240+
return value;
241+
}
242+
223243
get fingerprint() {
224244
let value = this[kInternalState].get('fingerprint');
225245
if (value === undefined) {

src/crypto/crypto_x509.cc

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ using v8::ArrayBufferView;
2121
using v8::BackingStore;
2222
using v8::Boolean;
2323
using v8::Context;
24+
using v8::Date;
2425
using v8::EscapableHandleScope;
2526
using v8::Function;
2627
using v8::FunctionCallbackInfo;
@@ -238,6 +239,18 @@ MaybeLocal<Value> GetValidTo(Environment* env, const ncrypto::X509View& view) {
238239
return ret;
239240
}
240241

242+
MaybeLocal<Value> GetValidFromDate(Environment* env,
243+
const ncrypto::X509View& view) {
244+
int64_t validFromTime = view.getValidFromTime();
245+
return Date::New(env->context(), validFromTime * 1000.);
246+
}
247+
248+
MaybeLocal<Value> GetValidToDate(Environment* env,
249+
const ncrypto::X509View& view) {
250+
int64_t validToTime = view.getValidToTime();
251+
return Date::New(env->context(), validToTime * 1000.);
252+
}
253+
241254
MaybeLocal<Value> GetSerialNumber(Environment* env,
242255
const ncrypto::X509View& view) {
243256
if (auto serial = view.getSerialNumber()) {
@@ -349,6 +362,26 @@ void ValidTo(const FunctionCallbackInfo<Value>& args) {
349362
}
350363
}
351364

365+
void ValidFromDate(const FunctionCallbackInfo<Value>& args) {
366+
Environment* env = Environment::GetCurrent(args);
367+
X509Certificate* cert;
368+
ASSIGN_OR_RETURN_UNWRAP(&cert, args.This());
369+
Local<Value> ret;
370+
if (GetValidFromDate(env, cert->view()).ToLocal(&ret)) {
371+
args.GetReturnValue().Set(ret);
372+
}
373+
}
374+
375+
void ValidToDate(const FunctionCallbackInfo<Value>& args) {
376+
Environment* env = Environment::GetCurrent(args);
377+
X509Certificate* cert;
378+
ASSIGN_OR_RETURN_UNWRAP(&cert, args.This());
379+
Local<Value> ret;
380+
if (GetValidToDate(env, cert->view()).ToLocal(&ret)) {
381+
args.GetReturnValue().Set(ret);
382+
}
383+
}
384+
352385
void SerialNumber(const FunctionCallbackInfo<Value>& args) {
353386
Environment* env = Environment::GetCurrent(args);
354387
X509Certificate* cert;
@@ -834,6 +867,8 @@ Local<FunctionTemplate> X509Certificate::GetConstructorTemplate(
834867
SetProtoMethodNoSideEffect(isolate, tmpl, "issuer", Issuer);
835868
SetProtoMethodNoSideEffect(isolate, tmpl, "validTo", ValidTo);
836869
SetProtoMethodNoSideEffect(isolate, tmpl, "validFrom", ValidFrom);
870+
SetProtoMethodNoSideEffect(isolate, tmpl, "validToDate", ValidToDate);
871+
SetProtoMethodNoSideEffect(isolate, tmpl, "validFromDate", ValidFromDate);
837872
SetProtoMethodNoSideEffect(
838873
isolate, tmpl, "fingerprint", Fingerprint<EVP_sha1>);
839874
SetProtoMethodNoSideEffect(
@@ -1001,6 +1036,8 @@ void X509Certificate::RegisterExternalReferences(
10011036
registry->Register(Issuer);
10021037
registry->Register(ValidTo);
10031038
registry->Register(ValidFrom);
1039+
registry->Register(ValidToDate);
1040+
registry->Register(ValidFromDate);
10041041
registry->Register(Fingerprint<EVP_sha1>);
10051042
registry->Register(Fingerprint<EVP_sha256>);
10061043
registry->Register(Fingerprint<EVP_sha512>);

test/parallel/test-crypto-x509.js

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ const der = Buffer.from(
9696
assert.strictEqual(x509.infoAccess, infoAccessCheck);
9797
assert.strictEqual(x509.validFrom, 'Sep 3 21:40:37 2022 GMT');
9898
assert.strictEqual(x509.validTo, 'Jun 17 21:40:37 2296 GMT');
99+
assert.deepStrictEqual(x509.validFromDate, new Date('2022-09-03T21:40:37Z'));
100+
assert.deepStrictEqual(x509.validToDate, new Date('2296-06-17T21:40:37Z'));
99101
assert.strictEqual(
100102
x509.fingerprint,
101103
'8B:89:16:C4:99:87:D2:13:1A:64:94:36:38:A5:32:01:F0:95:3B:53');
@@ -359,3 +361,78 @@ UcXd/5qu2GhokrKU2cPttU+XAN2Om6a0
359361

360362
assert.strictEqual(cert.checkIssued(cert), false);
361363
}
364+
365+
{
366+
// Test date parsing of `validFromDate` and `validToDate` fields, according to RFC 5280.
367+
368+
// Validity dates up until the year 2049 are encoded as UTCTime.
369+
// The fomatting of UTCTime changes from the year ~1949 to 1950~.
370+
const certPemUTCTime = `-----BEGIN CERTIFICATE-----
371+
MIIE/TCCAuWgAwIBAgIUHbXPaFnjeBehMvdHkXZ+E3a78QswDQYJKoZIhvcNAQEL
372+
BQAwDTELMAkGA1UEBhMCS1IwIBgPMTk0OTEyMjUyMzU5NThaFw01MDAxMDEyMzU5
373+
NThaMA0xCzAJBgNVBAYTAktSMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
374+
AgEAtFfV2DB2dZFFaR1PPZMmyo0mSDAxGReoixxlhQTFZZymU71emWV/6gR8MxAE
375+
L5+uzpgBvOZWgEbELWeV/gzZGU/x1Cki0dSJ0B8Qwr5HvKX6oOZrJ8t+wn4SRceq
376+
r6MRPskDpTjnvelt+VURGmawtKKHll5fSqfjRWkQC8WQHdogXylRjd3oIh9p1D5P
377+
hphK/jKddxsRkLhJKQWqTjAy2v8hsJAxvpCPnlqMCXxjbQV41UTY8+kY3RPG3d6c
378+
yHBGM7dzM7XWVc79V9z/rjdRcxE2eBqrJT/yR3Cok8wWVVfQEgBfpolHUZxA8K4N
379+
tubTez9zsJy7xUG7udf91wXWVHMBHXg6m/u5nIW0fAXGMtnG/H6FMyyBDbJoUlqm
380+
VRTG71DzvBXpd/qx2P5LkU1JjWY3U8HSn6Q1DJzMIrbOmWpdlFYXxzLlXU2vG8Q3
381+
PmdAHDDYW3M2YBVCdKqOtsuL2dMDuqRWdi3iCCPSR2UCm4HzAVYSe2FP8SPcY3xs
382+
1NX+oDSpTxXruJYHGUp10/pXoqMrGT1IBgv2Dhsm3jcfRLSXkaBDJIKLO6dXmLBt
383+
rlxM0DphiKnP6lDjpv7EDMdwsakz0zib3JrTmSLSbwZXR4abITmtbYbTpY3XAq7c
384+
adO8YCMTCtb50ZbYEpGDAjOcWFHUlQQMsgZM2zc8ZHPY4EkCAwEAAaNTMFEwHQYD
385+
VR0OBBYEFExDmZyzdo8ccjX7iFIwU7JYMV+qMB8GA1UdIwQYMBaAFExDmZyzdo8c
386+
cjX7iFIwU7JYMV+qMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIB
387+
ADEF/JIH+Ku9NqrO47Q/CEn9qpIgmqX10d1joDjchPY3OHIIyt8Xpo845mPBTM7L
388+
dnMJSlkzJEk0ep9qAGGdKpBnLq8B/1mgCWQ81jwrwdYSsY+4xark+7+y0fij6qAt
389+
L4T6aA37nbV5q5/DMOwZucFwRTf9ZI1IjC+MaQmnV01vGCogqqfLQ9v26bVBRE1K
390+
UIixH0r3f/LWtuo0KaebZbb+oq6Zb8ljKJaUlt5OB8Zy5NrcP69r29QJUR57ukT6
391+
rt7fk5mOj2NBLMCErLHa7E6+GAUG94QEgdKzZ4yr2aduhMAfnOnK/HfuXO8TVa8/
392+
+oYENr47M8x139+yu92C8Be1MRk0VHteBaScUL+IaY3HgGbYR1lT0azvIyBN/DCN
393+
bYczI7JQGYVitLuaUYFw/RtK7Qg1957/ZmGeGa+86aTLXbqsGjI951D81EIzdqod
394+
1QW/Jn3yMNeVIzF9eYVEy2DIJjGgM2A8NWbqfWGUAUMRgyTxH1j42tnWG3eRnMsX
395+
UnQfpY8i3v6gYoNNgEZktrqgpmukTWgl08TlDtBCjXTBkcBt4dxDApeoy7XWKq+/
396+
qBY/+uIsG30BRgJhAwApjdnCs7l5xpwtqluXFwOxyTWNV5IfChO7QFqWPlSVIHML
397+
UidvpWWipVLZgK+oDks+bKTobcoXGW9oXobiIYqslXPy
398+
-----END CERTIFICATE-----`.trim();
399+
const c1 = new X509Certificate(certPemUTCTime);
400+
401+
assert.deepStrictEqual(c1.validFromDate, new Date('1949-12-25T23:59:58Z'));
402+
assert.deepStrictEqual(c1.validToDate, new Date('1950-01-01T23:59:58Z'));
403+
404+
// The GeneralizedTime format is used for dates in 2050 or later.
405+
const certPemGeneralizedTime = `-----BEGIN CERTIFICATE-----
406+
MIIE/TCCAuWgAwIBAgIUYHPUNd6S5xlNMjrWSaekgCBrbDQwDQYJKoZIhvcNAQEL
407+
BQAwDTELMAkGA1UEBhMCS1IwIBcNNDkxMjI2MDAwMDAxWhgPMjA1MDAxMDIwMDAw
408+
MDFaMA0xCzAJBgNVBAYTAktSMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
409+
AgEAlBPjQXHTzQWflvq6Lc01E0gVSSUQd5XnfK9K8TEN8ic/6iJVBWK8OwTmwh6u
410+
KdSO+DrTpoTA3Wo4T7oSL89xsyJN5JHiIT2VdZvgcXkv+ZL+rZ2INzYSSXbPQ8V+
411+
Md5A7tNWGJOvneD1Pb+AKrVXn6N1+xiKuv08U+d6ZCcv8P2cGUJCQr5BSg6eXPm2
412+
ZIoFhNLDaqleci0P/Bs7uMwKjVr2IP99bCMwTS2STxexEmYf4J3wgNXBOHxspLcS
413+
p7Yt3JgezvzRn5kijQi7ceS24q/fsGCCwB706mOKdYLCfEL1DhhEr27+XICw7zOF
414+
Q8tSe33IfSdxejEVV+lf/jGW5zFH5m+lDTJC0VAUCBG5E7q57yFaoQ44CQWtbMHZ
415+
+dtodKx4B0lzWXJs8xkGo0rl9/1CuY2iPX3lB6xxlX50ruj8stccMwarRzUvfkjw
416+
AhnbUs9X1ooFyVXmVYXWzR0gP1/q05Zob03khX1NipGbMf0RBI4WlItkiRsrEl9x
417+
08YPbrUyd7JnFkgG0O5TcmTzHr9cTJHg5BzclQA9/V0HuslSVOkKMMlKHky2zcqY
418+
dDBmWtfTrvowaB7hTGD6YK4R9JCDUy7oeeK4ZUxRNCnJY698HodE9lQu+F0cJpbY
419+
uZExFapE/AWA8ftlw2/fXoK0L3DhYsOVQkHd2YbrvzZEHVMCAwEAAaNTMFEwHQYD
420+
VR0OBBYEFNptaIzozylFlD0+JKULue+5gvfZMB8GA1UdIwQYMBaAFNptaIzozylF
421+
lD0+JKULue+5gvfZMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIB
422+
AFXP4SCP6VFMINaKE/rLJOaFiVVuS4qDfpGRjuBryNxey4ErBFsXcPi/D0LIsNkb
423+
c3qsex1cJadZdg3sqdkyliyYgzjJn1fSsiPT8GMiMnckw9awIwqtucGf+6SPdL6o
424+
9jp0xg6KsRHWjN0GetCz89hy9HwSiSwiLpTxVYOMLjQ+ey8KXPk0LNaXve/++hrr
425+
gN+cvcPKkspAE5SMTSKqHwVUD4MRJgdQqYDqB6demCq9Yl+kyQg9gVnuzkpKeNBT
426+
qNVeeA6gczCpYV4rUMqT0UVVPbPOcygwZP2o7tUyNk6fmYzyLpi5R+FYD/PoowFp
427+
LOrIaG426QaXhLr4U0i+HD/LhHZ4AWWt0OYAvbkk/xrhmagUcyeOxUrcYl6tA3NQ
428+
sjPV2FNGitX+zOyxfMxcjf0RpaBbyMsO6DSfQidDchFvPR9VFX4THs/0mP02IK27
429+
MpsZj8AG2/jjPz6ytnWBJGuLeIt2sWnluZyldX+V9QEEhEmrEweUolacKF5ESODG
430+
SHyZZVSUCK0bJfDfk5rXCQokWCIe+jHbW3CSWWmBRz6blZDeO/wI8nN4TWHDMCu6
431+
lawls1QdAwfP4CWIq4T7gsn/YqxMs74zDCXIF0tfuPmw5FMeCYVgnXQ7et8HBfeE
432+
CWwQO8JZjJqFtqtuzy2n+gLCvqePgG/gmSqHOPm2ZbLW
433+
-----END CERTIFICATE-----`.trim();
434+
const c2 = new X509Certificate(certPemGeneralizedTime);
435+
436+
assert.deepStrictEqual(c2.validFromDate, new Date('2049-12-26T00:00:01Z'));
437+
assert.deepStrictEqual(c2.validToDate, new Date('2050-01-02T00:00:01Z'));
438+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy