Skip to content

Commit f8e925c

Browse files
AhmedAhmed
authored andcommitted
Fix cross-platform strong name signing by manually parsing RSA key blobs
1 parent f8427ae commit f8e925c

File tree

1 file changed

+73
-58
lines changed

1 file changed

+73
-58
lines changed

src/Compiler/AbstractIL/ilsign.fs

Lines changed: 73 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
1+
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
22

33
module internal FSharp.Compiler.AbstractIL.StrongNameSign
44

@@ -126,9 +126,11 @@ type BlobReader =
126126
val mutable _blob: byte array
127127
val mutable _offset: int
128128
new(blob: byte array) = { _blob = blob; _offset = 0 }
129-
130-
member x.Offset with get() = x._offset and set(v) = x._offset <- v
131-
129+
130+
member x.Offset
131+
with get () = x._offset
132+
and set (v) = x._offset <- v
133+
132134
member x.ReadInt32() : int =
133135
let offset = x._offset
134136
x._offset <- offset + 4
@@ -144,34 +146,40 @@ type BlobReader =
144146
x._offset <- x._offset + length
145147
arr |> Array.rev
146148

147-
let RSAParametersFromBlob blob keyType =
149+
/// Decodes an RSA functional blob into RSAParameters.
150+
/// Ref: https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-publickeystruc
151+
let RSAParametersFromBlob blob =
148152
let mutable reader = BlobReader blob
149153

150-
let header = reader.ReadInt32()
151-
if header <> 0x00000206 && header <> 0x00000207 && keyType = KeyType.KeyPair then
152-
raise (CryptographicException(getResourceString (FSComp.SR.ilSignPrivateKeyExpected ())))
153-
154-
reader.ReadInt32() |> ignore // ALG_ID
154+
// Skip PUBLICKEYSTRUC (8 bytes): bType(1), bVersion(1), reserved(2), aiKeyAlg(4)
155+
reader.ReadInt32() |> ignore
156+
reader.ReadInt32() |> ignore
155157

156-
if reader.ReadInt32() <> RSA_PRIV_MAGIC then
157-
raise (CryptographicException(getResourceString (FSComp.SR.ilSignRsaKeyExpected ()))) // 'RSA2'
158+
// Read RSAPUBKEY header (starts with magic)
159+
// Ref: https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-rsapubkey
160+
let magic = reader.ReadInt32()
158161

159-
let byteLen, halfLen =
160-
let bitLen = reader.ReadInt32()
162+
if magic <> RSA_PUB_MAGIC && magic <> RSA_PRIV_MAGIC then
163+
raise (CryptographicException(getResourceString (FSComp.SR.ilSignRsaKeyExpected ())))
161164

162-
match bitLen % 16 with
163-
| 0 -> (bitLen / 8, bitLen / 16)
164-
| _ -> raise (CryptographicException(getResourceString (FSComp.SR.ilSignInvalidBitLen ())))
165+
let bitLen = reader.ReadInt32()
166+
let byteLen, halfLen = (bitLen / 8, bitLen / 16)
165167

166168
let mutable key = RSAParameters()
167-
key.Exponent <- reader.ReadBigInteger 4
169+
key.Exponent <- reader.ReadBigInteger 4 // pubexp (4 bytes)
168170
key.Modulus <- reader.ReadBigInteger byteLen
169-
key.P <- reader.ReadBigInteger halfLen
170-
key.Q <- reader.ReadBigInteger halfLen
171-
key.DP <- reader.ReadBigInteger halfLen
172-
key.DQ <- reader.ReadBigInteger halfLen
173-
key.InverseQ <- reader.ReadBigInteger halfLen
174-
key.D <- reader.ReadBigInteger byteLen
171+
172+
// IMPORTANT: Conditional reading based on Magic.
173+
// Private fields (P, Q, DP, DQ, InverseQ, D) follow the modulus ONLY in PrivateKeyBlobs (RSA2).
174+
// Ref: https://learn.microsoft.com/en-us/windows/win32/seccrypto/base-provider-key-blobs
175+
if magic = RSA_PRIV_MAGIC then
176+
key.P <- reader.ReadBigInteger halfLen
177+
key.Q <- reader.ReadBigInteger halfLen
178+
key.DP <- reader.ReadBigInteger halfLen
179+
key.DQ <- reader.ReadBigInteger halfLen
180+
key.InverseQ <- reader.ReadBigInteger halfLen
181+
key.D <- reader.ReadBigInteger byteLen
182+
175183
key
176184

177185
let validateRSAField (field: byte array MaybeNull) expected (name: string) =
@@ -262,13 +270,19 @@ let toCLRKeyBlob (rsaParameters: RSAParameters) (algId: int) : byte array =
262270

263271
key
264272

265-
let createSignature (hash: byte array) keyBlob keyType =
273+
let createSignature (hash: byte array) keyBlob _keyType =
266274
use rsa = RSA.Create()
267-
rsa.ImportParameters(RSAParametersFromBlob keyBlob keyType)
275+
rsa.ImportParameters(RSAParametersFromBlob keyBlob)
268276

