1+ import { concatBytes , hexToBytes , type TArg } from "@noble/hashes/utils.js" ;
2+ import { test , expect } from "bun:test" ;
3+ import { getPublicKey , swapPoint , verify } from "." ;
4+ import { AsnConvert , AsnProp , AsnPropTypes , OctetString } from "@peculiar/asn1-schema" ;
5+ import { Certificate } from "@peculiar/asn1-x509" ;
6+ import { getCurveByOid , getHashByOid } from "../oids" ;
7+
8+ class PublicKeyParameters {
9+ @AsnProp ( { type : AsnPropTypes . ObjectIdentifier } )
10+ curve ! : string ;
11+
12+ @AsnProp ( { type : AsnPropTypes . ObjectIdentifier } )
13+ digest ! : string ;
14+ }
15+
16+ const _4 = new Uint8Array ( [ 4 ] ) ;
17+
18+ const proceedCertitificate = (
19+ privateKey : TArg < Uint8Array > ,
20+ certificate : TArg < Uint8Array > ,
21+ checkPublicKey : boolean = true
22+ ) => {
23+ const parsed = AsnConvert . parse ( certificate , Certificate ) ;
24+ const parameters = AsnConvert . parse ( parsed . tbsCertificate . subjectPublicKeyInfo . algorithm . parameters ! , PublicKeyParameters ) ;
25+
26+ const curve = getCurveByOid ( parameters . curve ) ;
27+ if ( ! curve ) throw new Error ( "Invalid curve parameters" ) ;
28+
29+ const digest = getHashByOid ( parameters . digest ) ;
30+ if ( ! digest ) throw new Error ( "Invalid hash function" ) ;
31+
32+ // Unfortunately, GOST doesn't have unificated serialization
33+ const spk = swapPoint (
34+ curve ,
35+ new Uint8Array (
36+ AsnConvert . parse ( parsed . tbsCertificate . subjectPublicKeyInfo . subjectPublicKey , OctetString ) . buffer
37+ ) . reverse ( )
38+ ) ;
39+
40+ if ( checkPublicKey ) {
41+ const pk = getPublicKey ( curve , privateKey ) ;
42+ // Unfortunately, GOST doesn't have unificated serialization
43+ expect ( pk . subarray ( 1 ) ) . toStrictEqual ( spk ) ;
44+ }
45+
46+ expect ( verify (
47+ curve ,
48+ concatBytes ( _4 , spk ) ,
49+ digest ( new Uint8Array ( parsed . tbsCertificateRaw ! ) ) . reverse ( ) ,
50+ swapPoint ( curve , new Uint8Array ( parsed . signatureValue ) )
51+ ) ) . toBeTrue ( ) ;
52+ }
53+
54+ test ( "[X.509] GOST R 34.10-2012 256 bit" , ( ) => {
55+ const privateKey = hexToBytes ( "BFCF1D623E5CDD3032A7C6EABB4A923C46E43D640FFEAAF2C3ED39A8FA399924" ) ;
56+ const certificate = `
57+ MIICYjCCAg+gAwIBAgIBATAKBggqhQMHAQEDAjBWMSkwJwYJKoZIhvcNAQkBFhpH
58+ b3N0UjM0MTAtMjAxMkBleGFtcGxlLmNvbTEpMCcGA1UEAxMgR29zdFIzNDEwLTIw
59+ MTIgKDI1NiBiaXQpIGV4YW1wbGUwHhcNMTMxMTA1MTQwMjM3WhcNMzAxMTAxMTQw
60+ MjM3WjBWMSkwJwYJKoZIhvcNAQkBFhpHb3N0UjM0MTAtMjAxMkBleGFtcGxlLmNv
61+ bTEpMCcGA1UEAxMgR29zdFIzNDEwLTIwMTIgKDI1NiBiaXQpIGV4YW1wbGUwZjAf
62+ BggqhQMHAQEBATATBgcqhQMCAiQABggqhQMHAQECAgNDAARAut/Qw1MUq9KPqkdH
63+ C2xAF3K7TugHfo9n525D2s5mFZdD5pwf90/i4vF0mFmr9nfRwMYP4o0Pg1mOn5Rl
64+ aXNYraOBwDCBvTAdBgNVHQ4EFgQU1fIeN1HaPbw+XWUzbkJ+kHJUT0AwCwYDVR0P
65+ BAQDAgHGMA8GA1UdEwQIMAYBAf8CAQEwfgYDVR0BBHcwdYAU1fIeN1HaPbw+XWUz
66+ bkJ+kHJUT0ChWqRYMFYxKTAnBgkqhkiG9w0BCQEWGkdvc3RSMzQxMC0yMDEyQGV4
67+ YW1wbGUuY29tMSkwJwYDVQQDEyBHb3N0UjM0MTAtMjAxMiAoMjU2IGJpdCkgZXhh
68+ bXBsZYIBATAKBggqhQMHAQEDAgNBAF5bm4BbARR6hJLEoWJkOsYV3Hd7kXQQjz3C
69+ dqQfmHrz6TI6Xojdh/t8ckODv/587NS5/6KsM77vc6Wh90NAT2s=
70+ ` ;
71+
72+ proceedCertitificate ( privateKey , Uint8Array . fromBase64 ( certificate ) ) ;
73+ } ) ;
74+
75+ test ( "[X.509] GOST R 34.10-2012 512 bit" , ( ) => {
76+ const privateKey = hexToBytes ( "3FC01CDCD4EC5F972EB482774C41E66DB7F380528DFE9E67992BA05AEE462435757530E641077CE587B976C8EEB48C48FD33FD175F0C7DE6A44E014E6BCB074B" ) ;
77+ const certificate = `
78+ MIIC6DCCAlSgAwIBAgIBATAKBggqhQMHAQEDAzBWMSkwJwYJKoZIhvcNAQkBFhpH
79+ b3N0UjM0MTAtMjAxMkBleGFtcGxlLmNvbTEpMCcGA1UEAxMgR29zdFIzNDEwLTIw
80+ MTIgKDUxMiBiaXQpIGV4YW1wbGUwHhcNMTMxMDA0MDczNjA0WhcNMzAxMDAxMDcz
81+ NjA0WjBWMSkwJwYJKoZIhvcNAQkBFhpHb3N0UjM0MTAtMjAxMkBleGFtcGxlLmNv
82+ bTEpMCcGA1UEAxMgR29zdFIzNDEwLTIwMTIgKDUxMiBiaXQpIGV4YW1wbGUwgaow
83+ IQYIKoUDBwEBAQIwFQYJKoUDBwECAQICBggqhQMHAQECAwOBhAAEgYATGQ9VCiM5
84+ FRGCQ8MEz2F1dANqhaEuywa8CbxOnTvaGJpFQVXQwkwvLFAKh7hk542vOEtxpKtT
85+ CXfGf84nRhMH/Q9bZeAc2eO/yhxrsQhTBufa1Fuou2oe/jUOaG6RAtUUvRzhNTpp
86+ RGGl1+EIY2vzzUua9j9Ol/gAoy/LNKQIfqOBwDCBvTAdBgNVHQ4EFgQUPcbTRXJZ
87+ nHtjj+eBP7b5lcTMekIwCwYDVR0PBAQDAgHGMA8GA1UdEwQIMAYBAf8CAQEwfgYD
88+ VR0BBHcwdYAUPcbTRXJZnHtjj+eBP7b5lcTMekKhWqRYMFYxKTAnBgkqhkiG9w0B
89+ CQEWGkdvc3RSMzQxMC0yMDEyQGV4YW1wbGUuY29tMSkwJwYDVQQDEyBHb3N0UjM0
90+ MTAtMjAxMiAoNTEyIGJpdCkgZXhhbXBsZYIBATAKBggqhQMHAQEDAwOBgQBObS7o
91+ ppPTXzHyVR1DtPa8b57nudJzI4czhsfeX5HDntOq45t9B/qSs8dC6eGxbhHZ9zCO
92+ SFtxWYdmg0au8XI9Xb8vTC1qdwWID7FFjMWDNQZb6lYh/J+8F2xKylvB5nIlRZqO
93+ o3eUNFkNyHJwQCk2WoOlO16zwGk2tdKH4KmD5w==
94+ ` ;
95+
96+ proceedCertitificate ( privateKey , Uint8Array . fromBase64 ( certificate ) ) ;
97+ } ) ;
98+
99+ test ( "[X.509] GOST R 34.10-2012 (Root CA)" , ( ) => {
100+ // ПАК «Головной удостоверяющий центр» -> серийный номер: 4E6D478B26F27D657F768E025CE3D393
101+ // https://e-trust.gosuslugi.ru/app/scc/portal/api/v1/portal/ca/download/4BC6DC14D97010C41A26E058AD851F81C842415A
102+ const certificate = `
103+ MIIFFDCCBMGgAwIBAgIQTm1HiybyfWV/do4CXOPTkzAKBggqhQMHAQEDAjCCASQxHjAcBgkqhkiG9w0B
104+ CQEWD2RpdEBtaW5zdnlhei5ydTELMAkGA1UEBhMCUlUxGDAWBgNVBAgMDzc3INCc0L7RgdC60LLQsDEZ
105+ MBcGA1UEBwwQ0LMuINCc0L7RgdC60LLQsDEuMCwGA1UECQwl0YPQu9C40YbQsCDQotCy0LXRgNGB0LrQ
106+ sNGPLCDQtNC+0LwgNzEsMCoGA1UECgwj0JzQuNC90LrQvtC80YHQstGP0LfRjCDQoNC+0YHRgdC40Lgx
107+ GDAWBgUqhQNkARINMTA0NzcwMjAyNjcwMTEaMBgGCCqFAwOBAwEBEgwwMDc3MTA0NzQzNzUxLDAqBgNV
108+ BAMMI9Cc0LjQvdC60L7QvNGB0LLRj9C30Ywg0KDQvtGB0YHQuNC4MB4XDTE4MDcwNjEyMTgwNloXDTM2
109+ MDcwMTEyMTgwNlowggEkMR4wHAYJKoZIhvcNAQkBFg9kaXRAbWluc3Z5YXoucnUxCzAJBgNVBAYTAlJV
110+ MRgwFgYDVQQIDA83NyDQnNC+0YHQutCy0LAxGTAXBgNVBAcMENCzLiDQnNC+0YHQutCy0LAxLjAsBgNV
111+ BAkMJdGD0LvQuNGG0LAg0KLQstC10YDRgdC60LDRjywg0LTQvtC8IDcxLDAqBgNVBAoMI9Cc0LjQvdC6
112+ 0L7QvNGB0LLRj9C30Ywg0KDQvtGB0YHQuNC4MRgwFgYFKoUDZAESDTEwNDc3MDIwMjY3MDExGjAYBggq
113+ hQMDgQMBARIMMDA3NzEwNDc0Mzc1MSwwKgYDVQQDDCPQnNC40L3QutC+0LzRgdCy0Y/Qt9GMINCg0L7R
114+ gdGB0LjQuDBmMB8GCCqFAwcBAQEBMBMGByqFAwICIwEGCCqFAwcBAQICA0MABEB1OSpFp7milX33EP0i
115+ kge6HbZacYp9fVj8sUa5RWFXrB27SKX5SvtIGepqKev69RSYeHHKR+jT9YX2NuSK9wONo4IBwjCCAb4w
116+ gfUGBSqFA2RwBIHrMIHoDDTQn9CQ0JrQnCDCq9Ca0YDQuNC/0YLQvtCf0YDQviBIU03CuyDQstC10YDR
117+ gdC40LggMi4wDEPQn9CQ0JogwqvQk9C+0LvQvtCy0L3QvtC5INGD0LTQvtGB0YLQvtCy0LXRgNGP0Y7R
118+ idC40Lkg0YbQtdC90YLRgMK7DDXQl9Cw0LrQu9GO0YfQtdC90LjQtSDihJYgMTQ5LzMvMi8yLzIzINC+
119+ 0YIgMDIuMDMuMjAxOAw00JfQsNC60LvRjtGH0LXQvdC40LUg4oSWIDE0OS83LzYvMTA1INC+0YIgMjcu
120+ MDYuMjAxODA/BgUqhQNkbwQ2DDTQn9CQ0JrQnCDCq9Ca0YDQuNC/0YLQvtCf0YDQviBIU03CuyDQstC1
121+ 0YDRgdC40LggMi4wMEMGA1UdIAQ8MDowCAYGKoUDZHEBMAgGBiqFA2RxAjAIBgYqhQNkcQMwCAYGKoUD
122+ ZHEEMAgGBiqFA2RxBTAGBgRVHSAAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
123+ DgQWBBTCVPG0a9RMt+BtNrQjkPH+wzybBjAKBggqhQMHAQEDAgNBAJr6/eI7rHL7+FsQnoH2i6DVxqal
124+ bIxLKj05edpZGPLLb6B2PTAMya7pSt9hb8QnFABgsR4IE5gT4VVkDWbX/n4=
125+ `
126+ proceedCertitificate ( new Uint8Array ( ) , Uint8Array . fromBase64 ( certificate ) , false ) ;
127+ } ) ;
0 commit comments