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
Fix SRP #5925
Fix SRP #5925
Conversation
Previously they were using EVP_EncodeBlock/EVP_DecodeBlock. These are low level functions that do not handle padding characters. This was causing the SRP code to fail. One side effect of using EVP_EncodeUpdate is that it inserts newlines which is not what we need in SRP so we add a flag to avoid that.
Historically we used to implement standalone base64 code for SRP. This was replaced by commit 3d3f21a with the standard base64 processing code. However, the SRP base64 code was designed to be compatible with other SRP libraries (notably libsrp, but also others) that use a variant of standard base64. Specifically a different alphabet is used and no padding '=' characters are used. Instead 0 padding is added to the front of the string. By changing to standard base64 we change the behaviour of the API which may impact interoperability. It also means that SRP verifier files created prior to 1.1.1 would not be readable in 1.1.1 and vice versa. Instead we expand our standard base64 processing with the capability to be able to read and generate the SRP base64 variant.
There are at least warnings that you want to fix. |
Fixup commit pushed to address the warning picked up by Travis. The Appveyor failure appear unrelated. |
It's being addressed in #5907. |
crypto/srp/srp_vfy.c
Outdated
EVP_ENCODE_CTX *ctx; | ||
int outl = 0, outl2 = 0; | ||
size_t size, padsize; | ||
unsigned char *pad = (unsigned char *)"00"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's possible and it would be only appropriate to declare this const. After all, literals can and customarily do reside in read-only segment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
crypto/srp/srp_vfy.c
Outdated
size_t size, padsize; | ||
unsigned char *pad = (unsigned char *)"00"; | ||
|
||
while (*src != '\0' && (*src == ' ' || *src == '\t' || *src == '\n')) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On side note, formally speaking non-zero check is redundant. This means that some compilers might issue warning. Not that we actually honour such warnings, as we do consciously choose to have constant conditions at occasions...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed the zero check.
crypto/srp/srp_vfy.c
Outdated
size = strlen(src); | ||
padsize = 4 - (size & 3); | ||
if (padsize == 4) | ||
padsize = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just reapply mask, i.e. padsize &= 3? [One can as well (0 - size) & 3, but that would be less readable.]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
crypto/srp/srp_vfy.c
Outdated
|
||
/* Four bytes in src become three bytes output. */ | ||
if (size > INT_MAX || (size / 4) * 3 > alen) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So alen is not longer used? Then why pass it at all? All occurrences are sizeof(something_on_stack), 2500, pretty high, but is there guarantee that it won't overflow?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm...I think this removing this was over-zealous. I have put this test back.
crypto/srp/srp_vfy.c
Outdated
if ((int)padsize >= outl) | ||
return -1; | ||
memmove(a, a + padsize, outl - padsize); | ||
outl -= padsize; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is utterly counter-intuitive. I mean the fact that padsize comes out as same value in encoded and decoded. Maybe a commentary would be appropriate...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a comment to explain this.
crypto/srp/srp_vfy.c
Outdated
evp_encode_ctx_set_flags(ctx, EVP_ENCODE_CTX_NO_NEWLINES | ||
| EVP_ENCODE_CTX_USE_SRP_ALPHABET); | ||
|
||
/* We pad at the front with zero bytes as required */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd appreciate more explicit note here. I mean instead of "as required" something like "till length is divisible by 3 so that encoded string needs no ="...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I expanded out the comment.
crypto/evp/encode.c
Outdated
void evp_encode_ctx_set_flags(EVP_ENCODE_CTX *ctx, uint64_t flags) | ||
{ | ||
ctx->flags = flags; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not impossible to imagine that this might go public at some point, and in such case I'd argue in favour of using types that don't require additional headers such as unsigned int. In other words I'd like to suggest to consider declaring flags as unsigned int, here and in structure...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to unsigned int.
crypto/evp/encode.c
Outdated
void EVP_DecodeInit(EVP_ENCODE_CTX *ctx) | ||
{ | ||
/* Only ctx->num is used during decoding. */ | ||
ctx->num = 0; | ||
ctx->length = 0; | ||
ctx->line_num = 0; | ||
ctx->expect_nl = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So flags don't need initialization? If so how come? I mean if you rely on structure being memset to 0, then you wouldn't need any of initializations. But if you zero them explicitly, then flags deserve it as much, don't they?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The structure is zalloc'd so none of these need initialisation. I removed them all.
Fix up commits pushed addressing the feedback so far. |
crypto/srp/srp_vfy.c
Outdated
EVP_ENCODE_CTX *ctx; | ||
int outl = 0, outl2 = 0; | ||
size_t size, padsize; | ||
const unsigned char *pad = (unsigned char *)"00"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor nit, (const unsigned char *)"00"
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
crypto/srp/srp_vfy.c
Outdated
++src; | ||
size = strlen(src); | ||
padsize = 4 - (size & 3); | ||
padsize &= 3; | ||
|
||
/* Four bytes in src become three bytes output. */ | ||
if (size > INT_MAX || (size / 4) * 3 > alen) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm, one can actually wonder if (size/4)*3 is actually accurate. After all, we'll pre-pad the string, so we do need space for ((size + padsize) / 4) * 3.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
crypto/evp/encode.c
Outdated
ctx->num = 0; | ||
ctx->length = 0; | ||
ctx->line_num = 0; | ||
ctx->expect_nl = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While you are at it, would you leave a commentary that everything is zalloc-ed [so that no member zeroing is required]?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I changed my mind and put it back to zeroing everything explicitly and added a line to additionally zero the flags. The reason is that it occurred to me that some applications might be reusing an EVP_ENCODE_CTX. This would have worked before but not after I removed the explicit zeroing.
New fixup commits pushed addressing latest feedback. |
osx build complains about memory leak... |
I can't recreate it locally but I think I've figured out the problem. Fixup commit pushed. Let's see if the CIs think I've fixed it too. |
Travis is now passing. |
Pushed. Thanks. |
Previously they were using EVP_EncodeBlock/EVP_DecodeBlock. These are low level functions that do not handle padding characters. This was causing the SRP code to fail. One side effect of using EVP_EncodeUpdate is that it inserts newlines which is not what we need in SRP so we add a flag to avoid that. Reviewed-by: Andy Polyakov <appro@openssl.org> (Merged from #5925)
Historically we used to implement standalone base64 code for SRP. This was replaced by commit 3d3f21a with the standard base64 processing code. However, the SRP base64 code was designed to be compatible with other SRP libraries (notably libsrp, but also others) that use a variant of standard base64. Specifically a different alphabet is used and no padding '=' characters are used. Instead 0 padding is added to the front of the string. By changing to standard base64 we change the behaviour of the API which may impact interoperability. It also means that SRP verifier files created prior to 1.1.1 would not be readable in 1.1.1 and vice versa. Instead we expand our standard base64 processing with the capability to be able to read and generate the SRP base64 variant. Reviewed-by: Andy Polyakov <appro@openssl.org> (Merged from #5925)
Reviewed-by: Andy Polyakov <appro@openssl.org> (Merged from #5925)
Historically we used to implement standalone base64 code for SRP. This
was replaced by commit 3d3f21a with the standard base64 processing code.
SRP has been broken since that time. The immediate issue is that the code was changed to use the low level EVP_EncodeBlock() and EVP_DecodeBlock() functions. Since these are low level functions they do not handle padding. This is done in the higher level EVP_(Encode|Decode)(Update|Final)() functions. This means that when decoding data the binary value does not match the originally encoded value causing SRP connection failures.
This problem caused me to take a deeper look at the SRP base64 code and revealed some more fundamental problems. The SRP base64 code was designed to be compatible with other SRP
libraries (notably libsrp, but also others) that use a variant of standard
base64. Specifically a different alphabet is used and no padding '='
characters are used. Instead 0 padding is added to the front of the string.
By changing to standard base64 we change the behaviour of the API which may
impact interoperability. It also means that SRP verifier files created prior
to 1.1.1 would not be readable in 1.1.1 and vice versa.
This PR expands the standard base64 processing code with the capability to be able to read and generate the SRP base64 variant.
The fact that this problem went undetected for so long is indicative of poor (i.e. non-existent) tests in this area. Therefore I have also added some SRP tests which would have caught these issues.