Created
September 24, 2025 00:34
-
-
Save SagePtr/1a0e88473b12a1c05f943466e53f98a8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| class DerHelpers | |
| { | |
| public const COSE_ALG_EDDSA = -8; | |
| public const COSE_ALG_ES256 = -7; | |
| public const COSE_ALG_RS256 = -257; | |
| public const COSE_KTY_OKP = 1; | |
| public const COSE_KTY_EC2 = 2; | |
| public const COSE_KTY_RSA = 3; | |
| public const COSE_CRV_P256 = 1; | |
| public const COSE_CRV_ED25519 = 6; | |
| private static function derLength(int $length): string | |
| { | |
| if ($length < 0x80) { | |
| return \chr($length); | |
| } | |
| $bytes = ''; | |
| while ($length > 0) { | |
| $bytes .= \chr($length & 0xFF); | |
| $length >>= 8; | |
| } | |
| return \chr(0x80 | \strlen($bytes)) . \strrev($bytes); | |
| } | |
| private static function derTLV(int $tag, string $value): string | |
| { | |
| return \chr($tag) . self::derLength(\strlen($value)) . $value; | |
| } | |
| private static function derSequence(array $items): string | |
| { | |
| return self::derTLV(0x30, implode('', $items)); | |
| } | |
| private static function derOID(string $oid): string | |
| { | |
| $oidbytes = \array_map('\intval', \explode('.', $oid)); | |
| $bytes = \chr($oidbytes[0] * 40 + $oidbytes[1]); | |
| foreach (\array_slice($oidbytes, 2) as $x) { | |
| $part = \chr($x & 0x7F); | |
| while ($x >>= 7) { | |
| $part .= \chr($x & 0x7F | 0x80); | |
| } | |
| $bytes .= \strrev($part); | |
| } | |
| return self::derTLV(0x06, $bytes); | |
| } | |
| private static function derBitString(string $bytes, int $unusedBits = 0): string | |
| { | |
| return self::derTLV(0x03, \chr($unusedBits) . $bytes); | |
| } | |
| private static function derPositiveInteger(string $bytes): string | |
| { | |
| $lzero = (\ord($bytes[0]) & 0x80) ? "\0" : ''; | |
| return self::derTLV(0x02, $lzero . $bytes); | |
| } | |
| public static function derNULL(): string | |
| { | |
| return "\x05\0"; | |
| } | |
| private static function derPublicKeyOKP(int $curve, string $bytes): ?string | |
| { | |
| $curveParams = match ($curve) { | |
| self::COSE_CRV_ED25519 => ['oid' => '1.3.101.112', 'length' => 32], | |
| default => null, | |
| }; | |
| if (!$curveParams) { | |
| return null; | |
| } | |
| if (\strlen($bytes) !== $curveParams['length']) { | |
| return null; | |
| } | |
| return self::derSequence([ | |
| self::derSequence([ | |
| self::derOID($curveParams['oid']), | |
| ]), | |
| self::derBitString($bytes), | |
| ]); | |
| } | |
| public static function derPublicKeyEC2(int $curve, string $x, string $y): ?string | |
| { | |
| $curveParams = match ($curve) { | |
| self::COSE_CRV_P256 => ['oid' => '1.2.840.10045.3.1.7', 'length' => 32], | |
| default => null, | |
| }; | |
| if (!$curveParams) { | |
| return null; | |
| } | |
| // @see https://www.rfc-editor.org/rfc/rfc9053.html#name-double-coordinate-curves | |
| // "Leading-zero octets MUST be preserved." | |
| if (\strlen($x) !== $curveParams['length'] || \strlen($y) !== $curveParams['length']) { | |
| return null; | |
| } | |
| return self::derSequence([ | |
| self::derSequence([ | |
| self::derOID('1.2.840.10045.2.1'), | |
| self::derOID($curveParams['oid']), | |
| ]), | |
| self::derBitString("\x04" . $x . $y), | |
| ]); | |
| } | |
| public static function derPublicKeyRSA(string $modulus, string $publicExponent): ?string | |
| { | |
| return self::derSequence([ | |
| self::derSequence([ | |
| self::derOID('1.2.840.113549.1.1.1'), | |
| self::derNULL(), | |
| ]), | |
| self::derBitString( | |
| self::derSequence([ | |
| self::derPositiveInteger($modulus), | |
| self::derPositiveInteger($publicExponent), | |
| ]) | |
| ), | |
| ]); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment