Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Signatures 007: trouble verifying §3.1_Signature #1876

Closed
bblfish opened this issue Dec 31, 2021 · 3 comments
Closed

Signatures 007: trouble verifying §3.1_Signature #1876

bblfish opened this issue Dec 31, 2021 · 3 comments

Comments

@bblfish
Copy link

bblfish commented Dec 31, 2021

I have built a test suite for a crypto library in Scala that uses the examples from Signing HTTP Messages 07 that compiles to JS and Java. Compiling to Java all 4 examples work except the verification of example 3.1 Signature. There is another problem in JS I am still looking into, but it also has trouble verifying 3.1.

I wrote up JS code that I think duplicates my Scala-JS code for the example as that should make it easier to work out what the problem is.

/* example from 
 * https://www.ietf.org/archive/id/draft-ietf-httpbis-message-signatures-07.html#section-2.3
 */
var testKeyRsaPssPublic = `MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr4tmm3r20Wd/PbqvP1s2
+QEtvpuRaV8Yq40gjUR8y2Rjxa6dpG2GXHbPfvMs8ct+Lh1GH45x28Rw3Ry53mm+
oAXjyQ86OnDkZ5N8lYbggD4O3w6M6pAvLkhk95AndTrifbIFPNU8PPMO7OyrFAHq
gDsznjPFmTOtCEcN2Z1FpWgchwuYLPL+Wokqltd11nqqzi+bJ9cvSKADYdUAAN5W
Utzdpiy6LbTgSxP7ociU4Tn0g5I6aDZJ7A8Lzo0KSyZYoA485mqcO0GVAdVw9lq4
aOT9v6d+nb4bnNkQVklLQ3fVAvJm+xdDOp9LCNCN48V2pnDOkFV6+U9nV5oyc6XI
2wIDAQAB`

var sigText = `"@method": GET
"@path": /foo
"@authority": example.org
"cache-control": max-age=60, must-revalidate
"x-empty-header":
"x-example": Example header with some whitespace.
"@signature-params": ("@method" "@path" "@authority" "cache-control" "x-empty-header" "x-example");created=1618884475;keyid="test-key-rsa-pss"`

var signature = `P0wLUszWQjoi54udOtydf9IWTfNhy+r53jGFj9XZuP4uKwxyJo1RSHi+oEF1FuX6O29
d+lbxwwBao1BAgadijW+7O/PyezlTnqAOVPWx9GlyntiCiHzC87qmSQjvu1CFyFuWSj
dGa3qLYYlNm7pVaJFalQiKWnUaqfT4LyttaXyoyZW84jS8gyarxAiWI97mPXU+OVM64
+HVBHmnEsS+lTeIsEQo36T3NFf2CujWARPQg53r58RmpZ+J9eKR2CD6IJQvacn5A4Ix
5BUAVGqlyp8JYm+S/CWJi31PNUjRRCusCVRj05NrxABNFv3r5S9IXf2fYJK+eyW4AiG
VMvMcOg==`


function str2ab(str) {
  const buf = new ArrayBuffer(str.length);
  const bufView = new Uint8Array(buf);
  for (let i = 0, strLen = str.length; i < strLen; i++) {
    bufView[i] = str.charCodeAt(i);
  }
  return buf;
}
function arrayBuf(hex) { return str2ab(window.atob(hex)) }
function aBufTxt(txt) {
	let enc = new TextEncoder();
   return enc.encode(txt)
}

var log = ""
function ok(o) { log = "ok="+o } 
function ko(o) { log = "ko="+o } 
var keyPub = window.crypto.subtle.importKey("spki",arrayBuf(testKeyRsaPssPublic), { name: "RSA-PSS", hash: "SHA-256" }, true, ["verify"])
keyPub.then( key => 
   window.crypto.subtle.verify(
		   { name: "RSA-PSS", saltLength: 64 }, 
	      key, arrayBuf(signature), aBufTxt(sigText)
		)
)

The Scala code on which this is based is part of a PR 48 to the bobcats Typelevel project. Main code is:

See Java Tests suite results.

@jricher
Copy link
Contributor

jricher commented Jan 1, 2022

I just did a quick static test in Python using all the given parameters:

print('HTTPSig Static Test')
print('*' * 30)