269-
let signature =
270-
rsa.SignHash(hash, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1)
277+
let hashAlgName =
278+
match hash.Length with
279+
| 20 -> HashAlgorithmName.SHA1
280+
| 32 -> HashAlgorithmName.SHA256
281+
| 48 -> HashAlgorithmName.SHA384
282+
| 64 -> HashAlgorithmName.SHA512
283+
| _ -> HashAlgorithmName.SHA1
271284

285+
let signature = rsa.SignHash(hash, hashAlgName, RSASignaturePadding.Pkcs1)
272286
signature |> Array.rev
273287

274288
let patchSignature (stream: Stream) (peReader: PEReader) (signature: byte array) =
@@ -304,29 +318,37 @@ let signStream stream keyBlob =
304318
patchSignature stream peReader signature
305319

306320
let signatureSize (pk: byte array) =
307-
if pk.Length < 20 then 0
321+
if (box pk |> isNull) || pk.Length < 8 + 12 then
322+
0
308323
else
309-
let reader = BlobReader pk
310-
reader.Offset <- 12
311-
let bitLen = reader.ReadInt32()
312-
let modulusLength = bitLen / 8
313-
314-
if modulusLength < 160 then 128 else modulusLength - 32
315-
// Key signing
316-
type keyContainerName = string
317-
type keyPair = byte array
318-
type pubkey = byte array
319-
type pubkeyOptions = byte array * bool
324+
let mutable reader = BlobReader pk
325+
reader.Offset <- 8
326+
327+
let magic = reader.ReadInt32()
328+
329+
if magic = RSA_PUB_MAGIC || magic = RSA_PRIV_MAGIC then
330+
let bitLen = reader.ReadInt32()
331+
let size = bitLen / 8
332+
(size + 7) &&& ~~~7
333+
else
334+
0
335+
336+
let signatureSizeFromKeyPair (keyBlob: byte array) = signatureSize keyBlob
320337

338+
// Returns a CLR Format Blob public key
321339
let getPublicKeyForKeyPair keyBlob =
322340
use rsa = RSA.Create()
323-
rsa.ImportParameters(RSAParametersFromBlob keyBlob KeyType.KeyPair)
341+
rsa.ImportParameters(RSAParametersFromBlob keyBlob)
324342
let rsaParameters = rsa.ExportParameters false
325343
toCLRKeyBlob rsaParameters CALG_RSA_KEYX
326344

327-
let signerGetPublicKeyForKeyPair (kp: keyPair) : pubkey = getPublicKeyForKeyPair kp
345+
// Key signing
346+
type keyContainerName = string
347+
type keyPair = byte array
348+
type pubkey = byte array
349+
type pubkeyOptions = byte array * bool
328350

329-
let signerSignatureSize (pk: pubkey) : int = signatureSize pk
351+
let signerGetPublicKeyForKeyPair (kp: keyPair) : pubkey = getPublicKeyForKeyPair kp
330352

331353
let signerSignStreamWithKeyPair stream keyBlob = signStream stream keyBlob
332354

@@ -345,37 +367,30 @@ type ILStrongNameSigner =
345367
static member OpenPublicKeyOptions kp p = PublicKeyOptionsSigner(kp, p)
346368

347369
static member OpenPublicKey bytes = PublicKeySigner bytes
348-
static member OpenKeyPairFile bytes = KeyPair(bytes)
370+
371+
static member OpenKeyPairFile bytes = KeyPair bytes
372+
349373
static member OpenKeyContainer s = KeyContainer s
350374

351375
member s.IsFullySigned =
352376
match s with
353377
| PublicKeySigner _ -> false
354-
| PublicKeyOptionsSigner pko ->
355-
let _, usePublicSign = pko
356-
usePublicSign
378+
| PublicKeyOptionsSigner(_, usePublicSign) -> usePublicSign
357379
| KeyPair _ -> true
358380
| KeyContainer _ -> failWithContainerSigningUnsupportedOnThisPlatform ()
359381

360382
member s.PublicKey =
361383
match s with
362384
| PublicKeySigner pk -> pk
363-
| PublicKeyOptionsSigner pko ->
364-
let pk, _ = pko
365-
pk
385+
| PublicKeyOptionsSigner(pk, _) -> pk
366386
| KeyPair kp -> signerGetPublicKeyForKeyPair kp
367387
| KeyContainer _ -> failWithContainerSigningUnsupportedOnThisPlatform ()
368388

369389
member s.SignatureSize =
370-
let pkSignatureSize pk =
371-
signerSignatureSize pk
372-
373390
match s with
374-
| PublicKeySigner pk -> pkSignatureSize pk
375-
| PublicKeyOptionsSigner pko ->
376-
let pk, _ = pko
377-
pkSignatureSize pk
378-
| KeyPair kp -> pkSignatureSize (signerGetPublicKeyForKeyPair kp)
391+
| PublicKeySigner pk
392+
| PublicKeyOptionsSigner(pk, _) -> signatureSize pk
393+
| KeyPair kp -> signatureSize kp
379394
| KeyContainer _ -> failWithContainerSigningUnsupportedOnThisPlatform ()
380395

381396
member s.SignStream stream =

0 commit comments

Comments
 (0)