Skip to content
This repository was archived by the owner on Jun 19, 2020. It is now read-only.

Check for SHA1 collision attempts #49

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ext/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
Makefile*
*.so
asn1c/
sha1collisiondetection/
3 changes: 3 additions & 0 deletions ext/README
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ cd ..
asn1c/asn1c/asn1c -S asn1c/skeletons -pdu=all -pdu=Certificate -fwide-types *.asn1
rm converter-sample.c

git clone https://github.com/cr-marcstevens/sha1collisiondetection.git
cp sha1collisiondetection/lib/* .

# RFC3280 has a Time type which will cause the compiler to create a Time.h
# file. This will conflict with <time.h> on a case insensitive filesystem.
# You can work around this problem with this hack:
Expand Down
38 changes: 31 additions & 7 deletions ext/asn1validator.c → ext/certlint_ext.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
/*
* Ruby bindings for interfacing with asn1c-generated ASN.1 PDU parsing code.
* Copyright (c) 2016 Matt Palmer <[email protected]>
* Ruby bindings for a variety of C libraries used for various parts of
* certlint's work. See README in this directory for build instructions and
* references to the library code used.
*
* Copyright (c) 2016,2017 Matt Palmer <[email protected]>
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not
* use this file except in compliance with the License. A copy of the License
Expand All @@ -21,6 +24,7 @@
#include <errno.h>

#include <asn_application.h>
#include "sha1collisiondetection/lib/sha1.h"

extern asn_TYPE_descriptor_t *asn_pdu_collection[];

Expand All @@ -36,7 +40,7 @@ static asn_TYPE_descriptor_t *asn1pdu_type_descriptor(VALUE pdu_type) {
}
pdu++;
}

rb_raise(rb_eArgError, "Unknown PDU type");
return NULL; /* Unreachable, we hope */
}
Expand Down Expand Up @@ -110,13 +114,13 @@ static VALUE asn1pdu_to_der(VALUE self) {
VALUE t = rb_iv_get(self, "@pdu_type");
void *pdu_structure = asn1pdu_decode_pdu(d, t);
asn_enc_rval_t rv;

rv.encoded = -1;

while (rv.encoded == -1) {
if (derbuf) free(derbuf);
derlen *= 2;

derbuf = malloc(derlen);
if (derbuf == NULL) {
rb_raise(rb_eNoMemError, "Unable to allocate memory for derbuf");
Expand All @@ -127,15 +131,35 @@ static VALUE asn1pdu_to_der(VALUE self) {
}

ASN_STRUCT_FREE(*asn1pdu_type_descriptor(t), pdu_structure);

return rb_str_new(derbuf, rv.encoded);
}

void Init_asn1validator() {
static VALUE sha1collision_check(VALUE self, VALUE data) {
SHA1_CTX ctx;
unsigned char hash[20];
SHA1DCInit(&ctx);
int iscoll;

SHA1DCSetSafeHash(&ctx, 0);
SHA1DCSetUseUBC(&ctx, 0);
SHA1DCUpdate(&ctx, RSTRING_PTR(data), RSTRING_LEN(data));
iscoll = SHA1DCFinal(hash,&ctx);

if (iscoll) {
return Qtrue;
} else {
return Qfalse;
}
}

void Init_certlint_ext() {
mod_certlint = rb_define_module("CertLint");
class_certlint_asn1pdu = rb_define_class_under(mod_certlint, "ASN1Validator", rb_cObject);

rb_define_method(class_certlint_asn1pdu, "initialize", asn1pdu_initialize, 2);
rb_define_method(class_certlint_asn1pdu, "check_constraints", asn1pdu_check_constraints, 0);
rb_define_method(class_certlint_asn1pdu, "to_der", asn1pdu_to_der, 0);

rb_define_singleton_method(mod_certlint, "sha1_collision?", sha1collision_check, 1);
}
4 changes: 2 additions & 2 deletions ext/extconf.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
require "mkmf"

dir_config("asn1validator")
dir_config("certlint_ext")

$srcs = Dir["*.c"]

create_makefile("asn1validator")
create_makefile("certlint_ext")
1 change: 1 addition & 0 deletions lib/certlint.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
require 'certlint/pemlint'
require 'certlint/namelint'
require 'certlint/generalnames'
require 'certlint/sha1lint'
5 changes: 4 additions & 1 deletion lib/certlint/certlint.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# express or implied. See the License for the specific language governing
# permissions and limitations under the License.
require 'rubygems'
require 'asn1validator'
require 'certlint_ext'
require 'openssl'

require_relative 'namelint'
Expand Down Expand Up @@ -262,6 +262,9 @@ def self.lint(der)
return messages
end

# Check for SHAnanigans
messages += SHA1Lint.lint(der)

# Check time fields
OpenSSL::ASN1.traverse(der) do |_depth, offset, header_len, length, _constructed, tag_class, tag|
start_c = offset + header_len
Expand Down
28 changes: 28 additions & 0 deletions lib/certlint/sha1lint.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/ruby -Eutf-8:utf-8
# encoding: UTF-8
# Copyright 2017 Matt Palmer <[email protected]>. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You may not
# use this file except in compliance with the License. A copy of the License
# is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is distributed on
# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
# express or implied. See the License for the specific language governing
# permissions and limitations under the License.

module CertLint
class SHA1Lint
def self.lint(der)
messages = []

if CertLint.sha1_collision?(der.to_s)
messages << 'E: SHA1 collision attempt detected'
end

messages
end
end
end