base = '''"@method": GET
"@path": /foo
"@authority": example.org
"cache-control": max-age=60, must-revalidate
"x-empty-header": 
"x-example": Example header with some whitespace.
"@signature-params": ("@method" "@path" "@authority" "cache-control" "x-empty-header" "x-example");created=1618884475;keyid="test-key-rsa-pss"'''

h = SHA512.new(base.encode('utf-8'))

signed = http_sfv.Item()

signed.parse(':P0wLUszWQjoi54udOtydf9IWTfNhy+r53jGFj9XZuP4uKwxyJo1RSHi+oEF1FuX6O29d+lbxwwBao1BAgadijW+7O/PyezlTnqAOVPWx9GlyntiCiHzC87qmSQjvu1CFyFuWSjdGa3qLYYlNm7pVaJFalQiKWnUaqfT4LyttaXyoyZW84jS8gyarxAiWI97mPXU+OVM64+HVBHmnEsS+lTeIsEQo36T3NFf2CujWARPQg53r58RmpZ+J9eKR2CD6IJQvacn5A4Ix5BUAVGqlyp8JYm+S/CWJi31PNUjRRCusCVRj05NrxABNFv3r5S9IXf2fYJK+eyW4AiGVMvMcOg==:'.encode('utf-8'))

pubKey = RSA.import_key(rsaTestKeyPssPublic)
verifier = pss.new(pubKey, mask_func=mgf512, salt_bytes=64)

try:
    verified = verifier.verify(h, signed.value)
    print("Verified:")
    print('> YES!')
    print()
except (ValueError, TypeError):
    print("Verified:")
    print('> NO!')
    print()

print('*' * 30)

And after a bit of debugging, it looks like the error is in your base string above. Namely, the construction of the x-empty-header line needs to end in a space, but some part of the tools strip the trailing spaces from the output, I think. The generated text is in fact missing this space, which is an error in this case. If you fix your base string and add that trailing space it should all work. I'm not sure how to get the tool chain to behave though -- @mnot any ideas here?

We can make it easier for people to verify like you're doing, though. Ultimately we don't :need: to include the empty header in the verified example, so long as we've got an example of the empty header normalized someplace and can show the trailing space properly.

I also tested against this newly-generated signature value from the generation script, which you can use for testing also:

Gu5RuUzQ1R3tAs9RbgsMfhnrRaNiJ6IbxLmu2wSvjntnlaEwUrJIU8zazmbxbqx5+io\
z/rAgICAIjOtOfRnynJwCX2cVmXcQsVvsYpnlUYR2ChnNIThgRj5WoVGpvzs91KsPhP\
2cn7a92ZLhfNsfd7jbTGS6GgZUvc8GW8EHwN5hQ10PIu7EwSeIiKDOpGWbsErEeg46r\
M2VxtJD+pObC82+E+hgdBPzWOCgOCmZex02OPOr/6UBO0Sb8TQ5XT3dG0QOiNzRPEN2\
e3gKkwhGMPFuPeHj1Sminnb/A+7L6o2KmT2d/cRmR5TN44WADCpQiqzxJHp/tSVW328\
pDjxCEQ==

Plugging this value into the static test above also validates, once I'd fixed the base string in my own tests also.

bblfish added a commit to bblfish/bobcats that referenced this issue Jan 1, 2022
@bblfish
Copy link
Author

bblfish commented Jan 1, 2022

Thanks. I was able to get the problematic test to compile with JS and Java (see test log) by changing the text to be signed to (see commit):

val txt = """"@method": GET
            |"@path": /foo
            |"@authority": example.org
            |"cache-control": max-age=60, must-revalidate
            |"x-empty-header": \
            |
            |"x-example": Example header with some whitespace.
            |"@signature-params": ("@method" "@path" "@authority" \
            |  "cache-control" "x-empty-header" "x-example");created=1618884475\
            |  ;keyid="test-key-rsa-pss"""".rfc8792single

I.e. append \ on the problematic line, so that it becomes "x-empty-header": \ and add an empty line after that.
One could add a little comment in the RFC to mitigate the surprise of a casual reader.

@jricher
Copy link
Contributor

jricher commented Jan 29, 2022

This header has been removed from the signed examples in #1903

@jricher jricher closed this as completed Jan 29, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

2 participants