28 Commits

Author SHA1 Message Date
idk
b4e9da89e9 Merge pull request #3 from hkh4n/tests
Added tests for addresses and keys. Changed NewDestination()
2024-09-17 18:18:41 -04:00
Haris Khan
afae8e6f14 changed urls to fit with tests 2024-09-15 21:56:23 -04:00
Haris Khan
6c95fc6ac7 -Critical change: trim newline from private key
-commented out newline investigation in Test_KeyGenerationAndHandling
-Test_KeyGnerationAndHandling works as expected now.
2024-09-15 21:48:51 -04:00
Haris Khan
ada0d39af4 Investigating LoadKeysIncompat (amend instead) 2024-09-15 15:17:33 -04:00
Haris Khan
59bffea3f3 Merge remote-tracking branch 'origin/tests-keys' into tests-keys
# Conflicts:
#	I2PAddr_test.go
2024-09-15 13:13:36 -04:00
Haris Khan
316dc840d6 Investigating LoadKeysIncompat (amend instead) 2024-09-15 13:08:11 -04:00
Haris Khan
90da121025 Investigating LoadKeysIncompat x4 2024-09-15 12:31:39 -04:00
Haris Khan
d362997650 Investigating LoadKeysIncompat x3 2024-09-15 11:28:47 -04:00
Haris Khan
4004d3050d Investigating LoadKeysIncompat x2 2024-09-15 11:16:46 -04:00
Haris Khan
0d58ebfa78 Investigating LoadKeysIncompat 2024-09-15 11:15:18 -04:00
Haris Khan
acbf68bc58 Added broken tests for keys (TODO) 2024-09-15 00:46:32 -04:00
Haris Khan
dc8ef52d46 Revamped failures for Test_basic() and Test_Basic_Lookup().
Added:
-Test_NewI2PAddrFromString()
-Test_I2PAddr()
-Test_DestHashFromString()
-Test_I2PAddrToBytes()

with sub-tests.
2024-09-15 00:45:09 -04:00
Haris Khan
aae46b4dec added TestKeyGenerationAndHandling 2024-09-14 21:31:52 -04:00
Haris Khan
597e1da68d Merge remote-tracking branch 'origin/tests' into tests + Correct Func names as "Test_"
# Conflicts:
#	I2PAddr_test.go
2024-09-14 08:04:19 -04:00
Haris Khan
4cf76aeec2 Added various tests 2024-09-14 07:59:56 -04:00
Haris Khan
80bfc77145 Added various tests 2024-09-14 00:32:23 -04:00
idk
5ae94bc639 Merge pull request #1 from hkh4n/error-handling
More robust error handling in I2PAddr.go
2024-09-10 14:33:24 -04:00
Haris Khan
c86c07c1df refactored error handling in HostnameEntry() 2024-09-09 20:20:43 -04:00
Haris Khan
fde718e1d8 refactored error handling in StoreKeysIncompat() 2024-09-09 20:17:21 -04:00
Haris Khan
3a99966c42 refactored error handling in LoadKeys() 2024-09-09 20:07:43 -04:00
Haris Khan
d23cb52f2c refactored error handling in fileExists() 2024-09-09 20:02:56 -04:00
eyedeekay
81f9b8a8cc update makefile 2024-01-09 14:43:01 -05:00
idk
28d6bf4d97 update go modules 2023-03-07 02:11:16 +00:00
idk
9307ae9cf4 StringIsBase64 is bittorrent mode 2022-09-22 23:14:43 -04:00
idk
e6cb984e8f add option to only return b64s 2022-09-21 00:36:57 -04:00
idk
18a714c195 fix missing LICENSE file 2022-09-11 19:55:14 -04:00
idk
1048b5ce6b update index.html 2022-08-04 18:07:22 -04:00
idk
910de44ac8 Make the StoreKeys function automatically set up the file if it's not already there 2022-08-03 16:46:30 -04:00
8 changed files with 510 additions and 29 deletions

0
.nojekyll Normal file
View File

View File

