From 3d374fe0f14bfbdbdf6fd09b37af0fcffa5ce58d Mon Sep 17 00:00:00 2001 From: David Koblas Date: Wed, 11 Nov 2026 08:06:59 -0500 Subject: [PATCH] fix(kr/rrn): skip the check digit for alien registration numbers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Numbers with century/gender digit 5-8 are alien registration numbers (외국인등록번호). Their check digit formula isn't published: the Enforcement Decree of the Immigration Act (Article 40-3) leaves the scheme to the Minister of Justice. python-stdnum applies the RRN formula, other validators add 2 to it, and neither cites a source, so valid numbers could be rejected. Check only the length, digits and birth date, as for numbers issued since 2020. --- src/kr/rrn.spec.ts | 20 ++++++++++++++++++++ src/kr/rrn.ts | 26 ++++++++++++++++++++++---- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/kr/rrn.spec.ts b/src/kr/rrn.spec.ts index e5cda65d..76b877f7 100644 --- a/src/kr/rrn.spec.ts +++ b/src/kr/rrn.spec.ts @@ -65,4 +65,24 @@ describe('kr/rrn', () => { expect(result.error).toBeInstanceOf(InvalidChecksum); }); + + // Alien registration numbers (7th digit 5-8): no published check digit + // formula, so only the length, digits and birth date are checked + test.each([ + '850126-6401473', // passes the RRN formula + '850126-6401475', // passes the "RRN + 2" formula used by some validators + '850126-5991470', // office code above 96 + '050126-7401470', + '050126-8401471', + ])('validate:%s', value => { + const result = validate(value); + + expect(result.isValid && result.isIndividual).toEqual(true); + }); + + test.each(['851326-6401473', '991231-7401470'])('validate:%s', value => { + const result = validate(value); + + expect(result.error).toBeInstanceOf(InvalidComponent); + }); }); diff --git a/src/kr/rrn.ts b/src/kr/rrn.ts index 6be9bd97..b4cc337b 100644 --- a/src/kr/rrn.ts +++ b/src/kr/rrn.ts @@ -3,8 +3,8 @@ * * The RRN (resident registration number, 주민등록번호) is a 13-digit number * issued to all residents of the Republic of Korea. Foreigners residing in the - * Republic of Korea receive an alien registration number (ARN) which follows - * the same encoding pattern. + * Republic of Korea receive an alien registration number (ARN) which has the + * same layout, but no published check digit (see below). * * The first six digits code the date of birth. The seventh digit encodes the * century and gender. The next four digits encode the place of birth for @@ -21,11 +21,22 @@ * Birth dates in the future are rejected; python-stdnum only checks that the * date exists. See https://github.com/koblas/stdnum-js/issues/91. * + * For an alien registration number (century/gender digit 5 to 8) only the + * length, digits and birth date are checked. The Enforcement Decree of the + * Immigration Act (Article 40-3) defines it as 13 digits showing the date of + * birth, sex and registering office, and leaves the rest of the scheme to the + * Minister of Justice, who hasn't published it. The check digit formula + * isn't public: python-stdnum applies the RRN formula, other validators add + * 2 to it, and neither cites a source. This differs from python-stdnum, see + * https://github.com/koblas/stdnum-js/issues/91. + * * Source: * http://www.law.go.kr/lsSc.do?tabMenuId=tab18&p1=&subMenu=1&nwYn=1§ion=&tabNo=&query=개인정보+보호법 * https://en.wikipedia.org/wiki/Resident_registration_number * https://techscience.org/a/2015092901/ * https://www.fnnews.com/news/202006291754239525 + * https://www.law.go.kr/LSW//lumLsLinkPop.do?lspttninfSeq=82633&chrClsCd=010202 + * https://overseas.mofa.go.kr/hn-ko/brd/m_6364/view.do?seq=918639 * * PERSON */ @@ -52,6 +63,9 @@ const centuryPrefix: Record = { 9: 18, }; +// Century/gender digits of alien registration numbers +const ARN_CENTURY = new Set(['5', '6', '7', '8']); + const impl: Validator = { name: 'South Korean Resident Registration Number', localName: '주민등록번호', @@ -92,8 +106,12 @@ const impl: Validator = { return { isValid: false, error: new exceptions.InvalidComponent() }; } - // Numbers issued since October 2020 end in six random digits - if (parseInt(birthDate.slice(0, 4), 10) >= 2020) { + // Numbers issued since October 2020 end in six random digits, and the + // check digit of alien registration numbers isn't published + if ( + parseInt(birthDate.slice(0, 4), 10) >= 2020 || + ARN_CENTURY.has(century) + ) { return { isValid: true, compact: value,