@@ -489,48 +489,71 @@ export class KeyUtil {
489489 return keys . map ( k => ( { id : k . id , emails : k . emails , armored : KeyUtil . armor ( k ) , family : k . family } ) ) ;
490490 }
491491
492- public static validateChecksum = ( armoredText : string ) : boolean => {
493- const lines = armoredText . split ( '\n' ) . map ( l => l . trim ( ) ) ;
494-
495- // Filter out known non-data lines
496- const dataCandidates = lines . filter ( line => line . length > 0 && ! line . startsWith ( '-----' ) && ! line . startsWith ( 'Version:' ) && ! line . startsWith ( 'Comment:' ) ) ;
492+ public static validateChecksum ( armoredText : string ) : boolean {
493+ // Regex to capture any PGP armor block, e.g. SIGNATURE, MESSAGE, etc.
494+ // It captures: the block type in group (1), and the content in group (2)
495+ const pgpBlockRegex = / - - - - - B E G I N P G P ( [ A - Z ] + ) - - - - - ( [ \s \S ] * ?) - - - - - E N D P G P \1- - - - - / g;
496+
497+ let match : RegExpExecArray | null ;
498+ let validFound = false ;
499+
500+ // Iterate over all PGP blocks in the text
501+ while ( ( match = pgpBlockRegex . exec ( armoredText ) ) ) {
502+ const blockContent = match [ 2 ] || '' ; // the captured block text
503+ const lines = blockContent . split ( '\n' ) . map ( l => l . trim ( ) ) ;
504+
505+ // Filter out known non-base64 lines
506+ const dataCandidates = lines . filter (
507+ line => line . length > 0 && ! line . startsWith ( 'Version:' ) && ! line . startsWith ( 'Comment:' ) && ! line . startsWith ( 'Hash:' ) && ! line . startsWith ( '-----' )
508+ ) ;
509+
510+ // Find the checksum line (starts with '=')
511+ const checksumIndex = dataCandidates . findIndex ( line => line . startsWith ( '=' ) ) ;
512+ if ( checksumIndex === - 1 ) {
513+ continue ; // No checksum line found in this block, skip
514+ }
497515
498- // Find checksum line
499- const checksumIndex = dataCandidates . findIndex ( line => line . startsWith ( '=' ) ) ;
500- if ( checksumIndex === - 1 ) return false ;
516+ // Get the base64 after the '='
517+ const checksumLine = dataCandidates [ checksumIndex ] . slice ( 1 ) ;
518+ let providedBytes : string ;
519+ try {
520+ providedBytes = atob ( checksumLine ) ;
521+ } catch {
522+ continue ; // Not valid base64, skip
523+ }
501524
502- const checksumLine = dataCandidates [ checksumIndex ] . slice ( 1 ) ;
503- const dataLines = dataCandidates . slice ( 0 , checksumIndex ) ;
525+ // Check that decoded checksum is 3 bytes for CRC24
526+ if ( providedBytes . length !== 3 ) {
527+ continue ;
528+ }
529+ const providedCRC = ( providedBytes . charCodeAt ( 0 ) << 16 ) | ( providedBytes . charCodeAt ( 1 ) << 8 ) | providedBytes . charCodeAt ( 2 ) ;
504530
505- // Decode checksum
506- let providedBytes : string ;
507- try {
508- providedBytes = atob ( checksumLine ) ;
509- } catch {
510- return false ;
511- }
512- if ( providedBytes . length !== 3 ) return false ;
531+ // Decode all lines before the checksum line
532+ const dataLines = dataCandidates . slice ( 0 , checksumIndex ) ;
533+ const decodedChunks : string [ ] = [ ] ;
534+ for ( const line of dataLines ) {
535+ try {
536+ decodedChunks . push ( atob ( line ) ) ;
537+ } catch {
538+ // skip lines that aren't valid base64
539+ }
540+ }
513541
514- const providedCRC = ( providedBytes . charCodeAt ( 0 ) << 16 ) | ( providedBytes . charCodeAt ( 1 ) << 8 ) | providedBytes . charCodeAt ( 2 ) ;
542+ if ( ! decodedChunks . length ) {
543+ continue ;
544+ }
515545
516- // Attempt to decode all data lines (some may not be base64)
517- const decodedChunks : string [ ] = [ ] ;
518- for ( const line of dataLines ) {
519- try {
520- decodedChunks . push ( atob ( line ) ) ;
521- } catch {
522- // Not a valid base64 line, skip it
546+ // Join all decoded base64 data and calculate its CRC
547+ const rawData = decodedChunks . join ( '' ) ;
548+ // eslint-disable-next-line @typescript-eslint/no-misused-spread
549+ const dataBytes = new Uint8Array ( [ ...rawData ] . map ( c => c . charCodeAt ( 0 ) ) ) ;
550+ if ( KeyUtil . crc24 ( dataBytes ) === providedCRC ) {
551+ validFound = true ;
523552 }
524553 }
525554
526- if ( decodedChunks . length === 0 ) return false ;
527-
528- const rawData = decodedChunks . join ( '' ) ;
529- // eslint-disable-next-line @typescript-eslint/no-misused-spread
530- const dataBytes = new Uint8Array ( [ ...rawData ] . map ( c => c . charCodeAt ( 0 ) ) ) ;
531-
532- return KeyUtil . crc24 ( dataBytes ) === providedCRC ;
533- } ;
555+ return validFound ;
556+ }
534557
535558 private static crc24 = ( dataBytes : Uint8Array ) : number => {
536559 let crc = 0xb704ce ;
0 commit comments