# POST /originality/read-sig/verify

Contrôler la signature d'originalité NXP Read_Sig d'une puce, à partir de son numéro de série et de la signature lue sur la puce. Point d'entrée public, sans clef d'API.

Source : https://docs.sealtrust.io/reference/post-originality-read-sig-verify/

---

Vous envoyez le numéro de série d'une puce et la signature d'originalité NXP
Read_Sig lue dessus, et le service vous renvoie un verdict.

## Autorisation

Aucune, point d'entrée public. Ce point d'entrée n'attend ni clef d'API, ni
cookie de session, ni en-tête `Authorization`.

Appelez-le depuis votre serveur. Un appel émis par une page de navigateur passe
par deux contrôles supplémentaires, l'origine de la requête et le jeton
anti-falsification, qui renvoient 403 quand ils ne sont pas satisfaits.

Le contrôle ne touche à aucun de vos enregistrements. Aucun scan n'est
enregistré, aucune statistique n'est alimentée, aucune notification n'est
déclenchée.

## Plafond d'appels

Aucun plafond propre à ce point d'entrée. Il relève du compteur général de
l'API, compté par adresse réseau appelante sur une fenêtre de 60 secondes.

Ce compteur général est commun à tous les points d'entrée qui n'ont pas de
plafond dédié. Vos appels ici entament donc le même budget que vos appels vers
ces autres adresses. Le préfixe `/v1` ne crée pas un second budget.

Chaque réponse porte trois en-têtes.

| En-tête | Contenu |
| --- | --- |
| `X-RateLimit-Limit` | le plafond que le compteur annonce pour la fenêtre |
| `X-RateLimit-Remaining` | ce qu'il vous reste dans la fenêtre en cours, plancher à `0` |
| `X-RateLimit-Reset` | l'horodatage de fin de la fenêtre, en secondes depuis le 1er janvier 1970 |

Lisez `X-RateLimit-Remaining` et ralentissez avant d'atteindre zéro. La valeur
de `X-RateLimit-Limit` peut changer sans préavis, donc n'inscrivez aucun nombre
en dur dans votre code.

> [!ATTENTION] Prévoyez le code 429 dans votre client
> Un appel émis au-delà du budget peut recevoir un code 429. Traitez ce cas dès
> votre première intégration : attendez la durée en secondes indiquée par
> l'en-tête `Retry-After` de la réponse, puis rejouez l'appel. Un client qui ne
> gère pas le 429 s'arrête net le jour où il dépasse le budget.

Ce point d'entrée ne consomme aucun quota de votre offre. Il ne demande ni
compte, ni clef d'API, et ne consulte aucune offre.

## Paramètres de chemin et de requête

Aucun. Ce point d'entrée n'a ni paramètre de chemin ni paramètre de requête.
Tout passe par le corps de la requête.

## Corps de la requête

Envoyez un objet JSON avec l'en-tête `Content-Type: application/json`.

| Nom | Type | Obligatoire | Description |
| --- | --- | --- | --- |
| `uid_hex` | `string` | oui | Le numéro de série de la puce, en hexadécimal. Il doit faire 7 ou 10 octets, soit 14 ou 20 caractères hexadécimaux. |
| `signature_hex` | `string` | oui | La signature lue sur la puce, en hexadécimal, sous la forme brute `r` suivi de `s`. Sa longueur attendue dépend de la courbe : 56 octets pour P-224, 64 octets pour P-256, soit 112 ou 128 caractères hexadécimaux. |
| `public_key_hex` | `string` | non | La clef publique avec laquelle contrôler la signature, en hexadécimal, au format SEC1 non compressé : `04` suivi de la coordonnée X puis de la coordonnée Y. Elle doit faire 57 octets pour P-224 ou 65 octets pour P-256, et décrire un point réel de la courbe. Champ absent ou chaîne vide : le contrôle se fait avec la clef publique d'originalité NXP de référence retenue par le service. Le champ `public_key_used` de la réponse vous dit laquelle a servi. |

Aucun autre champ n'est accepté. Un champ inconnu fait échouer la requête en
422, et aucun champ n'est ignoré en silence.

### Ce que le service nettoie avant de lire

Sur les trois valeurs, le service retire les espaces de début et de fin, ramène
les majuscules en minuscules, supprime les deux-points et les espaces internes,
puis retire un préfixe `0x` s'il en trouve un.

Les deux-points et les espaces sont donc les deux seuls séparateurs tolérés.
Tout autre séparateur, tiret ou point, doit être supprimé par vos soins avant
l'envoi : un numéro de série écrit `04-1a-2b-3c-4d-5e-6f` est refusé en 422.

