Crypt::PK::X25519 + Crypt::PK::Ed25519 (.pm + .t) key loading and basic stuff
Karel Miko
4 years ago
147 | 147 | ecc_key key; |
148 | 148 | } *Crypt__PK__ECC; |
149 | 149 | |
150 | struct curve25519_struct { | |
150 | typedef struct ed25519_struct { /* used by Crypt::PK::Ed25519 */ | |
151 | 151 | prng_state pstate; |
152 | 152 | int pindex; |
153 | 153 | curve25519_key key; |
154 | } ; | |
155 | typedef struct curve25519_struct *Crypt__PK__Ed25519; /* used by Crypt::PK::Ed25519 */ | |
156 | typedef struct curve25519_struct *Crypt__PK__X25519; /* used by Crypt::PK::X25519 */ | |
154 | int initialized; | |
155 | } *Crypt__PK__Ed25519; | |
156 | ||
157 | typedef struct x25519_struct { /* used by Crypt::PK::X25519 */ | |
158 | prng_state pstate; | |
159 | int pindex; | |
160 | curve25519_key key; | |
161 | int initialized; | |
162 | } *Crypt__PK__X25519; | |
157 | 163 | |
158 | 164 | int mp_tohex_with_leading_zero(mp_int * a, char *str, int maxlen, int minlen) { |
159 | 165 | int len, rv; |
6 | 6 | CODE: |
7 | 7 | { |
8 | 8 | int rv; |
9 | Newz(0, RETVAL, 1, struct curve25519_struct); | |
9 | Newz(0, RETVAL, 1, struct ed25519_struct); | |
10 | 10 | if (!RETVAL) croak("FATAL: Newz failed"); |
11 | RETVAL->initialized = 0; | |
11 | 12 | RETVAL->pindex = find_prng("chacha20"); |
12 | 13 | RETVAL->key.type = -1; |
13 | 14 | if (RETVAL->pindex == -1) { |
31 | 32 | /* gen the key */ |
32 | 33 | rv = ed25519_make_key(&self->pstate, self->pindex, &self->key); |
33 | 34 | if (rv != CRYPT_OK) croak("FATAL: ed25519_make_key failed: %s", error_to_string(rv)); |
35 | self->initialized = 1; | |
34 | 36 | XPUSHs(ST(0)); /* return self */ |
35 | 37 | } |
36 | 38 | |
46 | 48 | self->key.type = -1; |
47 | 49 | rv = ed25519_import(data, (unsigned long)data_len, &self->key); |
48 | 50 | if (rv != CRYPT_OK) croak("FATAL: ed25519_import failed: %s", error_to_string(rv)); |
51 | self->initialized = 1; | |
49 | 52 | XPUSHs(ST(0)); /* return self */ |
50 | 53 | } |
51 | 54 | |
64 | 67 | self->key.type = -1; |
65 | 68 | rv = ed25519_import_pkcs8(data, (unsigned long)data_len, pwd, (unsigned long)pwd_len, &self->key); |
66 | 69 | if (rv != CRYPT_OK) croak("FATAL: ed25519_import_pkcs8 failed: %s", error_to_string(rv)); |
70 | self->initialized = 1; | |
67 | 71 | XPUSHs(ST(0)); /* return self */ |
68 | 72 | } |
69 | 73 | |
79 | 83 | self->key.type = -1; |
80 | 84 | rv = ed25519_import_x509(data, (unsigned long)data_len, &self->key); |
81 | 85 | if (rv != CRYPT_OK) croak("FATAL: ed25519_import_x509 failed: %s", error_to_string(rv)); |
82 | XPUSHs(ST(0)); /* return self */ | |
83 | } | |
84 | ||
85 | void | |
86 | _import_key_data(Crypt::PK::Ed25519 self, SV * priv, SV * pub) | |
87 | PPCODE: | |
88 | { | |
89 | int rv, type; | |
90 | unsigned char *priv_data=NULL, *pub_data=NULL; | |
91 | STRLEN priv_len=0, pub_len=0; | |
92 | ||
93 | if (SvOK(priv)) { | |
94 | priv_data = (unsigned char *)SvPVbyte(priv, priv_len); | |
95 | } | |
96 | if (SvOK(pub)) { | |
97 | pub_data = (unsigned char *)SvPVbyte(pub, pub_len); | |
98 | } | |
99 | self->key.type = -1; | |
100 | rv = ed25519_set_key(priv_data, (unsigned long)priv_len, pub_data, (unsigned long)pub_len, &self->key); | |
101 | if (rv != CRYPT_OK) croak("FATAL: ed25519_set_key failed: %s", error_to_string(rv)); | |
86 | self->initialized = 1; | |
87 | XPUSHs(ST(0)); /* return self */ | |
88 | } | |
89 | ||
90 | void | |
91 | _import_raw(Crypt::PK::Ed25519 self, SV * key, int which) | |
92 | PPCODE: | |
93 | { | |
94 | int rv; | |
95 | unsigned char *key_data=NULL; | |
96 | STRLEN key_len=0; | |
97 | ||
98 | if (SvOK(key)) { | |
99 | key_data = (unsigned char *)SvPVbyte(key, key_len); | |
100 | } | |
101 | if (which == 0) { | |
102 | rv = ed25519_import_raw(key_data, (unsigned long)key_len, PK_PUBLIC, &self->key); | |
103 | } | |
104 | else if (which == 1) { | |
105 | rv = ed25519_import_raw(key_data, (unsigned long)key_len, PK_PRIVATE, &self->key); | |
106 | } | |
107 | else { | |
108 | croak("FATAL: import_raw invalid type '%d'", which); | |
109 | } | |
110 | if (rv != CRYPT_OK) croak("FATAL: ed25519_import_raw failed: %s", error_to_string(rv)); | |
111 | self->initialized = 1; | |
102 | 112 | XPUSHs(ST(0)); /* return self */ |
103 | 113 | } |
104 | 114 | |
105 | 115 | int |
106 | 116 | is_private(Crypt::PK::Ed25519 self) |
107 | 117 | CODE: |
108 | if (self->key.type == -1) XSRETURN_UNDEF; | |
118 | if (self->initialized == 0 || self->key.type == -1) XSRETURN_UNDEF; | |
109 | 119 | RETVAL = (self->key.type == PK_PRIVATE) ? 1 : 0; |
110 | 120 | OUTPUT: |
111 | 121 | RETVAL |
114 | 124 | key2hash(Crypt::PK::Ed25519 self) |
115 | 125 | PREINIT: |
116 | 126 | HV *rv_hash; |
117 | char buf[20001]; | |
127 | char buf[256]; | |
128 | unsigned long blen; | |
118 | 129 | SV **not_used; |
119 | CODE: | |
120 | if (self->key.type == -1) XSRETURN_UNDEF; | |
130 | int rv; | |
131 | CODE: | |
132 | if (self->initialized == 0) XSRETURN_UNDEF; | |
121 | 133 | rv_hash = newHV(); |
122 | 134 | /* priv */ |
123 | 135 | if (self->key.type == PK_PRIVATE) { |
124 | not_used = hv_store(rv_hash, "priv", 4, newSVpv(self->key.priv, sizeof(self->key.priv)), 0); | |
136 | blen = sizeof(buf); | |
137 | rv = base16_encode(self->key.priv, sizeof(self->key.priv), buf, &blen, 0); | |
138 | if (rv != CRYPT_OK) croak("FATAL: base16_encode failed: %s", error_to_string(rv)); | |
139 | not_used = hv_store(rv_hash, "priv", 4, newSVpv(buf, blen), 0); | |
125 | 140 | } |
126 | 141 | else { |
127 | 142 | not_used = hv_store(rv_hash, "priv", 4, &PL_sv_undef, 0); |
128 | 143 | } |
129 | 144 | /* pub */ |
130 | not_used = hv_store(rv_hash, "pub", 3, newSVpv(self->key.pub, sizeof(self->key.pub)), 0); | |
131 | /* algo */ | |
132 | not_used = hv_store(rv_hash, "algo", 4, newSVpv("ed25519", 0), 0); | |
145 | blen = sizeof(buf); | |
146 | rv = base16_encode(self->key.pub, sizeof(self->key.pub), buf, &blen, 0); | |
147 | if (rv != CRYPT_OK) croak("FATAL: base16_encode failed: %s", error_to_string(rv)); | |
148 | not_used = hv_store(rv_hash, "pub", 3, newSVpv(buf, blen), 0); | |
149 | /* curve */ | |
150 | not_used = hv_store(rv_hash, "curve", 5, newSVpv("ed25519", 0), 0); | |
133 | 151 | LTC_UNUSED_PARAM(not_used); |
134 | 152 | RETVAL = newRV_noinc((SV*)rv_hash); |
135 | 153 | OUTPUT: |
150 | 168 | RETVAL = newSVpvn((char*)out, out_len); |
151 | 169 | } |
152 | 170 | else if (strnEQ(type, "public", 6)) { |
153 | rv = ed25519_export(out, &out_len, PK_PUBLIC|PK_STD, &self->key); | |
154 | if (rv != CRYPT_OK) croak("FATAL: ed25519_export(PK_PUBLIC|PK_STD) failed: %s", error_to_string(rv)); | |
171 | rv = ed25519_export(out, &out_len, PK_PUBLIC, &self->key); | |
172 | if (rv != CRYPT_OK) croak("FATAL: ed25519_export(PK_PUBLIC) failed: %s", error_to_string(rv)); | |
155 | 173 | RETVAL = newSVpvn((char*)out, out_len); |
156 | 174 | } |
157 | 175 | else { |
221 | 239 | DESTROY(Crypt::PK::Ed25519 self) |
222 | 240 | CODE: |
223 | 241 | Safefree(self); |
224 |
6 | 6 | CODE: |
7 | 7 | { |
8 | 8 | int rv; |
9 | Newz(0, RETVAL, 1, struct curve25519_struct); | |
9 | Newz(0, RETVAL, 1, struct x25519_struct); | |
10 | 10 | if (!RETVAL) croak("FATAL: Newz failed"); |
11 | RETVAL->initialized = 0; | |
11 | 12 | RETVAL->pindex = find_prng("chacha20"); |
12 | 13 | RETVAL->key.type = -1; |
13 | 14 | if (RETVAL->pindex == -1) { |
31 | 32 | /* gen the key */ |
32 | 33 | rv = x25519_make_key(&self->pstate, self->pindex, &self->key); |
33 | 34 | if (rv != CRYPT_OK) croak("FATAL: x25519_make_key failed: %s", error_to_string(rv)); |
35 | self->initialized = 1; | |
34 | 36 | XPUSHs(ST(0)); /* return self */ |
35 | 37 | } |
36 | 38 | |
46 | 48 | self->key.type = -1; |
47 | 49 | rv = x25519_import(data, (unsigned long)data_len, &self->key); |
48 | 50 | if (rv != CRYPT_OK) croak("FATAL: x25519_import failed: %s", error_to_string(rv)); |
51 | self->initialized = 1; | |
49 | 52 | XPUSHs(ST(0)); /* return self */ |
50 | 53 | } |
51 | 54 | |
64 | 67 | self->key.type = -1; |
65 | 68 | rv = x25519_import_pkcs8(data, (unsigned long)data_len, pwd, (unsigned long)pwd_len, &self->key); |
66 | 69 | if (rv != CRYPT_OK) croak("FATAL: x25519_import_pkcs8 failed: %s", error_to_string(rv)); |
70 | self->initialized = 1; | |
67 | 71 | XPUSHs(ST(0)); /* return self */ |
68 | 72 | } |
69 | 73 | |
79 | 83 | self->key.type = -1; |
80 | 84 | rv = x25519_import_x509(data, (unsigned long)data_len, &self->key); |
81 | 85 | if (rv != CRYPT_OK) croak("FATAL: x25519_import_x509 failed: %s", error_to_string(rv)); |
82 | XPUSHs(ST(0)); /* return self */ | |
83 | } | |
84 | ||
85 | void | |
86 | _import_key_data(Crypt::PK::X25519 self, SV * priv, SV * pub) | |
87 | PPCODE: | |
88 | { | |
89 | int rv, type; | |
90 | unsigned char *priv_data=NULL, *pub_data=NULL; | |
91 | STRLEN priv_len=0, pub_len=0; | |
92 | ||
93 | if (SvOK(priv)) { | |
94 | priv_data = (unsigned char *)SvPVbyte(priv, priv_len); | |
95 | } | |
96 | if (SvOK(pub)) { | |
97 | pub_data = (unsigned char *)SvPVbyte(pub, pub_len); | |
98 | } | |
99 | self->key.type = -1; | |
100 | rv = x25519_set_key(priv_data, (unsigned long)priv_len, pub_data, (unsigned long)pub_len, &self->key); | |
101 | if (rv != CRYPT_OK) croak("FATAL: x25519_set_key failed: %s", error_to_string(rv)); | |
86 | self->initialized = 1; | |
87 | XPUSHs(ST(0)); /* return self */ | |
88 | } | |
89 | ||
90 | void | |
91 | _import_raw(Crypt::PK::X25519 self, SV * key, int which) | |
92 | PPCODE: | |
93 | { | |
94 | int rv; | |
95 | unsigned char *key_data=NULL; | |
96 | STRLEN key_len=0; | |
97 | ||
98 | if (SvOK(key)) { | |
99 | key_data = (unsigned char *)SvPVbyte(key, key_len); | |
100 | } | |
101 | if (which == 0) { | |
102 | rv = x25519_import_raw(key_data, (unsigned long)key_len, PK_PUBLIC, &self->key); | |
103 | } | |
104 | else if (which == 1) { | |
105 | rv = x25519_import_raw(key_data, (unsigned long)key_len, PK_PRIVATE, &self->key); | |
106 | } | |
107 | else { | |
108 | croak("FATAL: import_raw invalid type '%d'", which); | |
109 | } | |
110 | if (rv != CRYPT_OK) croak("FATAL: x25519_import_raw failed: %s", error_to_string(rv)); | |
111 | self->initialized = 1; | |
102 | 112 | XPUSHs(ST(0)); /* return self */ |
103 | 113 | } |
104 | 114 | |
114 | 124 | key2hash(Crypt::PK::X25519 self) |
115 | 125 | PREINIT: |
116 | 126 | HV *rv_hash; |
117 | char buf[20001]; | |
127 | char buf[256]; | |
128 | unsigned long blen; | |
118 | 129 | SV **not_used; |
119 | CODE: | |
120 | if (self->key.type == -1) XSRETURN_UNDEF; | |
130 | int rv; | |
131 | CODE: | |
132 | if (self->initialized == 0) XSRETURN_UNDEF; | |
121 | 133 | rv_hash = newHV(); |
122 | 134 | /* priv */ |
123 | 135 | if (self->key.type == PK_PRIVATE) { |
124 | not_used = hv_store(rv_hash, "priv", 4, newSVpv(self->key.priv, sizeof(self->key.priv)), 0); | |
136 | blen = sizeof(buf); | |
137 | rv = base16_encode(self->key.priv, sizeof(self->key.priv), buf, &blen, 0); | |
138 | if (rv != CRYPT_OK) croak("FATAL: base16_encode failed: %s", error_to_string(rv)); | |
139 | not_used = hv_store(rv_hash, "priv", 4, newSVpv(buf, blen), 0); | |
125 | 140 | } |
126 | 141 | else { |
127 | 142 | not_used = hv_store(rv_hash, "priv", 4, &PL_sv_undef, 0); |
128 | 143 | } |
129 | 144 | /* pub */ |
130 | not_used = hv_store(rv_hash, "pub", 3, newSVpv(self->key.pub, sizeof(self->key.pub)), 0); | |
131 | /* algo */ | |
132 | not_used = hv_store(rv_hash, "algo", 4, newSVpv("x25519", 0), 0); | |
145 | blen = sizeof(buf); | |
146 | rv = base16_encode(self->key.pub, sizeof(self->key.pub), buf, &blen, 0); | |
147 | if (rv != CRYPT_OK) croak("FATAL: base16_encode failed: %s", error_to_string(rv)); | |
148 | not_used = hv_store(rv_hash, "pub", 3, newSVpv(buf, blen), 0); | |
149 | /* curve */ | |
150 | not_used = hv_store(rv_hash, "curve", 5, newSVpv("x25519", 0), 0); | |
133 | 151 | LTC_UNUSED_PARAM(not_used); |
134 | 152 | RETVAL = newRV_noinc((SV*)rv_hash); |
135 | 153 | OUTPUT: |
150 | 168 | RETVAL = newSVpvn((char*)out, out_len); |
151 | 169 | } |
152 | 170 | else if (strnEQ(type, "public", 6)) { |
153 | rv = x25519_export(out, &out_len, PK_PUBLIC|PK_STD, &self->key); | |
154 | if (rv != CRYPT_OK) croak("FATAL: x25519_export(PK_PUBLIC|PK_STD) failed: %s", error_to_string(rv)); | |
171 | rv = x25519_export(out, &out_len, PK_PUBLIC, &self->key); | |
172 | if (rv != CRYPT_OK) croak("FATAL: x25519_export(PK_PUBLIC) failed: %s", error_to_string(rv)); | |
155 | 173 | RETVAL = newSVpvn((char*)out, out_len); |
156 | 174 | } |
157 | 175 | else { |
0 | package Crypt::PK::Ed25519; | |
1 | ||
2 | use strict; | |
3 | use warnings; | |
4 | our $VERSION = '0.066_001'; | |
5 | ||
6 | require Exporter; our @ISA = qw(Exporter); ### use Exporter 'import'; | |
7 | our %EXPORT_TAGS = ( all => [qw( )] ); | |
8 | our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); | |
9 | our @EXPORT = qw(); | |
10 | ||
11 | use Carp; | |
12 | $Carp::Internal{(__PACKAGE__)}++; | |
13 | use CryptX; | |
14 | use Crypt::PK; | |
15 | use Crypt::Misc qw(read_rawfile encode_b64u decode_b64u encode_b64 decode_b64 pem_to_der der_to_pem); | |
16 | ||
17 | sub new { | |
18 | my $self = shift->_new(); | |
19 | return @_ > 0 ? $self->import_key(@_) : $self; | |
20 | } | |
21 | ||
22 | sub import_key_raw { | |
23 | my ($self, $key, $type) = @_; | |
24 | croak "FATAL: undefined key" unless $key; | |
25 | croak "FATAL: undefined type" unless $type; | |
26 | return $self->_import_raw($key, 1) if $type eq 'private'; | |
27 | return $self->_import_raw($key, 0) if $type eq 'public'; | |
28 | croak "FATAL: invalid key type '$type'"; | |
29 | } | |
30 | ||
31 | sub import_key { | |
32 | my ($self, $key, $password) = @_; | |
33 | local $SIG{__DIE__} = \&CryptX::_croak; | |
34 | croak "FATAL: undefined key" unless $key; | |
35 | ||
36 | # special case | |
37 | if (ref($key) eq 'HASH') { | |
38 | if ($key->{kty} && $key->{kty} eq "OKP" && $key->{crv} && $key->{crv} eq 'Ed25519') { | |
39 | # JWK-like structure e.g. | |
40 | # {"kty":"OKP","crv":"Ed25519","d":"...","x":"..."} | |
41 | return $self->_import_raw(decode_b64u($key->{d}), 1) if $key->{d}; # private | |
42 | return $self->_import_raw(decode_b64u($key->{x}), 0) if $key->{x}; # public | |
43 | } | |
44 | if ($key->{curve} && $key->{curve} eq "ed25519" && ($key->{priv} || $key->{pub})) { | |
45 | # hash exported via key2hash | |
46 | return $self->_import_raw(pack("H*", $key->{priv}), 1) if $key->{priv}; | |
47 | return $self->_import_raw(pack("H*", $key->{pub}), 0) if $key->{pub}; | |
48 | } | |
49 | croak "FATAL: unexpected Ed25519 key hash"; | |
50 | } | |
51 | ||
52 | my $data; | |
53 | if (ref($key) eq 'SCALAR') { | |
54 | $data = $$key; | |
55 | } | |
56 | elsif (-f $key) { | |
57 | $data = read_rawfile($key); | |
58 | } | |
59 | else { | |
60 | croak "FATAL: non-existing file '$key'"; | |
61 | } | |
62 | croak "FATAL: invalid key data" unless $data; | |
63 | ||
64 | if ($data =~ /-----BEGIN PUBLIC KEY-----(.*?)-----END/sg) { | |
65 | $data = pem_to_der($data, $password); | |
66 | return $self->_import($data); | |
67 | } | |
68 | elsif ($data =~ /-----BEGIN PRIVATE KEY-----(.*?)-----END/sg) { | |
69 | $data = pem_to_der($data, $password); | |
70 | return $self->_import_pkcs8($data, $password); | |
71 | } | |
72 | elsif ($data =~ /-----BEGIN ENCRYPTED PRIVATE KEY-----(.*?)-----END/sg) { | |
73 | $data = pem_to_der($data, $password); | |
74 | return $self->_import_pkcs8($data, $password); | |
75 | } | |
76 | elsif ($data =~ /-----BEGIN ED25519 PRIVATE KEY-----(.*?)-----END/sg) { | |
77 | $data = pem_to_der($data, $password); | |
78 | return $self->_import_pkcs8($data, $password); | |
79 | } | |
80 | elsif ($data =~ /^\s*(\{.*?\})\s*$/s) { # JSON | |
81 | my $h = CryptX::_decode_json("$1"); | |
82 | if ($h->{kty} && $h->{kty} eq "OKP" && $h->{crv} && $h->{crv} eq 'Ed25519') { | |
83 | return $self->_import_raw(decode_b64u($h->{d}), 1) if $h->{d}; # private | |
84 | return $self->_import_raw(decode_b64u($h->{x}), 0) if $h->{x}; # public | |
85 | } | |
86 | } | |
87 | elsif ($data =~ /-----BEGIN CERTIFICATE-----(.*?)-----END CERTIFICATE-----/sg) { | |
88 | $data = pem_to_der($data); | |
89 | return $self->_import_x509($data); | |
90 | } | |
91 | elsif ($data =~ /-----BEGIN OPENSSH PRIVATE KEY-----(.*?)-----END/sg) { | |
92 | #XXX-FIXME-TODO | |
93 | # https://crypto.stackexchange.com/questions/71789/openssh-ed2215-private-key-format | |
94 | # https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.key?annotate=HEAD | |
95 | croak "FATAL: OPENSSH PRIVATE KEY not supported"; | |
96 | } | |
97 | elsif ($data =~ /(ssh-ed25519)\s+(\S+)/) { | |
98 | $data = decode_b64("$2"); | |
99 | my ($typ, $pubkey) = Crypt::PK::_ssh_parse($data); | |
100 | return $self->_import_raw($pubkey, 0) if $typ eq 'ssh-ed25519' && length($pubkey) == 32; | |
101 | } | |
102 | elsif (length($data) == 32) { | |
103 | croak "FATAL: use import_key_raw() to load raw (32 bytes) Ed25519 key"; | |
104 | } | |
105 | else { | |
106 | my $rv = eval { $self->_import($data) } || | |
107 | eval { $self->_import_pkcs8($data, $password) } || | |
108 | eval { $self->_import_x509($data) }; | |
109 | return $rv if $rv; | |
110 | } | |
111 | croak "FATAL: invalid or unsupported Ed25519 key format"; | |
112 | } | |
113 | ||
114 | sub export_key_pem { | |
115 | my ($self, $type, $password, $cipher) = @_; | |
116 | local $SIG{__DIE__} = \&CryptX::_croak; | |
117 | my $key = $self->export_key_der($type||''); | |
118 | return unless $key; | |
119 | return der_to_pem($key, "ED25519 PRIVATE KEY", $password, $cipher) if substr($type, 0, 7) eq 'private'; | |
120 | return der_to_pem($key, "PUBLIC KEY") if substr($type,0, 6) eq 'public'; | |
121 | } | |
122 | ||
123 | sub export_key_jwk { | |
124 | my ($self, $type, $wanthash) = @_; | |
125 | local $SIG{__DIE__} = \&CryptX::_croak; | |
126 | my $kh = $self->key2hash; | |
127 | return unless $kh; | |
128 | my $hash = { kty => "OKP", crv => "Ed25519" }; | |
129 | $hash->{x} = encode_b64u(pack("H*", $kh->{pub})); | |
130 | $hash->{d} = encode_b64u(pack("H*", $kh->{priv})) if $type && $type eq 'private' && $kh->{priv}; | |
131 | return $wanthash ? $hash : CryptX::_encode_json($hash); | |
132 | } | |
133 | ||
134 | sub CLONE_SKIP { 1 } # prevent cloning | |
135 | ||
136 | 1; | |
137 | ||
138 | =pod | |
139 | ||
140 | =head1 NAME | |
141 | ||
142 | Crypt::PK::Ed25519 - Digital signature based on Ed25519 | |
143 | ||
144 | =head1 SYNOPSIS | |
145 | ||
146 | =head1 METHODS | |
147 | ||
148 | =head2 new | |
149 | ||
150 | =head2 generate_key | |
151 | ||
152 | =head2 import_key | |
153 | ||
154 | =head2 import_key_raw | |
155 | ||
156 | =head2 export_key_der | |
157 | ||
158 | =head2 export_key_pem | |
159 | ||
160 | =head2 export_key_jwk | |
161 | ||
162 | =head2 export_key_raw | |
163 | ||
164 | =head2 sign_message | |
165 | ||
166 | =head2 verify_message | |
167 | ||
168 | =head2 is_private | |
169 | ||
170 | =head2 key2hash | |
171 | ||
172 | =head1 SEE ALSO | |
173 | ||
174 | =over | |
175 | ||
176 | =item * L<https://en.wikipedia.org/wiki/EdDSA#Ed25519> | |
177 | ||
178 | =item * L<https://en.wikipedia.org/wiki/Curve25519> | |
179 | ||
180 | =item * L<https://tools.ietf.org/html/rfc8032> | |
181 | ||
182 | =back | |
183 | ||
184 | =cut |
0 | package Crypt::PK::X25519; | |
1 | ||
2 | use strict; | |
3 | use warnings; | |
4 | our $VERSION = '0.066_001'; | |
5 | ||
6 | require Exporter; our @ISA = qw(Exporter); ### use Exporter 'import'; | |
7 | our %EXPORT_TAGS = ( all => [qw( )] ); | |
8 | our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); | |
9 | our @EXPORT = qw(); | |
10 | ||
11 | use Carp; | |
12 | $Carp::Internal{(__PACKAGE__)}++; | |
13 | use CryptX; | |
14 | use Crypt::PK; | |
15 | use Crypt::Misc qw(read_rawfile encode_b64u decode_b64u encode_b64 decode_b64 pem_to_der der_to_pem); | |
16 | ||
17 | sub new { | |
18 | my $self = shift->_new(); | |
19 | return @_ > 0 ? $self->import_key(@_) : $self; | |
20 | } | |
21 | ||
22 | sub import_key_raw { | |
23 | my ($self, $key, $type) = @_; | |
24 | croak "FATAL: undefined key" unless $key; | |
25 | croak "FATAL: undefined type" unless $type; | |
26 | return $self->_import_raw($key, 1) if $type eq 'private'; | |
27 | return $self->_import_raw($key, 0) if $type eq 'public'; | |
28 | croak "FATAL: invalid key type '$type'"; | |
29 | } | |
30 | ||
31 | sub import_key { | |
32 | my ($self, $key, $password) = @_; | |
33 | local $SIG{__DIE__} = \&CryptX::_croak; | |
34 | croak "FATAL: undefined key" unless $key; | |
35 | ||
36 | # special case | |
37 | if (ref($key) eq 'HASH') { | |
38 | if ($key->{kty} && $key->{kty} eq "OKP" && $key->{crv} && $key->{crv} eq 'X25519') { | |
39 | # JWK-like structure e.g. | |
40 | # {"kty":"OKP","crv":"X25519","d":"...","x":"..."} | |
41 | return $self->_import_raw(decode_b64u($key->{d}), 1) if $key->{d}; # private | |
42 | return $self->_import_raw(decode_b64u($key->{x}), 0) if $key->{x}; # public | |
43 | } | |
44 | if ($key->{curve} && $key->{curve} eq "x25519" && ($key->{priv} || $key->{pub})) { | |
45 | # hash exported via key2hash | |
46 | return $self->_import_raw(pack("H*", $key->{priv}), 1) if $key->{priv}; | |
47 | return $self->_import_raw(pack("H*", $key->{pub}), 0) if $key->{pub}; | |
48 | } | |
49 | croak "FATAL: unexpected X25519 key hash"; | |
50 | } | |
51 | ||
52 | my $data; | |
53 | if (ref($key) eq 'SCALAR') { | |
54 | $data = $$key; | |
55 | } | |
56 | elsif (-f $key) { | |
57 | $data = read_rawfile($key); | |
58 | } | |
59 | else { | |
60 | croak "FATAL: non-existing file '$key'"; | |
61 | } | |
62 | croak "FATAL: invalid key data" unless $data; | |
63 | ||
64 | if ($data =~ /-----BEGIN PUBLIC KEY-----(.*?)-----END/sg) { | |
65 | $data = pem_to_der($data, $password); | |
66 | return $self->_import($data); | |
67 | } | |
68 | elsif ($data =~ /-----BEGIN PRIVATE KEY-----(.*?)-----END/sg) { | |
69 | $data = pem_to_der($data, $password); | |
70 | return $self->_import_pkcs8($data, $password); | |
71 | } | |
72 | elsif ($data =~ /-----BEGIN ENCRYPTED PRIVATE KEY-----(.*?)-----END/sg) { | |
73 | $data = pem_to_der($data, $password); | |
74 | return $self->_import_pkcs8($data, $password); | |
75 | } | |
76 | elsif ($data =~ /-----BEGIN X25519 PRIVATE KEY-----(.*?)-----END/sg) { | |
77 | $data = pem_to_der($data, $password); | |
78 | return $self->_import_pkcs8($data, $password); | |
79 | } | |
80 | elsif ($data =~ /^\s*(\{.*?\})\s*$/s) { # JSON | |
81 | my $h = CryptX::_decode_json("$1"); | |
82 | if ($h->{kty} && $h->{kty} eq "OKP" && $h->{crv} && $h->{crv} eq 'X25519') { | |
83 | return $self->_import_raw(decode_b64u($h->{d}), 1) if $h->{d}; # private | |
84 | return $self->_import_raw(decode_b64u($h->{x}), 0) if $h->{x}; # public | |
85 | } | |
86 | } | |
87 | elsif (length($data) == 32) { | |
88 | croak "FATAL: use import_key_raw() to load raw (32 bytes) X25519 key"; | |
89 | } | |
90 | else { | |
91 | my $rv = eval { $self->_import($data) } || | |
92 | eval { $self->_import_pkcs8($data, $password) } || | |
93 | eval { $self->_import_x509($data) }; | |
94 | return $rv if $rv; | |
95 | } | |
96 | croak "FATAL: invalid or unsupported X25519 key format"; | |
97 | } | |
98 | ||
99 | sub export_key_pem { | |
100 | my ($self, $type, $password, $cipher) = @_; | |
101 | local $SIG{__DIE__} = \&CryptX::_croak; | |
102 | my $key = $self->export_key_der($type||''); | |
103 | return unless $key; | |
104 | return der_to_pem($key, "X25519 PRIVATE KEY", $password, $cipher) if substr($type, 0, 7) eq 'private'; | |
105 | return der_to_pem($key, "PUBLIC KEY") if substr($type,0, 6) eq 'public'; | |
106 | } | |
107 | ||
108 | sub export_key_jwk { | |
109 | my ($self, $type, $wanthash) = @_; | |
110 | local $SIG{__DIE__} = \&CryptX::_croak; | |
111 | my $kh = $self->key2hash; | |
112 | return unless $kh; | |
113 | my $hash = { kty => "OKP", crv => "X25519" }; | |
114 | $hash->{x} = encode_b64u(pack("H*", $kh->{pub})); | |
115 | $hash->{d} = encode_b64u(pack("H*", $kh->{priv})) if $type && $type eq 'private' && $kh->{priv}; | |
116 | return $wanthash ? $hash : CryptX::_encode_json($hash); | |
117 | } | |
118 | ||
119 | sub CLONE_SKIP { 1 } # prevent cloning | |
120 | ||
121 | 1; | |
122 | ||
123 | =pod | |
124 | ||
125 | =head1 NAME | |
126 | ||
127 | Crypt::PK::X25519 - Asymmetric cryptography based on X25519 | |
128 | ||
129 | =head1 SYNOPSIS | |
130 | ||
131 | =head1 METHODS | |
132 | ||
133 | =head2 new | |
134 | ||
135 | =head2 generate_key | |
136 | ||
137 | =head2 import_key | |
138 | ||
139 | =head2 import_key_raw | |
140 | ||
141 | =head2 export_key_der | |
142 | ||
143 | =head2 export_key_pem | |
144 | ||
145 | =head2 export_key_jwk | |
146 | ||
147 | =head2 export_key_raw | |
148 | ||
149 | =head2 shared_secret | |
150 | ||
151 | =head2 is_private | |
152 | ||
153 | =head2 key2hash | |
154 | ||
155 | =cut |
109 | 109 | |
110 | 110 | =item * Public key cryptography |
111 | 111 | |
112 | L<Crypt::PK::RSA>, L<Crypt::PK::DSA>, L<Crypt::PK::ECC>, L<Crypt::PK::DH> | |
112 | L<Crypt::PK::RSA>, L<Crypt::PK::DSA>, L<Crypt::PK::ECC>, L<Crypt::PK::DH>, L<Crypt::PK::Ed25519>, L<Crypt::PK::X25519> | |
113 | 113 | |
114 | 114 | =item * Cryptographically secure random number generators - see L<Crypt::PRNG> and related modules |
115 | 115 |
100 | 100 | use Crypt::PK::DSA; |
101 | 101 | use Crypt::PK::ECC; |
102 | 102 | use Crypt::PK::RSA; |
103 | use Crypt::PK::X25519; | |
104 | use Crypt::PK::Ed25519; | |
103 | 105 | use Crypt::PK; |
104 | 106 | use Crypt::PRNG::ChaCha20; |
105 | 107 | use Crypt::PRNG::Fortuna; |
5 | 5 | plan skip_all => "set AUTHOR_MODE to enable this test (developer only!)" unless $ENV{AUTHOR_MODE}; |
6 | 6 | plan skip_all => "File::Find not installed" unless eval { require File::Find }; |
7 | 7 | plan skip_all => "Test::Pod not installed" unless eval { require Test::Pod }; |
8 | plan tests => 107; | |
8 | plan tests => 109; | |
9 | 9 | |
10 | 10 | my @files; |
11 | 11 | File::Find::find({ wanted=>sub { push @files, $_ if /\.pm$/ }, no_chdir=>1 }, 'lib'); |
21 | 21 | }, |
22 | 22 | ); |
23 | 23 | |
24 | plan tests => 107; | |
24 | plan tests => 109; | |
25 | 25 | |
26 | 26 | my @files; |
27 | 27 | File::Find::find({ wanted=>sub { push @files, $_ if /\.pm$/ }, no_chdir=>1 }, 'lib'); |
5 | 5 | plan skip_all => "set AUTHOR_MODE to enable this test (developer only!)" unless $ENV{AUTHOR_MODE}; |
6 | 6 | plan skip_all => "Pod::Coverage not installed" unless eval { require Pod::Coverage }; |
7 | 7 | plan skip_all => "File::Find not installed" unless eval { require File::Find }; |
8 | plan tests => 107; | |
8 | plan tests => 109; | |
9 | 9 | |
10 | 10 | my @files; |
11 | 11 | File::Find::find({ wanted=>sub { push @files, $_ if /\.pm$/ }, no_chdir=>1 }, 'lib'); |
0 | use strict; | |
1 | use warnings; | |
2 | use Test::More tests => 55; | |
3 | ||
4 | use Crypt::PK::Ed25519; | |
5 | ||
6 | { | |
7 | my $k; | |
8 | ||
9 | # t/data/openssl_ed25519_sk.pem | |
10 | # ED25519 Private-Key: | |
11 | # priv = 45C109BA6FD24E8B67D23EFB6B92D99CD457E2137172C0D749FE2B5A0C142DAD == RcEJum_STotn0j77a5LZnNRX4hNxcsDXSf4rWgwULa0 | |
12 | # pub = A05D1AEA5830AC9A65CDFB384660D497E3697C46B419CF2CEC85DE8BD245459D == oF0a6lgwrJplzfs4RmDUl-NpfEa0Gc8s7IXei9JFRZ0 | |
13 | ||
14 | $k = Crypt::PK::Ed25519->new->import_key_raw(pack("H*", "45C109BA6FD24E8B67D23EFB6B92D99CD457E2137172C0D749FE2B5A0C142DAD"), 'private'); | |
15 | ok($k, 'new+import_key_raw raw-priv'); | |
16 | ok($k->is_private, 'is_private raw-priv'); | |
17 | is(uc($k->key2hash->{priv}), '45C109BA6FD24E8B67D23EFB6B92D99CD457E2137172C0D749FE2B5A0C142DAD', 'key2hash->{priv} raw-priv'); | |
18 | is(uc($k->key2hash->{pub}), 'A05D1AEA5830AC9A65CDFB384660D497E3697C46B419CF2CEC85DE8BD245459D', 'key2hash->{pub} raw-priv'); | |
19 | ||
20 | $k = Crypt::PK::Ed25519->new->import_key_raw(pack("H*", "A05D1AEA5830AC9A65CDFB384660D497E3697C46B419CF2CEC85DE8BD245459D"), 'public'); | |
21 | ok($k, 'new+import_key_raw raw-pub'); | |
22 | ok(!$k->is_private, '!is_private raw-pub'); | |
23 | is(uc($k->key2hash->{pub}), 'A05D1AEA5830AC9A65CDFB384660D497E3697C46B419CF2CEC85DE8BD245459D', 'key2hash->{pub} raw-pub'); | |
24 | ||
25 | $k = Crypt::PK::Ed25519->new({ kty=>"OKP",crv=>"Ed25519",d=>"RcEJum_STotn0j77a5LZnNRX4hNxcsDXSf4rWgwULa0",x=>"oF0a6lgwrJplzfs4RmDUl-NpfEa0Gc8s7IXei9JFRZ0"}); | |
26 | ok($k, 'new JWKHASH/priv'); | |
27 | ok($k->is_private, 'is_private JWKHASH/priv'); | |
28 | is(uc($k->key2hash->{priv}), '45C109BA6FD24E8B67D23EFB6B92D99CD457E2137172C0D749FE2B5A0C142DAD', 'key2hash->{priv} JWKHASH/priv'); | |
29 | ||
30 | $k = Crypt::PK::Ed25519->new({ kty=>"OKP",crv=>"Ed25519",x=>"oF0a6lgwrJplzfs4RmDUl-NpfEa0Gc8s7IXei9JFRZ0"}); | |
31 | ok($k, 'new JWKHASH/pub'); | |
32 | ok(!$k->is_private, '!is_private JWKHASH/pub'); | |
33 | is(uc($k->key2hash->{pub}), 'A05D1AEA5830AC9A65CDFB384660D497E3697C46B419CF2CEC85DE8BD245459D', 'key2hash->{pub} JWKHASH/pub'); | |
34 | ||
35 | $k = Crypt::PK::Ed25519->new('t/data/jwk_ed25519-priv1.json'); | |
36 | ok($k, 'new JWK/priv'); | |
37 | ok($k->is_private, 'is_private JWK/priv'); | |
38 | is(uc($k->key2hash->{priv}), '45C109BA6FD24E8B67D23EFB6B92D99CD457E2137172C0D749FE2B5A0C142DAD', 'key2hash->{priv} JWK/priv'); | |
39 | ||
40 | $k = Crypt::PK::Ed25519->new('t/data/jwk_ed25519-pub1.json'); | |
41 | ok($k, 'new JWK/pub'); | |
42 | ok(!$k->is_private, '!is_private JWK/pub'); | |
43 | is(uc($k->key2hash->{pub}), 'A05D1AEA5830AC9A65CDFB384660D497E3697C46B419CF2CEC85DE8BD245459D', 'key2hash->{pub} JWK/pub'); | |
44 | ||
45 | $k = Crypt::PK::Ed25519->new('t/data/openssl_ed25519_sk.der'); | |
46 | ok($k, 'new openssl_ed25519_sk.der'); | |
47 | ok($k->is_private, 'is_private openssl_ed25519_sk.der'); | |
48 | is(uc($k->key2hash->{priv}), '45C109BA6FD24E8B67D23EFB6B92D99CD457E2137172C0D749FE2B5A0C142DAD', 'key2hash->{priv} openssl_ed25519_sk.der'); | |
49 | ||
50 | $k = Crypt::PK::Ed25519->new('t/data/openssl_ed25519_sk.pem'); | |
51 | ok($k, 'new openssl_ed25519_sk.pem'); | |
52 | ok($k->is_private, 'is_private openssl_ed25519_sk.pem'); | |
53 | is(uc($k->key2hash->{priv}), '45C109BA6FD24E8B67D23EFB6B92D99CD457E2137172C0D749FE2B5A0C142DAD', 'key2hash->{priv} openssl_ed25519_sk.pem'); | |
54 | ||
55 | $k = Crypt::PK::Ed25519->new('t/data/openssl_ed25519_sk_t.pem'); | |
56 | ok($k, 'new openssl_ed25519_sk_t.pem'); | |
57 | ok($k->is_private, 'is_private openssl_ed25519_sk_t.pem'); | |
58 | is(uc($k->key2hash->{priv}), '45C109BA6FD24E8B67D23EFB6B92D99CD457E2137172C0D749FE2B5A0C142DAD', 'key2hash->{priv} openssl_ed25519_sk_t.pem'); | |
59 | ||
60 | $k = Crypt::PK::Ed25519->new('t/data/openssl_ed25519_sk.pkcs8'); | |
61 | ok($k, 'new openssl_ed25519_sk.pkcs8'); | |
62 | ok($k->is_private, 'is_private openssl_ed25519_sk.pkcs8'); | |
63 | is(uc($k->key2hash->{priv}), '45C109BA6FD24E8B67D23EFB6B92D99CD457E2137172C0D749FE2B5A0C142DAD', 'key2hash->{priv} openssl_ed25519_sk.pkcs8'); | |
64 | ||
65 | $k = Crypt::PK::Ed25519->new('t/data/openssl_ed25519_sk_pbes1.pkcs8', 'secret'); | |
66 | ok($k, 'new openssl_ed25519_sk_pbes1.pkcs8'); | |
67 | ok($k->is_private, 'is_private openssl_ed25519_sk_pbes1.pkcs8'); | |
68 | is(uc($k->key2hash->{priv}), '45C109BA6FD24E8B67D23EFB6B92D99CD457E2137172C0D749FE2B5A0C142DAD', 'key2hash->{priv} openssl_ed25519_sk_pbes1.pkcs8'); | |
69 | ||
70 | $k = Crypt::PK::Ed25519->new('t/data/openssl_ed25519_sk_pbes2.pkcs8', 'secret'); | |
71 | ok($k, 'new openssl_ed25519_sk_pbes2.pkcs8'); | |
72 | ok($k->is_private, 'is_private openssl_ed25519_sk_pbes2.pkcs8'); | |
73 | is(uc($k->key2hash->{priv}), '45C109BA6FD24E8B67D23EFB6B92D99CD457E2137172C0D749FE2B5A0C142DAD', 'key2hash->{priv} openssl_ed25519_sk_pbes2.pkcs8'); | |
74 | ||
75 | $k = Crypt::PK::Ed25519->new('t/data/openssl_ed25519_sk_pw.pem', 'secret'); | |
76 | ok($k, 'new openssl_ed25519_sk_pw.pem'); | |
77 | ok($k->is_private, 'is_private openssl_ed25519_sk_pw.pem'); | |
78 | is(uc($k->key2hash->{priv}), '45C109BA6FD24E8B67D23EFB6B92D99CD457E2137172C0D749FE2B5A0C142DAD', 'key2hash->{priv} openssl_ed25519_sk_pw.pem'); | |
79 | ||
80 | $k = Crypt::PK::Ed25519->new('t/data/openssl_ed25519_sk_pw_t.pem', 'secret'); | |
81 | ok($k, 'new openssl_ed25519_sk_pw_t.pem'); | |
82 | ok($k->is_private, 'is_private openssl_ed25519_sk_pw_t.pem'); | |
83 | is(uc($k->key2hash->{priv}), '45C109BA6FD24E8B67D23EFB6B92D99CD457E2137172C0D749FE2B5A0C142DAD', 'key2hash->{priv} openssl_ed25519_sk_pw_t.pem'); | |
84 | ||
85 | $k = Crypt::PK::Ed25519->new('t/data/openssl_ed25519_pk.pem'); | |
86 | ok($k, 'new openssl_ed25519_pk.pem'); | |
87 | ok(!$k->is_private, '!is_private openssl_ed25519_pk.pem'); | |
88 | is(uc($k->key2hash->{pub}), 'A05D1AEA5830AC9A65CDFB384660D497E3697C46B419CF2CEC85DE8BD245459D', 'key2hash->{pub} openssl_ed25519_pk.pem'); | |
89 | ||
90 | $k = Crypt::PK::Ed25519->new('t/data/openssl_ed25519_x509.pem'); | |
91 | ok($k, 'new openssl_ed25519_x509.pem'); | |
92 | ok(!$k->is_private, '!is_private openssl_ed25519_x509.pem'); | |
93 | is(uc($k->key2hash->{pub}), 'A05D1AEA5830AC9A65CDFB384660D497E3697C46B419CF2CEC85DE8BD245459D', 'key2hash->{pub} openssl_ed25519_x509.pem'); | |
94 | ||
95 | $k = Crypt::PK::Ed25519->new('t/data/openssl_ed25519_x509.der'); | |
96 | ok($k, 'new openssl_ed25519_x509.der'); | |
97 | ok(!$k->is_private, '!is_private openssl_ed25519_x509.der'); | |
98 | is(uc($k->key2hash->{pub}), 'A05D1AEA5830AC9A65CDFB384660D497E3697C46B419CF2CEC85DE8BD245459D', 'key2hash->{pub} openssl_ed25519_x509.der'); | |
99 | ||
100 | $k = Crypt::PK::Ed25519->new('t/data/ssh/ssh_ed25519.pub'); | |
101 | ok($k, 'new ssh_ed25519.pub'); | |
102 | ok(!$k->is_private, '!is_private ssh_ed25519.pub'); | |
103 | is(uc($k->key2hash->{pub}), 'BD17B2215C443A7A1E9B286A4F0E76288130984CD942ACCCD4F1A064BB749FBE', 'key2hash->{pub} ssh_ed25519.pub'); | |
104 | ||
105 | ### $k = Crypt::PK::Ed25519->new('t/data/ssh/ssh_ed25519.priv'); | |
106 | ### ok($k, 'new ssh_ed25519.priv'); | |
107 | ### ok($k->is_private, 'is_private ssh_ed25519.priv'); | |
108 | ### | |
109 | ### $k = Crypt::PK::Ed25519->new('t/data/ssh/ssh_ed25519_pw.priv', 'secret'); | |
110 | ### ok($k, 'new ssh_ed25519_pw.priv'); | |
111 | ### ok($k->is_private, 'is_private ssh_ed25519_pw.priv'); | |
112 | } |
0 | use strict; | |
1 | use warnings; | |
2 | use Test::More tests => 46; | |
3 | ||
4 | use Crypt::PK::X25519; | |
5 | ||
6 | { | |
7 | my $k; | |
8 | ||
9 | # t/data/openssl_x25519_sk.pem | |
10 | # X25519 Private-Key: | |
11 | # priv = 002F93D10BA5728D8DD8E9527721DABA3261C0BB1BEFDE7B4BBDAC631D454651 == AC-T0Qulco2N2OlSdyHaujJhwLsb7957S72sYx1FRlE | |
12 | # pub = EA7806F721A8570512C8F6EFB4E8D620C49A529E4DF5EAA77DEC646FB1E87E41 == 6ngG9yGoVwUSyPbvtOjWIMSaUp5N9eqnfexkb7HofkE | |
13 | ||
14 | $k = Crypt::PK::X25519->new->import_key_raw(pack("H*", "002F93D10BA5728D8DD8E9527721DABA3261C0BB1BEFDE7B4BBDAC631D454651"), 'private'); | |
15 | ok($k, 'new+import_key_raw raw-priv'); | |
16 | ok($k->is_private, 'is_private raw-priv'); | |
17 | is(uc($k->key2hash->{priv}), '002F93D10BA5728D8DD8E9527721DABA3261C0BB1BEFDE7B4BBDAC631D454651', 'key2hash->{priv} raw-priv'); | |
18 | is(uc($k->key2hash->{pub}), 'EA7806F721A8570512C8F6EFB4E8D620C49A529E4DF5EAA77DEC646FB1E87E41', 'key2hash->{pub} raw-priv'); | |
19 | ||
20 | $k = Crypt::PK::X25519->new->import_key_raw(pack("H*", "EA7806F721A8570512C8F6EFB4E8D620C49A529E4DF5EAA77DEC646FB1E87E41"), 'public'); | |
21 | ok($k, 'new+import_key_raw raw-pub'); | |
22 | ok(!$k->is_private, '!is_private raw-pub'); | |
23 | is(uc($k->key2hash->{pub}), 'EA7806F721A8570512C8F6EFB4E8D620C49A529E4DF5EAA77DEC646FB1E87E41', 'key2hash->{pub} raw-pub'); | |
24 | ||
25 | $k = Crypt::PK::X25519->new({ kty=>"OKP",crv=>"X25519",d=>"AC-T0Qulco2N2OlSdyHaujJhwLsb7957S72sYx1FRlE",x=>"6ngG9yGoVwUSyPbvtOjWIMSaUp5N9eqnfexkb7HofkE"}); | |
26 | ok($k, 'new JWKHASH/priv'); | |
27 | ok($k->is_private, 'is_private JWKHASH/priv'); | |
28 | is(uc($k->key2hash->{priv}), '002F93D10BA5728D8DD8E9527721DABA3261C0BB1BEFDE7B4BBDAC631D454651', 'key2hash->{priv} JWKHASH/priv'); | |
29 | ||
30 | $k = Crypt::PK::X25519->new({ kty=>"OKP",crv=>"X25519",x=>"6ngG9yGoVwUSyPbvtOjWIMSaUp5N9eqnfexkb7HofkE"}); | |
31 | ok($k, 'new JWKHASH/pub'); | |
32 | ok(!$k->is_private, '!is_private JWKHASH/pub'); | |
33 | is(uc($k->key2hash->{pub}), 'EA7806F721A8570512C8F6EFB4E8D620C49A529E4DF5EAA77DEC646FB1E87E41', 'key2hash->{pub} JWKHASH/pub'); | |
34 | ||
35 | $k = Crypt::PK::X25519->new('t/data/jwk_x25519-priv1.json'); | |
36 | ok($k, 'new JWK/priv'); | |
37 | ok($k->is_private, 'is_private JWK/priv'); | |
38 | is(uc($k->key2hash->{priv}), '002F93D10BA5728D8DD8E9527721DABA3261C0BB1BEFDE7B4BBDAC631D454651', 'key2hash->{priv} JWK/priv'); | |
39 | ||
40 | $k = Crypt::PK::X25519->new('t/data/jwk_x25519-pub1.json'); | |
41 | ok($k, 'new JWK/pub'); | |
42 | ok(!$k->is_private, '!is_private JWK/pub'); | |
43 | is(uc($k->key2hash->{pub}), 'EA7806F721A8570512C8F6EFB4E8D620C49A529E4DF5EAA77DEC646FB1E87E41', 'key2hash->{pub} JWK/pub'); | |
44 | ||
45 | $k = Crypt::PK::X25519->new('t/data/openssl_x25519_sk.der'); | |
46 | ok($k, 'new openssl_x25519_sk.der'); | |
47 | ok($k->is_private, 'is_private openssl_x25519_sk.der'); | |
48 | is(uc($k->key2hash->{priv}), '002F93D10BA5728D8DD8E9527721DABA3261C0BB1BEFDE7B4BBDAC631D454651', 'key2hash->{priv} openssl_x25519_sk.der'); | |
49 | ||
50 | $k = Crypt::PK::X25519->new('t/data/openssl_x25519_sk.pem'); | |
51 | ok($k, 'new openssl_x25519_sk.pem'); | |
52 | ok($k->is_private, 'is_private openssl_x25519_sk.pem'); | |
53 | is(uc($k->key2hash->{priv}), '002F93D10BA5728D8DD8E9527721DABA3261C0BB1BEFDE7B4BBDAC631D454651', 'key2hash->{priv} openssl_x25519_sk.pem'); | |
54 | ||
55 | $k = Crypt::PK::X25519->new('t/data/openssl_x25519_sk_t.pem'); | |
56 | ok($k, 'new openssl_x25519_sk_t.pem'); | |
57 | ok($k->is_private, 'is_private openssl_x25519_sk_t.pem'); | |
58 | is(uc($k->key2hash->{priv}), '002F93D10BA5728D8DD8E9527721DABA3261C0BB1BEFDE7B4BBDAC631D454651', 'key2hash->{priv} openssl_x25519_sk_t.pem'); | |
59 | ||
60 | $k = Crypt::PK::X25519->new('t/data/openssl_x25519_sk.pkcs8'); | |
61 | ok($k, 'new openssl_x25519_sk.pkcs8'); | |
62 | ok($k->is_private, 'is_private openssl_x25519_sk.pkcs8'); | |
63 | is(uc($k->key2hash->{priv}), '002F93D10BA5728D8DD8E9527721DABA3261C0BB1BEFDE7B4BBDAC631D454651', 'key2hash->{priv} openssl_x25519_sk.pkcs8'); | |
64 | ||
65 | $k = Crypt::PK::X25519->new('t/data/openssl_x25519_sk_pbes1.pkcs8', 'secret'); | |
66 | ok($k, 'new openssl_x25519_sk_pbes1.pkcs8'); | |
67 | ok($k->is_private, 'is_private openssl_x25519_sk_pbes1.pkcs8'); | |
68 | is(uc($k->key2hash->{priv}), '002F93D10BA5728D8DD8E9527721DABA3261C0BB1BEFDE7B4BBDAC631D454651', 'key2hash->{priv} openssl_x25519_sk_pbes1.pkcs8'); | |
69 | ||
70 | $k = Crypt::PK::X25519->new('t/data/openssl_x25519_sk_pbes2.pkcs8', 'secret'); | |
71 | ok($k, 'new openssl_x25519_sk_pbes2.pkcs8'); | |
72 | ok($k->is_private, 'is_private openssl_x25519_sk_pbes2.pkcs8'); | |
73 | is(uc($k->key2hash->{priv}), '002F93D10BA5728D8DD8E9527721DABA3261C0BB1BEFDE7B4BBDAC631D454651', 'key2hash->{priv} openssl_x25519_sk_pbes2.pkcs8'); | |
74 | ||
75 | $k = Crypt::PK::X25519->new('t/data/openssl_x25519_sk_pw.pem', 'secret'); | |
76 | ok($k, 'new openssl_x25519_sk_pw.pem'); | |
77 | ok($k->is_private, 'is_private openssl_x25519_sk_pw.pem'); | |
78 | is(uc($k->key2hash->{priv}), '002F93D10BA5728D8DD8E9527721DABA3261C0BB1BEFDE7B4BBDAC631D454651', 'key2hash->{priv} openssl_x25519_sk_pw.pem'); | |
79 | ||
80 | $k = Crypt::PK::X25519->new('t/data/openssl_x25519_sk_pw_t.pem', 'secret'); | |
81 | ok($k, 'new openssl_x25519_sk_pw_t.pem'); | |
82 | ok($k->is_private, 'is_private openssl_x25519_sk_pw_t.pem'); | |
83 | is(uc($k->key2hash->{priv}), '002F93D10BA5728D8DD8E9527721DABA3261C0BB1BEFDE7B4BBDAC631D454651', 'key2hash->{priv} openssl_x25519_sk_pw_t.pem'); | |
84 | ||
85 | $k = Crypt::PK::X25519->new('t/data/openssl_x25519_pk.pem'); | |
86 | ok($k, 'new openssl_x25519_pk.pem'); | |
87 | ok(!$k->is_private, '!is_private openssl_x25519_pk.pem'); | |
88 | is(uc($k->key2hash->{pub}), 'EA7806F721A8570512C8F6EFB4E8D620C49A529E4DF5EAA77DEC646FB1E87E41', 'key2hash->{pub} openssl_x25519_pk.pem'); | |
89 | } |