@@ -21,6 +21,9 @@ var (
i2pB32enc *base32.Encoding = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567")
)
// If you set this to true, Addr will return a base64 String()
var StringIsBase64 bool
// The public and private keys associated with an I2P destination. I2P hides the
// details of exactly what this is, so treat them as blobs, but generally: One
// pair of DSA keys, one pair of ElGamal keys, and sometimes (almost never) also
@@ -44,7 +47,7 @@ func fileExists(filename string) (bool, error) {
if os.IsNotExist(err) {
return false, nil
} else if err != nil {
return false, err
return false, fmt.Errorf("error checking file existence: %w", err)
}
return !info.IsDir(), nil
}
@@ -68,24 +71,36 @@ func LoadKeys(r string) (I2PKeys, error) {
if err != nil {
return I2PKeys{}, err
}
if exists {
fi, err := os.Open(r)
if err != nil {
return I2PKeys{}, err
}
defer fi.Close()
return LoadKeysIncompat(fi)
if !exists {
return I2PKeys{}, fmt.Errorf("file does not exist: %s", r)
}
return I2PKeys{}, err
fi, err := os.Open(r)
if err != nil {
return I2PKeys{}, fmt.Errorf("error opening file: %w", err)
}
defer fi.Close()
return LoadKeysIncompat(fi)
}
// store keys in non standard format
func StoreKeysIncompat(k I2PKeys, w io.Writer) (err error) {
_, err = io.WriteString(w, k.Address.Base64()+"\n"+k.Both)
return
func StoreKeysIncompat(k I2PKeys, w io.Writer) error {
_, err := io.WriteString(w, k.Address.Base64()+"\n"+k.Both)
if err != nil {
return fmt.Errorf("error writing keys: %w", err)
}
return nil
}
func StoreKeys(k I2PKeys, r string) error {
if _, err := os.Stat(r); err != nil {
if os.IsNotExist(err) {
fi, err := os.Create(r)
if err != nil {
return err
}
defer fi.Close()
return StoreKeysIncompat(k, fi)
}
}
fi, err := os.Open(r)
if err != nil {
return err
@@ -160,7 +175,7 @@ func (k I2PKeys) String() string {
func (k I2PKeys) HostnameEntry(hostname string, opts crypto.SignerOpts) (string, error) {
sig, err := k.Sign(rand.Reader, []byte(hostname), opts)
if err != nil {
return "", err
return "", fmt.Errorf("error signing hostname: %w", err)
}
return string(sig), nil
}
@@ -228,6 +243,9 @@ func (a I2PAddr) Base64() string {
// Returns the I2P destination (base32-encoded)
func (a I2PAddr) String() string {
if StringIsBase64 {
return a.Base64()
}
return string(a.Base32())
}
@@ -251,7 +269,7 @@ func NewI2PAddrFromString(addr string) (I2PAddr, error) {
addr = strings.Trim(addr, "\t\n\r\f ")
// very basic check
if len(addr) > 4096 || len(addr) < 516 {
return I2PAddr(""), errors.New("Not an I2P address")
return I2PAddr(""), errors.New(addr + " is not an I2P address")
}
buf := make([]byte, i2pB64enc.DecodedLen(len(addr)))
if _, err := i2pB64enc.Decode(buf, []byte(addr)); err != nil {
@@ -319,6 +337,10 @@ HELLO VERSION MIN=3.1 MAX=3.1
DEST GENERATE SIGNATURE_TYPE=7
*/
func NewDestination() (*I2PKeys, error) {
removeNewlines := func(s string) string {
return strings.ReplaceAll(strings.ReplaceAll(s, "\r\n", ""), "\n", "")
}
//
conn, err := net.Dial("tcp", "127.0.0.1:7656")
if err != nil {
return nil, err
@@ -351,6 +373,8 @@ func NewDestination() (*I2PKeys, error) {
pub := strings.Split(strings.Split(string(buf[:n]), "PRIV=")[0], "PUB=")[1]
priv := strings.Split(string(buf[:n]), "PRIV=")[1]
priv = removeNewlines(priv) //There is an extraneous newline in the private key, so we'll remove it.
return &I2PKeys{
Address: I2PAddr(pub),
Both: pub + priv,

View File

@@ -1,21 +1,28 @@
package i2pkeys
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
// "time"
)
const yoursam = "127.0.0.1:7656"
const (
yoursam = "127.0.0.1:7656"
validShortenedI2PAddr = "idk.i2p"
validI2PAddrB32 = "b2o47zwxqjbn7jj37yqkmvbmci7kqubwgxu3umqid7cexmc7xudq.b32.i2p"
validI2PAddrB64 = "spHxea2xhPjKH9yyEeFJ96aqtvKidH-GiWxs8dH6RWS2FrDoWFhuEkfw77pF~Hv57lLhMaMB3qqWjCtYXOjL48Q1zYbr3MAcTO44wwVPjOU1hU77vbJcUuwBeRvaSr2dZx-FiTSOdQuhPD1EozYNRIMFwZ0fZwKf~3Gj4dEWccOLKs~NbiPsj-~tc5tmhAs8yBeoZEqEBe40X75SfSHY-EnstcZevVAwIXYk3zX3KF0mji3bo2QXuTFcMZHHLiLd2AHLRANzWyvQ9DC1rnCsHJM4xxV4dVp0pHkP1hwBo7E0NJvN4nFkQcj-FI2RJ~cFUCk7qc86PRHwvKCjzSlrgjtDsMUwd83Dz1PfpzCqHNLUFWI7uPKbKcJZhasFm4kEhUyupd85q75Ch2IZE9J2JXodSxmseO5ZKcHK6pFtfR-HbzKjIe92TWHsNkmvtoHiUaOVrWnk-cmo2I1W1VxfL08teDxQ13P80uFaMcameRzuFM2F8pSOpoyEJUDRGLEeBQAEAAcAAA=="
)
func Test_Basic(t *testing.T) {
fmt.Println("Test_Basic")
fmt.Println("\tAttaching to SAM at " + yoursam)
keys, err := NewDestination()
if err != nil {
fmt.Println(err.Error())
t.Fail()
return
t.Fatal(err.Error())
}
fmt.Println(keys.String())
}
@@ -23,11 +30,211 @@ func Test_Basic(t *testing.T) {
func Test_Basic_Lookup(t *testing.T) {
fmt.Println("Test_Basic")
fmt.Println("\tAttaching to SAM at " + yoursam)
keys, err := Lookup("idk.i2p")
keys, err := Lookup(validShortenedI2PAddr)
if err != nil {
fmt.Println(err.Error())
t.Fail()
return
t.Fatal(err.Error())
}
fmt.Println(keys.String())
}
func Test_NewI2PAddrFromString(t *testing.T) {
t.Run("Valid base64 address", func(t *testing.T) {
addr, err := NewI2PAddrFromString(validI2PAddrB64)
if err != nil {
t.Fatalf("NewI2PAddrFromString failed for valid address: '%v'", err)
}
if addr.Base64() != validI2PAddrB64 {
t.Errorf("NewI2PAddrFromString returned incorrect address. Got '%s', want '%s'", addr.Base64(), validI2PAddrB64)
}
})
t.Run("Invalid address", func(t *testing.T) {
invalidAddr := "not-a-valid-address"
_, err := NewI2PAddrFromString(invalidAddr)
if err == nil {
t.Error("NewI2PAddrFromString should have failed for invalid address")
}
})
t.Run("Base32 address", func(t *testing.T) {
_, err := NewI2PAddrFromString(validI2PAddrB32)
if err == nil {
t.Error("NewI2PAddrFromString should have failed for base32 address")
}
})
t.Run("Empty address", func(t *testing.T) {
_, err := NewI2PAddrFromString("")
if err == nil {
t.Error("NewI2PAddrFromString should have failed for empty address")
}
})
t.Run("Address with .i2p suffix", func(t *testing.T) { //CHECK
addr, err := NewI2PAddrFromString(validI2PAddrB64 + ".i2p")
if err != nil {
t.Fatalf("NewI2PAddrFromString failed for address with .i2p suffix: '%v'", err)
}
if addr.Base64() != validI2PAddrB64 {
t.Errorf("NewI2PAddrFromString returned incorrect address. Got '%s', want '%s'", addr.Base64(), validI2PAddrB64)
}
})
}
func Test_I2PAddr(t *testing.T) {
addr := I2PAddr(validI2PAddrB64)
base32 := addr.Base32()
t.Run("Base32 suffix", func(t *testing.T) {
if !strings.HasSuffix(base32, ".b32.i2p") {
t.Errorf("Base32 address should end with .b32.i2p, got %s", base32)
}
})
t.Run("Base32 length", func(t *testing.T) {
if len(base32) != 60 {
t.Errorf("Base32 address should be 60 characters long, got %d", len(base32))
}
})
}
func Test_DestHashFromString(t *testing.T) {
t.Run("Valid hash", func(t *testing.T) {
hash, err := DestHashFromString(validI2PAddrB32)
if err != nil {
t.Fatalf("DestHashFromString failed for valid hash: '%v'", err)
}
if hash.String() != validI2PAddrB32 {
t.Errorf("DestHashFromString returned incorrect hash. Got '%s', want '%s'", hash.String(), validI2PAddrB32)
}
})
t.Run("Invalid hash", func(t *testing.T) {
invalidHash := "not-a-valid-hash"
_, err := DestHashFromString(invalidHash)
if err == nil {
t.Error("DestHashFromString should have failed for invalid hash")
}
})
t.Run("Empty hash", func(t *testing.T) {
_, err := DestHashFromString("")
if err == nil {
t.Error("DestHashFromString should have failed for empty hash")
}
})
}
func Test_I2PAddrToBytes(t *testing.T) {
addr := I2PAddr(validI2PAddrB64)
t.Run("ToBytes and back", func(t *testing.T) {
decodedBytes, err := addr.ToBytes()
if err != nil {
t.Fatalf("ToBytes failed: '%v'", err)
}
encodedString := i2pB64enc.EncodeToString(decodedBytes)
if encodedString != validI2PAddrB64 {
t.Errorf("Round-trip encoding/decoding failed. Got '%s', want '%s'", encodedString, validI2PAddrB64)
}
})
t.Run("Direct decoding comparison", func(t *testing.T) {
decodedBytes, err := addr.ToBytes()
if err != nil {
t.Fatalf("ToBytes failed: '%v'", err)
}
directlyDecoded, err := i2pB64enc.DecodeString(validI2PAddrB64)
if err != nil {
t.Fatalf("Failed to decode test string using i2pB64enc: '%v'", err)
}
if !bytes.Equal(decodedBytes, directlyDecoded) {
t.Errorf("Mismatch between ToBytes result and direct decoding. ToBytes len: '%d', Direct decoding len: '%d'", len(decodedBytes), len(directlyDecoded))
}
})
}
/*
func removeNewlines(s string) string {
return strings.ReplaceAll(strings.ReplaceAll(s, "\r\n", ""), "\n", "")
}
*/
func Test_KeyGenerationAndHandling(t *testing.T) {
// Generate new keys
keys, err := NewDestination()
if err != nil {
t.Fatalf("Failed to generate new I2P keys: %v", err)
}
t.Run("LoadKeysIncompat", func(t *testing.T) {
//extract keys
addr := keys.Address
fmt.Println(addr)
//both := removeNewlines(keys.Both)
both := keys.Both
fmt.Println(both)
//FORMAT TO LOAD: (Address, Both)
addrload := addr.Base64() + "\n" + both
r := strings.NewReader(addrload)
loadedKeys, err := LoadKeysIncompat(r)
if err != nil {
t.Fatalf("LoadKeysIncompat failed: %v", err)
}
if loadedKeys.Address != keys.Address {
//fmt.Printf("loadedKeys.Address md5hash: '%s'\n keys.Address md5hash: '%s'\n", getMD5Hash(string(loadedKeys.Address)), getMD5Hash(string(keys.Address)))
t.Errorf("LoadKeysIncompat returned incorrect address. Got '%s', want '%s'", loadedKeys.Address, keys.Address)
}
if loadedKeys.Both != keys.Both {
t.Errorf("LoadKeysIncompat returned incorrect pair. Got '%s'\nwant '%s'\n", loadedKeys.Both, keys.Both)
/*
if loadedKeys.Both == removeNewlines(keys.Both) {
fmt.Println("However, both pairs are correct if newline is removed in generated keys.")
}
*/
}
})
expected := keys.Address.Base64() + "\n" + keys.Both
t.Run("StoreKeysIncompat", func(t *testing.T) {
var buf bytes.Buffer
err := StoreKeysIncompat(*keys, &buf)
if err != nil {
t.Fatalf("StoreKeysIncompat failed: '%v'", err)
}
if buf.String() != expected {
t.Errorf("StoreKeysIncompat wrote incorrect data. Got '%s', want '%s'", buf.String(), expected)
}
})
t.Run("StoreKeys", func(t *testing.T) {
tmpDir, err := ioutil.TempDir("", "test_keys_")
if err != nil {
t.Fatalf("Failed to create temp directory: '%v'", err)
}
defer os.RemoveAll(tmpDir)
tmpFilePath := filepath.Join(tmpDir, "test_keys.txt")
err = StoreKeys(*keys, tmpFilePath)
if err != nil {
t.Fatalf("StoreKeys failed: '%v'", err)
}
content, err := ioutil.ReadFile(tmpFilePath)
if err != nil {
t.Fatalf("Failed to read temp file: '%v'", err)
}
if string(content) != expected {
t.Errorf("StoreKeys wrote incorrect data. Got '%s', want '%s'", string(content), expected)
}
})
}

24
LICENSE Normal file
View File

@@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>

View File

@@ -1,13 +1,31 @@
build:
go build -a -tags netgo -ldflags '-w -extldflags "-static"'
USER_GH=eyedeekay
VERSION=0.33.7
packagename=i2pkeys
echo:
@echo "$(GOPATH)"
@echo "type make version to do release $(VERSION)"
version:
github-release release -s $(GITHUB_TOKEN) -u $(USER_GH) -r $(packagename) -t v$(VERSION) -d "version $(VERSION)"
del:
github-release delete -s $(GITHUB_TOKEN) -u $(USER_GH) -r $(packagename) -t v$(VERSION)
tar:
tar --exclude .git \
--exclude .go \
--exclude bin \
-cJvf ../$(packagename)_$(VERSION).orig.tar.xz .
copier:
echo '#! /usr/bin/env sh' > deb/copy.sh
echo 'for f in $$(ls); do scp $$f/*.deb user@192.168.99.106:~/DEBIAN_PKGS/$$f/main/; done' >> deb/copy.sh
fmt:
find . -path ./.go -prune -o -name "*.go" -exec gofmt -w {} \;
find . -path ./.go -prune -o -name "*.i2pkeys" -exec rm {} \;
install:
install -m755 i2pkeys /usr/local/bin/i2pkeys
upload-linux:
github-release upload -R -u $(USER_GH) -r "$(packagename)" -t $(VERSION) -l `sha256sum ` -n "$(packagename)" -f "$(packagename)"

BIN
i2plogo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

51
index.html Normal file
View File

@@ -0,0 +1,51 @@
<html>
<head>
<title>
i2pkeys
</title>
<meta name="author" content="eyedeekay" />
<meta name="description" content="i2pkeys" />
<meta name="keywords" content="master" />
<link rel="stylesheet" type="text/css" href="style.css" />
</head>
<body>
<div id="navbar">
<a href="#shownav">
Show navigation
</a>
<div id="shownav">
<div id="hidenav">
<ul>
<li>
<a href="index.html">
index
</a>
</li>
</ul>
<br>
<a href="#hidenav">
Hide Navigation
</a>
</div>
</div>
</div>
<h1>
<a href="/">
i2pkeys
</a>
</h1>
<p>
Generates and displays the contents of files that are storing i2p keys in the
incompatible format used for sam3
</p>
<div>
<iframe src="https://snowflake.torproject.org/embed.html" width="320" height="240" frameborder="0" scrolling="no"></iframe>
</div>
<div>
<a href="https://geti2p.net/">
<img src="i2plogo.png"></img>
I2P
</a>
</div>
</body>
</html>

157
style.css Normal file
View File

@@ -0,0 +1,157 @@
/* edgar default CSS file */
body {
font-family: "Roboto";
font-family: monospace;
text-align: justify;
background-color: #373636;
color: whitesmoke;
font-size: 1.15em;
}
ul {
width: 55%;
display: block;
}
ol {
width: 55%;
display: block;
}
li {
margin-top: 1%;
}
p {
max-width: 90%;
margin-top: 1%;
margin-left: 3%;
margin-right: 3%;
}
img {
float: left;
top: 5%;
left: 5%;
max-width: 60%;
display: inline;
}
.inline {
display: inline;
}
.link-button:focus {
outline: none;
}
.link-button:active {
color: red;
}
code {
font-family: monospace;
border-radius: 5%;
padding: 1%;
border-color: darkgray;
font-size: .9em;
}
a {
color: #C6D9FE;
padding: 1%;
}
ul li {
color: #C6D9FE;
}
iframe {
background: aliceblue;
border-radius: 15%;
margin: 2%;
}
.container {
width: 36vw;
height: 64vh;
display: inline-block;
margin: 0;
padding: 0;
}
.editor-toolbar a {
display: inline-block;
text-align: center;
text-decoration: none !important;
color: whitesmoke !important;
}
#feed {
width: 60vw;
height: unset !important;
margin: 0;
padding: 0;
float: right;
background-color: #373636;
color: whitesmoke;
border: #C6D9FE solid 1px;
}
.thread-post,
.thread {
color: whitesmoke !important;
background-color: #373636;
border: 1px solid darkgray;
font-size: inherit;
padding-top: 1%;
padding-bottom: 1%;
}
.thread-post {
margin-left: 4%;
}
input {
text-align: center;
color: whitesmoke !important;
background-color: #373636;
border: 1px solid darkgray;
font: normal normal normal 14px/1 FontAwesome;
font-size: inherit;
padding-top: 1%;
padding-bottom: 1%;
}
.thread-hash {
text-align: right;
color: whitesmoke !important;
background-color: #373636;
border: 1px solid darkgray;
font-size: inherit;
padding-top: 1%;
padding-bottom: 1%;
}
.post-body {
text-align: left;
color: whitesmoke !important;
font-size: inherit;
padding-top: 1%;
padding-bottom: 1%;
}
#show {display:none; }
#hide {display:block; }
#show:target {display: block; }
#hide:target {display: none; }
#shownav {display:none; }
#hidenav {display:block; }
#shownav:target {display: block; }
#hidenav:target {display: none; }
#navbar {
float: right;
width: 10%;
}