Improperly signed update

Asked by George Anderson

I'm trying to solve a "The update is improperly signed" error. I've fruitlessly googled to the extent of my abilities and combed https://answers.launchpad.net/sparkle/, trying to find a solution. I've tried countless permutations, including choctop (http://drnic.github.com/choctop/), none of which worked for me. Here's my latest attempt, following the docs (http://wiki.github.com/andymatuschak/Sparkle/) as religiously as possible. Here are the details:

OS X 10.6.4 (27" iMac)
Xcode 3.2.3 (1688) (64 bit)
Create a new XCode Project => Cocoa Application (all options UNchecked)

Download Sparkle 1.5 b6 => http://sparkle.andymatuschak.org/files/Sparkle%201.5b6.zip

Drag Sparkle.framework (withOUT GC) into the Linked Frameworks (copying items into the destination group's folder)

New Copy Files Build Phase (destination: Frameworks)

Dragged Sparkle.framework from the Linked Frameworks folder to the Copy Files build phase

Set up a Sparkle updater object per the docs (http://wiki.github.com/andymatuschak/Sparkle/) (including “Check for Updates…” menu item in the application menu; setting its target to the SUUpdater instance and its action to checkForUpdates:)

Generated the dsa key pair via: ruby ~/Downloads/Sparkle\ 1/Extras/Signing\ Tools/generate_keys.rb

Added your public key (dsa_pub.pem) to the Resources folder

To SparkleTest-Info.plist:
 - added SUPublicDSAKeyFile, setting its value to dsa_pub.pem (careful not to include a trailing space)
 - added SUFeedURL, setting it to http://benevolentcode.com/sparkle_test/appcast.xml

Building 1st version:

Bundle versions string, short => 0.0.1
Bundle version => 0.0.1
Build => Clean All Targets
Active Configuration: Release
Active Target: SparkleTest
Active Executable: SparkleTest
Active Architecture: x86_64
Build => Build
You can get the results at: http://benevolentcode.com/sparkle_test/SparkleTest-0.0.1.zip

Building 2nd version:
Bundle versions string, short => 0.0.2
Bundle version => 0.0.2
(all the rest is identical to 1st build)
You can get the results at: http://benevolentcode.com/sparkle_test/SparkleTest-0.0.2.zip

Copied the sample Appcast.xml via:
cp ~/Downloads/Sparkle\ 1/Extras/Sample\ Appcast.xml appcast.xml
Signed the 2nd version: ruby ~/Downloads/Sparkle\ 1/Extras/Signing\ Tools/sign_update.rb build/Release/SparkleTest-0.0.2.zip dsa_priv.pem
Edited appcast.xml appropriately (I think). I got the "length" value from SparkleTest.app (Show Info in Finder).
Appcast URL: http://benevolentcode.com/sparkle_test/appcast.xml

Download 1st version: http://benevolentcode.com/sparkle_test/SparkleTest-0.0.1.zip
Package contents include Resources/dsa_pub.pem
Check for Updates...
Install Update
=> Update Error! The update is improperly signed.

Is there something obvious that I'm doing incorrectly?

GENERAL QUESTIONS

1. How do I build Sparkle from source? I grabbed the source from GitHub, opened it in Xcode.
Project => Edit Project Settings => Build; Build Active Architecture Only => YES (check)
Active Configuration => Release
Active Target => Sparkle
Active Architecture => x86_64

Build => Clean All Targets; Build => Build

Then I should use build/Release/Sparkle.framework in MY project? Does that sound correct?

2. I can NOT use Sparkle.framework in the "With Garbage Collection" with 10.6, correct?

3. Does the "length" attribute of the "enclosure" item (in appcast.xml) make a difference to signature validation? If so,
should it be the length of Foo.app or Foo.zip (which consists solely of Foo.app). Or if I'm delivering a dmg, should if be the
length of Foo.app (I think) or Foo.dmg?

4. Is there a way to debug "The update is improperly signed" errors? The console only lists the error message and nothing
more helpful.

5. Signing the update multiple times does NOT produce the same signature; this is expected, yes? But any signature generated by
"ruby ~/Downloads/Sparkle\ 1/Extras/Signing\ Tools/sign_update.rb build/Release/SparkleTest-0.0.2.zip dsa_priv.pem" should work?

As you likely guessed, I'm just getting into Cocoa/Sparkle and have more questions than answers.I appreciate any help anyone has to offer. Thanks for your patience (and for Sparkle).

/g

--

George Anderson
BenevolentCode LLC

Question information

Language:
English Edit question
Status:
Expired
For:
Sparkle Edit question
Assignee:
No assignee Edit question
Last query:
Last reply:
Revision history for this message
George Anderson (george-benevolentcode) said :
#1

> openssl version
OpenSSL 1.0.0a 1 Jun 2010

Revision history for this message
Hofman (cmhofman) said :
#2

1. This description would be all right, except that you should NOT build only the active architecture. If you want to build yourself, you may want to add a dependency, so the framework will be built with the correct build config.
2. You CAN use the one with garbage collection with 10.6 (always)), but you SHOULD only do that when you're using garbage collection in your app.
3. The length does not make any difference, Sparke ignores it. It should be set to the byte length of whatever you provide in the appcast (i.e. the .zip or .dmg).
4. There's no more information available to debug. There's also nothing more to know: the signature is not correct given the public key and the .zip file. And I can confirm that this is the case with the files you've linked to. The error must be in the way you have signed or created the keys, you must not have followed exactly the procedures as you described it. Are you really sure that the public and private keys are exactly the same all the time, i.e. you only ONCE generated them? And are you sure you signed the exact same .zip file that you link in the appcast (and not rebuild it or something)?
5. This is expected, as DSA uses a random key each time it generates a signature.

Revision history for this message
George Anderson (george-benevolentcode) said :
#3

Thanks, Hofman, for your input. Based on your response I've tried the following:

#1 I UNchecked "build only active architecture"
#2 I'm using the "With Garbage Collection" version

I went through all of the steps again, but still no joy.

I linked agains a debug build of Sparkle and set a breakpoint in SUBasicUpdateDriver.m (- (void)downloadDidFinish:). To be specific, it was this line:

if (![host publicDSAKey] || ![SUDSAVerifier validatePath:downloadPath withEncodedDSASignature:[updateItem DSASignature] withPublicDSAKey:[host publicDSAKey]])

Here's the output from poking around in gdb:

(gdb) po [host publicDSAKey]
-----BEGIN PUBLIC KEY-----
MIIDRjCCAjkGByqGSM44BAEwggIsAoIBAQDbstJqra0JL/cZ1V8v/m1RDp60ePlL
WuBnb23ZaA0ntv85yeBYKT2rGKhK4A/Eo+b2fNT05mlOlNES5u3yjh/8+eoVabS5
6PzTF7ENIwyTfR5VJA1e8I+C61HBqwKkNz2fD78/1IBr47EdtLPbC+DZ3BNIrMFO
W5B4RrPpQHF2N1kuva35Ims1tbvAapfGb0U2FpBjSMrMQUwB1OIWcRB09L48gyZo
pkfnIioOmgJpELOxiXSpQdnK52J223EUPXePZBlr16jDzCSPgYjOEw+aNielnKMd
Gt7oxLFaNEMb0rdpGOPLxgIU3+NlWrz/GSQR0ZYug/z1ceaWWaX18L/fAiEA6C6b
xEepxGZWpEuMr3C3DDrjxdviDNGok5JI4Hsp/GECggEAcY9UlG+AV397tTUpHucF
LBJ0DmD3NX/72vsVB9hyDBXgBL4RKrqWiL5xyBII4qN3NRrKPl6DGCCLEeG5uC+G
Oh0Mq1oYCTW+XsosiXFnhKtQuf/XXG3zlHUuAfe4FgUPO/ecMIApMpHJtgmHLQhA
NjiVk48HPLqQ9Sh/ss9Q1f2kNU+UzyhMuFiAy72d9z2OnOxmwQ93iEEPQiDG2/+l
x1ZPnV6VEBk5lSSJZZQBtJhaCW4rsmPF6qRqGkLee5Na6XncDoGO+SXiLUCT0Wa8
Gg5JFzGoiNaNtsXIhecrn2t8AbCphlfQ5vmarNNsMvbQyBBqTuXEsj8JS8k2oUaU
FwOCAQUAAoIBACD5FxJfDxAb3kyqPbLVRX/VYoIOZsd7XPRDoCvo9Ldf/exNmF8h
1UIvzvg7lz+ItD0HDmFtnWESc2H50JDHrA2jx7LCHkSkZd8GOUe0LZPYS6E1PuWe
PHBgjv2iibztITlFQw6WwX+ok1sNj9x6IEHuyF7qtB/hRA/iFW92T5J7o/h3CFuS
vWaafAzXHauwJ+jdpgjRcOQ0a60gOoGcIYigx9EufZaFO73Og43NLwC20O1cu/5L
u0zux44kNLSJjXYgFm1RGJWVsHjYkl6utWLjCT3z5q59S7Z0d57Fi2numcFsxRu7
OTOfKgi6ma82z8/BRa7jmRLLyW6tBJ8bVEw=
-----END PUBLIC KEY-----

(gdb) po downloadPath
/var/folders/hY/hY+zq2dXEdWnB74dwJBXWE+++TI/-Tmp-/SparkleTest 0.0.5 Update 6/SparkleTest-0.0.7.zip

(and that file exists)

(gdb) po [updateItem DSASignature]
MEYCIQCBet/AwYqmxQ6gGJ+7AL2fLx9oDKvYK+zmcq29HIpFPwIhAJ5RhqtXVIUF fk0lwLqicNQiLYPHH+BrQdgW95gs84KW

(which matches my appcast.xml)

HOWEVER:

(gdb) po [SUDSAVerifier validatePath:downloadPath withEncodedDSASignature:[updateItem DSASignature] withPublicDSAKey:[host publicDSAKey]]
No symbol "SUDSAVerifier" in current context.

SUDSAVerifier seems to be my problem. It's nowhere to be found. Is that part of the Sparkle binary that gets built? Any idea why it's choking here?

I'm trying my best to understand all of the moving parts here, but I'm still quite green. Thanks for any insights you might be able to provide.

Revision history for this message
Andy Matuschak (andymatuschak) said :
#4

Hey, George. I'm sorry you're having so much trouble with this. The errors can be mystifying to me, too. When the DSA code says an update isn't correctly signed, it's basically impossible to actually ask *why*.

> (gdb) po [SUDSAVerifier validatePath:downloadPath withEncodedDSASignature:[updateItem DSASignature] withPublicDSAKey:[host publicDSAKey]]
No symbol "SUDSAVerifier" in current context.

GDB behaves weirdly with class methods, so this doesn't surprise me at all. If there actually wasn't a SUDSAVerifier symbol available to the running program, you'd get very different and exciting errors.

So the first thing I notice is that you've got a newline in the sparkle:dsaSignature attribute in your appcast. That's definitely ruining things for you.

Just getting rid of that newline wasn't sufficient to fix the problem, but I was able to get your update installed by:
1. Running generate_keys.rb
2. Replacing your dsa_pub.pem with mine.
3. Changing SUFeedURL to a local copy of your feed, pointing the update to a local zip of 0.0.2.
4. Running sign_update.rb and using that DSA signature in my local feed.

Revision history for this message
George Anderson (george-benevolentcode) said :
#5

Thanks for your help, Andy. Unfortunately I'm getting the same failure after using your steps. Perhaps it's something wrong with a supporting library? I'm using "OpenSSL 1.0.0a 1 Jun 2010". What version are you running?

Are there any other libraries that factor into the equation?

Thanks again for your time,

/g

Revision history for this message
Andy Matuschak (andymatuschak) said :
#6

Unicycle:~ andym$ openssl version
OpenSSL 0.9.8l 5 Nov 2009

Revision history for this message
Launchpad Janitor (janitor) said :
#7

This question was expired because it remained in the 'Open' state without activity for the last 15 days.

Revision history for this message
tgunr (davec-polymicrosystems) said :
#8

I am in the same boat. I have narrowed the problem down to the call:

In my xml I have as my signature:
            sparkle:dsaSignature="MEUCIDfOo+OXzJLEmHvchEYh3kFTsFq1gA//1DI3+m3CT/PJAiEAk2SCzY8vmsXZ
wVF2vrA5PbfJIQwcaVjQQkgkjwJf0Oo="/>

Note, there is a line feed in there, I have checked very carefully with a hexdump to make sure. After the zip file is downloaded inside the call to
<pre>
+ (BOOL)validatePath:(NSString *)path withEncodedDSASignature:(NSString *)encodedSignature withPublicDSAKey:(NSString *)pkeyString
</pre>

at the point of
<pre>
 long length = b64decode(signature); // Decode the signature in-place and get the new length of the signature string.
</pre>

the signature is now:
<pre>
MEUCIDfOo+OXzJLEmHvchEYh3kFTsFq1gA//1DI3+m3CT/PJAiEAk2SCzY8vmsXZ wVF2vrA5PbfJIQwcaVjQQkgkjwJf0Oo=
</pre>
and the line feed has been replaced with a space somehow.

In the code for

static long b64decode(unsigned char* str)

a check is made for carriage return and line feed but not for a space (or tab) so the caracter is decoded as a -1 from the table. Since a -1 results in the skipping of the decode switch statement the phase of the alogorythm is unchaged wheras if it were a line feed or carriage return the phase (and dlast value) would be reset to 0.

This algorithm doesn't seem to me to be quite right as from what docs I have read, in the decoding any character not in the base64 table should be ignored. The way the b64decode is set up I don't think this is the case but I'm not quite sure.

My current concern is how did the signature get changed?

More to follow as I go along.