### C'est la clef publique qui fixe la courbe

Le service déduit la courbe de la longueur de la clef publique de contrôle : 57
octets donnent P-224, 65 octets donnent P-256. Vous n'avez aucun paramètre de
courbe à envoyer, et la réponse vous indique la courbe retenue.

La longueur de signature attendue en découle. Une signature de 64 octets
contrôlée avec une clef P-224 est refusée en 422, avant tout calcul.

## Requête d'exemple

Adresse complète :

```http
POST https://api.sealtrust.io/v1/originality/read-sig/verify
```

Le même point d'entrée répond aussi sans le préfixe `/v1`, à
`https://api.sealtrust.io/originality/read-sig/verify`. Les deux adresses
appellent le même code. Utilisez la forme `/v1` pour une nouvelle intégration.

:::onglets
```bash title="curl"
curl -i -X POST https://api.sealtrust.io/v1/originality/read-sig/verify \
  -H "Content-Type: application/json" \
  -d '{
    "uid_hex": "04000000000000",
    "signature_hex": "0000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000001",
    "public_key_hex": "04b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34"
  }'
```
```typescript
const response = await fetch(
  "https://api.sealtrust.io/v1/originality/read-sig/verify",
  {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      uid_hex: "04000000000000",
      signature_hex:
        "0000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000001",
      public_key_hex:
        "04b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34",
    }),
  },
);

console.log(response.status);
console.log(response.headers.get("X-RateLimit-Remaining"));
console.log(await response.json());
```
```python
import requests

response = requests.post(
    "https://api.sealtrust.io/v1/originality/read-sig/verify",
    json={
        "uid_hex": "04000000000000",
        "signature_hex": "0000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000001",
        "public_key_hex": "04b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34",
    },
    timeout=30,
)

print(response.status_code)
print(response.headers["X-RateLimit-Remaining"])
print(response.json())
```
:::

Ces valeurs sont inventées. La clef publique de l'exemple est le point
générateur de la courbe P-224, une constante publiée dans la norme qui décrit
cette courbe. Elle décrit un point réel de la courbe, donc l'appel va jusqu'au
contrôle cryptographique. Elle n'est la clef d'aucun fabricant.

Recopiées telles quelles, ces trois valeurs donnent un code 200 avec
`valid` à `false`, montré ci-dessous. Un verdict positif exige une signature
réellement lue sur une puce et la clef publique du fabricant qui l'a signée.

> [!INFO] Le SDK TypeScript ne couvre pas ce point d'entrée
> Le paquet `@sealtrust-io/sdk` n'expose aucune méthode pour la signature
> d'originalité. Ce contrôle s'appelle donc en HTTP direct, comme ci-dessus.

## Réponse d'exemple

Code HTTP 200. C'est la réponse exacte des valeurs de l'exemple ci-dessus.

```json
{
  "valid": false,
  "curve": "secp224r1",
  "uid_hex": "04000000000000",
  "signature_hex": "0000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000001",
  "error": "signature invalide",
  "public_key_used": "04b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34"
}
```

Un verdict négatif sort donc en 200, comme un verdict positif. Sur un verdict
positif, la même réponse revient avec `valid` à `true` et `error` à `null`. Les
quatre autres champs sont identiques.

Les six champs de la réponse.

| Champ | Type | Description |
| --- | --- | --- |
| `valid` | `boolean` | Le verdict. C'est le seul champ sur lequel construire votre logique. |
| `curve` | `string` | La courbe déduite de la clef publique utilisée. Deux valeurs possibles : `secp224r1` pour P-224, `secp256r1` pour P-256. Renseignée aussi bien sur un verdict positif que négatif. |
| `uid_hex` | `string` | Le numéro de série que vous avez envoyé, nettoyé et en minuscules, sans préfixe `0x` et sans séparateur. |
| `signature_hex` | `string` | La signature que vous avez envoyée, nettoyée de la même façon. |
| `error` | `string` ou `null` | `null` sur un verdict positif. Sur un verdict négatif, la valeur est `signature invalide`. Fondez vos décisions sur `valid` et traitez ce texte comme un message d'affichage. |
| `public_key_used` | `string` | La clef publique qui a servi au contrôle, nettoyée et en minuscules. C'est celle que vous avez envoyée, ou la clef d'originalité NXP de référence retenue par le service quand vous n'en envoyez aucune. Ce champ vous dit contre quoi le verdict a été rendu. |

Un code 200 signifie que le contrôle a pu être mené jusqu'au bout. Il ne
signifie pas que la signature est bonne. Lisez toujours `valid`.

