34
34
import java .util .Collections ;
35
35
import java .util .Enumeration ;
36
36
import java .util .List ;
37
+ import java .util .Locale ;
37
38
import java .util .Optional ;
38
39
import javax .crypto .KeyAgreement ;
39
40
import org .bouncycastle .asn1 .ASN1EncodableVector ;
63
64
import org .jruby .RubyModule ;
64
65
import org .jruby .RubyObject ;
65
66
import org .jruby .RubyString ;
67
+ import org .jruby .RubySymbol ;
66
68
import org .jruby .anno .JRubyClass ;
67
69
import org .jruby .anno .JRubyMethod ;
68
70
import org .jruby .exceptions .RaiseException ;
@@ -695,6 +697,14 @@ public RubyString to_pem(ThreadContext context, final IRubyObject[] args) {
695
697
}
696
698
}
697
699
700
+ private enum PointConversion {
701
+ COMPRESSED , UNCOMPRESSED , HYBRID ;
702
+
703
+ String toRubyString () {
704
+ return super .toString ().toLowerCase (Locale .ROOT );
705
+ }
706
+ }
707
+
698
708
@ JRubyClass (name = "OpenSSL::PKey::EC::Group" )
699
709
public static final class Group extends RubyObject {
700
710
@@ -713,6 +723,9 @@ static void createGroup(final Ruby runtime, final RubyClass EC, final RubyClass
713
723
714
724
private transient PKeyEC key ;
715
725
private ECParameterSpec paramSpec ;
726
+
727
+ private PointConversion conversionForm = PointConversion .UNCOMPRESSED ;
728
+
716
729
private RubyString curve_name ;
717
730
718
731
public Group (Ruby runtime , RubyClass type ) {
@@ -725,11 +738,6 @@ public Group(Ruby runtime, RubyClass type) {
725
738
this .paramSpec = key .publicKey .getParams ();
726
739
}
727
740
728
- private String getCurveName () {
729
- if (key != null ) return key .getCurveName ();
730
- return curve_name .toString ();
731
- }
732
-
733
741
@ JRubyMethod (rest = true , visibility = Visibility .PRIVATE )
734
742
public IRubyObject initialize (final ThreadContext context , final IRubyObject [] args ) {
735
743
final Ruby runtime = context .runtime ;
@@ -743,13 +751,20 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a
743
751
return this ;
744
752
}
745
753
746
- this .curve_name = (( RubyString ) arg );
754
+ this .curve_name = arg . convertToString ( );
747
755
748
- // TODO PEM/DER parsing not implemented
756
+ final ECNamedCurveParameterSpec ecCurveParamSpec = ECNamedCurveTable .getParameterSpec (curve_name .toString ());
757
+ final EllipticCurve curve = EC5Util .convertCurve (ecCurveParamSpec .getCurve (), ecCurveParamSpec .getSeed ());
758
+ this .paramSpec = EC5Util .convertSpec (curve , ecCurveParamSpec );
749
759
}
750
760
return this ;
751
761
}
752
762
763
+ private String getCurveName () {
764
+ if (key != null ) return key .getCurveName ();
765
+ return curve_name .toString ();
766
+ }
767
+
753
768
@ Override
754
769
@ JRubyMethod (name = { "==" , "eql?" })
755
770
public IRubyObject op_equal (final ThreadContext context , final IRubyObject obj ) {
@@ -831,13 +846,35 @@ public RubyString to_pem(final ThreadContext context, final IRubyObject[] args)
831
846
}
832
847
}
833
848
834
- final EllipticCurve getCurve () {
849
+ EllipticCurve getCurve () {
835
850
if (paramSpec == null ) {
836
851
paramSpec = getParamSpec (getCurveName ());
837
852
}
838
853
return paramSpec .getCurve ();
839
854
}
840
855
856
+ @ JRubyMethod
857
+ public RubySymbol point_conversion_form (final ThreadContext context ) {
858
+ return context .runtime .newSymbol (this .conversionForm .toRubyString ());
859
+ }
860
+
861
+ @ JRubyMethod (name = "point_conversion_form=" )
862
+ public IRubyObject set_point_conversion_form (final ThreadContext context , final IRubyObject form ) {
863
+ this .conversionForm = parse_point_conversion_form (context .runtime , form );
864
+ return form ;
865
+ }
866
+
867
+ static PointConversion parse_point_conversion_form (final Ruby runtime , final IRubyObject form ) {
868
+ if (form instanceof RubySymbol ) {
869
+ final String pointConversionForm = ((RubySymbol ) form ).asJavaString ();
870
+ if ("uncompressed" .equals (pointConversionForm )) return PointConversion .UNCOMPRESSED ;
871
+ if ("compressed" .equals (pointConversionForm )) return PointConversion .COMPRESSED ;
872
+ if ("hybrid" .equals (pointConversionForm )) return PointConversion .HYBRID ;
873
+ }
874
+ throw runtime .newArgumentError ("unsupported point conversion form: " + form .inspect ());
875
+ }
876
+
877
+
841
878
// @Override
842
879
// @JRubyMethod
843
880
// @SuppressWarnings("unchecked")
@@ -874,14 +911,12 @@ public Point(Ruby runtime, RubyClass type) {
874
911
super (runtime , type );
875
912
}
876
913
877
- // private transient ECPublicKey publicKey;
878
914
private ECPoint point ;
879
915
//private int bitLength;
880
916
private Group group ;
881
917
882
918
Point (Ruby runtime , ECPublicKey publicKey , Group group ) {
883
919
this (runtime , _EC (runtime ).getClass ("Point" ));
884
- //this.publicKey = publicKey;
885
920
this .point = publicKey .getW ();
886
921
this .group = group ;
887
922
}
@@ -914,7 +949,12 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a
914
949
this .group = (Group ) arg ;
915
950
}
916
951
if ( argc == 2 ) { // (group, bn)
917
- final byte [] encoded = ((BN ) args [1 ]).getValue ().abs ().toByteArray ();
952
+ final byte [] encoded ;
953
+ if (args [1 ] instanceof BN ) {
954
+ encoded = ((BN ) args [1 ]).getValue ().abs ().toByteArray ();
955
+ } else {
956
+ encoded = args [1 ].convertToString ().getBytes ();
957
+ }
918
958
try {
919
959
this .point = ECPointUtil .decodePoint (group .getCurve (), encoded );
920
960
}
@@ -951,15 +991,54 @@ private ECPoint asECPoint() {
951
991
}
952
992
953
993
private int bitLength () {
994
+ assert group != null ;
995
+ assert group .paramSpec != null ;
954
996
return group .paramSpec .getOrder ().bitLength ();
955
997
}
956
998
999
+ private PointConversion getPointConversionForm () {
1000
+ if (group == null ) return null ;
1001
+ return group .conversionForm ;
1002
+ }
1003
+
957
1004
@ JRubyMethod
958
1005
public BN to_bn (final ThreadContext context ) {
959
- final byte [] encoded = encode (bitLength (), point );
1006
+ return toBN (context , getPointConversionForm ()); // group.point_conversion_form
1007
+ }
1008
+
1009
+ @ JRubyMethod
1010
+ public BN to_bn (final ThreadContext context , final IRubyObject conversion_form ) {
1011
+ return toBN (context , Group .parse_point_conversion_form (context .runtime , conversion_form ));
1012
+ }
1013
+
1014
+ private BN toBN (final ThreadContext context , final PointConversion conversionForm ) {
1015
+ final byte [] encoded = encodePoint (conversionForm );
960
1016
return BN .newBN (context .runtime , new BigInteger (1 , encoded ));
961
1017
}
962
1018
1019
+ private byte [] encodePoint (final PointConversion conversionForm ) {
1020
+ final byte [] encoded ;
1021
+ switch (conversionForm ) {
1022
+ case UNCOMPRESSED :
1023
+ encoded = encodeUncompressed (bitLength (), point );
1024
+ break ;
1025
+ case COMPRESSED :
1026
+ encoded = encodeCompressed (point );
1027
+ break ;
1028
+ case HYBRID :
1029
+ throw getRuntime ().newNotImplementedError (":hybrid compression not implemented" );
1030
+ default :
1031
+ throw new AssertionError ("unexpected conversion form: " + conversionForm );
1032
+ }
1033
+ return encoded ;
1034
+ }
1035
+
1036
+ @ JRubyMethod
1037
+ public IRubyObject to_octet_string (final ThreadContext context , final IRubyObject conversion_form ) {
1038
+ final PointConversion conversionForm = Group .parse_point_conversion_form (context .runtime , conversion_form );
1039
+ return StringHelper .newString (context .runtime , encodePoint (conversionForm ));
1040
+ }
1041
+
963
1042
private boolean isInfinity () {
964
1043
return point == ECPoint .POINT_INFINITY ;
965
1044
}
@@ -986,34 +1065,49 @@ public IRubyObject inspect() {
986
1065
}
987
1066
988
1067
static byte [] encode (final ECPublicKey pubKey ) {
989
- return encode (pubKey .getParams ().getOrder ().bitLength (), pubKey .getW ());
1068
+ return encodeUncompressed (pubKey .getParams ().getOrder ().bitLength (), pubKey .getW ());
990
1069
}
991
1070
992
- private static byte [] encode (final int bitLength , final ECPoint point ) {
993
- if ( point == ECPoint .POINT_INFINITY ) return new byte [1 ];
1071
+ private static byte [] encodeUncompressed (final int fieldSize , final ECPoint point ) {
1072
+ if (point == ECPoint .POINT_INFINITY ) return new byte [1 ];
994
1073
995
- final int bytesLength = (bitLength + 7 ) / 8 ;
996
- byte [] encoded = new byte [1 + bytesLength + bytesLength ];
1074
+ final int expLength = (fieldSize + 7 ) / 8 ;
1075
+
1076
+ byte [] encoded = new byte [1 + expLength + expLength ];
997
1077
998
1078
encoded [0 ] = 0x04 ;
999
1079
1080
+ addIntBytes (point .getAffineX (), expLength , encoded , 1 );
1081
+ addIntBytes (point .getAffineY (), expLength , encoded , 1 + expLength );
1082
+
1083
+ return encoded ;
1084
+ }
1085
+
1086
+ private static byte [] encodeCompressed (final ECPoint point ) {
1087
+ if (point == ECPoint .POINT_INFINITY ) return new byte [1 ];
1088
+
1089
+ final int bytesLength = point .getAffineX ().bitLength () / 8 + 1 ;
1090
+
1091
+ byte [] encoded = new byte [1 + bytesLength ];
1092
+
1093
+ encoded [0 ] = (byte ) (point .getAffineY ().testBit (0 ) ? 0x03 : 0x02 );
1094
+
1000
1095
addIntBytes (point .getAffineX (), bytesLength , encoded , 1 );
1001
- addIntBytes (point .getAffineY (), bytesLength , encoded , 1 + bytesLength );
1002
1096
1003
1097
return encoded ;
1004
1098
}
1005
1099
1006
- private static void addIntBytes (BigInteger i , final int length , final byte [] dest , final int destOffset ) {
1007
- final byte [] bytes = i .toByteArray ();
1100
+ private static void addIntBytes (final BigInteger value , final int length , final byte [] dest , final int destOffset ) {
1101
+ final byte [] in = value .toByteArray ();
1008
1102
1009
- if (length < bytes .length ) {
1010
- System .arraycopy (bytes , bytes .length - length , dest , destOffset , length );
1103
+ if (length < in .length ) {
1104
+ System .arraycopy (in , in .length - length , dest , destOffset , length );
1011
1105
}
1012
- else if (length > bytes .length ) {
1013
- System .arraycopy (bytes , 0 , dest , destOffset + (length - bytes .length ), bytes .length );
1106
+ else if (length > in .length ) {
1107
+ System .arraycopy (in , 0 , dest , destOffset + (length - in .length ), in .length );
1014
1108
}
1015
1109
else {
1016
- System .arraycopy (bytes , 0 , dest , destOffset , length );
1110
+ System .arraycopy (in , 0 , dest , destOffset , length );
1017
1111
}
1018
1112
}
1019
1113
0 commit comments