<p>osmith <strong>merged</strong> this change.</p><p><a href="https://gerrit.osmocom.org/12524">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Harald Welte: Looks good to me, approved
  Jenkins Builder: Verified

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">gsm23003: add osmo_imei_str_valid()<br><br>Verify 14 digit and 15 digit IMEI strings. OsmoHLR will use the 14<br>digit version to check IMEIs before writing them to the DB.<br><br>Place the Luhn checksum code in a dedicated osmo_luhn() function, so<br>it can be used elsewhere.<br><br>Related: OS#2541<br>Change-Id: Id2d2a3a93b033bafc74c62e15297034bf4aafe61<br>---<br>M include/osmocom/core/utils.h<br>M include/osmocom/gsm/gsm23003.h<br>M src/gsm/gsm23003.c<br>M src/gsm/libosmogsm.map<br>M src/utils.c<br>M tests/gsm23003/gsm23003_test.c<br>M tests/gsm23003/gsm23003_test.ok<br>7 files changed, 107 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/osmocom/core/utils.h b/include/osmocom/core/utils.h</span><br><span>index 04f2fb4..987080e 100644</span><br><span>--- a/include/osmocom/core/utils.h</span><br><span>+++ b/include/osmocom/core/utils.h</span><br><span>@@ -140,4 +140,6 @@</span><br><span> </span><br><span> uint32_t osmo_isqrt32(uint32_t x);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+const char osmo_luhn(const char* in, int in_len);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! @} */</span><br><span>diff --git a/include/osmocom/gsm/gsm23003.h b/include/osmocom/gsm/gsm23003.h</span><br><span>index 2f380ae..b5c8dcd 100644</span><br><span>--- a/include/osmocom/gsm/gsm23003.h</span><br><span>+++ b/include/osmocom/gsm/gsm23003.h</span><br><span>@@ -93,6 +93,7 @@</span><br><span> </span><br><span> bool osmo_imsi_str_valid(const char *imsi);</span><br><span> bool osmo_msisdn_str_valid(const char *msisdn);</span><br><span style="color: hsl(120, 100%, 40%);">+bool osmo_imei_str_valid(const char *imei, bool with_15th_digit);</span><br><span> </span><br><span> const char *osmo_mcc_name(uint16_t mcc);</span><br><span> const char *osmo_mnc_name(uint16_t mnc, bool mnc_3_digits);</span><br><span>diff --git a/src/gsm/gsm23003.c b/src/gsm/gsm23003.c</span><br><span>index 4fdad48..1d9cefe 100644</span><br><span>--- a/src/gsm/gsm23003.c</span><br><span>+++ b/src/gsm/gsm23003.c</span><br><span>@@ -31,6 +31,7 @@</span><br><span> </span><br><span> #include <osmocom/gsm/gsm23003.h></span><br><span> #include <osmocom/gsm/protocol/gsm_23_003.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/utils.h></span><br><span> </span><br><span> static bool is_n_digits(const char *str, int min_digits, int max_digits)</span><br><span> {</span><br><span>@@ -71,6 +72,23 @@</span><br><span>       return is_n_digits(msisdn, 1, 15);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*! Determine whether the given IMEI is valid according to 3GPP TS 23.003,</span><br><span style="color: hsl(120, 100%, 40%);">+ * Section 6.2.1. It consists of 14 digits, the 15th check digit is not</span><br><span style="color: hsl(120, 100%, 40%);">+ * intended for digital transmission.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param imei  IMEI digits in ASCII string representation.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param with_15th_digit  when true, expect the 15th digit to be present and</span><br><span style="color: hsl(120, 100%, 40%);">+ *        verify it.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns true when the IMEI is valid, false for invalid characters or number</span><br><span style="color: hsl(120, 100%, 40%);">+ *          of digits.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+bool osmo_imei_str_valid(const char *imei, bool with_15th_digit)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  if (with_15th_digit)</span><br><span style="color: hsl(120, 100%, 40%);">+          return is_n_digits(imei, 15, 15) && osmo_luhn(imei, 14) == imei[14];</span><br><span style="color: hsl(120, 100%, 40%);">+  else</span><br><span style="color: hsl(120, 100%, 40%);">+          return is_n_digits(imei, 14, 14);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! Return MCC string as standardized 3-digit with leading zeros.</span><br><span>  * \param[in] mcc  MCC value.</span><br><span>  * \returns string in static buffer.</span><br><span>diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map</span><br><span>index a903787..331c3f0 100644</span><br><span>--- a/src/gsm/libosmogsm.map</span><br><span>+++ b/src/gsm/libosmogsm.map</span><br><span>@@ -551,6 +551,7 @@</span><br><span> </span><br><span> osmo_imsi_str_valid;</span><br><span> osmo_msisdn_str_valid;</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_imei_str_valid;</span><br><span> </span><br><span> osmo_mncc_stringify;</span><br><span> osmo_mncc_names;</span><br><span>diff --git a/src/utils.c b/src/utils.c</span><br><span>index 35d70ac..d1da4fa 100644</span><br><span>--- a/src/utils.c</span><br><span>+++ b/src/utils.c</span><br><span>@@ -765,4 +765,35 @@</span><br><span>  return buf;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*! Calculate the Luhn checksum (as used for IMEIs).</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] in  Input digits in ASCII string representation.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] in_len  Count of digits to use for the input (14 for IMEI).</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns checksum char (e.g. '3'); negative on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+const char osmo_luhn(const char* in, int in_len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      int i, sum = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* All input must be numbers */</span><br><span style="color: hsl(120, 100%, 40%);">+       for (i = 0; i < in_len; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+             if (!isdigit(in[i]))</span><br><span style="color: hsl(120, 100%, 40%);">+                  return -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Double every second digit and add it to sum */</span><br><span style="color: hsl(120, 100%, 40%);">+     for (i = in_len - 1; i >= 0; i -= 2) {</span><br><span style="color: hsl(120, 100%, 40%);">+             int dbl = (in[i] - '0') * 2;</span><br><span style="color: hsl(120, 100%, 40%);">+          if (dbl > 9)</span><br><span style="color: hsl(120, 100%, 40%);">+                       dbl -= 9;</span><br><span style="color: hsl(120, 100%, 40%);">+             sum += dbl;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Add other digits to sum */</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = in_len - 2; i >= 0; i -= 2)</span><br><span style="color: hsl(120, 100%, 40%);">+               sum += in[i] - '0';</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Final checksum */</span><br><span style="color: hsl(120, 100%, 40%);">+  return (sum * 9) % 10 + '0';</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! @} */</span><br><span>diff --git a/tests/gsm23003/gsm23003_test.c b/tests/gsm23003/gsm23003_test.c</span><br><span>index 79965cf..eac5a11 100644</span><br><span>--- a/tests/gsm23003/gsm23003_test.c</span><br><span>+++ b/tests/gsm23003/gsm23003_test.c</span><br><span>@@ -118,6 +118,47 @@</span><br><span>   return pass;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static struct {</span><br><span style="color: hsl(120, 100%, 40%);">+        bool with_15th_digit;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *imei;</span><br><span style="color: hsl(120, 100%, 40%);">+     bool expect_ok;</span><br><span style="color: hsl(120, 100%, 40%);">+} test_imeis[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+   /* without 15th digit */</span><br><span style="color: hsl(120, 100%, 40%);">+      {false, "12345678901234", true},</span><br><span style="color: hsl(120, 100%, 40%);">+    {false, "1234567890123", false},</span><br><span style="color: hsl(120, 100%, 40%);">+    {false, "123456789012345", false},</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* with 15th digit: valid */</span><br><span style="color: hsl(120, 100%, 40%);">+  {true, "357613004448485", true},</span><br><span style="color: hsl(120, 100%, 40%);">+    {true, "357805023984447", true},</span><br><span style="color: hsl(120, 100%, 40%);">+    {true, "352936001349777", true},</span><br><span style="color: hsl(120, 100%, 40%);">+    {true, "357663017768551", true},</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* with 15th digit: invalid */</span><br><span style="color: hsl(120, 100%, 40%);">+        {true, "357613004448480", false},</span><br><span style="color: hsl(120, 100%, 40%);">+   {true, "357613004448405", false},</span><br><span style="color: hsl(120, 100%, 40%);">+   {true, "357613004448085", false},</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ { NULL, false, false },</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+bool test_valid_imei()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      int i;</span><br><span style="color: hsl(120, 100%, 40%);">+        bool pass = true;</span><br><span style="color: hsl(120, 100%, 40%);">+     bool ok = true;</span><br><span style="color: hsl(120, 100%, 40%);">+       printf("----- %s\n", __func__);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   for (i = 0; i < ARRAY_SIZE(test_imeis); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+             ok = osmo_imei_str_valid(test_imeis[i].imei, test_imeis[i].with_15th_digit);</span><br><span style="color: hsl(120, 100%, 40%);">+          pass = pass && (ok == test_imeis[i].expect_ok);</span><br><span style="color: hsl(120, 100%, 40%);">+               printf("%2d: expect=%s result=%s imei='%s' with_15th_digit=%s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                  i, BOOL_STR(test_imeis[i].expect_ok), BOOL_STR(ok),</span><br><span style="color: hsl(120, 100%, 40%);">+                   test_imeis[i].imei, test_imeis[i].with_15th_digit ? "true" : "false");</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     return pass;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> struct test_mnc_from_str_result {</span><br><span>       int rc;</span><br><span>      uint16_t mnc;</span><br><span>@@ -248,6 +289,7 @@</span><br><span> </span><br><span>      pass = pass && test_valid_imsi();</span><br><span>    pass = pass && test_valid_msisdn();</span><br><span style="color: hsl(120, 100%, 40%);">+   pass = pass && test_valid_imei();</span><br><span>    pass = pass && test_mnc_from_str();</span><br><span>  pass = pass && test_gummei_name();</span><br><span>   pass = pass && test_domain_gen();</span><br><span>diff --git a/tests/gsm23003/gsm23003_test.ok b/tests/gsm23003/gsm23003_test.ok</span><br><span>index c64f515..2f7c37f 100644</span><br><span>--- a/tests/gsm23003/gsm23003_test.ok</span><br><span>+++ b/tests/gsm23003/gsm23003_test.ok</span><br><span>@@ -42,6 +42,18 @@</span><br><span> 17: expect=false result=false msisdn='123456     123456'</span><br><span> 18: expect=false result=false msisdn='123456 123456'</span><br><span> 19: expect=false result=false msisdn='(null)'</span><br><span style="color: hsl(120, 100%, 40%);">+----- test_valid_imei</span><br><span style="color: hsl(120, 100%, 40%);">+ 0: expect=true result=true imei='12345678901234' with_15th_digit=false</span><br><span style="color: hsl(120, 100%, 40%);">+ 1: expect=false result=false imei='1234567890123' with_15th_digit=false</span><br><span style="color: hsl(120, 100%, 40%);">+ 2: expect=false result=false imei='123456789012345' with_15th_digit=false</span><br><span style="color: hsl(120, 100%, 40%);">+ 3: expect=true result=true imei='357613004448485' with_15th_digit=true</span><br><span style="color: hsl(120, 100%, 40%);">+ 4: expect=true result=true imei='357805023984447' with_15th_digit=true</span><br><span style="color: hsl(120, 100%, 40%);">+ 5: expect=true result=true imei='352936001349777' with_15th_digit=true</span><br><span style="color: hsl(120, 100%, 40%);">+ 6: expect=true result=true imei='357663017768551' with_15th_digit=true</span><br><span style="color: hsl(120, 100%, 40%);">+ 7: expect=false result=false imei='357613004448480' with_15th_digit=true</span><br><span style="color: hsl(120, 100%, 40%);">+ 8: expect=false result=false imei='357613004448405' with_15th_digit=true</span><br><span style="color: hsl(120, 100%, 40%);">+ 9: expect=false result=false imei='357613004448085' with_15th_digit=true</span><br><span style="color: hsl(120, 100%, 40%);">+10: expect=false result=false imei='(null)' with_15th_digit=false</span><br><span> ----- test_mnc_from_str</span><br><span>  0: "0" rc=0 mnc=0 mnc_3_digits=0 pass</span><br><span>  1: "00" rc=0 mnc=0 mnc_3_digits=0 pass</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/12524">change 12524</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/12524"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: libosmocore </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: merged </div>
<div style="display:none"> Gerrit-Change-Id: Id2d2a3a93b033bafc74c62e15297034bf4aafe61 </div>
<div style="display:none"> Gerrit-Change-Number: 12524 </div>
<div style="display:none"> Gerrit-PatchSet: 5 </div>
<div style="display:none"> Gerrit-Owner: osmith <osmith@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Harald Welte <laforge@gnumonks.org> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder (1000002) </div>
<div style="display:none"> Gerrit-Reviewer: Max <msuraev@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Neels Hofmeyr <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Pau Espin Pedrol <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Vadim Yanitskiy <axilirator@gmail.com> </div>
<div style="display:none"> Gerrit-Reviewer: osmith <osmith@sysmocom.de> </div>