> [!ATTENTION] Ce contrôle ne dit rien de votre article
> Ce point d'entrée fait une seule chose : il vérifie une signature avec une
> clef publique. Il ne consulte aucun de nos enregistrements. Un verdict positif
> vous dit que la signature correspond bien à ce numéro de série pour cette clef
> publique. Il ne vous dit ni que la puce a été posée sur un de vos articles, ni
> que l'article existe chez nous, ni qu'il est authentique. La vérification d'un
> article se fait ailleurs.

## Erreurs

Toute réponse d'erreur a la même forme : un objet JSON avec un champ `detail`.

| Code | Condition | Que faire |
| --- | --- | --- |
| 403 | L'appel porte un cookie de session et n'a ni en-tête `Origin` ni en-tête `Referer`. `detail` vaut `Origin or Referer header required`. | Appelez ce point d'entrée depuis votre serveur, sans cookie de session. |
| 403 | L'en-tête `Origin` ou `Referer` désigne un site qui n'est pas dans la liste autorisée. `detail` vaut `Forbidden origin`. | Appelez ce point d'entrée depuis votre serveur. Un appel direct depuis la page d'un tiers est refusé. |
| 403 | L'appel porte un cookie de session, et le jeton anti-falsification de l'en-tête ne correspond pas à celui du cookie. `detail` vaut `bad_csrf`. | Appelez ce point d'entrée depuis votre serveur, sans cookie de session. |
| 422 | Un champ obligatoire manque, un champ n'est pas une chaîne de caractères, ou vous avez envoyé un champ qui n'existe pas. `detail` est alors une liste qui nomme chaque champ fautif et le motif du refus. | Corrigez le corps de la requête. Seuls `uid_hex`, `signature_hex` et `public_key_hex` sont acceptés. |
| 422 | `uid_hex` n'est pas de l'hexadécimal lisible. `detail` vaut `Invalid uid_hex`. | Vérifiez que la valeur ne contient que des chiffres et les lettres `a` à `f`, et qu'elle a un nombre pair de caractères. |
| 422 | `uid_hex` est lisible mais ne fait ni 7 ni 10 octets. `detail` vaut `uid_hex doit faire 7 ou 10 octets`. | Envoyez 14 ou 20 caractères hexadécimaux. Un numéro de série tronqué ou complété par des zéros est refusé. |
| 422 | `public_key_hex` n'est pas lisible, n'a pas une longueur de 57 ou 65 octets, ne commence pas par `04`, ou ne décrit pas un point réel de la courbe. `detail` commence par `public_key_hex invalide`. | Reprenez la clef publique à sa source, au format SEC1 non compressé, et envoyez-la entière. |
| 422 | `signature_hex` n'est pas de l'hexadécimal lisible. `detail` vaut `Invalid signature_hex`. | Même contrôle que pour `uid_hex` : caractères hexadécimaux uniquement, nombre pair de caractères. |
| 422 | `signature_hex` est lisible mais sa longueur ne correspond pas à la courbe de la clef publique. `detail` vaut `Invalid signature format`. | Envoyez 56 octets avec une clef P-224, 64 octets avec une clef P-256. Si votre signature est au format DER, convertissez-la en `r` suivi de `s` avant de l'envoyer. |
| 429 | Trop d'appels depuis votre adresse réseau. La réponse porte en plus l'en-tête `Retry-After`, en secondes. | Attendez la durée en secondes indiquée par `Retry-After`, puis rejouez l'appel. Lisez `X-RateLimit-Remaining` pour ralentir avant d'en arriver là. |
| 500 | Erreur inattendue du serveur. Corps figé `{"detail": "Internal Server Error"}`. | Réessayez. L'en-tête `X-Request-Id` identifie l'appel, transmettez-le-nous s'il se répète. |

> [!INFO] L'ordre des contrôles
> Les contrôles s'enchaînent dans cet ordre : forme du corps de la requête,
> lisibilité de `uid_hex`, longueur du numéro de série, chargement de la clef
> publique, lisibilité de `signature_hex`, longueur de la signature, puis
> contrôle cryptographique. Une clef publique refusée arrête donc l'appel avant
> que la signature soit examinée, et un 422 sur la clef ne dit rien de la
> validité de votre signature.

## Voir aussi

- [Identification physique, QR et NFC](/identification-physique/),
  choisir le porteur physique et la forme exacte du lien GS1 Digital Link.
- [Graver et encoder les sceaux NFC](/gravure-sceaux-nfc/),
  préparer un lot, graver chaque puce et contrôler le résultat.
- [Erreurs de l'API](/api-erreurs/),
  reconnaître un code d'erreur et décider s'il faut corriger ou rejouer.
