Skip to content

Commit f42de44

Browse files
committed
support for RSA-signed certificates and non-SHA256 algorithms
Signed-off-by: Ishan Mundra <[email protected]>
1 parent cee51c0 commit f42de44

File tree

8 files changed

+100
-3
lines changed

8 files changed

+100
-3
lines changed

.changeset/loud-ants-beam.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@sigstore/verify': minor
3+
'@sigstore/core': minor
4+
---
5+
6+
Add support for RSA-signed certificates and non-standard hash algorithms in bundle signature verification

packages/core/src/__tests__/__fixtures__/certs.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,4 +183,29 @@ export const certificates = {
183183
rtECMQCDR5Zb2cwiENFL1XGg5E7si96en+Hc/F9EMKjQUHLHWRnMSRukC7jr3GnF
184184
NWj3tGQ=
185185
-----END CERTIFICATE-----`,
186+
187+
// Certificate signed with sha512WithRSAEncryption
188+
// Used to test RSA signature algorithm support in X509Certificate
189+
rsaroot: `-----BEGIN CERTIFICATE-----
190+
MIIDmzCCAoOgAwIBAgIUEDvdwGGfqUtFVe7hUGjIoOx3coMwDQYJKoZIhvcNAQEN
191+
BQAwXDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRYwFAYDVQQHDA1TYW4gRnJh
192+
bmNpc2NvMRMwEQYDVQQKDApmb29iYXIuZGV2MRMwEQYDVQQDDApmb29iYXItcnNh
193+
MCAXDTI1MTEyNTE4NDA1NVoYDzIxMjUxMTAxMTg0MDU1WjBcMQswCQYDVQQGEwJV
194+
UzELMAkGA1UECAwCQ0ExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xEzARBgNVBAoM
195+
CmZvb2Jhci5kZXYxEzARBgNVBAMMCmZvb2Jhci1yc2EwggEiMA0GCSqGSIb3DQEB
196+
AQUAA4IBDwAwggEKAoIBAQCrEHZ7YIpcuUH5M0vKiBYtQpA89kR65nKxGAKXu3In
197+
WUkUKJFXcIJ7bz8wXSvqjUpXAdnpp8yu/Sn6Ms5+NQ4hMbUWDdkHV6H22vLhhF8F
198+
cqCceI/3mMaC/h8yIVd1b+vw1tnjjb5dBoBQAxSJywHo4jjU84KEK5bEWECHfwt7
199+
/TeyY41hsIsAS2Iry3G6x1v2i5Bkq5tFR7uHDoEcrUmm+RoRpBtm2/jHsVw7F+o6
200+
9g2DRnW+m+rKxwhuwlhD+AeSqEmWRR81nA8FsTxSnTUHEgDIFPzy5M860aVqCj5i
201+
1yibGSUVQlvhr3xJDsLG5xzLO1m4ApExtk3BNirBrrvJAgMBAAGjUzBRMB0GA1Ud
202+
DgQWBBQMvPy1jum7eirGaej8870fV5itVDAfBgNVHSMEGDAWgBQMvPy1jum7eirG
203+
aej8870fV5itVDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBDQUAA4IBAQB1
204+
bbu84m1A7t2NUKg9ftpRziNLAzIWthMe1LVGa1Ah+4kifsd0wNZa/P8X8vyFVoxN
205+
lRE0X31tTObFxIMOo6b/p5IqmAm6+G7ClQHUum1Cte4mP6XzCW/Zke5Mk+mhe+IH
206+
w9ZlFs1EfPY6G8ZjMH2yT30DInKEdXQd4XbynyMjWpsNlV7C8l8GE67uXWNHRkK9
207+
LAApDh/DuFMDb3MhfI2b8a+/G0SOHEtM2Yfu91jYUWH84xp7IdQ1vO7Wg8zHG70i
208+
4FBVRaS7fbdNXNfEkG7B4z2ApZA1DAmopbvpcxqI+PSLBFCKNLsMzYVv8FIKC6gX
209+
Pm/GTRVvePHiJSVHR7MO
210+
-----END CERTIFICATE-----`,
186211
};

packages/core/src/__tests__/x509/cert.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,4 +333,20 @@ describe('X509Certificate', () => {
333333
});
334334
});
335335
});
336+
337+
describe('#signatureAlgorithm', () => {
338+
describe('when the certificate is signed with ECDSA', () => {
339+
it('returns the correct algorithm', () => {
340+
const cert = X509Certificate.parse(certificates.root);
341+
expect(cert.signatureAlgorithm).toBe('sha384');
342+
});
343+
});
344+
345+
describe('when the certificate is signed with RSA', () => {
346+
it('returns the correct algorithm', () => {
347+
const cert = X509Certificate.parse(certificates.rsaroot);
348+
expect(cert.signatureAlgorithm).toBe('sha512');
349+
});
350+
});
351+
});
336352
});

packages/core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export * as crypto from './crypto';
1818
export * as dsse from './dsse';
1919
export * as encoding from './encoding';
2020
export * as json from './json';
21+
export { HASH_ALGORITHM_NAMES } from './oid';
2122
export * as pem from './pem';
2223
export { RFC3161Timestamp } from './rfc3161';
2324
export { ByteStream } from './stream';

packages/core/src/oid.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,23 @@ export const ECDSA_SIGNATURE_ALGOS: Record<string, string> = {
55
'1.2.840.10045.4.3.4': 'sha512',
66
};
77

8+
export const RSA_SIGNATURE_ALGOS: Record<string, string> = {
9+
'1.2.840.113549.1.1.14': 'sha224',
10+
'1.2.840.113549.1.1.11': 'sha256',
11+
'1.2.840.113549.1.1.12': 'sha384',
12+
'1.2.840.113549.1.1.13': 'sha512',
13+
};
14+
815
export const SHA2_HASH_ALGOS: Record<string, string> = {
916
'2.16.840.1.101.3.4.2.1': 'sha256',
1017
'2.16.840.1.101.3.4.2.2': 'sha384',
1118
'2.16.840.1.101.3.4.2.3': 'sha512',
1219
};
20+
21+
export const HASH_ALGORITHM_NAMES: Record<number, string> = {
22+
1: 'sha256',
23+
2: 'sha384',
24+
3: 'sha512',
25+
4: 'sha3-256',
26+
5: 'sha3-384',
27+
};

packages/core/src/x509/cert.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ limitations under the License.
1515
*/
1616
import { ASN1Obj } from '../asn1';
1717
import * as crypto from '../crypto';
18-
import { ECDSA_SIGNATURE_ALGOS } from '../oid';
18+
import { ECDSA_SIGNATURE_ALGOS, RSA_SIGNATURE_ALGOS } from '../oid';
1919
import * as pem from '../pem';
2020
import {
2121
X509AuthorityKeyIDExtension,
@@ -85,6 +85,10 @@ export class X509Certificate {
8585

8686
get signatureAlgorithm(): string {
8787
const oid: string = this.signatureAlgorithmObj.subs[0].toOID();
88+
if (RSA_SIGNATURE_ALGOS[oid]) {
89+
return RSA_SIGNATURE_ALGOS[oid];
90+
}
91+
8892
return ECDSA_SIGNATURE_ALGOS[oid];
8993
}
9094

packages/verify/src/__tests__/bundle/message.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,5 +76,33 @@ describe('MessageSignatureContent', () => {
7676
expect(subject.verifySignature(key.publicKey)).toBe(true);
7777
});
7878
});
79+
80+
describe('with SHA2_256 hash algorithm', () => {
81+
const message256 = Buffer.from('message256');
82+
const messageDigest256 = core.digest('sha256', message256);
83+
const messageSignature256: MessageSignature = {
84+
messageDigest: { digest: messageDigest256, algorithm: HashAlgorithm.SHA2_256 },
85+
signature: crypto.sign('sha256', message256, key.privateKey),
86+
};
87+
const subject256 = new MessageSignatureContent(messageSignature256, message256);
88+
89+
it('verifies signature with explicit hash algorithm', () => {
90+
expect(subject256.verifySignature(key.publicKey)).toBe(true);
91+
});
92+
});
93+
94+
describe('with SHA2_512 hash algorithm', () => {
95+
const message512 = Buffer.from('message512');
96+
const messageDigest512 = core.digest('sha512', message512);
97+
const messageSignature512: MessageSignature = {
98+
messageDigest: { digest: messageDigest512, algorithm: HashAlgorithm.SHA2_512 },
99+
signature: crypto.sign('sha512', message512, key.privateKey),
100+
};
101+
const subject512 = new MessageSignatureContent(messageSignature512, message512);
102+
103+
it('verifies signature with explicit hash algorithm', () => {
104+
expect(subject512.verifySignature(key.publicKey)).toBe(true);
105+
});
106+
});
79107
});
80108
});

packages/verify/src/bundle/message.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313
See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
16-
import { crypto } from '@sigstore/core';
16+
import { crypto, HASH_ALGORITHM_NAMES } from '@sigstore/core';
1717

1818
import type { MessageSignature } from '@sigstore/bundle';
1919
import type { SignatureContent } from '../shared.types';
@@ -22,11 +22,13 @@ export class MessageSignatureContent implements SignatureContent {
2222
public readonly signature: Buffer;
2323
private readonly messageDigest: Buffer;
2424
private readonly artifact: Buffer;
25+
private readonly hashAlgorithm: string | undefined;
2526

2627
constructor(messageSignature: MessageSignature, artifact: Buffer) {
2728
this.signature = messageSignature.signature;
2829
this.messageDigest = messageSignature.messageDigest.digest;
2930
this.artifact = artifact;
31+
this.hashAlgorithm = HASH_ALGORITHM_NAMES[messageSignature.messageDigest.algorithm];
3032
}
3133

3234
public compareSignature(signature: Buffer): boolean {
@@ -38,6 +40,6 @@ export class MessageSignatureContent implements SignatureContent {
3840
}
3941

4042
public verifySignature(key: crypto.KeyObject): boolean {
41-
return crypto.verify(this.artifact, key, this.signature);
43+
return crypto.verify(this.artifact, key, this.signature, this.hashAlgorithm);
4244
}
4345
}

0 commit comments

Comments
 (0)